diff --git a/README.md b/README.md
index 62151a5e..bf5168a1 100644
--- a/README.md
+++ b/README.md
@@ -870,6 +870,8 @@ package.json:
## Vite integration todos
* [ ] Check again, why we need to use `.value` on some vue-i18n properties (e.g. i18n.global.locale.value). Source: https://vue-i18n.intlify.dev/guide/advanced/lazy.html#lazy-loading
* [ ] `manifest.json` uses a different format than before. Compatible with Pimcore/Java?
+* [ ] Check if module polyfill is required: https://vitejs.dev/config/build-options.html#build-modulepreload
+* [ ] CSS Prefixing
* [ ] Update storybook to use vite config.
* [ ] Check if "style only components" from `src/setup/components.ts` works as expected.
* [ ] Try to add README content to styleguide index page again.
@@ -880,6 +882,7 @@ package.json:
* [ ] Should there be basic styles for form elements (if no class applied)?
* [ ] cmd + click in PhpStorm leads to Interface instead of implementation.
* [ ] Update README
+* [ ] Check "Vite Inspect"
## Roadmap
* [ ] Implement dual build (ES5/ES2015+)
diff --git a/package.json b/package.json
index cb1eb0f3..4c953549 100644
--- a/package.json
+++ b/package.json
@@ -16,6 +16,7 @@
"lint:eslint": "eslint --ext .ts,.js,.vue ./",
"test": "npm run lint && npm run jest; exit 0;",
"build": "vite build",
+ "build:pimcore": "npm run build -- --mode='pimcore'",
"build:w": "NODE_ENV=production webpack --mode production",
"build:profile": "npm run build -- --mode='profile'",
"build:profile:w": "NODE_ENV=production webpack --mode production --profile",
@@ -36,7 +37,8 @@
"styleguideBuildPath": "./dist",
"styleguidePath": "/",
"developmentPath": "/",
- "outputAssetsFolder": "assets/",
+ "outputAssetsFolder": "assets",
+ "appName": "app",
"filePrefix": "",
"themeSource": "src/setup/scss/themes/",
"themeFiles": [
diff --git a/pimcore.html b/pimcore.html
new file mode 100644
index 00000000..761b5a9b
--- /dev/null
+++ b/pimcore.html
@@ -0,0 +1,52 @@
+
+
+
+
+
+ Vue template
+
+
+
+
+
+
+
+
+
diff --git a/src/pimcore/_gui.scss b/src/pimcore/_gui.scss
new file mode 100644
index 00000000..80badad7
--- /dev/null
+++ b/src/pimcore/_gui.scss
@@ -0,0 +1,266 @@
+// stylelint-disable selector-class-pattern
+@use '../setup/scss/variables';
+@use '../setup/scss/mixins';
+
+$pimcore-gui--color: #3c3f41;
+
+[class].p-grid { // The [class] selector was required to increase the weight inside .wysiwyg.
+ $this: &;
+
+ display: grid;
+ grid-template-columns: 1fr 1fr 1fr 1fr;
+ gap: variables.$spacing--20;
+ margin-bottom: variables.$spacing--50;
+
+ &__item {
+ align-self: start;
+
+ .pimcore_block_buttons {
+ display: flex;
+ justify-content: center;
+ }
+ }
+
+ &--item-outline {
+ #{$this}__item {
+ border: 1px solid variables.$color-grayscale--500;
+ }
+ }
+}
+
+.p-content-placeholder {
+ padding: variables.$spacing--30;
+ outline: 1px dashed variables.$color-grayscale--500;
+ background-color: variables.$color-grayscale--700;
+
+ &--text-align-center {
+ text-align: center;
+ }
+
+ &--spacing-500 {
+ margin: variables.$spacing--50 0;
+ }
+}
+
+.p-meta {
+ $this: &;
+
+ @include mixins.font(variables.$font-size--10, 12px);
+
+ display: flex;
+ align-items: center;
+ margin-bottom: variables.$spacing--5;
+ opacity: 0.2;
+ color: $pimcore-gui--color;
+ font-family: monospace;
+
+ .pimcore_block_entry:hover & {
+ opacity: 1;
+ }
+
+ .pimcore_block_entry:hover .pimcore_block_entry:not(:hover) & {
+ opacity: 0.2; // Reset for nested components elements.
+ }
+
+ dd,
+ dt {
+ margin: 0;
+ padding: 0;
+
+ + dt {
+ padding-left: variables.$spacing--5;
+ border-left: 1px solid rgba($pimcore-gui--color, 0.25);
+ }
+ }
+
+ dt {
+ padding: variables.$spacing--2 variables.$spacing--5;
+ }
+
+ dd {
+ margin-left: variables.$spacing--5;
+ opacity: 0.75;
+ }
+
+ dd + dd::before {
+ content: ' | ';
+ }
+
+ &--align-right {
+ #{$this}__list {
+ right: auto;
+ left: calc(100% + #{variables.$spacing--2});
+ }
+ }
+}
+
+[class].p-options { // The [class] selector was required to increase the weight inside .wysiwyg.
+ $this: &;
+
+ display: flex;
+ align-items: center;
+ padding: variables.$spacing--5 0;
+
+ .pimcore_block_entry & {
+ opacity: 0.3;
+ }
+
+ &--visible,
+ .pimcore_block_entry_over &,
+ .pimcore_block_entry:hover & {
+ opacity: 1 !important; // stylelint-disable-line declaration-no-important
+ }
+
+ &__item {
+ margin-right: variables.$spacing--10;
+
+ &:last-child {
+ margin-right: 0;
+ }
+ }
+
+ // Non-empty pimcore_input are not recognizable as such.
+ &__item--force-input-border .pimcore_editable_input:not(.empty) {
+ outline: rgb(186, 186, 186) dashed 1px;
+ }
+
+ &__label {
+ display: flex;
+ align-items: center;
+ }
+
+ &__label-text {
+ margin-right: variables.$spacing--5;
+ }
+
+ .pimcore_tag_select,
+ .pimcore_editable_select {
+ display: block; // Simplyfies the use of labels.
+ }
+
+ &--vertical {
+ flex-direction: column;
+ align-items: flex-start;
+
+ #{$this}__item {
+ margin: variables.$spacing--10 0 0 0;
+
+ &:first-child {
+ margin-top: 0;
+ }
+ }
+ }
+}
+
+[class].p-section {
+ margin-bottom: variables.$spacing--20;
+}
+
+[class].p-areablock {
+ &--horizontal {
+ display: flex;
+
+ .pimcore_area_dropzone {
+ min-width: 20px;
+ min-height: 100%;
+ }
+
+ .pimcore_area_buttons .pimcore_icon_plus_down {
+ transform: rotate(-90deg);
+ }
+
+ .pimcore_area_buttons .pimcore_icon_plus_up {
+ transform: rotate(-90deg);
+ }
+ }
+
+ &--group-indicator {
+ position: relative;
+ padding: 0 0 0 variables.$spacing--20;
+
+ &::before {
+ @include mixins.z-index(front);
+
+ position: absolute;
+ top: 0;
+ bottom: 0;
+ left: 0;
+ content: '';
+ width: 0;
+ opacity: 0.1;
+ border-left: 3px solid $pimcore-gui--color;
+ }
+
+ &:hover::before {
+ opacity: 0.5;
+ }
+ }
+}
+
+.p-renderlet-info {
+ margin: variables.$spacing--10;
+}
+
+.p-monospace {
+ font-family: monospace;
+}
+
+.p-danger-zone {
+ margin: variables.$spacing--30 0;
+ padding: variables.$spacing--10;
+ border: 1px solid variables.$color-status--danger;
+ background: rgba(variables.$color-status--danger, 0.2);
+
+ &__message {
+ color: variables.$color-status--danger;
+ }
+
+ &__content {
+ margin: variables.$spacing--10 0;
+ padding: variables.$spacing--10;
+ background: variables.$color-grayscale--1000;
+ }
+}
+
+.p-message {
+ margin: variables.$spacing--10;
+ padding: variables.$spacing--10;
+ border: 1px dashed variables.$color-grayscale--400;
+ background: variables.$color-grayscale--600;
+ color: variables.$color-grayscale--200;
+ text-align: center;
+
+ &--info {
+ border-color: variables.$color-status--info;
+ background: rgba(variables.$color-status--info, 0.2);
+ color: variables.$color-status--info;
+ }
+
+ &--error {
+ border-color: variables.$color-status--danger;
+ background: rgba(variables.$color-status--danger, 0.2);
+ color: variables.$color-status--danger;
+ }
+}
+
+.p-deprecated-field {
+ margin: variables.$spacing--5 0;
+ padding: 0 variables.$spacing--5 variables.$spacing--5;
+ border: 1px solid variables.$color-status--danger;
+
+ &::before {
+ @include mixins.font-size(variables.$font-size--14);
+
+ display: block;
+ content: 'Deprecated field';
+ margin: 0 (-(variables.$spacing--5) variables.$spacing--5);
+ padding: variables.$spacing--5;
+ background: variables.$color-status--danger;
+ color: variables.$color-grayscale--1000;
+ font-weight: bold;
+ }
+
+ &[data-message]::before {
+ content: attr(data-message);
+ }
+}
diff --git a/src/pimcore/index.ts b/src/pimcore/index.ts
new file mode 100644
index 00000000..fc19e55f
--- /dev/null
+++ b/src/pimcore/index.ts
@@ -0,0 +1 @@
+import './_gui.scss';
diff --git a/src/pimcore/pimcore.scss b/src/pimcore/pimcore.scss
new file mode 100644
index 00000000..a5a5c870
--- /dev/null
+++ b/src/pimcore/pimcore.scss
@@ -0,0 +1 @@
+@use '_gui.scss';
diff --git a/src/setup/scss/themes/theme-01.scss b/src/setup/scss/themes/theme-01.scss
index aaa14c4d..77ae7b44 100644
--- a/src/setup/scss/themes/theme-01.scss
+++ b/src/setup/scss/themes/theme-01.scss
@@ -16,8 +16,9 @@ $theme-01: (
color-gradient-2-0: #2c68a0,
color-gradient-2-1: #75b4e0,
color-status-success: #2fa61c,
- color-status-info: #e2951f,
+ color-status-danger: #e2951f,
color-status-error: #b50f0f,
+ color-status-info: #75b4e0,
);
:root {
diff --git a/src/setup/scss/variables/_color.scss b/src/setup/scss/variables/_color.scss
index 69dc2f5f..e70a1530 100644
--- a/src/setup/scss/variables/_color.scss
+++ b/src/setup/scss/variables/_color.scss
@@ -39,6 +39,8 @@ $color-gradient--2-1--rgb: var(--theme-color-gradient-2-1--rgb);
$color-status--success: var(--theme-color-status-success);
$color-status--success--rgb: var(--theme-color-status-success--rgb);
+$color-status--danger: var(--theme-color-status-danger);
+$color-status--danger--rgb: var(--theme-color-status-danger--rgb);
$color-status--error: var(--theme-color-status-error);
$color-status--error--rgb: var(--theme-color-status-error--rgb);
$color-status--info: var(--theme-color-status-info);
diff --git a/vite.config.ts b/vite.config.ts
index 9ca4468b..58101007 100644
--- a/vite.config.ts
+++ b/vite.config.ts
@@ -1,18 +1,22 @@
-import { defineConfig, splitVendorChunkPlugin, UserConfigExport } from 'vite';
+import { defineConfig, UserConfigExport } from 'vite';
import vue from '@vitejs/plugin-vue';
-import path from 'path';
+import { resolve } from 'path';
import { visualizer } from 'rollup-plugin-visualizer';
import { webpack } from './package.json'; // TODO: rename config property in package.json.
+/**
+ * Notes:
+ * - Style only components are imported by src/setup/components.ts.
+ */
+
export default defineConfig(({ command, mode }) => {
const config: UserConfigExport = {
plugins: [
vue(),
- splitVendorChunkPlugin(),
],
resolve: {
alias: {
- '@': path.resolve(__dirname, 'src/'),
+ '@': resolve(__dirname, 'src/'),
'vue': 'vue/dist/vue.esm-bundler.js', // Was required because inline import of vue.esm-bundler.js resulted in TS issues.
},
},
@@ -22,34 +26,63 @@ export default defineConfig(({ command, mode }) => {
__VUE_I18N_FULL_INSTALL__: true,
__VUE_I18N_LEGACY_API__: false,
},
+ json: {
+ stringify: true,
+ },
};
switch (command) {
case 'build': // @see https://vitejs.dev/config/build-options.html
config.base = webpack.productionPath; // TODO: rename to buildBase.
config.build = {
- outDir: webpack.buildPath, // TODO: rename to 'buildOutDir'
- assetsDir: webpack.outputAssetsFolder, // TODO: rename to 'buildAssetsDir'.
+ outDir: `${webpack.buildPath}`, // TODO: rename to 'buildOutDir'
assetsInlineLimit: 0, // TODO: check if it makes sense to increase this value.
manifest: true,
emptyOutDir: true,
copyPublicDir: true,
+
// TODO: watch?
rollupOptions: {
+ input: {
+ 'app/index': './src/main.ts',
+ 'pimcore/index': './src/pimcore/index.ts',
+ },
output: {
- assetFileNames: (assetInfo) => {
- const extType = assetInfo?.name?.split('.').at(1) || '';
- let assetsPath = 'assets';
+ entryFileNames: '[name].[hash].js',
+ chunkFileNames: `${webpack.outputAssetsFolder}/[name].[hash].js`,
+ assetFileNames(assetInfo) {
+ const fileName = assetInfo?.name || '';
+ const imageExtensions = /\.(png|jpe?g|svg|gif|tiff|bmp|ico)$/i;
+ const styleExtensions = /\.(css|sass|scss)$/i;
+ const fontExtensions = /\.(woff|woff2|eot|ttf|otf)$/i;
+ const scriptExtensions = /\.(vue|js|ts)$/i;
+ let name = '[name]';
+ let assetsPath = webpack.outputAssetsFolder;
- if ((/png|jpe?g|svg|gif|tiff|bmp|ico/i).test(extType)) {
+ if (imageExtensions.test(fileName)) {
assetsPath += '/img';
- } else if ((/css|sass|scss/i).test(extType)) {
+ } else if (styleExtensions.test(fileName)) {
assetsPath += '/css';
- } else if ((/woff|woff2|eot|ttf|otf/i).test(extType)) {
+ } else if (fontExtensions.test(fileName)) {
assetsPath += '/fonts';
+ } else if (scriptExtensions.test(fileName)) {
+ assetsPath = '';
+ }
+
+ switch (fileName) {
+ case 'main.css':
+ name = webpack.appName;
+
+ // no default
}
- return `${assetsPath}/[name][extname]`;
+ return `${assetsPath}/${name}.[hash].[ext]`;
+ },
+ manualChunks(source) { // eslint-disable-line consistent-return -- giving no return value is legit.
+ // Additional tests can be added to create additional splits.
+ if (source.includes('node_modules')) {
+ return 'vendor';
+ }
},
},
},
@@ -68,8 +101,6 @@ export default defineConfig(({ command, mode }) => {
})
);
}
-
- // no default
}
return config;