Skip to content

feat(gallery): Vue refactor and unified modal styles#121

Open
Ibochkarev wants to merge 2 commits intobetafrom
feat/gallery-vue-refactor-modal-styles
Open

feat(gallery): Vue refactor and unified modal styles#121
Ibochkarev wants to merge 2 commits intobetafrom
feat/gallery-vue-refactor-modal-styles

Conversation

@Ibochkarev
Copy link
Member

@Ibochkarev Ibochkarev commented Feb 24, 2026

Описание

Рефакторинг галереи товара: замена старого JS (gallery.panel, toolbar, view, window) на Vue-компоненты и унификация стилей модальных окон.

Галерея:

  • Новые компоненты: ProductGallery, ProductGalleryGrid, ProductGalleryToolbar, ProductGalleryEditDialog
  • Композабл useGalleryApi для работы с API галереи
  • Интеграция GalleryUploader в новую структуру
  • Обновлён контроллер товара и процессор загрузки, лексиконы ru/en

Модальные окна:

  • Введена переменная --ms3-modal-width (design tokens), ширина ConfirmDialog и Dialog приведена к единому виду (без растягивания на 90vw по умолчанию)
  • Кнопка закрытия в диалоге «Изменить свойства»: использование closeButtonProps для применения класса к корневому элементу кнопки; круглая обводка при фокусе через box-shadow вместо outline

Тип изменений

  • Рефакторинг (без изменения функциональности)
  • Исправление бага (non-breaking change)
  • Новая функциональность (non-breaking change)
  • Breaking change (изменение, ломающее обратную совместимость)
  • Документация
  • Другое (опишите):

Связанные Issues

(номер issue при наличии)

Как это было протестировано?

  • Ручное тестирование
  • Автоматические тесты (PHPStan, ESLint)
  • Тестирование на разных версиях PHP/MODX

Конфигурация тестирования:

  • MiniShop3: 1.5.0-beta
  • MODX: 3x
  • PHP: 8.1

Скриншоты (если применимо)

До После
image image image

Чеклист

  • Код соответствует стилю проекта
  • Добавлены/обновлены комментарии в сложных местах
  • Изменения не ломают существующую функциональность
  • Лексиконы добавлены на двух языках (ru/en)
  • PHPStan проходит без новых ошибок
  • ESLint проходит без ошибок (для JS/Vue изменений)
  • Обновлён CHANGELOG.md (для значимых изменений)

Дополнительные заметки

  • Удалены файлы: gallery.panel.js, gallery.toolbar.js, gallery.view.js, gallery.window.js; стили галереи убраны из main.css
  • Общие стили модалей (overflow-x: hidden) вынесены в primevue.scss

- Replace legacy gallery JS (panel, toolbar, view, window) with Vue components
- Add ProductGallery, ProductGalleryGrid, ProductGalleryToolbar, ProductGalleryEditDialog
- Add useGalleryApi composable, integrate GalleryUploader
- Unify modal width via --ms3-modal-width, fix ConfirmDialog width (90vw max)
- Fix Edit Dialog close button: use closeButtonProps for circular focus ring
- Add lexicon entries (ru/en), update Upload processor and product controller
@Ibochkarev Ibochkarev linked an issue Feb 24, 2026 that may be closed by this pull request
@Ibochkarev Ibochkarev requested a review from biz87 February 24, 2026 03:58
@Ibochkarev Ibochkarev marked this pull request as ready for review February 24, 2026 03:59
Copy link
Member

@biz87 biz87 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ревью PR #121: feat(gallery): Vue refactor and unified modal styles

Большой и качественный PR — полная замена ExtJS-галереи на Vue. Архитектура хорошая: отдельные компоненты, composable useGalleryApi с withLoading, provide/inject для thumb, design-token для модалей. Лексиконы на двух языках, Uppy полностью локализован.


1. Безопасность: getMediaSourcesList() без проверки прав

public function getMediaSourcesList()
{
    $sources = $this->modx->getCollection('sources.modMediaSource', ['id:>' => 0]);
    ...
}

Загружает все медиа-источники без проверки access policies. MODX поддерживает ACL на медиа-источники — пользователь может не иметь доступа к некоторым из них.

Старый ms3-combo-source (ExtJS) загружал список через коннектор с проверкой прав. Новый код отдаёт все источники в JSON-конфиг страницы.

Нужно фильтровать через $source->checkPolicy('view') или использовать стандартный механизм MODX для получения доступных источников.

2. updateProductSource — частичное обновление через Product\Update

export async function updateProductSource(productId, sourceId) {
  await connectorRequest(PRODUCT_UPDATE, {
    id: productId,
    source_id: sourceId,
  })
  location.reload()
}

Старый ExtJS код отправлял всю форму через MODx.activePage.submitForm(). Новый код отправляет только id + source_id в Product\Update. Если процессор ожидает обязательные поля (pagetitle и т.д.) или обнуляет пустые — это может привести к потере данных товара. Нужно проверить поведение процессора при частичном вводе.

3. Мёртвый watcher в ProductGalleryGrid.vue

watch(
  () => props.first,
  (v) => {
    if (searchQuery.value !== undefined) return
  }
)

searchQuery — всегда строка (инициализирован как ''), !== undefined всегда true. Watcher ничего не делает — мёртвый код, удалить.

4. Глобальные стили ConfirmDialog

В ProductGallery.vue (unscoped <style>):

.p-confirm-dialog {
  width: var(--ms3-modal-width, 28rem);
  max-width: 90vw;
  ...
}

Затронет все ConfirmDialog на странице, не только галерейные. Может сломать другие диалоги (ProductData, будущие компоненты). Стоит ограничить scope или использовать отдельный CSS-класс.

5. CSS галереи загружается безусловно

Старый код загружал CSS/JS аплоадера только при $show_gallery === true. Новый код загружает GalleryUploader.min.css всегда, без проверки настройки ms3_product_tab_gallery.

6. Двойной emit при закрытии EditDialog

<Dialog
    @update:visible="(v) => $emit('update:visible', v)"
    @hide="close"
>

close() вызывает emit('update:visible', false). При закрытии Dialog сначала вызовет @update:visible(false), потом @hideclose() → ещё один emit. Не вредит, но стоит убрать одно из двух.

7. Имя CSS-файла

gallery-uploader.min.cssGalleryUploader.min.css (PascalCase). Проверить, что Vite генерирует файл именно с таким именем.

Что сделано хорошо

  • Composable useGalleryApi с withLoading — элегантный паттерн
  • provide/inject для обновления thumb
  • vuedraggable для drag&drop, context menu на правый клик
  • Robust JSON parsing с fallback для PHP notices в getResponseData
  • Debounced search
  • Полная локализация Uppy
  • Design-token --ms3-modal-width

Итог

Пункты 1-2 — блокирующие (безопасность + потенциальная потеря данных). Остальное — улучшения.

…yles

- Update product gallery components to enhance styling and functionality
- Refactor ConfirmDialog and ProductGalleryEditDialog for consistent class usage
- Adjust CSS class names for better specificity
- Modify product update API endpoint for clarity
- Remove unused watch functionality in ProductGalleryGrid
@Ibochkarev Ibochkarev requested a review from biz87 February 27, 2026 09:37
@Ibochkarev Ibochkarev self-assigned this Feb 27, 2026
Copy link
Member

@biz87 biz87 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ревью

PR отличный по архитектуре — composable с withLoading, provide/inject для thumb, отдельный UpdateSource процессор. Замечания biz87 учтены (checkPolicy, dedicated processor, переписанный watcher). Два момента:

1. Блокирующее: watch не импортирован в ProductGalleryGrid.vue

// строка 10
import { computed, ref } from 'vue'
// строка 57 — вызов watch(), которого нет в импорте
watch(
  () => props.items,
  (val) => { orderedItems.value = val?.length ? [...val] : [] },
  { immediate: true }
)

Компонент упадёт с ReferenceError: watch is not defined при рендере. Без этого watcher'а сетка галереи не будет обновляться при загрузке данных.

Фикс: import { computed, ref, watch } from 'vue'

2. Не блокирующее: connectorRequest фильтрует пустые строки — нельзя очистить description

В useGalleryApi.js:

Object.entries(allParams).filter(([_, v]) => v !== undefined && v !== null && v !== '')

updateFile передаёт description: payload.description ?? ''. Если пользователь очистит description → пустая строка отфильтруется → процессор не получит параметр → значение останется прежним.

Старый ExtJS-код отправлял форму как есть, пустые поля доходили до процессора.

Варианты:

  • Убрать v !== '' из общего фильтра
  • Или не фильтровать пустые строки только для updateFile (передавать params напрямую)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Feature] Переезд вкладки Галерея на Vue

2 participants