@sberbusiness/triplex-next-mcp-server 1.50.0-beta → 1.53.1

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 (74) hide show
  1. package/README.md +1 -0
  2. package/bundle/components/AlertProcess.md +6 -3
  3. package/bundle/components/AlertProcessSpoiler.md +216 -0
  4. package/bundle/components/Badge.md +4 -3
  5. package/bundle/components/ButtonDropdown.md +5 -3
  6. package/bundle/components/Carousel.md +671 -0
  7. package/bundle/components/CarouselExtended.md +7 -5
  8. package/bundle/components/CarouselIndicators.md +666 -0
  9. package/bundle/components/CarouselItem.md +579 -0
  10. package/bundle/components/CarouselNextButton.md +633 -0
  11. package/bundle/components/CarouselPrevButton.md +633 -0
  12. package/bundle/components/CarouselViewport.md +631 -0
  13. package/bundle/components/CheckboxTreeExtended.md +21 -7
  14. package/bundle/components/CheckboxTreeExtendedCheckbox.md +591 -0
  15. package/bundle/components/CheckboxTreeExtendedNode.md +577 -0
  16. package/bundle/components/Chip.md +1 -0
  17. package/bundle/components/ChipDatePicker.md +332 -0
  18. package/bundle/components/ChipDatePickerTarget.md +316 -0
  19. package/bundle/components/ChipMonthYearPicker.md +4 -3
  20. package/bundle/components/ChipMonthYearPickerTarget.md +318 -0
  21. package/bundle/components/ChipMultiselect.md +63 -73
  22. package/bundle/components/ChipSelect.md +385 -0
  23. package/bundle/components/ChipSelectTarget.md +388 -0
  24. package/bundle/components/ChipSuggest.md +10 -16
  25. package/bundle/components/ChipSuggestTarget.md +506 -0
  26. package/bundle/components/CollapsibleTree.md +2 -1
  27. package/bundle/components/CollapsibleTreeExtended.md +2 -1
  28. package/bundle/components/DateField.md +4 -2
  29. package/bundle/components/DatePickerExtended.md +16 -14
  30. package/bundle/components/DateRange.md +17 -10
  31. package/bundle/components/Dropdown.md +36 -63
  32. package/bundle/components/EmptyView.md +9 -4
  33. package/bundle/components/ExpandAnimation.md +9 -5
  34. package/bundle/components/FormField.md +1 -1
  35. package/bundle/components/ImageGallery.md +19 -52
  36. package/bundle/components/ImageGalleryExtended.md +123 -139
  37. package/bundle/components/ListItemControlsButtonDropdown.md +3 -2
  38. package/bundle/components/LoaderSmall.md +5 -4
  39. package/bundle/components/MonthYearRange.md +334 -0
  40. package/bundle/components/MultiselectField.md +81 -143
  41. package/bundle/components/Notification.md +2 -1
  42. package/bundle/components/NotificationGrouped.md +174 -0
  43. package/bundle/components/NumberField.md +259 -116
  44. package/bundle/components/OrderedList.md +149 -112
  45. package/bundle/components/Overlay.md +3 -2
  46. package/bundle/components/PageIndicators.md +306 -0
  47. package/bundle/components/Pagination.md +81 -3
  48. package/bundle/components/PaginationExtended.md +160 -0
  49. package/bundle/components/Portal.md +5 -2
  50. package/bundle/components/Puller.md +375 -0
  51. package/bundle/components/SMSField.md +277 -102
  52. package/bundle/components/SelectExtendedField.md +11 -7
  53. package/bundle/components/SelectField.md +7 -4
  54. package/bundle/components/SmartSpot.md +259 -0
  55. package/bundle/components/Step.md +2 -1
  56. package/bundle/components/Stepper.md +7 -7
  57. package/bundle/components/Suggest.md +7 -6
  58. package/bundle/components/SuggestField.md +8 -6
  59. package/bundle/components/TableBasic.md +12 -14
  60. package/bundle/components/Tooltip.md +9 -8
  61. package/bundle/guides/commits.md +4 -1
  62. package/bundle/guides/context.md +5 -5
  63. package/bundle/guides/stories.md +29 -21
  64. package/bundle/manifest.json +145 -13
  65. package/bundle/release-notes/1.50.0.mdx +4 -0
  66. package/bundle/release-notes/1.51.0.mdx +57 -0
  67. package/bundle/release-notes/1.52.0.mdx +55 -0
  68. package/bundle/release-notes/1.53.0.mdx +60 -0
  69. package/bundle/release-notes/1.54.0.mdx +45 -0
  70. package/bundle/release-notes/1.55.0.mdx +7 -0
  71. package/bundle/tokens.json +377 -222
  72. package/dist/index.js +39 -1
  73. package/dist/index.js.map +1 -1
  74. package/package.json +1 -1
@@ -0,0 +1,577 @@
1
+ ---
2
+ component: CheckboxTreeExtendedNode
3
+ category: Inputs
4
+ related: [CheckboxTreeExtended, CollapsibleTreeExtendedNode]
5
+ tokens:
6
+ - Checkbox.BorderColor_Focus
7
+ stories: stories/CheckboxTreeExtended/CheckboxTreeExtended.stories.tsx
8
+ version: "1.0"
9
+ ---
10
+
11
+ # CheckboxTreeExtendedNode
12
+
13
+ ## Назначение
14
+
15
+ Узел декларативного дерева чекбоксов. Используется как `CheckboxTreeExtended.Node`
16
+ внутри `CheckboxTreeExtended`: формирует заголовок с чекбоксом и отступы вложенных
17
+ веток, передаёт состояние активности в render-функцию `checkbox`. Семантику узла и
18
+ регистрацию в дереве получает через `CollapsibleTreeExtended.Node` от `TreeView`.
19
+
20
+ Используй когда: дерево собирается вручную, а состояние выбора и связь родителя с
21
+ потомками контролирует потребитель. Узел сам не хранит `checked` / `bulk` и не
22
+ пересчитывает выбор дочерних чекбоксов.
23
+
24
+ Не используй когда: нужен готовый каскадный выбор по массиву данных — для этого
25
+ есть `CheckboxTree`; нужен сворачиваемый узел с произвольным заголовком — используй
26
+ `CollapsibleTreeExtended.Node`.
27
+
28
+ Основное обращение в копируемых примерах — через родителя:
29
+
30
+ ```tsx
31
+ import { CheckboxTreeExtended } from "@sberbusiness/triplex-next";
32
+
33
+ <CheckboxTreeExtended>
34
+ <CheckboxTreeExtended.Node
35
+ id="item"
36
+ checkbox={(props) => <CheckboxTreeExtended.Checkbox {...props}>Значение</CheckboxTreeExtended.Checkbox>}
37
+ />
38
+ </CheckboxTreeExtended>;
39
+ ```
40
+
41
+ Именованные экспорты `CheckboxTreeExtendedNode`, `ICheckboxTreeExtendedNodeProps`
42
+ и `ICheckboxTreeExtendedCheckboxProvideProps` также доступны через публичный
43
+ barrel семейства и корень пакета. Тип props нужен, например, для собственной
44
+ обёртки узла; deep import из `components/CheckboxTreeExtended/components/` не нужен.
45
+
46
+ ---
47
+
48
+ ## Варианты и props
49
+
50
+ ### Идентификация и порядок
51
+
52
+ | Prop | Назначение и ограничения |
53
+ |---|---|
54
+ | `id` | Обязательный идентификатор, уникальный в пределах дерева. Используется и как DOM `id`, и для регистрации в `TreeView`; сохраняй его стабильным в течение жизни узла |
55
+ | `prevNodeId` / `nextNodeId` | Унаследованы через `ICollapsibleTreeExtendedNodeProps` от `ITreeViewNodeProps`. Задают позицию при регистрации: после / перед уже зарегистрированным соседом того же уровня. Без найденного соседа сохраняется порядок рендера; перестановка DOM сама по себе порядок зарегистрированных узлов не меняет |
56
+
57
+ ### Содержимое и состояние
58
+
59
+ | Prop | Назначение и ограничения |
60
+ |---|---|
61
+ | `checkbox` | Обязательная render-функция, возвращающая `JSX.Element`. Полученные `active` / `opened` передавай в `CheckboxTreeExtended.Checkbox`, чтобы сохранить передачу фокуса при навигации |
62
+ | `children` | Вложенные узлы `CheckboxTreeExtended.Node`, обычный `React.ReactNode`. Заголовок и тело формируются самим компонентом; `renderHeader` / `renderBody` исключены из публичного типа |
63
+ | `opened` | Унаследован от нижнего слоя, но при текущем `isStaticCheckboxTreeExtended = true` игнорируется: узел всегда раскрыт, включая `opened={false}` |
64
+ | `toggle` / `onToggle` | Унаследованы от `CollapsibleTreeExtended.Node` и передаются ему. В статичном дереве UI раскрытия отсутствует, `checkbox` не получает `toggle`; изменение выбора чекбокса не вызывает эти callback'и. В нижнем слое `toggle` запрашивает состояние раскрытия, `onToggle` сообщает его после анимации |
65
+
66
+ Аргумент `checkbox` описан публичным `ICheckboxTreeExtendedCheckboxProvideProps`:
67
+
68
+ | Поле | Текущее поведение |
69
+ |---|---|
70
+ | `active` | Флаг активности текущего узла из `TreeView`. Передаётся и веткам, и листьям, поскольку в статичном режиме фокус получает чекбокс |
71
+ | `opened` | Всегда `true` в статичном режиме |
72
+
73
+ `checked`, `bulk`, `disabled`, подпись и обработчик `onChange` задаются на чекбоксе
74
+ внутри render-функции. У Node отдельного состояния выбора или prop `size` нет:
75
+ размер заголовка читается из `CheckboxTreeExtendedContext`, который заполняет
76
+ родитель. По умолчанию это `MD`, в мобильном представлении родитель всегда
77
+ передаёт `MD`.
78
+
79
+ ### DOM-атрибуты и события
80
+
81
+ `className` объединяется через `clsx` с `checkboxTreeExtendedNode` на корневом
82
+ `<li>`. Остальные HTML-атрибуты, включая `aria-*`, `data-*`, `onClick`, `onFocus`
83
+ и `onBlur`, уходят через нижние слои на этот же элемент. `TreeView` сохраняет
84
+ `role="treeitem"` и `aria-expanded` за собой; пользовательские `onFocus` / `onBlur`
85
+ вызываются после внутренней смены активности с исходным событием.
86
+
87
+ ---
88
+
89
+ ## Дизайн-токены
90
+
91
+ Переопределяются через `ThemeProvider` (prop `tokens`) — см. `ThemeProvider-ai.md` →
92
+ «Как переопределять токены». Значение по умолчанию —
93
+ `src/components/DesignTokens/components/Checkbox.ts`.
94
+
95
+ Собственных токенов у узла нет. Единственный токен в общем модуле
96
+ `styles/CheckboxTreeExtended.module.less` используется внутренней стрелкой
97
+ раскрытия, сохранённой для будущего сворачиваемого режима:
98
+
99
+ ```text
100
+ Checkbox.BorderColor_Focus
101
+ ```
102
+
103
+ Пока дерево статично, стрелка не рендерится. Цвета и состояния самого чекбокса
104
+ определяет `Checkbox`. Заголовок узла задаёт вертикальный отступ 8px для `SM` и
105
+ 12px для `MD` / `LG`; отступ вложенной ветки — соответственно 16px и 24px.
106
+
107
+ ---
108
+
109
+ ## Инварианты
110
+
111
+ - **Узел остаётся статичным.** `isStaticCheckboxTreeExtended = true`
112
+ принудительно задаёт `opened={true}` нижнему слою и `opened: true` в
113
+ render-функции; стрелка не рендерится даже у веток с детьми. Обе ветки флага
114
+ оставлены намеренно до появления стандартного сворачиваемого дерева.
115
+ - **Компонент — `React.FC` без внешнего ref.** У Node нет `forwardRef` и ref на
116
+ DOM в текущем API; не добавляй его попутно при рефакторинге. `forwardRef`
117
+ родительского `CheckboxTreeExtended` сохраняется и отдаёт корневой `<ul>`.
118
+ - **Разметка узла — `<li role="treeitem">`.** Регистрация, активность,
119
+ `aria-expanded` и порядок обхода принадлежат `TreeView`. Не переносить эту
120
+ логику в Node и не менять нижние слои ради его документации.
121
+ - **Render-prop получает только `active` / `opened`.** Состояние выбора и
122
+ обработка `onChange` остаются у потребителя; `children` передаются в тело
123
+ узла, а не в заголовок.
124
+ - **Размер берётся из контекста.** Класс заголовка и отступ ветки должны
125
+ соответствовать размеру дерева; `className` потребителя не затирает
126
+ собственный класс узла.
127
+ - **`mousedown` отменяется только на самом контейнере заголовка**, когда
128
+ `event.target === event.currentTarget`. События от label, input и любого
129
+ другого потомка не должны отменяться этим guard.
130
+ - **Публичные имена и barrel-экспорты сохраняются:** `CheckboxTreeExtended.Node`,
131
+ `CheckboxTreeExtendedNode`, оба интерфейса Node, а также
132
+ `displayName = "CheckboxTreeExtendedNode"`.
133
+
134
+ ---
135
+
136
+ ## Accessibility
137
+
138
+ - Через `TreeView.Node` узел получает `role="treeitem"`, `aria-expanded="true"`
139
+ и `tabIndex`: у первого узла первого уровня `0`, у остальных `-1`, если
140
+ потребитель не переопределил `tabIndex` сам.
141
+ Вложенные зарегистрированные узлы оборачиваются в `<ul role="group">`.
142
+ - Фокус внутри узла делает его активным. `TreeView` обрабатывает
143
+ `ArrowDown` / `ArrowUp` и перемещает активность по видимым узлам с переходом
144
+ через границы дерева; `active` из render-функции позволяет
145
+ `CheckboxTreeExtended.Checkbox` перевести DOM-фокус на свой input.
146
+ `Home` / `End` здесь не реализованы; стрелки раскрытия отсутствуют в
147
+ статичном режиме.
148
+ - Пользовательский чекбокс должен обеспечивать доступную подпись. Для
149
+ `CheckboxTreeExtended.Checkbox` это `children` либо `aria-label`, заданный
150
+ потребителем. Node не добавляет текст и не назначает состояние `aria-checked`
151
+ самому `treeitem` — состояние выбора принадлежит вложенному чекбоксу.
152
+ - `mousedown` на свободном месте заголовка отменяется, чтобы клик сбоку не
153
+ переводил фокус на чекбокс. Взаимодействие с самим чекбоксом сохраняется.
154
+ - Известное ограничение нижнего слоя: на первом рендере возможен React warning
155
+ о вложенных `<li>` до регистрации дочерних узлов и появления `TreeView.Group`.
156
+ Это общее поведение семейства, описанное в `CheckboxTreeExtended-ai.md`.
157
+
158
+ ---
159
+
160
+ ## Связанные компоненты
161
+
162
+ - `CheckboxTreeExtended` — родитель, предоставляет размер через контекст и
163
+ семантику дерева через `CollapsibleTreeExtended`; Node доступен его статическим
164
+ полем `.Node`.
165
+ - `CollapsibleTreeExtendedNode` (`CollapsibleTreeExtended.Node`) — нижний слой,
166
+ который Node рендерит и от которого наследует props. Соединяет заголовок и
167
+ тело с `TreeView.Node` и accordion-логикой.
168
+ - `CheckboxTreeExtended.Checkbox` — получатель `active` / `opened` из
169
+ render-функции. Добавляет оформление дерева к `Checkbox` и переводит на
170
+ себя фокус при активности узла.
171
+ - `CheckboxTreeExtendedArrow` — внутренняя стрелка; в текущем статичном режиме
172
+ отсутствует. Из публичного barrel не экспортируется.
173
+
174
+ ---
175
+
176
+ ## Stories
177
+
178
+ Отдельного набора stories у Node нет: существующие истории родителя показывают
179
+ статичное дерево, вложенность и все размеры. Node используется через
180
+ `CheckboxTreeExtended`, импортированный из корня пакета.
181
+
182
+ Основные истории: `stories/CheckboxTreeExtended/CheckboxTreeExtended.stories.tsx`
183
+ Файлы примеров: `stories/CheckboxTreeExtended/examples/`
184
+
185
+ | Story | Example file | Что демонстрирует |
186
+ |---|---|---|
187
+ | `Playground` | `PlaygroundExample.tsx` | Размер дерева с вложенными узлами |
188
+ | `Default` | `DefaultExample.tsx` | Рекурсивная сборка узлов, render-функция чекбокса и состояние выбора у потребителя |
189
+ | `Sizes` | `SizesExample.tsx` | Заголовки и отступы узлов для SM / MD / LG |
190
+
191
+ Интеграция: `stories/Chips/ChipMultiselect.stories.tsx`
192
+ Файлы примеров: `stories/Chips/examples/ChipMultiselect/`
193
+
194
+ | Story | Example file | Что демонстрирует |
195
+ |---|---|---|
196
+ | `WithCheckboxTree` | `WithCheckboxTreeExample.tsx` | Узлы в dropdown мультиселекта, фильтрация и каскадный выбор на стороне потребителя |
197
+
198
+ ---
199
+
200
+ ## История изменений
201
+
202
+ | Дата | Изменение |
203
+ |---|---|
204
+ | 2026-10-21 | Создан документ. Зафиксированы статичный режим, render-prop, наследуемые props, публичные экспорты и передача активности из TreeView |
205
+
206
+ ---
207
+
208
+ ## Примеры использования
209
+
210
+ ### Default
211
+
212
+ `stories/CheckboxTreeExtended/examples/DefaultExample.tsx`
213
+
214
+ ```tsx
215
+ import React from "react";
216
+ import { CheckboxTreeExtended, EComponentSize } from "@sberbusiness/triplex-next";
217
+
218
+ type CheckboxNode = { id: string; label: string; checked: boolean; bulk?: boolean; children?: CheckboxNode[] };
219
+
220
+ const INITIAL: CheckboxNode[] = [
221
+ { id: "1", label: "Группа 1", checked: false, children: [{ id: "1-1", label: "Значение 1-1", checked: false }] },
222
+ { id: "2", label: "Группа 2", checked: false },
223
+ ];
224
+
225
+ export const DefaultExample = ({ size = EComponentSize.MD }: { size?: EComponentSize }) => {
226
+ const [checkboxes, setCheckboxes] = React.useState<CheckboxNode[]>(INITIAL);
227
+
228
+ const onChange = (target: CheckboxNode) => (event: React.ChangeEvent<HTMLInputElement>) => {
229
+ target.checked = event.target.checked;
230
+ setCheckboxes([...checkboxes]);
231
+ };
232
+
233
+ const renderNode = (node: CheckboxNode) => (
234
+ <CheckboxTreeExtended.Node
235
+ id={node.id}
236
+ key={node.id}
237
+ checkbox={(props) => (
238
+ <CheckboxTreeExtended.Checkbox
239
+ {...props}
240
+ onChange={onChange(node)}
241
+ bulk={node.bulk}
242
+ checked={node.checked}
243
+ >
244
+ {node.label}
245
+ </CheckboxTreeExtended.Checkbox>
246
+ )}
247
+ >
248
+ {node.children?.map((child) => renderNode(child))}
249
+ </CheckboxTreeExtended.Node>
250
+ );
251
+
252
+ return <CheckboxTreeExtended size={size}>{checkboxes.map((node) => renderNode(node))}</CheckboxTreeExtended>;
253
+ };
254
+ ```
255
+
256
+ ### Sizes
257
+
258
+ `stories/CheckboxTreeExtended/examples/SizesExample.tsx`
259
+
260
+ ```tsx
261
+ import React from "react";
262
+ import { CheckboxTreeExtended, Col, EComponentSize, Row } from "@sberbusiness/triplex-next";
263
+
264
+ type CheckboxNode = { id: string; label: string; checked: boolean; bulk?: boolean; children?: CheckboxNode[] };
265
+
266
+ const INITIAL: CheckboxNode[] = [
267
+ { id: "1", label: "Группа 1", checked: false, children: [{ id: "1-1", label: "Значение 1-1", checked: false }] },
268
+ { id: "2", label: "Группа 2", checked: false },
269
+ ];
270
+
271
+ const TreeBySize = ({ size = EComponentSize.MD }: { size?: EComponentSize }) => {
272
+ const [checkboxes, setCheckboxes] = React.useState<CheckboxNode[]>(INITIAL);
273
+
274
+ const onChange = (target: CheckboxNode) => (event: React.ChangeEvent<HTMLInputElement>) => {
275
+ target.checked = event.target.checked;
276
+ setCheckboxes([...checkboxes]);
277
+ };
278
+
279
+ const renderNode = (node: CheckboxNode) => (
280
+ <CheckboxTreeExtended.Node
281
+ id={node.id}
282
+ key={node.id}
283
+ checkbox={(props) => (
284
+ <CheckboxTreeExtended.Checkbox
285
+ {...props}
286
+ onChange={onChange(node)}
287
+ bulk={node.bulk}
288
+ checked={node.checked}
289
+ >
290
+ {node.label}
291
+ </CheckboxTreeExtended.Checkbox>
292
+ )}
293
+ >
294
+ {node.children?.map((child) => renderNode(child))}
295
+ </CheckboxTreeExtended.Node>
296
+ );
297
+
298
+ return <CheckboxTreeExtended size={size}>{checkboxes.map((node) => renderNode(node))}</CheckboxTreeExtended>;
299
+ };
300
+
301
+ export const SizesExample = () => (
302
+ <Row>
303
+ <Col size={4}>
304
+ <div style={{ marginBottom: "8px", fontSize: "16px", fontWeight: "700" }}>SM</div>
305
+ <TreeBySize size={EComponentSize.SM} />
306
+ </Col>
307
+ <Col size={4}>
308
+ <div style={{ marginBottom: "8px", fontSize: "16px", fontWeight: "700" }}>MD</div>
309
+ <TreeBySize size={EComponentSize.MD} />
310
+ </Col>
311
+ <Col size={4}>
312
+ <div style={{ marginBottom: "8px", fontSize: "16px", fontWeight: "700" }}>LG</div>
313
+ <TreeBySize size={EComponentSize.LG} />
314
+ </Col>
315
+ </Row>
316
+ );
317
+ ```
318
+
319
+ ### WithCheckboxTree
320
+
321
+ `stories/Chips/examples/ChipMultiselect/WithCheckboxTreeExample.tsx`
322
+
323
+ ```tsx
324
+ import React, { useState } from "react";
325
+ import {
326
+ MultiselectField,
327
+ PullerHeader,
328
+ PullerInput,
329
+ PullerClose,
330
+ PullerBody,
331
+ PullerFooter,
332
+ Button,
333
+ EButtonTheme,
334
+ EComponentSize,
335
+ FormField,
336
+ FormFieldLabel,
337
+ FormFieldInput,
338
+ FormFieldPostfix,
339
+ FormFieldClear,
340
+ ChipMultiselect,
341
+ ETextSize,
342
+ Text,
343
+ CheckboxTreeExtended,
344
+ checkChildrenCheckboxes,
345
+ traverseCheckboxes,
346
+ checkParentCheckboxes,
347
+ ICheckboxTreeCheckboxData,
348
+ } from "@sberbusiness/triplex-next";
349
+
350
+ export const WithCheckboxTreeExample = () => {
351
+ const checkboxesInitial = [
352
+ {
353
+ bulk: false,
354
+ checked: false,
355
+ children: [
356
+ {
357
+ bulk: false,
358
+ checked: false,
359
+ children: [
360
+ {
361
+ checked: false,
362
+ id: "multiselect-option-1-1",
363
+ label: "Значение 1-1",
364
+ },
365
+ {
366
+ checked: false,
367
+ id: "multiselect-option-1-2",
368
+ label: "Значение 1-2",
369
+ },
370
+ {
371
+ checked: false,
372
+ id: "multiselect-option-1-3",
373
+ label: "Значение 1-3",
374
+ },
375
+ ],
376
+ id: "multiselect-option-1",
377
+ label: "Группа 1",
378
+ },
379
+ {
380
+ bulk: false,
381
+ checked: false,
382
+ children: [
383
+ {
384
+ checked: false,
385
+ id: "multiselect-option-2-1",
386
+ label: "Значение 2-1",
387
+ },
388
+ {
389
+ checked: false,
390
+ id: "multiselect-option-2-2",
391
+ label: "Значение 2-2",
392
+ },
393
+ ],
394
+ id: "multiselect-option-2",
395
+ label: "Группа 2",
396
+ },
397
+ {
398
+ checked: false,
399
+ id: "multiselect-option-3",
400
+ label: "Значение 3",
401
+ },
402
+ ],
403
+ id: "multiselect-option-0",
404
+ label: "Все",
405
+ },
406
+ ];
407
+
408
+ const [checkboxes, setCheckboxes] = useState(() => structuredClone(checkboxesInitial));
409
+ const [filter, setFilter] = useState("");
410
+ const [filteredCheckboxesId, setFilteredCheckboxesId] = useState<string[]>([]);
411
+
412
+ const handleChange = (checkbox: ICheckboxTreeCheckboxData) => (event: React.ChangeEvent<HTMLInputElement>) => {
413
+ checkbox.checked = checkbox.bulk ? true : event.target.checked;
414
+ checkChildrenCheckboxes(checkbox);
415
+ traverseCheckboxes(checkboxes, checkParentCheckboxes);
416
+ setCheckboxes([...checkboxes]);
417
+ };
418
+
419
+ const renderCheckboxNode = (checkbox: ICheckboxTreeCheckboxData) => {
420
+ if (filter && !filteredCheckboxesId.includes(checkbox.id)) return null;
421
+ return (
422
+ <CheckboxTreeExtended.Node
423
+ key={checkbox.id}
424
+ id={checkbox.id}
425
+ checkbox={(props) => (
426
+ <CheckboxTreeExtended.Checkbox
427
+ {...props}
428
+ bulk={checkbox.bulk}
429
+ checked={checkbox.checked}
430
+ onChange={handleChange(checkbox)}
431
+ >
432
+ {checkbox.label}
433
+ </CheckboxTreeExtended.Checkbox>
434
+ )}
435
+ >
436
+ {checkbox.children && checkbox.children.map((child) => renderCheckboxNode(child))}
437
+ </CheckboxTreeExtended.Node>
438
+ );
439
+ };
440
+
441
+ const renderDropdownContent = () => {
442
+ const renderCheckboxes = !filter || (filteredCheckboxesId.length && filter);
443
+ return renderCheckboxes ? (
444
+ <CheckboxTreeExtended>{checkboxes.map((checkbox) => renderCheckboxNode(checkbox))}</CheckboxTreeExtended>
445
+ ) : (
446
+ <div className="not-found md">
447
+ <Text size={ETextSize.B3}>Nothing was found</Text>
448
+ </div>
449
+ );
450
+ };
451
+
452
+ const unselectAll = () => {
453
+ const nextCheckboxes = [...checkboxes];
454
+ traverseCheckboxes(nextCheckboxes, (checkbox) => {
455
+ checkbox.checked = false;
456
+ checkbox.bulk = false;
457
+ });
458
+ setCheckboxes(nextCheckboxes);
459
+ };
460
+
461
+ const handleClickClearFilter = () => {
462
+ setFilter("");
463
+ setFilteredCheckboxesId([]);
464
+ unselectAll();
465
+ };
466
+
467
+ const handleClearInput = () => {
468
+ setFilter("");
469
+ };
470
+
471
+ const handleFilterChange = (event: React.ChangeEvent<HTMLInputElement>) => {
472
+ const { value } = event.target;
473
+ const filteredCheckboxes = [...checkboxes];
474
+ const filteredCheckboxesId = new Set<string>();
475
+
476
+ const addMatchedSubtree = (node: ICheckboxTreeCheckboxData) => {
477
+ filteredCheckboxesId.add(node.id);
478
+ node.children?.forEach(addMatchedSubtree);
479
+ };
480
+
481
+ const setFilteredValue = (checkbox: ICheckboxTreeCheckboxData) => {
482
+ if (
483
+ checkbox.label &&
484
+ typeof checkbox.label === "string" &&
485
+ checkbox.label.toLowerCase().includes(value.toLowerCase())
486
+ ) {
487
+ addMatchedSubtree(checkbox);
488
+ } else if (checkbox.children) {
489
+ const intersection = checkbox.children
490
+ .map((item: ICheckboxTreeCheckboxData) => item.id)
491
+ .filter((id) => Array.from(filteredCheckboxesId).includes(id));
492
+
493
+ if (intersection.length) filteredCheckboxesId.add(checkbox.id);
494
+ }
495
+ };
496
+
497
+ traverseCheckboxes(filteredCheckboxes, setFilteredValue);
498
+
499
+ setFilter(value);
500
+ setFilteredCheckboxesId(Array.from(filteredCheckboxesId));
501
+ };
502
+
503
+ const renderDropdown = ({
504
+ opened,
505
+ setOpened,
506
+ targetRef,
507
+ dropdownRef,
508
+ }: {
509
+ opened: boolean;
510
+ setOpened: (opened: boolean) => void;
511
+ targetRef: React.RefObject<HTMLDivElement>;
512
+ dropdownRef: React.RefObject<HTMLDivElement>;
513
+ }) => (
514
+ <MultiselectField.Dropdown
515
+ opened={opened}
516
+ setOpened={setOpened}
517
+ targetRef={targetRef}
518
+ ref={dropdownRef}
519
+ focusTrapProps={{
520
+ focusTrapOptions: { initialFocus: false },
521
+ }}
522
+ pullerProps={{
523
+ children: (
524
+ <>
525
+ <PullerHeader controlButtons={<PullerClose onClick={() => setOpened(false)} />}>
526
+ <PullerInput placeholder="Type to proceed" value={filter} onChange={handleFilterChange} />
527
+ </PullerHeader>
528
+ <PullerBody>{renderDropdownContent()}</PullerBody>
529
+ <PullerFooter>
530
+ <Button
531
+ theme={EButtonTheme.GENERAL}
532
+ size={EComponentSize.SM}
533
+ onClick={() => setOpened(false)}
534
+ >
535
+ Button text
536
+ </Button>
537
+ <Button theme={EButtonTheme.LINK} size={EComponentSize.SM} onClick={handleClickClearFilter}>
538
+ Button link text
539
+ </Button>
540
+ </PullerFooter>
541
+ </>
542
+ ),
543
+ }}
544
+ >
545
+ <MultiselectField.Dropdown.Header>
546
+ <FormField size={EComponentSize.SM}>
547
+ <FormFieldLabel>Type to proceed</FormFieldLabel>
548
+ <FormFieldInput value={filter} onChange={handleFilterChange} />
549
+ <FormFieldPostfix>
550
+ <FormFieldClear onClick={handleClearInput} />
551
+ </FormFieldPostfix>
552
+ </FormField>
553
+ </MultiselectField.Dropdown.Header>
554
+ <MultiselectField.Dropdown.Content>{renderDropdownContent()}</MultiselectField.Dropdown.Content>
555
+ <MultiselectField.Dropdown.Footer>
556
+ <Button theme={EButtonTheme.SECONDARY} size={EComponentSize.SM} onClick={() => setOpened(false)}>
557
+ Button text
558
+ </Button>
559
+ <Button theme={EButtonTheme.LINK} size={EComponentSize.SM} onClick={handleClickClearFilter}>
560
+ Button link text
561
+ </Button>
562
+ </MultiselectField.Dropdown.Footer>
563
+ </MultiselectField.Dropdown>
564
+ );
565
+
566
+ return (
567
+ <ChipMultiselect
568
+ clearSelected={unselectAll}
569
+ selected={Boolean(checkboxes.filter((checkbox) => checkbox.checked).length)}
570
+ label="Multiselect label"
571
+ >
572
+ {(dropdownProps) => renderDropdown(dropdownProps)}
573
+ </ChipMultiselect>
574
+ );
575
+ };
576
+ ```
577
+
@@ -241,6 +241,7 @@ Default и Hover. Цвет текста (`Color_*`) от типа не зави
241
241
  | 2026-07-29 | Правки по ревью PR #487: уточнена атрибуция класса `.chipGroupItem` (его проставляет семейство Chip*, а не раскладка `ChipGroup`) и описание API `ChipClearButton` / `ChipDropdownArrow` |
242
242
  | 2026-09-07 | В семейство добавлен `ChipMonthYearPicker` — упомянут в списках потребителей `Chip` и `.chipGroupItem` |
243
243
  | 2026-09-29 | Аудит симметрии `related` (TRI-156): из `related` убраны имена без строки в `docs/ai/ROADMAP.md` (`ChipIcon`, `ChipClearButton`, `ChipDropdownArrow`). Пояснения к убранным именам сохранены прозой в «Связанные компоненты»; публичный API и поведение не затронуты. |
244
+ | 2026-10-19 | В story `WithNotificationIcon` подключён исходный код примера через `?raw` для копирования со страницы документации. |
244
245
 
245
246
  ---
246
247