Files
magistr/docs/UI_COMPONENTS.md
2026-10-02 04:07:56 +03:00

7.5 KiB
Raw Blame History

🎨 Использование UI компонентов: Выпадающие списки (Dropdowns)

В проекте Magistr используется премиальная кастомная дизайн-система выпадающих списков. В связи с ограничениями браузеров на стилизацию стандартных элементов <select>, мы реализовали два типа компонентов, которые выглядят потрясающе (с эффектом glassmorphism, встроенными микро-анимациями и свечением), но интегрируются максимально просто.


1. Одинарные списки на React

В админке используется frontend/react/shared/ui/CustomSelect.jsx. Он сохраняет стили .custom-select-wrapper, нативный select для формы и событие change, но разметкой меню, выбранной подписью и состоянием disabled управляет React. Глобальный MutationObserver из admin/js/dropdown.js больше не запускается. Его прежнее применение к React-дереву оставляло устаревшие подписи и лишние DOM-узлы.

import { CustomSelect } from '../../shared/ui/CustomSelect.jsx';

<div className="form-group">
    <label htmlFor="my-new-select">Выберите опцию</label>
    <CustomSelect
        id="my-new-select"
        value={selectedId}
        disabled={loading}
        onChange={event => setSelectedId(event.target.value)}
    >
        <option value="">Все значения</option>
        {options.map(option => (
            <option key={option.id} value={option.id}>{option.name}</option>
        ))}
    </CustomSelect>
</div>

Список обновляется через options и value, без innerHTML. Пустое значение можно выбрать для сброса фильтра; недоступные пункты помечаются disabled. При количестве вариантов больше шести появляется поиск. Поддерживаются стрелки, Home/End, Escape, закрытие снаружи и снятие обработчиков при размонтировании. Для обязательного поля передайте required: пустое значение блокирует отправку, показывает «Выберите значение» и переводит фокус на видимую кнопку селекта. Ошибка связана с кнопкой через aria-describedby и снимается после выбора. Для поиска по API используется отдельный AsyncCombobox.


2. Множественный выбор (Multi-Select с чекбоксами)

На React-страницах меню и закрытие снаружи управляются состоянием компонентов (EquipmentMultiSelect в ClassroomsTab.jsx, WorkloadMultiSelect в AuditoriumWorkloadTab.jsx и аналогичные компоненты остальных вкладок). Глобальные initMultiSelect и closeAllDropdownsOnOutsideClick к React-дереву не применяются. HTML-примеры ниже описывают прежний контракт CSS-классов; новые элементы следует создавать в JSX и управлять ими через React-состояние.

Этот UI-компонент позволяет выбирать сразу несколько элементов из выпадающего списка. Он включает в себя кастомные красивые галочки (checkmarks) с неоновой подсветкой и кастомный скроллбар.

Этот компонент требует написания определённой HTML-структуры, так как нативного тега select multiple с похожей функциональностью не существует.

Как добавить мульти-селект:

1. HTML Структура:

<div class="form-group">
    <label>Выберите оборудование</label>
    <div class="custom-multi-select">
        <!-- Кнопка-триггер (то, на что нажимаем) -->
        <div class="select-box" id="my-multi-box">
            <span class="select-text" id="my-multi-text">Выберите...</span>
            <svg class="dropdown-icon" width="12" height="8" viewBox="0 0 12 8" fill="none" xmlns="http://www.w3.org/2000/svg">
                <path d="M1 1.5L6 6.5L11 1.5" stroke="#9ca3af" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
            </svg>
        </div>
        <!-- Само выпадающее меню -->
        <div class="dropdown-menu" id="my-multi-menu">
            <div id="my-multi-checkboxes" class="checkbox-group-vertical">
                <!-- Сюда JS добавит чекбоксы -->
            </div>
        </div>
    </div>
</div>

2. Инициализация (в вашем JS-файле): Используйте готовую утилиту initMultiSelect из utils.js (она обрабатывает клики и открытие/закрытие):

import { initMultiSelect } from '../utils.js';

// Передаем ID: box, menu, text, container
initMultiSelect('my-multi-box', 'my-multi-menu', 'my-multi-text', 'my-multi-checkboxes');

3. Рендеринг элементов с кастомными галочками: Чтобы нарисовать сами чекбоксы, нужно использовать класс .checkbox-item и обязательный пустой span.checkmark. Пример генерации HTML:

const container = document.getElementById('my-multi-checkboxes');
const items = [{id: 1, name: "Проектор"}, {id: 2, name: "Компьютер"}];

container.innerHTML = items.map(item => `
    <label class="checkbox-item">
        <input type="checkbox" value="${item.id}">
        <!-- Обязательный элемент для красивой галочки: -->
        <span class="checkmark"></span>
        <span class="checkbox-label">${item.name}</span>
    </label>
`).join('');

Как прочитать выбранные значения:

Просто соберите массив value у выбранных чекбоксов внутри контейнера:

const checkedBoxes = Array.from(document.querySelectorAll('#my-multi-checkboxes input:checked'));
const selectedIds = checkedBoxes.map(chk => parseInt(chk.value, 10));
console.log(selectedIds); // [1, 2]

Итог и правила

  1. Никогда не пытайтесь "красить" нативные теги <option>. Браузеры (особенно Safari и Chrome) не позволяют этого сделать.
  2. Для отдельного выбора (1 из N) всегда используйте стандартный select. Наша обёртка сделает всю магию сама.
  3. Для множественного выбора (N из M) используйте HTML-шаблон .custom-multi-select (с span.checkmark).