# Decision: Извлечение компонента Modal **Контекст**: В проекте 7 мест с ad-hoc модалками и проблема со скроллбаром на оверлее. Нужно было выбрать между починкой на месте и извлечением переиспользуемого компонента. ## Альтернативы | Подход | Плюсы | Минусы | |--------|-------|--------| | **Исправить на месте** (добавить `max-h-[90vh] overflow-y-auto` в каждую) | Минимум изменений, нет нового кода | Нарушает DRY, будущие модалки снова ad-hoc, сложнее поддерживать единообразие | | **Извлечь компонент Modal** (выбрано) | DRY, единый источник стилей, новые модалки сразу правильные, упрощает рефакторинг | +1 файл, need migration 7 мест | | **Headless UI / Radix Dialog** | a11y из коробки, portal, focus trap | Зависимость, переписывание всех стилей, избыточно для текущего масштаба | ## Выбор **Извлечение компонента Modal**. Почему: 1. **DRY**: 7 копий одинакового оверлея + `stopPropagation` — явный код-смэлл 2. **Единый источник истины**: скроллбар теперь всегда внутри контента (`max-h-[90vh] overflow-y-auto` на inner div) 3. **Расширяемость**: новые размеры (`sm/md/lg/xl`), `className` override для кастомного фона 4. **Простота**: без зависимостей, чистый Tailwind 5. **Будущие модалки**: больше не будут дублировать разметку ### Компромиссы - Не добавили portal / focus trap / escape-key (a11y) — оставлено на будущее - Не использовали готовую библиотеку (Headless UI/Radix) — не переусложняем ## Реализация Файл: `frontend/src/components/Modal.tsx` ```tsx // Ключевая строка — скроллбар внутри контента className={`w-full ${sizeClasses[size]} max-h-[90vh] overflow-y-auto ... ${className}`} ``` ## Результат ```mermaid graph TD A[7 ad-hoc модалок
разный scroll/no-scroll] --> B[1 компонент Modal] B --> C[Скроллбар внутри модалки] B --> D[Единый стиль] B --> E[Будущие модалки = 1 строчка] ``` - Удалено: ~30 строк дублированной разметки (по 4-5 строк на каждую модалку) - Добавлено: 1 компонент, 40 строк - Чистый профит: +10 строк, но DRY и гарантированный скроллбар ## Связанные заметки - [[atomic-modal-scrollbar-inside]] — детали рефакторинга - [[architecture-overview]] - [[frontend-api-client]] ## Источник Задача 2026-07-04: «Если в модалке нужен скроллбар, то он должен быть внутри модалки». ## Теги #decision #frontend #react #modal #DRY #refactoring