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

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (140) hide show
  1. package/README.md +124 -0
  2. package/bundle/.gitkeep +0 -0
  3. package/bundle/components/AbstractTree.md +198 -0
  4. package/bundle/components/AccordionBase.md +227 -0
  5. package/bundle/components/Alert.md +317 -0
  6. package/bundle/components/Amount.md +225 -0
  7. package/bundle/components/AmountField.md +289 -0
  8. package/bundle/components/Avatar.md +209 -0
  9. package/bundle/components/Badge.md +277 -0
  10. package/bundle/components/Body.md +167 -0
  11. package/bundle/components/Button.md +230 -0
  12. package/bundle/components/ButtonDropdown.md +159 -0
  13. package/bundle/components/ButtonIcon.md +122 -0
  14. package/bundle/components/Calendar.md +445 -0
  15. package/bundle/components/Card.md +374 -0
  16. package/bundle/components/CarouselExtended.md +250 -0
  17. package/bundle/components/Checkbox.md +126 -0
  18. package/bundle/components/CheckboxTree.md +280 -0
  19. package/bundle/components/CheckboxTreeExtended.md +321 -0
  20. package/bundle/components/CheckboxXGroup.md +68 -0
  21. package/bundle/components/CheckboxYGroup.md +67 -0
  22. package/bundle/components/Chip.md +404 -0
  23. package/bundle/components/ChipGroup.md +179 -0
  24. package/bundle/components/Col.md +221 -0
  25. package/bundle/components/CollapsableTree.md +255 -0
  26. package/bundle/components/Confirm.md +343 -0
  27. package/bundle/components/DateField.md +371 -0
  28. package/bundle/components/DatePickerExtended.md +342 -0
  29. package/bundle/components/DateRange.md +275 -0
  30. package/bundle/components/DesignTokens.md +385 -0
  31. package/bundle/components/Divider.md +196 -0
  32. package/bundle/components/DocumentNumberEdit.md +280 -0
  33. package/bundle/components/Dropdown.md +433 -0
  34. package/bundle/components/Ellipsis.md +208 -0
  35. package/bundle/components/EmptyView.md +99 -0
  36. package/bundle/components/ExpandAnimation.md +281 -0
  37. package/bundle/components/Footer.md +339 -0
  38. package/bundle/components/FormField.md +326 -0
  39. package/bundle/components/FormGroup.md +99 -0
  40. package/bundle/components/Gap.md +113 -0
  41. package/bundle/components/Header.md +280 -0
  42. package/bundle/components/HelpBox.md +154 -0
  43. package/bundle/components/IconWrapper.md +145 -0
  44. package/bundle/components/Island.md +197 -0
  45. package/bundle/components/IslandAccordion.md +230 -0
  46. package/bundle/components/IslandWidget.md +233 -0
  47. package/bundle/components/KeyDownListener.md +157 -0
  48. package/bundle/components/LightBox.md +322 -0
  49. package/bundle/components/Link.md +161 -0
  50. package/bundle/components/List.md +247 -0
  51. package/bundle/components/ListMaster.md +204 -0
  52. package/bundle/components/Loader.md +161 -0
  53. package/bundle/components/LoaderScreen.md +134 -0
  54. package/bundle/components/Marker.md +127 -0
  55. package/bundle/components/MarkerStatus.md +148 -0
  56. package/bundle/components/MediaWidth.md +169 -0
  57. package/bundle/components/MobileView.md +120 -0
  58. package/bundle/components/ModalWindow.md +308 -0
  59. package/bundle/components/MonthYearField.md +168 -0
  60. package/bundle/components/MultiselectField.md +213 -0
  61. package/bundle/components/Notification.md +233 -0
  62. package/bundle/components/NumberField.md +178 -0
  63. package/bundle/components/OrderedList.md +169 -0
  64. package/bundle/components/Overlay.md +237 -0
  65. package/bundle/components/Page.md +246 -0
  66. package/bundle/components/Pagination.md +298 -0
  67. package/bundle/components/Portal.md +119 -0
  68. package/bundle/components/Radio.md +174 -0
  69. package/bundle/components/Row.md +125 -0
  70. package/bundle/components/SMSField.md +160 -0
  71. package/bundle/components/SegmentedControl.md +235 -0
  72. package/bundle/components/SelectExtendedField.md +322 -0
  73. package/bundle/components/SelectField.md +213 -0
  74. package/bundle/components/Skeleton.md +129 -0
  75. package/bundle/components/Slider.md +157 -0
  76. package/bundle/components/SliderExtended.md +210 -0
  77. package/bundle/components/SliderRange.md +160 -0
  78. package/bundle/components/SmallInput.md +101 -0
  79. package/bundle/components/Spoiler.md +121 -0
  80. package/bundle/components/StatusTracker.md +151 -0
  81. package/bundle/components/Step.md +121 -0
  82. package/bundle/components/Stepper.md +163 -0
  83. package/bundle/components/Suggest.md +160 -0
  84. package/bundle/components/SuggestField.md +157 -0
  85. package/bundle/components/SwipeableArea.md +144 -0
  86. package/bundle/components/Table.md +199 -0
  87. package/bundle/components/Tabs.md +152 -0
  88. package/bundle/components/TabsExtended.md +273 -0
  89. package/bundle/components/TabsLine.md +189 -0
  90. package/bundle/components/Tag.md +135 -0
  91. package/bundle/components/TagColor.md +121 -0
  92. package/bundle/components/TagGroup.md +100 -0
  93. package/bundle/components/TextField.md +152 -0
  94. package/bundle/components/TextareaField.md +124 -0
  95. package/bundle/components/ThemeProvider.md +181 -0
  96. package/bundle/components/Tooltip.md +254 -0
  97. package/bundle/components/TopOverlay.md +145 -0
  98. package/bundle/components/TreeView.md +224 -0
  99. package/bundle/components/Triggers.md +121 -0
  100. package/bundle/guides/codestyle.md +205 -0
  101. package/bundle/guides/commits.md +166 -0
  102. package/bundle/guides/context.md +277 -0
  103. package/bundle/guides/stories.md +618 -0
  104. package/bundle/guides/template.md +154 -0
  105. package/bundle/guides/tests.md +301 -0
  106. package/bundle/icons.json +4156 -0
  107. package/bundle/manifest.json +796 -0
  108. package/bundle/release-notes/1.1.0.mdx +26 -0
  109. package/bundle/release-notes/1.1.1.mdx +14 -0
  110. package/bundle/release-notes/1.10.0.mdx +43 -0
  111. package/bundle/release-notes/1.11.0.mdx +19 -0
  112. package/bundle/release-notes/1.12.0.mdx +21 -0
  113. package/bundle/release-notes/1.13.0.mdx +43 -0
  114. package/bundle/release-notes/1.14.0.mdx +29 -0
  115. package/bundle/release-notes/1.15.0.mdx +56 -0
  116. package/bundle/release-notes/1.16.0.mdx +55 -0
  117. package/bundle/release-notes/1.17.0.mdx +15 -0
  118. package/bundle/release-notes/1.18.0.mdx +24 -0
  119. package/bundle/release-notes/1.19.0.mdx +48 -0
  120. package/bundle/release-notes/1.19.1.mdx +9 -0
  121. package/bundle/release-notes/1.2.0.mdx +80 -0
  122. package/bundle/release-notes/1.20.0.mdx +48 -0
  123. package/bundle/release-notes/1.21.0.mdx +66 -0
  124. package/bundle/release-notes/1.22.0.mdx +75 -0
  125. package/bundle/release-notes/1.23.0.mdx +50 -0
  126. package/bundle/release-notes/1.24.0.mdx +59 -0
  127. package/bundle/release-notes/1.25.0.mdx +52 -0
  128. package/bundle/release-notes/1.26.0.mdx +11 -0
  129. package/bundle/release-notes/1.3.0.mdx +21 -0
  130. package/bundle/release-notes/1.4.0.mdx +37 -0
  131. package/bundle/release-notes/1.5.0.mdx +24 -0
  132. package/bundle/release-notes/1.6.0.mdx +37 -0
  133. package/bundle/release-notes/1.6.1.mdx +11 -0
  134. package/bundle/release-notes/1.7.0.mdx +29 -0
  135. package/bundle/release-notes/1.8.0.mdx +38 -0
  136. package/bundle/release-notes/1.9.0.mdx +28 -0
  137. package/dist/index.d.ts +2 -0
  138. package/dist/index.js +324 -0
  139. package/dist/index.js.map +1 -0
  140. package/package.json +51 -0
@@ -0,0 +1,152 @@
1
+ ---
2
+ component: Tabs
3
+ category: Navigation
4
+ figma-node: ""
5
+ figma-file: ""
6
+ related: [TabsExtended, TabsLine]
7
+ tokens: []
8
+ stories: stories/Tabs/Tabs.stories.tsx
9
+ version: "1.0"
10
+ ---
11
+
12
+ # Tabs
13
+
14
+ ## Назначение
15
+
16
+ Готовый компонент табов с overflow-меню (`ButtonDropdown` с `DOTS_SECONDARY`):
17
+ табы, не помещающиеся в строку, автоматически уезжают в выпадающее меню.
18
+ Принимает простой массив `tabs: ITabsItem[]` вместо children-композиции.
19
+ Под капотом — `TabsExtended` с фиксированной разметкой
20
+ `Content > TabsWrapper + DropdownWrapper`.
21
+
22
+ Используй когда: нужен стандартный таб-бар с поддержкой переполнения.
23
+ Для кастомной разметки (свой dropdown, иконки, другие заголовки) —
24
+ `TabsExtended` напрямую. Для простых горизонтальных линейных табов без
25
+ overflow — `TabsLine`.
26
+
27
+ ---
28
+
29
+ ## Файловая структура
30
+
31
+ ```text
32
+ src/components/Tabs/
33
+ ├── Tabs.tsx # FC<ITabsProps>; склейка TabsExtended + ButtonDropdown
34
+ ├── styles/
35
+ │ ├── Tabs.module.less
36
+ │ └── Tabs.less
37
+ └── index.ts
38
+ ```
39
+
40
+ ---
41
+
42
+ ## Публичный API
43
+
44
+ ### `ITabsItem`
45
+
46
+ ```ts
47
+ extends Omit<ITabsExtendedTabProps, "children" | "onSelect">
48
+ ```
49
+
50
+ | Поле | Тип | Описание |
51
+ |---|---|---|
52
+ | `id` | `string` | Уникальный id таба (используется в `selectedId`/`onSelectTab`). |
53
+ | `label` | `ReactNode` | Содержимое кнопки таба. |
54
+ | `showNotificationIcon?` | `boolean` | Точка-индикатор уведомления на табе. |
55
+
56
+ ### `ITabsProps`
57
+
58
+ ```ts
59
+ extends ITabsExtendedProps
60
+ ```
61
+
62
+ | Prop | Тип | Default | Описание |
63
+ |---|---|---|---|
64
+ | `tabs` | `ITabsItem[]` | — | Массив табов. **Обязателен.** |
65
+ | `selectedId` | `string` | — | Id выбранного таба (от `ITabsExtendedProps`). |
66
+ | `onSelectTab` | `(selectedId: string) => void` | — | Колбэк выбора таба. |
67
+ | `size?` | `EComponentSize` | `MD` | Размер табов и кнопки overflow. |
68
+ | `buttonDropdownAttributes?` | `ButtonHTMLAttributes<HTMLButtonElement>` | — | HTML-атрибуты для `ButtonDropdown`. |
69
+ | `type?` | `ETabsExtendedType` | `TYPE_1` | Тип оформления (от `TabsExtended`). |
70
+ | `children` | `never` | — | Запрещено — структура фиксирована. |
71
+ | `...HTMLAttributes<HTMLDivElement>` | — | — | На корневой `TabsExtended` (role=tablist). |
72
+
73
+ ---
74
+
75
+ ## Поведение
76
+
77
+ ### Overflow → Dropdown
78
+
79
+ `TabsExtended.Content.DropdownWrapper` получает массив `dropdownItemsIds`
80
+ табов, не поместившихся в строку. `Tabs` фильтрует `tabs` по этим id и
81
+ передаёт в `ButtonDropdown.options`. Выбранный таб подсвечивается через
82
+ `selected={tabs.find(t => t.id === selectedId)}`.
83
+
84
+ ### Keyboard navigation
85
+
86
+ Roving tabindex по `availableToFocusTabId`:
87
+
88
+ - `ArrowLeft` → переключает фокус на предыдущий таб (если не первый в строке —
89
+ `isFirstInlineTab`), `preventDefault` для подавления скролла.
90
+ - `ArrowRight` → следующий таб (если не последний в строке — `isLastInlineTab`).
91
+ - При `onFocus` запоминаются id `prevAvailableToFocusTabId` /
92
+ `nextAvailableToFocusTabId` — на их кнопки навешиваются `prevTabRef` /
93
+ `nextTabRef` для программного `.focus()`.
94
+
95
+ `isFirstInlineTab`/`isLastInlineTab` приходят через render-prop
96
+ `<TabsExtended.Content.Tab>{({ selected, isFirstInlineTab, isLastInlineTab }) => ...}</TabsExtended.Content.Tab>`.
97
+
98
+ ### Defaults
99
+
100
+ - `size = EComponentSize.MD`
101
+ - `availableToFocusTabId` инициализируется как `selectedId || tabs[0].id` —
102
+ при пустом `tabs` упадёт (`tabs[0].id`); потребитель должен передавать
103
+ непустой массив.
104
+
105
+ ---
106
+
107
+ ## Инварианты
108
+
109
+ - **`children: never`** — структура зашита (`TabsWrapper` + `DropdownWrapper`).
110
+ Для своей разметки — `TabsExtended`.
111
+ - **`tabs` непустой** — `tabs[0].id` читается без guard'а.
112
+ - **`onSelectTab` обязателен** — таб не сам мутирует `selectedId`, контроллер на потребителе.
113
+ - **`selectedId` управляемый** — `TabsExtended` гарантирует, что повторный выбор того же id не триггерит `onSelectTab` (`if (selectedId !== id)`).
114
+ - **Overflow-кнопка — `ButtonDropdown` с `EButtonDotsTheme.DOTS_SECONDARY`** — менять только осознанно (UX-канон).
115
+ - **Размер кнопки overflow привязан к `size`** через `clsx(styles.tabButtonDropdown, styles[size])`.
116
+ - **`FC, не forwardRef`** — ref не пробрасывается. Если нужен ref на `tablist` — использовать `TabsExtended` напрямую.
117
+
118
+ ---
119
+
120
+ ## Accessibility
121
+
122
+ - Корневой `<div role="tablist">` приходит из `TabsExtended`.
123
+ - На каждой кнопке таба (`TabsExtended.Content.TabButton`) — `role="tab"` + `aria-selected`.
124
+ - Roving tabindex (`tabIndex = availableToFocusTabId === item.id ? 0 : -1`) —
125
+ только один таб в tab-стопе клавиатуры.
126
+ - ArrowLeft/ArrowRight перемещают фокус **только в пределах inline-табов**
127
+ (не в overflow-меню) — для overflow используется родной dropdown.
128
+ - `showNotificationIcon` — визуальный индикатор; для screen-reader потребитель
129
+ должен добавить текстовый намёк в `label` сам.
130
+
131
+ ---
132
+
133
+ ## Связанные компоненты
134
+
135
+ - `TabsExtended` — низкоуровневый базовый компонент со слотами.
136
+ - `TabsLine` — горизонтальные табы без overflow-меню.
137
+ - `ButtonDropdown` (`EButtonDotsTheme.DOTS_SECONDARY`) — кнопка overflow.
138
+
139
+ ---
140
+
141
+ ## Stories
142
+
143
+ Основные истории: `stories/Tabs/Tabs.stories.tsx`
144
+ Примеры: `stories/Tabs/examples/`
145
+
146
+ ---
147
+
148
+ ## История изменений
149
+
150
+ | Дата | Изменение |
151
+ |---|---|
152
+ | 2026-04-18 | Создан документ |
@@ -0,0 +1,273 @@
1
+ ---
2
+ component: TabsExtended
3
+ category: Navigation (base)
4
+ figma-node: ""
5
+ figma-file: ""
6
+ related: [Tabs, TabsLine]
7
+ tokens: []
8
+ stories: ""
9
+ version: "1.0"
10
+ ---
11
+
12
+ # TabsExtended
13
+
14
+ ## Назначение
15
+
16
+ Низкоуровневая основа табов: рендерит `<div role="tablist">` с контекстом
17
+ для слотов (`Content`, `TabsWrapper`, `Tab`, `TabButton`, `DropdownWrapper`).
18
+ Сам не задаёт компоновку — потребитель выбирает структуру вручную.
19
+ `TabsWrapper` автоматически вычисляет, какие табы не помещаются в строку,
20
+ и передаёт их id в `DropdownWrapper` для overflow-меню.
21
+
22
+ Используй когда: нужны табы с кастомной разметкой или своим dropdown'ом.
23
+ Для готового решения с `ButtonDropdown` overflow — `Tabs`. Для линейных
24
+ табов без overflow — `TabsLine`.
25
+
26
+ ---
27
+
28
+ ## Файловая структура
29
+
30
+ ```text
31
+ src/components/TabsExtended/
32
+ ├── TabsExtended.tsx # FC + Composition; Provider
33
+ ├── TabsExtendedContext.ts # state overflow-распределения
34
+ ├── enums.ts # ETabsExtendedType
35
+ ├── types.ts # TTabsExtendedOnSelectTab
36
+ ├── utils.ts # SIZE→TEXT, TYPE→className maps
37
+ ├── components/
38
+ │ ├── TabsExtendedContent.tsx # FC + Composition
39
+ │ ├── TabsExtendedTabsWrapper.tsx # measurement + overflow
40
+ │ ├── TabsExtendedTab.tsx # forwardRef<HTMLSpanElement>
41
+ │ ├── TabsExtendedTabButton.tsx # forwardRef<HTMLButtonElement>
42
+ │ ├── TabsExtendedDropdownWrapper.tsx # render-prop slot
43
+ │ └── TabsExtendedTabContext.ts # { isFakeTab }
44
+ ├── styles/
45
+ └── index.ts
46
+ ```
47
+
48
+ ---
49
+
50
+ ## Композиция
51
+
52
+ ```tsx
53
+ <TabsExtended selectedId={id} onSelectTab={setId} type={ETabsExtendedType.TYPE_1}>
54
+ <TabsExtended.Content size={EComponentSize.MD}>
55
+ <TabsExtended.Content.TabsWrapper>
56
+ {tabs.map(t => (
57
+ <TabsExtended.Content.Tab key={t.id} id={t.id}>
58
+ {({ selected, isFirstInlineTab, isLastInlineTab }) => (
59
+ <TabsExtended.Content.TabButton selected={selected}>
60
+ {t.label}
61
+ </TabsExtended.Content.TabButton>
62
+ )}
63
+ </TabsExtended.Content.Tab>
64
+ ))}
65
+ </TabsExtended.Content.TabsWrapper>
66
+
67
+ <TabsExtended.Content.DropdownWrapper>
68
+ {({ dropdownItemsIds, onSelectTab }) => (/* свой dropdown */)}
69
+ </TabsExtended.Content.DropdownWrapper>
70
+ </TabsExtended.Content>
71
+ </TabsExtended>
72
+ ```
73
+
74
+ Все компоненты экспортируются также напрямую: `TabsExtendedContent`,
75
+ `TabsExtendedTabsWrapper`, `TabsExtendedTab`, `TabsExtendedTabButton`,
76
+ `TabsExtendedDropdownWrapper`.
77
+
78
+ ---
79
+
80
+ ## Публичный API
81
+
82
+ ### `ITabsExtendedProps`
83
+
84
+ | Prop | Тип | Default | Описание |
85
+ |---|---|---|---|
86
+ | `selectedId` | `string` | — | Id выбранного таба. |
87
+ | `onSelectTab` | `(id: string) => void` | — | Колбэк выбора. Не вызывается, если `id === selectedId`. |
88
+ | `type?` | `ETabsExtendedType` | `TYPE_1` | Стиль таба. |
89
+ | `...HTMLAttributes<HTMLDivElement>` | — | — | На корневой div (role=tablist). |
90
+
91
+ ### `ITabsExtendedContentProps`
92
+
93
+ | Prop | Тип | Default | Описание |
94
+ |---|---|---|---|
95
+ | `size?` | `EComponentSize` | `MD` | Размер; маппится на CSS-класс через `createSizeToClassNameMap`. |
96
+ | `...HTMLAttributes<HTMLDivElement>` | — | — | На контейнер. |
97
+
98
+ ### `ITabsExtendedTabProps`
99
+
100
+ ```ts
101
+ extends Omit<HTMLAttributes<HTMLSpanElement>, "children">
102
+ {
103
+ id: string;
104
+ children: (props: ITabsExtendedItemProvideProps) => ReactNode;
105
+ }
106
+ ```
107
+
108
+ `ITabsExtendedItemProvideProps`:
109
+
110
+ | Поле | Тип | Описание |
111
+ |---|---|---|
112
+ | `selected` | `boolean` | `selectedId === id`. |
113
+ | `isFirstInlineTab` | `boolean` | Первый в `inlineItemsIds`. |
114
+ | `isLastInlineTab` | `boolean` | Последний в `inlineItemsIds`. |
115
+
116
+ ### `ITabsExtendedButtonProps`
117
+
118
+ ```ts
119
+ extends ButtonHTMLAttributes<HTMLButtonElement>
120
+ ```
121
+
122
+ | Prop | Тип | Default | Описание |
123
+ |---|---|---|---|
124
+ | `selected?` | `boolean` | — | Подсветка + `aria-selected`. |
125
+ | `size?` | `EComponentSize` | `MD` | Размер. |
126
+ | `showNotificationIcon?` | `boolean` | — | Рендерит `Badge.Dot`. |
127
+
128
+ Контент рендерится дважды (`Text` default + hover-вариант) для CSS-эффектов.
129
+
130
+ ### `ITabsExtendedTabsWrapperProps`
131
+
132
+ ```ts
133
+ extends HTMLAttributes<HTMLDivElement>
134
+ ```
135
+
136
+ Без публичных полей — только htmlDivAttributes.
137
+
138
+ ### `ITabsExtendedDropdownWrapperProps`
139
+
140
+ ```ts
141
+ extends Omit<HTMLAttributes<HTMLDivElement>, "children">
142
+ {
143
+ children: (props: ITabsExtendedDropdownWrapperProvideProps) => ReactNode;
144
+ }
145
+ ```
146
+
147
+ `ITabsExtendedDropdownWrapperProvideProps`:
148
+
149
+ | Поле | Тип | Описание |
150
+ |---|---|---|
151
+ | `dropdownItemsIds` | `string[]` | Id табов, не поместившихся inline. |
152
+ | `onSelectTab` | `(id: string) => void` | Колбэк выбора (тот же, что в Context). |
153
+
154
+ ### Enums
155
+
156
+ - `ETabsExtendedType`: `TYPE_1` | `TYPE_2`
157
+
158
+ ---
159
+
160
+ ## Контекст
161
+
162
+ ### `TabsExtendedContext`
163
+
164
+ ```ts
165
+ {
166
+ dropdownItemsIds: string[]; // id табов в overflow
167
+ inlineItemsIds: string[]; // id видимых inline
168
+ setDropdownItemsIds(ids): void;
169
+ setInlineItemsIds(ids): void;
170
+ selectedId: string;
171
+ onSelectTab(id): void; // wrapper, не вызывает onSelect повторно
172
+ type: ETabsExtendedType;
173
+ dropdownRef: RefObject<HTMLDivElement>; // на DropdownWrapper, для замера ширины
174
+ }
175
+ ```
176
+
177
+ ### `TabsExtendedTabContext`
178
+
179
+ ```ts
180
+ { isFakeTab: boolean }
181
+ ```
182
+
183
+ Включается только внутри fake-копии табов (см. ниже).
184
+
185
+ ---
186
+
187
+ ## Overflow-механика (`TabsExtendedTabsWrapper`)
188
+
189
+ Рендерит **две копии** табов:
190
+
191
+ 1. **`tabsFake`** (скрытый) — внутри `TabsExtendedTabContext.Provider value={isFakeTab: true}`. Табы рендерятся без `id` и без `onClick` (см. `TabsExtendedTab`), `data-` атрибуты у детей удаляются (`stripDataAttributes`). Используется для замера фактической ширины каждого таба.
192
+ 2. **`tabsReal`** (видимый) — настоящие табы.
193
+
194
+ `checkVisibleItems` (вызывается в `useLayoutEffect` + по `useResizeDetector` throttle 150ms):
195
+ - Идёт по детям `tabsFake`, читает `getBoundingClientRect`.
196
+ - Если `right > containerRight - 4 - dropdownWidth` → таб уходит в `dropdownIds`.
197
+ - `containerRight - 4` — учёт padding (2px) + gap (2px).
198
+ - Если **хотя бы один** таб реально вылез за `containerRight` (`overflow = true`),
199
+ выставляется `setDropdownItemsIds(dropdownIds)`. Иначе — `[]` (всё помещается).
200
+ - Inline-табы — те, у которых `data-tab-item-id` есть и right укладывается.
201
+
202
+ Размер dropdown отслеживается через `dropdownRef.getBoundingClientRect()` в
203
+ `useLayoutEffect` каждый рендер; при изменении — повторный замер.
204
+
205
+ ---
206
+
207
+ ## `TabsExtendedTab` поведение
208
+
209
+ - `<span role="presentation" data-tab-item-id={id}>` — оборачивает render-prop children.
210
+ - `id` атрибут НЕ ставится, если `isFakeTab` или таб в `dropdownItemsIds` (избегаем дублирования id в DOM).
211
+ - `onClick` блокируется в fake-режиме.
212
+ - Видимость: при попадании в overflow — добавляется `styles.hidden` (display:none), но таб остаётся в DOM (для пересчёта inline-кадра).
213
+
214
+ ---
215
+
216
+ ## `TabsExtendedTabButton` поведение
217
+
218
+ - `<button type="button" role="tab" aria-selected={selected}>`.
219
+ - Внутри — два `<Text>` (default + hover) для CSS-перехода стилей.
220
+ - `Badge.Dot` — при `showNotificationIcon`.
221
+ - Размер текста: SIZE→`ETextSize` через `TABS_EXTENDED_SIZE_TO_TEXT_SIZE_MAP` (LG=B2, MD=B3, SM=B4).
222
+
223
+ ---
224
+
225
+ ## Инварианты
226
+
227
+ - **`TabsExtended` — `FC` + composition** (`TabsExtended.Content = ...`).
228
+ - **`TabsExtended.Content` — `FC` + composition** (`TabsWrapper`/`Tab`/`TabButton`/`DropdownWrapper`).
229
+ - **`TabsExtendedTab` и `TabsExtendedTabButton` — `forwardRef`** (Span/Button). Не убирать.
230
+ - **`Tab.children` — render-prop**: `(state) => ReactNode`. Не accept'ить плоские дети.
231
+ - **`DropdownWrapper.children` — render-prop**: получает `dropdownItemsIds` + `onSelectTab`.
232
+ - **`TabsWrapper` рендерит две копии children** (fake + real). Любой таб должен корректно работать как fake (без `id`, без onClick).
233
+ - **`stripDataAttributes` чистит только `data-*` пропсы у прямых children** (на 1 уровень). Глубже — потребитель ответственен.
234
+ - **`onSelectTab` в Context — wrapper**, который НЕ вызывает props.onSelectTab при `id === selectedId`. Дедупликация выбора.
235
+ - **`dropdownRef` — на `DropdownWrapper`** (не на самой кнопке dropdown'а потребителя). Используется для замера зарезервированной ширины.
236
+ - **`role="tablist"` / `role="tab"` / `aria-selected`** — обязательная семантика; не переопределять без причины.
237
+ - **`ETabsExtendedType` экспортируется** — часть публичного API.
238
+
239
+ ---
240
+
241
+ ## Accessibility
242
+
243
+ - `<div role="tablist">` на корне.
244
+ - `<button role="tab" aria-selected>` на каждой кнопке.
245
+ - Управление фокусом (roving tabindex, ArrowLeft/Right) — НЕ встроено;
246
+ потребитель реализует сам (см. `Tabs`).
247
+ - `<TabsExtendedTab>` — `role="presentation"` (это обёртка, не tab сам по себе).
248
+ - Таб в overflow не имеет id и убран из таб-ордера через `display:none` —
249
+ попадает в кастомный dropdown потребителя (там ARIA — на потребителе).
250
+
251
+ ---
252
+
253
+ ## Связанные компоненты
254
+
255
+ - `Tabs` — готовый таб-бар поверх TabsExtended с `ButtonDropdown` overflow.
256
+ - `TabsLine` — горизонтальные линейные табы (без overflow, своя реализация).
257
+ - `Badge.Dot` — индикатор в `TabButton`.
258
+ - `Typography.Text` — текст лейбла.
259
+
260
+ ---
261
+
262
+ ## Stories
263
+
264
+ Отдельных stories нет — демонстрируется через `Tabs` и собственные примеры
265
+ в `src/components/TabsExtended/components/index.ts` (`Type1Example`, `Type2Example`, `PlaygroundExample`).
266
+
267
+ ---
268
+
269
+ ## История изменений
270
+
271
+ | Дата | Изменение |
272
+ |---|---|
273
+ | 2026-04-18 | Создан документ |
@@ -0,0 +1,189 @@
1
+ ---
2
+ component: TabsLine
3
+ category: Navigation
4
+ figma-node: ""
5
+ figma-file: ""
6
+ related: [Tabs, TabsExtended, MobileView, Dropdown]
7
+ tokens: []
8
+ stories: stories/TabsLine/TabsLine.stories.tsx
9
+ version: "1.0"
10
+ ---
11
+
12
+ # TabsLine
13
+
14
+ ## Назначение
15
+
16
+ Линейные табы — кнопки, разделённые тонкой линией снизу, без текстового
17
+ содержимого внутри. Отдельная реализация (НЕ поверх `TabsExtended`).
18
+ Адаптивная: desktop рендерит `TabsLineDesktop` с overflow в `Dropdown`,
19
+ mobile — горизонтальный скролл (`TabsLineMobile`). Переключение через
20
+ `MobileView` по media query.
21
+
22
+ Используй когда: нужны "линейные" табы Sber-стиля с поддержкой свыше
23
+ нескольких табов и автосворачиванием в dropdown. Для табов с кастомным
24
+ overflow — `Tabs` (общий стиль) или `TabsExtended` (низкоуровневый).
25
+
26
+ ---
27
+
28
+ ## Файловая структура
29
+
30
+ ```text
31
+ src/components/TabsLine/
32
+ ├── TabsLine.tsx # FC, MobileView switcher + paddingX/withSeparator
33
+ ├── types.ts # ITabsLineBaseProps
34
+ ├── utils.ts # SIZE→ETextSize map
35
+ ├── components/
36
+ │ ├── TabsLineDesktop.tsx # FC, расчёт inline/dropdown по maxVisible
37
+ │ ├── TabsLineMobile.tsx # FC, горизонтальный скролл
38
+ │ ├── TabsLineItem.tsx # forwardRef<HTMLButtonElement>
39
+ │ └── TabsLineDropdown.tsx # Dropdown + DropdownList для overflow
40
+ ├── styles/
41
+ ├── __tests__/
42
+ └── index.ts
43
+ ```
44
+
45
+ ---
46
+
47
+ ## Публичный API
48
+
49
+ ### `ITabsLineProps`
50
+
51
+ ```ts
52
+ extends ITabsLineDesktopProps
53
+ ```
54
+
55
+ | Prop | Тип | Default | Описание |
56
+ |---|---|---|---|
57
+ | `tabs` | `ITabsLineItemProps[]` | — | Список табов. |
58
+ | `selectedId` | `string` | — | Id выбранного. |
59
+ | `onChangeTab` | `(tabId: string) => void` | — | Колбэк выбора. |
60
+ | `size?` | `EComponentSize` | — | Размер табов (LG/MD/SM). |
61
+ | `maxVisible?` | `number` | — | Максимум inline-табов; остальные → Dropdown (только desktop). |
62
+ | `dropdownTargetHtmlAttributes?` | `HTMLAttributes<HTMLButtonElement> & TestProps` | — | Атрибуты кнопки overflow-dropdown. |
63
+ | `paddingX?` | `0 \| 8 \| 16 \| 24` | — | Горизонтальный отступ от первого/последнего таба. Записывается в `data-paddingx-size`. |
64
+ | `withSeparator?` | `boolean` | — | Нижний бордер контейнера (`styles.withSeparator`). |
65
+ | `...HTMLAttributes<HTMLDivElement>` | — | — | На корневой div (role=tablist). |
66
+
67
+ ### `ITabsLineItemProps`
68
+
69
+ ```ts
70
+ extends HTMLAttributes<HTMLButtonElement>, TestProps
71
+ ```
72
+
73
+ | Поле | Тип | Описание |
74
+ |---|---|---|
75
+ | `id` | `string` | Уникальный id. |
76
+ | `label` | `string` | Текст таба. |
77
+ | `selected?` | `boolean` | Подсветка (выставляется родителем по `selectedId === id`). |
78
+ | `showNotificationIcon?` | `boolean` | Точка-индикатор (`Badge.Dot`, всегда `EComponentSize.MD`). |
79
+ | `size?` | `EComponentSize` | Размер; индивидуально override'ит `size` контейнера. |
80
+
81
+ ### `ITabsLineBaseProps`
82
+
83
+ ```ts
84
+ extends Omit<HTMLAttributes<HTMLDivElement>, "children">
85
+ { tabs; onChangeTab; selectedId; size? }
86
+ ```
87
+
88
+ Общая база Desktop/Mobile вариантов.
89
+
90
+ ---
91
+
92
+ ## Поведение Desktop (`TabsLineDesktop`)
93
+
94
+ ### Распределение inline / dropdown
95
+
96
+ `useEffect` на `[tabs, maxVisible]`:
97
+ - Если `maxVisible` задан И `i + 1 >= maxVisible` И `tabs.length > maxVisible` → `dropdownTabs`. Иначе → `inlineTabs`.
98
+ - Сравнение через `isEqual` (lodash) перед `setState` — избегаем лишних рендеров.
99
+
100
+ `maxVisible` без overflow (когда `tabs.length <= maxVisible`) — все остаются inline.
101
+
102
+ ### Overflow-кнопка (`TabsLineDropdown`)
103
+
104
+ - Рендерится после inline-табов, если `dropdownTabs.length > 0`.
105
+ - `label` — label выбранного таба, если он в overflow; иначе — label первого dropdown-таба.
106
+ - `active` — `true`, если выбранный таб в overflow.
107
+ - Внутри — `Dropdown` + `DropdownList` (а не ButtonDropdown как в `Tabs`).
108
+ - Open/close: ArrowUp/Down открывают; Tab/Esc закрывают; mouseup outside — закрывает.
109
+ - ARIA: `role="tab" aria-haspopup="menu" aria-expanded aria-controls aria-activedescendant`.
110
+
111
+ ### Keyboard navigation
112
+
113
+ Roving tabindex по `focusableTabIndexRef` (ref, не state):
114
+ - `ArrowLeft` / `ArrowRight` (на `event.code`!) — двигают фокус по `inlineTabsRefs.current[index ± 1]`.
115
+ - ArrowRight на последнем inline-табе **не** переходит на dropdown-кнопку (`isLastInlineTab` блокирует).
116
+ - `onFocus` обновляет `focusableTabIndexRef.current = index`; `onBlur` сбрасывает в `0`.
117
+
118
+ ---
119
+
120
+ ## Поведение Mobile (`TabsLineMobile`)
121
+
122
+ - Простая горизонтальная лента (`overflow-x: auto` через CSS).
123
+ - НЕТ overflow-меню — табы прокручиваются.
124
+ - НЕ принимает `maxVisible`, `paddingX`, `withSeparator`, `size` — игнорируются на mobile.
125
+ - Возвращает `null` при пустом `tabs`.
126
+
127
+ ---
128
+
129
+ ## `TabsLineItem` поведение
130
+
131
+ - `<button type="button" role="tab" aria-selected>`.
132
+ - Внутренний state: `focused`, `hovered` — для смены `EFontType` (`PRIMARY` при selected/focused/hovered, иначе `SECONDARY`).
133
+ - `Text` size маппится через `tabsLineSizeToTextSizeMap` (LG=B2, MD=B3, SM=B4).
134
+ - Default `size = MD`.
135
+ - `Badge.Dot` — всегда `EComponentSize.MD` (НЕ зависит от `size` таба!).
136
+
137
+ ---
138
+
139
+ ## Инварианты
140
+
141
+ - **`TabsLine` — `FC`, не `forwardRef`** — ref не пробрасывается.
142
+ - **`TabsLineItem` — `forwardRef<HTMLButtonElement>`** — Desktop хранит refs в `inlineTabsRefs` для управления фокусом.
143
+ - **`onChangeTab` обязателен** — нет внутреннего state выбора, всегда контролируемый.
144
+ - **`paddingX` ограничен 4 значениями** (`0 | 8 | 16 | 24`) — расширять с осторожностью (CSS привязан к `data-paddingx-size`).
145
+ - **`withSeparator`** включает `styles.withSeparator` на wrapper'е.
146
+ - **Mobile рендерит ВСЕ табы inline** (нет maxVisible) — не пытаться добавить overflow без миграции UX.
147
+ - **Keyboard navigation на `event.code`** (не `event.key`) — отличие от `Tabs`. Не унифицировать без проверки тестов.
148
+ - **`isEqual` сравнение перед `setInlineTabs`/`setDropdownTabs`** — не убирать, иначе бесконечный ререндер.
149
+ - **`Badge.Dot size = MD`** на TabsLineItem — намеренно (не наследует `size` таба).
150
+ - **Реализация не использует `TabsExtended`** — отдельная архитектура. Изменения должны быть синхронизированы вручную, если нужна согласованность.
151
+ - **`TabsLineDropdown` использует `Dropdown` + `DropdownList`**, не `ButtonDropdown` (как в `Tabs`) — это разные подсистемы.
152
+
153
+ ---
154
+
155
+ ## Accessibility
156
+
157
+ - Корень: `<div role="tablist">`.
158
+ - Каждый таб: `<button role="tab" aria-selected={selected}>`.
159
+ - Overflow-кнопка: `role="tab" aria-haspopup="menu" aria-expanded aria-controls aria-activedescendant`.
160
+ - Roving tabindex (`tabIndex = focusableTabIndexRef.current === index ? 0 : -1`).
161
+ - ArrowLeft/ArrowRight перемещают фокус. ArrowUp/Down открывают dropdown.
162
+ - Tab / Escape закрывают открытый dropdown.
163
+ - Mobile: естественный таб-ордер всех кнопок (нет roving tabindex).
164
+
165
+ ---
166
+
167
+ ## Связанные компоненты
168
+
169
+ - `Tabs` — табы с overflow через `ButtonDropdown`.
170
+ - `TabsExtended` — низкоуровневая альтернатива (composition slots).
171
+ - `MobileView` — переключение desktop/mobile.
172
+ - `Dropdown` / `DropdownList` — overflow-меню в desktop-варианте.
173
+ - `Badge.Dot` — индикатор уведомлений.
174
+ - `Typography.Text` — текст лейбла.
175
+
176
+ ---
177
+
178
+ ## Stories
179
+
180
+ Основные истории: `stories/TabsLine/TabsLine.stories.tsx`
181
+ Примеры: `stories/TabsLine/examples/`
182
+
183
+ ---
184
+
185
+ ## История изменений
186
+
187
+ | Дата | Изменение |
188
+ |---|---|
189
+ | 2026-04-18 | Создан документ |