From 22d7d96ed42388c94bdf8b0af1563d5548a40229 Mon Sep 17 00:00:00 2001 From: Ivan Bochkarev Date: Sat, 29 Aug 2026 08:06:48 +0600 Subject: [PATCH 01/13] build: require VueTools 1.1.3-pl for ModxManagerTheme Manager SPA entries need the VueTools Import Map barrel that exports ModxManagerTheme; beta1 is too old for that contract. --- _build/resolvers/resolver_01_setup.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/_build/resolvers/resolver_01_setup.php b/_build/resolvers/resolver_01_setup.php index c69cbde92..f628bf9f4 100644 --- a/_build/resolvers/resolver_01_setup.php +++ b/_build/resolvers/resolver_01_setup.php @@ -19,7 +19,7 @@ 'service_url' => 'modstore.pro', ], 'VueTools' => [ - 'version' => '1.0.0-beta1', + 'version' => '1.1.3-pl', 'service_url' => 'modstore.pro', ], ]; From 7187b3c0faa44c5cb803b98ecea249e25e3126af Mon Sep 17 00:00:00 2001 From: Ivan Bochkarev Date: Sat, 29 Aug 2026 08:06:49 +0600 Subject: [PATCH 02/13] fix(mgr): bust Vue module cache with asset filemtime Package version alone stays the same across vue-dist rebuilds, so browsers keep stale plugin tabs. Mix filemtime into the v= query when the module maps under assets_path. --- .../minishop3/controllers/manager.class.php | 4 +-- .../controllers/resource_create.class.php | 4 +-- .../controllers/resource_update.class.php | 4 +-- .../controllers/vue_module_cache_bust.inc.php | 31 +++++++++++++++++++ 4 files changed, 37 insertions(+), 6 deletions(-) create mode 100644 core/components/minishop3/controllers/vue_module_cache_bust.inc.php diff --git a/core/components/minishop3/controllers/manager.class.php b/core/components/minishop3/controllers/manager.class.php index 33e36f1ac..6ab29d221 100644 --- a/core/components/minishop3/controllers/manager.class.php +++ b/core/components/minishop3/controllers/manager.class.php @@ -89,8 +89,8 @@ public function addVueModule($src) self::$vueCoreCheckRegistered = true; } - // Add version to URL - $src = $src . '?v=' . $this->ms3->version; + require_once __DIR__ . '/vue_module_cache_bust.inc.php'; + $src = ms3_vue_module_cache_bust_url($this->modx, $src, (string) $this->ms3->version); // Register the module (will be blocked by check if VueCore not installed) $this->modx->regClientStartupHTMLBlock( diff --git a/core/components/minishop3/controllers/resource_create.class.php b/core/components/minishop3/controllers/resource_create.class.php index 85f846302..1c98bdfd4 100644 --- a/core/components/minishop3/controllers/resource_create.class.php +++ b/core/components/minishop3/controllers/resource_create.class.php @@ -65,8 +65,8 @@ public function addVueModule($src) self::$vueCoreCheckRegistered = true; } - // Add version to URL - $src = $src . '?v=' . $this->ms3->version; + require_once __DIR__ . '/vue_module_cache_bust.inc.php'; + $src = ms3_vue_module_cache_bust_url($this->modx, $src, (string) $this->ms3->version); // Register the module (will be blocked by check if VueCore not installed) $this->modx->regClientStartupHTMLBlock( diff --git a/core/components/minishop3/controllers/resource_update.class.php b/core/components/minishop3/controllers/resource_update.class.php index 2e81d6782..80b8341b5 100644 --- a/core/components/minishop3/controllers/resource_update.class.php +++ b/core/components/minishop3/controllers/resource_update.class.php @@ -69,8 +69,8 @@ public function addVueModule($src) self::$vueCoreCheckRegistered = true; } - // Add version to URL - $src = $src . '?v=' . $this->ms3->version; + require_once __DIR__ . '/vue_module_cache_bust.inc.php'; + $src = ms3_vue_module_cache_bust_url($this->modx, $src, (string) $this->ms3->version); // Register the module (will be blocked by check if VueCore not installed) $this->modx->regClientStartupHTMLBlock( diff --git a/core/components/minishop3/controllers/vue_module_cache_bust.inc.php b/core/components/minishop3/controllers/vue_module_cache_bust.inc.php new file mode 100644 index 000000000..a564c6a1d --- /dev/null +++ b/core/components/minishop3/controllers/vue_module_cache_bust.inc.php @@ -0,0 +1,31 @@ +getOption('assets_url'), '/'); + $assetsPath = rtrim((string) $modx->getOption('assets_path'), '/'); + $fsPath = null; + if ($assetsUrl !== '' && str_starts_with($path, $assetsUrl)) { + $fsPath = $assetsPath . substr($path, strlen($assetsUrl)); + } elseif (defined('MODX_ASSETS_PATH') && str_starts_with($path, '/assets/')) { + $fsPath = rtrim(MODX_ASSETS_PATH, '/') . substr($path, strlen('/assets')); + } + if (is_string($fsPath) && is_file($fsPath)) { + $version .= '.' . filemtime($fsPath); + } + } + + $separator = str_contains($src, '?') ? '&' : '?'; + + return $src . $separator . 'v=' . rawurlencode($version); +} From 536be35a0330f7b365420e6c6ade170b6cbd59e7 Mon Sep 17 00:00:00 2001 From: Ivan Bochkarev Date: Sat, 29 Aug 2026 08:06:50 +0600 Subject: [PATCH 03/13] feat(vue): adopt ModxManagerTheme and ban primevue subpaths Wire entries to the VueTools theme barrel, drop runtime form style injection, align MS3 tokens with theme primary, and reject primevue/* imports that duplicate Theme and break --p-* tokens. --- assets/components/minishop3/css/mgr/main.css | 6 +- vueManager/src/entries/api-test.js | 12 +- vueManager/src/entries/category-options.js | 13 +- vueManager/src/entries/category-products.js | 12 +- vueManager/src/entries/customers.js | 12 +- vueManager/src/entries/help.js | 10 +- vueManager/src/entries/notifications.js | 12 +- vueManager/src/entries/order.js | 12 +- vueManager/src/entries/orders.js | 12 +- vueManager/src/entries/product-tabs.js | 12 +- vueManager/src/entries/settings.js | 12 +- vueManager/src/entries/utilities.js | 12 +- vueManager/src/main.js | 14 +- vueManager/src/scss/_variables.scss | 6 +- vueManager/src/scss/primevue.scss | 385 ++++++++++++++++++- vueManager/src/utils/formStyles.js | 79 +--- vueManager/vite.config.js | 21 +- 17 files changed, 425 insertions(+), 217 deletions(-) diff --git a/assets/components/minishop3/css/mgr/main.css b/assets/components/minishop3/css/mgr/main.css index 1aa2a2c09..501821e6e 100644 --- a/assets/components/minishop3/css/mgr/main.css +++ b/assets/components/minishop3/css/mgr/main.css @@ -494,6 +494,8 @@ a.x-menu-item .x-menu-item-text .icon { box-sizing: border-box !important; } -.p-select-filter { - width: 90% !important; +/* Select filter must fill IconField (was 90% — left a dead strip and misaligned search icon). */ +.p-select-overlay .p-select-filter, +.p-multiselect-overlay .p-multiselect-filter { + width: 100% !important; } diff --git a/vueManager/src/entries/api-test.js b/vueManager/src/entries/api-test.js index 123095537..0b990ca00 100644 --- a/vueManager/src/entries/api-test.js +++ b/vueManager/src/entries/api-test.js @@ -7,12 +7,9 @@ import '../scss/primevue.scss' import 'primeicons/primeicons.css' -import Aura from '@primeuix/themes/aura' import { getPrimeVueLocale } from '@vuetools/usePrimeVueLocale' import { createPinia } from 'pinia' -import PrimeVue from 'primevue/config' -import ConfirmationService from 'primevue/confirmationservice' -import ToastService from 'primevue/toastservice' +import { ConfirmationService, ModxManagerTheme, PrimeVue, ToastService } from 'primevue' import { createApp } from 'vue' import VueApiTest from '../components/ApiTest.vue' @@ -28,12 +25,7 @@ function createVueApp() { app.use(pinia) app.use(PrimeVue, { - theme: { - preset: Aura, - options: { - darkModeSelector: 'none', - }, - }, + theme: ModxManagerTheme, locale: getPrimeVueLocale(), }) diff --git a/vueManager/src/entries/category-options.js b/vueManager/src/entries/category-options.js index 3806632f2..f84c60e14 100644 --- a/vueManager/src/entries/category-options.js +++ b/vueManager/src/entries/category-options.js @@ -5,11 +5,8 @@ * ms3-grid-category-option + ms3-window-option-add + ms3-window-copy-category. */ -import Aura from '@primeuix/themes/aura' import { getPrimeVueLocale } from '@vuetools/usePrimeVueLocale' -import PrimeVue from 'primevue/config' -import ConfirmationService from 'primevue/confirmationservice' -import ToastService from 'primevue/toastservice' +import { ConfirmationService, ModxManagerTheme, PrimeVue, ToastService } from 'primevue' import { createApp } from 'vue' import CategoryOptionsTab from '../components/CategoryOptionsTab.vue' @@ -34,13 +31,7 @@ function mountApp() { app = createApp(CategoryOptionsTab, { categoryId }) app.use(PrimeVue, { - theme: { - preset: Aura, - options: { - darkModeSelector: '.dark-mode', - cssLayer: false, - }, - }, + theme: ModxManagerTheme, locale: getPrimeVueLocale(), }) app.use(ToastService) diff --git a/vueManager/src/entries/category-products.js b/vueManager/src/entries/category-products.js index 3691df92e..7cc6ce7ae 100644 --- a/vueManager/src/entries/category-products.js +++ b/vueManager/src/entries/category-products.js @@ -8,12 +8,9 @@ import '../scss/primevue.scss' import 'primeicons/primeicons.css' -import Aura from '@primeuix/themes/aura' import { getPrimeVueLocale } from '@vuetools/usePrimeVueLocale' import { createPinia } from 'pinia' -import PrimeVue from 'primevue/config' -import ConfirmationService from 'primevue/confirmationservice' -import ToastService from 'primevue/toastservice' +import { ConfirmationService, ModxManagerTheme, PrimeVue, ToastService } from 'primevue' import { createApp } from 'vue' import CategoryProductsGrid from '../components/CategoryProductsGrid.vue' @@ -35,12 +32,7 @@ function createVueApp(categoryId) { app.use(pinia) app.use(PrimeVue, { - theme: { - preset: Aura, - options: { - darkModeSelector: 'none', - }, - }, + theme: ModxManagerTheme, locale: getPrimeVueLocale(), }) diff --git a/vueManager/src/entries/customers.js b/vueManager/src/entries/customers.js index a8ba079b0..397fbe000 100644 --- a/vueManager/src/entries/customers.js +++ b/vueManager/src/entries/customers.js @@ -7,12 +7,9 @@ import '../scss/primevue.scss' import 'primeicons/primeicons.css' -import Aura from '@primeuix/themes/aura' import { getPrimeVueLocale } from '@vuetools/usePrimeVueLocale' import { createPinia } from 'pinia' -import PrimeVue from 'primevue/config' -import ConfirmationService from 'primevue/confirmationservice' -import ToastService from 'primevue/toastservice' +import { ConfirmationService, ModxManagerTheme, PrimeVue, ToastService } from 'primevue' import { createApp } from 'vue' import CustomersGrid from '../components/CustomersGrid.vue' @@ -28,12 +25,7 @@ function createVueApp() { app.use(pinia) app.use(PrimeVue, { - theme: { - preset: Aura, - options: { - darkModeSelector: 'none', - }, - }, + theme: ModxManagerTheme, locale: getPrimeVueLocale(), }) diff --git a/vueManager/src/entries/help.js b/vueManager/src/entries/help.js index 9eec73fd1..2cc4b2ac6 100644 --- a/vueManager/src/entries/help.js +++ b/vueManager/src/entries/help.js @@ -5,8 +5,7 @@ import '../scss/primevue.scss' import 'primeicons/primeicons.css' -import Aura from '@primeuix/themes/aura' -import PrimeVue from 'primevue/config' +import { ModxManagerTheme, PrimeVue } from 'primevue' import { createApp } from 'vue' import HelpPage from '../components/HelpPage.vue' @@ -19,12 +18,7 @@ function createVueApp() { const app = createApp(HelpPage) app.use(PrimeVue, { - theme: { - preset: Aura, - options: { - darkModeSelector: 'none', - }, - }, + theme: ModxManagerTheme, }) return app diff --git a/vueManager/src/entries/notifications.js b/vueManager/src/entries/notifications.js index 44c0e198a..0b9c0832b 100644 --- a/vueManager/src/entries/notifications.js +++ b/vueManager/src/entries/notifications.js @@ -7,12 +7,9 @@ import '../scss/primevue.scss' import 'primeicons/primeicons.css' -import Aura from '@primeuix/themes/aura' import { getPrimeVueLocale } from '@vuetools/usePrimeVueLocale' import { createPinia } from 'pinia' -import PrimeVue from 'primevue/config' -import ConfirmationService from 'primevue/confirmationservice' -import ToastService from 'primevue/toastservice' +import { ConfirmationService, ModxManagerTheme, PrimeVue, ToastService } from 'primevue' import { createApp } from 'vue' import NotificationsGrid from '../components/NotificationsGrid.vue' @@ -28,12 +25,7 @@ function createVueApp() { app.use(pinia) app.use(PrimeVue, { - theme: { - preset: Aura, - options: { - darkModeSelector: 'none', - }, - }, + theme: ModxManagerTheme, locale: getPrimeVueLocale(), }) diff --git a/vueManager/src/entries/order.js b/vueManager/src/entries/order.js index d2dbcf045..28dfe70f6 100644 --- a/vueManager/src/entries/order.js +++ b/vueManager/src/entries/order.js @@ -5,12 +5,9 @@ import '../scss/primevue.scss' import 'primeicons/primeicons.css' -import Aura from '@primeuix/themes/aura' import { getPrimeVueLocale } from '@vuetools/usePrimeVueLocale' import { createPinia } from 'pinia' -import PrimeVue from 'primevue/config' -import ConfirmationService from 'primevue/confirmationservice' -import ToastService from 'primevue/toastservice' +import { ConfirmationService, ModxManagerTheme, PrimeVue, ToastService } from 'primevue' import { createApp } from 'vue' import OrderView from '../components/OrderView.vue' @@ -136,12 +133,7 @@ function createVueApp() { app.use(pinia) app.use(PrimeVue, { - theme: { - preset: Aura, - options: { - darkModeSelector: 'none', - }, - }, + theme: ModxManagerTheme, locale: getPrimeVueLocale(), }) diff --git a/vueManager/src/entries/orders.js b/vueManager/src/entries/orders.js index 3a19ada0e..0114f6d15 100644 --- a/vueManager/src/entries/orders.js +++ b/vueManager/src/entries/orders.js @@ -5,12 +5,9 @@ import '../scss/primevue.scss' import 'primeicons/primeicons.css' -import Aura from '@primeuix/themes/aura' import { getPrimeVueLocale } from '@vuetools/usePrimeVueLocale' import { createPinia } from 'pinia' -import PrimeVue from 'primevue/config' -import ConfirmationService from 'primevue/confirmationservice' -import ToastService from 'primevue/toastservice' +import { ConfirmationService, ModxManagerTheme, PrimeVue, ToastService } from 'primevue' import { createApp } from 'vue' import OrdersGrid from '../components/OrdersGrid.vue' @@ -25,12 +22,7 @@ function createVueApp() { app.use(pinia) app.use(PrimeVue, { - theme: { - preset: Aura, - options: { - darkModeSelector: 'none', - }, - }, + theme: ModxManagerTheme, locale: getPrimeVueLocale(), }) diff --git a/vueManager/src/entries/product-tabs.js b/vueManager/src/entries/product-tabs.js index 259dcd364..b7a5b9dfa 100644 --- a/vueManager/src/entries/product-tabs.js +++ b/vueManager/src/entries/product-tabs.js @@ -9,12 +9,9 @@ import '../scss/primevue.scss' import 'primeicons/primeicons.css' -import Aura from '@primeuix/themes/aura' import { getPrimeVueLocale } from '@vuetools/usePrimeVueLocale' import { createPinia } from 'pinia' -import PrimeVue from 'primevue/config' -import ConfirmationService from 'primevue/confirmationservice' -import ToastService from 'primevue/toastservice' +import { ConfirmationService, ModxManagerTheme, PrimeVue, ToastService } from 'primevue' import { createApp } from 'vue' import ProductTabs from '../components/product/ProductTabs.vue' @@ -130,12 +127,7 @@ function createVueApp(props) { app.use(pinia) app.use(PrimeVue, { - theme: { - preset: Aura, - options: { - darkModeSelector: 'none', - }, - }, + theme: ModxManagerTheme, locale: getPrimeVueLocale(), }) diff --git a/vueManager/src/entries/settings.js b/vueManager/src/entries/settings.js index 5a6f58ce0..acbd43e05 100644 --- a/vueManager/src/entries/settings.js +++ b/vueManager/src/entries/settings.js @@ -5,11 +5,8 @@ import '../scss/primevue.scss' import 'primeicons/primeicons.css' -import Aura from '@primeuix/themes/aura' import { getPrimeVueLocale } from '@vuetools/usePrimeVueLocale' -import PrimeVue from 'primevue/config' -import ConfirmationService from 'primevue/confirmationservice' -import ToastService from 'primevue/toastservice' +import { ConfirmationService, ModxManagerTheme, PrimeVue, ToastService } from 'primevue' import { createApp } from 'vue' import SettingsPage from '../components/SettingsPage.vue' @@ -19,12 +16,7 @@ function createVueApp() { const app = createApp(SettingsPage) app.use(PrimeVue, { - theme: { - preset: Aura, - options: { - darkModeSelector: 'none', - }, - }, + theme: ModxManagerTheme, locale: getPrimeVueLocale(), }) app.use(ConfirmationService) diff --git a/vueManager/src/entries/utilities.js b/vueManager/src/entries/utilities.js index db8bccda4..abed4d4b9 100644 --- a/vueManager/src/entries/utilities.js +++ b/vueManager/src/entries/utilities.js @@ -5,12 +5,9 @@ import '../scss/primevue.scss' import 'primeicons/primeicons.css' -import Aura from '@primeuix/themes/aura' import { getPrimeVueLocale } from '@vuetools/usePrimeVueLocale' import { createPinia } from 'pinia' -import PrimeVue from 'primevue/config' -import ConfirmationService from 'primevue/confirmationservice' -import ToastService from 'primevue/toastservice' +import { ConfirmationService, ModxManagerTheme, PrimeVue, ToastService } from 'primevue' import { createApp } from 'vue' import UtilitiesPage from '../components/UtilitiesPage.vue' @@ -25,12 +22,7 @@ export function init(selector = '#ms3-vue-utilities') { const app = createApp(UtilitiesPage) app.use(createPinia()) app.use(PrimeVue, { - theme: { - preset: Aura, - options: { - darkModeSelector: 'none', - }, - }, + theme: ModxManagerTheme, locale: getPrimeVueLocale(), }) app.use(ConfirmationService) diff --git a/vueManager/src/main.js b/vueManager/src/main.js index 6d82daf90..64cc064ef 100644 --- a/vueManager/src/main.js +++ b/vueManager/src/main.js @@ -1,12 +1,9 @@ import './scss/primevue.scss' import 'primeicons/primeicons.css' -import Aura from '@primeuix/themes/aura' import { getPrimeVueLocale } from '@vuetools/usePrimeVueLocale' import { createPinia } from 'pinia' -import PrimeVue from 'primevue/config' -import ConfirmationService from 'primevue/confirmationservice' -import ToastService from 'primevue/toastservice' +import { ConfirmationService, ModxManagerTheme, PrimeVue, ToastService } from 'primevue' import { createApp } from 'vue' /** @@ -40,14 +37,7 @@ export function createVueApp(rootComponent) { app.use(pinia) app.use(PrimeVue, { - theme: { - preset: Aura, - options: { - darkModeSelector: 'none', - cssLayer: false, - prefix: 'p', - }, - }, + theme: ModxManagerTheme, locale: getPrimeVueLocale(), pt: { directives: { diff --git a/vueManager/src/scss/_variables.scss b/vueManager/src/scss/_variables.scss index ef997e9cf..3419359c5 100644 --- a/vueManager/src/scss/_variables.scss +++ b/vueManager/src/scss/_variables.scss @@ -64,9 +64,9 @@ --ms3-text-info-result: #0c5460; --ms3-text-error-result: #721c24; - /* Colors — accents */ - --ms3-accent-primary: #3b82f6; - --ms3-accent-focus: rgb(59 130 246 / 30%); + /* Colors — accents (alias ModxManagerTheme primary, not Tailwind blue) */ + --ms3-accent-primary: var(--p-primary-color, #234368); + --ms3-accent-focus: color-mix(in srgb, var(--p-primary-color, #234368) 30%, transparent); --ms3-accent-teal: #32ab9a; --ms3-accent-green: #4caf50; --ms3-border-upload: #ddd; diff --git a/vueManager/src/scss/primevue.scss b/vueManager/src/scss/primevue.scss index 39441f63b..33eb5f015 100644 --- a/vueManager/src/scss/primevue.scss +++ b/vueManager/src/scss/primevue.scss @@ -73,7 +73,7 @@ .vueApp .mt-3, .p-dialog .mt-3 { - margin-top: 0.75rem; + margin-top: var(--p-modx-space-panel, 15px); } .vueApp .mt-4, @@ -108,12 +108,12 @@ .vueApp .mb-2, .p-dialog .mb-2 { - margin-bottom: 0.5rem; + margin-bottom: 0.25rem; /* 4px — label→control (MODX form rhythm) */ } .vueApp .mb-3, .p-dialog .mb-3 { - margin-bottom: 0.75rem; + margin-bottom: var(--p-modx-space-panel, 15px); } .vueApp .mb-4, @@ -334,21 +334,92 @@ color: var(--ms3-text-muted); } -/* Form fields in dialogs (for ModelFieldsGrid and similar) */ +/* Form fields in dialogs — MODX: 15px between fields, 4px label→control. + * Do not stack per-field padding on top of grid gap (was ~32px visually). */ +.p-dialog .edit-field-form, +.p-dialog .edit-form { + padding: 0; +} + +.p-dialog .form-grid { + display: flex; + flex-wrap: wrap; + gap: var(--p-modx-space-panel, 15px); + margin: 0; +} + .p-dialog .field { - margin-bottom: 1rem; + margin-bottom: 0; +} + +.p-dialog .edit-form > .field:not(:last-child), +.p-dialog .edit-form > .field.mb-3:not(:last-child) { + margin-bottom: var(--p-modx-space-panel, 15px); +} + +.p-dialog .edit-field-form .field, +.p-dialog .edit-form .field { + display: flex; + flex-direction: column; + gap: 0.25rem; + padding: 0; + box-sizing: border-box; } .p-dialog .field label { display: block; - margin-bottom: 0.5rem; + margin-bottom: 0.25rem; font-weight: 500; + font-size: 0.875rem; } -.p-dialog .edit-form .field label { +.p-dialog .edit-form .field label, +.p-dialog .edit-field-form .field label { display: block; - margin-bottom: 0.5rem; + margin-bottom: 0; font-weight: 500; + font-size: 0.875rem; + color: var(--ms3-text-primary, #333); +} + +.p-dialog .edit-field-form .field small, +.p-dialog .edit-form .field small { + color: var(--ms3-text-muted, #64748b); + font-size: 0.75rem; + margin-top: 0; + line-height: 1.35; +} + +.p-dialog .edit-field-form .w-full { + width: 100%; +} + +.p-dialog .col-6 { + flex: 0 0 calc(50% - (var(--p-modx-space-panel, 15px) / 2)); + max-width: calc(50% - (var(--p-modx-space-panel, 15px) / 2)); +} + +.p-dialog .col-12 { + flex: 0 0 100%; + max-width: 100%; +} + +.p-dialog .edit-field-form .checkbox-wrapper { + display: flex; + gap: 0.5rem; + align-items: center; +} + +.p-dialog .edit-field-form .checkbox-label { + margin: 0; + cursor: pointer; + user-select: none; +} + +.p-dialog .edit-form .ms3-dialog-grid { + display: grid; + grid-template-columns: 1fr 1fr; + gap: var(--p-modx-space-panel, 15px); } .p-dialog .grid { @@ -365,16 +436,302 @@ /* ============================================ */ +/* Also under .vueApp — dialogs with append-to="self" stay inside the SPA root. */ +.vueApp .edit-field-form, +.vueApp .edit-form { + padding: 0; +} + +.vueApp .form-grid { + display: flex; + flex-wrap: wrap; + gap: var(--p-modx-space-panel, 15px); + margin: 0; +} + +.vueApp .edit-field-form .field, +.vueApp .edit-form .field { + display: flex; + flex-direction: column; + gap: 0.25rem; + padding: 0; + margin-bottom: 0; + box-sizing: border-box; +} + +.vueApp .edit-form > .field:not(:last-child) { + margin-bottom: var(--p-modx-space-panel, 15px); +} + +.vueApp .edit-field-form .field label, +.vueApp .edit-form .field label { + display: block; + margin-bottom: 0; + font-weight: 500; + font-size: 0.875rem; + color: var(--ms3-text-primary, #333); +} + +.vueApp .edit-field-form .field small, +.vueApp .edit-form .field small { + color: var(--ms3-text-muted, #64748b); + font-size: 0.75rem; + margin-top: 0; + line-height: 1.35; +} + +.vueApp .col-6 { + flex: 0 0 calc(50% - (var(--p-modx-space-panel, 15px) / 2)); + max-width: calc(50% - (var(--p-modx-space-panel, 15px) / 2)); +} + +.vueApp .col-12 { + flex: 0 0 100%; + max-width: 100%; +} + +.vueApp .edit-form .ms3-dialog-grid { + display: grid; + grid-template-columns: 1fr 1fr; + gap: var(--p-modx-space-panel, 15px); +} + +/* + * PageBuilder (resource/update) defines Bootstrap-like `.vueApp .col-N { width:% }`. + * MS3 product/settings grids use the same class names for 12-col CSS Grid spans — + * reset width/flex so cells fill their tracks (15px gap only). + */ +.vueApp[id^='ms3-'] .fields-grid > .field-item[class*='col-'], +.vueApp[id^='ms3-'] .fields-grid > [class*='col-'] { + width: 100% !important; + max-width: none !important; + flex: none !important; + padding: 0 !important; + min-width: 0; +} + +.vueApp[id^='ms3-'] .fields-grid > .field-item.field-checkbox-row { + width: 100% !important; + max-width: none !important; + flex: none !important; + padding: 0 !important; + grid-column: span 12; +} + +.vueApp[id^='ms3-'] .fields-grid > .field-item.field-checkbox { + width: max-content !important; + max-width: none !important; + flex: none !important; + padding: 0 !important; + grid-column: auto; + justify-self: start; +} + +/* Config panels (sections / fields) — same chrome as Modx Panel */ +.vueApp .ms3-config-panel.p-panel { + margin-bottom: var(--p-modx-space-panel, 15px); +} + +.vueApp .ms3-config-panel .p-panel-header { + display: flex; + align-items: center; + gap: 0.5rem; + min-height: calc( + var(--p-modx-control-height, 2.25rem) + (2 * var(--p-modx-space-panel, 15px)) + ); + padding: var(--p-modx-space-panel, 15px); + background: transparent; +} + +/* + * Section titles must outrank field labels (14px). Default inherits mgr 13px body + * and reads smaller than labels — “маловат” vs Modx hierarchy. + */ +.vueApp .ms3-config-panel .p-panel-title { + flex: 1 1 auto; + margin: 0; + font-size: 1.125rem; + font-weight: var(--p-panel-title-font-weight, 700); + line-height: var(--p-modx-control-height, 2.25rem); + color: var(--p-panel-header-color, #2a2a2a); +} + +.vueApp .ms3-config-panel .p-panel-content { + padding: var(--p-modx-space-panel, 15px); +} + +.vueApp .ms3-config-panel .p-panel-icons, +.vueApp .ms3-config-panel .p-panel-header-actions { + display: flex; + flex-wrap: wrap; + align-items: center; + gap: 0.5rem; + flex-shrink: 0; +} + +.vueApp .row-actions-cell { + vertical-align: middle; + white-space: nowrap; +} + +.vueApp .row-actions { + display: inline-flex; + flex-direction: row; + flex-wrap: nowrap; + align-items: center; + gap: 0.25rem; +} + +/* Shared grid action strip (ActionsColumn CSS chunk may not be linked on every page). */ +.vueApp .actions-column.row-actions, +.vueApp .actions-column { + display: inline-flex !important; + flex-direction: row !important; + flex-wrap: nowrap !important; + align-items: center; + gap: 0.25rem; + white-space: nowrap; +} + +.vueApp .actions-column .p-button { + flex-shrink: 0; +} + /* Tab Description */ /* ============================================ */ +/* Plain muted lead — no boxed callout (matches MODX field hints). */ .vueApp .tab-description { - margin: 0 0 1rem; - padding: 0.75rem 1rem; - background-color: var(--ms3-bg-muted); - border-left: var(--ms3-border-width-accent) solid var(--ms3-accent-primary); - color: var(--ms3-text-info); + margin: 0 0 var(--p-modx-space-panel, 15px); + padding: 0; + background: none; + border: none; + color: var(--ms3-text-muted, #64748b); font-size: 0.875rem; line-height: 1.5; - border-radius: 0 var(--ms3-radius-sm) var(--ms3-radius-sm) 0; +} + +/* + * PageBuilder paints focus borders green on every `.vueApp .p-select` and + * restyles `.p-select-dropdown`. Its CSS loads after MS3 on resource/update. + * Use `.vueApp[id^=ms3-]` (same node) so postcss-prefix leave it unprefixed + * and the mount root matches. + */ +.vueApp[id^='ms3-'] .p-select:not(.p-disabled).p-focus, +.vueApp[id^='ms3-'] .p-multiselect:not(.p-disabled).p-focus, +.vueApp[id^='ms3-'] .p-autocomplete.p-focus .p-autocomplete-input { + border-color: var(--p-form-field-focus-border-color, var(--p-primary-color, #234368)) !important; +} + +.vueApp[id^='ms3-'] .p-select-dropdown { + width: var(--p-select-dropdown-width, 1.875rem) !important; + min-width: var(--p-select-dropdown-width, 1.875rem) !important; + color: var(--p-select-dropdown-color, var(--p-form-field-icon-color, #686868)); + background: transparent; + border-inline-start: 1px solid var(--p-select-border-color, #e4e4e4); +} + +.vueApp[id^='ms3-'] .p-select { + border-color: var(--p-select-border-color, #e4e4e4); +} + +/* + * InputChips / MultiSelect chips: Modx highlight tokens (not default/Tailwind blue). + * OptionsChips uses the same highlight pair in component CSS. + */ +.vueApp[id^='ms3-'] .p-inputchips-chip, +.vueApp[id^='ms3-'] .p-multiselect-chip, +.vueApp[id^='ms3-'] .p-autocomplete-chip, +.p-dialog .p-inputchips-chip, +.p-dialog .p-multiselect-chip, +.p-dialog .p-autocomplete-chip { + background: var(--p-highlight-background, #d6e7f8); + color: var(--p-highlight-color, #1b3451); + border-radius: var(--p-autocomplete-chip-border-radius, 2px); +} + +.vueApp[id^='ms3-'] .p-inputchips-chip-icon, +.vueApp[id^='ms3-'] .p-multiselect-chip-icon, +.vueApp[id^='ms3-'] .p-autocomplete-chip-icon, +.p-dialog .p-inputchips-chip-icon, +.p-dialog .p-multiselect-chip-icon, +.p-dialog .p-autocomplete-chip-icon { + color: inherit; +} + +/* + * MODX manager (`index.css`): `.ext-webkit *:focus-visible { outline: auto !important }`. + * Ext fields are exempt via `.x-form-focus`; Vue controls are not, so the UA dark ring + * sits on top of ModxManagerTheme focus (border + ring tokens). + * Selectors must start with `.vueApp[id^=ms3-]` or `.p-` so postcss-prefix leaves them intact. + */ +.vueApp[id^='ms3-'] .p-inputtext:focus-visible, +.vueApp[id^='ms3-'] .p-textarea:focus-visible, +.vueApp[id^='ms3-'] .p-select:focus-visible, +.vueApp[id^='ms3-'] .p-multiselect:focus-visible, +.vueApp[id^='ms3-'] .p-autocomplete-input:focus-visible, +.vueApp[id^='ms3-'] .p-inputchips:focus-visible, +.vueApp[id^='ms3-'] .p-datepicker-input:focus-visible, +.p-select-overlay .p-inputtext:focus-visible, +.p-multiselect-overlay .p-inputtext:focus-visible, +.p-datepicker-panel .p-inputtext:focus-visible, +.p-dialog .p-inputtext:focus-visible, +.p-dialog .p-textarea:focus-visible, +.p-dialog .p-select:focus-visible, +.p-dialog .p-multiselect:focus-visible, +.p-dialog .p-autocomplete-input:focus-visible { + outline: var(--p-form-field-focus-ring-width, 2px) + var(--p-form-field-focus-ring-style, solid) + var( + --p-form-field-focus-ring-color, + var(--p-primary-color, #234368) + ) !important; + outline-offset: var(--p-form-field-focus-ring-offset, -1px) !important; +} + +/* Clear sits before the chevron; keep label clear of both icons. */ +.vueApp[id^='ms3-'] .p-select.p-inputwrapper-filled .p-select-label { + padding-inline-end: calc( + var(--p-select-dropdown-width, 1.875rem) + var(--p-icon-size, 0.875rem) + 0.75rem + ); +} + +.vueApp[id^='ms3-'] .p-select-clear-icon { + inset-inline-end: var(--p-select-dropdown-width, 1.875rem); + margin-inline-end: 0.25rem; +} + +/* + * Overlay filter (Select/MultiSelect): Modx control height + full-width IconField. + * main.css used to force 90% width; overlays teleport outside .vueApp. + */ +.p-select-overlay .p-select-header .p-iconfield, +.p-multiselect-overlay .p-multiselect-header .p-iconfield { + display: flex; + align-items: center; + width: 100%; +} + +.p-select-overlay .p-select-filter, +.p-multiselect-overlay .p-multiselect-filter { + width: 100% !important; + height: var(--p-modx-control-height, 2.25rem) !important; + min-height: var(--p-modx-control-height, 2.25rem) !important; + /* Overlays teleport outside .vueApp; theme/PageBuilder may leave content-box. */ + box-sizing: border-box !important; +} + +.p-select-overlay .p-select-header .p-iconfield, +.p-multiselect-overlay .p-multiselect-header .p-iconfield { + min-height: var(--p-modx-control-height, 2.25rem); + height: auto; +} + +/* PageBuilder forces width:100% on all .vueApp .p-select — keep paginator RPP compact. */ +.vueApp[id^='ms3-'] .p-paginator .p-select, +.vueApp[id^='ms3-'] .rows-per-page-select { + width: auto !important; + min-width: 5rem !important; + max-width: 6.5rem !important; + flex: 0 0 auto; } diff --git a/vueManager/src/utils/formStyles.js b/vueManager/src/utils/formStyles.js index 55a67b1ce..937810b4a 100644 --- a/vueManager/src/utils/formStyles.js +++ b/vueManager/src/utils/formStyles.js @@ -1,79 +1,8 @@ /** - * Runtime injection переопределений для контролов PrimeVue (font-size, height и т.п.). - * - * Вызывать после app.mount(). PrimeVue с @primeuix/themes встраивает тему в runtime - * после загрузки бандла, поэтому стили из SCSS (даже с !important) загружаются раньше - * и перезаписываются. Runtime injection обеспечивает применение наших правил последними. + * @deprecated Control density (Normal = 36px) and form font sizes live in + * VueTools `ModxManagerTheme` (`modx.control.height`, preset CSS). Kept as a + * no-op so existing entry `injectFormStylesOverride()` calls stay harmless. */ -const STYLE_ID = 'ms3-form-styles-override' - export function injectFormStylesOverride() { - if (document.getElementById(STYLE_ID)) return - - const css = ` -.vueApp .p-dropdown .p-dropdown-label, -.vueApp .p-dropdown .p-inputtext, -.vueApp .p-select [data-pc-section="label"], -.vueApp .p-select .p-select-label, -.vueApp .p-select .p-inputtext, -.vueApp .p-multiselect [data-pc-section="label"], -.vueApp .p-multiselect .p-multiselect-label, -.vueApp .p-inputtext, -.vueApp .p-inputnumber .p-inputnumber-input, -.vueApp .p-inputnumber-input, -.vueApp .p-autocomplete .p-autocomplete-input, -.vueApp .p-autocomplete-input, -.vueApp .p-textarea, -.vueApp .p-cascadeselect [data-pc-section="label"], -.vueApp .p-treeselect [data-pc-section="label"], -.p-dialog .p-dropdown .p-dropdown-label, -.p-dialog .p-dropdown .p-inputtext, -.p-dialog .p-select [data-pc-section="label"], -.p-dialog .p-select .p-select-label, -.p-dialog .p-select .p-inputtext, -.p-dialog .p-multiselect [data-pc-section="label"], -.p-dialog .p-multiselect .p-multiselect-label, -.p-dialog .p-inputtext, -.p-dialog .p-inputnumber .p-inputnumber-input, -.p-dialog .p-inputnumber-input, -.p-dialog .p-autocomplete .p-autocomplete-input, -.p-dialog .p-autocomplete-input, -.p-dialog .p-textarea, -.p-dialog .p-cascadeselect [data-pc-section="label"], -.p-dialog .p-treeselect [data-pc-section="label"] { - font-size: 0.875rem !important; -} - -/* Компактные бейджи (Tag, status-badge, статусы) */ -.vueApp .p-tag, -.vueApp .status-badge, -.p-dialog .p-tag, -.p-dialog .status-badge { - padding: 0.125rem 0.5rem !important; - font-size: 0.75rem !important; -} - -/* Единая высота всех однострочных контролов */ -.vueApp .p-inputtext, -.vueApp .p-select, -.vueApp .p-dropdown, -.vueApp .p-inputnumber .p-inputnumber-input, -.vueApp .p-autocomplete .p-autocomplete-input, -.vueApp .p-datepicker .p-datepicker-input, -.p-dialog .p-inputtext, -.p-dialog .p-select, -.p-dialog .p-dropdown, -.p-dialog .p-inputnumber .p-inputnumber-input, -.p-dialog .p-autocomplete .p-autocomplete-input, -.p-dialog .p-datepicker .p-datepicker-input { - height: 2.25rem !important; - min-height: 2.25rem !important; - box-sizing: border-box !important; -} -` - - const el = document.createElement('style') - el.id = STYLE_ID - el.textContent = css - document.head.appendChild(el) + // no-op — see VueTools src/theme/modx/ } diff --git a/vueManager/vite.config.js b/vueManager/vite.config.js index f2f7fd584..d41acfdf0 100644 --- a/vueManager/vite.config.js +++ b/vueManager/vite.config.js @@ -101,6 +101,23 @@ export default defineConfig(({ command }) => { '@vuetools/usePrimeVueLocale', ] + // Subpath imports (primevue/button) resolve to node_modules and ship a second + // @primeuix Theme copy. Theme tokens then land on the Import Map instance while + // components read the bundled one → missing --p-button-* / broken chrome. + // Always: import { Button, DataTable } from 'primevue' + const rejectPrimeVueSubpaths = { + name: 'ms3-reject-primevue-subpaths', + resolveId(id) { + if (id === 'primevue' || !id.startsWith('primevue/')) { + return null + } + return this.error( + `Import "${id}" is not allowed. Use a named import from "primevue" ` + + `(VueTools Import Map barrel) so theme and components share one Theme instance.`, + ) + }, + } + if (command === 'serve') { return { build: { @@ -110,7 +127,7 @@ export default defineConfig(({ command }) => { external: [...external, ...vuetoolsComposables], }, }, - plugins: [vue(), vueDevTools()], + plugins: [rejectPrimeVueSubpaths, vue(), vueDevTools()], resolve: { alias: { '@': fileURLToPath(new URL('./src', import.meta.url)), @@ -133,7 +150,7 @@ export default defineConfig(({ command }) => { cssMinify: false, // Отключаем минификацию CSS чтобы сохранить Unicode символы в PrimeIcons minify: 'esbuild', }, - plugins: [vue()], + plugins: [rejectPrimeVueSubpaths, vue()], resolve: { alias: { '@': fileURLToPath(new URL('./src', import.meta.url)), From 8e0d3c3a9a5bda9e9da71f216f989b20d5ae8f9e Mon Sep 17 00:00:00 2001 From: Ivan Bochkarev Date: Sat, 29 Aug 2026 08:07:01 +0600 Subject: [PATCH 04/13] fix(mgr): HTML content_type for products and category options spacing Hide content_type on the product settings tab (force HTML) so friendly URIs do not pick up .css/.js. Tighten category options heading gap and load ActionsColumn CSS with the category Vue modules. --- .../minishop3/js/mgr/category/update.js | 11 +++++-- .../js/mgr/product/product.common.js | 32 +++++++++++++++++++ .../controllers/category/update.class.php | 1 + 3 files changed, 41 insertions(+), 3 deletions(-) diff --git a/assets/components/minishop3/js/mgr/category/update.js b/assets/components/minishop3/js/mgr/category/update.js index 5506d0ace..ef12921d0 100644 --- a/assets/components/minishop3/js/mgr/category/update.js +++ b/assets/components/minishop3/js/mgr/category/update.js @@ -172,12 +172,17 @@ Ext.extend(ms3.panel.UpdateCategory, ms3.panel.Category, { return [{ layout: 'form', items: [items, { - html: String.format('

{0}

', _('ms3_product_options')), - style: 'margin-top: 20px', + html: String.format( + '

{0}

', + _('ms3_product_options') + ), + // Theme panel step (15px) above the section; heading→toolbar gap via CSS. + style: 'margin-top: 15px', border: false, }, { xtype: 'container', - cls: 'main-wrapper', + // Avoid .main-wrapper (15px pad) stacking on h3 margin — doubles title→toolbar gap. + style: 'padding: 0', html: '
', }] }]; diff --git a/assets/components/minishop3/js/mgr/product/product.common.js b/assets/components/minishop3/js/mgr/product/product.common.js index 9801cd171..d4ac3d95a 100644 --- a/assets/components/minishop3/js/mgr/product/product.common.js +++ b/assets/components/minishop3/js/mgr/product/product.common.js @@ -90,6 +90,38 @@ Ext.extend(ms3.panel.Product, MODx.panel.Resource, { }, + /** + * Force HTML content type for products (same idea as categories). + * CSS/JS types append ".css"/".js" to friendly URIs and break preview. + */ + getSettingFields: function (config) { + const originals = MODx.panel.Resource.prototype.getSettingFields.call(this, config) + const htmlType = MODx.config['default_content_type'] || 1 + const walk = function (items) { + if (!Ext.isArray(items)) { + return + } + Ext.each(items, function (field) { + if (!field) { + return + } + if (field.name === 'content_type' || field.id === 'modx-resource-content-type') { + field.xtype = 'hidden' + field.value = htmlType + } + if (field.items) { + walk(field.items) + } + }) + } + Ext.each(originals, function (block) { + if (block && block.items) { + walk(block.items) + } + }) + return originals + }, + /** * Get Product tab with nested Vue TabView * Contains: Properties, Gallery, Categories, Links, Options diff --git a/core/components/minishop3/controllers/category/update.class.php b/core/components/minishop3/controllers/category/update.class.php index caa64694e..d3f6126b3 100644 --- a/core/components/minishop3/controllers/category/update.class.php +++ b/core/components/minishop3/controllers/category/update.class.php @@ -60,6 +60,7 @@ public function loadCustomCssJs() // Vue modules with VueTools dependency check $this->addCss($assetsUrl . 'css/mgr/vue-dist/primeicons.min.css'); + $this->addCss($assetsUrl . 'css/mgr/vue-dist/ActionsColumn.min.css'); $this->addCss($assetsUrl . 'css/mgr/vue-dist/category-products.min.css'); $this->addCss($assetsUrl . 'css/mgr/vue-dist/category-options.min.css'); $this->addVueModule($assetsUrl . 'js/mgr/vue-dist/category-products.min.js'); From 14883c060122bac951afe9f5e050ad5338e0a262 Mon Sep 17 00:00:00 2001 From: Ivan Bochkarev Date: Sat, 29 Aug 2026 08:07:02 +0600 Subject: [PATCH 05/13] style(mgr): drop inline padding on Vue page wrappers Theme panel spacing owns the inset; the old 20px style fought it. --- core/components/minishop3/templates/default/customers.tpl | 2 +- core/components/minishop3/templates/default/notifications.tpl | 2 +- core/components/minishop3/templates/default/order.tpl | 2 +- core/components/minishop3/templates/default/orders.tpl | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/core/components/minishop3/templates/default/customers.tpl b/core/components/minishop3/templates/default/customers.tpl index b1de06d4d..18d54dca9 100644 --- a/core/components/minishop3/templates/default/customers.tpl +++ b/core/components/minishop3/templates/default/customers.tpl @@ -1,3 +1,3 @@
-
+
diff --git a/core/components/minishop3/templates/default/notifications.tpl b/core/components/minishop3/templates/default/notifications.tpl index d552891fd..5a283354e 100644 --- a/core/components/minishop3/templates/default/notifications.tpl +++ b/core/components/minishop3/templates/default/notifications.tpl @@ -1,3 +1,3 @@
-
+
diff --git a/core/components/minishop3/templates/default/order.tpl b/core/components/minishop3/templates/default/order.tpl index e8729dc17..7574a7f29 100644 --- a/core/components/minishop3/templates/default/order.tpl +++ b/core/components/minishop3/templates/default/order.tpl @@ -1,3 +1,3 @@
-
+
diff --git a/core/components/minishop3/templates/default/orders.tpl b/core/components/minishop3/templates/default/orders.tpl index d1cbebc07..38933ec90 100644 --- a/core/components/minishop3/templates/default/orders.tpl +++ b/core/components/minishop3/templates/default/orders.tpl @@ -1,3 +1,3 @@
-
+
From 740b92f9403eecf7a09b87feace49c2b61d1f102 Mon Sep 17 00:00:00 2001 From: Ivan Bochkarev Date: Sat, 29 Aug 2026 08:07:11 +0600 Subject: [PATCH 06/13] fix(help): redesign Help page for ModxManagerTheme Replace the Aura-era card grid with theme-aligned sections, clearer nav/resources hierarchy, and updated support copy. --- .../minishop3/lexicon/en/help.inc.php | 10 +- .../minishop3/lexicon/ru/help.inc.php | 10 +- vueManager/src/components/HelpPage.vue | 500 +++++++++++------- 3 files changed, 329 insertions(+), 191 deletions(-) diff --git a/core/components/minishop3/lexicon/en/help.inc.php b/core/components/minishop3/lexicon/en/help.inc.php index a3c07dbaa..45707e442 100644 --- a/core/components/minishop3/lexicon/en/help.inc.php +++ b/core/components/minishop3/lexicon/en/help.inc.php @@ -11,7 +11,7 @@ $_lang['ms3_components_href'] = 'https://modstore.pro/'; $_lang['ms3_components_text'] = 'Paid and free addons'; $_lang['ms3_components_title'] = 'Addons'; -$_lang['ms3_demo_href'] = '#'; +$_lang['ms3_demo_href'] = 'https://minishop3.ru/'; $_lang['ms3_demo_text'] = 'Go to demo site'; $_lang['ms3_demo_title'] = 'Demo'; $_lang['ms3_docs_href'] = 'https://docs.modx.pro/components/minishop3/'; @@ -24,9 +24,11 @@ $_lang['ms3_github_text'] = 'Found an issue? Report it!'; $_lang['ms3_github_title'] = 'github.com'; $_lang['ms3_help'] = 'Help and support'; +$_lang['ms3_help_nav_title'] = 'Manager sections'; +$_lang['ms3_help_resources_title'] = 'Resources'; $_lang['ms3_help_text'] = 'MiniShop3 - the most flexible and fast e-commerce component for MODX3.'; -$_lang['ms3_help_text_support'] = 'You can support MiniShop3 financially: -
- All details here'; +$_lang['ms3_help_text_support'] = 'Support MiniShop3 development +
Payment details are on the about page.'; $_lang['ms3_lexicons_href'] = '?a=workspaces/lexicon&ns=minishop3'; $_lang['ms3_lexicons_text'] = 'Lexicon management'; $_lang['ms3_localization_href'] = 'https://crowdin.com/project/minishop3-ecommerce'; @@ -41,6 +43,6 @@ $_lang['ms3_settings_href'] = '?a=mgr/settings&namespace=minishop3'; $_lang['ms3_settings_text'] = 'Settings'; $_lang['ms3_sys_settings_href'] = '?a=system/settings&ns=minishop3'; -$_lang['ms3_sys_settings_text'] = 'System settings & Events'; +$_lang['ms3_sys_settings_text'] = 'System settings'; $_lang['ms3_utilities_href'] = '?a=mgr/utilities&namespace=minishop3'; $_lang['ms3_utilities_text'] = 'Utilities'; diff --git a/core/components/minishop3/lexicon/ru/help.inc.php b/core/components/minishop3/lexicon/ru/help.inc.php index 55143ba58..2d1ac70a2 100644 --- a/core/components/minishop3/lexicon/ru/help.inc.php +++ b/core/components/minishop3/lexicon/ru/help.inc.php @@ -11,7 +11,7 @@ $_lang['ms3_components_href'] = 'https://modstore.pro/'; $_lang['ms3_components_text'] = 'Платные и бесплатные дополнения'; $_lang['ms3_components_title'] = 'Дополнения'; -$_lang['ms3_demo_href'] = '#'; +$_lang['ms3_demo_href'] = 'https://minishop3.ru/'; $_lang['ms3_demo_text'] = 'Перейти на демо-сайт'; $_lang['ms3_demo_title'] = 'Демо'; $_lang['ms3_docs_href'] = 'https://docs.modx.pro/components/minishop3/'; @@ -24,9 +24,11 @@ $_lang['ms3_github_text'] = 'Нашли проблему? Сообщите об этом!'; $_lang['ms3_github_title'] = 'github.com'; $_lang['ms3_help'] = 'Помощь и поддержка'; +$_lang['ms3_help_nav_title'] = 'Разделы менеджера'; +$_lang['ms3_help_resources_title'] = 'Ресурсы'; $_lang['ms3_help_text'] = 'MiniShop3 - самый гибкий и быстрый компонент интернет-магазина для MODX3.'; -$_lang['ms3_help_text_support'] = 'Вы можете поддержать MiniShop3 финансово: -
- Все реквизиты здесь'; +$_lang['ms3_help_text_support'] = 'Поддержите разработку MiniShop3 +
Реквизиты для перевода — на странице о проекте.'; $_lang['ms3_lexicons_href'] = '?a=workspaces/lexicon&ns=minishop3'; $_lang['ms3_lexicons_text'] = 'Управление словарями'; $_lang['ms3_localization_href'] = 'https://crowdin.com/project/minishop3-ecommerce'; @@ -41,6 +43,6 @@ $_lang['ms3_settings_href'] = '?a=mgr/settings&namespace=minishop3'; $_lang['ms3_settings_text'] = 'Настройки'; $_lang['ms3_sys_settings_href'] = '?a=system/settings&ns=minishop3'; -$_lang['ms3_sys_settings_text'] = 'Системные настройки & События'; +$_lang['ms3_sys_settings_text'] = 'Системные настройки'; $_lang['ms3_utilities_href'] = '?a=mgr/utilities&namespace=minishop3'; $_lang['ms3_utilities_text'] = 'Утилиты'; diff --git a/vueManager/src/components/HelpPage.vue b/vueManager/src/components/HelpPage.vue index 1ec9beb42..ade321f94 100644 --- a/vueManager/src/components/HelpPage.vue +++ b/vueManager/src/components/HelpPage.vue @@ -1,247 +1,381 @@ From add1f72b0afd6436279af031fb4b980cd1ed07b8 Mon Sep 17 00:00:00 2001 From: Ivan Bochkarev Date: Sat, 29 Aug 2026 08:07:12 +0600 Subject: [PATCH 07/13] fix(product): polish product tabs for Modx theme density Panel-based properties, vendor Select/filter copy, links empty state, gallery actions control, and chips that follow theme highlight tokens. --- .../minishop3/lexicon/en/product.inc.php | 1 + .../minishop3/lexicon/ru/product.inc.php | 1 + vueManager/src/components/OptionsChips.vue | 30 +- vueManager/src/components/ProductData.vue | 6 +- .../src/components/ProductDataConfig.vue | 399 +++++++----------- .../src/components/ProductDataFields.vue | 375 ++++++++++------ vueManager/src/components/VendorCombo.vue | 41 +- .../src/components/gallery/ProductGallery.vue | 6 +- .../gallery/ProductGalleryEditDialog.vue | 5 +- .../components/gallery/ProductGalleryGrid.vue | 4 +- .../gallery/ProductGalleryToolbar.vue | 23 +- .../components/product/ProductLinksTab.vue | 186 +++++--- .../components/product/ProductOptionField.vue | 9 +- .../components/product/ProductOptionsTab.vue | 2 +- .../src/components/product/ProductTabs.vue | 25 +- 15 files changed, 607 insertions(+), 506 deletions(-) diff --git a/core/components/minishop3/lexicon/en/product.inc.php b/core/components/minishop3/lexicon/en/product.inc.php index 8adb34a43..0c317e3e7 100644 --- a/core/components/minishop3/lexicon/en/product.inc.php +++ b/core/components/minishop3/lexicon/en/product.inc.php @@ -145,6 +145,7 @@ $_lang['ms3_gallery_file_source'] = 'File Source'; $_lang['ms3_gallery_button_upload'] = 'Select Files'; +$_lang['ms3_gallery_actions'] = 'Gallery actions'; $_lang['ms3_gallery_file_show'] = 'Open in New Window'; $_lang['ms3_gallery_file_update'] = 'Edit Properties'; diff --git a/core/components/minishop3/lexicon/ru/product.inc.php b/core/components/minishop3/lexicon/ru/product.inc.php index 00303ec0b..2dfd015d9 100644 --- a/core/components/minishop3/lexicon/ru/product.inc.php +++ b/core/components/minishop3/lexicon/ru/product.inc.php @@ -145,6 +145,7 @@ $_lang['ms3_gallery_file_source'] = 'Источник файла'; $_lang['ms3_gallery_button_upload'] = 'Выбрать файлы'; +$_lang['ms3_gallery_actions'] = 'Действия галереи'; $_lang['ms3_gallery_file_show'] = 'Открыть в новом окне'; $_lang['ms3_gallery_file_update'] = 'Изменить свойства'; diff --git a/vueManager/src/components/OptionsChips.vue b/vueManager/src/components/OptionsChips.vue index 1161cfc5c..843dced4a 100644 --- a/vueManager/src/components/OptionsChips.vue +++ b/vueManager/src/components/OptionsChips.vue @@ -260,31 +260,34 @@ onMounted(() => { } .vueApp .chips-container:focus-within { - border-color: var(--ms3-accent-primary); + border-color: var(--p-form-field-focus-border-color, var(--p-primary-color, #234368)); outline: none; - box-shadow: 0 0 0 0.2rem var(--ms3-accent-focus); + box-shadow: none; } .vueApp .chip-item { display: inline-flex; align-items: center; - background: var(--ms3-accent-primary); - color: var(--ms3-text-on-primary); - padding: 0.25rem 0.5rem; - border-radius: 0.1875rem; + gap: 0.25rem; + background: var(--p-highlight-background, #d6e7f8); + color: var(--p-highlight-color, #1b3451); + border: var(--ms3-border-width) solid var(--p-primary-200, #b9d3ee); + padding: 0.125rem 0.5rem; + border-radius: var(--p-autocomplete-chip-border-radius, 2px); font-size: 0.875rem; + line-height: 1.25rem; white-space: nowrap; } .vueApp .chip-text { - margin-right: 0.25rem; + margin-right: 0; } .vueApp .chip-remove { cursor: pointer; - margin-left: 0.375rem; - opacity: 0.9; - transition: all 0.2s; + margin-left: 0; + opacity: 0.75; + transition: opacity 0.15s, background-color 0.15s; display: inline-flex; align-items: center; justify-content: center; @@ -293,7 +296,8 @@ onMounted(() => { min-width: 1.125rem; min-height: 1.125rem; border-radius: 50%; - background: rgba(255, 255, 255, 0.2); + background: transparent; + color: inherit; user-select: none; font-size: 0.65rem; } @@ -304,8 +308,8 @@ onMounted(() => { .vueApp .chip-remove:hover { opacity: 1; - background: rgba(255, 255, 255, 0.4); - transform: scale(1.15); + background: color-mix(in srgb, var(--p-highlight-color, #1b3451) 12%, transparent); + transform: none; } .vueApp .chip-input { diff --git a/vueManager/src/components/ProductData.vue b/vueManager/src/components/ProductData.vue index efe113980..7c0d78c85 100644 --- a/vueManager/src/components/ProductData.vue +++ b/vueManager/src/components/ProductData.vue @@ -1,10 +1,6 @@