@sberbusiness/triplex-next-mcp-server 0.24.0-beta1

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 (140) hide show
  1. package/README.md +124 -0
  2. package/bundle/.gitkeep +0 -0
  3. package/bundle/components/AbstractTree.md +198 -0
  4. package/bundle/components/AccordionBase.md +227 -0
  5. package/bundle/components/Alert.md +317 -0
  6. package/bundle/components/Amount.md +225 -0
  7. package/bundle/components/AmountField.md +289 -0
  8. package/bundle/components/Avatar.md +209 -0
  9. package/bundle/components/Badge.md +277 -0
  10. package/bundle/components/Body.md +167 -0
  11. package/bundle/components/Button.md +230 -0
  12. package/bundle/components/ButtonDropdown.md +159 -0
  13. package/bundle/components/ButtonIcon.md +122 -0
  14. package/bundle/components/Calendar.md +445 -0
  15. package/bundle/components/Card.md +374 -0
  16. package/bundle/components/CarouselExtended.md +250 -0
  17. package/bundle/components/Checkbox.md +126 -0
  18. package/bundle/components/CheckboxTree.md +280 -0
  19. package/bundle/components/CheckboxTreeExtended.md +321 -0
  20. package/bundle/components/CheckboxXGroup.md +68 -0
  21. package/bundle/components/CheckboxYGroup.md +67 -0
  22. package/bundle/components/Chip.md +404 -0
  23. package/bundle/components/ChipGroup.md +179 -0
  24. package/bundle/components/Col.md +221 -0
  25. package/bundle/components/CollapsableTree.md +255 -0
  26. package/bundle/components/Confirm.md +343 -0
  27. package/bundle/components/DateField.md +371 -0
  28. package/bundle/components/DatePickerExtended.md +342 -0
  29. package/bundle/components/DateRange.md +275 -0
  30. package/bundle/components/DesignTokens.md +385 -0
  31. package/bundle/components/Divider.md +196 -0
  32. package/bundle/components/DocumentNumberEdit.md +280 -0
  33. package/bundle/components/Dropdown.md +433 -0
  34. package/bundle/components/Ellipsis.md +208 -0
  35. package/bundle/components/EmptyView.md +99 -0
  36. package/bundle/components/ExpandAnimation.md +281 -0
  37. package/bundle/components/Footer.md +339 -0
  38. package/bundle/components/FormField.md +326 -0
  39. package/bundle/components/FormGroup.md +99 -0
  40. package/bundle/components/Gap.md +113 -0
  41. package/bundle/components/Header.md +280 -0
  42. package/bundle/components/HelpBox.md +154 -0
  43. package/bundle/components/IconWrapper.md +145 -0
  44. package/bundle/components/Island.md +197 -0
  45. package/bundle/components/IslandAccordion.md +230 -0
  46. package/bundle/components/IslandWidget.md +233 -0
  47. package/bundle/components/KeyDownListener.md +157 -0
  48. package/bundle/components/LightBox.md +322 -0
  49. package/bundle/components/Link.md +161 -0
  50. package/bundle/components/List.md +247 -0
  51. package/bundle/components/ListMaster.md +204 -0
  52. package/bundle/components/Loader.md +161 -0
  53. package/bundle/components/LoaderScreen.md +134 -0
  54. package/bundle/components/Marker.md +127 -0
  55. package/bundle/components/MarkerStatus.md +148 -0
  56. package/bundle/components/MediaWidth.md +169 -0
  57. package/bundle/components/MobileView.md +120 -0
  58. package/bundle/components/ModalWindow.md +308 -0
  59. package/bundle/components/MonthYearField.md +168 -0
  60. package/bundle/components/MultiselectField.md +213 -0
  61. package/bundle/components/Notification.md +233 -0
  62. package/bundle/components/NumberField.md +178 -0
  63. package/bundle/components/OrderedList.md +169 -0
  64. package/bundle/components/Overlay.md +237 -0
  65. package/bundle/components/Page.md +246 -0
  66. package/bundle/components/Pagination.md +298 -0
  67. package/bundle/components/Portal.md +119 -0
  68. package/bundle/components/Radio.md +174 -0
  69. package/bundle/components/Row.md +125 -0
  70. package/bundle/components/SMSField.md +160 -0
  71. package/bundle/components/SegmentedControl.md +235 -0
  72. package/bundle/components/SelectExtendedField.md +322 -0
  73. package/bundle/components/SelectField.md +213 -0
  74. package/bundle/components/Skeleton.md +129 -0
  75. package/bundle/components/Slider.md +157 -0
  76. package/bundle/components/SliderExtended.md +210 -0
  77. package/bundle/components/SliderRange.md +160 -0
  78. package/bundle/components/SmallInput.md +101 -0
  79. package/bundle/components/Spoiler.md +121 -0
  80. package/bundle/components/StatusTracker.md +151 -0
  81. package/bundle/components/Step.md +121 -0
  82. package/bundle/components/Stepper.md +163 -0
  83. package/bundle/components/Suggest.md +160 -0
  84. package/bundle/components/SuggestField.md +157 -0
  85. package/bundle/components/SwipeableArea.md +144 -0
  86. package/bundle/components/Table.md +199 -0
  87. package/bundle/components/Tabs.md +152 -0
  88. package/bundle/components/TabsExtended.md +273 -0
  89. package/bundle/components/TabsLine.md +189 -0
  90. package/bundle/components/Tag.md +135 -0
  91. package/bundle/components/TagColor.md +121 -0
  92. package/bundle/components/TagGroup.md +100 -0
  93. package/bundle/components/TextField.md +152 -0
  94. package/bundle/components/TextareaField.md +124 -0
  95. package/bundle/components/ThemeProvider.md +181 -0
  96. package/bundle/components/Tooltip.md +254 -0
  97. package/bundle/components/TopOverlay.md +145 -0
  98. package/bundle/components/TreeView.md +224 -0
  99. package/bundle/components/Triggers.md +121 -0
  100. package/bundle/guides/codestyle.md +205 -0
  101. package/bundle/guides/commits.md +166 -0
  102. package/bundle/guides/context.md +277 -0
  103. package/bundle/guides/stories.md +618 -0
  104. package/bundle/guides/template.md +154 -0
  105. package/bundle/guides/tests.md +301 -0
  106. package/bundle/icons.json +4156 -0
  107. package/bundle/manifest.json +796 -0
  108. package/bundle/release-notes/1.1.0.mdx +26 -0
  109. package/bundle/release-notes/1.1.1.mdx +14 -0
  110. package/bundle/release-notes/1.10.0.mdx +43 -0
  111. package/bundle/release-notes/1.11.0.mdx +19 -0
  112. package/bundle/release-notes/1.12.0.mdx +21 -0
  113. package/bundle/release-notes/1.13.0.mdx +43 -0
  114. package/bundle/release-notes/1.14.0.mdx +29 -0
  115. package/bundle/release-notes/1.15.0.mdx +56 -0
  116. package/bundle/release-notes/1.16.0.mdx +55 -0
  117. package/bundle/release-notes/1.17.0.mdx +15 -0
  118. package/bundle/release-notes/1.18.0.mdx +24 -0
  119. package/bundle/release-notes/1.19.0.mdx +48 -0
  120. package/bundle/release-notes/1.19.1.mdx +9 -0
  121. package/bundle/release-notes/1.2.0.mdx +80 -0
  122. package/bundle/release-notes/1.20.0.mdx +48 -0
  123. package/bundle/release-notes/1.21.0.mdx +66 -0
  124. package/bundle/release-notes/1.22.0.mdx +75 -0
  125. package/bundle/release-notes/1.23.0.mdx +50 -0
  126. package/bundle/release-notes/1.24.0.mdx +59 -0
  127. package/bundle/release-notes/1.25.0.mdx +52 -0
  128. package/bundle/release-notes/1.26.0.mdx +11 -0
  129. package/bundle/release-notes/1.3.0.mdx +21 -0
  130. package/bundle/release-notes/1.4.0.mdx +37 -0
  131. package/bundle/release-notes/1.5.0.mdx +24 -0
  132. package/bundle/release-notes/1.6.0.mdx +37 -0
  133. package/bundle/release-notes/1.6.1.mdx +11 -0
  134. package/bundle/release-notes/1.7.0.mdx +29 -0
  135. package/bundle/release-notes/1.8.0.mdx +38 -0
  136. package/bundle/release-notes/1.9.0.mdx +28 -0
  137. package/dist/index.d.ts +2 -0
  138. package/dist/index.js +324 -0
  139. package/dist/index.js.map +1 -0
  140. package/package.json +51 -0
@@ -0,0 +1,125 @@
1
+ ---
2
+ component: Row
3
+ category: Layout
4
+ figma-node: ""
5
+ figma-file: ""
6
+ related: [Col, Container]
7
+ tokens: []
8
+ stories: stories/Row/Row.stories.tsx
9
+ version: "1.0"
10
+ ---
11
+
12
+ # Row
13
+
14
+ ## Назначение
15
+
16
+ Строка grid-системы — `<div>` с горизонтальным flex-wrap layout'ом и
17
+ **отрицательными margin'ами по бокам** для компенсации gutter'ов
18
+ дочерних `Col`. Рекомендованный child — `Col`.
19
+
20
+ Используй когда: компонуешь форму/контент в колоночной сетке проекта.
21
+ Для нестандартного layout'а — flex/grid-CSS напрямую.
22
+
23
+ ---
24
+
25
+ ## Файловая структура
26
+
27
+ ```text
28
+ src/components/Row/
29
+ ├── Row.tsx # FC; <div> + flex layout
30
+ ├── styles/Row.module.less # margin -horizontal-gap, padding-bottom
31
+ ├── index.ts # Barrel
32
+ └── __tests__/
33
+ ```
34
+
35
+ ---
36
+
37
+ ## Варианты и props
38
+
39
+ ### `IRowProps extends React.HTMLAttributes<HTMLDivElement>`
40
+
41
+ | Prop | Тип | Default | Описание |
42
+ |---|---|---|---|
43
+ | `paddingBottom` | `boolean` | `true` | Нижний отступ строки. `false` — отключить. |
44
+ | `...HTMLAttributes` | — | — | На корневой `<div>`. |
45
+
46
+ - `paddingBottom: false` → класс `noPaddingBottom`.
47
+ - **Последняя `Row` в родителе** автоматически получает `padding-bottom: 0` через CSS-селектор `:last-child`.
48
+
49
+ ---
50
+
51
+ ## Ключевые особенности реализации
52
+
53
+ ### Compensating margins
54
+
55
+ ```less
56
+ .row {
57
+ display: flex;
58
+ flex-wrap: wrap;
59
+ margin-left: -@grid-horizontal-gap;
60
+ margin-right: -@grid-horizontal-gap;
61
+ padding-bottom: @row-bottom-padding;
62
+ }
63
+ ```
64
+
65
+ - Отрицательные margin компенсируют padding'и `Col`'ов — содержимое крайних колонок выровнено по краю Row.
66
+ - Стандартный bootstrap-style grid pattern.
67
+
68
+ ### `padding-bottom` через CSS, а не inline
69
+
70
+ - LESS-переменная `@row-bottom-padding` определяет величину.
71
+ - `:last-child` — обнуление: серия Row друг под другом не накапливает отступ снизу.
72
+
73
+ ### `flex-wrap: wrap`
74
+
75
+ - Колонки с суммарной шириной > 100% переносятся на новую строку (внутри одной Row).
76
+
77
+ ### **Только базовый `<div>`**
78
+
79
+ - Нет `forwardRef`, нет slots, нет компонентного API. Простой layout helper.
80
+
81
+ ---
82
+
83
+ ## Инварианты
84
+
85
+ - **`<div>` с `display: flex; flex-wrap: wrap`** — изменение типа layout — breaking.
86
+ - **`paddingBottom: true` default** — большинство строк с отступом.
87
+ - **`:last-child` снимает padding-bottom** — публичное CSS-поведение.
88
+ - **Отрицательные margin'ы** = ожидание дочерних `Col`'ов с padding'ами равными `@grid-horizontal-gap`.
89
+ - **`FC, не forwardRef`**.
90
+ - **Контракт child** — `Col` (на уровне типов не enforced).
91
+
92
+ ---
93
+
94
+ ## Accessibility
95
+
96
+ - Нейтральный `<div>` — без семантики. Если строка несёт смысл (form-row), потребитель добавляет `role` сам.
97
+
98
+ ---
99
+
100
+ ## Дизайн-токены
101
+
102
+ LESS-переменные (через `@import '../../../styles/style'`):
103
+ - `@grid-horizontal-gap` — горизонтальный gap.
104
+ - `@row-bottom-padding` — нижний отступ.
105
+
106
+ ---
107
+
108
+ ## Связанные компоненты
109
+
110
+ - `Col` — колонка, основной child.
111
+ - Прочая grid-система (`Container`) — если есть в проекте.
112
+
113
+ ---
114
+
115
+ ## Stories
116
+
117
+ `stories/Row/Row.stories.tsx`
118
+
119
+ ---
120
+
121
+ ## История изменений
122
+
123
+ | Дата | Изменение |
124
+ |---|---|
125
+ | 2026-04-18 | Создан документ |
@@ -0,0 +1,160 @@
1
+ ---
2
+ component: SMSField
3
+ category: Form Controls
4
+ figma-node: ""
5
+ figma-file: ""
6
+ related: [TextField, Tooltip, ButtonIcon, FormField]
7
+ tokens:
8
+ - --triplex-next-SMSField-Submit_Background_Default
9
+ - --triplex-next-SMSField-Submit_Background_Hover
10
+ - --triplex-next-SMSField-Submit_Background_Active
11
+ - --triplex-next-SMSField-Refresh_Fill_Empty
12
+ - --triplex-next-SMSField-Refresh_Fill_Full
13
+ - --triplex-next-SMSField-Refresh_Disabled
14
+ stories: stories/SMSField/SMSField.stories.tsx
15
+ version: "1.0"
16
+ ---
17
+
18
+ # SMSField
19
+
20
+ ## Назначение
21
+
22
+ Композитный компонент для ввода SMS-кода: поле ввода + кнопка отправки +
23
+ кнопка повторного запроса кода с обратным отсчётом + tooltip над Refresh.
24
+ Работает через `SMSFieldContext` — подкомпоненты обязаны быть детьми `SMSField`.
25
+
26
+ Используй когда: нужно подтверждение действия SMS-кодом (логин, оплата,
27
+ verification). Для произвольных цифровых полей используй `TextField` с
28
+ `type="tel"`.
29
+
30
+ ---
31
+
32
+ ## Файловая структура
33
+
34
+ ```text
35
+ src/components/SMSField/
36
+ ├── SMSField.tsx # FC + статичные Composition
37
+ ├── SMSFieldContext.ts # Shared state (code, disabled, size...)
38
+ ├── types.ts # ISMSFieldProps
39
+ ├── components/
40
+ │ ├── SMSFieldInput.tsx # <input> на базе FormField/FormFieldInput
41
+ │ ├── SMSFieldSubmit.tsx # ButtonIcon (CIRCLE) с SubmitIcon
42
+ │ ├── SMSFieldRefresh.tsx # ButtonIcon (SQUIRCLE) + круговой прогресс
43
+ │ ├── SMSFieldTooltip.tsx # Оборачивает Refresh в Tooltip
44
+ │ ├── RefreshIcon.tsx
45
+ │ └── SubmitIcon.tsx
46
+ ├── styles/
47
+ └── index.ts
48
+ ```
49
+
50
+ ---
51
+
52
+ ## Slot-компоненты
53
+
54
+ | Slot | Props | Назначение |
55
+ |---|---|---|
56
+ | `SMSField.Input` | `ISMSFieldInputProps` (`InputHTMLAttributes` + `counter?`, `description?`) | Поле ввода кода; фильтрует ввод (только цифры), `maxLength = 8` по умолчанию, `Enter` → `onSubmitCode`. |
57
+ | `SMSField.Submit` | `ButtonHTMLAttributes` | Кнопка отправки (ButtonIcon CIRCLE); автоматически disabled, когда `code === ""`. |
58
+ | `SMSField.Refresh` | `countdownTime`, `countdownTimeLeft`, `onRefresh` | Кнопка запроса кода с круговым прогрессом; disabled пока `countdownTimeLeft > 0`. |
59
+ | `SMSField.Tooltip` | `Partial<ITooltipProps>` + `message`, `children` (ReactElement), `targetRef` | Оборачивает `Refresh` (через `targetRef`) в `Tooltip` с hover-триггером. |
60
+
61
+ ---
62
+
63
+ ## Варианты и props
64
+
65
+ ### `ISMSFieldProps`
66
+
67
+ | Prop | Тип | Default | Описание |
68
+ |---|---|---|---|
69
+ | `code` | `string` | — | Текущее значение кода. |
70
+ | `onChangeCode` | `(code: string) => void` | — | Вызывается при валидном вводе (только цифры). |
71
+ | `onSubmitCode` | `(code: string) => void` | — | Вызывается из `SMSField.Submit` и по Enter в `SMSField.Input`. |
72
+ | `size` | `EComponentSize` | — | Прокидывается в дочерние FormField/ButtonIcon. |
73
+ | `disabled?` | `boolean` | `false` | Блокирует все вложенные контролы. |
74
+ | `error?` | `boolean` | `false` | Устанавливает `EFormFieldStatus.ERROR` у `SMSField.Input`. |
75
+ | `...HTMLAttributes<HTMLDivElement>` | — | — | Прокидываются на корневой div. |
76
+ | `TestProps` | — | — | `data-test-*` атрибуты. |
77
+
78
+ ### `ISMSFieldInputProps`
79
+
80
+ | Prop | Тип | Default | Описание |
81
+ |---|---|---|---|
82
+ | `counter?` | `ReactNode` | — | Отображается в `FormFieldCounter` внутри description. |
83
+ | `description?` | `ReactNode` | — | Текст под полем. |
84
+ | `maxLength?` | `number` | `8` | Максимальная длина кода. |
85
+ | `...InputHTMLAttributes` | — | — | Стандартные атрибуты input. |
86
+
87
+ ### `ISMSFieldRefreshProps`
88
+
89
+ | Prop | Тип | Описание |
90
+ |---|---|---|
91
+ | `countdownTime` | `number` | Полное время отсчёта (сек). Для расчёта прогресса. |
92
+ | `countdownTimeLeft` | `number` | Оставшееся время (сек). Если `> 0` — кнопка disabled. |
93
+ | `onRefresh` | `() => void` | Вызывается при клике. |
94
+
95
+ ---
96
+
97
+ ## Контекст (`SMSFieldContext`)
98
+
99
+ Поля: `code`, `disabled`, `error`, `onChangeCode`, `onSubmitCode`, `size`,
100
+ `sizeClassName`, `disabledSubmit` / `setDisabledSubmit`, `tooltipId` / `setTooltipId`.
101
+
102
+ - `disabledSubmit` — вычисляется в `SMSField.Submit` (через `useEffect`) и
103
+ читается в `SMSField.Input` для обработки Enter.
104
+ - `tooltipId` — генерируется в `SMSField.Tooltip` и читается в `SMSField.Refresh`
105
+ для `aria-describedby`.
106
+
107
+ ---
108
+
109
+ ## Инварианты
110
+
111
+ - **Регэксп ввода `/^[0-9]*$/`** — только цифры; нецифровой ввод отбрасывается в `SMSFieldInput.handleChange`.
112
+ - **`maxLength` default `8`** — переопределяемо, но не нужно без причины.
113
+ - **Enter в `Input`** вызывает `onSubmitCode(code)` только когда `!disabledSubmit`.
114
+ - **`Submit` disabled, если `code === ""`** — «пустой» код не отправляется.
115
+ - **`Refresh` disabled, если `countdownTimeLeft > 0`** — пользователь не может запросить код повторно до окончания отсчёта.
116
+ - **`Refresh` остаётся enabled при `allDisabled && error`** (`(allDisabled && !error) || disabled || isSmsCountdownTicking`) — пользователь должен иметь возможность перезапросить код при ошибке.
117
+ - **`SMSField.Tooltip` требует `targetRef`** — ref на Refresh-кнопку.
118
+ - **`ISMSFieldTooltipProps.children: React.ReactElement`** — единственный ребёнок.
119
+ - **`SMSField` — FC** (не forwardRef). `Input`, `Refresh` — forwardRef на соответствующие HTML-элементы.
120
+ - **`FormField`/`FormFieldInput`** используется внутри Input — это фиксирует error/disabled-стилизацию.
121
+
122
+ ---
123
+
124
+ ## Accessibility
125
+
126
+ - `SMSField.Input` наследует accessibility от `FormFieldInput`.
127
+ - `SMSField.Refresh` получает `aria-describedby={tooltipId}` → screen reader озвучивает tooltip как описание кнопки.
128
+ - `autoComplete="off"` захардкожен на input — предотвращает автозаполнение.
129
+ - Tooltip-триггер — `hover`; для клавиатуры — фокус на кнопке активирует tooltip в Tooltip-компоненте (см. Tooltip-AI).
130
+
131
+ ---
132
+
133
+ ## Связанные компоненты
134
+
135
+ - `FormField`, `FormFieldInput`, `FormFieldCounter`, `FormFieldDescription` — основа поля ввода.
136
+ - `ButtonIcon` — основа для Submit/Refresh.
137
+ - `Tooltip` — обёртка Refresh.
138
+ - `TextField` — общий аналог для не-SMS кейсов.
139
+
140
+ ---
141
+
142
+ ## Stories
143
+
144
+ Основные истории: `stories/SMSField/SMSField.stories.tsx`
145
+ Примеры: `stories/SMSField/examples/`
146
+
147
+ | Story | Что демонстрирует |
148
+ |---|---|
149
+ | `Playground` | Интерактивный контроль |
150
+ | `Error` | Состояние ошибки + Refresh доступен |
151
+ | `Disabled` | Общий disabled |
152
+ | `Sizes` | Все размеры (MD/LG) |
153
+
154
+ ---
155
+
156
+ ## История изменений
157
+
158
+ | Дата | Изменение |
159
+ |---|---|
160
+ | 2026-04-18 | Создан документ |
@@ -0,0 +1,235 @@
1
+ ---
2
+ component: SegmentedControl
3
+ category: Form Controls
4
+ figma-node: ""
5
+ figma-file: ""
6
+ related: [Tabs, Radio, Checkbox, ButtonBase, IconWrapper]
7
+ tokens: []
8
+ stories: stories/SegmentedControl/SegmentedControl.stories.tsx
9
+ version: "1.0"
10
+ ---
11
+
12
+ # SegmentedControl
13
+
14
+ ## Назначение
15
+
16
+ Группа «сегментов»-кнопок: выбор **одного** (`SINGLE`) или **нескольких**
17
+ (`MULTIPLE`) вариантов из небольшого набора. Каждый сегмент —
18
+ `<button aria-pressed>`. Похоже на iOS UISegmentedControl. Управление
19
+ выбором — через `value` + `onSelect` снаружи; внутреннее состояние нет.
20
+
21
+ Используй когда: 2–6 коротких опций (фильтры, переключатели view).
22
+ Для много опций / длинных лейблов — `Tabs` или `SelectField`. Для
23
+ визуально не-кнопочного выбора — `Radio` / `Checkbox` группы.
24
+
25
+ ---
26
+
27
+ ## Файловая структура
28
+
29
+ ```text
30
+ src/components/SegmentedControl/
31
+ ├── SegmentedControl.tsx # forwardRef + Object.assign({Segment})
32
+ ├── SegmentedControlSegment.tsx # FC; ButtonBase + IconWrapper(active=selected)
33
+ ├── SegmentedControlContext.ts # { type, value, disabled, onSegmentSelect }
34
+ ├── enums.ts # ESegmentedControlType / Theme / Size
35
+ ├── types.ts # discriminated union по type
36
+ ├── styles/SegmentedControl.module.less + SegmentedControlSegment.module.less
37
+ ├── index.ts # Barrel
38
+ └── __tests__/
39
+ ```
40
+
41
+ ---
42
+
43
+ ## Compound-структура
44
+
45
+ ```tsx
46
+ <SegmentedControl
47
+ type={ESegmentedControlType.SINGLE}
48
+ theme={ESegmentedControlTheme.GENERAL_1}
49
+ size={ESegmentedControlSize.MD}
50
+ value={selected} // string в SINGLE; string[] в MULTIPLE
51
+ onSelect={setSelected}
52
+ >
53
+ <SegmentedControl.Segment value="day">День</SegmentedControl.Segment>
54
+ <SegmentedControl.Segment value="week">Неделя</SegmentedControl.Segment>
55
+ <SegmentedControl.Segment value="month">Месяц</SegmentedControl.Segment>
56
+ </SegmentedControl>
57
+ ```
58
+
59
+ ---
60
+
61
+ ## Варианты и props
62
+
63
+ ### SegmentedControl — `TSegmentedControlProps = ISegmentedControlSingleProps | ISegmentedControlMultipleProps`
64
+
65
+ #### Общее (`ISegmentedControlCommonProps extends Omit<HTMLAttributes<HTMLDivElement>, "onSelect">`)
66
+
67
+ | Prop | Тип | Default | Описание |
68
+ |---|---|---|---|
69
+ | `theme` | `ESegmentedControlTheme` | — (обязателен) | `GENERAL_1` / `GENERAL_2` / `SECONDARY_1` / `SECONDARY_2`. |
70
+ | `size` | `ESegmentedControlSize` | — (обязателен) | `SM` / `MD` / `LG`. **Собственный enum**, не `EComponentSize`. |
71
+ | `disabled?` | `boolean` | `false` | Дизейблит **все сегменты** (через context). |
72
+ | `...HTMLAttributes` | — | — | На корневой `<div>`. |
73
+
74
+ #### SINGLE
75
+
76
+ | Prop | Тип | Описание |
77
+ |---|---|---|
78
+ | `type` | `ESegmentedControlType.SINGLE` | Discriminator. |
79
+ | `value` | `string` | Текущий выбранный. |
80
+ | `onSelect` | `(value: string) => void` | Передаётся **новый** value. |
81
+
82
+ #### MULTIPLE
83
+
84
+ | Prop | Тип | Описание |
85
+ |---|---|---|
86
+ | `type` | `ESegmentedControlType.MULTIPLE` | Discriminator. |
87
+ | `value` | `string[]` | Массив выбранных. |
88
+ | `onSelect` | `(value: string[]) => void` | Передаётся **полный новый массив** (не дельта). |
89
+
90
+ ### SegmentedControl.Segment — `ISegmentedControlSegmentProps extends IButtonBaseProps`
91
+
92
+ | Prop | Тип | Описание |
93
+ |---|---|---|
94
+ | `value` | `string` | Идентификатор сегмента (соответствует `value` SegmentedControl'а). |
95
+ | `disabled?` | от ButtonBase | Локальный disabled (мержится с групповым). |
96
+ | `title?` | `string` | HTML-атрибут tooltip; если пусто и `children` — string, берётся children. |
97
+ | `onClick?` | от ButtonBase | Дополнительный handler (вызывается **после** `onSegmentSelect`). |
98
+ | `children` | `ReactNode` | Контент кнопки. |
99
+ | `...IButtonBaseProps` | — | — |
100
+
101
+ ### Context — `ISegmentedControlContextType`
102
+
103
+ ```ts
104
+ { type, value, disabled, onSegmentSelect: ({ selected, value }) => void }
105
+ ```
106
+
107
+ ### Enums
108
+
109
+ - `ESegmentedControlType.SINGLE | MULTIPLE`
110
+ - `ESegmentedControlTheme.GENERAL_1 | GENERAL_2 | SECONDARY_1 | SECONDARY_2`
111
+ - `ESegmentedControlSize.SM | MD | LG`
112
+
113
+ ---
114
+
115
+ ## Ключевые особенности реализации
116
+
117
+ ### Discriminated union по `type`
118
+
119
+ ```ts
120
+ type TSegmentedControlProps = ISegmentedControlSingleProps | ISegmentedControlMultipleProps;
121
+ ```
122
+
123
+ - TS гарантирует: `value: string` ↔ `type: SINGLE`; `value: string[]` ↔ `type: MULTIPLE`. `onSelect` сигнатура соответственно.
124
+ - Discriminator обязателен на потребителе.
125
+
126
+ ### `handleSegmentSelect` — превращение `(value, selected)` в новый список
127
+
128
+ ```ts
129
+ case SINGLE: if (selected) onSelect(newValue); // selected всегда true в Segment.handleClick для SINGLE
130
+ case MULTIPLE:
131
+ if (selected) onSelect([...value, newValue])
132
+ else onSelect(value.filter(v => v !== newValue));
133
+ ```
134
+
135
+ - В SINGLE: повторный клик **не** снимает выбор (`selected: true` всегда отправляется).
136
+ - В MULTIPLE: клик инвертирует — добавление или удаление из массива.
137
+
138
+ ### `Segment.handleClick`
139
+
140
+ ```ts
141
+ case SINGLE: onSegmentSelect({ value, selected: true });
142
+ case MULTIPLE: onSegmentSelect({ value, selected: !selected });
143
+ ```
144
+
145
+ ### `IconWrapper displayContents disabled active={selected}`
146
+
147
+ ```tsx
148
+ <IconWrapper displayContents disabled={...} active={selected}>
149
+ <ButtonBase aria-pressed={selected} ...>
150
+ <span className={styles.content}>{children}</span>
151
+ </ButtonBase>
152
+ </IconWrapper>
153
+ ```
154
+
155
+ - **`displayContents`** — IconWrapper не вводит свой DOM-узел.
156
+ - **`active`/`disabled`** — публичные классы из `@sberbusiness/icons-next`, влияющие на цвет иконок внутри `children`.
157
+ - `aria-pressed` — корректная семантика toggle-button (toggle для MULTIPLE; для SINGLE — тоже технически верно).
158
+
159
+ ### Default-значения context
160
+
161
+ ```ts
162
+ { type: SINGLE, value: "", disabled: false, onSegmentSelect: noop }
163
+ ```
164
+
165
+ - При использовании `Segment` вне `SegmentedControl` — `value === ""`, ничего не выбрано, клик noop. Безопасно.
166
+
167
+ ### `getTitle`
168
+
169
+ - Если `title` передан — использует его.
170
+ - Иначе — если `children: string` — берёт children (для tooltip'а на overflow-text).
171
+ - Иначе — undefined.
172
+
173
+ ### Темы — 4 варианта
174
+
175
+ - `GENERAL_1`, `GENERAL_2`, `SECONDARY_1`, `SECONDARY_2`. Проверять в LESS — отличия в фоне/border/цвете.
176
+
177
+ ### Sizes — собственный enum
178
+
179
+ - `ESegmentedControlSize.SM/MD/LG` — **не** `EComponentSize`. Расхождение с типичной практикой (исторически).
180
+
181
+ ---
182
+
183
+ ## Инварианты
184
+
185
+ - **`type` обязателен**, discriminator union'а. Меняет тип `value`/`onSelect`.
186
+ - **SINGLE: повторный клик не снимает выбор** (нет «снять всё»). Для optional-single — потребитель сам реализует.
187
+ - **MULTIPLE: `onSelect` получает полный массив**, не дельту.
188
+ - **`theme` и `size` обязательны** (нет default'ов).
189
+ - **`size` — собственный enum** `ESegmentedControlSize`, не `EComponentSize`.
190
+ - **`disabled` через context** — групповой; локальный `Segment.disabled` мержится.
191
+ - **`aria-pressed={selected}`** — публичный a11y-контракт.
192
+ - **`SegmentedControlContext` default — `{ SINGLE, "", false, noop }`** — Segment безопасен вне группы.
193
+ - **ForwardRef** на корне SegmentedControl, **`FC`** на Segment.
194
+ - **`IconWrapper displayContents`** — обязателен для не-DOM обёртки.
195
+ - **`Segment.onClick` вызывается после `onSegmentSelect`** — порядок может быть важен.
196
+
197
+ ---
198
+
199
+ ## Accessibility
200
+
201
+ - `<button aria-pressed>` — корректная семантика toggle (особенно для MULTIPLE).
202
+ - **Нет `role="group"` / `aria-label`** на контейнере — потребитель должен передать `aria-label`/`aria-labelledby` через `...rest`.
203
+ - **Tab-навигация** — каждый Segment в фокусе по Tab. Для radio-like UX (стрелки) семантика отсутствует — это `<button>`, не `<input type="radio">`.
204
+ - `disabled` через `<button disabled>` — корректно.
205
+ - `title` атрибут — даёт tooltip; для длинных лейблов с overflow — UX полезно.
206
+
207
+ ---
208
+
209
+ ## Дизайн-токены
210
+
211
+ Нет собственных — наследуются от `Button`/тем (`general_1`, `secondary_1`, ...). Token-структура — в `styles/`.
212
+
213
+ ---
214
+
215
+ ## Связанные компоненты
216
+
217
+ - `Tabs` — для большего количества опций / длинных лейблов.
218
+ - `Radio` / `Checkbox` (+ `RadioYGroup` / аналог) — альтернатива для form-input семантики.
219
+ - `ButtonBase` — нижележащий primitive.
220
+ - `IconWrapper` — управление цветом иконок внутри (active/disabled).
221
+
222
+ ---
223
+
224
+ ## Stories
225
+
226
+ `stories/SegmentedControl/SegmentedControl.stories.tsx`
227
+ Примеры: `stories/SegmentedControl/examples/`
228
+
229
+ ---
230
+
231
+ ## История изменений
232
+
233
+ | Дата | Изменение |
234
+ |---|---|
235
+ | 2026-04-18 | Создан документ |