@sberbusiness/triplex-next-mcp-server 1.45.0-beta → 1.48.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 (60) hide show
  1. package/README.md +7 -7
  2. package/bundle/components/AmountField.md +222 -323
  3. package/bundle/components/Avatar.md +138 -146
  4. package/bundle/components/CardAction.md +615 -0
  5. package/bundle/components/CardStatic.md +402 -0
  6. package/bundle/components/CheckboxTree.md +157 -215
  7. package/bundle/components/CheckboxTreeExtended.md +7 -7
  8. package/bundle/components/Chip.md +7 -6
  9. package/bundle/components/ChipGroup.md +1 -1
  10. package/bundle/components/ChipMonthYearPicker.md +344 -0
  11. package/bundle/components/ChipMultiselect.md +1251 -0
  12. package/bundle/components/ChipOptions.md +329 -0
  13. package/bundle/components/ChipSort.md +333 -0
  14. package/bundle/components/ChipSuggest.md +588 -0
  15. package/bundle/components/CollapsibleTree.md +8 -4
  16. package/bundle/components/CollapsibleTreeExtended.md +8 -6
  17. package/bundle/components/Confirm.md +346 -281
  18. package/bundle/components/DateField.md +222 -303
  19. package/bundle/components/DatePickerExtended.md +3 -1
  20. package/bundle/components/DateRange.md +351 -210
  21. package/bundle/components/FooterPage.md +1 -0
  22. package/bundle/components/FormField.md +14 -5
  23. package/bundle/components/FormGroup.md +3 -2
  24. package/bundle/components/HelpBox.md +7 -1
  25. package/bundle/components/IslandAccordion.md +443 -152
  26. package/bundle/components/IslandWidget.md +622 -157
  27. package/bundle/components/LightBox.md +8 -0
  28. package/bundle/components/ListMaster.md +2 -2
  29. package/bundle/components/ListTableItem.md +6 -6
  30. package/bundle/components/MaskedField.md +2 -1
  31. package/bundle/components/MonthYearField.md +18 -5
  32. package/bundle/components/Slider.md +389 -97
  33. package/bundle/components/SliderRange.md +486 -105
  34. package/bundle/components/StatusTracker.md +386 -94
  35. package/bundle/components/Stepper.md +355 -102
  36. package/bundle/components/StepperExtended.md +633 -0
  37. package/bundle/components/SuggestField.md +1049 -96
  38. package/bundle/components/SwipeableArea.md +7 -7
  39. package/bundle/components/Tabs.md +265 -92
  40. package/bundle/components/TabsExtended.md +18 -18
  41. package/bundle/components/TabsLine.md +301 -127
  42. package/bundle/components/TagColor.md +187 -66
  43. package/bundle/components/TagGroup.md +184 -48
  44. package/bundle/components/TextareaField.md +278 -70
  45. package/bundle/components/TopOverlay.md +17 -1
  46. package/bundle/components/TreeView.md +16 -7
  47. package/bundle/components/TriggerClickOnKeyDownEvent.md +277 -0
  48. package/bundle/components/UploadZone.md +318 -0
  49. package/bundle/guides/codestyle.md +16 -1
  50. package/bundle/guides/commits.md +47 -1
  51. package/bundle/guides/tests.md +23 -2
  52. package/bundle/manifest.json +92 -24
  53. package/bundle/release-notes/1.46.0.mdx +59 -0
  54. package/bundle/release-notes/1.47.0.mdx +122 -0
  55. package/bundle/release-notes/1.48.0.mdx +68 -0
  56. package/bundle/release-notes/1.49.0.mdx +7 -0
  57. package/bundle/tokens.json +3 -3
  58. package/dist/index.js +220 -6
  59. package/dist/index.js.map +1 -1
  60. package/package.json +1 -1
@@ -1,9 +1,7 @@
1
1
  ---
2
2
  component: CheckboxTree
3
3
  category: Inputs
4
- figma-node: ""
5
- figma-file: ""
6
- related: [CheckboxTreeExtended, Checkbox, CheckboxYGroup, CheckboxXGroup]
4
+ related: [CheckboxTreeExtended, Checkbox]
7
5
  tokens: []
8
6
  stories: stories/CheckboxTree/CheckboxTree.stories.tsx
9
7
  version: "1.0"
@@ -13,263 +11,206 @@ version: "1.0"
13
11
 
14
12
  ## Назначение
15
13
 
16
- "Готовый" компонент дерева чекбоксов с автоматической логикой
17
- checked/bulk-состояний: при клике на родителя все потомки чекаются/
18
- разчекаются; при частичном выборе потомков родитель получает `bulk: true`
19
- (состояние "indeterminate").
14
+ Дерево чекбоксов, которое строится из массива данных и само связывает состояние
15
+ родителей и потомков. Обёртка над `CheckboxTreeExtended`: разметку узлов
16
+ компонент формирует сам, потребитель передаёт только описание дерева
17
+ (`ICheckboxTreeCheckboxData[]`) и получает в `onChange` новый массив с
18
+ пересчитанными флагами `checked` и `bulk`.
20
19
 
21
- Является **тонкой data-driven обёрткой** над `CheckboxTreeExtended`:
22
- потребитель передаёт плоское описание дерева (`ICheckboxTreeCheckboxData[]`),
23
- компонент сам рендерит `CheckboxTreeExtended.Node` + `.Checkbox`, управляет
24
- состоянием и отдаёт обновлённый массив в `onChange`.
20
+ Каскадная логика — единственное, что компонент добавляет к
21
+ `CheckboxTreeExtended`:
25
22
 
26
- Используй когда: данные дерева — линейные/сериализуемые (например, из
27
- бэкенда), нужна стандартная семантика bulk/чек-каскада, не нужны кастомные
28
- UI-вставки в узлы.
29
- Не используй когда: нужен кастомный рендер узла (заголовки, иконки,
30
- доп. контент) — бери `CheckboxTreeExtended` напрямую; нужны 1–2 независимых
31
- чекбокса (бери `Checkbox` / `CheckboxYGroup`).
23
+ - выбор узла выбирает всех его потомков, снятие — снимает всех;
24
+ - если выбрана только часть потомков, родитель становится выбранным частично
25
+ (`bulk`) и рисует bulk-иконку вместо галочки;
26
+ - клик по частично выбранному узлу выбирает поддерево целиком, а не снимает
27
+ выбор;
28
+ - флаги пересчитываются снизу вверх по всему дереву на каждое изменение.
32
29
 
33
- ---
34
-
35
- ## Файловая структура
36
-
37
- ```text
38
- src/components/CheckboxTree/
39
- ├── CheckboxTree.tsx # React.FC обёртка над CheckboxTreeExtended
40
- ├── types.ts # ICheckboxTreeCheckboxData
41
- ├── utils.ts # traverseCheckboxes, checkParentCheckboxes, checkChildrenCheckboxes
42
- ├── index.ts # Barrel: компонент + types + utils
43
- └── __tests__/
44
- ```
30
+ Используй когда: есть готовая иерархия значений (категории, права доступа,
31
+ фильтры) и нужна стандартная логика «родитель ↔ потомки» без ручного
32
+ пересчёта.
45
33
 
46
- Особенности:
47
- - **Нет своих стилей** — весь визуальный вид из `CheckboxTreeExtended` и его
48
- внутреннего `Checkbox`.
49
- - **Нет `forwardRef`** — FC без ref-форвардинга. Это осознанное упрощение
50
- (потребитель обычно работает с самими `checkboxes` через onChange, не с DOM).
51
- Если нужен ref — переключаться на `CheckboxTreeExtended`.
52
- - **Utils экспортируются через barrel** (`traverseCheckboxes`,
53
- `checkParentCheckboxes`, `checkChildrenCheckboxes`) — намеренно, потребитель
54
- может использовать их для предварительной обработки данных или тестов.
34
+ Не используй когда: нужна своя структура данных, произвольный контент рядом с
35
+ чекбоксом, ленивая загрузка ветки или другая логика связи родителя и потомков —
36
+ бери `CheckboxTreeExtended` и описывай узлы вручную. Если иерархии нет вовсе,
37
+ достаточно `CheckboxYGroup`.
55
38
 
56
39
  ---
57
40
 
58
41
  ## Варианты и props
59
42
 
60
- ### `ICheckboxTreeProps extends Omit<ICheckboxTreeExtendedProps, "children" | "onChange">`
61
-
62
- Наследует от `CheckboxTreeExtended` всё, кроме `children` (ставится из
63
- `checkboxes`) и `onChange` (своя сигнатура).
64
-
65
- | Prop | Тип | Обязателен | Default | Описание |
66
- |---|---|---|---|---|
67
- | `checkboxes` | `ICheckboxTreeCheckboxData[]` | Да | — | Плоский массив (каждый элемент может иметь `children`) — описание дерева. |
68
- | `onChange` | `(options: ICheckboxTreeCheckboxData[]) => void` | Да | — | Получает **новую копию** массива после каскадной обработки. Потребитель должен сохранить её в свой state. |
69
- | `size` | `EComponentSize` | Нет | `MD` | Размер чекбоксов (SM/MD/LG). Форвардится в CheckboxTreeExtended. |
70
- | `children` | `never` | — | — | **Запрещён** через `never` — компонент сам генерирует DOM. |
43
+ `ICheckboxTreeProps` наследует `ICheckboxTreeExtendedProps` без `children` и
44
+ `onChange`, поэтому тип разрешает и остальные атрибуты `<ul>` (`className`,
45
+ `id`, `data-*`). Собственными компонент считает только `checkboxes`, `onChange`
46
+ и `size`, всё остальное уходит через `...rest` на корневой `<ul role="tree">`.
47
+ `className` при этом складывается с внутренними классами, а не заменяет их.
48
+
49
+ | Prop | Тип | По умолчанию | Описание |
50
+ |---|---|---|---|
51
+ | `checkboxes` | `ICheckboxTreeCheckboxData[]` | — | Описание дерева. Вложенность задаётся полем `children` элементов. Обязателен |
52
+ | `onChange` | `(options: ICheckboxTreeCheckboxData[]) => void` | — | Вызывается после каждого клика по чекбоксу — с новым массивом верхнего уровня и уже пересчитанными флагами. Обязателен |
53
+ | `size` | `EComponentSize` | `EComponentSize.MD` | Размер чекбоксов и отступов веток. Раздаётся через контекст `CheckboxTreeExtended`, в DOM не попадает. В мобильном представлении всегда `MD` |
54
+ | `children` | `never` | — | Запрещён типом: разметку узлов формирует сам компонент |
55
+ | `ref` | `React.Ref<HTMLUListElement>` | — | Указывает на корневой `<ul role="tree">` |
56
+ | остальные | `React.HTMLAttributes<HTMLUListElement>` | — | Прокидываются на корневой `<ul>` как есть |
71
57
 
72
58
  ### `ICheckboxTreeCheckboxData`
73
59
 
74
- Единственный тип данных дерева:
75
-
76
- ```ts
77
- interface ICheckboxTreeCheckboxData {
78
- id: string; // уникальный — ключ и aria-поддержка
79
- label: React.ReactNode; // подпись
80
- checked: boolean; // текущее состояние
81
- bulk?: boolean; // "indeterminate", рассчитывается автоматически
82
- children?: ICheckboxTreeCheckboxData[]; // рекурсивно
83
- }
84
- ```
85
-
86
- **`bulk` — вычисляется компонентом**, потребитель не должен устанавливать
87
- его руками. На входе можно передать `false`/`undefined`; после onChange
88
- массив вернётся с актуальным bulk.
89
-
90
- ---
91
-
92
- ## Ключевые особенности реализации
93
-
94
- ### Мутация массива на месте
95
-
96
- ```ts
97
- checkbox.checked = checkbox.bulk ? true : checked;
98
- checkChildrenCheckboxes(checkbox);
99
- traverseCheckboxes(checkboxes, checkParentCheckboxes);
100
- onChange([...checkboxes]);
101
- ```
102
-
103
- Компонент **мутирует** исходный `checkboxes` массив (меняет `checked`/`bulk`
104
- на узлах) и только на outer-level передаёт новую ссылку через спред. Это
105
- значит:
106
- - Потребитель НЕ должен передавать readonly/immutable данные (Object.freeze
107
- сломает).
108
- - Потребитель должен либо хранить в state `useState` (React сравнит ссылку
109
- верхнего уровня), либо всегда клонировать перед передачей.
110
- - Внутренние референсы не меняются — потомки, которые хранят ссылку на
111
- конкретный узел, увидят обновлённое значение.
112
-
113
- Это архитектурный компромисс ради простоты алгоритма. **Не рефакторить
114
- на immutable-подход без серьёзного повода** — сломает внешний контракт.
115
-
116
- ### `bulk → checked = true` при клике на bulk-узел
117
-
118
- ```ts
119
- checkbox.checked = checkbox.bulk ? true : checked;
120
- ```
121
-
122
- Клик по узлу в `bulk`-состоянии (частично выбран) делает **всех** потомков
123
- `checked: true`, не снимает выбор. Это соответствует стандартной UX
124
- tri-state чекбокса: indeterminate → всё выбрано (не → ничего).
125
-
126
- ### `checkChildrenCheckboxes` — рекурсивный каскад вниз
127
-
128
- ```ts
129
- checkbox.children.forEach((c) => {
130
- c.checked = checkbox.checked;
131
- checkChildrenCheckboxes(c);
132
- });
133
- ```
60
+ Описание одного узла. Поля `checked` и `bulk` у узлов с потомками
61
+ пересчитываются компонентом — задавать их вручную имеет смысл только как
62
+ начальное состояние.
134
63
 
135
- После клика на узел все его потомки получают то же `checked`-значение.
136
- `bulk` у потомков НЕ сбрасывается в этой функции — его потом пересчитает
137
- `checkParentCheckboxes` при обходе снизу-вверх.
138
-
139
- ### `checkParentCheckboxes` — каскад вверх (post-order обход)
140
-
141
- ```ts
142
- if (c.children) traverseCheckboxes(c.children, cb);
143
- cb(c);
144
- ```
145
-
146
- `traverseCheckboxes` идёт **post-order** (сначала потомки, потом узел) —
147
- это ключевое: чтобы `checkParentCheckboxes` к моменту обработки узла видел
148
- актуальное состояние потомков.
149
-
150
- Логика узла:
151
- - Все потомки checked → `checked: true`, `bulk: true` только если у кого-то
152
- был bulk (не обычный полный чек).
153
- - Часть потомков checked → `checked: true, bulk: true`.
154
- - Никого не checked → `checked: false`.
155
-
156
- **Нюанс:** "все checked" с хотя бы одним bulk → родитель тоже bulk. То есть
157
- bulk распространяется вверх, даже если с точки зрения текущего уровня всё
158
- выбрано.
159
-
160
- ### `onChange([...checkboxes])` — shallow clone
161
-
162
- ```ts
163
- onChange([...checkboxes]);
164
- ```
64
+ | Поле | Тип | Описание |
65
+ |---|---|---|
66
+ | `id` | `string` | Идентификатор узла, уникальный в пределах всего дерева |
67
+ | `label` | `React.ReactNode` | Видимая подпись чекбокса. За счёт вложенности `<input>` в корневой `<label>` она же становится доступным именем |
68
+ | `checked` | `boolean` | Признак выбора |
69
+ | `bulk` | `boolean` | Признак частичного выбора: bulk-иконка вместо галочки |
70
+ | `children` | `ICheckboxTreeCheckboxData[]` | Вложенные узлы |
165
71
 
166
- Только outer array клонируется — узлы внутри те же мутированные объекты.
167
- Это совпадает с тем, что нужно React для перерендера при `useState`
168
- (новая ссылка на массив).
72
+ ### Утилиты пересчёта
169
73
 
170
- ### render-функция `renderNode` с prev/next linking
74
+ `utils.ts` экспортируется через barrel, поэтому три функции — часть публичного
75
+ API. Они предназначены для случая, когда потребитель считает состояние дерева
76
+ сам; рабочий пример — `stories/Chips/examples/ChipMultiselect/WithCheckboxTreeExample.tsx`,
77
+ где они импортируются из `@sberbusiness/triplex-next` и пересчитывают дерево
78
+ опций внутри `ChipMultiselect`:
171
79
 
172
- ```tsx
173
- <CheckboxTreeExtended.Node
174
- prevNodeId={prevCheckbox?.id}
175
- nextNodeId={nextCheckbox?.id}
176
- />
177
- ```
178
-
179
- CheckboxTreeExtended использует prev/next для клавиатурной навигации
180
- (стрелки вверх/вниз между узлами). CheckboxTree автоматически вычисляет
181
- их из массива. Это значит, что arrow-navigation работает только в пределах
182
- одного parent'а — между siblings. **Не менять порядок вызовов в children**:
183
- первый элемент родителя получает prevNodeId=undefined, и это ожидаемо.
184
-
185
- ### Функциональный чекбокс через render-проп
80
+ | Функция | Что делает |
81
+ |---|---|
82
+ | `traverseCheckboxes(checkboxes, callback)` | Обход в глубину **снизу вверх**: callback вызывается сначала для потомков, затем для родителя |
83
+ | `checkParentCheckboxes(checkbox)` | Пересчитывает `checked` / `bulk` узла по его прямым потомкам. Узел без потомков не трогает |
84
+ | `checkChildrenCheckboxes(checkbox)` | Проставляет `checked` узла всем его потомкам вниз по дереву |
186
85
 
187
- ```tsx
188
- checkbox={(props) => (
189
- <CheckboxTreeExtended.Checkbox {...props} onChange={...} bulk={...} checked={...}>
190
- {checkbox.label}
191
- </CheckboxTreeExtended.Checkbox>
192
- )}
193
- ```
86
+ Порядок обхода в `traverseCheckboxes` — не деталь реализации, а контракт:
87
+ `checkParentCheckboxes` работает корректно только если потомки уже пересчитаны.
194
88
 
195
- Node принимает `checkbox` как функцию, получающую provide-props (id,
196
- aria-атрибуты). CheckboxTree передаёт их наружу и добавляет свои
197
- onChange/bulk/checked. **Не упрощать** — CheckboxTreeExtended полагается
198
- на render-проп для управления фокусом и ARIA.
89
+ ---
199
90
 
200
- ### `onChange={handleChange(checkbox)}` — каррирование
91
+ ## Дизайн-токены
201
92
 
202
- Замыкание на конкретный `checkbox` через первый аргумент `handleChange`.
203
- Позволяет внутри обработчика знать, какой именно узел меняется. Важно:
204
- `checkbox` — ссылка на объект в массиве, который мутирует. Если потребитель
205
- заменит массив на новый — замыкание станет устаревшим, но это решается
206
- новым рендером (новая функция `handleChange` захватит свежий объект).
93
+ Собственных стилей и токенов у компонента нет — он не содержит ни одного
94
+ LESS-модуля. Всё оформление приходит из `CheckboxTreeExtended` (отступы веток,
95
+ общий токен `Checkbox.BorderColor_Focus`) и из `Checkbox` (цвета, размеры,
96
+ состояния). Переопределять их нужно у этих компонентов — см.
97
+ `CheckboxTreeExtended-ai.md` и `Checkbox-ai.md`.
207
98
 
208
99
  ---
209
100
 
210
101
  ## Инварианты
211
102
 
212
- - **`children: never`** — не делать компонент композитным, он data-driven.
213
- Для композитного — `CheckboxTreeExtended`.
214
- - **`checkboxes` мутируется на месте** — не передавать Object.freeze /
215
- immutable. Не рефакторить на immer/immutability без серьёзного повода.
216
- - **`bulk` вычисляется автоматически** — не устанавливать руками во
217
- входных данных (перезатрётся).
218
- - **`id` в каждом узле — уникальный** по всему дереву (не просто в рамках
219
- одного parent'а). Используется как React `key` и для a11y.
220
- - **`size` → CheckboxTreeExtended** — наследуется, значение по умолчанию MD.
221
- - **`onChange` отдаёт новую shallow-copy массива** — не менять на передачу
222
- исходной ссылки (сломает `useState`-сравнение).
223
- - **Post-order обход в `traverseCheckboxes`** — не менять на pre-order,
224
- иначе `checkParentCheckboxes` будет работать на неактуальных данных.
225
- - **`checkbox.checked = checkbox.bulk ? true : checked`** — "bulk → всё в
226
- true". Не инвертировать на "bulk → всё в false" без согласования UX.
103
+ - **Компонент мутирует переданные объекты.** `handleChange` пишет `checked` и
104
+ `bulk` прямо в элементы массива `checkboxes`, а в `onChange` отдаёт поверхностную
105
+ копию массива верхнего уровня (`[...checkboxes]`). Идентичность самих узлов при
106
+ этом сохраняется. На этом держится связка с `useState` у потребителя (новая
107
+ ссылка на массив → перерисовка), и это наблюдаемое поведение: переход на
108
+ иммутабельный пересчёт — breaking change, а не рефакторинг.
109
+ - **`children` запрещён типом** (`children?: never`). Разметка узлов —
110
+ ответственность компонента; своя разметка — это `CheckboxTreeExtended`.
111
+ - **`id` узла обязан быть уникальным во всём дереве.** По `id` строятся
112
+ `prevNodeId` / `nextNodeId`, а по ним `TreeView` ведёт клавиатурную навигацию.
113
+ Дубликаты ломают перемещение по дереву.
114
+ - **Порядок клавиатурного обхода задаётся порядком массива** на каждом уровне:
115
+ соседями узла считаются элементы `checkboxes` (или `children`) по соседним
116
+ индексам.
117
+ - **Дерево всегда полностью раскрыто** — флаг `isStaticCheckboxTreeExtended` в
118
+ `CheckboxTreeExtended` прячет стрелки и раскрывает все узлы. Сворачивания у
119
+ `CheckboxTree` нет по построению.
120
+ - **`ref` указывает на корневой `<ul role="tree">`.** Компонент обёрнут
121
+ `forwardRef`, ссылка проходит насквозь через `CheckboxTreeExtended` →
122
+ `CollapsibleTreeExtended` → `TreeView`. Собственного DOM-узла у `CheckboxTree`
123
+ нет, поэтому другого разумного адресата у `ref` тоже нет.
124
+ - **Публичный API** — имена `ICheckboxTreeProps`, `ICheckboxTreeCheckboxData`,
125
+ функции `traverseCheckboxes`, `checkParentCheckboxes`,
126
+ `checkChildrenCheckboxes` и состав barrel `index.ts` менять нельзя.
127
+ `displayName` — `"CheckboxTree"`.
227
128
 
228
129
  ---
229
130
 
230
- ## Accessibility
231
-
232
- - Всё управление a11y — внутри `CheckboxTreeExtended` (role="tree"/"treeitem",
233
- `aria-checked` (true/false/mixed), клавиатурная навигация).
234
- - `bulk: true` → `aria-checked="mixed"` (indeterminate-состояние) на узле.
235
- - `id` узла используется в `aria-controls` / `headers`-подобных связях —
236
- обязательно уникален.
237
- - Навигация:
238
- - Tab — переход внутри дерева/наружу.
239
- - Space/Enter — toggle чекбокса.
240
- - Arrow Up/Down — между siblings через prev/nextNodeId.
241
- - Если нужна expand/collapse семантика — это задача CheckboxTreeExtended
242
- (через Node's expand-проп, если используется).
131
+ ## Известные ограничения
132
+
133
+ Найдено при AI-рефакторинге (TRI-22). Поведение оставлено как есть — его меняют
134
+ только отдельным решением, это публичный контракт.
135
+
136
+ - **Вход в поддерево с клавиатуры не переводит фокус.** Пока фокус стоит на
137
+ `<li>` ветки, стрелка вниз меняет активный узел на её потомка, но фокус
138
+ остаётся на ветке. Причина — guard в `CheckboxTreeExtendedCheckbox`:
139
+ `if (!document.activeElement?.contains(checkboxNode.current))`. Он защищается
140
+ от лишнего перевода фокуса при клике мышью, но `contains` истинно и для любого
141
+ предка, а `<li>` ветки содержит чекбоксы всех своих потомков. При переходе на
142
+ узел вне текущего поддерева фокус двигается штатно. Лечится на уровне
143
+ `CheckboxTreeExtended` (сравнивать с самим узлом, а не через `contains`) —
144
+ наблюдаемое изменение, отдельное решение и запись в release notes.
145
+ Покрыто тестом `Should move focus between nodes on ArrowDown`, который
146
+ фиксирует текущее поведение, включая это залипание. Когда ограничение
147
+ починят, тест упадёт на трёх «холостых» `ArrowDown` в начале — это ожидаемо,
148
+ его нужно будет переписать под новое поведение.
149
+ - **`bulk` у листа не пересчитывается.** `checkParentCheckboxes` выходит сразу
150
+ для узла без потомков, поэтому флаг остаётся таким, каким его задал
151
+ потребитель. Лист с `bulk: true` невозможно снять кликом: `checked` ставится
152
+ по правилу «клик по частичному выбирает целиком». Задавать `bulk` листу не
153
+ нужно — флаг осмыслен только для узлов с потомками.
154
+ - **`bulk` не сбрасывается, когда все потомки сняты.** В ветке «не выбран ни
155
+ один потомок» пишется только `checked = false`. Визуально это незаметно:
156
+ `Checkbox` показывает иконку (галочку или bulk) только при `checked`, а при
157
+ следующем выборе флаг всё равно пересчитается.
158
+ - **`<li> cannot appear as a descendant of <li>`** — предупреждение React на
159
+ первом рендере вложенных узлов. Приходит из `TreeView` и общее для всего
160
+ семейства (`CollapsibleTree`, `CollapsibleTreeExtended`, `CheckboxTreeExtended`),
161
+ лечится только на уровне `TreeView`.
243
162
 
244
163
  ---
245
164
 
246
- ## Дизайн-токены
165
+ ## Accessibility
247
166
 
248
- Нет своих. Всё через `CheckboxTreeExtended` и внутренний `Checkbox`.
167
+ - Семантику (`<ul role="tree">`, `<li>`, группы) и клавиатурную навигацию
168
+ обеспечивает `TreeView` через `CheckboxTreeExtended`. `CheckboxTree` добавляет
169
+ к этому только связывание соседних узлов: каждому `CheckboxTreeExtended.Node`
170
+ передаются `prevNodeId` и `nextNodeId` соседей по массиву.
171
+ - Фокус при навигации с клавиатуры забирает чекбокс активного узла — это делает
172
+ `CheckboxTreeExtended.Checkbox`, подробности в `CheckboxTreeExtended-ai.md` →
173
+ «Accessibility».
174
+ - Подпись чекбокса — поле `label`. Связь подписи и поля структурная: в
175
+ `Checkbox` корневой элемент — `<label>`, а `<input type="checkbox">` лежит
176
+ внутри него (`htmlFor` / `id` не используются), поэтому связь работает и при
177
+ пустом `label` — только доступного имени у такого чекбокса не будет.
178
+ Собственных текстов компонент не содержит: библиотека мультиязычная, и
179
+ доступное имя для узла без видимой подписи передаёт потребитель через `label`.
180
+ - Частичный выбор кодируется только визуально (bulk-иконка). `aria-checked="mixed"`
181
+ компонент не выставляет — под капотом нативный `<input type="checkbox">` без
182
+ `indeterminate`. Если полумерное состояние нужно ассистивным технологиям,
183
+ это отдельная доработка `Checkbox`.
249
184
 
250
185
  ---
251
186
 
252
187
  ## Связанные компоненты
253
188
 
254
- - `CheckboxTreeExtended` (`src/components/CheckboxTreeExtended`) — нижний
255
- уровень, composable, с полным контролем рендера узлов.
256
- - `Checkbox` (`src/components/Checkbox`) — одиночный чекбокс.
257
- - `CheckboxYGroup` / `CheckboxXGroup` — группы плоских чекбоксов (без
258
- иерархии).
259
- - `EComponentSize` — общий enum SM/MD/LG.
189
+ - `CheckboxTreeExtended` (`src/components/CheckboxTreeExtended`) — нижний слой и
190
+ одновременно альтернатива. `ICheckboxTreeProps` наследует его props, а
191
+ `CheckboxTree` рендерит `CheckboxTreeExtended`, `.Node` и `.Checkbox`. Бери
192
+ его, когда нужна своя разметка узлов или своя логика связи родителя и
193
+ потомков.
194
+ - `Checkbox` (`src/components/Checkbox`) — чекбокс каждого узла. Отсюда приходят
195
+ все токены, размеры и семантика `bulk` (иконка вместо галочки, видимость по
196
+ `checked`).
197
+ - `CheckboxYGroup` (`src/components/Checkbox`) — плоская вертикальная группа
198
+ чекбоксов. Подходит, когда иерархии нет и каскадный пересчёт не нужен.
199
+ - `MultiselectField` и `ChipMultiselect` — типовые потребители: показывают
200
+ `CheckboxTree` как дерево опций внутри выпадающего блока (см. их stories).
260
201
 
261
202
  ---
262
203
 
263
204
  ## Stories
264
205
 
265
- `stories/CheckboxTree/CheckboxTree.stories.tsx`
206
+ Основные истории: `stories/CheckboxTree/CheckboxTree.stories.tsx`
266
207
  Файлы примеров: `stories/CheckboxTree/examples/`
267
208
 
268
209
  | Story | Example file | Что демонстрирует |
269
210
  |---|---|---|
270
- | `Playground` | `PlaygroundExample.tsx` | Интерактивный контроль `size` + управляемое дерево |
271
- | `Default` | `DefaultExample.tsx` | Базовое дерево с одним уровнем вложенности |
272
- | `Sizes` | `SizesExample.tsx` | Все три размера чекбоксов |
211
+ | `Playground` | `PlaygroundExample.tsx` | Интерактивный контроль `size` на дереве из трёх веток |
212
+ | `Default` | `DefaultExample.tsx` | Минимальный сценарий: массив в `useState` потребителя, `onChange` передаётся сеттером |
213
+ | `Sizes` | `SizesExample.tsx` | Размеры SM / MD / LG рядом |
273
214
 
274
215
  ---
275
216
 
@@ -277,7 +218,8 @@ onChange/bulk/checked. **Не упрощать** — CheckboxTreeExtended пол
277
218
 
278
219
  | Дата | Изменение |
279
220
  |---|---|
280
- | 2026-04-18 | Создан документ |
221
+ | 2026-09-17 | TRI-22: добавлен `forwardRef` на корневой `<ul>` (насквозь через всё семейство Tree), прокидываются html-атрибуты, узел с `children: []` больше не становится выбранным. В «Известные ограничения» добавлено залипание фокуса при входе в поддерево |
222
+ | 2026-09-14 | Создан документ. Проведён AI-рефакторинг (TRI-22): JSDoc на props и полях `ICheckboxTreeCheckboxData`, убраны `eslint-disable` в `utils.ts`, рендер узлов вынесен в общий `renderNodes`; добавлены unit-тесты на утилиты и на каскадный пересчёт |
281
223
 
282
224
  ---
283
225
 
@@ -114,14 +114,13 @@ Checkbox.BorderColor_Focus
114
114
  отрисовывается. Файл и обе ветки кода намеренно оставлены — по TODO флаг
115
115
  снимут, когда сворачиваемое дерево станет стандартом. Не удаляй ветки
116
116
  `!isStaticCheckboxTreeExtended` как «мёртвый код».
117
- - **`forwardRef` нет ни у одной части** — `CheckboxTreeExtended`,
118
- `.Node` и `.Checkbox` объявлены как `React.FC`. Это осознанно и повторяет
119
- `CollapsibleTreeExtended`: под капотом class-компоненты `TreeView` /
120
- `TreeViewNode` с собственной работой с DOM. Добавление `forwardRef` меняет
121
- форму экспортируемого `ICheckboxTreeExtendedSFC` — это отдельное решение
122
- мейнтейнера, а не рефакторинг.
117
+ - **`ref` указывает на корневой `<ul role="tree">`.** Компонент обёрнут
118
+ `forwardRef`, ссылка уходит в `CollapsibleTreeExtended` и дальше в `TreeView`.
119
+ У `.Node` и `.Checkbox` своего `forwardRef` по-прежнему нет: `.Node` —
120
+ class-компонент, `.Checkbox` управляет фокусом сам.
123
121
  - **Публичный API** — имена `ICheckboxTreeExtendedProps`,
124
- `ICheckboxTreeExtendedSFC`, `ICheckboxTreeExtendedCheckboxProvideProps`,
122
+ `ICheckboxTreeExtendedFC` (и deprecated-алиас `ICheckboxTreeExtendedSFC`),
123
+ `ICheckboxTreeExtendedCheckboxProvideProps`,
125
124
  `ICheckboxTreeExtendedNodeProps`,
126
125
  статические `Checkbox` / `Node` и barrel-экспорты `index.ts` менять нельзя:
127
126
  на них опирается `CheckboxTree`, а также stories `Chips` и `MultiselectField`.
@@ -224,6 +223,7 @@ Checkbox.BorderColor_Focus
224
223
  | 2026-08-17 | `className` у `CheckboxTreeExtended.Node` больше не затирает собственный класс узла — классы мерджатся через `clsx` (правка по ревью PR #535) |
225
224
  | 2026-08-17 | Убран мёртвый селектор `.checkboxTreeCheckbox` из мобильной медиа-выборки (рабочие `> span` и `> svg` оставлены), `.caretIconWrapper:focus` заменён на `:focus-visible` (правки по ревью PR #535) |
226
225
  | 2026-08-17 | `ICheckboxTreeExtendedNodeProps` экспортируется — тип уходит в barrel и становится частью публичного API (решение владельца по ревью PR #535) |
226
+ | 2026-09-17 | TRI-22: `CheckboxTreeExtended` обёрнут `forwardRef` — `ref` отдаёт корневой `<ul>`; тип переименован в `ICheckboxTreeExtendedFC`, старое имя оставлено deprecated-алиасом |
227
227
 
228
228
  ---
229
229
 
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  component: Chip
3
3
  category: Chips
4
- related: [ChipGroup, ChipIcon, ChipOptions, ChipSelect, ChipMultiselect, ChipSort, ChipSuggest, ChipDatePicker, ChipClearButton, ChipDropdownArrow, Badge, IconWrapper]
4
+ related: [ChipGroup, ChipIcon, ChipOptions, ChipSelect, ChipMultiselect, ChipSort, ChipSuggest, ChipDatePicker, ChipMonthYearPicker, ChipClearButton, ChipDropdownArrow, Badge, IconWrapper]
5
5
  tokens:
6
6
  - Chip.Background_Type1_Default
7
7
  - Chip.Background_Type1_Hover
@@ -33,7 +33,7 @@ version: "1.0"
33
33
  Компактный интерактивный элемент-«чипс»: выполняет действие по нажатию и отображает
34
34
  выбранное (`selected`) состояние. Одновременно служит базой для всего семейства Chip*
35
35
  — поверх него построены `ChipIcon`, `ChipOptions`, а также target-элементы
36
- `ChipSelect`, `ChipMultiselect`, `ChipSuggest`, `ChipDatePicker`.
36
+ `ChipSelect`, `ChipMultiselect`, `ChipSuggest`, `ChipDatePicker`, `ChipMonthYearPicker`.
37
37
 
38
38
  Используй когда: нужен переключаемый фильтр, тег или компактная кнопка-действие
39
39
  в ряду однотипных элементов.
@@ -152,7 +152,7 @@ Default и Hover. Цвет текста (`Color_*`) от типа не зави
152
152
  - **Класс `.chipGroupItem`** выставляется всегда, наравне с `.chip`. Объявлен
153
153
  в `styles/Chip.module.less` и служит общим маркером «элемент верхнего уровня
154
154
  в ряду чипсов»: его проставляют себе все члены семейства — `Chip`, `ChipSort`,
155
- `ChipMultiselect`, `ChipSelect`, `ChipDatePicker`, `ChipSuggest`. Причём
155
+ `ChipMultiselect`, `ChipSelect`, `ChipDatePicker`, `ChipMonthYearPicker`, `ChipSuggest`. Причём
156
156
  `.chipGroupItem` не всегда совпадает с `.chip`: в `ChipSelect` / `ChipMultiselect`
157
157
  `.chip` лежит внутри элемента с `.chipGroupItem`. Раскладка `ChipGroup` на этот
158
158
  класс не опирается — в её стилях он не встречается. Не удалять и не переименовывать.
@@ -177,7 +177,7 @@ Default и Hover. Цвет текста (`Color_*`) от типа не зави
177
177
  компонент не генерирует (у `<span role="button">` нет нативного поведения кнопки),
178
178
  внешний `onKeyDown` вызывается всегда и после `preventDefault`. Если чипс должен
179
179
  срабатывать с клавиатуры, потребитель обрабатывает Enter/Space сам через `onKeyDown`.
180
- Обёртки семейства (`ChipSelect`, `ChipSuggest`, `ChipDatePicker`) делают это на своём уровне.
180
+ Обёртки семейства (`ChipSelect`, `ChipSuggest`, `ChipDatePicker`, `ChipMonthYearPicker`) делают это на своём уровне.
181
181
  - Фокус-стиль через `:focus-visible` (`Chip.Shadow_Focus`) — виден только
182
182
  при клавиатурной навигации. Нативная обводка на корневом элементе намеренно отключена
183
183
  (`outline: none` в LESS) в пользу `box-shadow` — убирать правило нельзя, иначе фокус
@@ -206,8 +206,8 @@ Default и Hover. Цвет текста (`Color_*`) от типа не зави
206
206
  - `ChipDropdownArrow` — стрелка дропдауна для `postfix`; объявлена как `React.FC`
207
207
  (не `forwardRef`), собственных обработчиков не имеет. Props: **обязательный**
208
208
  `rotated: boolean` (даёт класс `.rotated`) и `size?: EComponentSize`
209
- - `ChipSelect`, `ChipMultiselect`, `ChipSuggest`, `ChipDatePicker`, `ChipSort` —
210
- составные компоненты, использующие `Chip` как target-элемент дропдауна
209
+ - `ChipSelect`, `ChipMultiselect`, `ChipSuggest`, `ChipDatePicker`, `ChipMonthYearPicker`,
210
+ `ChipSort` — составные компоненты, использующие `Chip` как target-элемент дропдауна
211
211
  - `Badge` (`Badge.Dot`) — значок уведомлений при `showNotificationIcon`
212
212
  - `IconWrapper` — обёртка `prefix` / `postfix`, добавляет классы `hoverable` / `disabled`,
213
213
  управляющие цветом иконки при взаимодействии
@@ -239,6 +239,7 @@ Default и Hover. Цвет текста (`Color_*`) от типа не зави
239
239
  | 2026-07-29 | Создан документ (TRI-26, AI-Ready Phase 1) |
240
240
  | 2026-07-29 | AI-рефакторинг (TRI-26): codestyle-чистка `Chip.tsx` (константы-маппинги в UPPER_SNAKE_CASE, эквивалентные упрощения в `clsx`, JSDoc на `children` и `handleKeyDown`), unit-тесты расширены с 5 до 22 кейсов; публичный API, DOM и визуал не изменены |
241
241
  | 2026-07-29 | Правки по ревью PR #487: уточнена атрибуция класса `.chipGroupItem` (его проставляет семейство Chip*, а не раскладка `ChipGroup`) и описание API `ChipClearButton` / `ChipDropdownArrow` |
242
+ | 2026-09-07 | В семейство добавлен `ChipMonthYearPicker` — упомянут в списках потребителей `Chip` и `.chipGroupItem` |
242
243
 
243
244
  ---
244
245
 
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  component: ChipGroup
3
3
  category: Chips
4
- related: [Chip]
4
+ related: [Chip, TagGroup]
5
5
  tokens: []
6
6
  stories: stories/Chips/ChipGroup.stories.tsx
7
7
  version: "1.0"