@sberbusiness/triplex-next-mcp-server 1.46.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.
- package/README.md +7 -7
- package/bundle/components/AmountField.md +222 -323
- package/bundle/components/Avatar.md +138 -146
- package/bundle/components/CardStatic.md +402 -0
- package/bundle/components/CheckboxTree.md +157 -215
- package/bundle/components/CheckboxTreeExtended.md +7 -7
- package/bundle/components/ChipGroup.md +1 -1
- package/bundle/components/ChipMonthYearPicker.md +4 -4
- package/bundle/components/ChipSuggest.md +7 -6
- package/bundle/components/CollapsibleTree.md +8 -4
- package/bundle/components/CollapsibleTreeExtended.md +8 -6
- package/bundle/components/Confirm.md +346 -281
- package/bundle/components/DateRange.md +351 -210
- package/bundle/components/FooterPage.md +1 -0
- package/bundle/components/FormField.md +11 -4
- package/bundle/components/FormGroup.md +3 -2
- package/bundle/components/LightBox.md +8 -0
- package/bundle/components/ListMaster.md +2 -2
- package/bundle/components/ListTableItem.md +6 -6
- package/bundle/components/MonthYearField.md +15 -4
- package/bundle/components/Slider.md +389 -97
- package/bundle/components/SliderRange.md +486 -105
- package/bundle/components/StatusTracker.md +386 -94
- package/bundle/components/Stepper.md +355 -102
- package/bundle/components/StepperExtended.md +633 -0
- package/bundle/components/SuggestField.md +1049 -96
- package/bundle/components/SwipeableArea.md +7 -7
- package/bundle/components/Tabs.md +265 -92
- package/bundle/components/TabsExtended.md +18 -18
- package/bundle/components/TabsLine.md +301 -127
- package/bundle/components/TagColor.md +187 -66
- package/bundle/components/TagGroup.md +184 -48
- package/bundle/components/TextareaField.md +278 -70
- package/bundle/components/TopOverlay.md +17 -1
- package/bundle/components/TreeView.md +16 -7
- package/bundle/components/TriggerClickOnKeyDownEvent.md +277 -0
- package/bundle/components/UploadZone.md +318 -0
- package/bundle/guides/codestyle.md +16 -1
- package/bundle/guides/commits.md +47 -1
- package/bundle/guides/tests.md +23 -2
- package/bundle/manifest.json +48 -21
- package/bundle/release-notes/1.47.0.mdx +115 -0
- package/bundle/release-notes/1.48.0.mdx +68 -0
- package/bundle/release-notes/1.49.0.mdx +7 -0
- package/bundle/tokens.json +3 -3
- package/dist/index.js +220 -6
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
|
@@ -1,9 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
component: CheckboxTree
|
|
3
3
|
category: Inputs
|
|
4
|
-
|
|
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
|
-
|
|
18
|
-
|
|
19
|
-
(
|
|
14
|
+
Дерево чекбоксов, которое строится из массива данных и само связывает состояние
|
|
15
|
+
родителей и потомков. Обёртка над `CheckboxTreeExtended`: разметку узлов
|
|
16
|
+
компонент формирует сам, потребитель передаёт только описание дерева
|
|
17
|
+
(`ICheckboxTreeCheckboxData[]`) и получает в `onChange` новый массив с
|
|
18
|
+
пересчитанными флагами `checked` и `bulk`.
|
|
20
19
|
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
компонент сам рендерит `CheckboxTreeExtended.Node` + `.Checkbox`, управляет
|
|
24
|
-
состоянием и отдаёт обновлённый массив в `onChange`.
|
|
20
|
+
Каскадная логика — единственное, что компонент добавляет к
|
|
21
|
+
`CheckboxTreeExtended`:
|
|
25
22
|
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
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
|
-
|
|
48
|
-
|
|
49
|
-
|
|
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
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
`
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
| `
|
|
69
|
-
| `
|
|
70
|
-
| `
|
|
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
|
-
|
|
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
|
-
|
|
136
|
-
|
|
137
|
-
`
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
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
|
-
|
|
167
|
-
Это совпадает с тем, что нужно React для перерендера при `useState`
|
|
168
|
-
(новая ссылка на массив).
|
|
72
|
+
### Утилиты пересчёта
|
|
169
73
|
|
|
170
|
-
|
|
74
|
+
`utils.ts` экспортируется через barrel, поэтому три функции — часть публичного
|
|
75
|
+
API. Они предназначены для случая, когда потребитель считает состояние дерева
|
|
76
|
+
сам; рабочий пример — `stories/Chips/examples/ChipMultiselect/WithCheckboxTreeExample.tsx`,
|
|
77
|
+
где они импортируются из `@sberbusiness/triplex-next` и пересчитывают дерево
|
|
78
|
+
опций внутри `ChipMultiselect`:
|
|
171
79
|
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
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
|
-
|
|
188
|
-
|
|
189
|
-
<CheckboxTreeExtended.Checkbox {...props} onChange={...} bulk={...} checked={...}>
|
|
190
|
-
{checkbox.label}
|
|
191
|
-
</CheckboxTreeExtended.Checkbox>
|
|
192
|
-
)}
|
|
193
|
-
```
|
|
86
|
+
Порядок обхода в `traverseCheckboxes` — не деталь реализации, а контракт:
|
|
87
|
+
`checkParentCheckboxes` работает корректно только если потомки уже пересчитаны.
|
|
194
88
|
|
|
195
|
-
|
|
196
|
-
aria-атрибуты). CheckboxTree передаёт их наружу и добавляет свои
|
|
197
|
-
onChange/bulk/checked. **Не упрощать** — CheckboxTreeExtended полагается
|
|
198
|
-
на render-проп для управления фокусом и ARIA.
|
|
89
|
+
---
|
|
199
90
|
|
|
200
|
-
|
|
91
|
+
## Дизайн-токены
|
|
201
92
|
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
`
|
|
205
|
-
|
|
206
|
-
|
|
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
|
-
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
- **`
|
|
219
|
-
|
|
220
|
-
- **`
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
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
|
-
##
|
|
231
|
-
|
|
232
|
-
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
(
|
|
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
|
-
|
|
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
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
- `
|
|
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-
|
|
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
|
-
- **`
|
|
118
|
-
|
|
119
|
-
`
|
|
120
|
-
`
|
|
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
|
-
`
|
|
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
|
|
|
@@ -216,7 +216,7 @@ export const DefaultExample = () => {
|
|
|
216
216
|
<ChipMonthYearPicker
|
|
217
217
|
value={value}
|
|
218
218
|
label="MonthYear label"
|
|
219
|
-
placeholder="
|
|
219
|
+
placeholder="Select to proceed"
|
|
220
220
|
onChange={setValue}
|
|
221
221
|
alignment={EDropdownAlignment.LEFT}
|
|
222
222
|
size={EComponentSize.MD}
|
|
@@ -248,7 +248,7 @@ const TypeItem = ({ type, label }: TypeItemProps) => {
|
|
|
248
248
|
<ChipMonthYearPicker
|
|
249
249
|
value={value}
|
|
250
250
|
label="MonthYear label"
|
|
251
|
-
placeholder="
|
|
251
|
+
placeholder="Select to proceed"
|
|
252
252
|
clearButtonProps={{ "aria-label": "Очистить" }}
|
|
253
253
|
type={type}
|
|
254
254
|
alignment={EDropdownAlignment.LEFT}
|
|
@@ -296,7 +296,7 @@ const SizeItem = ({ size, label }: SizeItemProps) => {
|
|
|
296
296
|
alignment={EDropdownAlignment.LEFT}
|
|
297
297
|
value={value}
|
|
298
298
|
label="MonthYear label"
|
|
299
|
-
placeholder="
|
|
299
|
+
placeholder="Select to proceed"
|
|
300
300
|
clearButtonProps={{ "aria-label": "Очистить" }}
|
|
301
301
|
onChange={setValue}
|
|
302
302
|
/>
|
|
@@ -332,7 +332,7 @@ export const WithCustomDisplayedValueExample = () => {
|
|
|
332
332
|
<ChipMonthYearPicker
|
|
333
333
|
value={value}
|
|
334
334
|
label="MonthYear label"
|
|
335
|
-
placeholder="
|
|
335
|
+
placeholder="Select to proceed"
|
|
336
336
|
onChange={setValue}
|
|
337
337
|
alignment={EDropdownAlignment.LEFT}
|
|
338
338
|
size={EComponentSize.MD}
|
|
@@ -133,12 +133,13 @@ version: "1.0"
|
|
|
133
133
|
переименование файла в папке `Suggest` ломает мобильный режим `ChipSuggest`.
|
|
134
134
|
- Корневой `<div>` всегда получает класс `chipGroupItem` — на нём держатся отступы внутри
|
|
135
135
|
`ChipGroup`.
|
|
136
|
-
- `setForwardedRef`
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
136
|
+
- `setForwardedRef` живёт в `src/helpers/setForwardedRef.ts` — внутренний хелпер,
|
|
137
|
+
общий для `AmountField`, `ChipSuggest`, `FormField`, `List` и `SliderExtended`.
|
|
138
|
+
Из barrel не экспортируется и в публичные `src/utils` не переносится. Импортируется
|
|
139
|
+
относительным путём (`../../../helpers/setForwardedRef`), не через алиас
|
|
140
|
+
`@sberbusiness/triplex-next/...` — см. `docs/ai/codestyle.md`. Не плоди локальные копии:
|
|
141
|
+
та же пятистрочка ещё инлайнится в ряде компонентов (включая `Suggest.tsx`) — при
|
|
142
|
+
случае сошлись на общий модуль.
|
|
142
143
|
|
|
143
144
|
---
|
|
144
145
|
|
|
@@ -122,10 +122,13 @@ CollapsibleTree.Header_Shadow_Focus
|
|
|
122
122
|
|
|
123
123
|
## Инварианты
|
|
124
124
|
|
|
125
|
-
- **`CollapsibleTree` — FC без `forwardRef`.**
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
class-компонент
|
|
125
|
+
- **`CollapsibleTree` — FC без `forwardRef`.** С 1.47.0 это уже не общий
|
|
126
|
+
инвариант семейства: `CollapsibleTreeExtended` и `TreeView` под ним `ref`
|
|
127
|
+
пробрасывают на корневой `<ul role="tree">`, а `CollapsibleTree` его
|
|
128
|
+
по-прежнему теряет. Прежнее обоснование («под капотом class-компонент
|
|
129
|
+
`TreeView`») больше не действует — это оставшийся долг, а не решение.
|
|
130
|
+
Починка — наблюдаемое изменение публичного API, отдельная задача с записью
|
|
131
|
+
в release notes.
|
|
129
132
|
- **`CollapsibleTreeNodeHeader` — `forwardRef<HTMLButtonElement>`** — ref
|
|
130
133
|
идёт на корневой `<button>`, чтобы потребитель мог управлять фокусом
|
|
131
134
|
заголовка (например, программно фокусировать конкретный узел).
|
|
@@ -227,6 +230,7 @@ CollapsibleTree.Header_Shadow_Focus
|
|
|
227
230
|
| 2026-07-06 | Шеврон открытого узла теперь указывает вверх (`rotate(-90deg)`), а не вниз |
|
|
228
231
|
| 2026-07-06 | Добавлено свойство `disabled` у `ICollapsibleTreeNodeBranch` и поддержка пропа `disabled` в `CollapsibleTreeNodeHeader`. Без новых токенов: шеврон приглушается глобальными классами `hoverable disabled` (icons-next), текст — через `type={EFontType.DISABLED}` у лейбла |
|
|
229
232
|
| 2026-07-14 | Глобальный класс `hoverable` (icons-next) на кнопке заголовка теперь стоит всегда, а не только при `disabled` — для корректной окраски шеврона. Добавлен класс `active` на раскрытый узел с детьми (по паттерну `IconWrapper`/`Button`) |
|
|
233
|
+
| 2026-09-17 | TRI-22: инвариант про отсутствие `forwardRef` переформулирован — `CollapsibleTreeExtended` и `TreeView` под ним теперь пробрасывают `ref` на корневой `<ul>`, поэтому прежнее обоснование не действует, а `CollapsibleTree` остаётся долгом |
|
|
230
234
|
|
|
231
235
|
---
|
|
232
236
|
|