@sberbusiness/triplex-next-mcp-server 0.24.0-beta2 → 1.40.0-beta

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.
Files changed (73) hide show
  1. package/README.md +10 -0
  2. package/bundle/components/AlertContext.md +268 -0
  3. package/bundle/components/BodyPage.md +186 -0
  4. package/bundle/components/Button.md +93 -85
  5. package/bundle/components/ButtonDropdown.md +36 -70
  6. package/bundle/components/ButtonDropdownExtended.md +422 -0
  7. package/bundle/components/ButtonIcon.md +41 -48
  8. package/bundle/components/Caption.md +291 -0
  9. package/bundle/components/Checkbox.md +43 -122
  10. package/bundle/components/CheckboxXGroup.md +109 -46
  11. package/bundle/components/CheckboxYGroup.md +77 -49
  12. package/bundle/components/CodeText.md +197 -0
  13. package/bundle/components/Col.md +311 -162
  14. package/bundle/components/CollapsibleTree.md +584 -0
  15. package/bundle/components/CollapsibleTreeExtended.md +422 -0
  16. package/bundle/components/Dropdown.md +748 -346
  17. package/bundle/components/EmptyView.md +14 -15
  18. package/bundle/components/FooterPage.md +203 -0
  19. package/bundle/components/Gap.md +115 -57
  20. package/bundle/components/HeaderPage.md +226 -0
  21. package/bundle/components/HelpBox.md +45 -84
  22. package/bundle/components/ImageGallery.md +423 -0
  23. package/bundle/components/ImageGalleryExtended.md +606 -0
  24. package/bundle/components/LightBox.md +2252 -233
  25. package/bundle/components/List.md +216 -183
  26. package/bundle/components/ListActionItem.md +157 -0
  27. package/bundle/components/ListItem.md +276 -0
  28. package/bundle/components/ListItemControlsButton.md +103 -0
  29. package/bundle/components/ListItemControlsButtonDropdown.md +107 -0
  30. package/bundle/components/ListItemSelectable.md +99 -0
  31. package/bundle/components/ListItemTable.md +390 -0
  32. package/bundle/components/ListMaster.md +876 -156
  33. package/bundle/components/ListMasterFooter.md +80 -0
  34. package/bundle/components/ListMasterHeader.md +86 -0
  35. package/bundle/components/ListSortable.md +153 -0
  36. package/bundle/components/ListSortableItem.md +175 -0
  37. package/bundle/components/ListTableItem.md +391 -0
  38. package/bundle/components/ModalWindow.md +427 -237
  39. package/bundle/components/MonthYearField.md +145 -109
  40. package/bundle/components/Notification.md +625 -155
  41. package/bundle/components/Overlay.md +271 -170
  42. package/bundle/components/Page.md +284 -186
  43. package/bundle/components/Pagination.md +206 -229
  44. package/bundle/components/Portal.md +131 -67
  45. package/bundle/components/Row.md +171 -69
  46. package/bundle/components/Text.md +320 -0
  47. package/bundle/components/TextField.md +364 -97
  48. package/bundle/components/Title.md +296 -0
  49. package/bundle/guides/codestyle.md +38 -11
  50. package/bundle/guides/commits.md +119 -26
  51. package/bundle/guides/context.md +34 -30
  52. package/bundle/guides/stories.md +110 -40
  53. package/bundle/guides/template.md +3 -7
  54. package/bundle/guides/tests.md +48 -2
  55. package/bundle/manifest.json +275 -30
  56. package/bundle/release-notes/1.26.0.mdx +33 -2
  57. package/bundle/release-notes/1.27.0.mdx +43 -0
  58. package/bundle/release-notes/1.28.0.mdx +25 -0
  59. package/bundle/release-notes/1.29.0.mdx +47 -0
  60. package/bundle/release-notes/1.30.0.mdx +56 -0
  61. package/bundle/release-notes/1.31.0.mdx +52 -0
  62. package/bundle/release-notes/1.32.0.mdx +35 -0
  63. package/bundle/release-notes/1.33.0.mdx +61 -0
  64. package/bundle/release-notes/1.34.0.mdx +35 -0
  65. package/bundle/release-notes/1.35.0.mdx +21 -0
  66. package/bundle/release-notes/1.35.1.mdx +11 -0
  67. package/bundle/release-notes/1.36.0.mdx +21 -0
  68. package/bundle/release-notes/1.37.0.mdx +71 -0
  69. package/bundle/release-notes/1.38.0.mdx +36 -0
  70. package/bundle/release-notes/1.39.0.mdx +66 -0
  71. package/bundle/release-notes/1.40.0.mdx +27 -0
  72. package/bundle/release-notes/1.41.0.mdx +7 -0
  73. package/package.json +1 -1
@@ -1,9 +1,7 @@
1
1
  ---
2
2
  component: List
3
- category: Data Display
4
- figma-node: ""
5
- figma-file: ""
6
- related: [ListMaster, Table, TreeView, Checkbox, LoaderScreen]
3
+ category: List
4
+ related: [ListSortable, ListItem, ListEmptyState, ListItemLoading, LoaderScreen]
7
5
  tokens: []
8
6
  stories: stories/List/List.stories.tsx
9
7
  version: "1.0"
@@ -13,235 +11,270 @@ version: "1.0"
13
11
 
14
12
  ## Назначение
15
13
 
16
- Семейство компонентов для списков (`<ul>` / `<li>`) с набором слотов
17
- для строк: контент, правый контролы-блок, хвосты слева/справа, чекбокс-
18
- выбор, вложенный DropdownButton, drag-and-drop. Поддерживает loading-overlay
19
- на уровне списка.
14
+ Контейнер `<ul>` для набора элементов списка. Управляет состоянием загрузки
15
+ через prop `loading` (показывает overlay-спиннер `LoaderScreen` поверх
16
+ содержимого).
20
17
 
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
- ```
18
+ Используй `List` когда: нужен стандартный список с произвольным контентом.
19
+ Используй `ListSortable` когда: нужна сортировка элементов через drag-and-drop.
52
20
 
53
21
  ---
54
22
 
55
23
  ## Варианты и props
56
24
 
57
- ### List — `IListProps extends HTMLAttributes<HTMLUListElement>`
25
+ ### Опциональные props
58
26
 
59
- | Prop | Тип | Default | Описание |
27
+ | Prop | Тип | По умолчанию | Описание |
60
28
  |---|---|---|---|
61
- | `loading` | `boolean` | — | Накладывает `LoaderScreen type="middle"` поверх списка. |
62
- | `...ulAttributes` | — | — | Стандартные `<ul>`-атрибуты. |
29
+ | `loading` | `boolean` | `false` | Показывает `LoaderScreen` поверх содержимого. Используется при обновлении текущего списка новыми данными (например, после применения фильтра) |
30
+ | `children` | `React.ReactNode` | — | Элементы списка (обычно `<ListItem>` или `<ListTableItem>`) |
31
+ | `...HTMLUListElementAttributes` | — | — | Все стандартные атрибуты `<ul>` |
63
32
 
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
33
+ ---
81
34
 
82
- | Компонент | Назначение |
83
- |---|---|
84
- | `ListItemControls` | Контейнер для кнопок справа. |
85
- | `ListItemControlsButton` | Стилизованная кнопка-иконка. |
86
- | `ListItemControlsButtonDropdown` | Кнопка + вложенное выпадающее меню. |
35
+ ## Инварианты
87
36
 
88
- ### ListItemTailLeft / ListItemTailRight — хвосты (иконки, аватары, статусы) по краям строки.
37
+ - **`forwardRef`** — обязателен, target — `HTMLUListElement`. Не убирать.
38
+ - **Корневой элемент `<ul>`** — не менять. Семантика списка важна для accessibility.
39
+ - `loading` — overlay-стратегия (содержимое не размонтируется), не путать с `ListItemLoading` (отдельный элемент-спиннер для пагинации).
40
+ - **`.listLoaderScreen` — `z-index: @z-index-step`**. Понижает z-index относительно глобального `@z-index-loader-screen` у `LoaderScreen`, чтобы спиннер перекрывал только содержимое списка, а не `LightBox` / `ModalWindow`. Не повышать без проверки наложения на модальные слои.
89
41
 
90
- ### ListItemTable
42
+ ---
91
43
 
92
- Табличный вид строки: внутренняя сетка для выравнивания колонок между строками.
44
+ ## Связанные компоненты
93
45
 
94
- ### ListItemLoading — скелетон-строка, показывается во время загрузки.
46
+ - `ListItem` — базовый элемент списка, провайдер контекста для `selected`-состояния.
47
+ - `ListSortable` — тот же `<ul>`, но с DnD-обвязкой через `@dnd-kit`.
48
+ - `ListEmptyState` — отображается вместо элементов, когда данных нет (например, после фильтра).
49
+ - `ListItemLoading` — элемент-спиннер для подгрузки следующей страницы (пагинация).
50
+ - `LoaderScreen` — используется внутри `List` для состояния `loading`.
95
51
 
96
- ### ListEmptyState — рендерится, когда `items.length === 0`, параметры для заголовка/описания/иконки.
52
+ ---
97
53
 
98
- ### ListSortable<T extends { id: string }> — `IListSortableProps<T>`
54
+ ## Stories
99
55
 
100
- | Prop | Тип | Обязателен | Описание |
101
- |---|---|---|---|
102
- | `items` | `T[]` | Да | Массив элементов, у каждого должно быть поле `id`. |
103
- | `onItemsChange` | `(items: T[]) => void` | Да | Callback после перестановки. |
104
- | `...IListProps` | — | — | Проброс на `<List>`. |
56
+ Основные истории: `stories/List/List.stories.tsx`
57
+ Файлы примеров: `stories/List/examples/List/`
105
58
 
106
- - Обёртка над `@dnd-kit/core` + `@dnd-kit/sortable`.
107
- - Modifiers: `restrictToParentElement` + `restrictToVerticalAxis` — нельзя перетащить в другой родитель или по горизонтали.
108
- - Внутри использует `AdvancedMouseSensor` / `AdvancedTouchSensor` — расширенные sensor'ы.
59
+ | Story | Example file | Что демонстрирует |
60
+ |---|---|---|
61
+ | `Playground` | `Playground.tsx` | Интерактивный контроль `loading` |
62
+ | `Default` | `Default.tsx` | Базовый список |
63
+ | `Loading` | `Loading.tsx` | Состояние загрузки (overlay) |
64
+ | `EmptyState` | `EmptyState.tsx` | Использование `ListEmptyState` |
65
+ | `Virtualized` | `Virtualized.tsx` | Виртуализированный список (большие наборы данных) |
66
+ | `Sortable` | `Sortable.tsx` | DnD-сортировка через `ListSortable` + `ListSortableItem` |
67
+ | `WithActionItems` | `WithActionItems.tsx` | Список из кликабельных строк `ListActionItem` |
68
+ | `SortableWithInteractiveElements` | `SortableWithInteractiveElements.tsx` | DnD с интерактивными элементами внутри строк (`data-draggable="false"`) |
109
69
 
110
- ### ListSortableItem — `IListSortableItemProps extends Omit<LiHTMLAttributes, "children">`
70
+ ---
111
71
 
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 }`. |
72
+ ## История изменений
117
73
 
118
- - Использует `useSortable` из dnd-kit, применяет `transform` стиль.
119
- - Render-props позволяют **селективно назначать drag handle** (через `listeners` + `setActivatorNodeRef`), а не делать перетаскиваемым всю строку.
120
- - `ListSortableItem.Target` (Object.assign) — подкомпонент `ListSortableItemTarget`.
74
+ | Дата | Изменение |
75
+ |---|---|
76
+ | 2026-04-29 | Создан документ |
77
+ | 2026-06-17 | `List.module.less`: локальный `z-index: @z-index-step` на `.listLoaderScreen` — исправлено перекрытие `LoaderScreen` поверх `LightBox` / `ModalWindow` |
121
78
 
122
79
  ---
123
80
 
124
- ## Ключевые особенности реализации
81
+ ## Примеры использования
125
82
 
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
- ```
83
+ ### Default
134
84
 
135
- - Любой потомок с `data-draggable="false"` **блокирует инициализацию drag'а**. Используется для кнопок/ссылок внутри строки — иначе клик по ним случайно запускает перетаскивание.
136
- - `MouseSensor`: игнорирует правую кнопку (`button === 2`).
137
- - `TouchSensor`: игнорирует мультитач (`touches.length > 1`).
138
-
139
- ### `ListItemContext` — связь выбора и стилей сиблингов
85
+ `stories/List/examples/List/Default.tsx`
140
86
 
141
87
  ```tsx
142
- <ListItemContext.Provider value={{ selected, setSelected }}>
143
- <li>{children}</li>
144
- </ListItemContext.Provider>
88
+ import React from "react";
89
+ import { List, ListItem } from "@sberbusiness/triplex-next";
90
+
91
+ export const Default = () => (
92
+ <div style={{ width: "500px" }}>
93
+ <List>
94
+ <ListItem>Элемент списка</ListItem>
95
+ <ListItem>Элемент списка</ListItem>
96
+ <ListItem>Элемент списка</ListItem>
97
+ </List>
98
+ </div>
99
+ );
145
100
  ```
146
101
 
147
- - `ListItemSelectable` пишет `selected` в контекст через `useEffect`.
148
- - Другие подкомпоненты (например, `ListItemContent`) могут подписаться и изменить стиль.
149
- - **Паттерн "выбор известен всей строке"** — без prop-drilling.
102
+ ### Loading
150
103
 
151
- ### `loading` на уровне `List`
104
+ `stories/List/examples/List/Loading.tsx`
152
105
 
153
106
  ```tsx
154
- <ul>
155
- {children}
156
- {loading ? <LoaderScreen type="middle" /> : null}
157
- </ul>
107
+ import React from "react";
108
+ import { List, ListItem } from "@sberbusiness/triplex-next";
109
+
110
+ export const Loading = () => (
111
+ <div style={{ width: "150px" }}>
112
+ <List loading>
113
+ <ListItem>Элемент списка</ListItem>
114
+ <ListItem>Элемент списка</ListItem>
115
+ <ListItem>Элемент списка</ListItem>
116
+ <ListItem>Элемент списка</ListItem>
117
+ <ListItem>Элемент списка</ListItem>
118
+ <ListItem>Элемент списка</ListItem>
119
+ </List>
120
+ </div>
121
+ );
158
122
  ```
159
123
 
160
- LoaderScreen рендерится **внутри `<ul>`**, но позиционируется абсолютно (overlay). Структурно `<ul>` содержит `<li>` и невалидный для семантики элемент — это сознательный trade-off в пользу простоты позиционирования.
161
-
162
- ### `data-tx={process.env.npm_package_version}` на `<ul>` и `<li>`
163
-
164
- Версия библиотеки в DOM для отладки. Сохранять при изменениях.
124
+ ### EmptyState
165
125
 
166
- ### ListSortable — vertical-axis only
126
+ `stories/List/examples/List/EmptyState.tsx`
167
127
 
168
- ```ts
169
- modifiers: [restrictToParentElement, restrictToVerticalAxis]
128
+ ```tsx
129
+ import React from "react";
130
+ import {
131
+ Button,
132
+ EButtonTheme,
133
+ EComponentSize,
134
+ EFontWeightTitle,
135
+ ETextSize,
136
+ ETitleSize,
137
+ Gap,
138
+ ListEmptyState,
139
+ Text,
140
+ Title,
141
+ } from "@sberbusiness/triplex-next";
142
+ import { EmptytableSysIcon96 } from "@sberbusiness/icons-next";
143
+
144
+ export const EmptyState = () => (
145
+ <ListEmptyState>
146
+ <div style={{ display: "flex", justifyContent: "center" }}>
147
+ <EmptytableSysIcon96 />
148
+ </div>
149
+
150
+ <Gap size={16} />
151
+
152
+ <Title size={ETitleSize.H3} weight={EFontWeightTitle.REGULAR}>
153
+ Title text
154
+ </Title>
155
+
156
+ <Gap size={8} />
157
+
158
+ <Text size={ETextSize.B3}>Нет данных, но можно предложить какие-то действия для заполнения таблицы.</Text>
159
+
160
+ <Gap size={24} />
161
+
162
+ <Button theme={EButtonTheme.SECONDARY} size={EComponentSize.SM}>
163
+ Button Name
164
+ </Button>
165
+ <Button theme={EButtonTheme.GENERAL} size={EComponentSize.SM}>
166
+ Button Name
167
+ </Button>
168
+ </ListEmptyState>
169
+ );
170
170
  ```
171
171
 
172
- - Список только вертикальный. Для горизонтальных списков — нужен отдельный компонент (сейчас не реализован).
172
+ ### Virtualized
173
173
 
174
- ### ListSortableItem render-props
174
+ `stories/List/examples/List/Virtualized.tsx`
175
175
 
176
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>
177
+ import React from "react";
178
+ import { FixedSizeList } from "react-window";
179
+ import { List, ListItem } from "@sberbusiness/triplex-next";
180
+
181
+ const itemData = Array.from({ length: 100 }).map((_, index) => `List item ${index}`);
182
+
183
+ export const Virtualized = () => (
184
+ <FixedSizeList
185
+ itemData={itemData}
186
+ itemCount={itemData.length}
187
+ itemSize={20}
188
+ width="100%"
189
+ height={200}
190
+ innerElementType={List}
191
+ >
192
+ {({ data, index, style }) => <ListItem style={style}>{data[index]}</ListItem>}
193
+ </FixedSizeList>
194
+ );
187
195
  ```
188
196
 
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`).
197
+ ### Sortable
220
198
 
221
- ---
199
+ `stories/List/examples/List/Sortable.tsx`
222
200
 
223
- ## Связанные компоненты
201
+ ```tsx
202
+ import React, { useState } from "react";
203
+ import { ListSortable, ListSortableItem } from "@sberbusiness/triplex-next";
204
+
205
+ export const Sortable = () => {
206
+ const [items, setItems] = useState(
207
+ Array.from({ length: 10 }, (_, index) => ({ id: `list-sortable-item-0-${index}`, index })),
208
+ );
209
+
210
+ return (
211
+ <ListSortable items={items} onItemsChange={setItems}>
212
+ {items.map(({ id, index }) => (
213
+ <ListSortableItem key={id} id={id}>
214
+ {({ listeners, dragging, setActivatorNodeRef }) => (
215
+ <ListSortableItem.Target {...listeners} dragging={dragging} ref={setActivatorNodeRef}>
216
+ List item {index}
217
+ </ListSortableItem.Target>
218
+ )}
219
+ </ListSortableItem>
220
+ ))}
221
+ </ListSortable>
222
+ );
223
+ };
224
+ ```
224
225
 
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-инфраструктура.
226
+ ### WithActionItems
231
227
 
232
- ---
228
+ `stories/List/examples/List/WithActionItems.tsx`
233
229
 
234
- ## Stories
230
+ ```tsx
231
+ import React from "react";
232
+ import { List, ListActionItem } from "@sberbusiness/triplex-next";
233
+
234
+ export const WithActionItems = () => (
235
+ <div style={{ maxWidth: "500px" }}>
236
+ <List>
237
+ {Array.from({ length: 5 }, (_, index) => (
238
+ <ListActionItem
239
+ key={index}
240
+ listItemContentProps={{ onClick: () => alert(`Клик по элементу списка ${index}`) }}
241
+ >
242
+ Элемент списка {index}
243
+ </ListActionItem>
244
+ ))}
245
+ </List>
246
+ </div>
247
+ );
248
+ ```
235
249
 
236
- - `stories/List/List.stories.tsx`
237
- - `stories/List/ListItem.stories.tsx`
238
- - `stories/List/ListItemControls.stories.tsx`
239
- - Примеры: `stories/List/examples/`
250
+ ### SortableWithInteractiveElements
240
251
 
241
- ---
252
+ `stories/List/examples/List/SortableWithInteractiveElements.tsx`
242
253
 
243
- ## История изменений
254
+ ```tsx
255
+ import React, { useState } from "react";
256
+ import { Checkbox, ListSortable, ListSortableItem, ListSortableItemControls } from "@sberbusiness/triplex-next";
257
+
258
+ export const SortableWithInteractiveElements = () => {
259
+ const [items, setItems] = useState(
260
+ Array.from({ length: 10 }, (_, index) => ({ id: `list-sortable-item-1-${index}`, index })),
261
+ );
262
+
263
+ return (
264
+ <ListSortable items={items} onItemsChange={setItems}>
265
+ {items.map(({ id, index }) => (
266
+ <ListSortableItem key={id} id={id}>
267
+ {({ listeners, dragging, setActivatorNodeRef }) => (
268
+ <ListSortableItem.Target {...listeners} dragging={dragging} ref={setActivatorNodeRef}>
269
+ <ListSortableItemControls>
270
+ <Checkbox>List item {index}</Checkbox>
271
+ </ListSortableItemControls>
272
+ </ListSortableItem.Target>
273
+ )}
274
+ </ListSortableItem>
275
+ ))}
276
+ </ListSortable>
277
+ );
278
+ };
279
+ ```
244
280
 
245
- | Дата | Изменение |
246
- |---|---|
247
- | 2026-04-18 | Создан документ |
@@ -0,0 +1,157 @@
1
+ ---
2
+ component: ListActionItem
3
+ category: List
4
+ related: [ListItem, ListItemContent, ListSortableItem]
5
+ tokens:
6
+ - --triplex-next-ListItem-Background_Hover
7
+ - --triplex-next-ListItem-BorderColor_Focus
8
+ stories: stories/List/ListItem.stories.tsx
9
+ version: "1.0"
10
+ ---
11
+
12
+ # ListActionItem
13
+
14
+ ## Назначение
15
+
16
+ Самодостаточный интерактивный элемент списка. Сам рендерит `ListItem` и
17
+ `ListItemContent` (по аналогии с тем, как `ListSortableItem` рендерит внутри себя
18
+ `ListItem`), поэтому потребителю не нужно вручную составлять композицию. Добавляет
19
+ hover-подсветку, focus-обводку по Tab и активацию по клику / Enter / Space.
20
+
21
+ Ключевая идея: интерактивные атрибуты (`role`, `tabIndex`, обработчики) и класс
22
+ `styles.listActionItem` навешиваются на сам `ListItemContent`, поэтому фоном
23
+ управляет один элемент — базовый фон приходит из `.listItemContent`, а hover-фон
24
+ из `.listActionItem:hover` на том же узле. Нет перекрытия фона между обёрткой и
25
+ контентом.
26
+
27
+ Используй `ListActionItem` когда: строка целиком кликабельна (навигация, открытие
28
+ деталей, выбор из списка без чекбокса). Не используй внутри `ListTableItem` —
29
+ там `onClickItem` вешается напрямую на `ListItemContent` без hover/focus/keyboard.
30
+
31
+ Распределение пропсов:
32
+
33
+ - **собственные пропсы `ListActionItem`** (`extends React.LiHTMLAttributes<HTMLLIElement>`)
34
+ уходят на корневой `<li>` (`ListItem`) — `className`, `style`, `id`, `data-*` строки;
35
+ - **`listItemContentProps`** — на интерактивный `ListItemContent`: `onClick`,
36
+ `onKeyDown`, переопределение `role` / `tabIndex`, `className` контента и т.д.
37
+
38
+ Типичная композиция:
39
+
40
+ ```tsx
41
+ <List>
42
+ <ListActionItem listItemContentProps={{ onClick: handleClick }}>Текст строки</ListActionItem>
43
+ </List>
44
+ ```
45
+
46
+ ---
47
+
48
+ ## Варианты и props
49
+
50
+ ### Опциональные props
51
+
52
+ | Prop | Тип | По умолчанию | Описание |
53
+ |---|---|---|---|
54
+ | `children` | `React.ReactNode` | — | Содержимое строки (рендерится внутри `ListItemContent`) |
55
+ | `listItemContentProps` | `React.HTMLAttributes<HTMLDivElement>` | — | Пропсы интерактивного слоя (`ListItemContent`): `onClick`, `onKeyDown`, `role`, `tabIndex`, `className` контента и т.д. |
56
+ | `className` | `string` | — | CSS-класс корневого `<li>` (`ListItem`) |
57
+ | `...HTMLLIAttributes` | — | — | Остальные атрибуты `<li>` спредятся на внутренний `ListItem` |
58
+
59
+ ### Пропсы внутри `listItemContentProps`
60
+
61
+ | Prop | Тип | По умолчанию | Описание |
62
+ |---|---|---|---|
63
+ | `onClick` | `(event: React.MouseEvent<HTMLDivElement>) => void` | — | Обработчик клика по всей строке |
64
+ | `onKeyDown` | `(event: React.KeyboardEvent<HTMLDivElement>) => void` | — | Вызывается до внутренней обработки Enter/Space |
65
+ | `className` | `string` | — | Дополнительный CSS-класс, мержится с `styles.listActionItem` на `ListItemContent` |
66
+ | `tabIndex` | `number` | `0` | Фокусируемость по Tab. Можно переопределить (например, `-1`) |
67
+ | `role` | `string` | `"button"` | ARIA-роль. Можно переопределить (например, `"link"`) |
68
+
69
+ ---
70
+
71
+ ## Дизайн-токены
72
+
73
+ ```text
74
+ --triplex-next-ListItem-Background_Hover // фон при hover (desktop)
75
+ --triplex-next-ListItem-BorderColor_Focus // цвет focus-обводки (:focus-visible)
76
+ ```
77
+
78
+ Класс `styles.listActionItem` навешивается на сам `ListItemContent`, поэтому весь
79
+ фон живёт на одном элементе: базовый фон и `selected`/`selectable` — из
80
+ `.listItemContent`, а hover-фон (`&:hover`) и focus-обводка (`&:focus-visible`) —
81
+ из `.listActionItem` на том же узле.
82
+
83
+ ---
84
+
85
+ ## Инварианты
86
+
87
+ - **`forwardRef`** — обязателен, target — `HTMLDivElement` (внутренний
88
+ `ListItemContent`). Не убирать.
89
+ - **Самодостаточная композиция** — компонент сам рендерит `ListItem` →
90
+ `ListItemContent` (как `ListSortableItem` рендерит `ListItem`). Использовать
91
+ прямо внутри `List`, не оборачивать вручную в `ListItem`/`ListItemContent`.
92
+ - **Интерактивность на `ListItemContent`** — `role`, `tabIndex`, обработчики и
93
+ класс `styles.listActionItem` навешиваются на сам `ListItemContent`, а не на
94
+ отдельную обёртку. Это то, что позволяет одному элементу управлять фоном. Не
95
+ вводить промежуточный `<div>` между `ListItemContent` и hover-фоном — вернётся
96
+ перекрытие слоёв.
97
+ - **Разделение пропсов** — собственные пропсы компонента (`extends
98
+ LiHTMLAttributes`) идут на корневой `ListItem` (`<li>`), интерактивные — только
99
+ через `listItemContentProps` на `ListItemContent`. Не спредить `...rest` на
100
+ `ListItemContent`: тогда пропсы `<li>` и контента снова смешаются.
101
+ - **`role="button"` и `tabIndex={0}`** — задаются по умолчанию, но переопределяются
102
+ через `listItemContentProps` (`{...restListItemContentProps}` идёт после дефолтов). Не
103
+ убирать дефолты без обновления тестов и a11y-контракта.
104
+ - **Клавиатура** — Enter и Space обрабатываются через `isKey` из
105
+ `@sberbusiness/triplex-next/utils/keyboard`; Space вызывает `preventDefault()`,
106
+ затем `event.currentTarget.click()`. Не заменять на прямое сравнение `event.key`.
107
+ - **`data-tx`** — атрибут версии пакета, не убирать.
108
+
109
+ ---
110
+
111
+ ## Accessibility
112
+
113
+ - Элемент объявлен как `role="button"` и попадает в tab-order (`tabIndex={0}`).
114
+ - Активация: клик мышью, Enter, Space (через программный `click()`).
115
+ - Focus-индикатор — `border-color` на `:focus-visible`, `outline: none`.
116
+ - **`onKeyDown` вызывается до** внутренней обработки Enter/Space.
117
+ - **Не вкладывай другие интерактивные элементы** (кнопки, ссылки, чекбоксы) внутрь
118
+ `ListActionItem` — получится nested interactive elements. Для выбора с чекбоксом
119
+ используй `ListItemSelectable` без `ListActionItem`; для клика по всей строке в
120
+ табличном списке — `ListTableItem` + `onClickItem`.
121
+ - Для навигации по ссылке предпочтительнее нативный `<a>` / роутер-линк, а не
122
+ `ListActionItem` с `role="link"` — компонент не даёт семантики ссылки из коробки.
123
+
124
+ ---
125
+
126
+ ## Связанные компоненты
127
+
128
+ - `ListItem` — `<li>`, который `ListActionItem` рендерит сам внутри себя.
129
+ - `ListItemContent` — слой с фоном и padding, который `ListActionItem` рендерит
130
+ сам и на который навешивает интерактивность. Именно он держит весь фон.
131
+ - `ListSortableItem` — образец паттерна: тоже самодостаточный компонент, рендерит
132
+ `ListItem` внутри себя.
133
+ - `ListItemSelectable` — альтернатива для multi-select с чекбоксом. Не комбинировать
134
+ с `ListActionItem` в одной строке без явной a11y-стратегии.
135
+ - `ListTableItem` — использует `onClickItem` на `ListItemContent`, не `ListActionItem`.
136
+ - `CardAction` — аналогичный паттерн интерактивной обёртки в семействе Card.
137
+
138
+ ---
139
+
140
+ ## Stories
141
+
142
+ Основные истории: `stories/List/ListItem.stories.tsx`
143
+ Файл примера: `stories/List/examples/ListItem/Action.tsx`
144
+
145
+ | Story | Example file | Что демонстрирует |
146
+ |---|---|---|
147
+ | `Action` | `Action.tsx` | `ListActionItem` напрямую внутри `List` |
148
+ | `WithActionItems` (в `List.stories.tsx`) | `examples/List/WithActionItems.tsx` | Список из нескольких кликабельных строк `ListActionItem` |
149
+
150
+ ---
151
+
152
+ ## История изменений
153
+
154
+ | Дата | Изменение |
155
+ |---|---|
156
+ | 2026-07-17 | Добавлена story `WithActionItems` в `List.stories.tsx` — список из нескольких `ListActionItem`. |
157
+ | 2026-07-16 | Создан компонент `ListActionItem` — самодостаточная интерактивная строка (hover, focus по Tab, `onClick` + Enter/Space). Сам рендерит `ListItem` + `ListItemContent`; интерактивность и весь фон живут на одном `ListItemContent`. Пропсы разделены: собственные — на `<li>`, интерактивные — через `listItemContentProps`. |