@sberbusiness/triplex-next-mcp-server 0.24.0-beta1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +124 -0
- package/bundle/.gitkeep +0 -0
- package/bundle/components/AbstractTree.md +198 -0
- package/bundle/components/AccordionBase.md +227 -0
- package/bundle/components/Alert.md +317 -0
- package/bundle/components/Amount.md +225 -0
- package/bundle/components/AmountField.md +289 -0
- package/bundle/components/Avatar.md +209 -0
- package/bundle/components/Badge.md +277 -0
- package/bundle/components/Body.md +167 -0
- package/bundle/components/Button.md +230 -0
- package/bundle/components/ButtonDropdown.md +159 -0
- package/bundle/components/ButtonIcon.md +122 -0
- package/bundle/components/Calendar.md +445 -0
- package/bundle/components/Card.md +374 -0
- package/bundle/components/CarouselExtended.md +250 -0
- package/bundle/components/Checkbox.md +126 -0
- package/bundle/components/CheckboxTree.md +280 -0
- package/bundle/components/CheckboxTreeExtended.md +321 -0
- package/bundle/components/CheckboxXGroup.md +68 -0
- package/bundle/components/CheckboxYGroup.md +67 -0
- package/bundle/components/Chip.md +404 -0
- package/bundle/components/ChipGroup.md +179 -0
- package/bundle/components/Col.md +221 -0
- package/bundle/components/CollapsableTree.md +255 -0
- package/bundle/components/Confirm.md +343 -0
- package/bundle/components/DateField.md +371 -0
- package/bundle/components/DatePickerExtended.md +342 -0
- package/bundle/components/DateRange.md +275 -0
- package/bundle/components/DesignTokens.md +385 -0
- package/bundle/components/Divider.md +196 -0
- package/bundle/components/DocumentNumberEdit.md +280 -0
- package/bundle/components/Dropdown.md +433 -0
- package/bundle/components/Ellipsis.md +208 -0
- package/bundle/components/EmptyView.md +99 -0
- package/bundle/components/ExpandAnimation.md +281 -0
- package/bundle/components/Footer.md +339 -0
- package/bundle/components/FormField.md +326 -0
- package/bundle/components/FormGroup.md +99 -0
- package/bundle/components/Gap.md +113 -0
- package/bundle/components/Header.md +280 -0
- package/bundle/components/HelpBox.md +154 -0
- package/bundle/components/IconWrapper.md +145 -0
- package/bundle/components/Island.md +197 -0
- package/bundle/components/IslandAccordion.md +230 -0
- package/bundle/components/IslandWidget.md +233 -0
- package/bundle/components/KeyDownListener.md +157 -0
- package/bundle/components/LightBox.md +322 -0
- package/bundle/components/Link.md +161 -0
- package/bundle/components/List.md +247 -0
- package/bundle/components/ListMaster.md +204 -0
- package/bundle/components/Loader.md +161 -0
- package/bundle/components/LoaderScreen.md +134 -0
- package/bundle/components/Marker.md +127 -0
- package/bundle/components/MarkerStatus.md +148 -0
- package/bundle/components/MediaWidth.md +169 -0
- package/bundle/components/MobileView.md +120 -0
- package/bundle/components/ModalWindow.md +308 -0
- package/bundle/components/MonthYearField.md +168 -0
- package/bundle/components/MultiselectField.md +213 -0
- package/bundle/components/Notification.md +233 -0
- package/bundle/components/NumberField.md +178 -0
- package/bundle/components/OrderedList.md +169 -0
- package/bundle/components/Overlay.md +237 -0
- package/bundle/components/Page.md +246 -0
- package/bundle/components/Pagination.md +298 -0
- package/bundle/components/Portal.md +119 -0
- package/bundle/components/Radio.md +174 -0
- package/bundle/components/Row.md +125 -0
- package/bundle/components/SMSField.md +160 -0
- package/bundle/components/SegmentedControl.md +235 -0
- package/bundle/components/SelectExtendedField.md +322 -0
- package/bundle/components/SelectField.md +213 -0
- package/bundle/components/Skeleton.md +129 -0
- package/bundle/components/Slider.md +157 -0
- package/bundle/components/SliderExtended.md +210 -0
- package/bundle/components/SliderRange.md +160 -0
- package/bundle/components/SmallInput.md +101 -0
- package/bundle/components/Spoiler.md +121 -0
- package/bundle/components/StatusTracker.md +151 -0
- package/bundle/components/Step.md +121 -0
- package/bundle/components/Stepper.md +163 -0
- package/bundle/components/Suggest.md +160 -0
- package/bundle/components/SuggestField.md +157 -0
- package/bundle/components/SwipeableArea.md +144 -0
- package/bundle/components/Table.md +199 -0
- package/bundle/components/Tabs.md +152 -0
- package/bundle/components/TabsExtended.md +273 -0
- package/bundle/components/TabsLine.md +189 -0
- package/bundle/components/Tag.md +135 -0
- package/bundle/components/TagColor.md +121 -0
- package/bundle/components/TagGroup.md +100 -0
- package/bundle/components/TextField.md +152 -0
- package/bundle/components/TextareaField.md +124 -0
- package/bundle/components/ThemeProvider.md +181 -0
- package/bundle/components/Tooltip.md +254 -0
- package/bundle/components/TopOverlay.md +145 -0
- package/bundle/components/TreeView.md +224 -0
- package/bundle/components/Triggers.md +121 -0
- package/bundle/guides/codestyle.md +205 -0
- package/bundle/guides/commits.md +166 -0
- package/bundle/guides/context.md +277 -0
- package/bundle/guides/stories.md +618 -0
- package/bundle/guides/template.md +154 -0
- package/bundle/guides/tests.md +301 -0
- package/bundle/icons.json +4156 -0
- package/bundle/manifest.json +796 -0
- package/bundle/release-notes/1.1.0.mdx +26 -0
- package/bundle/release-notes/1.1.1.mdx +14 -0
- package/bundle/release-notes/1.10.0.mdx +43 -0
- package/bundle/release-notes/1.11.0.mdx +19 -0
- package/bundle/release-notes/1.12.0.mdx +21 -0
- package/bundle/release-notes/1.13.0.mdx +43 -0
- package/bundle/release-notes/1.14.0.mdx +29 -0
- package/bundle/release-notes/1.15.0.mdx +56 -0
- package/bundle/release-notes/1.16.0.mdx +55 -0
- package/bundle/release-notes/1.17.0.mdx +15 -0
- package/bundle/release-notes/1.18.0.mdx +24 -0
- package/bundle/release-notes/1.19.0.mdx +48 -0
- package/bundle/release-notes/1.19.1.mdx +9 -0
- package/bundle/release-notes/1.2.0.mdx +80 -0
- package/bundle/release-notes/1.20.0.mdx +48 -0
- package/bundle/release-notes/1.21.0.mdx +66 -0
- package/bundle/release-notes/1.22.0.mdx +75 -0
- package/bundle/release-notes/1.23.0.mdx +50 -0
- package/bundle/release-notes/1.24.0.mdx +59 -0
- package/bundle/release-notes/1.25.0.mdx +52 -0
- package/bundle/release-notes/1.26.0.mdx +11 -0
- package/bundle/release-notes/1.3.0.mdx +21 -0
- package/bundle/release-notes/1.4.0.mdx +37 -0
- package/bundle/release-notes/1.5.0.mdx +24 -0
- package/bundle/release-notes/1.6.0.mdx +37 -0
- package/bundle/release-notes/1.6.1.mdx +11 -0
- package/bundle/release-notes/1.7.0.mdx +29 -0
- package/bundle/release-notes/1.8.0.mdx +38 -0
- package/bundle/release-notes/1.9.0.mdx +28 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +324 -0
- package/dist/index.js.map +1 -0
- package/package.json +51 -0
|
@@ -0,0 +1,247 @@
|
|
|
1
|
+
---
|
|
2
|
+
component: List
|
|
3
|
+
category: Data Display
|
|
4
|
+
figma-node: ""
|
|
5
|
+
figma-file: ""
|
|
6
|
+
related: [ListMaster, Table, TreeView, Checkbox, LoaderScreen]
|
|
7
|
+
tokens: []
|
|
8
|
+
stories: stories/List/List.stories.tsx
|
|
9
|
+
version: "1.0"
|
|
10
|
+
---
|
|
11
|
+
|
|
12
|
+
# List
|
|
13
|
+
|
|
14
|
+
## Назначение
|
|
15
|
+
|
|
16
|
+
Семейство компонентов для списков (`<ul>` / `<li>`) с набором слотов
|
|
17
|
+
для строк: контент, правый контролы-блок, хвосты слева/справа, чекбокс-
|
|
18
|
+
выбор, вложенный DropdownButton, drag-and-drop. Поддерживает loading-overlay
|
|
19
|
+
на уровне списка.
|
|
20
|
+
|
|
21
|
+
Используй когда: нужен табличный список с повторяющимися строками и
|
|
22
|
+
фиксированной визуальной структурой. Для таблиц с колонками — `Table`,
|
|
23
|
+
для древовидных — `TreeView`, для master-detail интерфейсов — `ListMaster`.
|
|
24
|
+
|
|
25
|
+
---
|
|
26
|
+
|
|
27
|
+
## Файловая структура
|
|
28
|
+
|
|
29
|
+
```text
|
|
30
|
+
src/components/List/
|
|
31
|
+
├── List.tsx # <ul>, loading-overlay
|
|
32
|
+
├── ListSortable.tsx # DnD обёртка (dnd-kit)
|
|
33
|
+
├── index.ts # Barrel всех слотов
|
|
34
|
+
├── components/
|
|
35
|
+
│ ├── ListItem.tsx # <li> + Provider ListItemContext
|
|
36
|
+
│ ├── ListItemContext.ts # { selected, setSelected }
|
|
37
|
+
│ ├── ListItemContent.tsx # Основной контент строки
|
|
38
|
+
│ ├── ListItemControls.tsx # Правый блок кнопок
|
|
39
|
+
│ ├── ListItemControlsButton.tsx # Стилизованная кнопка в Controls
|
|
40
|
+
│ ├── ListItemControlsButtonDropdown.tsx # Кнопка с выпадающим меню
|
|
41
|
+
│ ├── ListItemSelectable.tsx # Обёртка с Checkbox, пишет selected в Context
|
|
42
|
+
│ ├── ListItemTable.tsx # Табличный вид строки
|
|
43
|
+
│ ├── ListItemTailLeft.tsx # Левый хвост (иконка, аватар)
|
|
44
|
+
│ ├── ListItemTailRight.tsx # Правый хвост
|
|
45
|
+
│ ├── ListItemLoading.tsx # Placeholder-строка при loading
|
|
46
|
+
│ ├── ListEmptyState.tsx # Пустое состояние
|
|
47
|
+
│ ├── ListSortableItem.tsx # <li> с useSortable (DnD)
|
|
48
|
+
│ ├── ListSortableItemControls.tsx # DnD-handle control (drag)
|
|
49
|
+
│ └── ListSortableItemTarget.tsx # Цель сортировки
|
|
50
|
+
└── styles/*.module.less
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
---
|
|
54
|
+
|
|
55
|
+
## Варианты и props
|
|
56
|
+
|
|
57
|
+
### List — `IListProps extends HTMLAttributes<HTMLUListElement>`
|
|
58
|
+
|
|
59
|
+
| Prop | Тип | Default | Описание |
|
|
60
|
+
|---|---|---|---|
|
|
61
|
+
| `loading` | `boolean` | — | Накладывает `LoaderScreen type="middle"` поверх списка. |
|
|
62
|
+
| `...ulAttributes` | — | — | Стандартные `<ul>`-атрибуты. |
|
|
63
|
+
|
|
64
|
+
### ListItem — `IListItemProps extends LiHTMLAttributes<HTMLLIElement>`
|
|
65
|
+
|
|
66
|
+
Создаёт собственный `ListItemContext` с `{ selected, setSelected }`. Подкомпоненты (`ListItemSelectable`) пишут в него.
|
|
67
|
+
|
|
68
|
+
### ListItemSelectable — `IListItemSelectableProps extends Omit<HTMLDivAttributes, "onSelect">`
|
|
69
|
+
|
|
70
|
+
| Prop | Тип | Обязателен | Описание |
|
|
71
|
+
|---|---|---|---|
|
|
72
|
+
| `selected` | `boolean` | Да | Управляемое состояние выбора. |
|
|
73
|
+
| `onSelect` | `(selected: boolean) => void` | Да | Callback при изменении. |
|
|
74
|
+
|
|
75
|
+
- Внутри — `<Checkbox>` + `useEffect(() => setSelected(selected))` — синхронизирует с `ListItemContext`, чтобы сиблинги (например, стили строки) могли реагировать на выбор.
|
|
76
|
+
- `<span className={styles.checkboxLabelClickArea} />` — растягивающаяся область клика (не только на чекбокс).
|
|
77
|
+
|
|
78
|
+
### ListItemContent — базовый слот для текста/описания строки.
|
|
79
|
+
|
|
80
|
+
### ListItemControls / ListItemControlsButton / …ButtonDropdown
|
|
81
|
+
|
|
82
|
+
| Компонент | Назначение |
|
|
83
|
+
|---|---|
|
|
84
|
+
| `ListItemControls` | Контейнер для кнопок справа. |
|
|
85
|
+
| `ListItemControlsButton` | Стилизованная кнопка-иконка. |
|
|
86
|
+
| `ListItemControlsButtonDropdown` | Кнопка + вложенное выпадающее меню. |
|
|
87
|
+
|
|
88
|
+
### ListItemTailLeft / ListItemTailRight — хвосты (иконки, аватары, статусы) по краям строки.
|
|
89
|
+
|
|
90
|
+
### ListItemTable
|
|
91
|
+
|
|
92
|
+
Табличный вид строки: внутренняя сетка для выравнивания колонок между строками.
|
|
93
|
+
|
|
94
|
+
### ListItemLoading — скелетон-строка, показывается во время загрузки.
|
|
95
|
+
|
|
96
|
+
### ListEmptyState — рендерится, когда `items.length === 0`, параметры для заголовка/описания/иконки.
|
|
97
|
+
|
|
98
|
+
### ListSortable<T extends { id: string }> — `IListSortableProps<T>`
|
|
99
|
+
|
|
100
|
+
| Prop | Тип | Обязателен | Описание |
|
|
101
|
+
|---|---|---|---|
|
|
102
|
+
| `items` | `T[]` | Да | Массив элементов, у каждого должно быть поле `id`. |
|
|
103
|
+
| `onItemsChange` | `(items: T[]) => void` | Да | Callback после перестановки. |
|
|
104
|
+
| `...IListProps` | — | — | Проброс на `<List>`. |
|
|
105
|
+
|
|
106
|
+
- Обёртка над `@dnd-kit/core` + `@dnd-kit/sortable`.
|
|
107
|
+
- Modifiers: `restrictToParentElement` + `restrictToVerticalAxis` — нельзя перетащить в другой родитель или по горизонтали.
|
|
108
|
+
- Внутри использует `AdvancedMouseSensor` / `AdvancedTouchSensor` — расширенные sensor'ы.
|
|
109
|
+
|
|
110
|
+
### ListSortableItem — `IListSortableItemProps extends Omit<LiHTMLAttributes, "children">`
|
|
111
|
+
|
|
112
|
+
| Prop | Тип | Обязателен | Описание |
|
|
113
|
+
|---|---|---|---|
|
|
114
|
+
| `id` | `string` | Да | Уникальный идентификатор, должен совпадать с `items[i].id`. |
|
|
115
|
+
| `disabled` | `boolean` | Нет | Блокирует drag. |
|
|
116
|
+
| `children` | `ReactNode \| (provideProps) => ReactNode` | — | **Render-props или обычные children**. Provide-props: `{ dragging, disabled, listeners, setActivatorNodeRef }`. |
|
|
117
|
+
|
|
118
|
+
- Использует `useSortable` из dnd-kit, применяет `transform` стиль.
|
|
119
|
+
- Render-props позволяют **селективно назначать drag handle** (через `listeners` + `setActivatorNodeRef`), а не делать перетаскиваемым всю строку.
|
|
120
|
+
- `ListSortableItem.Target` (Object.assign) — подкомпонент `ListSortableItemTarget`.
|
|
121
|
+
|
|
122
|
+
---
|
|
123
|
+
|
|
124
|
+
## Ключевые особенности реализации
|
|
125
|
+
|
|
126
|
+
### `AdvancedMouseSensor` / `AdvancedTouchSensor` — `data-draggable="false"` escape hatch
|
|
127
|
+
|
|
128
|
+
```ts
|
|
129
|
+
while (element !== null) {
|
|
130
|
+
if (element.dataset?.draggable === "false") return false;
|
|
131
|
+
element = element.parentElement;
|
|
132
|
+
}
|
|
133
|
+
```
|
|
134
|
+
|
|
135
|
+
- Любой потомок с `data-draggable="false"` **блокирует инициализацию drag'а**. Используется для кнопок/ссылок внутри строки — иначе клик по ним случайно запускает перетаскивание.
|
|
136
|
+
- `MouseSensor`: игнорирует правую кнопку (`button === 2`).
|
|
137
|
+
- `TouchSensor`: игнорирует мультитач (`touches.length > 1`).
|
|
138
|
+
|
|
139
|
+
### `ListItemContext` — связь выбора и стилей сиблингов
|
|
140
|
+
|
|
141
|
+
```tsx
|
|
142
|
+
<ListItemContext.Provider value={{ selected, setSelected }}>
|
|
143
|
+
<li>{children}</li>
|
|
144
|
+
</ListItemContext.Provider>
|
|
145
|
+
```
|
|
146
|
+
|
|
147
|
+
- `ListItemSelectable` пишет `selected` в контекст через `useEffect`.
|
|
148
|
+
- Другие подкомпоненты (например, `ListItemContent`) могут подписаться и изменить стиль.
|
|
149
|
+
- **Паттерн "выбор известен всей строке"** — без prop-drilling.
|
|
150
|
+
|
|
151
|
+
### `loading` на уровне `List`
|
|
152
|
+
|
|
153
|
+
```tsx
|
|
154
|
+
<ul>
|
|
155
|
+
{children}
|
|
156
|
+
{loading ? <LoaderScreen type="middle" /> : null}
|
|
157
|
+
</ul>
|
|
158
|
+
```
|
|
159
|
+
|
|
160
|
+
LoaderScreen рендерится **внутри `<ul>`**, но позиционируется абсолютно (overlay). Структурно `<ul>` содержит `<li>` и невалидный для семантики элемент — это сознательный trade-off в пользу простоты позиционирования.
|
|
161
|
+
|
|
162
|
+
### `data-tx={process.env.npm_package_version}` на `<ul>` и `<li>`
|
|
163
|
+
|
|
164
|
+
Версия библиотеки в DOM для отладки. Сохранять при изменениях.
|
|
165
|
+
|
|
166
|
+
### ListSortable — vertical-axis only
|
|
167
|
+
|
|
168
|
+
```ts
|
|
169
|
+
modifiers: [restrictToParentElement, restrictToVerticalAxis]
|
|
170
|
+
```
|
|
171
|
+
|
|
172
|
+
- Список только вертикальный. Для горизонтальных списков — нужен отдельный компонент (сейчас не реализован).
|
|
173
|
+
|
|
174
|
+
### ListSortableItem render-props
|
|
175
|
+
|
|
176
|
+
```tsx
|
|
177
|
+
<ListSortableItem id="1">
|
|
178
|
+
{({ listeners, setActivatorNodeRef }) => (
|
|
179
|
+
<>
|
|
180
|
+
<ListSortableItem.Target ref={setActivatorNodeRef} {...listeners}>
|
|
181
|
+
≡
|
|
182
|
+
</ListSortableItem.Target>
|
|
183
|
+
{/* остальной контент — не перетаскиваемый */}
|
|
184
|
+
</>
|
|
185
|
+
)}
|
|
186
|
+
</ListSortableItem>
|
|
187
|
+
```
|
|
188
|
+
|
|
189
|
+
Стандартный паттерн dnd-kit. `setActivatorNodeRef` задаёт элемент-триггер перетаскивания (handle), `listeners` — события.
|
|
190
|
+
|
|
191
|
+
---
|
|
192
|
+
|
|
193
|
+
## Инварианты
|
|
194
|
+
|
|
195
|
+
- **`<ul>` + `<li>`** семантика — не заменять на `<div>`.
|
|
196
|
+
- **`ListItemContext` shape** (`selected`, `setSelected`) — публичная точка расширения для сторонних кастомных подкомпонентов.
|
|
197
|
+
- **`data-draggable="false"`** — атрибут-escape-hatch для ListSortable. Потребители в product-коде могут опираться на него, не менять семантику.
|
|
198
|
+
- **`ListSortable` требует `items[].id: string`** — id как в React key, так и в dnd-kit. Переход на number/symbol — breaking.
|
|
199
|
+
- **`arrayMove` от dnd-kit** — не собственная реализация. При смене DnD-библиотеки переписать.
|
|
200
|
+
- **`IListSortableItemProps.children` — `ReactNode` или функция** — публичный render-props API.
|
|
201
|
+
- **`LoaderScreen` внутри `<ul>`** — визуальное решение, не менять без согласования.
|
|
202
|
+
- **ForwardRef** на всех компонентах; `ListSortable` — generic через `as <T>(props) => ...` cast.
|
|
203
|
+
- **`Object.assign` на `ListSortableItem` → `Target`** — сохранить.
|
|
204
|
+
|
|
205
|
+
---
|
|
206
|
+
|
|
207
|
+
## Accessibility
|
|
208
|
+
|
|
209
|
+
- Семантика списка `<ul>`/`<li>` — правильная. Screen reader'ы объявляют "list, N items".
|
|
210
|
+
- `ListItemSelectable` — `<Checkbox>` внутри: нативная клавиатурная навигация, label через расширяющую clickable-область.
|
|
211
|
+
- `ListItemControlsButton` / `ListItemControlsButtonDropdown` — проверь, что `aria-label` проставлен (иконки без текста).
|
|
212
|
+
- **DnD не доступен с клавиатуры** из коробки (dnd-kit поддерживает `KeyboardSensor`, но здесь не подключён). Known debt.
|
|
213
|
+
- Loader'овый overlay скрывает контент — добавь `aria-busy="true"` на `<ul>` через `...rest` на время загрузки.
|
|
214
|
+
|
|
215
|
+
---
|
|
216
|
+
|
|
217
|
+
## Дизайн-токены
|
|
218
|
+
|
|
219
|
+
Нет прямых — визуал строки зависит от подкомпонентов (`ListItemSelectable`, `ListItemControls` используют собственные стили в `styles/*.module.less`).
|
|
220
|
+
|
|
221
|
+
---
|
|
222
|
+
|
|
223
|
+
## Связанные компоненты
|
|
224
|
+
|
|
225
|
+
- `ListMaster` — master-detail layout на базе List.
|
|
226
|
+
- `Table` — табличный вид с колонками.
|
|
227
|
+
- `TreeView` — древовидный список.
|
|
228
|
+
- `Checkbox` — используется в `ListItemSelectable`.
|
|
229
|
+
- `LoaderScreen` — overlay для loading.
|
|
230
|
+
- `@dnd-kit/core`, `@dnd-kit/sortable`, `@dnd-kit/modifiers` — DnD-инфраструктура.
|
|
231
|
+
|
|
232
|
+
---
|
|
233
|
+
|
|
234
|
+
## Stories
|
|
235
|
+
|
|
236
|
+
- `stories/List/List.stories.tsx`
|
|
237
|
+
- `stories/List/ListItem.stories.tsx`
|
|
238
|
+
- `stories/List/ListItemControls.stories.tsx`
|
|
239
|
+
- Примеры: `stories/List/examples/`
|
|
240
|
+
|
|
241
|
+
---
|
|
242
|
+
|
|
243
|
+
## История изменений
|
|
244
|
+
|
|
245
|
+
| Дата | Изменение |
|
|
246
|
+
|---|---|
|
|
247
|
+
| 2026-04-18 | Создан документ |
|
|
@@ -0,0 +1,204 @@
|
|
|
1
|
+
---
|
|
2
|
+
component: ListMaster
|
|
3
|
+
category: Data Display
|
|
4
|
+
figma-node: ""
|
|
5
|
+
figma-file: ""
|
|
6
|
+
related: [List, ChipGroup, Checkbox]
|
|
7
|
+
tokens: []
|
|
8
|
+
stories: ""
|
|
9
|
+
version: "1.0"
|
|
10
|
+
---
|
|
11
|
+
|
|
12
|
+
# ListMaster
|
|
13
|
+
|
|
14
|
+
## Назначение
|
|
15
|
+
|
|
16
|
+
Тонкий layout-контейнер для экрана "список + фильтры + контролы выбора":
|
|
17
|
+
оборачивает Header (sticky, со sticky-компенсацией scroll'а), Body (контент),
|
|
18
|
+
ChipGroup (горизонтальные фильтры-чипы), Footer (sticky), SelectionControls
|
|
19
|
+
(пакетные действия над выбранными элементами). Всё через compound-api
|
|
20
|
+
`ListMaster.Header` / `.Body` / `.Footer` / `.ChipGroup` / `.SelectionControls`.
|
|
21
|
+
|
|
22
|
+
Используй когда: нужен стандартный экран списка с фильтрами сверху и
|
|
23
|
+
sticky-действиями снизу. Для самого списка — отдельно `List` / `Table`.
|
|
24
|
+
|
|
25
|
+
---
|
|
26
|
+
|
|
27
|
+
## Файловая структура
|
|
28
|
+
|
|
29
|
+
```text
|
|
30
|
+
src/components/ListMaster/
|
|
31
|
+
├── ListMaster.tsx # <div>, compound через Object.assign
|
|
32
|
+
├── index.ts # Barrel
|
|
33
|
+
├── components/
|
|
34
|
+
│ ├── ListMasterHeader.tsx # sticky + scroll-компенсация при mount/unmount
|
|
35
|
+
│ ├── ListMasterBody.tsx # прозрачный <div>-wrapper
|
|
36
|
+
│ ├── ListMasterFooter.tsx # sticky
|
|
37
|
+
│ ├── ListMasterFooterControls.tsx
|
|
38
|
+
│ ├── ListMasterFooterDescription.tsx
|
|
39
|
+
│ ├── ListMasterChipGroup.tsx # обёртка над <ChipGroup oneLine>
|
|
40
|
+
│ └── SelectionControls.tsx # блок пакетных действий
|
|
41
|
+
└── styles/
|
|
42
|
+
├── ListMasterHeader.module.less
|
|
43
|
+
├── ListMasterFooter.module.less
|
|
44
|
+
├── ListMasterChipGroup.module.less
|
|
45
|
+
└── SelectionControls.module.less
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
---
|
|
49
|
+
|
|
50
|
+
## Compound-структура
|
|
51
|
+
|
|
52
|
+
```tsx
|
|
53
|
+
<ListMaster>
|
|
54
|
+
<ListMaster.Header sticky>
|
|
55
|
+
{/* Заголовок страницы, поиск, крупные фильтры */}
|
|
56
|
+
</ListMaster.Header>
|
|
57
|
+
|
|
58
|
+
<ListMaster.ChipGroup>
|
|
59
|
+
<Chip>Фильтр 1</Chip>
|
|
60
|
+
<Chip>Фильтр 2</Chip>
|
|
61
|
+
</ListMaster.ChipGroup>
|
|
62
|
+
|
|
63
|
+
<ListMaster.SelectionControls>
|
|
64
|
+
{/* Кнопки массовых действий: "Удалить", "Экспортировать" */}
|
|
65
|
+
</ListMaster.SelectionControls>
|
|
66
|
+
|
|
67
|
+
<ListMaster.Body>
|
|
68
|
+
<List>{/* ListItem'ы */}</List>
|
|
69
|
+
</ListMaster.Body>
|
|
70
|
+
|
|
71
|
+
<ListMaster.Footer sticky>
|
|
72
|
+
<ListMaster.FooterDescription>{/* "Показано 10 из 100" */}</ListMaster.FooterDescription>
|
|
73
|
+
<ListMaster.FooterControls>{/* Pagination */}</ListMaster.FooterControls>
|
|
74
|
+
</ListMaster.Footer>
|
|
75
|
+
</ListMaster>
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
---
|
|
79
|
+
|
|
80
|
+
## Варианты и props
|
|
81
|
+
|
|
82
|
+
### ListMaster — `IListMasterProps extends HTMLAttributes<HTMLDivElement>`
|
|
83
|
+
|
|
84
|
+
Без собственных props'ов. Просто `<div>` + `data-tx` + compound-подкомпоненты.
|
|
85
|
+
|
|
86
|
+
### ListMaster.Header — `IListMasterHeaderProps`
|
|
87
|
+
|
|
88
|
+
| Prop | Тип | Default | Описание |
|
|
89
|
+
|---|---|---|---|
|
|
90
|
+
| `sticky` | `boolean` | `true` | Добавляет класс `.sticky` (CSS `position: sticky`). |
|
|
91
|
+
|
|
92
|
+
**Scroll-компенсация при mount/unmount** — см. ниже.
|
|
93
|
+
|
|
94
|
+
### ListMaster.Footer — `IListMasterFooterProps`
|
|
95
|
+
|
|
96
|
+
| Prop | Тип | Default | Описание |
|
|
97
|
+
|---|---|---|---|
|
|
98
|
+
| `sticky` | `boolean` | `true` | Аналогично Header. Но без scroll-компенсации. |
|
|
99
|
+
|
|
100
|
+
### ListMaster.FooterControls / FooterDescription
|
|
101
|
+
|
|
102
|
+
Простые `<div>`-слоты с семантическими именами для Footer.
|
|
103
|
+
|
|
104
|
+
### ListMaster.ChipGroup
|
|
105
|
+
|
|
106
|
+
Обёртка над `<ChipGroup oneLine>` (из компонента `ChipGroup`) с доп. стилем (horizontal padding). **`oneLine` зашит** — нельзя отключить через props.
|
|
107
|
+
|
|
108
|
+
### ListMaster.Body
|
|
109
|
+
|
|
110
|
+
Прозрачный forwardRef-wrapper, без стилей.
|
|
111
|
+
|
|
112
|
+
### ListMaster.SelectionControls
|
|
113
|
+
|
|
114
|
+
`<div>` с классом `.selectionControls`. Рендерится, когда нужно показать действия над выбранными элементами (например, по флагу контроллера).
|
|
115
|
+
|
|
116
|
+
---
|
|
117
|
+
|
|
118
|
+
## Ключевые особенности реализации
|
|
119
|
+
|
|
120
|
+
### Scroll-компенсация в `ListMasterHeader`
|
|
121
|
+
|
|
122
|
+
```tsx
|
|
123
|
+
useEffect(() => {
|
|
124
|
+
containerHeightRef.current = containerRef.current.getBoundingClientRect().height;
|
|
125
|
+
window.scrollTo({ top: window.scrollY + containerHeightRef.current });
|
|
126
|
+
return () => window.scrollTo({ top: Math.max(window.scrollY - containerHeightRef.current, 0) });
|
|
127
|
+
}, []);
|
|
128
|
+
```
|
|
129
|
+
|
|
130
|
+
- **При монтировании Header** (который обычно появляется при выборе элементов — `SelectionControls` mode) — страница "проскроллена" на высоту header'а, чтобы чекбоксы не "прыгнули" вниз.
|
|
131
|
+
- **При размонтировании** — обратный scroll.
|
|
132
|
+
- **Критичный side effect**: меняет `window.scrollY`. Если Header рендерится и пропадает часто — поведение видно. Этот UX-паттерн специфичен для случаев "выбрал строку → появился контрол-бар".
|
|
133
|
+
|
|
134
|
+
### `data-tx` на нескольких слотах
|
|
135
|
+
|
|
136
|
+
`ListMaster`, `ListMasterBody`, `SelectionControls` имеют `data-tx={process.env.npm_package_version}`. Header/Footer/ChipGroup — **нет**. Неконсистентно, но это исторический паттерн — не добавляй/убирай без согласования.
|
|
137
|
+
|
|
138
|
+
### `ListMasterChipGroup` зашивает `oneLine`
|
|
139
|
+
|
|
140
|
+
```tsx
|
|
141
|
+
<ChipGroup className={...} oneLine {...rest} ref={ref}>
|
|
142
|
+
```
|
|
143
|
+
|
|
144
|
+
Если потребителю нужен многострочный ChipGroup в ListMaster — использовать `<ChipGroup>` напрямую, не через `.ChipGroup` слот.
|
|
145
|
+
|
|
146
|
+
### Compound через `FC & { ...slots }` + присваивания
|
|
147
|
+
|
|
148
|
+
```tsx
|
|
149
|
+
export interface IListMasterFC extends FC<IListMasterProps> { Body, ChipGroup, ... }
|
|
150
|
+
export const ListMaster: IListMasterFC = (...) => <div>...</div>;
|
|
151
|
+
ListMaster.Body = ListMasterBody;
|
|
152
|
+
// ...
|
|
153
|
+
```
|
|
154
|
+
|
|
155
|
+
**Не `Object.assign`** — явное присваивание после определения. Паттерн-вариант, используй тот же при добавлении слотов.
|
|
156
|
+
|
|
157
|
+
---
|
|
158
|
+
|
|
159
|
+
## Инварианты
|
|
160
|
+
|
|
161
|
+
- **`sticky=true` по умолчанию** на Header/Footer — публичный контракт.
|
|
162
|
+
- **Scroll-компенсация в Header** — UX-решение, не удалять без обсуждения (ломает visual continuity в scenario "выбор элементов").
|
|
163
|
+
- **`ListMasterChipGroup` зашитый `oneLine`** — контракт. Для многострочного — потребитель использует `ChipGroup` напрямую.
|
|
164
|
+
- **ForwardRef** на всех слотах.
|
|
165
|
+
- **Compound: `ListMaster.{Body,ChipGroup,Footer,FooterControls,FooterDescription,Header,SelectionControls}`** — публичный API.
|
|
166
|
+
- **Отсутствуют собственные токены** — визуал наследуется от `ChipGroup` и общих CSS-переменных.
|
|
167
|
+
- **Нет stories/тестов** — compound тонкий, покрытие лежит на демо-приложении. При добавлении новых слотов **обязательно** написать story.
|
|
168
|
+
|
|
169
|
+
---
|
|
170
|
+
|
|
171
|
+
## Accessibility
|
|
172
|
+
|
|
173
|
+
- `<div>`-контейнеры, без ARIA-ролей. Семантика — на совести потребителя (добавь `role="region"` + `aria-label` через `...rest` для скринридеров).
|
|
174
|
+
- Sticky Header не ломает фокусируемость (CSS, не portal).
|
|
175
|
+
- **Scroll-переход в Header при mount** — может сбивать скринридер на слабых устройствах (меняется позиция scroll'а). Не отключается через prop.
|
|
176
|
+
|
|
177
|
+
---
|
|
178
|
+
|
|
179
|
+
## Дизайн-токены
|
|
180
|
+
|
|
181
|
+
Нет собственных.
|
|
182
|
+
|
|
183
|
+
---
|
|
184
|
+
|
|
185
|
+
## Связанные компоненты
|
|
186
|
+
|
|
187
|
+
- `List` / `Table` / `TreeView` — то, что обычно внутри `<ListMaster.Body>`.
|
|
188
|
+
- `ChipGroup` — используется в `.ChipGroup`.
|
|
189
|
+
- `Checkbox` — часто в `.SelectionControls`.
|
|
190
|
+
- `Pagination` — типично в `.FooterControls`.
|
|
191
|
+
|
|
192
|
+
---
|
|
193
|
+
|
|
194
|
+
## Stories
|
|
195
|
+
|
|
196
|
+
Нет. Кандидат на добавление при следующей работе с компонентом.
|
|
197
|
+
|
|
198
|
+
---
|
|
199
|
+
|
|
200
|
+
## История изменений
|
|
201
|
+
|
|
202
|
+
| Дата | Изменение |
|
|
203
|
+
|---|---|
|
|
204
|
+
| 2026-04-18 | Создан документ |
|
|
@@ -0,0 +1,161 @@
|
|
|
1
|
+
---
|
|
2
|
+
component: Loader
|
|
3
|
+
category: Feedback
|
|
4
|
+
figma-node: ""
|
|
5
|
+
figma-file: ""
|
|
6
|
+
related: [LoaderScreen, Skeleton]
|
|
7
|
+
tokens: []
|
|
8
|
+
stories: stories/Loaders/LoaderMiddle/LoaderMiddle.stories.tsx
|
|
9
|
+
version: "1.0"
|
|
10
|
+
---
|
|
11
|
+
|
|
12
|
+
# Loader
|
|
13
|
+
|
|
14
|
+
## Назначение
|
|
15
|
+
|
|
16
|
+
Две точечные анимации для индикации загрузки:
|
|
17
|
+
- **`LoaderSmall`** — три точки, встраивается в кнопки/инпуты, поддерживает темы (`BRAND`/`NEUTRAL`) и размеры (`SM`/`MD`/`LG`).
|
|
18
|
+
- **`LoaderMiddle`** — точки с линией, для центрирования в блоке (карточки, empty-state'ы).
|
|
19
|
+
|
|
20
|
+
Для полноэкранной/мозаичной загрузки — `LoaderScreen`. Для skeleton-stub — `Skeleton`.
|
|
21
|
+
|
|
22
|
+
---
|
|
23
|
+
|
|
24
|
+
## Файловая структура
|
|
25
|
+
|
|
26
|
+
```text
|
|
27
|
+
src/components/Loader/
|
|
28
|
+
├── index.ts # Barrel: LoaderSmall, enum (ELoaderSmallTheme/Size), LoaderMiddle
|
|
29
|
+
├── LoaderSmall/
|
|
30
|
+
│ ├── LoaderSmall.tsx # FC, <span role="status">
|
|
31
|
+
│ ├── enum.ts # ELoaderSmallTheme, ELoaderSmallSize (unused? см. ниже)
|
|
32
|
+
│ ├── styles/LoaderSmall.module.less
|
|
33
|
+
│ └── __tests__/LoaderSmall.test.tsx
|
|
34
|
+
└── LoaderMiddle/
|
|
35
|
+
├── LoaderMiddle.tsx # FC, без props
|
|
36
|
+
├── styles/LoaderMiddle.module.less
|
|
37
|
+
└── __tests__/LoaderMiddle.test.tsx
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
---
|
|
41
|
+
|
|
42
|
+
## Варианты и props
|
|
43
|
+
|
|
44
|
+
### LoaderSmall — `ILoaderSmallProps extends HTMLAttributes<HTMLSpanElement>`
|
|
45
|
+
|
|
46
|
+
| Prop | Тип | Обязателен | Описание |
|
|
47
|
+
|---|---|---|---|
|
|
48
|
+
| `theme` | `ELoaderSmallTheme` (`BRAND` / `NEUTRAL`) | Да | Цветовая тема точек. |
|
|
49
|
+
| `size` | `EComponentSize` (`SM`/`MD`/`LG`) | Да | Размер. |
|
|
50
|
+
| `className`, `...HTMLSpanAttributes` | — | — | Стандартные. |
|
|
51
|
+
|
|
52
|
+
**Важно:** используется общий `EComponentSize`, а НЕ `ELoaderSmallSize` (хотя последний определён в `enum.ts` и экспортирован). **`ELoaderSmallSize` — unused в самом компоненте**. Считать legacy; рефакторинг — убрать из экспорта или переключить компонент на него.
|
|
53
|
+
|
|
54
|
+
### LoaderMiddle — без props
|
|
55
|
+
|
|
56
|
+
```ts
|
|
57
|
+
export const LoaderMiddle: React.FC = () => (...);
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
Чистая визуальная анимация. Не принимает `className`/других атрибутов — если нужна кастомизация позиционирования, оборачивай снаружи.
|
|
61
|
+
|
|
62
|
+
### ELoaderSmallTheme
|
|
63
|
+
|
|
64
|
+
```ts
|
|
65
|
+
enum ELoaderSmallTheme { BRAND = "brand", NEUTRAL = "neutral" }
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
### ELoaderSmallSize (экспортируется, но не используется компонентом)
|
|
69
|
+
|
|
70
|
+
```ts
|
|
71
|
+
enum ELoaderSmallSize { SM = "sm", MD = "md", LG = "lg" }
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
---
|
|
75
|
+
|
|
76
|
+
## Ключевые особенности реализации
|
|
77
|
+
|
|
78
|
+
### `role="status"` + `aria-label="loading"` — хардкод
|
|
79
|
+
|
|
80
|
+
Оба лоадера используют:
|
|
81
|
+
```tsx
|
|
82
|
+
<span role="status" aria-label="loading" ...>
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
- **Хардкод английского текста** `"loading"` — технический долг для мультиязычной версии.
|
|
86
|
+
- `role="status"` + непустой accessible name — скринридер анонсирует появление loader'а (live region).
|
|
87
|
+
- **Не менять `role="status"`** без согласования a11y-regression.
|
|
88
|
+
|
|
89
|
+
### LoaderMiddle — 4 точки + статическая линия
|
|
90
|
+
|
|
91
|
+
```tsx
|
|
92
|
+
<span className={styles.line} />
|
|
93
|
+
<span className={clsx(styles.dot, styles.dot1)} />
|
|
94
|
+
... dot2, dot3, dot4
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
4 точки с разной задержкой анимации — передают последовательную "волну". Линия — визуальный baseline.
|
|
98
|
+
|
|
99
|
+
### LoaderSmall — 3 точки
|
|
100
|
+
|
|
101
|
+
```tsx
|
|
102
|
+
<span .dot .dot1 />
|
|
103
|
+
<span .dot .dot2 />
|
|
104
|
+
<span .dot .dot3 />
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
Темы (цвет) задаются через класс на корне, цвет точек — через CSS-переменные или текущий `color` (не проверено, зависит от less-файла).
|
|
108
|
+
|
|
109
|
+
### `createSizeToClassNameMap(styles)` — утилита общая
|
|
110
|
+
|
|
111
|
+
Мапит `EComponentSize.SM` → `styles.sm`, и т.д. Используется в других компонентах библиотеки — паттерн сохранять.
|
|
112
|
+
|
|
113
|
+
---
|
|
114
|
+
|
|
115
|
+
## Инварианты
|
|
116
|
+
|
|
117
|
+
- **`LoaderSmall.theme` и `LoaderSmall.size` обязательны** — не делать опциональными без default'а.
|
|
118
|
+
- **`ELoaderSmallTheme`** — публичный API.
|
|
119
|
+
- **`ELoaderSmallSize`** — экспортирован, но не используется — **если трогаешь**, проверь потребителей (возможно, кто-то уже импортирует) прежде чем удалять.
|
|
120
|
+
- **`LoaderSmall` использует `EComponentSize`**, а не `ELoaderSmallSize` — не менять без breaking-change-плана.
|
|
121
|
+
- **`LoaderMiddle` без props** — расширение API требует RFC.
|
|
122
|
+
- **`role="status"` + `aria-label="loading"`** — ARIA-контракт. Не менять на `"Загрузка"` без i18n-решения для всей библиотеки.
|
|
123
|
+
- **`<span>` контейнер** у LoaderSmall (inline) vs **`<div>`** у LoaderMiddle (block) — семантическое различие, не унифицировать.
|
|
124
|
+
|
|
125
|
+
---
|
|
126
|
+
|
|
127
|
+
## Accessibility
|
|
128
|
+
|
|
129
|
+
- `role="status"` создаёт **polite live region** — screen reader анонсирует появление без прерывания. Для одновременного показа двух лоадеров скринридер может анонсировать дважды.
|
|
130
|
+
- `aria-label="loading"` — **захардкожен en**. Для i18n-проекта потребитель должен переопределить через `aria-label={t("loading")}` (prop принимается через `...restProps` только у LoaderSmall).
|
|
131
|
+
- `LoaderMiddle` не принимает `aria-label` через props — текст "loading" фиксирован. Known debt.
|
|
132
|
+
- Нет `aria-busy` на родительском контейнере — потребитель должен добавить на секцию, находящуюся в процессе загрузки.
|
|
133
|
+
|
|
134
|
+
---
|
|
135
|
+
|
|
136
|
+
## Дизайн-токены
|
|
137
|
+
|
|
138
|
+
Нет публичных CSS-переменных. Цвета приходят от теории (BRAND/NEUTRAL) напрямую через LESS.
|
|
139
|
+
|
|
140
|
+
---
|
|
141
|
+
|
|
142
|
+
## Связанные компоненты
|
|
143
|
+
|
|
144
|
+
- `LoaderScreen` — оверлей с лоадером для больших областей/полноэкранного режима (использует `LoaderMiddle` или `LoaderSmall` внутри).
|
|
145
|
+
- `Skeleton` — альтернатива при загрузке контента с известной структурой.
|
|
146
|
+
- `Button` — LoaderSmall встраивается в кнопку при `isLoading`.
|
|
147
|
+
|
|
148
|
+
---
|
|
149
|
+
|
|
150
|
+
## Stories
|
|
151
|
+
|
|
152
|
+
- `stories/Loaders/LoaderMiddle/LoaderMiddle.stories.tsx`
|
|
153
|
+
- `stories/Loaders/LoaderSmall/LoaderSmall.stories.tsx`
|
|
154
|
+
|
|
155
|
+
---
|
|
156
|
+
|
|
157
|
+
## История изменений
|
|
158
|
+
|
|
159
|
+
| Дата | Изменение |
|
|
160
|
+
|---|---|
|
|
161
|
+
| 2026-04-18 | Создан документ |
|