@sberbusiness/triplex-next-mcp-server 0.24.0-beta2 → 0.36.0-beta0
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 +10 -0
- package/bundle/components/BodyPage.md +186 -0
- package/bundle/components/Button.md +90 -85
- package/bundle/components/ButtonDropdown.md +28 -70
- package/bundle/components/ButtonIcon.md +35 -48
- package/bundle/components/Checkbox.md +43 -122
- package/bundle/components/CheckboxXGroup.md +109 -46
- package/bundle/components/CheckboxYGroup.md +77 -49
- package/bundle/components/CollapsibleTree.md +581 -0
- package/bundle/components/CollapsibleTreeExtended.md +418 -0
- package/bundle/components/EmptyView.md +14 -15
- package/bundle/components/FooterPage.md +203 -0
- package/bundle/components/Gap.md +115 -57
- package/bundle/components/HeaderPage.md +226 -0
- package/bundle/components/HelpBox.md +45 -84
- package/bundle/components/ImageGallery.md +423 -0
- package/bundle/components/ImageGalleryExtended.md +606 -0
- package/bundle/components/List.md +194 -186
- package/bundle/components/ListItem.md +251 -0
- package/bundle/components/ListItemControlsButton.md +103 -0
- package/bundle/components/ListItemControlsButtonDropdown.md +107 -0
- package/bundle/components/ListItemSelectable.md +99 -0
- package/bundle/components/ListItemTable.md +390 -0
- package/bundle/components/ListMaster.md +877 -156
- package/bundle/components/ListMasterFooter.md +80 -0
- package/bundle/components/ListMasterHeader.md +86 -0
- package/bundle/components/ListSortable.md +153 -0
- package/bundle/components/ListSortableItem.md +175 -0
- package/bundle/components/ModalWindow.md +427 -237
- package/bundle/components/MonthYearField.md +145 -109
- package/bundle/components/Notification.md +625 -155
- package/bundle/components/Overlay.md +271 -170
- package/bundle/components/Page.md +283 -186
- package/bundle/components/Pagination.md +206 -229
- package/bundle/guides/codestyle.md +38 -11
- package/bundle/guides/commits.md +1 -2
- package/bundle/guides/context.md +34 -30
- package/bundle/guides/stories.md +110 -40
- package/bundle/guides/template.md +3 -7
- package/bundle/guides/tests.md +6 -2
- package/bundle/manifest.json +194 -24
- package/bundle/release-notes/1.26.0.mdx +33 -2
- package/bundle/release-notes/1.27.0.mdx +43 -0
- package/bundle/release-notes/1.28.0.mdx +25 -0
- package/bundle/release-notes/1.29.0.mdx +47 -0
- package/bundle/release-notes/1.30.0.mdx +56 -0
- package/bundle/release-notes/1.31.0.mdx +52 -0
- package/bundle/release-notes/1.32.0.mdx +35 -0
- package/bundle/release-notes/1.33.0.mdx +61 -0
- package/bundle/release-notes/1.34.0.mdx +35 -0
- package/bundle/release-notes/1.35.0.mdx +21 -0
- package/bundle/release-notes/1.35.1.mdx +11 -0
- package/bundle/release-notes/1.36.0.mdx +21 -0
- package/bundle/release-notes/1.37.0.mdx +30 -0
- package/package.json +1 -1
|
@@ -0,0 +1,418 @@
|
|
|
1
|
+
---
|
|
2
|
+
component: CollapsibleTreeExtended
|
|
3
|
+
category: CollapsibleTree
|
|
4
|
+
related: [CollapsibleTree, TreeView, AccordionBase]
|
|
5
|
+
tokens: []
|
|
6
|
+
stories: stories/CollapsibleTreeExtended/CollapsibleTreeExtended.stories.tsx
|
|
7
|
+
version: "1.0"
|
|
8
|
+
---
|
|
9
|
+
|
|
10
|
+
# CollapsibleTreeExtended
|
|
11
|
+
|
|
12
|
+
## Назначение
|
|
13
|
+
|
|
14
|
+
Низкоуровневое декларативное дерево с раскрывающимися узлами. Тонкая обёртка
|
|
15
|
+
над `TreeView`, дополняющая каждую ноду accordion-логикой (анимация раскрытия,
|
|
16
|
+
controlled/uncontrolled-состояние) через `CollapsibleTreeExtended.Node`.
|
|
17
|
+
|
|
18
|
+
Узел рендерится через две render-функции:
|
|
19
|
+
|
|
20
|
+
- `renderHeader` — видимая часть узла, всегда отрисовывается в `<li>`. Кнопка с
|
|
21
|
+
шевроном, текст, иконка-toggle, бэйдж — любая разметка.
|
|
22
|
+
- `renderBody` — раскрываемое содержимое (обычно дочерние ноды, но может быть
|
|
23
|
+
любым контентом).
|
|
24
|
+
|
|
25
|
+
Используй `CollapsibleTreeExtended` когда: нужен нестандартный UI узла (своя
|
|
26
|
+
иконка раскрытия, кликабельный header целиком, layout с несколькими колонками,
|
|
27
|
+
полный контроль состояния раскрытия из родителя).
|
|
28
|
+
|
|
29
|
+
Используй `CollapsibleTree` когда: достаточно типового UI «папка/файл» с
|
|
30
|
+
шевроном, hover и focus-visible. `CollapsibleTree` построен поверх
|
|
31
|
+
`CollapsibleTreeExtended` и закрывает большинство случаев.
|
|
32
|
+
|
|
33
|
+
---
|
|
34
|
+
|
|
35
|
+
## Варианты и props
|
|
36
|
+
|
|
37
|
+
### `CollapsibleTreeExtended`
|
|
38
|
+
|
|
39
|
+
FC-обёртка над `TreeView` без собственных props.
|
|
40
|
+
|
|
41
|
+
| Prop | Тип | По умолчанию | Описание |
|
|
42
|
+
|---|---|---|---|
|
|
43
|
+
| `children` | `React.ReactNode` | — | Дочерние `CollapsibleTreeExtended.Node` |
|
|
44
|
+
| `...HTMLUListElementAttributes` | — | — | Все стандартные атрибуты `<ul>` (наследуются от `TreeView`) |
|
|
45
|
+
|
|
46
|
+
Доступ к ноде: `CollapsibleTreeExtended.Node` (статическое свойство, ссылка на
|
|
47
|
+
`CollapsibleTreeExtendedNode`).
|
|
48
|
+
|
|
49
|
+
### `CollapsibleTreeExtended.Node` (`CollapsibleTreeExtendedNode`)
|
|
50
|
+
|
|
51
|
+
Обязательные props:
|
|
52
|
+
|
|
53
|
+
| Prop | Тип | Описание |
|
|
54
|
+
|---|---|---|
|
|
55
|
+
| `id` | `string` | Уникальный идентификатор ноды (наследуется из `TreeView.Node`) |
|
|
56
|
+
| `renderHeader` | `(props: ICollapsibleTreeExtendedNodeHeaderProvideProps) => React.ReactNode` | Видимая часть ноды |
|
|
57
|
+
| `renderBody` | `(props: ICollapsibleTreeExtendedNodeBodyProvideProps) => React.ReactNode` | Раскрываемое содержимое |
|
|
58
|
+
|
|
59
|
+
Опциональные props:
|
|
60
|
+
|
|
61
|
+
| Prop | Тип | По умолчанию | Описание |
|
|
62
|
+
|---|---|---|---|
|
|
63
|
+
| `opened` | `boolean` | `undefined` | Контролируемое состояние раскрытия. Если задан — нода controlled |
|
|
64
|
+
| `toggle` | `(opened: boolean) => void` | — | Запрос на смену `opened` в controlled-режиме. Вызывается с новым значением |
|
|
65
|
+
| `onToggle` | `(opened: boolean) => void` | — | Колбэк по факту завершения анимации раскрытия/сворачивания |
|
|
66
|
+
| `children` | `never` | — | Запрещён — содержимое задаётся через `renderBody` |
|
|
67
|
+
|
|
68
|
+
### Provide-props в render-функциях
|
|
69
|
+
|
|
70
|
+
`renderHeader` получает `ICollapsibleTreeExtendedNodeHeaderProvideProps`:
|
|
71
|
+
|
|
72
|
+
| Поле | Описание |
|
|
73
|
+
|---|---|
|
|
74
|
+
| `opened` | Текущее состояние раскрытия |
|
|
75
|
+
| `toggle(opened)` | Функция смены `opened` (no-op при совпадении значений) |
|
|
76
|
+
| `animating` | Идёт ли анимация раскрытия (из `AccordionBase`) |
|
|
77
|
+
| `activeNode` | Текущая активная нода дерева (из `TreeView`) |
|
|
78
|
+
| `hasChildNodes` | Есть ли у текущей ноды дочерние |
|
|
79
|
+
| `isLastNode` | Последняя ли нода на своём уровне |
|
|
80
|
+
|
|
81
|
+
`renderBody` получает `ICollapsibleTreeExtendedNodeBodyProvideProps` — тот же
|
|
82
|
+
набор без `isLastNode`.
|
|
83
|
+
|
|
84
|
+
### Controlled vs uncontrolled
|
|
85
|
+
|
|
86
|
+
Режим вычисляется на **каждом** рендере по `props.opened !== undefined` —
|
|
87
|
+
переход controlled ↔ uncontrolled во время жизни ноды поддерживается.
|
|
88
|
+
|
|
89
|
+
- **Controlled** (`opened` задан): состояние читается из props, `toggle(next)`
|
|
90
|
+
делегируется в `props.toggle`. `setOpenedNode` (внутреннее состояние
|
|
91
|
+
`TreeView`) не вызывается.
|
|
92
|
+
- **Uncontrolled** (`opened === undefined`): состояние хранит `TreeView`,
|
|
93
|
+
`toggle(next)` вызывает `setOpenedNode`. `props.toggle` не дергается.
|
|
94
|
+
- В обоих режимах `onToggle` срабатывает после завершения анимации
|
|
95
|
+
раскрытия/сворачивания.
|
|
96
|
+
|
|
97
|
+
---
|
|
98
|
+
|
|
99
|
+
## Дизайн-токены
|
|
100
|
+
|
|
101
|
+
Собственных CSS-переменных компонент не определяет. Используется
|
|
102
|
+
`visibility: hidden` для скрытого содержимого (чтобы Tab не уходил в свёрнутую
|
|
103
|
+
ветку) — без токенов.
|
|
104
|
+
|
|
105
|
+
Анимация раскрытия и визуальные токены наследуются от `AccordionBase` и
|
|
106
|
+
`TreeView`.
|
|
107
|
+
|
|
108
|
+
---
|
|
109
|
+
|
|
110
|
+
## Инварианты
|
|
111
|
+
|
|
112
|
+
- **`CollapsibleTreeExtended.Node` — class-компонент.** Намеренное решение —
|
|
113
|
+
не переписывать на FC + hooks без согласования. `forwardRef` к нему не
|
|
114
|
+
применяется (классы используют обычный `ref`).
|
|
115
|
+
- **`CollapsibleTreeExtended` — FC без `forwardRef`.** Это тонкая обёртка
|
|
116
|
+
`props → <TreeView>`, своего рендера нет; `ref` не имеет смысла, потому
|
|
117
|
+
что под капотом — class-компонент `TreeView` с собственной обработкой
|
|
118
|
+
фокуса.
|
|
119
|
+
- **Доступ к ноде только через `CollapsibleTreeExtended.Node`** (compound
|
|
120
|
+
pattern). Прямой импорт `CollapsibleTreeExtendedNode` из barrel разрешён,
|
|
121
|
+
но в потребительском коде предпочтительнее `.Node`.
|
|
122
|
+
- **Публичный API** — имена `ICollapsibleTreeExtendedNodeProps`,
|
|
123
|
+
`ICollapsibleTreeExtendedNodeBodyProvideProps`,
|
|
124
|
+
`ICollapsibleTreeExtendedNodeHeaderProvideProps`, `ICollapsibleTreeExtendedProps`,
|
|
125
|
+
`ICollapsibleTreeExtendedSFC` и сигнатуры `toggle`, `onToggle`,
|
|
126
|
+
`renderHeader`, `renderBody` — нельзя менять без breaking. Зависимости:
|
|
127
|
+
`CollapsibleTree`, `CollapsibleTreeBranchNode`, `CollapsibleTreeLeafNode`.
|
|
128
|
+
- **`children: never` на `Node`** — содержимое задаётся только через
|
|
129
|
+
`renderBody`. Передача `children` запрещена типом.
|
|
130
|
+
- **`opened` — controlled-флаг.** Считать `controlled = props.opened !== undefined`
|
|
131
|
+
на каждом рендере. Не возвращать хранение режима в state (был баг, починен
|
|
132
|
+
в 2026-05).
|
|
133
|
+
- **Корневой DOM** — `<ul>` от `TreeView`, узлы — `<li>` (см. accessibility).
|
|
134
|
+
|
|
135
|
+
---
|
|
136
|
+
|
|
137
|
+
## Accessibility
|
|
138
|
+
|
|
139
|
+
- Семантика дерева задаётся `TreeView` — корневой `<ul>`, узлы `<li>`, ветки
|
|
140
|
+
оборачиваются в `<TreeView.Group>` (доп. `<ul role="group">`) при наличии
|
|
141
|
+
детей.
|
|
142
|
+
- Клавиатурная навигация по дереву (стрелки, Home/End, раскрытие/сворачивание)
|
|
143
|
+
обрабатывается на уровне `TreeView` — `CollapsibleTreeExtended` ничего не
|
|
144
|
+
добавляет сверху.
|
|
145
|
+
- `aria-expanded`, `aria-label` на toggle-кнопке и любые другие aria-атрибуты
|
|
146
|
+
узла **передаёт потребитель** в разметке `renderHeader`. Компонент не
|
|
147
|
+
хардкодит текст и aria-атрибуты — библиотека мультиязычная и `renderHeader`
|
|
148
|
+
по контракту произвольный.
|
|
149
|
+
- Свёрнутое `renderBody` получает `visibility: hidden` — фокус Tab не уходит
|
|
150
|
+
в скрытое содержимое, но reflow сохраняется (важно для анимации).
|
|
151
|
+
- Активная нода (`activeNode` в provide-props) — это нода в фокусе клавиатуры
|
|
152
|
+
`TreeView`, не selected-состояние. Визуальную подсветку реализует
|
|
153
|
+
потребитель через `renderHeader`.
|
|
154
|
+
|
|
155
|
+
---
|
|
156
|
+
|
|
157
|
+
## Связанные компоненты
|
|
158
|
+
|
|
159
|
+
- `CollapsibleTree` (`src/components/CollapsibleTree`) — готовая обёртка
|
|
160
|
+
верхнего уровня с типовым UI узла (шеврон, hover, focus-visible). Если
|
|
161
|
+
нестандартный UI узла не нужен — используй её.
|
|
162
|
+
- `TreeView` (`src/components/TreeView`) — нижний слой: семантика дерева,
|
|
163
|
+
абстрактное дерево нод, фокус, активная нода, клавиатурная навигация.
|
|
164
|
+
- `AccordionBase` (`src/components/AccordionBase`) — анимация раскрытия и
|
|
165
|
+
`onToggle` после завершения анимации. Используется внутри каждой ноды.
|
|
166
|
+
|
|
167
|
+
---
|
|
168
|
+
|
|
169
|
+
## Stories
|
|
170
|
+
|
|
171
|
+
Основные истории: `stories/CollapsibleTreeExtended/CollapsibleTreeExtended.stories.tsx`
|
|
172
|
+
Файлы примеров: `stories/CollapsibleTreeExtended/examples/`
|
|
173
|
+
|
|
174
|
+
| Story | Example file | Что демонстрирует |
|
|
175
|
+
|---|---|---|
|
|
176
|
+
| `Playground` | `PlaygroundExample.tsx` | Интерактивный контроль `defaultOpened` и `withCustomToggle` |
|
|
177
|
+
| `Default` | `DefaultExample.tsx` | Типовой UI: кнопка с шевроном для веток, плоский `div` для листьев (по сути воспроизводит `CollapsibleTree`) |
|
|
178
|
+
| `Custom toggle (Plus/Minus)` | `CustomToggleExample.tsx` | Альтернативная иконка раскрытия — Plus/Minus вместо шеврона |
|
|
179
|
+
| `VisualTests` | `VisualTestsExample.tsx` | Три состояния в одном кадре: collapsed, expanded, custom toggle — для baseline-скриншотов |
|
|
180
|
+
|
|
181
|
+
---
|
|
182
|
+
|
|
183
|
+
## История изменений
|
|
184
|
+
|
|
185
|
+
| Дата | Изменение |
|
|
186
|
+
|---|---|
|
|
187
|
+
| 2026-05-15 | Создан документ. Зафиксирован фикс controlled-инварианта (режим вычисляется на каждом рендере) и пополнение stories (`Playground`, `VisualTests`) |
|
|
188
|
+
| 2026-07-06 | В story-примерах шеврон открытого узла теперь указывает вверх (`rotate(-90deg)`), а не вниз — синхронно с `CollapsibleTree` |
|
|
189
|
+
|
|
190
|
+
---
|
|
191
|
+
|
|
192
|
+
## Примеры использования
|
|
193
|
+
|
|
194
|
+
### Default
|
|
195
|
+
|
|
196
|
+
`stories/CollapsibleTreeExtended/examples/DefaultExample.tsx`
|
|
197
|
+
|
|
198
|
+
```tsx
|
|
199
|
+
import React from "react";
|
|
200
|
+
import clsx from "clsx";
|
|
201
|
+
import { CaretrightStrokeSrvIcon24 } from "@sberbusiness/icons-next";
|
|
202
|
+
import { CollapsibleTreeExtended, EFontWeightText, ETextSize, Text } from "@sberbusiness/triplex-next";
|
|
203
|
+
import "./DefaultExample.less";
|
|
204
|
+
|
|
205
|
+
interface ITreeNodeData {
|
|
206
|
+
id: string;
|
|
207
|
+
label: React.ReactNode;
|
|
208
|
+
defaultOpened?: boolean;
|
|
209
|
+
children?: ITreeNodeData[];
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
const nodes: ITreeNodeData[] = [
|
|
213
|
+
{
|
|
214
|
+
id: "folder-1",
|
|
215
|
+
label: "Folder text",
|
|
216
|
+
children: [{ id: "file-1", label: "File text" }],
|
|
217
|
+
},
|
|
218
|
+
{
|
|
219
|
+
id: "folder-2",
|
|
220
|
+
label: "Folder text",
|
|
221
|
+
defaultOpened: true,
|
|
222
|
+
children: [
|
|
223
|
+
{
|
|
224
|
+
id: "folder-2-1",
|
|
225
|
+
label: "Folder text",
|
|
226
|
+
defaultOpened: true,
|
|
227
|
+
children: [
|
|
228
|
+
{ id: "file-2-1-1", label: "File text" },
|
|
229
|
+
{ id: "file-2-1-2", label: "File text" },
|
|
230
|
+
],
|
|
231
|
+
},
|
|
232
|
+
],
|
|
233
|
+
},
|
|
234
|
+
{
|
|
235
|
+
id: "folder-3",
|
|
236
|
+
label: "Folder text",
|
|
237
|
+
defaultOpened: true,
|
|
238
|
+
children: [{ id: "file-3", label: "File text" }],
|
|
239
|
+
},
|
|
240
|
+
];
|
|
241
|
+
|
|
242
|
+
interface ITreeNodeProps {
|
|
243
|
+
node: ITreeNodeData;
|
|
244
|
+
prevNode?: ITreeNodeData;
|
|
245
|
+
nextNode?: ITreeNodeData;
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
const TreeNode: React.FC<ITreeNodeProps> = ({ node, prevNode, nextNode }) => {
|
|
249
|
+
const hasChildren = Boolean(node.children?.length);
|
|
250
|
+
const [opened, setOpened] = React.useState(node.defaultOpened ?? false);
|
|
251
|
+
|
|
252
|
+
return (
|
|
253
|
+
<CollapsibleTreeExtended.Node
|
|
254
|
+
id={node.id}
|
|
255
|
+
opened={opened}
|
|
256
|
+
toggle={setOpened}
|
|
257
|
+
prevNodeId={prevNode?.id}
|
|
258
|
+
nextNodeId={nextNode?.id}
|
|
259
|
+
renderHeader={({ hasChildNodes }) =>
|
|
260
|
+
hasChildNodes ? (
|
|
261
|
+
<button
|
|
262
|
+
type="button"
|
|
263
|
+
className="collapsible-tree-extended-example-header"
|
|
264
|
+
onClick={() => setOpened(!opened)}
|
|
265
|
+
aria-expanded={opened}
|
|
266
|
+
>
|
|
267
|
+
<CaretrightStrokeSrvIcon24
|
|
268
|
+
paletteIndex={5}
|
|
269
|
+
className={clsx("collapsible-tree-extended-example-chevron", { opened })}
|
|
270
|
+
aria-hidden
|
|
271
|
+
/>
|
|
272
|
+
<Text size={ETextSize.B1} tag="span" weight={EFontWeightText.SEMIBOLD}>
|
|
273
|
+
{node.label}
|
|
274
|
+
</Text>
|
|
275
|
+
</button>
|
|
276
|
+
) : (
|
|
277
|
+
<div className="collapsible-tree-extended-example-leaf">
|
|
278
|
+
<Text size={ETextSize.B1} tag="span">
|
|
279
|
+
{node.label}
|
|
280
|
+
</Text>
|
|
281
|
+
</div>
|
|
282
|
+
)
|
|
283
|
+
}
|
|
284
|
+
renderBody={() =>
|
|
285
|
+
hasChildren
|
|
286
|
+
? node.children!.map((child, index) => (
|
|
287
|
+
<TreeNode
|
|
288
|
+
key={child.id}
|
|
289
|
+
node={child}
|
|
290
|
+
prevNode={node.children![index - 1]}
|
|
291
|
+
nextNode={node.children![index + 1]}
|
|
292
|
+
/>
|
|
293
|
+
))
|
|
294
|
+
: null
|
|
295
|
+
}
|
|
296
|
+
/>
|
|
297
|
+
);
|
|
298
|
+
};
|
|
299
|
+
|
|
300
|
+
export const DefaultExample = () => (
|
|
301
|
+
<div className="collapsible-tree-extended-example" style={{ maxWidth: 420 }}>
|
|
302
|
+
<CollapsibleTreeExtended>
|
|
303
|
+
{nodes.map((node, index) => (
|
|
304
|
+
<TreeNode key={node.id} node={node} prevNode={nodes[index - 1]} nextNode={nodes[index + 1]} />
|
|
305
|
+
))}
|
|
306
|
+
</CollapsibleTreeExtended>
|
|
307
|
+
</div>
|
|
308
|
+
);
|
|
309
|
+
```
|
|
310
|
+
|
|
311
|
+
### Custom toggle (Plus/Minus)
|
|
312
|
+
|
|
313
|
+
`stories/CollapsibleTreeExtended/examples/CustomToggleExample.tsx`
|
|
314
|
+
|
|
315
|
+
```tsx
|
|
316
|
+
import React from "react";
|
|
317
|
+
import { MinusStrokeSrvIcon24, PlusStrokeSrvIcon24 } from "@sberbusiness/icons-next";
|
|
318
|
+
import { CollapsibleTreeExtended, EFontWeightText, ETextSize, Text } from "@sberbusiness/triplex-next";
|
|
319
|
+
import "./CustomToggleExample.less";
|
|
320
|
+
|
|
321
|
+
interface ITreeNodeData {
|
|
322
|
+
id: string;
|
|
323
|
+
label: React.ReactNode;
|
|
324
|
+
defaultOpened?: boolean;
|
|
325
|
+
children?: ITreeNodeData[];
|
|
326
|
+
}
|
|
327
|
+
|
|
328
|
+
const nodes: ITreeNodeData[] = [
|
|
329
|
+
{
|
|
330
|
+
id: "section-1",
|
|
331
|
+
label: "Section 1",
|
|
332
|
+
defaultOpened: true,
|
|
333
|
+
children: [
|
|
334
|
+
{ id: "item-1-1", label: "Item 1.1" },
|
|
335
|
+
{ id: "item-1-2", label: "Item 1.2" },
|
|
336
|
+
],
|
|
337
|
+
},
|
|
338
|
+
{
|
|
339
|
+
id: "section-2",
|
|
340
|
+
label: "Section 2",
|
|
341
|
+
children: [
|
|
342
|
+
{ id: "item-2-1", label: "Item 2.1" },
|
|
343
|
+
{ id: "item-2-2", label: "Item 2.2" },
|
|
344
|
+
],
|
|
345
|
+
},
|
|
346
|
+
];
|
|
347
|
+
|
|
348
|
+
interface ITreeNodeProps {
|
|
349
|
+
node: ITreeNodeData;
|
|
350
|
+
prevNode?: ITreeNodeData;
|
|
351
|
+
nextNode?: ITreeNodeData;
|
|
352
|
+
}
|
|
353
|
+
|
|
354
|
+
const TreeNode: React.FC<ITreeNodeProps> = ({ node, prevNode, nextNode }) => {
|
|
355
|
+
const hasChildren = Boolean(node.children?.length);
|
|
356
|
+
const [opened, setOpened] = React.useState(node.defaultOpened ?? false);
|
|
357
|
+
|
|
358
|
+
return (
|
|
359
|
+
<CollapsibleTreeExtended.Node
|
|
360
|
+
id={node.id}
|
|
361
|
+
opened={opened}
|
|
362
|
+
toggle={setOpened}
|
|
363
|
+
prevNodeId={prevNode?.id}
|
|
364
|
+
nextNodeId={nextNode?.id}
|
|
365
|
+
renderHeader={({ hasChildNodes }) =>
|
|
366
|
+
hasChildNodes ? (
|
|
367
|
+
<button
|
|
368
|
+
type="button"
|
|
369
|
+
className="custom-toggle-example-header"
|
|
370
|
+
onClick={() => setOpened(!opened)}
|
|
371
|
+
aria-expanded={opened}
|
|
372
|
+
>
|
|
373
|
+
<span className="custom-toggle-example-icon" aria-hidden>
|
|
374
|
+
{opened ? (
|
|
375
|
+
<MinusStrokeSrvIcon24 paletteIndex={5} />
|
|
376
|
+
) : (
|
|
377
|
+
<PlusStrokeSrvIcon24 paletteIndex={5} />
|
|
378
|
+
)}
|
|
379
|
+
</span>
|
|
380
|
+
<Text size={ETextSize.B1} tag="span" weight={EFontWeightText.SEMIBOLD}>
|
|
381
|
+
{node.label}
|
|
382
|
+
</Text>
|
|
383
|
+
</button>
|
|
384
|
+
) : (
|
|
385
|
+
<div className="custom-toggle-example-leaf">
|
|
386
|
+
<Text size={ETextSize.B1} tag="span">
|
|
387
|
+
{node.label}
|
|
388
|
+
</Text>
|
|
389
|
+
</div>
|
|
390
|
+
)
|
|
391
|
+
}
|
|
392
|
+
renderBody={() =>
|
|
393
|
+
hasChildren
|
|
394
|
+
? node.children!.map((child, index) => (
|
|
395
|
+
<TreeNode
|
|
396
|
+
key={child.id}
|
|
397
|
+
node={child}
|
|
398
|
+
prevNode={node.children![index - 1]}
|
|
399
|
+
nextNode={node.children![index + 1]}
|
|
400
|
+
/>
|
|
401
|
+
))
|
|
402
|
+
: null
|
|
403
|
+
}
|
|
404
|
+
/>
|
|
405
|
+
);
|
|
406
|
+
};
|
|
407
|
+
|
|
408
|
+
export const CustomToggleExample = () => (
|
|
409
|
+
<div className="custom-toggle-example" style={{ maxWidth: 420 }}>
|
|
410
|
+
<CollapsibleTreeExtended>
|
|
411
|
+
{nodes.map((node, index) => (
|
|
412
|
+
<TreeNode key={node.id} node={node} prevNode={nodes[index - 1]} nextNode={nodes[index + 1]} />
|
|
413
|
+
))}
|
|
414
|
+
</CollapsibleTreeExtended>
|
|
415
|
+
</div>
|
|
416
|
+
);
|
|
417
|
+
```
|
|
418
|
+
|
|
@@ -1,8 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
component: EmptyView
|
|
3
3
|
category: Feedback
|
|
4
|
-
figma-node: "80-48"
|
|
5
|
-
figma-file: "https://www.figma.com/design/lpkxmWZ512mG2OJyDvGtHD/-W--LIB-DSN-01-Components-DCB-Next-v.1.3.0"
|
|
6
4
|
related: [Button]
|
|
7
5
|
tokens:
|
|
8
6
|
- --triplex-next-Typography-Primary_Color
|
|
@@ -28,22 +26,22 @@ version: "1.0"
|
|
|
28
26
|
|
|
29
27
|
| Prop | Тип | Описание |
|
|
30
28
|
|---|---|---|
|
|
31
|
-
| `size` | `EEmptyViewSize` | Размер: SM / MD |
|
|
29
|
+
| `size` | `EEmptyViewSize` | Размер: `SM` / `MD` |
|
|
32
30
|
|
|
33
31
|
### Опциональные props
|
|
34
32
|
|
|
35
33
|
| Prop | Тип | По умолчанию | Описание |
|
|
36
34
|
|---|---|---|---|
|
|
37
|
-
| `icon` | `ReactNode` | — | Иконка (96px для SM
|
|
35
|
+
| `icon` | `ReactNode` | — | Иконка (96px для `SM`, 128px для `MD`) |
|
|
38
36
|
| `title` | `ReactNode` | — | Заголовок |
|
|
39
37
|
| `description` | `ReactNode` | — | Текст описания |
|
|
40
38
|
| `caption` | `ReactNode` | — | Подпись |
|
|
41
39
|
| `buttons` | `ReactNode` | — | Кнопки действий |
|
|
42
40
|
|
|
43
|
-
###
|
|
41
|
+
### Ограничения по темам
|
|
44
42
|
|
|
45
|
-
- Если `title`
|
|
46
|
-
- Если `title` не
|
|
43
|
+
- Если `title` задан — описание отображается вторичным цветом (Secondary).
|
|
44
|
+
- Если `title` не задан — описание становится основным текстом (Primary).
|
|
47
45
|
|
|
48
46
|
---
|
|
49
47
|
|
|
@@ -58,22 +56,22 @@ version: "1.0"
|
|
|
58
56
|
|
|
59
57
|
## Инварианты
|
|
60
58
|
|
|
61
|
-
- `forwardRef` на компоненте — не
|
|
62
|
-
- `EEmptyViewSize` — публичный enum, менять значения
|
|
63
|
-
- CSS-классы `emptyView`, `hasTitle` используются в
|
|
59
|
+
- `forwardRef` на компоненте — не убирать.
|
|
60
|
+
- `EEmptyViewSize` — публичный enum, менять значения нельзя.
|
|
61
|
+
- CSS-классы `emptyView`, `hasTitle` используются в тестах.
|
|
64
62
|
|
|
65
63
|
---
|
|
66
64
|
|
|
67
65
|
## Accessibility
|
|
68
66
|
|
|
69
|
-
- Компонент не требует обязательных aria
|
|
70
|
-
- Потребитель может передать `role`, `aria-label` и другие атрибуты через spread
|
|
67
|
+
- Компонент не требует обязательных aria-атрибутов.
|
|
68
|
+
- Потребитель может передать `role`, `aria-label` и другие атрибуты через spread.
|
|
71
69
|
|
|
72
70
|
---
|
|
73
71
|
|
|
74
72
|
## Связанные компоненты
|
|
75
73
|
|
|
76
|
-
- `Button` — используется в `buttons` для
|
|
74
|
+
- `Button` — используется в `buttons` для действий.
|
|
77
75
|
|
|
78
76
|
---
|
|
79
77
|
|
|
@@ -86,7 +84,7 @@ version: "1.0"
|
|
|
86
84
|
|---|---|---|
|
|
87
85
|
| `Playground` | `PlaygroundExample.tsx` | Интерактивный контроль всех props |
|
|
88
86
|
| `Default` | `DefaultExample.tsx` | Минимальное состояние с title, description и caption |
|
|
89
|
-
| `Sizes` | `SizesExample.tsx` | Размеры SM / MD |
|
|
87
|
+
| `Sizes` | `SizesExample.tsx` | Размеры `SM` / `MD` |
|
|
90
88
|
| `WithoutTitle` | `WithoutTitleExample.tsx` | Вариант без заголовка |
|
|
91
89
|
| `WithButtons` | `WithButtonsExample.tsx` | Вариант с кнопками действий |
|
|
92
90
|
|
|
@@ -96,7 +94,8 @@ version: "1.0"
|
|
|
96
94
|
|
|
97
95
|
| Дата | Изменение |
|
|
98
96
|
|---|---|
|
|
99
|
-
| 2026-04-09 | Создан компонент и
|
|
97
|
+
| 2026-04-09 | Создан компонент и документ. |
|
|
98
|
+
| 2026-04-27 | Приведён в соответствие с `docs/ai/template-ai.md`: добавлена колонка `Example file`, переименована подсекция «Поведение описания» в «Ограничения по темам». |
|
|
100
99
|
|
|
101
100
|
---
|
|
102
101
|
|