@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,169 @@
1
+ ---
2
+ component: OrderedList
3
+ category: Typography
4
+ figma-node: ""
5
+ figma-file: ""
6
+ related: [UnorderedList, Typography, Text]
7
+ tokens: []
8
+ stories: stories/OrderedList/OrderedList.stories.tsx
9
+ version: "1.0"
10
+ ---
11
+
12
+ # OrderedList
13
+
14
+ ## Назначение
15
+
16
+ Семантический нумерованный список — `<ol>` с собственной нумерацией через
17
+ CSS counter (а не нативные list-style-маркеры). Это даёт контроль над
18
+ типографикой нумерации и позиционированием. Элементы — `<li>` с
19
+ типографикой `Text size=B3`. Поддерживает `start` (с какой цифры начать).
20
+
21
+ Используй когда: нужен пронумерованный список с типографикой проекта.
22
+ Для маркированного — `UnorderedList`.
23
+
24
+ ---
25
+
26
+ ## Файловая структура
27
+
28
+ ```text
29
+ src/components/OrderedList/
30
+ ├── OrderedList.tsx # forwardRef<HTMLOListElement> + Object.assign({Item})
31
+ ├── OrderedListItem.tsx # forwardRef<HTMLLIElement>
32
+ ├── types.ts # IOrderedListProps, IOrderedListItemProps
33
+ ├── index.ts # Barrel
34
+ ├── styles/
35
+ │ ├── OrderedList.module.less # counter-reset
36
+ │ └── OrderedListItem.module.less # counter-increment + ::before
37
+ └── __tests__/
38
+ ```
39
+
40
+ ---
41
+
42
+ ## Compound-структура
43
+
44
+ ```tsx
45
+ <OrderedList start={5}>
46
+ <OrderedList.Item>Пятый</OrderedList.Item>
47
+ <OrderedList.Item>Шестой</OrderedList.Item>
48
+ </OrderedList>
49
+ ```
50
+
51
+ `OrderedList.Item` — единственный валидный child.
52
+
53
+ ---
54
+
55
+ ## Варианты и props
56
+
57
+ ### `IOrderedListProps extends React.OlHTMLAttributes<HTMLOListElement>`
58
+
59
+ | Prop | Тип | Default | Описание |
60
+ |---|---|---|---|
61
+ | `start` | `number` | — | С какой цифры начать (как у нативного `<ol>`). Влияет на CSS-counter (`--start-index-tx = start - 1`). |
62
+ | `...OlHTMLAttributes` | — | — | Все атрибуты `<ol>`. |
63
+
64
+ ### `IOrderedListItemProps extends React.LiHTMLAttributes<HTMLLIElement>`
65
+
66
+ | Prop | Тип | Описание |
67
+ |---|---|---|
68
+ | `...LiHTMLAttributes` | — | Все атрибуты `<li>`. |
69
+
70
+ Внутри: `<Text tag="li" size=B3>` — типографика B3 фиксирована.
71
+
72
+ ---
73
+
74
+ ## Ключевые особенности реализации
75
+
76
+ ### Нумерация — через CSS counter, не нативный list-style
77
+
78
+ ```less
79
+ .orderedList {
80
+ --start-index-tx: 0;
81
+ list-style: none;
82
+ counter-reset: list-item-tx var(--start-index-tx);
83
+ }
84
+ .orderedListItem {
85
+ counter-increment: list-item-tx;
86
+ &::before { content: counter(list-item-tx) "."; ... }
87
+ }
88
+ ```
89
+
90
+ - **`list-style: none`** + `::before` с `counter()` — позволяет полностью контролировать визуал нумерации (font, цвет, позиционирование).
91
+ - **`+ .orderedListItem { margin-top: 8px }`** — gap между соседними items.
92
+ - **`position: absolute; left: 0`** на `::before` — нумерация ровняется по левому краю; `padding-left: 25px` на `<ol>` оставляет место.
93
+
94
+ ### `start` через CSS-переменную
95
+
96
+ ```tsx
97
+ style={start !== undefined ? { ...style, "--start-index-tx": start - 1 } : style}
98
+ ```
99
+
100
+ - React inline-style с CSS-переменной (`as keyof React.CSSProperties` cast — TS-хак).
101
+ - `start - 1` потому что `counter-increment` срабатывает **до** рендера `::before`, поэтому нужно начать на 1 меньше.
102
+ - Также пробрасывается **нативный `start` атрибут** на `<ol>` — для семантики (скринридеры, copy-paste).
103
+
104
+ ### `OrderedListItem` — типография зашита
105
+
106
+ ```tsx
107
+ <Text tag="li" size={ETextSize.B3}>
108
+ ```
109
+
110
+ - Размер **`B3`** — изменение требует копирования компонента или создания новой `size`-prop'ы.
111
+
112
+ ### `Object.assign` — `OrderedList.Item`
113
+
114
+ Стандартный pattern для compound.
115
+
116
+ ### `data-tx` — версия пакета
117
+
118
+ На обоих корнях (`<ol>` и `<li>`).
119
+
120
+ ---
121
+
122
+ ## Инварианты
123
+
124
+ - **`<ol>` с `list-style: none`** — нумерация рисуется через CSS-counter (`::before` с `counter()`).
125
+ - **`OrderedListItem` — `<li>` через `<Text tag="li" size=B3>`** — типография B3 не настраивается.
126
+ - **`start - 1`** в CSS-переменной (counter-increment-механика). При смене на `counter-reset` без `-1` — начнётся со start+1.
127
+ - **Gap = 8px** между items (`+ .orderedListItem`).
128
+ - **`start` дублируется** в DOM (`<ol start>`) и CSS-переменной — оба источника правды (DOM — для семантики, CSS — для рендера).
129
+ - **ForwardRef** на обоих компонентах.
130
+ - **Compound — `OrderedList.Item`** через `Object.assign`.
131
+ - **`--start-index-tx`** — публичное имя CSS-переменной; не переименовывать.
132
+
133
+ ---
134
+
135
+ ## Accessibility
136
+
137
+ - `<ol>` + `<li>` — нативная семантика списка → скринридеры читают «список из N элементов, элемент 1 из N».
138
+ - `start` пробрасывается в DOM — скринридер читает корректные номера.
139
+ - `list-style: none` отключает **нативные маркеры**, но семантика `<ol>` сохраняется (Safari/VoiceOver на старых версиях иногда теряют семантику с `list-style: none` — известный edge case).
140
+ - `::before` content `"1."` — может быть прочитан в зависимости от reader (не на всех — спецификация неоднозначна).
141
+
142
+ ---
143
+
144
+ ## Дизайн-токены
145
+
146
+ Нет собственных. Типография — `B3` от `Text`. Размер padding-left = 25px, gap = 8px — захардкожены в LESS (не переменные).
147
+
148
+ ---
149
+
150
+ ## Связанные компоненты
151
+
152
+ - `UnorderedList` — маркированный аналог.
153
+ - `Text` (Typography) — типография элементов.
154
+ - `ETextSize.B3` — размер.
155
+
156
+ ---
157
+
158
+ ## Stories
159
+
160
+ `stories/OrderedList/OrderedList.stories.tsx`
161
+ Примеры: `stories/OrderedList/examples/`
162
+
163
+ ---
164
+
165
+ ## История изменений
166
+
167
+ | Дата | Изменение |
168
+ |---|---|
169
+ | 2026-04-18 | Создан документ |
@@ -0,0 +1,237 @@
1
+ ---
2
+ component: Overlay
3
+ category: Overlays
4
+ figma-node: ""
5
+ figma-file: ""
6
+ related: [SideOverlay, ModalWindow, LightBox]
7
+ tokens: [triplex-next-Overlay-Panel_Background]
8
+ stories: stories/Overlay/Overlay.stories.tsx
9
+ version: "1.0"
10
+ ---
11
+
12
+ # Overlay
13
+
14
+ ## Назначение
15
+
16
+ Низкоуровневый строительный блок для всплывающих панелей с CSS-анимацией
17
+ выезда из четырёх направлений (top/right/bottom/left). Сам компонент —
18
+ **stage manager** жизненного цикла (opening → opened → closing → closed)
19
+ и контейнер с position absolute/fixed; визуальная панель и затемнение —
20
+ `Overlay.Panel` и `Overlay.Mask`, рендерятся через render-prop'ы.
21
+
22
+ Используй когда: строишь свой нестандартный overlay (drawer, side-sheet)
23
+ и нужны хуки на фазах жизненного цикла. Для готовых решений —
24
+ `SideOverlay` (на основе Overlay), `ModalWindow`, `LightBox`.
25
+
26
+ ---
27
+
28
+ ## Файловая структура
29
+
30
+ ```text
31
+ src/components/Overlay/
32
+ ├── Overlay.tsx # FC + slots; контейнер-обёртка, локальный closing для css-класса
33
+ ├── OverlayBase.tsx # FC; render-prop, lifecycle (opening/closing), enum direction
34
+ ├── OverlayMask.tsx # forwardRef; полупрозрачный фон
35
+ ├── OverlayPanel.tsx # forwardRef; выезжающая панель + onTransitionEnd
36
+ ├── index.ts # Barrel: Overlay, OverlayBase, OverlayMask, OverlayPanel + enum
37
+ └── styles/Overlay.module.less
38
+ ```
39
+
40
+ ---
41
+
42
+ ## Compound-структура (через render-prop)
43
+
44
+ ```tsx
45
+ <Overlay
46
+ direction={EOverlayDirection.RIGHT}
47
+ opened={opened}
48
+ setOpened={setOpened}
49
+ onOpen={...}
50
+ onClose={...}
51
+ fixed
52
+ >
53
+ {(provided) => (
54
+ <>
55
+ <Overlay.Mask opened={provided.opened} onClick={() => provided.setOpened(false)} />
56
+ <Overlay.Panel {...provided}>
57
+ {/* контент */}
58
+ </Overlay.Panel>
59
+ </>
60
+ )}
61
+ </Overlay>
62
+ ```
63
+
64
+ `Overlay.children` — **функция**, не ReactNode. Получает `IOverlayChildrenProvideProps`.
65
+
66
+ ---
67
+
68
+ ## Варианты и props
69
+
70
+ ### Overlay — `IOverlayProps extends IOverlayBaseProps, Omit<HTMLAttributes<HTMLDivElement>, "children">`
71
+
72
+ | Prop | Тип | Default | Описание |
73
+ |---|---|---|---|
74
+ | `direction` | `EOverlayDirection` | (default `RIGHT` в `OverlayBase`) | Направление выезда. |
75
+ | `opened` | `boolean` | — | Контролируемое состояние открытия. |
76
+ | `setOpened` | `(opened: boolean) => void` | — | Сеттер `opened` (передаётся в render-prop). |
77
+ | `fixed` | `boolean` | `false` | `position: fixed` (на всю страницу) vs `absolute` (поверх родителя). |
78
+ | `onOpening` / `onOpen` / `onClosing` / `onClose` | `() => void` | — | Lifecycle-callback'и (см. ниже). |
79
+ | `children` | `(props) => ReactElement` | — | render-prop. |
80
+ | `...HTMLAttributes` | — | — | На корневой `<div>`. |
81
+
82
+ ### OverlayBase — `IOverlayBaseProps`
83
+
84
+ То же подмножество, что и Overlay (без `fixed` и без HTML-атрибутов). Можно использовать **без** wrapper'а Overlay, если не нужна обёртка-`<div>`.
85
+
86
+ ### `IOverlayChildrenProvideProps`
87
+
88
+ ```ts
89
+ {
90
+ direction: EOverlayDirection;
91
+ opened: boolean;
92
+ setOpened: (opened: boolean) => void;
93
+ closing: boolean;
94
+ opening: boolean;
95
+ setClosing: (closing: boolean) => void;
96
+ setOpening: (opening: boolean) => void;
97
+ }
98
+ ```
99
+
100
+ ### EOverlayDirection
101
+
102
+ `BOTTOM | LEFT | RIGHT | TOP`.
103
+
104
+ ### OverlayMask — `IOverlayMaskProps extends Pick<IOverlayChildrenProvideProps, "opened">, HTMLAttributes<HTMLDivElement>`
105
+
106
+ - `opened: boolean` — управляет CSS-классом `overlayOpened` (тригерит fade-in).
107
+ - `forwardRef<HTMLDivElement>`.
108
+ - Не реагирует на клик сам — потребитель вешает `onClick` для закрытия.
109
+
110
+ ### OverlayPanel — `IOverlayPanelProps extends HTMLAttributes<HTMLDivElement>, IOverlayChildrenProvideProps`
111
+
112
+ - `forwardRef<HTMLDivElement>`.
113
+ - Внутренний `onTransitionEnd` сбрасывает `closing`/`opening` в `false` **только если** `event.target === currentTarget` (защита от вложенных transitions).
114
+ - Контент оборачивается в `<div className={overlayContent}>`.
115
+
116
+ ---
117
+
118
+ ## Ключевые особенности реализации
119
+
120
+ ### Lifecycle: `opening` → `opened` → `closing` → `closed`
121
+
122
+ ```ts
123
+ useLayoutEffect(() => {
124
+ if (prevOpened.current && !opened) { setClosing(true); onClosing?.(); }
125
+ else if (!prevOpened.current && opened) { setOpening(true); onOpening?.(); }
126
+ prevOpened.current = opened;
127
+ }, [opened]);
128
+
129
+ useEffect(() => { closing ? onClosing?.() : onClose?.(); }, [closing]);
130
+ useEffect(() => { opening ? onOpening?.() : onOpen?.(); }, [opening]);
131
+ ```
132
+
133
+ - **`useLayoutEffect`** при смене `opened` — намеренно (комментарий в коде: «иначе в браузере может быть мигание при закрытии»).
134
+ - `closing` / `opening` сбрасываются в `false` через `onTransitionEnd` в `OverlayPanel` → тогда вызывается `onClose` / `onOpen`.
135
+ - **`onClosing` / `onOpening`** вызывается **дважды** в начале (явно из `useLayoutEffect` + из `useEffect` на `closing`/`opening`). Известный нюанс — потребитель должен быть идемпотентен.
136
+
137
+ ### Двойной слой `closing` (Overlay + OverlayBase)
138
+
139
+ - `Overlay` хранит свой `closing` для CSS-класса `styles.closing` (виден до окончания анимации).
140
+ - `OverlayBase` хранит свой `closing` для render-prop'ов.
141
+ - **Причина**: `OverlayBase` без обёртки не имеет доступа к корневому `<div>` — нужно дублировать.
142
+
143
+ ### `fixed` vs `absolute`
144
+
145
+ ```less
146
+ .overlay { position: absolute; ... &.fixed { position: fixed; } }
147
+ ```
148
+
149
+ - По умолчанию — `absolute` (поверх родителя). `fixed` — на весь viewport.
150
+
151
+ ### `left: -10000px; visibility: hidden` для closed-state
152
+
153
+ ```less
154
+ .overlay {
155
+ left: -10000px;
156
+ visibility: hidden;
157
+ &.closing, &.opened { visibility: visible; left: 0; right: 0; }
158
+ }
159
+ ```
160
+
161
+ - Закрытый overlay уезжает за viewport, чтобы:
162
+ - Анимация при закрытии не была обрезана `display: none`.
163
+ - Внутренние элементы не получали focus (через `visibility: hidden`).
164
+
165
+ ### Transform-based анимация в Panel
166
+
167
+ ```less
168
+ .overlayPanel { transition: transform 0.3s ease-in-out; }
169
+ .right { transform: translateX(100%); ... &.opened { transform: translateX(0); } }
170
+ ```
171
+
172
+ - Каждое направление — свой `translate` (100% начальное, 0 открытое).
173
+ - **Длительность 300ms** — захардкожена.
174
+ - **`box-shadow`** — захардкожен (`rgba(0,0,0,0.14)` 13px-blur 17px), не из токенов.
175
+
176
+ ### Mask — отдельный компонент, не часть Panel
177
+
178
+ - Потребитель сам решает, нужна ли маска и как она реагирует на клик. Это даёт гибкость: можно сделать non-modal overlay (без затемнения).
179
+
180
+ ### Render-prop, не Context
181
+
182
+ - API через render-prop, а не Context, чтобы дочерние Mask/Panel могли быть переданы по-разному в каждом случае.
183
+ - Минус: верстать вложенные компоненты сложнее (handler'ы внутри замыкания).
184
+
185
+ ---
186
+
187
+ ## Инварианты
188
+
189
+ - **`children` — render-prop** (`(props) => ReactElement`), не ReactNode. Изменение API — breaking.
190
+ - **`direction` default `RIGHT`** в `OverlayBase`, в `Overlay` — без default'а (пробрасывается как-есть).
191
+ - **`closing`/`opening` сбрасываются через `onTransitionEnd`** на `OverlayPanel` — если потребитель не использует `OverlayPanel` или ломает transition, lifecycle зависнет.
192
+ - **`onClosing`/`onOpening` вызываются дважды** на каждое открытие/закрытие. Делай идемпотентным.
193
+ - **Двойной `closing` state** (Overlay + OverlayBase) — не упрощать без миграции CSS.
194
+ - **`fixed` — `position: fixed`** на корневой `<div>`, иначе `absolute`. Не работает в transformed-родителях (фундаментальный CSS).
195
+ - **300ms transition** — захардкожена, не настраивается.
196
+ - **CSS-переменная `--triplex-next-Overlay-Panel_Background`** — публичный токен.
197
+ - **Mask и Panel — `forwardRef`**, Overlay/OverlayBase — `FC`.
198
+ - **Overlay slots — `Object.assign`-style** (`Overlay.Mask = OverlayMask`) — но через property assignment.
199
+
200
+ ---
201
+
202
+ ## Accessibility
203
+
204
+ - **Нет встроенного focus management / focus trap** — потребитель должен реализовать сам (или использовать SideOverlay/ModalWindow поверх).
205
+ - **Нет ESC-обработчика** — закрытие зависит от `setOpened` через клик по Mask или явный close-button.
206
+ - **Нет `role="dialog"` / `aria-modal`** — overlay семантически нейтрален. Потребитель должен добавить через `...htmlDivAttributes`.
207
+ - `visibility: hidden` в closed-state — корректно убирает focusable-элементы из tab-последовательности.
208
+ - Mask — `<div>`, не интерактивный по умолчанию; клик-обработчик потребитель добавляет сам.
209
+
210
+ ---
211
+
212
+ ## Дизайн-токены
213
+
214
+ - `--triplex-next-Overlay-Panel_Background` — фон панели.
215
+ - `box-shadow` — захардкожен, не из токенов (тех-долг).
216
+
217
+ ---
218
+
219
+ ## Связанные компоненты
220
+
221
+ - `SideOverlay` — высокоуровневая обёртка (Overlay + Mask + ESC + focus + контент).
222
+ - `ModalWindow` — модал на отдельном механизме (Portal + CSSTransition).
223
+ - `LightBox` — full-screen viewer на отдельном механизме.
224
+
225
+ ---
226
+
227
+ ## Stories
228
+
229
+ `stories/Overlay/Overlay.stories.tsx`
230
+
231
+ ---
232
+
233
+ ## История изменений
234
+
235
+ | Дата | Изменение |
236
+ |---|---|
237
+ | 2026-04-18 | Создан документ |
@@ -0,0 +1,246 @@
1
+ ---
2
+ component: Page
3
+ category: Layout
4
+ figma-node: ""
5
+ figma-file: ""
6
+ related: [Header, Body, Footer, Island, LightBox, Spoiler]
7
+ tokens: []
8
+ stories: stories/Page/Page.stories.tsx
9
+ version: "1.0"
10
+ ---
11
+
12
+ # Page
13
+
14
+ ## Назначение
15
+
16
+ Layout-компонент **страницы**: контейнер `<div class="global-page">` +
17
+ три compound-слота `Header`, `Body`, `Footer`, каждый из которых
18
+ существует в **двух типах**:
19
+
20
+ - **`FIRST`** — заворачивает контент в `Island` (карточка с фоном,
21
+ скруглением, padding'ом). Используется для типичной layout'ной
22
+ страницы.
23
+ - **`SECOND`** — оставляет «голый» Header/Body/Footer без `Island` —
24
+ для случаев, когда фон/обёртка управляется внешним контейнером
25
+ (например, `LightBox`).
26
+
27
+ Дополнительно `HeaderPage` / `FooterPage` поддерживают **`sticky`** —
28
+ sticky-позиционирование с **плавным обнулением скруглений углов** при
29
+ прилипании (через `useStickyCornerRadius`).
30
+
31
+ Используй когда: компонуешь страницу из секций. Если нужен только
32
+ header / footer без всей структуры — используй `Header`/`Footer`/`Body`
33
+ напрямую.
34
+
35
+ ---
36
+
37
+ ## Файловая структура
38
+
39
+ ```text
40
+ src/components/Page/
41
+ ├── Page.tsx # forwardRef + Object.assign({Header, Body, Footer})
42
+ ├── components/
43
+ │ ├── HeaderPage.tsx # forwardRef; FIRST=Island+Header, SECOND=Header
44
+ │ ├── BodyPage.tsx # forwardRef; FIRST=Island+Body, SECOND=Body
45
+ │ ├── FooterPage.tsx # forwardRef; FIRST=Island+Footer, SECOND=Footer
46
+ │ ├── enums.ts # EHeaderPageType / EBodyPageType / EFooterPageType / EBodyPageVerticalMargin
47
+ │ └── useStickyCornerRadius.ts # rAF-анимация corner-radius при sticky-прилипании
48
+ ├── styles/Page.module.less + BodyPage.module.less
49
+ ├── index.ts # Barrel: Page + enums
50
+ └── __tests__/
51
+ ```
52
+
53
+ ---
54
+
55
+ ## Compound-структура
56
+
57
+ ```tsx
58
+ <Page>
59
+ <Page.Header type={EHeaderPageType.FIRST} sticky size={EComponentSize.MD}>
60
+ <Page.Header.Title>Заголовок</Page.Header.Title>
61
+ <Page.Header.Subhead>Подзаголовок</Page.Header.Subhead>
62
+ <Page.Header.Tabs>...</Page.Header.Tabs>
63
+ </Page.Header>
64
+ <Page.Body type={EBodyPageType.FIRST} verticalMargin={EBodyPageVerticalMargin.LARGE}>
65
+ Контент
66
+ </Page.Body>
67
+ <Page.Footer type={EFooterPageType.FIRST} sticky>
68
+ <Page.Footer.Description>Подпись</Page.Footer.Description>
69
+ <Button>Действие</Button>
70
+ </Page.Footer>
71
+ </Page>
72
+ ```
73
+
74
+ `HeaderPage.Title/Subhead/Tabs/LayoutSidebar` — **реэкспорты** `Header.*`. То же — `FooterPage.Description = Footer.Description`.
75
+
76
+ ---
77
+
78
+ ## Варианты и props
79
+
80
+ ### Page — `IPageProps extends HTMLAttributes<HTMLDivElement>`
81
+
82
+ | Prop | Тип | Описание |
83
+ |---|---|---|
84
+ | `...HTMLAttributes` | — | На корневой `<div>`. |
85
+ | `children` | `ReactNode` | Только Header / Body / Footer (контракт). |
86
+
87
+ CSS-классы: `styles.page` + **`styles["global-page"]`** (последний — публичный, для глобальных стилей вне модуля).
88
+
89
+ ### HeaderPage — `IHeaderPageTypeFirstProps | IHeaderPageTypeSecondProps`
90
+
91
+ | Prop | FIRST | SECOND | Default | Описание |
92
+ |---|---|---|---|---|
93
+ | `type` | `EHeaderPageType.FIRST` | `EHeaderPageType.SECOND` | — (обязателен) | Discriminator. |
94
+ | `sticky` | `boolean` | `never` | — | Sticky к верху. **Только FIRST** и **только внутри LightBox** (по комментарию). |
95
+ | `size` | `EComponentSize` | `never` | — | Размер `Island`. **Только FIRST**. |
96
+ | `...IHeaderProps` | — | — | — | От `Header`. |
97
+
98
+ - FIRST: `<Island type=TYPE_1 size={size}><Header /></Island>`.
99
+ - SECOND: `<Header className={headerPageTypeSecond} />`.
100
+ - Slots: **`HeaderPage.Title / Subhead / Tabs / LayoutSidebar`** = реэкспорты `Header.*`.
101
+
102
+ ### BodyPage — `IBodyPageTypeFirstProps | IBodyPageTypeSecondProps`
103
+
104
+ | Prop | FIRST | SECOND | Default | Описание |
105
+ |---|---|---|---|---|
106
+ | `type` | `EBodyPageType.FIRST` | `EBodyPageType.SECOND` | — | Discriminator. |
107
+ | `size` | `EComponentSize` | `never` | — | Размер `Island`. Только FIRST. |
108
+ | `verticalMargin` | `EBodyPageVerticalMargin` | (то же) | `LARGE` | `LARGE` = 24px, `SMALL` = 16px. В LightBox — `SMALL`. |
109
+ | `...IBodyProps` | — | — | — | От `Body`. |
110
+
111
+ ### FooterPage — `IFooterPageTypeFirstProps | IFooterPageTypeSecondProps`
112
+
113
+ | Prop | FIRST | SECOND | Default | Описание |
114
+ |---|---|---|---|---|
115
+ | `type` | `EFooterPageType.FIRST` | `EFooterPageType.SECOND` | — | Discriminator. |
116
+ | `sticky` | `boolean` | `never` | — | Sticky к низу. **Только FIRST внутри LightBox** (по комментарию). |
117
+ | `size` | `EComponentSize` | `never` | — | Размер `Island`. Только FIRST. |
118
+ | `...IFooterProps` | — | — | — | От `Footer`. |
119
+
120
+ - Slots: **`FooterPage.Description`** = `Footer.Description`.
121
+
122
+ ### Enums
123
+
124
+ - `EHeaderPageType.FIRST | SECOND`
125
+ - `EBodyPageType.FIRST | SECOND`
126
+ - `EFooterPageType.FIRST | SECOND`
127
+ - `EBodyPageVerticalMargin.LARGE` (24px) | `SMALL` (16px)
128
+
129
+ ---
130
+
131
+ ## Ключевые особенности реализации
132
+
133
+ ### `useStickyCornerRadius(ref, edge, isEnabled)`
134
+
135
+ ```ts
136
+ useEffect(() => {
137
+ // Подписка на scroll/resize/transitionend ближайшего scroll-родителя.
138
+ // ResizeObserver на всех предках до scroller.
139
+ // На каждом frame: вычисляем расстояние до edge → r = clamp(0, 24, dist).
140
+ // Пишем в --r-top / --r-bottom (CSS-var) и dataset.stuck = "true" если r ≤ 0.5.
141
+ }, [ref, edge, isEnabled]);
142
+ ```
143
+
144
+ - **Нативный sticky** + **анимированный corner-radius**:
145
+ - Когда элемент далеко от прилипшей границы → `--r-top: 24px`.
146
+ - При прилипании → `--r-top: 0px` плавно (через rAF).
147
+ - `data-stuck="true"` — для подключения тени через CSS.
148
+ - **`getScrollParent`**: ищет ближайший `overflowY: auto/scroll/overlay`. Default — `window`.
149
+ - **`ResizeObserver`** на ВСЕХ предках до scroller — нужен для случая, когда `Spoiler` раскрывается без scroll-события, но меняет высоту контента.
150
+ - **`transitionend` listener** — обновление при завершении анимации `lightBoxSideOverlayContent` (cross-component contract по className!).
151
+ - **`setTimeout(initialize, 100)`** — задержка для совместимости с открытием LightBox (без задержки анимация не работает).
152
+ - **Жёсткие зависимости**: ref, edge, isEnabled — пересоздание подписки при изменении.
153
+
154
+ ### CSS-переменные `--r-top`, `--r-bottom`
155
+
156
+ - Публичный контракт: устанавливаются JS, читаются в LESS (`border-radius: var(--r-top, 24px) var(--r-top, 24px) 0 0` или подобное).
157
+ - При `isEnabled=false` или unmount — переменная удаляется через `style.removeProperty`.
158
+
159
+ ### Cross-component className-зависимость: `lightBoxSideOverlayContent`
160
+
161
+ ```ts
162
+ event.target.classList.contains("lightBoxSideOverlayContent")
163
+ ```
164
+
165
+ - Hardcoded substring matching на className из `LightBox` package. **При переименовании в LightBox — sticky-corner для page внутри SideOverlay перестанет реагировать на анимацию**. Тех-долг.
166
+
167
+ ### `setIslandRef` — мерж forwardRef + локального ref
168
+
169
+ ```ts
170
+ const setIslandRef = (instance) => {
171
+ islandRef.current = instance;
172
+ if (typeof ref === "function") ref(instance);
173
+ else if (ref) ref.current = instance;
174
+ };
175
+ ```
176
+
177
+ - Стандартный паттерн merge. Используется в HeaderPage и FooterPage.
178
+
179
+ ### Discriminated union по `type`
180
+
181
+ - TypeScript обеспечивает: `sticky` / `size` доступны только при `FIRST`. При `SECOND` — `never`.
182
+ - Discriminator обязателен на потребителе: `<Page.Header type={EHeaderPageType.FIRST} ...>`.
183
+
184
+ ### `Island type=TYPE_1` — фиксирован
185
+
186
+ - При `FIRST` Island всегда `TYPE_1` (нельзя сменить на `TYPE_2`).
187
+
188
+ ### `Page.tsx` — `global-page` className
189
+
190
+ - Кроме CSS-Modules класса `styles.page` добавляется `styles["global-page"]` (это композитный класс, не глобальный селектор `:global` — но имя зарезервированное). Используется для cross-component хуков.
191
+
192
+ ---
193
+
194
+ ## Инварианты
195
+
196
+ - **Page может содержать только Header / Body / Footer** (контракт документации; типы шире).
197
+ - **Discriminated union по `type`** — `EHeaderPageType.FIRST | SECOND` (то же для Body/Footer). Несовместимо с runtime-вычисляемым type без cast'а.
198
+ - **`sticky` — только FIRST**, и **только внутри LightBox** (по комментарию в коде; вне LightBox поведение не специфицировано).
199
+ - **`useStickyCornerRadius`** жёстко завязан на:
200
+ - className `lightBoxSideOverlayContent` (cross-package contract).
201
+ - CSS-переменные `--r-top` / `--r-bottom` (публичный контракт со стилями).
202
+ - **`maxRadius = 24`** — захардкожен в JS.
203
+ - **`Island type=TYPE_1`** при FIRST — фиксирован.
204
+ - **`verticalMargin` default `LARGE` (24px)**, в LightBox — `SMALL` (16px).
205
+ - **HeaderPage slots = `Header.*`** (Title/Subhead/Tabs/LayoutSidebar) — реэкспорты, не свои реализации.
206
+ - **FooterPage slots = `Footer.Description`** — реэкспорт.
207
+ - **ForwardRef** на всех уровнях.
208
+ - **`global-page` className** на Page — публичное имя.
209
+
210
+ ---
211
+
212
+ ## Accessibility
213
+
214
+ - Базовая семантика наследуется от `Header`, `Body`, `Footer` (заголовки h1/h2 — внутри `Header.Title`).
215
+ - `Page` сам — нейтральный `<div>`, не landmark. Если нужен `<main>` — потребитель должен обернуть/передать `role`.
216
+ - Sticky-поведение — клавиатурно нормально (sticky == native CSS), screen readers видят корректный flow.
217
+
218
+ ---
219
+
220
+ ## Дизайн-токены
221
+
222
+ Нет собственных. Цвета/spacing — от `Island`, `Header`, `Body`, `Footer`. CSS-переменные `--r-top` / `--r-bottom` — управляющие, не токены.
223
+
224
+ ---
225
+
226
+ ## Связанные компоненты
227
+
228
+ - `Header`, `Body`, `Footer` — нижележащие компоненты (FIRST оборачивает в Island).
229
+ - `Island` (TYPE_1) — карточка-обёртка для FIRST.
230
+ - `LightBox` — типичный контекст для `type=SECOND` и `sticky`.
231
+ - `Spoiler` — потребитель ResizeObserver (срабатывание sticky-corner при раскрытии).
232
+
233
+ ---
234
+
235
+ ## Stories
236
+
237
+ `stories/Page/Page.stories.tsx`
238
+ Примеры: `stories/Page/examples/`
239
+
240
+ ---
241
+
242
+ ## История изменений
243
+
244
+ | Дата | Изменение |
245
+ |---|---|
246
+ | 2026-04-18 | Создан документ |