@sberbusiness/triplex-next-mcp-server 1.48.0-beta → 1.50.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/bundle/components/Amount.md +2 -1
- package/bundle/components/Badge.md +2 -1
- package/bundle/components/BodyPage.md +2 -1
- package/bundle/components/Button.md +5 -1
- package/bundle/components/ButtonDropdownExtended.md +3 -1
- package/bundle/components/ButtonIcon.md +3 -1
- package/bundle/components/Calendar.md +3 -1
- package/bundle/components/CardAction.md +1 -0
- package/bundle/components/CheckboxXGroup.md +2 -1
- package/bundle/components/Chip.md +2 -1
- package/bundle/components/ChipMonthYearPicker.md +2 -1
- package/bundle/components/ChipMultiselect.md +4 -1
- package/bundle/components/ChipOptions.md +2 -1
- package/bundle/components/ChipSort.md +4 -1
- package/bundle/components/CollapsibleTree.md +2 -1
- package/bundle/components/ComposedKeyDownListener.md +4 -3
- package/bundle/components/DateField.md +7 -1
- package/bundle/components/Divider.md +85 -138
- package/bundle/components/DocumentNumberEdit.md +173 -214
- package/bundle/components/Dropdown.md +11 -1
- package/bundle/components/Ellipsis.md +131 -147
- package/bundle/components/FooterPage.md +3 -1
- package/bundle/components/FormField.md +2 -1
- package/bundle/components/Gap.md +1 -1
- package/bundle/components/Header.md +1 -0
- package/bundle/components/HeaderPage.md +3 -1
- package/bundle/components/HelpBox.md +2 -1
- package/bundle/components/IconWrapper.md +10 -6
- package/bundle/components/ImageGallery.md +2 -1
- package/bundle/components/ImageGalleryExtended.md +2 -1
- package/bundle/components/IslandAccordion.md +6 -1
- package/bundle/components/KeyDownListener.md +3 -2
- package/bundle/components/LightBox.md +7 -1
- package/bundle/components/List.md +2 -1
- package/bundle/components/ListActionItem.md +2 -1
- package/bundle/components/ListItem.md +3 -1
- package/bundle/components/ListItemControlsButton.md +2 -1
- package/bundle/components/ListItemSelectable.md +2 -1
- package/bundle/components/ListMasterFooter.md +2 -1
- package/bundle/components/ListMasterHeader.md +2 -1
- package/bundle/components/ListSortable.md +2 -1
- package/bundle/components/ListSortableItem.md +2 -1
- package/bundle/components/ListTableItem.md +4 -1
- package/bundle/components/LoaderMiddle.md +5 -1
- package/bundle/components/LoaderScreen.md +7 -1
- package/bundle/components/LoaderSmall.md +10 -4
- package/bundle/components/MaskedField.md +3 -1
- package/bundle/components/MediaWidth.md +2 -1
- package/bundle/components/MobileView.md +2 -1
- package/bundle/components/ModalWindow.md +4 -1
- package/bundle/components/MonthYearField.md +4 -1
- package/bundle/components/Notification.md +1 -0
- package/bundle/components/Overlay.md +7 -2
- package/bundle/components/Pagination.md +1 -7
- package/bundle/components/Radio.md +238 -115
- package/bundle/components/RadioXGroup.md +259 -0
- package/bundle/components/RadioYGroup.md +266 -0
- package/bundle/components/SegmentedControl.md +505 -158
- package/bundle/components/SelectExtendedField.md +2 -1
- package/bundle/components/SelectField.md +4 -1
- package/bundle/components/Skeleton.md +140 -69
- package/bundle/components/Spoiler.md +245 -63
- package/bundle/components/Step.md +257 -68
- package/bundle/components/SuggestField.md +30 -18
- package/bundle/components/Tabs.md +2 -1
- package/bundle/components/TabsLine.md +2 -1
- package/bundle/components/TextField.md +8 -4
- package/bundle/components/TextareaField.md +2 -1
- package/bundle/components/ThemeProvider.md +2 -1
- package/bundle/components/Tooltip.md +6 -3
- package/bundle/guides/context.md +4 -1
- package/bundle/manifest.json +32 -10
- package/bundle/release-notes/1.49.0.mdx +78 -0
- package/bundle/release-notes/1.50.0.mdx +35 -0
- package/bundle/release-notes/1.51.0.mdx +7 -0
- package/bundle/tokens.json +161 -11
- package/package.json +1 -1
|
@@ -1,10 +1,58 @@
|
|
|
1
1
|
---
|
|
2
2
|
component: SegmentedControl
|
|
3
|
-
category:
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
3
|
+
category: SegmentedControl
|
|
4
|
+
related: [Tabs, TabsLine, IconWrapper]
|
|
5
|
+
tokens:
|
|
6
|
+
- SegmentedControl.General_1_Background
|
|
7
|
+
- SegmentedControl.General_2_Background
|
|
8
|
+
- SegmentedControl.Secondary_1_Background
|
|
9
|
+
- SegmentedControl.Secondary_2_Background
|
|
10
|
+
- SegmentedControlSegment.BorderColor_Default
|
|
11
|
+
- SegmentedControlSegment.BorderColor_Focus
|
|
12
|
+
- SegmentedControlSegment.General_1_Background_Default
|
|
13
|
+
- SegmentedControlSegment.General_1_Background_Disabled
|
|
14
|
+
- SegmentedControlSegment.General_1_Background_Hover
|
|
15
|
+
- SegmentedControlSegment.General_1_Background_Selected_Default
|
|
16
|
+
- SegmentedControlSegment.General_1_Background_Selected_Disabled
|
|
17
|
+
- SegmentedControlSegment.General_1_Background_Selected_Hover
|
|
18
|
+
- SegmentedControlSegment.General_1_Color_Default
|
|
19
|
+
- SegmentedControlSegment.General_1_Color_Disabled
|
|
20
|
+
- SegmentedControlSegment.General_1_Color_Hover
|
|
21
|
+
- SegmentedControlSegment.General_1_Color_Selected_Default
|
|
22
|
+
- SegmentedControlSegment.General_1_Color_Selected_Disabled
|
|
23
|
+
- SegmentedControlSegment.General_1_Color_Selected_Hover
|
|
24
|
+
- SegmentedControlSegment.General_2_Background_Default
|
|
25
|
+
- SegmentedControlSegment.General_2_Background_Disabled
|
|
26
|
+
- SegmentedControlSegment.General_2_Background_Hover
|
|
27
|
+
- SegmentedControlSegment.General_2_Background_Selected_Default
|
|
28
|
+
- SegmentedControlSegment.General_2_Background_Selected_Disabled
|
|
29
|
+
- SegmentedControlSegment.General_2_Background_Selected_Hover
|
|
30
|
+
- SegmentedControlSegment.General_2_Color_Default
|
|
31
|
+
- SegmentedControlSegment.General_2_Color_Disabled
|
|
32
|
+
- SegmentedControlSegment.General_2_Color_Hover
|
|
33
|
+
- SegmentedControlSegment.General_2_Color_Selected_Default
|
|
34
|
+
- SegmentedControlSegment.General_2_Color_Selected_Disabled
|
|
35
|
+
- SegmentedControlSegment.General_2_Color_Selected_Hover
|
|
36
|
+
- SegmentedControlSegment.Secondary_1_Background_Default
|
|
37
|
+
- SegmentedControlSegment.Secondary_1_Background_Disabled
|
|
38
|
+
- SegmentedControlSegment.Secondary_1_Background_Hover
|
|
39
|
+
- SegmentedControlSegment.Secondary_1_Background_Selected_Default
|
|
40
|
+
- SegmentedControlSegment.Secondary_1_Background_Selected_Disabled
|
|
41
|
+
- SegmentedControlSegment.Secondary_1_Color_Default
|
|
42
|
+
- SegmentedControlSegment.Secondary_1_Color_Disabled
|
|
43
|
+
- SegmentedControlSegment.Secondary_1_Color_Hover
|
|
44
|
+
- SegmentedControlSegment.Secondary_1_Color_Selected_Default
|
|
45
|
+
- SegmentedControlSegment.Secondary_1_Color_Selected_Disabled
|
|
46
|
+
- SegmentedControlSegment.Secondary_2_Background_Default
|
|
47
|
+
- SegmentedControlSegment.Secondary_2_Background_Disabled
|
|
48
|
+
- SegmentedControlSegment.Secondary_2_Background_Hover
|
|
49
|
+
- SegmentedControlSegment.Secondary_2_Background_Selected_Default
|
|
50
|
+
- SegmentedControlSegment.Secondary_2_Background_Selected_Disabled
|
|
51
|
+
- SegmentedControlSegment.Secondary_2_Color_Default
|
|
52
|
+
- SegmentedControlSegment.Secondary_2_Color_Disabled
|
|
53
|
+
- SegmentedControlSegment.Secondary_2_Color_Hover
|
|
54
|
+
- SegmentedControlSegment.Secondary_2_Color_Selected_Default
|
|
55
|
+
- SegmentedControlSegment.Secondary_2_Color_Selected_Disabled
|
|
8
56
|
stories: stories/SegmentedControl/SegmentedControl.stories.tsx
|
|
9
57
|
version: "1.0"
|
|
10
58
|
---
|
|
@@ -13,223 +61,522 @@ version: "1.0"
|
|
|
13
61
|
|
|
14
62
|
## Назначение
|
|
15
63
|
|
|
16
|
-
|
|
17
|
-
(`MULTIPLE`)
|
|
18
|
-
|
|
19
|
-
|
|
64
|
+
Ряд сегментов-кнопок в общей подложке, из которых пользователь выбирает один (`type: SINGLE`)
|
|
65
|
+
или несколько (`type: MULTIPLE`) вариантов. Компонент полностью управляемый: выбранное значение
|
|
66
|
+
приходит в `value`, запрос на смену — через `onSelect`. Состав сегментов задаёт потребитель
|
|
67
|
+
через `children` и составной субкомпонент `SegmentedControl.Segment`; сегменты делят ширину
|
|
68
|
+
контрола поровну (`flex: 1 1 0`).
|
|
20
69
|
|
|
21
|
-
Используй когда: 2–
|
|
22
|
-
|
|
23
|
-
визуально не-кнопочного выбора — `Radio` / `Checkbox` группы.
|
|
70
|
+
Используй когда: вариантов немного (2–5), все они должны быть видны одновременно, и выбор
|
|
71
|
+
переключает режим отображения или фильтр — «день / неделя / месяц», «список / плитка».
|
|
24
72
|
|
|
25
|
-
|
|
73
|
+
Не используй когда:
|
|
26
74
|
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
├── types.ts # discriminated union по type
|
|
36
|
-
├── styles/SegmentedControl.module.less + SegmentedControlSegment.module.less
|
|
37
|
-
├── index.ts # Barrel
|
|
38
|
-
└── __tests__/
|
|
39
|
-
```
|
|
75
|
+
- Выбор переключает **страницы или панели контента** — нужна семантика табов
|
|
76
|
+
(`role="tablist"`, связка `aria-controls`, навигация стрелками). Возьми `Tabs`
|
|
77
|
+
или `TabsLine`: `SegmentedControl` этой семантики не даёт (см. Accessibility).
|
|
78
|
+
- Вариантов много или они не влезают в строку — сегменты не переносятся и не скроллятся,
|
|
79
|
+
а сжимаются, обрезая подписи по `text-overflow: ellipsis`.
|
|
80
|
+
- Нужен выбор из списка внутри формы с лейблом и статусом — возьми `SelectField`
|
|
81
|
+
или `RadioYGroup`.
|
|
82
|
+
- Нужны сбрасываемые фильтры с крестиком и счётчиком — это `Chip` / `ChipGroup`.
|
|
40
83
|
|
|
41
84
|
---
|
|
42
85
|
|
|
43
|
-
##
|
|
86
|
+
## Варианты и props
|
|
44
87
|
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
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
|
-
```
|
|
88
|
+
`TSegmentedControlProps` — discriminated union по `type`: `ISegmentedControlSingleProps`
|
|
89
|
+
(`type: SINGLE`) и `ISegmentedControlMultipleProps` (`type: MULTIPLE`). Обе ветки расширяют
|
|
90
|
+
`ISegmentedControlCommonProps`, а он — `Omit<React.HTMLAttributes<HTMLDivElement>, "onSelect">`:
|
|
91
|
+
неизвестные компоненту атрибуты уходят на корневой `<div>` через `...rest`.
|
|
58
92
|
|
|
59
|
-
|
|
93
|
+
### Обязательные props
|
|
60
94
|
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
95
|
+
| Prop | Тип | Описание |
|
|
96
|
+
|---|---|---|
|
|
97
|
+
| `type` | `ESegmentedControlType` | Дискриминант союза. `SINGLE` — выбран ровно один сегмент, `MULTIPLE` — любое подмножество. Меняет типы `value` и `onSelect`, а не внешний вид |
|
|
98
|
+
| `value` | `string` при `SINGLE`, `string[]` при `MULTIPLE` | Выбранное значение. Сегмент считает себя выбранным, сравнивая свой `value` с этим (`===` при SINGLE, `includes` при MULTIPLE). Собственного состояния у компонента нет |
|
|
99
|
+
| `onSelect` | `(value: string) => void` при `SINGLE`, `(value: string[]) => void` при `MULTIPLE` | Запрос на смену значения. При `SINGLE` получает `value` нажатого сегмента, при `MULTIPLE` — **новый массив** выбранных значений (компонент сам добавляет или убирает значение) |
|
|
100
|
+
| `theme` | `ESegmentedControlTheme` | Визуальный стиль: пара цветов подложки и сегментов. Выбирается по фону страницы, см. ниже |
|
|
101
|
+
| `size` | `ESegmentedControlSize` | Размер: высота сегмента, размер шрифта, радиусы и паддинги |
|
|
64
102
|
|
|
65
|
-
|
|
103
|
+
### Опциональные props
|
|
66
104
|
|
|
67
|
-
| Prop | Тип |
|
|
105
|
+
| Prop | Тип | По умолчанию | Описание |
|
|
68
106
|
|---|---|---|---|
|
|
69
|
-
| `
|
|
70
|
-
| `
|
|
71
|
-
|
|
72
|
-
|
|
107
|
+
| `disabled` | `boolean` | `false` | Выключает **все** сегменты сразу: значение уходит в контекст, и каждый сегмент ставит `disabled` на свой `<button>`. Выбранный сегмент остаётся визуально выбранным |
|
|
108
|
+
| `children` | `React.ReactNode` | — | Сегменты. Ожидается `SegmentedControl.Segment`; компонент не проверяет и не фильтрует состав `children` |
|
|
109
|
+
|
|
110
|
+
### Props сегмента (`SegmentedControl.Segment`)
|
|
73
111
|
|
|
74
|
-
|
|
112
|
+
`ISegmentedControlSegmentProps extends IButtonBaseProps` (то есть
|
|
113
|
+
`React.ButtonHTMLAttributes<HTMLButtonElement>`), поэтому сегмент принимает любые атрибуты
|
|
114
|
+
кнопки. Собственный prop один:
|
|
75
115
|
|
|
76
116
|
| Prop | Тип | Описание |
|
|
77
117
|
|---|---|---|
|
|
78
|
-
| `
|
|
79
|
-
| `value` | `string` | Текущий выбранный. |
|
|
80
|
-
| `onSelect` | `(value: string) => void` | Передаётся **новый** value. |
|
|
118
|
+
| `value` | `string` | Значение сегмента. Сравнивается со `value` контрола, чтобы определить выбранное состояние, и уходит в `onSelect` при нажатии |
|
|
81
119
|
|
|
82
|
-
|
|
120
|
+
Поведение остальных атрибутов кнопки:
|
|
83
121
|
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
122
|
+
- `disabled` — выключает **один** сегмент. Складывается с `disabled` контрола по `||`:
|
|
123
|
+
выключить сегмент можно, включить обратно вопреки контролу — нельзя.
|
|
124
|
+
- `onClick` — вызывается **после** запроса на смену значения, с исходным событием. Не
|
|
125
|
+
отменяет выбор: `preventDefault()` на выбор не влияет.
|
|
126
|
+
- `title` — если не передан, а `children` сегмента является строкой, эта строка
|
|
127
|
+
подставляется в `title` автоматически (подсказка для обрезанной подписи). Пустая строка
|
|
128
|
+
в `title` считается непереданной. Для нестрокового `children` (иконка) `title` не
|
|
129
|
+
выставляется — передавай его или `aria-label` сам.
|
|
130
|
+
- `className` — мерджится через `clsx` с собственными классами, не затирая их.
|
|
89
131
|
|
|
90
|
-
###
|
|
132
|
+
### Выбор темы
|
|
91
133
|
|
|
92
|
-
|
|
|
134
|
+
| Тема | Подложка | Когда |
|
|
93
135
|
|---|---|---|
|
|
94
|
-
| `
|
|
95
|
-
| `
|
|
96
|
-
| `
|
|
97
|
-
| `
|
|
98
|
-
| `children` | `ReactNode` | Контент кнопки. |
|
|
99
|
-
| `...IButtonBaseProps` | — | — |
|
|
100
|
-
|
|
101
|
-
### Context — `ISegmentedControlContextType`
|
|
136
|
+
| `GENERAL_1` | серая (`ColorNeutral.90`) | базовый вариант на белой странице |
|
|
137
|
+
| `GENERAL_2` | белая (`ColorNeutral.100`) | на затенённой странице или внутри серого блока |
|
|
138
|
+
| `SECONDARY_1` | серая | вторичный, менее акцентный выбранный сегмент |
|
|
139
|
+
| `SECONDARY_2` | белая | вторичный вариант на затенённой странице |
|
|
102
140
|
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
```
|
|
141
|
+
Пара `GENERAL` / `SECONDARY` отличается только цветами выбранного сегмента, геометрия
|
|
142
|
+
одинаковая.
|
|
106
143
|
|
|
107
|
-
###
|
|
144
|
+
### Размер и тема независимы
|
|
108
145
|
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
146
|
+
Оси не пересекаются: в `SegmentedControlSegment.module.less` классы размера (`.sm`, `.md`,
|
|
147
|
+
`.lg`) задают только геометрию (высота 20 / 32 / 40px, размер шрифта, радиус), классы темы —
|
|
148
|
+
только цвета. Любая комбинация допустима, поэтому stories показывают оси по отдельности,
|
|
149
|
+
а матрицу целиком не снимают.
|
|
112
150
|
|
|
113
151
|
---
|
|
114
152
|
|
|
115
|
-
##
|
|
153
|
+
## Дизайн-токены
|
|
154
|
+
|
|
155
|
+
Переопределяются через `ThemeProvider` (prop `tokens`) — см. `ThemeProvider-ai.md` →
|
|
156
|
+
«Как переопределять токены». Значения по умолчанию —
|
|
157
|
+
`src/components/DesignTokens/components/SegmentedControl.ts` и
|
|
158
|
+
`src/components/DesignTokens/components/SegmentedControlSegment.ts`.
|
|
116
159
|
|
|
117
|
-
|
|
160
|
+
Две группы: `SegmentedControl` — только фон подложки (по одному токену на тему),
|
|
161
|
+
`SegmentedControlSegment` — цвет и фон сегмента в разрезе «тема × выбранность × состояние»
|
|
162
|
+
плюс два токена рамки:
|
|
118
163
|
|
|
119
|
-
```
|
|
120
|
-
|
|
164
|
+
```text
|
|
165
|
+
SegmentedControl.{Theme}_Background
|
|
166
|
+
SegmentedControlSegment.{Theme}_Color_[Selected_]{Default|Hover|Disabled}
|
|
167
|
+
SegmentedControlSegment.{Theme}_Background_[Selected_]{Default|Hover|Disabled}
|
|
168
|
+
SegmentedControlSegment.BorderColor_Default
|
|
169
|
+
SegmentedControlSegment.BorderColor_Focus
|
|
121
170
|
```
|
|
122
171
|
|
|
123
|
-
|
|
124
|
-
-
|
|
172
|
+
Рамка у сегмента есть всегда (`1px solid BorderColor_Default`) — она резервирует место,
|
|
173
|
+
чтобы фокусная рамка не сдвигала вёрстку. При `:focus-visible` меняется только её цвет
|
|
174
|
+
на `BorderColor_Focus`.
|
|
125
175
|
|
|
126
|
-
|
|
176
|
+
**Известное расхождение (не исправлено осознанно).** В группе `SegmentedControlSegment`
|
|
177
|
+
объявлены, но не используются в стилях четыре токена: `Secondary_1_Color_Selected_Hover`,
|
|
178
|
+
`Secondary_1_Background_Selected_Hover`, `Secondary_2_Color_Selected_Hover`,
|
|
179
|
+
`Secondary_2_Background_Selected_Hover`. У тем `SECONDARY_*` в LESS нет правила
|
|
180
|
+
`&.selected:hover`, поэтому наведение на выбранный сегмент этих тем ничего не меняет,
|
|
181
|
+
хотя значения токенов отличаются от `Selected_Default`. Это pre-existing поведение;
|
|
182
|
+
добавление правила — визуальное изменение, его нужно согласовывать с дизайном и
|
|
183
|
+
перегенерировать baseline-скриншоты.
|
|
127
184
|
|
|
128
|
-
|
|
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
|
-
```
|
|
185
|
+
---
|
|
134
186
|
|
|
135
|
-
|
|
136
|
-
- В MULTIPLE: клик инвертирует — добавление или удаление из массива.
|
|
187
|
+
## Инварианты
|
|
137
188
|
|
|
138
|
-
|
|
189
|
+
- `forwardRef<HTMLDivElement>` на `SegmentedControl` — не убирать. Ref указывает на корневую
|
|
190
|
+
подложку (`<div class="segmentedControl">`).
|
|
191
|
+
- `forwardRef<HTMLButtonElement>` на `SegmentedControlSegment` — не убирать. Ref указывает на
|
|
192
|
+
`<button>`, который рендерит `ButtonBase`, а не на обёртку `IconWrapper`.
|
|
193
|
+
- Составной API `SegmentedControl.Segment` собирается через `Object.assign(forwardRef(...), { Segment })`.
|
|
194
|
+
`SegmentedControlSegment` при этом остаётся и самостоятельным barrel-экспортом — оба пути
|
|
195
|
+
импорта публичные, сохранять нужно оба.
|
|
196
|
+
- Барел `src/components/SegmentedControl/index.ts` экспортирует `enums`, `types`,
|
|
197
|
+
`SegmentedControlSegment` и `SegmentedControl`. `SegmentedControlContext`,
|
|
198
|
+
`ISegmentedControlContextType` и `utils.ts` из барела **не** экспортируются — это
|
|
199
|
+
внутренний слой, менять его можно свободно.
|
|
200
|
+
- `displayName` — `"SegmentedControl"` и `"SegmentedControlSegment"`. Не менять.
|
|
201
|
+
- Значения `ESegmentedControlTheme` (`general_1` / `general_2` / `secondary_1` / `secondary_2`)
|
|
202
|
+
и `ESegmentedControlType` (`single` / `multiple`) — часть публичного API. Значения темы
|
|
203
|
+
дополнительно связаны с именами CSS-классов (`.general1` и т.д. — через
|
|
204
|
+
`themeToClassNameMap`) и с именами токенов `{Theme}_*`.
|
|
205
|
+
- **Значения `ESegmentedControlSize` обязаны совпадать с `EComponentSize`** (`sm` / `md` / `lg`).
|
|
206
|
+
Классы размера берутся из `createSizeToClassNameMap(styles)`, а он типизирован как
|
|
207
|
+
`Record<EComponentSize, string>` — индексация `sizeToClassNameMap[size]` работает только
|
|
208
|
+
потому, что строковые значения обоих enum совпадают. Добавление размера в
|
|
209
|
+
`ESegmentedControlSize`, которого нет в `EComponentSize`, даст `undefined` вместо класса
|
|
210
|
+
без ошибки типов.
|
|
211
|
+
- Оба файла стилей лежат в `src/components/SegmentedControl/styles/`, и это важно:
|
|
212
|
+
`generateScopedName` (`scripts/generate-scoped-name.ts`) хеширует класс по **имени папки
|
|
213
|
+
компонента** + имени класса + версии. Поэтому `.general1` и `.sm` из
|
|
214
|
+
`SegmentedControl.module.less` и селекторы `.general1 &` / `.sm &` из
|
|
215
|
+
`SegmentedControlSegment.module.less` получают одинаковый хеш и сцепляются. Перенос
|
|
216
|
+
одного из файлов в другую папку компонента молча сломает стилизацию сегментов.
|
|
217
|
+
- При `type: SINGLE` нажатие на уже выбранный сегмент **всё равно вызывает** `onSelect` с его
|
|
218
|
+
значением (сегмент сообщает `selected: true`). Это наблюдаемое поведение, зафиксировано
|
|
219
|
+
тестом; отмена выбора при SINGLE не поддерживается.
|
|
220
|
+
- При `type: MULTIPLE` компонент не хранит порядок выбора: новое значение добавляется в
|
|
221
|
+
конец массива (`[...value, newValue]`), удаление сохраняет исходный порядок остальных.
|
|
222
|
+
- `[...value]` в обработчике MULTIPLE — не избыточный спред, а сужение для TS: `value`
|
|
223
|
+
внутри деструктурированного союза имеет тип `string | string[]`, и `.filter` у строки нет.
|
|
224
|
+
- Класс `content` на внутреннем `<span>` сегмента отвечает за обрезку подписи
|
|
225
|
+
(`overflow: hidden`, `text-overflow: ellipsis`) — не удалять и не переименовывать.
|
|
139
226
|
|
|
140
|
-
|
|
141
|
-
case SINGLE: onSegmentSelect({ value, selected: true });
|
|
142
|
-
case MULTIPLE: onSegmentSelect({ value, selected: !selected });
|
|
143
|
-
```
|
|
227
|
+
---
|
|
144
228
|
|
|
145
|
-
|
|
229
|
+
## Accessibility
|
|
146
230
|
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
231
|
+
- Каждый сегмент — нативная `<button type="button">` (через `ButtonBase`), поэтому фокус,
|
|
232
|
+
`Enter` / `Space` и `disabled` работают нативно. Собственных обработчиков клавиатуры
|
|
233
|
+
компонент не добавляет.
|
|
234
|
+
- Выбранность кодируется `aria-pressed` на каждой кнопке: `true` у выбранных, `false`
|
|
235
|
+
у остальных. Это модель «группа кнопок-переключателей», а не табов и не радиогруппы.
|
|
236
|
+
- **Корневой `<div>` роли не имеет.** Группа для скринридера не объявлена: ни
|
|
237
|
+
`role="group"`, ни `role="tablist"`, ни `aria-label` компонент не выставляет. Если группу
|
|
238
|
+
нужно озвучить, передай атрибуты сам — они уйдут на корневой `<div>` через `...rest`:
|
|
239
|
+
`<SegmentedControl role="group" aria-label="Период" ...>`. Роль обязательна вместе
|
|
240
|
+
с именем: `<div>` без роли получает роль `generic`, которой
|
|
241
|
+
[HTML-AAM](https://www.w3.org/TR/html-aria/) запрещает доступное имя.
|
|
242
|
+
- Навигации стрелками между сегментами нет — обход идёт `Tab`'ом, каждый сегмент
|
|
243
|
+
отдельная остановка. Если нужна семантика табов с одной остановкой на группу,
|
|
244
|
+
это `Tabs` / `TabsLine`, а не `SegmentedControl`.
|
|
245
|
+
- Фокус виден только с клавиатуры: цвет рамки меняется по `:focus-visible`.
|
|
246
|
+
- Сегмент с одной иконкой доступного имени не получает: `title` подставляется из `children`
|
|
247
|
+
только для строкового содержимого. Для иконки передавай `aria-label` (или `title`) сам —
|
|
248
|
+
компонент не хардкодит текст, библиотека мультиязычная.
|
|
249
|
+
- `disabled` (и на контроле, и на сегменте) ставится нативным атрибутом, поэтому выключенные
|
|
250
|
+
сегменты выпадают из порядка фокуса и не озвучиваются как активируемые.
|
|
154
251
|
|
|
155
|
-
|
|
156
|
-
- **`active`/`disabled`** — публичные классы из `@sberbusiness/icons-next`, влияющие на цвет иконок внутри `children`.
|
|
157
|
-
- `aria-pressed` — корректная семантика toggle-button (toggle для MULTIPLE; для SINGLE — тоже технически верно).
|
|
252
|
+
---
|
|
158
253
|
|
|
159
|
-
|
|
254
|
+
## Связанные компоненты
|
|
160
255
|
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
256
|
+
- `Tabs` — альтернатива, когда выбор переключает панели контента: даёт `role="tablist"`,
|
|
257
|
+
навигацию стрелками и работу с непоместившимися табами. Их легко спутать, потому что
|
|
258
|
+
`SegmentedControl` часто используют как визуальный переключатель вида.
|
|
259
|
+
- `TabsLine` — альтернатива для табов-подчёркиваний в шапке раздела. Тот же критерий
|
|
260
|
+
выбора, что и у `Tabs`: нужна ли семантика табов.
|
|
261
|
+
- `IconWrapper` — рендерится внутри каждого сегмента (`displayContents`) и по классам
|
|
262
|
+
`active` / `disabled` управляет цветом иконки из `@sberbusiness/icons-next`. Поэтому
|
|
263
|
+
сегмент с иконкой перекрашивается вместе с выбранным состоянием; `paletteIndex` в примерах
|
|
264
|
+
задаёт базовый цвет.
|
|
265
|
+
- `ButtonBase` — внутренняя кнопка, на которой построен сегмент (в barrel библиотеки не
|
|
266
|
+
экспортируется). Даёт `type="button"`, `data-tx` и проброс ref.
|
|
164
267
|
|
|
165
|
-
|
|
268
|
+
---
|
|
166
269
|
|
|
167
|
-
|
|
270
|
+
## Stories
|
|
168
271
|
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
- Иначе — undefined.
|
|
272
|
+
Основные истории: `stories/SegmentedControl/SegmentedControl.stories.tsx`
|
|
273
|
+
Файлы примеров: `stories/SegmentedControl/examples/`
|
|
172
274
|
|
|
173
|
-
|
|
275
|
+
| Story | Example file | Что демонстрирует |
|
|
276
|
+
|---|---|---|
|
|
277
|
+
| `Playground` | `Playground.tsx` | Интерактивный контроль `type`, `theme`, `size`, `disabled` |
|
|
278
|
+
| `Default` | `Default.tsx` | Минимальный контрол: `SINGLE`, `GENERAL_1`, `LG`, пять сегментов |
|
|
279
|
+
| `Types` | `Types.tsx` | `SINGLE` и `MULTIPLE` рядом; у MULTIPLE выбраны два сегмента |
|
|
280
|
+
| `Themes` | `Themes.tsx` | Все четыре значения `ESegmentedControlTheme` |
|
|
281
|
+
| `Sizes` | `Sizes.tsx` | Размеры SM / MD / LG |
|
|
282
|
+
| `Disabled` | `Disabled.tsx` | `disabled` на всём контроле, выбранный сегмент сохраняет вид |
|
|
283
|
+
| `Example` | `Example.tsx` | Production-like: три сегмента только с иконками в контейнере 144px |
|
|
284
|
+
| `VisualTests` | `VisualTests.tsx` | Краевые состояния для скриншотов: `:focus-visible` (открывается `play`), выключенные отдельные сегменты (включая выбранный), обрезка длинной подписи, сегменты-иконки, MULTIPLE без выбора и с полным выбором |
|
|
285
|
+
|
|
286
|
+
Из скриншот-тестов исключены (`testRunner: { skip: true }`) `Playground`, `Default` и
|
|
287
|
+
`Example`: `Default` визуально дублирует блок SINGLE в `Types`, а сегменты только с иконкой
|
|
288
|
+
покрыты отдельным блоком в `VisualTests` (там они сняты в теме `SECONDARY_1`, тогда как
|
|
289
|
+
`Example` использует `GENERAL_1`).
|
|
290
|
+
|
|
291
|
+
Story ID участвуют в именах baseline-скриншотов (`segmentedcontrol--themes--xs.png` и т.п.),
|
|
292
|
+
поэтому переименование story требует перегенерации baseline и удаления осиротевших файлов.
|
|
174
293
|
|
|
175
|
-
|
|
294
|
+
---
|
|
176
295
|
|
|
177
|
-
|
|
296
|
+
## История изменений
|
|
178
297
|
|
|
179
|
-
|
|
298
|
+
| Дата | Изменение |
|
|
299
|
+
|---|---|
|
|
300
|
+
| 2026-09-30 | Создан документ. AI-рефакторинг: `SegmentedControlSegment` переведён с `React.FC` на `forwardRef<HTMLButtonElement>`, чистые хелперы вынесены в `utils.ts` (`isSegmentSelected`, `getSegmentTitle`), JSDoc на props и полях контекста, внутренние импорты приведены к относительным. Добавлены unit-тесты (`__tests__/SegmentedControlSegment.test.tsx`, `__tests__/utils.test.tsx`, классы тем и размеров в `__tests__/SegmentedControl.test.tsx`). Stories перенесены в `stories/SegmentedControl/` по modern pattern, добавлена story `VisualTests`. |
|
|
180
301
|
|
|
181
302
|
---
|
|
182
303
|
|
|
183
|
-
##
|
|
304
|
+
## Примеры использования
|
|
184
305
|
|
|
185
|
-
|
|
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`** — порядок может быть важен.
|
|
306
|
+
### Default
|
|
196
307
|
|
|
197
|
-
|
|
308
|
+
`stories/SegmentedControl/examples/Default.tsx`
|
|
198
309
|
|
|
199
|
-
|
|
310
|
+
```tsx
|
|
311
|
+
import React, { useState } from "react";
|
|
312
|
+
import {
|
|
313
|
+
SegmentedControl,
|
|
314
|
+
ESegmentedControlSize,
|
|
315
|
+
ESegmentedControlTheme,
|
|
316
|
+
ESegmentedControlType,
|
|
317
|
+
} from "@sberbusiness/triplex-next";
|
|
318
|
+
|
|
319
|
+
export const Default = () => {
|
|
320
|
+
const [value, setValue] = useState("segment_3");
|
|
321
|
+
|
|
322
|
+
return (
|
|
323
|
+
<SegmentedControl
|
|
324
|
+
type={ESegmentedControlType.SINGLE}
|
|
325
|
+
theme={ESegmentedControlTheme.GENERAL_1}
|
|
326
|
+
size={ESegmentedControlSize.LG}
|
|
327
|
+
value={value}
|
|
328
|
+
onSelect={setValue}
|
|
329
|
+
>
|
|
330
|
+
<SegmentedControl.Segment value="segment_1">Segment</SegmentedControl.Segment>
|
|
331
|
+
<SegmentedControl.Segment value="segment_2">Segment</SegmentedControl.Segment>
|
|
332
|
+
<SegmentedControl.Segment value="segment_3">Segment</SegmentedControl.Segment>
|
|
333
|
+
<SegmentedControl.Segment value="segment_4">Segment</SegmentedControl.Segment>
|
|
334
|
+
<SegmentedControl.Segment value="segment_5">Segment</SegmentedControl.Segment>
|
|
335
|
+
</SegmentedControl>
|
|
336
|
+
);
|
|
337
|
+
};
|
|
338
|
+
```
|
|
200
339
|
|
|
201
|
-
|
|
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 полезно.
|
|
340
|
+
### Types
|
|
206
341
|
|
|
207
|
-
|
|
342
|
+
`stories/SegmentedControl/examples/Types.tsx`
|
|
208
343
|
|
|
209
|
-
|
|
344
|
+
```tsx
|
|
345
|
+
import React, { useState } from "react";
|
|
346
|
+
import {
|
|
347
|
+
SegmentedControl,
|
|
348
|
+
ESegmentedControlSize,
|
|
349
|
+
ESegmentedControlTheme,
|
|
350
|
+
ESegmentedControlType,
|
|
351
|
+
} from "@sberbusiness/triplex-next";
|
|
352
|
+
|
|
353
|
+
export const Types = () => {
|
|
354
|
+
const [singleValue, setSingleValue] = useState("segment_3");
|
|
355
|
+
const [multipleValue, setMultipleValue] = useState(["segment_2", "segment_4"]);
|
|
356
|
+
|
|
357
|
+
return (
|
|
358
|
+
<div style={{ display: "flex", flexDirection: "column", gap: 16 }}>
|
|
359
|
+
<div>
|
|
360
|
+
<div style={{ marginBottom: 8, fontSize: 16, fontWeight: 700 }}>SINGLE</div>
|
|
361
|
+
<SegmentedControl
|
|
362
|
+
type={ESegmentedControlType.SINGLE}
|
|
363
|
+
theme={ESegmentedControlTheme.GENERAL_1}
|
|
364
|
+
size={ESegmentedControlSize.LG}
|
|
365
|
+
value={singleValue}
|
|
366
|
+
onSelect={setSingleValue}
|
|
367
|
+
>
|
|
368
|
+
<SegmentedControl.Segment value="segment_1">Segment</SegmentedControl.Segment>
|
|
369
|
+
<SegmentedControl.Segment value="segment_2">Segment</SegmentedControl.Segment>
|
|
370
|
+
<SegmentedControl.Segment value="segment_3">Segment</SegmentedControl.Segment>
|
|
371
|
+
<SegmentedControl.Segment value="segment_4">Segment</SegmentedControl.Segment>
|
|
372
|
+
<SegmentedControl.Segment value="segment_5">Segment</SegmentedControl.Segment>
|
|
373
|
+
</SegmentedControl>
|
|
374
|
+
</div>
|
|
375
|
+
<div>
|
|
376
|
+
<div style={{ marginBottom: 8, fontSize: 16, fontWeight: 700 }}>MULTIPLE</div>
|
|
377
|
+
<SegmentedControl
|
|
378
|
+
type={ESegmentedControlType.MULTIPLE}
|
|
379
|
+
theme={ESegmentedControlTheme.GENERAL_1}
|
|
380
|
+
size={ESegmentedControlSize.LG}
|
|
381
|
+
value={multipleValue}
|
|
382
|
+
onSelect={setMultipleValue}
|
|
383
|
+
>
|
|
384
|
+
<SegmentedControl.Segment value="segment_1">Segment</SegmentedControl.Segment>
|
|
385
|
+
<SegmentedControl.Segment value="segment_2">Segment</SegmentedControl.Segment>
|
|
386
|
+
<SegmentedControl.Segment value="segment_3">Segment</SegmentedControl.Segment>
|
|
387
|
+
<SegmentedControl.Segment value="segment_4">Segment</SegmentedControl.Segment>
|
|
388
|
+
<SegmentedControl.Segment value="segment_5">Segment</SegmentedControl.Segment>
|
|
389
|
+
</SegmentedControl>
|
|
390
|
+
</div>
|
|
391
|
+
</div>
|
|
392
|
+
);
|
|
393
|
+
};
|
|
394
|
+
```
|
|
210
395
|
|
|
211
|
-
|
|
396
|
+
### Themes
|
|
212
397
|
|
|
213
|
-
|
|
398
|
+
`stories/SegmentedControl/examples/Themes.tsx`
|
|
214
399
|
|
|
215
|
-
|
|
400
|
+
```tsx
|
|
401
|
+
import React, { useState } from "react";
|
|
402
|
+
import {
|
|
403
|
+
SegmentedControl,
|
|
404
|
+
ESegmentedControlSize,
|
|
405
|
+
ESegmentedControlTheme,
|
|
406
|
+
ESegmentedControlType,
|
|
407
|
+
} from "@sberbusiness/triplex-next";
|
|
408
|
+
|
|
409
|
+
interface IThemeItemProps {
|
|
410
|
+
theme: ESegmentedControlTheme;
|
|
411
|
+
}
|
|
412
|
+
|
|
413
|
+
const ThemeItem = ({ theme }: IThemeItemProps) => {
|
|
414
|
+
const [value, setValue] = useState("segment_3");
|
|
415
|
+
|
|
416
|
+
return (
|
|
417
|
+
<div>
|
|
418
|
+
<div style={{ marginBottom: 8, fontSize: 16, fontWeight: 700 }}>{theme.toUpperCase()}</div>
|
|
419
|
+
<SegmentedControl
|
|
420
|
+
type={ESegmentedControlType.SINGLE}
|
|
421
|
+
theme={theme}
|
|
422
|
+
size={ESegmentedControlSize.LG}
|
|
423
|
+
value={value}
|
|
424
|
+
onSelect={setValue}
|
|
425
|
+
>
|
|
426
|
+
<SegmentedControl.Segment value="segment_1">Segment</SegmentedControl.Segment>
|
|
427
|
+
<SegmentedControl.Segment value="segment_2">Segment</SegmentedControl.Segment>
|
|
428
|
+
<SegmentedControl.Segment value="segment_3">Segment</SegmentedControl.Segment>
|
|
429
|
+
<SegmentedControl.Segment value="segment_4">Segment</SegmentedControl.Segment>
|
|
430
|
+
<SegmentedControl.Segment value="segment_5">Segment</SegmentedControl.Segment>
|
|
431
|
+
</SegmentedControl>
|
|
432
|
+
</div>
|
|
433
|
+
);
|
|
434
|
+
};
|
|
435
|
+
|
|
436
|
+
const THEMES = Object.values(ESegmentedControlTheme);
|
|
437
|
+
|
|
438
|
+
export const Themes = () => (
|
|
439
|
+
<div style={{ display: "flex", flexDirection: "column", gap: 16 }}>
|
|
440
|
+
{THEMES.map((theme) => (
|
|
441
|
+
<ThemeItem key={theme} theme={theme} />
|
|
442
|
+
))}
|
|
443
|
+
</div>
|
|
444
|
+
);
|
|
445
|
+
```
|
|
216
446
|
|
|
217
|
-
|
|
218
|
-
- `Radio` / `Checkbox` (+ `RadioYGroup` / аналог) — альтернатива для form-input семантики.
|
|
219
|
-
- `ButtonBase` — нижележащий primitive.
|
|
220
|
-
- `IconWrapper` — управление цветом иконок внутри (active/disabled).
|
|
447
|
+
### Sizes
|
|
221
448
|
|
|
222
|
-
|
|
449
|
+
`stories/SegmentedControl/examples/Sizes.tsx`
|
|
223
450
|
|
|
224
|
-
|
|
451
|
+
```tsx
|
|
452
|
+
import React, { useState } from "react";
|
|
453
|
+
import {
|
|
454
|
+
SegmentedControl,
|
|
455
|
+
ESegmentedControlSize,
|
|
456
|
+
ESegmentedControlTheme,
|
|
457
|
+
ESegmentedControlType,
|
|
458
|
+
} from "@sberbusiness/triplex-next";
|
|
459
|
+
|
|
460
|
+
interface ISizeItemProps {
|
|
461
|
+
size: ESegmentedControlSize;
|
|
462
|
+
}
|
|
463
|
+
|
|
464
|
+
const SizeItem = ({ size }: ISizeItemProps) => {
|
|
465
|
+
const [value, setValue] = useState("segment_3");
|
|
466
|
+
|
|
467
|
+
return (
|
|
468
|
+
<div>
|
|
469
|
+
<div style={{ marginBottom: 8, fontSize: 16, fontWeight: 700 }}>{size.toUpperCase()}</div>
|
|
470
|
+
<SegmentedControl
|
|
471
|
+
type={ESegmentedControlType.SINGLE}
|
|
472
|
+
theme={ESegmentedControlTheme.GENERAL_1}
|
|
473
|
+
size={size}
|
|
474
|
+
value={value}
|
|
475
|
+
onSelect={setValue}
|
|
476
|
+
>
|
|
477
|
+
<SegmentedControl.Segment value="segment_1">Segment</SegmentedControl.Segment>
|
|
478
|
+
<SegmentedControl.Segment value="segment_2">Segment</SegmentedControl.Segment>
|
|
479
|
+
<SegmentedControl.Segment value="segment_3">Segment</SegmentedControl.Segment>
|
|
480
|
+
<SegmentedControl.Segment value="segment_4">Segment</SegmentedControl.Segment>
|
|
481
|
+
<SegmentedControl.Segment value="segment_5">Segment</SegmentedControl.Segment>
|
|
482
|
+
</SegmentedControl>
|
|
483
|
+
</div>
|
|
484
|
+
);
|
|
485
|
+
};
|
|
486
|
+
|
|
487
|
+
const SIZES = Object.values(ESegmentedControlSize);
|
|
488
|
+
|
|
489
|
+
export const Sizes = () => (
|
|
490
|
+
<div style={{ display: "flex", flexDirection: "column", gap: 16 }}>
|
|
491
|
+
{SIZES.map((size) => (
|
|
492
|
+
<SizeItem key={size} size={size} />
|
|
493
|
+
))}
|
|
494
|
+
</div>
|
|
495
|
+
);
|
|
496
|
+
```
|
|
225
497
|
|
|
226
|
-
|
|
227
|
-
Примеры: `stories/SegmentedControl/examples/`
|
|
498
|
+
### Disabled
|
|
228
499
|
|
|
229
|
-
|
|
500
|
+
`stories/SegmentedControl/examples/Disabled.tsx`
|
|
230
501
|
|
|
231
|
-
|
|
502
|
+
```tsx
|
|
503
|
+
import React, { useState } from "react";
|
|
504
|
+
import {
|
|
505
|
+
SegmentedControl,
|
|
506
|
+
ESegmentedControlSize,
|
|
507
|
+
ESegmentedControlTheme,
|
|
508
|
+
ESegmentedControlType,
|
|
509
|
+
} from "@sberbusiness/triplex-next";
|
|
510
|
+
|
|
511
|
+
export const Disabled = () => {
|
|
512
|
+
const [value, setValue] = useState("segment_3");
|
|
513
|
+
|
|
514
|
+
return (
|
|
515
|
+
<SegmentedControl
|
|
516
|
+
type={ESegmentedControlType.SINGLE}
|
|
517
|
+
theme={ESegmentedControlTheme.GENERAL_1}
|
|
518
|
+
size={ESegmentedControlSize.LG}
|
|
519
|
+
value={value}
|
|
520
|
+
onSelect={setValue}
|
|
521
|
+
disabled
|
|
522
|
+
>
|
|
523
|
+
<SegmentedControl.Segment value="segment_1">Segment</SegmentedControl.Segment>
|
|
524
|
+
<SegmentedControl.Segment value="segment_2">Segment</SegmentedControl.Segment>
|
|
525
|
+
<SegmentedControl.Segment value="segment_3">Segment</SegmentedControl.Segment>
|
|
526
|
+
<SegmentedControl.Segment value="segment_4">Segment</SegmentedControl.Segment>
|
|
527
|
+
<SegmentedControl.Segment value="segment_5">Segment</SegmentedControl.Segment>
|
|
528
|
+
</SegmentedControl>
|
|
529
|
+
);
|
|
530
|
+
};
|
|
531
|
+
```
|
|
532
|
+
|
|
533
|
+
### Example
|
|
534
|
+
|
|
535
|
+
`stories/SegmentedControl/examples/Example.tsx`
|
|
536
|
+
|
|
537
|
+
```tsx
|
|
538
|
+
import React, { useState } from "react";
|
|
539
|
+
import { DefaulticonStrokePrdIcon20 } from "@sberbusiness/icons-next";
|
|
540
|
+
import {
|
|
541
|
+
SegmentedControl,
|
|
542
|
+
ESegmentedControlSize,
|
|
543
|
+
ESegmentedControlTheme,
|
|
544
|
+
ESegmentedControlType,
|
|
545
|
+
} from "@sberbusiness/triplex-next";
|
|
546
|
+
|
|
547
|
+
/** Индекс палитры иконки: выбранный сегмент рисует иконку на контрастном фоне. */
|
|
548
|
+
const SELECTED_ICON_PALETTE_INDEX = 10;
|
|
549
|
+
const ICON_PALETTE_INDEX = 5;
|
|
550
|
+
|
|
551
|
+
const SEGMENTS = [
|
|
552
|
+
{ value: "segment_1", label: "segment 1" },
|
|
553
|
+
{ value: "segment_2", label: "segment 2" },
|
|
554
|
+
{ value: "segment_3", label: "segment 3" },
|
|
555
|
+
];
|
|
556
|
+
|
|
557
|
+
export const Example = () => {
|
|
558
|
+
const [value, setValue] = useState("segment_2");
|
|
559
|
+
|
|
560
|
+
return (
|
|
561
|
+
<div style={{ width: 144 }}>
|
|
562
|
+
<SegmentedControl
|
|
563
|
+
type={ESegmentedControlType.SINGLE}
|
|
564
|
+
theme={ESegmentedControlTheme.GENERAL_1}
|
|
565
|
+
size={ESegmentedControlSize.LG}
|
|
566
|
+
value={value}
|
|
567
|
+
onSelect={setValue}
|
|
568
|
+
>
|
|
569
|
+
{SEGMENTS.map((segment) => (
|
|
570
|
+
<SegmentedControl.Segment key={segment.value} value={segment.value} aria-label={segment.label}>
|
|
571
|
+
<DefaulticonStrokePrdIcon20
|
|
572
|
+
paletteIndex={value === segment.value ? SELECTED_ICON_PALETTE_INDEX : ICON_PALETTE_INDEX}
|
|
573
|
+
style={{ display: "block" }}
|
|
574
|
+
/>
|
|
575
|
+
</SegmentedControl.Segment>
|
|
576
|
+
))}
|
|
577
|
+
</SegmentedControl>
|
|
578
|
+
</div>
|
|
579
|
+
);
|
|
580
|
+
};
|
|
581
|
+
```
|
|
232
582
|
|
|
233
|
-
| Дата | Изменение |
|
|
234
|
-
|---|---|
|
|
235
|
-
| 2026-04-18 | Создан документ |
|