@sberbusiness/triplex-next-mcp-server 1.44.0-beta → 1.46.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 -6
- package/bundle/components/AlertContext.md +11 -8
- package/bundle/components/AlertProcess.md +13 -10
- package/bundle/components/Badge.md +6 -3
- package/bundle/components/Button.md +46 -43
- package/bundle/components/ButtonDropdownExtended.md +3 -2
- package/bundle/components/ButtonIcon.md +7 -4
- package/bundle/components/Calendar.md +62 -59
- package/bundle/components/Caption.md +26 -23
- package/bundle/components/CardAction.md +615 -0
- package/bundle/components/Checkbox.md +32 -29
- package/bundle/components/CheckboxTreeExtended.md +9 -5
- package/bundle/components/Chip.md +51 -47
- package/bundle/components/ChipMonthYearPicker.md +344 -0
- package/bundle/components/ChipMultiselect.md +1251 -0
- package/bundle/components/ChipOptions.md +329 -0
- package/bundle/components/ChipSort.md +333 -0
- package/bundle/components/ChipSuggest.md +587 -0
- package/bundle/components/CodeText.md +26 -23
- package/bundle/components/Col.md +1 -2
- package/bundle/components/CollapsibleTree.md +8 -5
- package/bundle/components/DateField.md +222 -303
- package/bundle/components/DatePickerExtended.md +3 -1
- package/bundle/components/Dropdown.md +33 -28
- package/bundle/components/EmptyView.md +12 -5
- package/bundle/components/FooterPage.md +6 -2
- package/bundle/components/FormField.md +47 -42
- package/bundle/components/FormGroup.md +192 -52
- package/bundle/components/Header.md +491 -211
- package/bundle/components/HeaderPage.md +6 -2
- package/bundle/components/HelpBox.md +7 -1
- package/bundle/components/ImageGallery.md +33 -34
- package/bundle/components/ImageGalleryExtended.md +33 -34
- package/bundle/components/Island.md +16 -13
- package/bundle/components/IslandAccordion.md +443 -152
- package/bundle/components/IslandWidget.md +622 -157
- package/bundle/components/LightBox.md +14 -12
- package/bundle/components/Link.md +211 -96
- package/bundle/components/ListActionItem.md +7 -4
- package/bundle/components/ListItem.md +11 -8
- package/bundle/components/ListItemControlsButton.md +16 -13
- package/bundle/components/ListItemControlsButtonDropdown.md +9 -6
- package/bundle/components/ListItemSelectable.md +7 -4
- package/bundle/components/ListMasterFooter.md +8 -5
- package/bundle/components/ListMasterHeader.md +8 -5
- package/bundle/components/ListSortableItem.md +11 -8
- package/bundle/components/ListTableItem.md +7 -4
- package/bundle/components/LoaderMiddle.md +7 -4
- package/bundle/components/LoaderScreen.md +11 -8
- package/bundle/components/LoaderSmall.md +7 -4
- package/bundle/components/Marker.md +184 -69
- package/bundle/components/MarkerStatus.md +206 -88
- package/bundle/components/MaskedField.md +4 -5
- package/bundle/components/MasterTable.md +602 -0
- package/bundle/components/ModalWindow.md +7 -4
- package/bundle/components/MonthYearField.md +3 -1
- package/bundle/components/MultiselectField.md +2183 -132
- package/bundle/components/Notification.md +12 -9
- package/bundle/components/Overlay.md +9 -4
- package/bundle/components/Page.md +2 -2
- package/bundle/components/Pagination.md +15 -8
- package/bundle/components/Row.md +1 -1
- package/bundle/components/SelectExtendedField.md +3 -3
- package/bundle/components/SelectField.md +466 -148
- package/bundle/components/SliderExtended.md +39 -36
- package/bundle/components/SmallInput.md +194 -51
- package/bundle/components/Suggest.md +535 -99
- package/bundle/components/SwipeableArea.md +428 -79
- package/bundle/components/TableBasic.md +19 -11
- package/bundle/components/TabsExtended.md +24 -21
- package/bundle/components/Tag.md +6 -2
- package/bundle/components/Text.md +28 -25
- package/bundle/components/TextField.md +3 -4
- package/bundle/components/ThemeProvider.md +50 -1
- package/bundle/components/Title.md +26 -23
- package/bundle/components/Tooltip.md +24 -21
- package/bundle/components/TopOverlay.md +7 -4
- package/bundle/guides/codestyle.md +3 -0
- package/bundle/guides/context.md +84 -12
- package/bundle/guides/guidelines.md +321 -0
- package/bundle/guides/template.md +22 -12
- package/bundle/manifest.json +75 -17
- package/bundle/release-notes/1.45.0.mdx +117 -0
- package/bundle/release-notes/1.46.0.mdx +66 -0
- package/bundle/release-notes/1.47.0.mdx +7 -0
- package/bundle/tokens.json +6362 -0
- package/dist/index.js +177 -28
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
|
@@ -0,0 +1,329 @@
|
|
|
1
|
+
---
|
|
2
|
+
component: ChipOptions
|
|
3
|
+
category: Chips
|
|
4
|
+
related: [Chip, ChipGroup, ChipSort, ChipMultiselect]
|
|
5
|
+
tokens: []
|
|
6
|
+
stories: stories/Chips/ChipOptions.stories.tsx
|
|
7
|
+
version: "1.0"
|
|
8
|
+
---
|
|
9
|
+
|
|
10
|
+
# ChipOptions
|
|
11
|
+
|
|
12
|
+
## Назначение
|
|
13
|
+
|
|
14
|
+
Чипс-точка входа в настройку опций: слева всегда иконка опций, в центре —
|
|
15
|
+
количество выбранных опций, справа в выбранном состоянии — кнопка сброса.
|
|
16
|
+
Сам список опций компонент не содержит: по клику потребитель открывает свой
|
|
17
|
+
`Dropdown`, модальное окно или боковую панель и сам считает, что выбрано.
|
|
18
|
+
|
|
19
|
+
Используй когда: рядом с фильтрами нужен чипс «Опции / Настройки» с индикацией
|
|
20
|
+
количества выбранного и быстрым сбросом.
|
|
21
|
+
Не используй когда: выбор идёт из готового списка значений — возьми `ChipSelect`
|
|
22
|
+
(одно значение) или `ChipMultiselect` (несколько, тоже со сбросом); нужен просто
|
|
23
|
+
чипс с иконкой без счётчика и сброса — возьми `Chip` с `prefix`.
|
|
24
|
+
|
|
25
|
+
---
|
|
26
|
+
|
|
27
|
+
## Варианты и props
|
|
28
|
+
|
|
29
|
+
`IChipOptionsProps` наследует `Omit<IChipProps, "prefix" | "postfix">`: доступен
|
|
30
|
+
весь API `Chip` (см. `Chip-ai.md`), кроме `prefix` / `postfix` — их занимает сам
|
|
31
|
+
компонент.
|
|
32
|
+
|
|
33
|
+
### Обязательные props
|
|
34
|
+
|
|
35
|
+
| Prop | Тип | Описание |
|
|
36
|
+
|---|---|---|
|
|
37
|
+
| `clearSelected` | `() => void` | Сброс выбора: вызывается кликом по кнопке сброса в `postfix`. Обязателен всегда, даже если чипс никогда не бывает `selected` |
|
|
38
|
+
|
|
39
|
+
### Опциональные props
|
|
40
|
+
|
|
41
|
+
| Prop | Тип | По умолчанию | Описание |
|
|
42
|
+
|---|---|---|---|
|
|
43
|
+
| `selected` | `boolean` | `false` | Есть ли выбранные опции. Управляет и визуалом чипса, и палитрой иконки, и показом кнопки сброса |
|
|
44
|
+
| `children` | `React.ReactNode` | — | Обычно число выбранных опций; оборачивается в `.chipOptionsContent` |
|
|
45
|
+
| `size` | `EComponentSize` | `MD` | Размер чипса; тем же размером рисуется кнопка сброса (иконка-крестик 16 / 20 / 24px) |
|
|
46
|
+
| `type` | `EChipType` | `TYPE_1` | Визуальный тип фона, как у `Chip` |
|
|
47
|
+
| `disabled` | `boolean` | `false` | Визуальная блокировка, `tabIndex={-1}`. Кнопку сброса сам по себе не отключает — см. Accessibility (прочие ограничения — в `Chip-ai.md`) |
|
|
48
|
+
| `clearButtonProps` | `Omit<IChipClearButtonProps, "size">` | — | Props кнопки сброса: `aria-label` для её доступного имени, `disabled`, `className`, `data-*`, собственные `onClick` / `onKeyDown`. `size` берётся из `size` чипса и в тип не входит |
|
|
49
|
+
| `showNotificationIcon` | `boolean` | `false` | Значок новых уведомлений, как у `Chip` |
|
|
50
|
+
| `...HTMLSpanAttributes` | — | — | Все атрибуты `<span>` кроме `prefix`, включая `onClick`, `aria-label`, `data-*` |
|
|
51
|
+
|
|
52
|
+
### Ограничения
|
|
53
|
+
|
|
54
|
+
- **Компонент неуправляемый.** `selected` и содержимое считает потребитель;
|
|
55
|
+
`clearSelected` обязан сам привести состояние к «ничего не выбрано» — внутреннего
|
|
56
|
+
состояния у компонента нет.
|
|
57
|
+
- **`children` для «ничего не выбрано» передавай как `undefined`, а не `0`.** Обёртка
|
|
58
|
+
`.chipOptionsContent` создаётся по `children !== undefined`, поэтому ноль — это
|
|
59
|
+
определённое значение, и он отрисуется в чипсе. В примерах stories ноль отсекается
|
|
60
|
+
явно: `count > 0 ? count : undefined` или `count || undefined`.
|
|
61
|
+
- **Иконка опций всегда 24×24** (`OptionsStrokeSrvIcon24`) и от `size` не зависит —
|
|
62
|
+
в отличие от иконки кнопки сброса. В `SM` она визуально крупнее, чем крестик.
|
|
63
|
+
- **`prefix` / `postfix` недоступны** — исключены из типа props. Нужен свой postfix —
|
|
64
|
+
собирай чипс из `Chip` напрямую.
|
|
65
|
+
- **Кнопкой сброса управляют только через `clearButtonProps`** — сам `ChipClearButton`
|
|
66
|
+
наружу не выведен. Из типа исключён единственный prop — `size`: размер кнопки задаёт
|
|
67
|
+
`size` чипса.
|
|
68
|
+
- **`onClick` и `onKeyDown` из `clearButtonProps` не заменяют обработчики компонента,
|
|
69
|
+
а вызываются после них.** В `onClick` это `event.stopPropagation()` и `clearSelected()`,
|
|
70
|
+
затем внешний обработчик; в `onKeyDown` — `event.stopPropagation()` для `Enter` / `Space`,
|
|
71
|
+
затем внешний. Разрешить всплытие до чипса через внешний обработчик нельзя: гашение
|
|
72
|
+
уже произошло.
|
|
73
|
+
- **`event.preventDefault()` во внешнем `onKeyDown` отменяет сброс.** Это нативная семантика
|
|
74
|
+
`<button>`: сброс приходит не из `keydown`, а из клика, которым браузер активирует кнопку
|
|
75
|
+
по `Enter` / `Space`, — отменённая активация означает и отсутствие `clearSelected()`.
|
|
76
|
+
Компонент эту возможность у потребителя не отбирает. Нужен полный контроль над кнопкой —
|
|
77
|
+
собирай чипс из `Chip` + `ChipClearButton` вручную.
|
|
78
|
+
|
|
79
|
+
---
|
|
80
|
+
|
|
81
|
+
## Дизайн-токены
|
|
82
|
+
|
|
83
|
+
Собственных токенов нет: `ChipOptions.module.less` описывает только раскладку
|
|
84
|
+
контента (`min-width`, `text-align`). Весь цвет, фон, скругления и фокус приходят
|
|
85
|
+
из токенов `Chip.*` — список и правила переопределения см. в `Chip-ai.md`.
|
|
86
|
+
|
|
87
|
+
---
|
|
88
|
+
|
|
89
|
+
## Инварианты
|
|
90
|
+
|
|
91
|
+
- **`forwardRef`** — обязателен, `ref` указывает на корневой `<span>` компонента `Chip`.
|
|
92
|
+
Не убирать.
|
|
93
|
+
- **`postfix` вне выбранного состояния — пустой `<span />`, а не `undefined`.** `Chip`
|
|
94
|
+
выставляет класс `.withPostfix` (обнуляет правый `padding`) по `postfix !== undefined`,
|
|
95
|
+
поэтому замена заглушки на `undefined` сдвинет содержимое чипса при каждом
|
|
96
|
+
переключении `selected`. Тот же приём используется в `ChipIcon`.
|
|
97
|
+
- **`event.stopPropagation()` в обработчике клика по кнопке сброса** — без него клик
|
|
98
|
+
по крестику всплывёт до `Chip` и потребительский `onClick` (обычно открытие списка
|
|
99
|
+
опций) отработает одновременно со сбросом.
|
|
100
|
+
- **`event.stopPropagation()` на `Enter` / `Space` в обработчике `keydown` кнопки сброса** —
|
|
101
|
+
без него `Chip` вызовет `preventDefault()` на `Space` (гасит прокрутку страницы) и заодно
|
|
102
|
+
отменит нативную активацию кнопки, а на `Enter` потребительский `onKeyDown` чипса
|
|
103
|
+
отработает одновременно со сбросом. Тот же приём в `ChipMultiselect`.
|
|
104
|
+
- **Палитра иконки опций переключается по `selected`** (`paletteIndex` 6 в выбранном
|
|
105
|
+
состоянии, 5 в обычном) — так иконка остаётся читаемой на контрастном фоне
|
|
106
|
+
выбранного чипса.
|
|
107
|
+
- **Класс `.chipOptionsContent`** (`min-width: 14px`, `text-align: center`, `display: block`)
|
|
108
|
+
удерживает ширину чипса при смене однозначного счётчика — не удалять. `display: block`
|
|
109
|
+
здесь не декоративен: родительский `.content` не флекс-контейнер, и на inline-`<span>`
|
|
110
|
+
`min-width` не подействует.
|
|
111
|
+
- **Barrel-экспорты** `ChipOptions` и `IChipOptionsProps` из `src/components/Chip/index.ts` —
|
|
112
|
+
сохранять.
|
|
113
|
+
- **Story ids `chips-chipoptions--*`** завязаны на baseline-скриншоты в `__screenshots__/` —
|
|
114
|
+
переименование story требует синхронного обновления baseline.
|
|
115
|
+
|
|
116
|
+
---
|
|
117
|
+
|
|
118
|
+
## Accessibility
|
|
119
|
+
|
|
120
|
+
- Корневой элемент — `<span role="button">` из `Chip`; клавиатурную активацию самого
|
|
121
|
+
чипса компонент не реализует (подробности и причина — в `Chip-ai.md`).
|
|
122
|
+
- **Доступное имя кнопки сброса задавай сам.** Иконка-крестик рендерится с `aria-hidden`,
|
|
123
|
+
текста у кнопки нет, поэтому по умолчанию доступного имени у неё не будет. Передавай
|
|
124
|
+
`clearButtonProps={{ "aria-label": "Сбросить выбранные опции" }}` — так сделано в примере
|
|
125
|
+
story `Default`.
|
|
126
|
+
- **`disabled` чипса сам по себе не отключает кнопку сброса.** `Chip` применяет `disabled`
|
|
127
|
+
только к корневому `<span>` (`tabIndex={-1}`) и к CSS-классам `IconWrapper`, на вложенную
|
|
128
|
+
кнопку он не распространяется. В состоянии `disabled selected` крестик остаётся в
|
|
129
|
+
таб-порядке и кликабельным, то есть `clearSelected` отработает на заблокированном чипсе.
|
|
130
|
+
Пробрасывай флаг явно — `clearButtonProps={{ disabled }}`.
|
|
131
|
+
- **`Enter` и `Space` на сфокусированной кнопке сброса работают и до чипса не доходят.**
|
|
132
|
+
Компонент гасит всплытие этих клавиш от кнопки, поэтому `Chip` не отменяет нативную
|
|
133
|
+
активацию кнопки на `Space`, а внешний `onKeyDown` чипса не срабатывает заодно со
|
|
134
|
+
сбросом. Остальные клавиши всплывают как обычно — внешний `onKeyDown` их видит и тогда,
|
|
135
|
+
когда фокус находится на кнопке сброса.
|
|
136
|
+
- Чипс со счётчиком вместо текста (`{3}`) сам себя не объясняет — передавай `aria-label`
|
|
137
|
+
через `...rest`.
|
|
138
|
+
|
|
139
|
+
---
|
|
140
|
+
|
|
141
|
+
## Связанные компоненты
|
|
142
|
+
|
|
143
|
+
- `Chip` — база: `ChipOptions` рендерит его и наследует его props через
|
|
144
|
+
`Omit<IChipProps, "prefix" | "postfix">`
|
|
145
|
+
- `ChipGroup` — рекомендуемый контейнер для ряда чипсов; `ChipOptions` имеет класс
|
|
146
|
+
`.chip` и потому не сжимается в режиме `oneLine`
|
|
147
|
+
- `ChipSort` — тот же приём (иконка в `prefix` + смена палитры по `selected`), но
|
|
148
|
+
для сортировки и без кнопки сброса
|
|
149
|
+
- `ChipMultiselect` — альтернатива, когда опции — это готовый список значений:
|
|
150
|
+
тоже `clearSelected` и счётчик, но с дропдауном и собственным target-элементом
|
|
151
|
+
- `ChipClearButton` (`src/components/Chip/ChipClearButton.tsx`) — кнопка сброса в
|
|
152
|
+
`postfix`; размер иконки-крестика берёт из `size` чипса
|
|
153
|
+
- `OptionsStrokeSrvIcon24` из `@sberbusiness/icons-next` — иконка в `prefix`
|
|
154
|
+
|
|
155
|
+
---
|
|
156
|
+
|
|
157
|
+
## Stories
|
|
158
|
+
|
|
159
|
+
Основные истории: `stories/Chips/ChipOptions.stories.tsx`
|
|
160
|
+
Файлы примеров: `stories/Chips/examples/ChipOptions/`
|
|
161
|
+
|
|
162
|
+
| Story | Example file | Что демонстрирует |
|
|
163
|
+
|---|---|---|
|
|
164
|
+
| `Playground` | `PlaygroundExample.tsx` | Интерактивный контроль `type`, `size`, `disabled` на живом счётчике |
|
|
165
|
+
| `Default` | `DefaultExample.tsx` | Базовый сценарий: клик увеличивает счётчик, крестик сбрасывает его в ноль; заодно показывает `clearButtonProps` с `aria-label` |
|
|
166
|
+
| `Types` | `TypesExample.tsx` | `TYPE_1` и `TYPE_2` |
|
|
167
|
+
| `Sizes` | `SizesExample.tsx` | Размеры SM / MD / LG |
|
|
168
|
+
| `States` | `StatesExample.tsx` | Выбранное и заблокированное состояния во всех размерах |
|
|
169
|
+
|
|
170
|
+
---
|
|
171
|
+
|
|
172
|
+
## История изменений
|
|
173
|
+
|
|
174
|
+
| Дата | Изменение |
|
|
175
|
+
|---|---|
|
|
176
|
+
| 2026-09-04 | Создан документ (TRI-133, AI-Ready Phase 1) |
|
|
177
|
+
| 2026-09-04 | AI-рефакторинг (TRI-133): JSDoc на интерфейсе, `size` разобран из props явно, иконка опций собрана в один элемент со сменой `paletteIndex`, `typeof children !== "undefined"` → `children !== undefined`, комментарий про `<span />`-заглушку в `postfix`; unit-тесты расширены с 3 до 19 кейсов. Публичный API, DOM и визуал не изменены |
|
|
178
|
+
| 2026-09-08 | Добавлен prop `clearButtonProps` (TRI-133): кнопке сброса можно задать `aria-label`, `disabled` и прочие props `ChipClearButton`; переданные `onClick` / `onKeyDown` вызываются после собственных обработчиков компонента. `Enter` / `Space` на кнопке сброса больше не всплывают до `Chip` — `Space` активирует кнопку, внешний `onKeyDown` чипса не срабатывает заодно со сбросом |
|
|
179
|
+
|
|
180
|
+
---
|
|
181
|
+
|
|
182
|
+
## Примеры использования
|
|
183
|
+
|
|
184
|
+
### Default
|
|
185
|
+
|
|
186
|
+
`stories/Chips/examples/ChipOptions/DefaultExample.tsx`
|
|
187
|
+
|
|
188
|
+
```tsx
|
|
189
|
+
import React, { useState } from "react";
|
|
190
|
+
import { ChipOptions } from "@sberbusiness/triplex-next";
|
|
191
|
+
|
|
192
|
+
export const DefaultExample = () => {
|
|
193
|
+
const [count, setCount] = useState(0);
|
|
194
|
+
|
|
195
|
+
return (
|
|
196
|
+
<ChipOptions
|
|
197
|
+
selected={count > 0}
|
|
198
|
+
onClick={() => setCount((c) => c + 1)}
|
|
199
|
+
clearSelected={() => setCount(0)}
|
|
200
|
+
clearButtonProps={{ "aria-label": "Сбросить выбранные опции" }}
|
|
201
|
+
>
|
|
202
|
+
{count > 0 ? count : undefined}
|
|
203
|
+
</ChipOptions>
|
|
204
|
+
);
|
|
205
|
+
};
|
|
206
|
+
```
|
|
207
|
+
|
|
208
|
+
### Types
|
|
209
|
+
|
|
210
|
+
`stories/Chips/examples/ChipOptions/TypesExample.tsx`
|
|
211
|
+
|
|
212
|
+
```tsx
|
|
213
|
+
import React, { useState } from "react";
|
|
214
|
+
import { ChipOptions, EChipType } from "@sberbusiness/triplex-next";
|
|
215
|
+
|
|
216
|
+
type TypeItemProps = {
|
|
217
|
+
type: EChipType;
|
|
218
|
+
label: string;
|
|
219
|
+
};
|
|
220
|
+
|
|
221
|
+
const TypeItem = ({ type, label }: TypeItemProps) => {
|
|
222
|
+
const [count, setCount] = useState(0);
|
|
223
|
+
|
|
224
|
+
const handleClick = () => setCount((prevCount) => prevCount + 1);
|
|
225
|
+
|
|
226
|
+
const handleClearSelected = () => setCount(0);
|
|
227
|
+
|
|
228
|
+
return (
|
|
229
|
+
<div>
|
|
230
|
+
<div style={{ marginBottom: 8, fontSize: 16, fontWeight: 700 }}>{label}</div>
|
|
231
|
+
<ChipOptions type={type} selected={count > 0} onClick={handleClick} clearSelected={handleClearSelected}>
|
|
232
|
+
{count || undefined}
|
|
233
|
+
</ChipOptions>
|
|
234
|
+
</div>
|
|
235
|
+
);
|
|
236
|
+
};
|
|
237
|
+
|
|
238
|
+
const typeOptions = Object.values(EChipType).map((type) => ({
|
|
239
|
+
value: type,
|
|
240
|
+
label: type.toUpperCase(),
|
|
241
|
+
}));
|
|
242
|
+
|
|
243
|
+
export const TypesExample = () => (
|
|
244
|
+
<div style={{ display: "flex", flexDirection: "column", gap: 12 }}>
|
|
245
|
+
{typeOptions.map(({ value, label }) => (
|
|
246
|
+
<TypeItem key={value} type={value} label={label} />
|
|
247
|
+
))}
|
|
248
|
+
</div>
|
|
249
|
+
);
|
|
250
|
+
```
|
|
251
|
+
|
|
252
|
+
### Sizes
|
|
253
|
+
|
|
254
|
+
`stories/Chips/examples/ChipOptions/SizesExample.tsx`
|
|
255
|
+
|
|
256
|
+
```tsx
|
|
257
|
+
import React, { useState } from "react";
|
|
258
|
+
import { ChipOptions, EComponentSize } from "@sberbusiness/triplex-next";
|
|
259
|
+
|
|
260
|
+
type SizeItemProps = {
|
|
261
|
+
size: EComponentSize;
|
|
262
|
+
label: string;
|
|
263
|
+
};
|
|
264
|
+
|
|
265
|
+
const SizeItem = ({ size, label }: SizeItemProps) => {
|
|
266
|
+
const [count, setCount] = useState(0);
|
|
267
|
+
|
|
268
|
+
const handleClick = () => setCount((prevCount) => prevCount + 1);
|
|
269
|
+
|
|
270
|
+
const handleClearSelected = () => setCount(0);
|
|
271
|
+
|
|
272
|
+
return (
|
|
273
|
+
<div>
|
|
274
|
+
<div style={{ marginBottom: 8, fontSize: 16, fontWeight: 700 }}>{label}</div>
|
|
275
|
+
<ChipOptions size={size} selected={count > 0} onClick={handleClick} clearSelected={handleClearSelected}>
|
|
276
|
+
{count || undefined}
|
|
277
|
+
</ChipOptions>
|
|
278
|
+
</div>
|
|
279
|
+
);
|
|
280
|
+
};
|
|
281
|
+
|
|
282
|
+
const sizeOptions = Object.values(EComponentSize).map((size) => ({
|
|
283
|
+
value: size,
|
|
284
|
+
label: size.toUpperCase(),
|
|
285
|
+
}));
|
|
286
|
+
|
|
287
|
+
export const SizesExample = () => (
|
|
288
|
+
<div style={{ display: "flex", flexDirection: "column", gap: 12 }}>
|
|
289
|
+
{sizeOptions.map(({ value, label }) => (
|
|
290
|
+
<SizeItem key={value} size={value} label={label} />
|
|
291
|
+
))}
|
|
292
|
+
</div>
|
|
293
|
+
);
|
|
294
|
+
```
|
|
295
|
+
|
|
296
|
+
### States
|
|
297
|
+
|
|
298
|
+
`stories/Chips/examples/ChipOptions/StatesExample.tsx`
|
|
299
|
+
|
|
300
|
+
```tsx
|
|
301
|
+
import React from "react";
|
|
302
|
+
import { ChipOptions, EComponentSize } from "@sberbusiness/triplex-next";
|
|
303
|
+
|
|
304
|
+
export const StatesExample = () => {
|
|
305
|
+
return (
|
|
306
|
+
<div style={{ display: "flex", gap: 24 }}>
|
|
307
|
+
<div style={{ display: "flex", flexDirection: "column", alignItems: "flex-start", gap: "8px" }}>
|
|
308
|
+
<div style={{ marginBottom: "8px", fontSize: "16px", fontWeight: "700" }}>Selected</div>
|
|
309
|
+
<ChipOptions size={EComponentSize.SM} selected clearSelected={() => {}}>
|
|
310
|
+
{1}
|
|
311
|
+
</ChipOptions>
|
|
312
|
+
<ChipOptions size={EComponentSize.MD} selected clearSelected={() => {}}>
|
|
313
|
+
{2}
|
|
314
|
+
</ChipOptions>
|
|
315
|
+
<ChipOptions size={EComponentSize.LG} selected clearSelected={() => {}}>
|
|
316
|
+
{3}
|
|
317
|
+
</ChipOptions>
|
|
318
|
+
</div>
|
|
319
|
+
<div style={{ display: "flex", flexDirection: "column", alignItems: "flex-start", gap: "8px" }}>
|
|
320
|
+
<div style={{ marginBottom: "8px", fontSize: "16px", fontWeight: "700" }}>Disabled</div>
|
|
321
|
+
<ChipOptions size={EComponentSize.SM} disabled clearSelected={() => {}} />
|
|
322
|
+
<ChipOptions size={EComponentSize.MD} disabled clearSelected={() => {}} />
|
|
323
|
+
<ChipOptions size={EComponentSize.LG} disabled clearSelected={() => {}} />
|
|
324
|
+
</div>
|
|
325
|
+
</div>
|
|
326
|
+
);
|
|
327
|
+
};
|
|
328
|
+
```
|
|
329
|
+
|
|
@@ -0,0 +1,333 @@
|
|
|
1
|
+
---
|
|
2
|
+
component: ChipSort
|
|
3
|
+
category: Chips
|
|
4
|
+
related: [Chip, ChipSelect, ChipGroup, SelectExtendedField, IconWrapper]
|
|
5
|
+
tokens: []
|
|
6
|
+
stories: stories/Chips/ChipSort.stories.tsx
|
|
7
|
+
version: "1.0"
|
|
8
|
+
---
|
|
9
|
+
|
|
10
|
+
# ChipSort
|
|
11
|
+
|
|
12
|
+
## Назначение
|
|
13
|
+
|
|
14
|
+
Чипс-переключатель сортировки: компактная иконка сортировки, по нажатию на которую
|
|
15
|
+
открывается выпадающий список вариантов. Выбранное значение текстом не показывается —
|
|
16
|
+
единственная обратная связь в свёрнутом виде это подсветка иконки, и она включается,
|
|
17
|
+
только когда текущее значение отличается от `defaultValue`.
|
|
18
|
+
|
|
19
|
+
Используй когда: в панели фильтров нужен выбор порядка сортировки, а места под
|
|
20
|
+
текстовый лейбл нет — иконка занимает ровно ширину `ChipIcon`.
|
|
21
|
+
Не используй когда: выбранное значение должно быть видно текстом (возьми `ChipSelect`),
|
|
22
|
+
нужен множественный выбор (`ChipMultiselect`) или нужен обычный select в форме
|
|
23
|
+
(`SelectField`).
|
|
24
|
+
|
|
25
|
+
Компонент управляемый: `value` и `onChange` держит потребитель. Рекомендуется
|
|
26
|
+
располагать внутри `ChipGroup` — он задаёт отступы и режим одной строки.
|
|
27
|
+
|
|
28
|
+
---
|
|
29
|
+
|
|
30
|
+
## Варианты и props
|
|
31
|
+
|
|
32
|
+
`IChipSortProps` — это `Omit<IChipSelectProps, "targetProps" | "clearSelected" | "defaultValue">`
|
|
33
|
+
плюс собственный `defaultValue`. То есть API совпадает с `ChipSelect`, кроме
|
|
34
|
+
обязательного сброса значения (`clearSelected`): у `ChipSort` сбрасывать нечего,
|
|
35
|
+
у сортировки всегда есть текущий порядок.
|
|
36
|
+
|
|
37
|
+
Неизвестные компоненту атрибуты (`data-*`, `aria-*`, `style`, `id`, обработчики `<div>`)
|
|
38
|
+
попадают на корневой `<div>` `SelectExtendedField`, а не на чипс-таргет.
|
|
39
|
+
|
|
40
|
+
### Обязательные props
|
|
41
|
+
|
|
42
|
+
| Prop | Тип | Описание |
|
|
43
|
+
|---|---|---|
|
|
44
|
+
| `size` | `EComponentSize` | Размер чипса и выпадающего списка: `SM` / `MD` / `LG` |
|
|
45
|
+
| `options` | `ISelectFieldOption[]` | Список вариантов сортировки. Опция сопоставляется по `id` |
|
|
46
|
+
| `onChange` | `(option: ISelectFieldOption) => void` | Вызывается с выбранной опцией; список после выбора закрывается |
|
|
47
|
+
|
|
48
|
+
### Опциональные props
|
|
49
|
+
|
|
50
|
+
| Prop | Тип | По умолчанию | Описание |
|
|
51
|
+
|---|---|---|---|
|
|
52
|
+
| `value` | `ISelectFieldOption` | — | Текущее значение. Помечает опцию в списке как выбранную и участвует в подсветке иконки |
|
|
53
|
+
| `defaultValue` | `ISelectExtendedFieldDefaultOption` | — | Значение «по умолчанию». Пока `value` **глубоко равен** ему, чипс не подсвечен |
|
|
54
|
+
| `type` | `EChipType` | `TYPE_1` | Визуальный тип чипса, прокидывается в `ChipIcon` |
|
|
55
|
+
| `disabled` | `boolean` | `false` | Блокирует таргет (визуально + `tabIndex={-1}`, см. ограничения `Chip`) |
|
|
56
|
+
| `label` | `React.ReactNode` | — | Используется **только** как заголовок списка в мобильном режиме (`mobileTitle`). В десктопной разметке не отображается |
|
|
57
|
+
| `className` | `string` | — | Дополнительный класс корневого `<div>`, рядом с `.chipGroupItem` |
|
|
58
|
+
| `onOpen` / `onClose` | `() => void` | — | Колбэки открытия и закрытия списка, приходят из `SelectExtendedField` |
|
|
59
|
+
| `closeOnTab` | `boolean` | `false` | Закрывать список по Tab |
|
|
60
|
+
|
|
61
|
+
### Семантика `defaultValue` и подсветки
|
|
62
|
+
|
|
63
|
+
Это главное отличие `ChipSort` от остальных чипсов-селектов:
|
|
64
|
+
|
|
65
|
+
```text
|
|
66
|
+
selected = value != null && !isEqual(defaultValue, value)
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
- `value` не задан → иконка не подсвечена;
|
|
70
|
+
- `value` задан, `defaultValue` не задан → подсвечена;
|
|
71
|
+
- `value` глубоко равен `defaultValue` (`lodash-es/isEqual`, не по ссылке) → **не**
|
|
72
|
+
подсвечена: сортировка по умолчанию — это не изменённый фильтр;
|
|
73
|
+
- иначе → подсвечена (класс `.selected` на чипсе плюс акцентный `paletteIndex` иконки).
|
|
74
|
+
|
|
75
|
+
Сравнение глубокое, поэтому `defaultValue={{...options[0]}}` работает так же, как
|
|
76
|
+
`defaultValue={options[0]}`. Но `label` опции — `React.ReactNode`: если в нём лежит
|
|
77
|
+
JSX, `isEqual` сравнивает деревья элементов. Держи в `defaultValue` ту же опцию,
|
|
78
|
+
что и в `options`, — тогда сравнение остаётся дешёвым и предсказуемым.
|
|
79
|
+
|
|
80
|
+
### Известные ограничения (унаследованы от `IChipSelectProps`)
|
|
81
|
+
|
|
82
|
+
- **`displayedValue`** входит в тип props, но компонентом не используется: отображать
|
|
83
|
+
нечего, таргет — иконка. Переданное значение уходит в `...restProps` на корневой
|
|
84
|
+
`<div>` и вызывает React-предупреждение о неизвестном DOM-атрибуте. Не передавай.
|
|
85
|
+
- **`dropdownProps`** объявлен в типе (через `ISelectFieldProps`), но до
|
|
86
|
+
`SelectExtendedFieldDropdownDefault` не доходит — попадает в тот же спред на
|
|
87
|
+
корневой `<div>`. Настроить `Dropdown` через него сейчас нельзя. Тот же разрыв
|
|
88
|
+
есть у `ChipSelect`; чинить нужно синхронно по всему семейству, отдельной задачей.
|
|
89
|
+
- **Доступное имя таргета задать нечем.** У icon-only чипса нет текста, а
|
|
90
|
+
`role="combobox"` стоит на `<span>` из `ChipIcon`. Переданные потребителем
|
|
91
|
+
`aria-label` / `aria-labelledby` попадают в `...restProps` и приземляются на
|
|
92
|
+
корневой `<div>`, у которого нет `role`: на generic-элементе эти атрибуты
|
|
93
|
+
спецификацией ARIA не поддерживаются и скринридером не озвучиваются. Добраться
|
|
94
|
+
до таргета через публичный API нельзя — `targetProps` исключён `Omit`'ом, а
|
|
95
|
+
`renderTarget` пользовательские props в `ChipIcon` не спредит. Чинится пробросом
|
|
96
|
+
до `ChipIcon` (например, возвратом `targetProps`) — изменение публичного API,
|
|
97
|
+
отдельной задачей синхронно по семейству `Chip*`.
|
|
98
|
+
- **Цель `ref` разошлась с объявленным типом.** `ref` уходит на чипс-таргет
|
|
99
|
+
(`<span>` из `ChipIcon`), а тип объявлен `HTMLDivElement` — компилируется только
|
|
100
|
+
из-за структурной совместимости. Соседний `ChipSelect` прокидывает `ref` на
|
|
101
|
+
корневой `<div>` и своему типу соответствует, то есть внутри семейства контракт
|
|
102
|
+
разъехался. Кандидат на исправление в мажорной версии, не вечное правило.
|
|
103
|
+
- **Ширина списка** не настраивается: `width` в `SelectExtendedFieldDropdownDefault`
|
|
104
|
+
не передаётся, поэтому работает дефолт `Dropdown` — `EDropdownWidth.CONTENT`,
|
|
105
|
+
ширина по контенту, а не по ширине таргета-иконки (что для иконки и правильно).
|
|
106
|
+
|
|
107
|
+
---
|
|
108
|
+
|
|
109
|
+
## Дизайн-токены
|
|
110
|
+
|
|
111
|
+
Собственных токенов у компонента нет: в `styles/Chip.module.less` ему принадлежит
|
|
112
|
+
только `.chipGroupItem` (`display: inline-block`). Весь визуал приходит из компонентов,
|
|
113
|
+
на которых он построен:
|
|
114
|
+
|
|
115
|
+
- таргет — `Chip` / `ChipIcon`, группа `Chip` (фон, цвет, тень фокуса, состояние `selected`);
|
|
116
|
+
- иконка сортировки — `IconWrapper` (группа `IconWrapper`) плюс палитра иконки из
|
|
117
|
+
`@sberbusiness/icons-next` (`paletteIndex`, а не токен);
|
|
118
|
+
- выпадающий список — `Dropdown` и `DropdownList`.
|
|
119
|
+
|
|
120
|
+
Полный список токенов чипса — в `Chip-ai.md` → «Дизайн-токены».
|
|
121
|
+
|
|
122
|
+
---
|
|
123
|
+
|
|
124
|
+
## Инварианты
|
|
125
|
+
|
|
126
|
+
- **`forwardRef`** — не убирать. `ref` указывает **не** на корневой `<div>`, а на
|
|
127
|
+
чипс-таргет (`<span>` из `ChipIcon`). Цель `ref` и объявленный тип не менять без
|
|
128
|
+
breaking-окна — почему они разошлись, см. «Известные ограничения».
|
|
129
|
+
- **`clearSelected` исключён из props** намеренно (`Omit`). Не возвращать: у сортировки
|
|
130
|
+
нет состояния «ничего не выбрано».
|
|
131
|
+
- **`defaultValue` переопределяет DOM-атрибут** `defaultValue` из
|
|
132
|
+
`React.HTMLAttributes<HTMLDivElement>` (для этого он и в `Omit`). Тип
|
|
133
|
+
`ISelectExtendedFieldDefaultOption`, а не строка.
|
|
134
|
+
- **Подсветка считается через `isEqual`**, а не по ссылке. Замена на `===` изменит
|
|
135
|
+
поведение у потребителей, передающих новый объект на каждый рендер.
|
|
136
|
+
- **`role="combobox"` на таргете** перекрывает `role="button"` из `Chip` — он выставляется
|
|
137
|
+
после дефолтов `Chip` в спреде props. Порядок не менять.
|
|
138
|
+
- **`aria-controls` таргета и `listId` списка — один и тот же `uniqueId()`**, живущий
|
|
139
|
+
в `useRef` на всё время жизни экземпляра. Связка target ↔ список держится только на нём.
|
|
140
|
+
- **`uniqueId()` из `lodash-es`, а не `React.useId`** — код должен собираться в ветке
|
|
141
|
+
`release-0` под React 17.
|
|
142
|
+
- **Класс `.chipGroupItem`** на корневом `<div>` — общий маркер семейства Chip*.
|
|
143
|
+
Не удалять и не переименовывать.
|
|
144
|
+
- **Barrel `src/components/Chip/index.ts`** экспортирует `ChipSort` и `IChipSortProps` —
|
|
145
|
+
сохранять.
|
|
146
|
+
- **Story ids `chips-chipsort--*`** завязаны на baseline-скриншоты в `__screenshots__/` —
|
|
147
|
+
переименование story требует обновления baseline.
|
|
148
|
+
|
|
149
|
+
---
|
|
150
|
+
|
|
151
|
+
## Accessibility
|
|
152
|
+
|
|
153
|
+
- Таргет — `<span role="combobox">` с `aria-expanded`, отражающим состояние списка,
|
|
154
|
+
и `aria-controls`, указывающим на `id` списка (`role="listbox"`). Список рендерится
|
|
155
|
+
только когда открыт, поэтому в закрытом состоянии `aria-controls` ссылается на
|
|
156
|
+
отсутствующий элемент — это осознанный компромисс текущей реализации дропдауна.
|
|
157
|
+
- **Клавиатура:** Enter и Space на закрытом таргете открывают список и вызывают
|
|
158
|
+
`preventDefault()` (гасят прокрутку страницы и «клик» по пробелу). На открытом
|
|
159
|
+
списке обработчик ничего не делает — закрытием занимается `SelectExtendedField`
|
|
160
|
+
(Escape, клик вне компонента, при `closeOnTab` — Tab). Стрелочная навигация по
|
|
161
|
+
опциям — внутри `DropdownList`.
|
|
162
|
+
- Компонент **не задаёт себе доступное имя**, и задать его снаружи сейчас нечем —
|
|
163
|
+
см. «Известные ограничения». Библиотека мультиязычная, текст не хардкодится.
|
|
164
|
+
- `disabled` у `Chip` не нативный: `pointer-events: none` плюс `tabIndex={-1}`.
|
|
165
|
+
Программно диспатченные события до обработчиков дойдут — см. `Chip-ai.md`.
|
|
166
|
+
- В мобильном режиме (`max-width: SM_MAX`) список показывается как мобильный
|
|
167
|
+
`Dropdown` с заголовком из `label` — без `label` шапка мобильного списка пустая.
|
|
168
|
+
|
|
169
|
+
---
|
|
170
|
+
|
|
171
|
+
## Связанные компоненты
|
|
172
|
+
|
|
173
|
+
- `Chip` — база всего семейства; отсюда классы состояний, фокус и ограничения `disabled`
|
|
174
|
+
- `ChipIcon` — конкретный таргет `ChipSort`: чипс, у которого `children` уходит в `prefix`,
|
|
175
|
+
а `postfix` — пустой `<span />` ради симметричных отступов
|
|
176
|
+
- `ChipSelect` — альтернатива с тем же API (`IChipSortProps` наследует `IChipSelectProps`):
|
|
177
|
+
показывает выбранное значение текстом и требует `clearSelected`
|
|
178
|
+
- `ChipGroup` — рекомендуемый контейнер: отступы, перенос строк, режим `oneLine`
|
|
179
|
+
- `SelectExtendedField` — база: владеет состоянием открытости, правилами закрытия
|
|
180
|
+
и корневым `<div>`; список рендерится через `SelectExtendedFieldDropdownDefault`
|
|
181
|
+
- `IconWrapper` — обёртка иконки сортировки, добавляет состояния `active` / `disabled`
|
|
182
|
+
|
|
183
|
+
---
|
|
184
|
+
|
|
185
|
+
## Stories
|
|
186
|
+
|
|
187
|
+
Основные истории: `stories/Chips/ChipSort.stories.tsx`
|
|
188
|
+
Файлы примеров: `stories/Chips/examples/ChipSort/`
|
|
189
|
+
|
|
190
|
+
| Story | Example file | Что демонстрирует |
|
|
191
|
+
|---|---|---|
|
|
192
|
+
| `Playground` | `PlaygroundExample.tsx` | Интерактивный контроль `type`, `size`, `disabled` и индекса `defaultValue` |
|
|
193
|
+
| `Default` | `DefaultExample.tsx` | Базовое управляемое использование: `value` + `defaultValue` + `onChange` |
|
|
194
|
+
| `Types` | `TypesExample.tsx` | `TYPE_1` и `TYPE_2` |
|
|
195
|
+
| `Sizes` | `SizesExample.tsx` | Размеры SM / MD / LG |
|
|
196
|
+
| `VisualTests` | `VisualTestsExample.tsx` | Скриншот-регрессия: три размера с открытым списком |
|
|
197
|
+
|
|
198
|
+
---
|
|
199
|
+
|
|
200
|
+
## История изменений
|
|
201
|
+
|
|
202
|
+
| Дата | Изменение |
|
|
203
|
+
|---|---|
|
|
204
|
+
| 2026-09-04 | Создан документ (TRI-132, AI-Ready Phase 1) |
|
|
205
|
+
| 2026-09-04 | AI-рефакторинг (TRI-132): codestyle-чистка `ChipSort.tsx` (объединены дублирующиеся импорты, пути приведены к `../`, magic-числа палитры иконки вынесены в именованные константы, добавлены JSDoc и комментарии к нетривиальной логике), unit-тесты расширены с 1 до 19 кейсов; публичный API, DOM и визуал не изменены |
|
|
206
|
+
|
|
207
|
+
---
|
|
208
|
+
|
|
209
|
+
## Примеры использования
|
|
210
|
+
|
|
211
|
+
### Default
|
|
212
|
+
|
|
213
|
+
`stories/Chips/examples/ChipSort/DefaultExample.tsx`
|
|
214
|
+
|
|
215
|
+
```tsx
|
|
216
|
+
import React, { useState } from "react";
|
|
217
|
+
import { ChipSort, EComponentSize, type ISelectFieldOption } from "@sberbusiness/triplex-next";
|
|
218
|
+
|
|
219
|
+
const options: ISelectFieldOption[] = [
|
|
220
|
+
{ id: "chip-sort-1", label: "По дате", value: "i1" },
|
|
221
|
+
{ id: "chip-sort-2", label: "По времени", value: "i2" },
|
|
222
|
+
{ id: "chip-sort-3", label: "По названию", value: "i3" },
|
|
223
|
+
];
|
|
224
|
+
|
|
225
|
+
export const DefaultExample = () => {
|
|
226
|
+
const [value, setValue] = useState<ISelectFieldOption>(options[0]);
|
|
227
|
+
return (
|
|
228
|
+
<ChipSort
|
|
229
|
+
size={EComponentSize.MD}
|
|
230
|
+
defaultValue={options[0]}
|
|
231
|
+
value={value}
|
|
232
|
+
options={options}
|
|
233
|
+
onChange={setValue}
|
|
234
|
+
/>
|
|
235
|
+
);
|
|
236
|
+
};
|
|
237
|
+
```
|
|
238
|
+
|
|
239
|
+
### Types
|
|
240
|
+
|
|
241
|
+
`stories/Chips/examples/ChipSort/TypesExample.tsx`
|
|
242
|
+
|
|
243
|
+
```tsx
|
|
244
|
+
import React, { useState } from "react";
|
|
245
|
+
import { ChipSort, EChipType, EComponentSize, type ISelectFieldOption } from "@sberbusiness/triplex-next";
|
|
246
|
+
|
|
247
|
+
const options: ISelectFieldOption[] = [
|
|
248
|
+
{ id: "chip-sort-1", label: "По дате", value: "i1" },
|
|
249
|
+
{ id: "chip-sort-2", label: "По времени", value: "i2" },
|
|
250
|
+
{ id: "chip-sort-3", label: "По названию", value: "i3" },
|
|
251
|
+
];
|
|
252
|
+
|
|
253
|
+
type TypeItemProps = {
|
|
254
|
+
type: EChipType;
|
|
255
|
+
label: string;
|
|
256
|
+
};
|
|
257
|
+
|
|
258
|
+
const TypeItem = ({ type, label }: TypeItemProps) => {
|
|
259
|
+
const [value, setValue] = useState<ISelectFieldOption>(options[0]);
|
|
260
|
+
|
|
261
|
+
return (
|
|
262
|
+
<div>
|
|
263
|
+
<div style={{ marginBottom: 8, fontSize: 16, fontWeight: 700 }}>{label}</div>
|
|
264
|
+
<ChipSort
|
|
265
|
+
type={type}
|
|
266
|
+
size={EComponentSize.MD}
|
|
267
|
+
defaultValue={options[0]}
|
|
268
|
+
value={value}
|
|
269
|
+
options={options}
|
|
270
|
+
onChange={setValue}
|
|
271
|
+
/>
|
|
272
|
+
</div>
|
|
273
|
+
);
|
|
274
|
+
};
|
|
275
|
+
|
|
276
|
+
const sizeOptions = Object.values(EChipType).map((type) => ({
|
|
277
|
+
value: type,
|
|
278
|
+
label: type.toUpperCase(),
|
|
279
|
+
}));
|
|
280
|
+
|
|
281
|
+
export const TypesExample = () => (
|
|
282
|
+
<div style={{ display: "flex", flexDirection: "column", gap: 12 }}>
|
|
283
|
+
{sizeOptions.map(({ value, label }) => (
|
|
284
|
+
<TypeItem key={value} type={value} label={label} />
|
|
285
|
+
))}
|
|
286
|
+
</div>
|
|
287
|
+
);
|
|
288
|
+
```
|
|
289
|
+
|
|
290
|
+
### Sizes
|
|
291
|
+
|
|
292
|
+
`stories/Chips/examples/ChipSort/SizesExample.tsx`
|
|
293
|
+
|
|
294
|
+
```tsx
|
|
295
|
+
import React, { useState } from "react";
|
|
296
|
+
import { ChipSort, EComponentSize, type ISelectFieldOption } from "@sberbusiness/triplex-next";
|
|
297
|
+
|
|
298
|
+
const options: ISelectFieldOption[] = [
|
|
299
|
+
{ id: "chip-sort-1", label: "По дате", value: "i1" },
|
|
300
|
+
{ id: "chip-sort-2", label: "По времени", value: "i2" },
|
|
301
|
+
{ id: "chip-sort-3", label: "По названию", value: "i3" },
|
|
302
|
+
];
|
|
303
|
+
|
|
304
|
+
type SizeItemProps = {
|
|
305
|
+
size: EComponentSize;
|
|
306
|
+
label: string;
|
|
307
|
+
};
|
|
308
|
+
|
|
309
|
+
const SizeItem = ({ size, label }: SizeItemProps) => {
|
|
310
|
+
const [value, setValue] = useState<ISelectFieldOption>(options[0]);
|
|
311
|
+
|
|
312
|
+
return (
|
|
313
|
+
<div>
|
|
314
|
+
<div style={{ marginBottom: 8, fontSize: 16, fontWeight: 700 }}>{label}</div>
|
|
315
|
+
<ChipSort size={size} defaultValue={options[0]} value={value} options={options} onChange={setValue} />
|
|
316
|
+
</div>
|
|
317
|
+
);
|
|
318
|
+
};
|
|
319
|
+
|
|
320
|
+
const sizeOptions = Object.values(EComponentSize).map((size) => ({
|
|
321
|
+
value: size,
|
|
322
|
+
label: size.toUpperCase(),
|
|
323
|
+
}));
|
|
324
|
+
|
|
325
|
+
export const SizesExample = () => (
|
|
326
|
+
<div style={{ display: "flex", flexDirection: "column", gap: 12 }}>
|
|
327
|
+
{sizeOptions.map(({ value, label }) => (
|
|
328
|
+
<SizeItem key={value} size={value} label={label} />
|
|
329
|
+
))}
|
|
330
|
+
</div>
|
|
331
|
+
);
|
|
332
|
+
```
|
|
333
|
+
|