@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.
Files changed (89) hide show
  1. package/README.md +7 -6
  2. package/bundle/components/AlertContext.md +11 -8
  3. package/bundle/components/AlertProcess.md +13 -10
  4. package/bundle/components/Badge.md +6 -3
  5. package/bundle/components/Button.md +46 -43
  6. package/bundle/components/ButtonDropdownExtended.md +3 -2
  7. package/bundle/components/ButtonIcon.md +7 -4
  8. package/bundle/components/Calendar.md +62 -59
  9. package/bundle/components/Caption.md +26 -23
  10. package/bundle/components/CardAction.md +615 -0
  11. package/bundle/components/Checkbox.md +32 -29
  12. package/bundle/components/CheckboxTreeExtended.md +9 -5
  13. package/bundle/components/Chip.md +51 -47
  14. package/bundle/components/ChipMonthYearPicker.md +344 -0
  15. package/bundle/components/ChipMultiselect.md +1251 -0
  16. package/bundle/components/ChipOptions.md +329 -0
  17. package/bundle/components/ChipSort.md +333 -0
  18. package/bundle/components/ChipSuggest.md +587 -0
  19. package/bundle/components/CodeText.md +26 -23
  20. package/bundle/components/Col.md +1 -2
  21. package/bundle/components/CollapsibleTree.md +8 -5
  22. package/bundle/components/DateField.md +222 -303
  23. package/bundle/components/DatePickerExtended.md +3 -1
  24. package/bundle/components/Dropdown.md +33 -28
  25. package/bundle/components/EmptyView.md +12 -5
  26. package/bundle/components/FooterPage.md +6 -2
  27. package/bundle/components/FormField.md +47 -42
  28. package/bundle/components/FormGroup.md +192 -52
  29. package/bundle/components/Header.md +491 -211
  30. package/bundle/components/HeaderPage.md +6 -2
  31. package/bundle/components/HelpBox.md +7 -1
  32. package/bundle/components/ImageGallery.md +33 -34
  33. package/bundle/components/ImageGalleryExtended.md +33 -34
  34. package/bundle/components/Island.md +16 -13
  35. package/bundle/components/IslandAccordion.md +443 -152
  36. package/bundle/components/IslandWidget.md +622 -157
  37. package/bundle/components/LightBox.md +14 -12
  38. package/bundle/components/Link.md +211 -96
  39. package/bundle/components/ListActionItem.md +7 -4
  40. package/bundle/components/ListItem.md +11 -8
  41. package/bundle/components/ListItemControlsButton.md +16 -13
  42. package/bundle/components/ListItemControlsButtonDropdown.md +9 -6
  43. package/bundle/components/ListItemSelectable.md +7 -4
  44. package/bundle/components/ListMasterFooter.md +8 -5
  45. package/bundle/components/ListMasterHeader.md +8 -5
  46. package/bundle/components/ListSortableItem.md +11 -8
  47. package/bundle/components/ListTableItem.md +7 -4
  48. package/bundle/components/LoaderMiddle.md +7 -4
  49. package/bundle/components/LoaderScreen.md +11 -8
  50. package/bundle/components/LoaderSmall.md +7 -4
  51. package/bundle/components/Marker.md +184 -69
  52. package/bundle/components/MarkerStatus.md +206 -88
  53. package/bundle/components/MaskedField.md +4 -5
  54. package/bundle/components/MasterTable.md +602 -0
  55. package/bundle/components/ModalWindow.md +7 -4
  56. package/bundle/components/MonthYearField.md +3 -1
  57. package/bundle/components/MultiselectField.md +2183 -132
  58. package/bundle/components/Notification.md +12 -9
  59. package/bundle/components/Overlay.md +9 -4
  60. package/bundle/components/Page.md +2 -2
  61. package/bundle/components/Pagination.md +15 -8
  62. package/bundle/components/Row.md +1 -1
  63. package/bundle/components/SelectExtendedField.md +3 -3
  64. package/bundle/components/SelectField.md +466 -148
  65. package/bundle/components/SliderExtended.md +39 -36
  66. package/bundle/components/SmallInput.md +194 -51
  67. package/bundle/components/Suggest.md +535 -99
  68. package/bundle/components/SwipeableArea.md +428 -79
  69. package/bundle/components/TableBasic.md +19 -11
  70. package/bundle/components/TabsExtended.md +24 -21
  71. package/bundle/components/Tag.md +6 -2
  72. package/bundle/components/Text.md +28 -25
  73. package/bundle/components/TextField.md +3 -4
  74. package/bundle/components/ThemeProvider.md +50 -1
  75. package/bundle/components/Title.md +26 -23
  76. package/bundle/components/Tooltip.md +24 -21
  77. package/bundle/components/TopOverlay.md +7 -4
  78. package/bundle/guides/codestyle.md +3 -0
  79. package/bundle/guides/context.md +84 -12
  80. package/bundle/guides/guidelines.md +321 -0
  81. package/bundle/guides/template.md +22 -12
  82. package/bundle/manifest.json +75 -17
  83. package/bundle/release-notes/1.45.0.mdx +117 -0
  84. package/bundle/release-notes/1.46.0.mdx +66 -0
  85. package/bundle/release-notes/1.47.0.mdx +7 -0
  86. package/bundle/tokens.json +6362 -0
  87. package/dist/index.js +177 -28
  88. package/dist/index.js.map +1 -1
  89. package/package.json +1 -1
@@ -0,0 +1,602 @@
1
+ ---
2
+ component: MasterTable
3
+ category: Table
4
+ related:
5
+ - TableBasic
6
+ - Pagination
7
+ tokens: []
8
+ stories: stories/MasterTable/MasterTable.stories.tsx
9
+ version: "1.0"
10
+ ---
11
+
12
+ # MasterTable
13
+
14
+ ## Назначение
15
+
16
+ Контейнер таблицы. Сам рисует только позиционирующую обёртку (`<div>` с `position: relative`), а его настоящая работа — собрать вокруг таблицы панели (фильтры, чипы, настройки колонок, подвал, пагинацию) и раздать им общее состояние через `MasterTableContext`: признак загрузки и текущую структуру колонок.
17
+
18
+ Используй когда: строишь табличный экран из нескольких частей — над таблицей фильтры, под ней подвал с массовыми действиями и пагинация, а состояние загрузки должно синхронно блокировать всё это.
19
+
20
+ Не используй когда: нужна только таблица без обвязки — тогда достаточно `TableBasic` (он работает и вне `MasterTable`, читая значения контекста по умолчанию). Для простых списков без колонок бери `List`.
21
+
22
+ ---
23
+
24
+ ## Варианты и props
25
+
26
+ `MasterTable` — контейнер, а не управляющий компонент: он не хранит данные, страницы и фильтры. Единственное собственное состояние — массив колонок, который в него записывает `MasterTable.TableBasic`.
27
+
28
+ ### Props
29
+
30
+ | Prop | Тип | По умолчанию | Описание |
31
+ |---|---|---|---|
32
+ | `loading` | `boolean` | `false` | Состояние загрузки. Не рисуется на самом контейнере, а раздаётся потомкам через контекст. |
33
+ | `children` | `React.ReactNode` | — | Панели и таблица. Порядок в разметке = порядок на экране, компонент ничего не переставляет. |
34
+ | `...rest` | `React.HTMLAttributes<HTMLDivElement>` | — | Прокидывается на корневой `<div>`. `className` объединяется с собственным классом. |
35
+
36
+ Интерфейс props — `IMasterTableProps` в `src/components/Table/TableBasic/types.ts` (там же живут интерфейсы остальных частей семейства).
37
+
38
+ ### Статические субкомпоненты
39
+
40
+ Публичный API строится через статические свойства. Те же компоненты доступны и напрямую из barrel `src/components/Table/index.ts` — статики нужны для читаемой композиции.
41
+
42
+ | Субкомпонент | Роль |
43
+ |---|---|
44
+ | `MasterTable.FilterPanel` | Панель под элементы фильтрации над таблицей. |
45
+ | `MasterTable.ChipPanel` | Панель чипов активных фильтров; `.Links` — блок ссылок справа. |
46
+ | `MasterTable.TableBasicSettings` | Выпадающие настройки таблицы (видимость и порядок колонок). |
47
+ | `MasterTable.TableBasic` | Сама таблица. |
48
+ | `MasterTable.NoColumns` | Заглушка для случая, когда пользователь скрыл все колонки. |
49
+ | `MasterTable.TableFooter` | Подвал: `.Summary` (подытоги, выбор всех) и `.Controls` (кнопки). |
50
+ | `MasterTable.PaginationPanel` | Панель под пагинацию. |
51
+
52
+ ### Что раздаёт контекст
53
+
54
+ `MasterTableContext` (`src/components/Table/MasterTableContext.ts`) — единственный канал связи между частями:
55
+
56
+ | Поле | Кто пишет | Кто читает |
57
+ |---|---|---|
58
+ | `loading` | `MasterTable` из props | `TableBasic` — лоадер поверх таблицы; `PaginationNavigationButton`, `PaginationPageButton`, `PaginationSelect` — блокировка элементов пагинации |
59
+ | `columns` | `TableBasic` в `useEffect`, из своего props `columns` | `ColumnSettings` — список колонок для настройки видимости |
60
+ | `setColumns` | — | `TableBasic` |
61
+
62
+ Из этого следует неочевидное: `ColumnSettings` показывает колонки, **только если** внутри того же `MasterTable` отрисован `TableBasic` — до его первого эффекта контекст пуст.
63
+
64
+ ---
65
+
66
+ ## Дизайн-токены
67
+
68
+ Собственных токенов нет. `MasterTable.module.less` задаёт только `position: relative` — контекст позиционирования для лоадера, который `TableBasic` кладёт поверх таблицы. Визуал целиком приходит от вложенных частей (`TableBasic`, `TableFooter`, `Pagination`, `Typography`).
69
+
70
+ ---
71
+
72
+ ## Инварианты
73
+
74
+ - `forwardRef` на компоненте — не убирать. Ref идёт на корневой `<div>` (`HTMLDivElement`).
75
+ - Все семь статических субкомпонентов (`NoColumns`, `FilterPanel`, `ChipPanel`, `TableBasic`, `TableBasicSettings`, `TableFooter`, `PaginationPanel`) — часть публичного API. Ни переименовывать, ни удалять, ни менять их набор.
76
+ - `displayName` — `"MasterTable"`.
77
+ - Корневой элемент — `<div>`. `data-tx` выставляется **после** спреда `...rest`, поэтому потребитель не может его переопределить — это осознанное поведение, общее для семейства (`FilterPanel`, `PaginationPanel`).
78
+ - Все обёртки семейства (`FilterPanel`, `ChipPanel`, `ChipPanelLinks`, `NoColumns`, `PaginationPanel`, `TableFooter`, `TableBasicSettings`) пробрасывают `ref` на свой корневой элемент — не убирать. У составных (`ChipPanel`, `TableFooter`, `TableBasicSettings`) статики объявлены через `Object.assign` с явным интерфейсом: при добавлении или удалении статики интерфейс нужно править синхронно, иначе TypeScript промолчит.
79
+ - Собственный класс `styles.masterTable` объединяется с пользовательским `className` через `clsx`, а не затирается им.
80
+ - Значение контекста мемоизировано (`useMemo` по `columns` и `loading`) — не откатывать: возврат к новому объекту на каждый рендер снова заставит `useEffect` в `TableBasic` перезапускаться на каждый рендер контейнера.
81
+ - Колонки потребитель обязан заменять иммутабельно (`map`/спред, а не мутация на месте): эффект в `TableBasic` сравнивает `isEqual(columns, context.columns)`, а после первой синхронизации в контексте лежит **та же ссылка**, что пришла в props, — поэтому мутация на месте до `ColumnSettings` не доходит. К мемоизации это отношения не имеет, ограничение существовало и до неё; откат `useMemo` мутации не «починит».
82
+ - Статики перечислены в `IMasterTableStatics` вручную. Явная аннотация типа снимает проверку лишних свойств у `Object.assign`, поэтому набор в интерфейсе и в `Object.assign` правится синхронно — иначе новая статика не попадёт в публичный тип.
83
+ - `IMasterTableProps` объявлен в `src/components/Table/TableBasic/types.ts`, а не рядом с компонентом. Расположение историческое; перенос — breaking change для тех, кто импортирует тип по глубокому пути.
84
+ - Компонент React 17-совместим: никакого `useId`/`useSyncExternalStore` — код синхронизируется в ветку `release-0`.
85
+
86
+ ---
87
+
88
+ ## Accessibility
89
+
90
+ Собственной семантики контейнер не добавляет: это `<div>` без роли, вся доступность живёт в частях (таблица — `<table>` с заголовками, пагинация — `<nav>` со списком, настройки — `aria-haspopup`/`aria-expanded` на ссылке-триггере).
91
+
92
+ Что важно для потребителя:
93
+
94
+ - `loading` — визуальное и функциональное состояние, но не объявляемое: скринридер о начале загрузки не узнает. Если это критично, добавь свой `aria-live`-регион рядом с таблицей.
95
+ - Роль и подпись контейнера (`role`, `aria-label`) не хардкодятся — библиотека мультиязычная, передавай их сам через `...rest`, если экран того требует.
96
+
97
+ ---
98
+
99
+ ## Связанные компоненты
100
+
101
+ - `TableBasic` — сама таблица; единственная часть, которая пишет в контекст (`columns`). Работает и вне `MasterTable`, но тогда `loading` всегда `false`.
102
+ - `Pagination` — обычно кладётся в `MasterTable.PaginationPanel`. Её кнопки и селект читают `MasterTableContext.loading` и блокируются в загрузке; это опора на деталь `MasterTable`, поэтому связь зафиксирована с обеих сторон.
103
+ - `FilterPanel`, `ChipPanel` (`.Links`), `PaginationPanel`, `NoColumns` — тривиальные обёртки: `clsx(className)` + спред + собственный класс, без своей логики. Отличаются только стилями и тем, что `FilterPanel` и `PaginationPanel` выставляют `data-tx`. Все пробрасывают `ref` на свой корневой `<div>`; отдельных строк в `docs/ai/ROADMAP.md` у них нет — состояние учитывается здесь.
104
+ - `TableFooter` — подвал поверх `FooterDescription`; `.Summary` даёт `.SelectedCount`, `.SelectAllButton` и `.Amount`, `.Controls` — это `FooterDescriptionControls` из семейства `Footer`.
105
+ - `TableBasicSettings` — выпадающие настройки поверх `ButtonDropdownExtended`; внутри `ColumnSettings` читает `columns` из контекста и поддерживает children-функцию `({ columns }) => ...`. Его `ref` идёт на корневой `<div>`, который рисует `ButtonDropdownExtended`, — поэтому `forwardRef` пришлось добавить и туда.
106
+
107
+ ---
108
+
109
+ ## Stories
110
+
111
+ Основные истории: `stories/MasterTable/MasterTable.stories.tsx`
112
+ Файлы примеров: `stories/MasterTable/examples/`
113
+
114
+ | Story | Example file | Что демонстрирует |
115
+ |---|---|---|
116
+ | `Playground` | `Playground.tsx` | Интерактивный контроль `loading` и набора панелей (фильтры, подвал, пагинация, пустая таблица) |
117
+ | `Default` | `Default.tsx` | Минимальная композиция: контейнер + `MasterTable.TableBasic` |
118
+ | `Loading` | `Loading.tsx` | `loading` — лоадер поверх заполненной таблицы |
119
+ | `NoData` | `NoData.tsx` | Пустой `data` — таблица показывает `renderNoData` |
120
+ | `NoColumns` | `NoColumns.tsx` | Все колонки с `hidden` — таблица показывает `renderNoColumns` с `MasterTable.NoColumns` |
121
+ | `Example` | `Example.tsx` | Production-like композиция: фильтры, настройки колонок, сортировка, выбор строк, подвал с подытогом, пагинация |
122
+ | `VisualTests` | `VisualTests.tsx` | Скриншот-регрессия: загрузка поверх пустой таблицы, выделенная строка, пагинация, заблокированная загрузкой |
123
+
124
+ ---
125
+
126
+ ## История изменений
127
+
128
+ | Дата | Изменение |
129
+ |---|---|
130
+ | 2026-08-31 | Обёртки семейства (`FilterPanel`, `ChipPanel` + `.Links`, `NoColumns`, `PaginationPanel`, `TableFooter`, `TableBasicSettings`) переведены с `React.FC` на `forwardRef`, у составных статики — через `Object.assign`. Добавлены smoke-тесты на каждую. Ради `TableBasicSettings` `forwardRef` добавлен и в `ButtonDropdownExtended`. Набор props и DOM не изменились. |
131
+ | 2026-08-27 | Создан документ. Компонент переведён с `React.FC` на `Object.assign(forwardRef(...), {...})` — теперь пробрасывает `ref` на корневой `<div>`; значение контекста мемоизировано. Публичный API не изменился. |
132
+
133
+ ---
134
+
135
+ ## Примеры использования
136
+
137
+ ### Default
138
+
139
+ `stories/MasterTable/examples/Default.tsx`
140
+
141
+ ```tsx
142
+ import React from "react";
143
+ import { EHorizontalAlign, ITableBasicColumn, ITableBasicRow, MasterTable } from "@sberbusiness/triplex-next";
144
+
145
+ const columns: ITableBasicColumn[] = [
146
+ { fieldKey: "docNumber", label: "Номер", width: "15%" },
147
+ { fieldKey: "recipient", label: "Получатель", width: "45%" },
148
+ { fieldKey: "status", label: "Статус", width: "20%" },
149
+ { fieldKey: "sum", label: "Сумма", width: "20%", horizontalAlign: EHorizontalAlign.RIGHT },
150
+ ];
151
+
152
+ const data: ITableBasicRow[] = [
153
+ {
154
+ rowKey: "1350",
155
+ rowData: { docNumber: "1350", recipient: "ООО Ромашка", status: "Создан", sum: "12 500,00 ₽" },
156
+ },
157
+ {
158
+ rowKey: "1351",
159
+ rowData: { docNumber: "1351", recipient: "ИП Иванов Иван Иванович", status: "Подписан", sum: "8 300,00 ₽" },
160
+ },
161
+ {
162
+ rowKey: "1352",
163
+ rowData: { docNumber: "1352", recipient: "ООО Василёк", status: "Оплачен", sum: "145 000,00 ₽" },
164
+ },
165
+ ];
166
+
167
+ export const Default = () => (
168
+ <MasterTable>
169
+ <MasterTable.TableBasic columns={columns} data={data} renderNoData={() => <div>Нет данных</div>} />
170
+ </MasterTable>
171
+ );
172
+ ```
173
+
174
+ ### Loading
175
+
176
+ `stories/MasterTable/examples/Loading.tsx`
177
+
178
+ ```tsx
179
+ import React from "react";
180
+ import { EHorizontalAlign, ITableBasicColumn, ITableBasicRow, MasterTable } from "@sberbusiness/triplex-next";
181
+
182
+ const columns: ITableBasicColumn[] = [
183
+ { fieldKey: "docNumber", label: "Номер", width: "15%" },
184
+ { fieldKey: "recipient", label: "Получатель", width: "45%" },
185
+ { fieldKey: "status", label: "Статус", width: "20%" },
186
+ { fieldKey: "sum", label: "Сумма", width: "20%", horizontalAlign: EHorizontalAlign.RIGHT },
187
+ ];
188
+
189
+ const data: ITableBasicRow[] = [
190
+ {
191
+ rowKey: "1350",
192
+ rowData: { docNumber: "1350", recipient: "ООО Ромашка", status: "Создан", sum: "12 500,00 ₽" },
193
+ },
194
+ {
195
+ rowKey: "1351",
196
+ rowData: { docNumber: "1351", recipient: "ИП Иванов Иван Иванович", status: "Подписан", sum: "8 300,00 ₽" },
197
+ },
198
+ {
199
+ rowKey: "1352",
200
+ rowData: { docNumber: "1352", recipient: "ООО Василёк", status: "Оплачен", sum: "145 000,00 ₽" },
201
+ },
202
+ ];
203
+
204
+ /**
205
+ * Свойство loading раздаётся дочерним компонентам через MasterTableContext:
206
+ * таблица закрывается лоадером.
207
+ */
208
+ export const Loading = () => (
209
+ <MasterTable loading>
210
+ <MasterTable.TableBasic columns={columns} data={data} renderNoData={() => <div>Нет данных</div>} />
211
+ </MasterTable>
212
+ );
213
+ ```
214
+
215
+ ### NoData
216
+
217
+ `stories/MasterTable/examples/NoData.tsx`
218
+
219
+ ```tsx
220
+ import React from "react";
221
+ import { EmptytableSysIcon96 } from "@sberbusiness/icons-next";
222
+ import {
223
+ EComponentSize,
224
+ EFontType,
225
+ EFontWeightTitle,
226
+ EHorizontalAlign,
227
+ ETextSize,
228
+ ETitleSize,
229
+ Button,
230
+ EButtonTheme,
231
+ Gap,
232
+ ITableBasicColumn,
233
+ MasterTable,
234
+ Text,
235
+ Title,
236
+ } from "@sberbusiness/triplex-next";
237
+
238
+ const columns: ITableBasicColumn[] = [
239
+ { fieldKey: "docNumber", label: "Номер", width: "15%" },
240
+ { fieldKey: "recipient", label: "Получатель", width: "45%" },
241
+ { fieldKey: "status", label: "Статус", width: "20%" },
242
+ { fieldKey: "sum", label: "Сумма", width: "20%", horizontalAlign: EHorizontalAlign.RIGHT },
243
+ ];
244
+
245
+ /** Заглушка, которую таблица показывает вместо строк, когда data пустой. */
246
+ const renderNoData = () => (
247
+ <>
248
+ <EmptytableSysIcon96 />
249
+ <Gap size={8} />
250
+ <Title size={ETitleSize.H3} weight={EFontWeightTitle.REGULAR}>
251
+ Здесь пока пусто
252
+ </Title>
253
+ <Gap size={12} />
254
+ <Text tag="div" size={ETextSize.B3} type={EFontType.SECONDARY}>
255
+ Создайте первый документ, и он появится в таблице.
256
+ </Text>
257
+ <Gap size={24} />
258
+ <Button theme={EButtonTheme.GENERAL} size={EComponentSize.MD}>
259
+ Создать документ
260
+ </Button>
261
+ </>
262
+ );
263
+
264
+ export const NoData = () => (
265
+ <MasterTable>
266
+ <MasterTable.TableBasic columns={columns} data={[]} renderNoData={renderNoData} />
267
+ </MasterTable>
268
+ );
269
+ ```
270
+
271
+ ### NoColumns
272
+
273
+ `stories/MasterTable/examples/NoColumns.tsx`
274
+
275
+ ```tsx
276
+ import React from "react";
277
+ import {
278
+ EComponentSize,
279
+ EFontType,
280
+ EFontWeightTitle,
281
+ EHorizontalAlign,
282
+ ETextSize,
283
+ ETitleSize,
284
+ Button,
285
+ EButtonTheme,
286
+ Gap,
287
+ ITableBasicColumn,
288
+ ITableBasicRow,
289
+ MasterTable,
290
+ Text,
291
+ Title,
292
+ } from "@sberbusiness/triplex-next";
293
+
294
+ /** Все колонки скрыты — пользователь снял их в настройках таблицы. */
295
+ const columns: ITableBasicColumn[] = [
296
+ { fieldKey: "docNumber", label: "Номер", width: "15%", hidden: true },
297
+ { fieldKey: "recipient", label: "Получатель", width: "45%", hidden: true },
298
+ { fieldKey: "status", label: "Статус", width: "20%", hidden: true },
299
+ { fieldKey: "sum", label: "Сумма", width: "20%", horizontalAlign: EHorizontalAlign.RIGHT, hidden: true },
300
+ ];
301
+
302
+ const data: ITableBasicRow[] = [
303
+ {
304
+ rowKey: "1350",
305
+ rowData: { docNumber: "1350", recipient: "ООО Ромашка", status: "Создан", sum: "12 500,00 ₽" },
306
+ },
307
+ {
308
+ rowKey: "1351",
309
+ rowData: { docNumber: "1351", recipient: "ИП Иванов Иван Иванович", status: "Подписан", sum: "8 300,00 ₽" },
310
+ },
311
+ ];
312
+
313
+ /** Заглушка, которую таблица показывает вместо разметки, когда каждая колонка имеет hidden. */
314
+ const renderNoColumns = () => (
315
+ <MasterTable.NoColumns>
316
+ <Title size={ETitleSize.H3} weight={EFontWeightTitle.REGULAR}>
317
+ Все колонки таблицы скрыты
318
+ </Title>
319
+ <Gap size={12} />
320
+ <Text tag="div" size={ETextSize.B3} type={EFontType.SECONDARY}>
321
+ Выберите нужные вам для отображения колонки в настройках таблицы.
322
+ </Text>
323
+ <Gap size={24} />
324
+ <Button theme={EButtonTheme.GENERAL} size={EComponentSize.MD}>
325
+ Сбросить настройки
326
+ </Button>
327
+ </MasterTable.NoColumns>
328
+ );
329
+
330
+ export const NoColumns = () => (
331
+ <MasterTable>
332
+ <MasterTable.TableBasic
333
+ columns={columns}
334
+ data={data}
335
+ renderNoColumns={renderNoColumns}
336
+ renderNoData={() => <div>Нет данных</div>}
337
+ />
338
+ </MasterTable>
339
+ );
340
+ ```
341
+
342
+ ### Example
343
+
344
+ `stories/MasterTable/examples/Example.tsx`
345
+
346
+ ```tsx
347
+ import React, { useState } from "react";
348
+ import { EmptytableSysIcon96 } from "@sberbusiness/icons-next";
349
+ import {
350
+ Amount,
351
+ Button,
352
+ Checkbox,
353
+ ColumnSettings,
354
+ ECellType,
355
+ EButtonTheme,
356
+ EComponentSize,
357
+ EFontType,
358
+ EFontWeightTitle,
359
+ EHorizontalAlign,
360
+ EOrderDirection,
361
+ ETextSize,
362
+ ETitleSize,
363
+ Gap,
364
+ ISelectFieldOption,
365
+ ISortOrder,
366
+ ITableBasicColumn,
367
+ ITableBasicRow,
368
+ MasterTable,
369
+ Pagination,
370
+ Text,
371
+ TextField,
372
+ Title,
373
+ } from "@sberbusiness/triplex-next";
374
+
375
+ /** Документы таблицы. Данные захардкожены, чтобы пример был воспроизводимым. */
376
+ const DOCUMENTS = [
377
+ { docNumber: "1350", recipient: "ООО Ромашка", status: "Создан", sum: "12500.00" },
378
+ { docNumber: "1351", recipient: "ИП Иванов Иван Иванович", status: "Подписан", sum: "8300.00" },
379
+ { docNumber: "1352", recipient: "ООО Василёк", status: "Оплачен", sum: "145000.00" },
380
+ { docNumber: "1353", recipient: "ООО Ромашка", status: "Создан", sum: "3200.00" },
381
+ ];
382
+
383
+ const PAGE_SIZE_OPTIONS: ISelectFieldOption[] = [
384
+ { id: "2", value: "2", label: "2" },
385
+ { id: "4", value: "4", label: "4" },
386
+ ];
387
+
388
+ const INITIAL_COLUMNS: ITableBasicColumn[] = [
389
+ { fieldKey: "selection", label: "", width: "48px", cellType: ECellType.CHECKBOX },
390
+ { fieldKey: "docNumber", label: "Номер", width: "15%", orderDirection: EOrderDirection.NONE },
391
+ { fieldKey: "recipient", label: "Получатель", width: "40%" },
392
+ { fieldKey: "status", label: "Статус", width: "20%" },
393
+ {
394
+ fieldKey: "sum",
395
+ label: "Сумма",
396
+ width: "20%",
397
+ horizontalAlign: EHorizontalAlign.RIGHT,
398
+ orderDirection: EOrderDirection.NONE,
399
+ renderCell: (value) => <Amount value={String(value)} currency="RUB" />,
400
+ },
401
+ ];
402
+
403
+ const renderNoData = () => (
404
+ <>
405
+ <EmptytableSysIcon96 />
406
+ <Gap size={8} />
407
+ <Title size={ETitleSize.H3} weight={EFontWeightTitle.REGULAR}>
408
+ Здесь пока пусто
409
+ </Title>
410
+ <Gap size={12} />
411
+ <Text tag="div" size={ETextSize.B3} type={EFontType.SECONDARY}>
412
+ Измените параметры фильтрации, чтобы увидеть документы.
413
+ </Text>
414
+ </>
415
+ );
416
+
417
+ export const Example = () => {
418
+ const [columns, setColumns] = useState<ITableBasicColumn[]>(INITIAL_COLUMNS);
419
+ const [selectedKeys, setSelectedKeys] = useState<string[]>(["1351"]);
420
+ const [settingsOpened, setSettingsOpened] = useState(false);
421
+ const [recipientFilter, setRecipientFilter] = useState("");
422
+ const [sortOrder, setSortOrder] = useState<ISortOrder | null>(null);
423
+ const [pageSize, setPageSize] = useState(PAGE_SIZE_OPTIONS[1]);
424
+ const [currentPage, setCurrentPage] = useState(1);
425
+
426
+ const toggleRow = (rowKey: string) =>
427
+ setSelectedKeys((keys) => (keys.includes(rowKey) ? keys.filter((key) => key !== rowKey) : [...keys, rowKey]));
428
+
429
+ const toggleColumn = (fieldKey: string, visible: boolean) =>
430
+ setColumns((current) =>
431
+ current.map((column) => (column.fieldKey === fieldKey ? { ...column, hidden: !visible } : column)),
432
+ );
433
+
434
+ const handleOrderBy = ({ fieldKey, direction }: ISortOrder) => {
435
+ setSortOrder(direction === EOrderDirection.NONE ? null : { fieldKey, direction });
436
+ setCurrentPage(1);
437
+ setColumns((current) =>
438
+ current.map((column) =>
439
+ column.fieldKey === fieldKey
440
+ ? { ...column, orderDirection: direction }
441
+ : { ...column, orderDirection: column.orderDirection ? EOrderDirection.NONE : undefined },
442
+ ),
443
+ );
444
+ };
445
+
446
+ const handlePageSizeChange = (option: ISelectFieldOption) => {
447
+ setPageSize(option);
448
+ setCurrentPage(1);
449
+ };
450
+
451
+ const handleFilterChange = (value: string) => {
452
+ setRecipientFilter(value);
453
+ setCurrentPage(1);
454
+ };
455
+
456
+ // Фильтрация, сортировка и постраничная нарезка — работа потребителя: таблица только отображает то, что ей передали.
457
+ const filtered = DOCUMENTS.filter((document) =>
458
+ document.recipient.toLowerCase().includes(recipientFilter.trim().toLowerCase()),
459
+ );
460
+
461
+ const sorted = sortOrder
462
+ ? [...filtered].sort((left, right) => {
463
+ const comparison = String(left[sortOrder.fieldKey as keyof typeof left]).localeCompare(
464
+ String(right[sortOrder.fieldKey as keyof typeof right]),
465
+ "ru",
466
+ { numeric: true },
467
+ );
468
+
469
+ return sortOrder.direction === EOrderDirection.DESC ? -comparison : comparison;
470
+ })
471
+ : filtered;
472
+
473
+ const selectedSum = DOCUMENTS.filter((document) => selectedKeys.includes(document.docNumber))
474
+ .reduce((total, document) => total + Number(document.sum), 0)
475
+ .toFixed(2);
476
+
477
+ const rowsPerPage = Number(pageSize.value);
478
+ const totalPages = Math.max(1, Math.ceil(sorted.length / rowsPerPage));
479
+ const pageDocuments = sorted.slice((currentPage - 1) * rowsPerPage, currentPage * rowsPerPage);
480
+
481
+ const data: ITableBasicRow[] = pageDocuments.map((document) => ({
482
+ rowKey: document.docNumber,
483
+ selected: selectedKeys.includes(document.docNumber),
484
+ rowData: {
485
+ ...document,
486
+ selection: (
487
+ <Checkbox
488
+ checked={selectedKeys.includes(document.docNumber)}
489
+ onChange={() => toggleRow(document.docNumber)}
490
+ aria-label={`Выбрать документ ${document.docNumber}`}
491
+ />
492
+ ),
493
+ },
494
+ }));
495
+
496
+ return (
497
+ <MasterTable>
498
+ <MasterTable.FilterPanel>
499
+ <TextField
500
+ size={EComponentSize.MD}
501
+ label="Получатель"
502
+ inputProps={{
503
+ value: recipientFilter,
504
+ onChange: (event: React.ChangeEvent<HTMLInputElement>) =>
505
+ handleFilterChange(event.target.value),
506
+ }}
507
+ />
508
+ </MasterTable.FilterPanel>
509
+
510
+ <MasterTable.ChipPanel>
511
+ <MasterTable.ChipPanel.Links>
512
+ <MasterTable.TableBasicSettings
513
+ linkTitle={settingsOpened ? "Скрыть настройки" : "Настройки"}
514
+ opened={settingsOpened}
515
+ setOpened={setSettingsOpened}
516
+ >
517
+ <MasterTable.TableBasicSettings.Header>Показать столбцы</MasterTable.TableBasicSettings.Header>
518
+ <MasterTable.TableBasicSettings.Body>
519
+ <ColumnSettings>
520
+ {({ columns: contextColumns }) =>
521
+ contextColumns
522
+ .filter((column) => column.fieldKey !== "selection")
523
+ .map((column) => (
524
+ <Checkbox
525
+ key={column.fieldKey}
526
+ checked={column.hidden !== true}
527
+ onChange={(event) =>
528
+ toggleColumn(column.fieldKey, event.target.checked)
529
+ }
530
+ >
531
+ {column.label}
532
+ </Checkbox>
533
+ ))
534
+ }
535
+ </ColumnSettings>
536
+ </MasterTable.TableBasicSettings.Body>
537
+ <MasterTable.TableBasicSettings.Footer>
538
+ <Button
539
+ theme={EButtonTheme.LINK}
540
+ size={EComponentSize.MD}
541
+ onClick={() => {
542
+ setColumns(INITIAL_COLUMNS);
543
+ setSortOrder(null);
544
+ }}
545
+ >
546
+ Сбросить настройки
547
+ </Button>
548
+ </MasterTable.TableBasicSettings.Footer>
549
+ </MasterTable.TableBasicSettings>
550
+ </MasterTable.ChipPanel.Links>
551
+ </MasterTable.ChipPanel>
552
+
553
+ <MasterTable.TableBasic
554
+ columns={columns}
555
+ data={data}
556
+ highlightRowOnHover
557
+ onOrderBy={handleOrderBy}
558
+ renderNoData={renderNoData}
559
+ />
560
+
561
+ <MasterTable.TableFooter>
562
+ <MasterTable.TableFooter.Summary>
563
+ <MasterTable.TableFooter.Summary.SelectedCount>
564
+ Выбрано {selectedKeys.length} из {DOCUMENTS.length}
565
+ </MasterTable.TableFooter.Summary.SelectedCount>
566
+ <MasterTable.TableFooter.Summary.SelectAllButton
567
+ onClick={() => setSelectedKeys(DOCUMENTS.map((document) => document.docNumber))}
568
+ >
569
+ Выбрать все
570
+ </MasterTable.TableFooter.Summary.SelectAllButton>
571
+ <MasterTable.TableFooter.Summary.Amount label="Сумма" sum={selectedSum} currency="RUB" />
572
+ </MasterTable.TableFooter.Summary>
573
+ <MasterTable.TableFooter.Controls>
574
+ <Button theme={EButtonTheme.SECONDARY} size={EComponentSize.MD}>
575
+ Скачать
576
+ </Button>
577
+ <Button theme={EButtonTheme.GENERAL} size={EComponentSize.MD}>
578
+ Подписать
579
+ </Button>
580
+ </MasterTable.TableFooter.Controls>
581
+ </MasterTable.TableFooter>
582
+
583
+ <MasterTable.PaginationPanel>
584
+ <Pagination
585
+ paginationSelectProps={{
586
+ paginationLabel: "Показывать по",
587
+ options: PAGE_SIZE_OPTIONS,
588
+ value: pageSize,
589
+ onChange: handlePageSizeChange,
590
+ }}
591
+ paginationNavigationProps={{
592
+ currentPage,
593
+ totalPages,
594
+ onCurrentPageChange: setCurrentPage,
595
+ }}
596
+ />
597
+ </MasterTable.PaginationPanel>
598
+ </MasterTable>
599
+ );
600
+ };
601
+ ```
602
+
@@ -3,8 +3,8 @@ component: ModalWindow
3
3
  category: ModalWindow
4
4
  related: [ModalWindowContent, ModalWindowHeader, ModalWindowBody, ModalWindowFooter, ModalWindowClose, ModalWindowViewManager, Portal, FocusTrap, Page, HeaderPage, FooterPage, Island, LoaderScreen]
5
5
  tokens:
6
- - --triplex-next-ModalWindow-Backdrop_Background
7
- - --triplex-next-ModalWindow-Background
6
+ - ModalWindow.Backdrop_Background
7
+ - ModalWindow.Background
8
8
  stories: stories/ModalWindow/ModalWindow.stories.tsx
9
9
  version: "1.0"
10
10
  ---
@@ -53,9 +53,12 @@ version: "1.0"
53
53
 
54
54
  ## Дизайн-токены
55
55
 
56
+ Переопределяются через `ThemeProvider` (prop `tokens`) — см. `ThemeProvider-ai.md` →
57
+ «Как переопределять токены». Значения по умолчанию — `src/components/DesignTokens/components/ModalWindow.ts`.
58
+
56
59
  ```text
57
- --triplex-next-ModalWindow-Backdrop_Background
58
- --triplex-next-ModalWindow-Background
60
+ ModalWindow.Backdrop_Background
61
+ ModalWindow.Background
59
62
  ```
60
63
 
61
64
  Помимо токенов, компонент инжектит и читает несколько runtime CSS-переменных, не относящихся к дизайн-системе:
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  component: MonthYearField
3
3
  category: Date components
4
- related: [DatePickerExtended, MonthYearRange, TextField, Calendar]
4
+ related: [DatePickerExtended, MonthYearRange, ChipMonthYearPicker, TextField, Calendar]
5
5
  tokens: []
6
6
  stories: stories/MonthYearField/MonthYearField.stories.tsx
7
7
  version: "1.0"
@@ -80,6 +80,7 @@ version: "1.0"
80
80
 
81
81
  - `DatePickerExtended` — родительский компонент выпадающего календаря; `MonthYearField` конфигурирует его в режим `MONTH_YEAR` через render-props.
82
82
  - `MonthYearRange` — выбор диапазона месяцев; использует `MonthYearField` как поля «от» и «до».
83
+ - `ChipMonthYearPicker` — тот же выбор месяца в виде чипса-фильтра: наследует `IMonthYearFieldProps` и утилиты `MonthYearPickerUtils`, но вместо поля формы рендерит `Chip`.
83
84
  - `TextField` — внутреннее поле-триггер.
84
85
  - `MonthYearFieldTarget` (внутренний, не в barrel) — рендерит `TextField` с иконкой календаря и кнопкой очистки, обрабатывает клик/клавиатуру для открытия дропдауна. Описан здесь, отдельного AI.md не имеет.
85
86
  - `MonthYearFieldContext` (внутренний, не в barrel) — прокидывает `onChange` в target; тривиальный контекст без своей логики.
@@ -110,6 +111,7 @@ version: "1.0"
110
111
  |---|---|
111
112
  | 2026-06-16 | Создан документ. AI-рефакторинг (JSDoc-фиксы), unit-тесты для `utils.ts`, story `VisualTests` и включение скриншот-тестов |
112
113
  | 2026-06-16 | Добавлена story `VisualTestsOpen` — скриншот раскрытого dropdown с календарём (через `play`) |
114
+ | 2026-09-07 | В «Связанные компоненты» добавлен `ChipMonthYearPicker` — чипс-вариант того же выбора месяца |
113
115
 
114
116
  ---
115
117