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

124 lines
7.5 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 🎨 Использование 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-узлы.
```jsx
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 Структура:**
```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` (она обрабатывает клики и открытие/закрытие):
```javascript
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:
```javascript
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 у выбранных чекбоксов внутри контейнера:
```javascript
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`).