@sberbusiness/triplex-next-mcp-server 0.24.0-beta2 → 1.40.0-beta
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +10 -0
- package/bundle/components/AlertContext.md +268 -0
- package/bundle/components/BodyPage.md +186 -0
- package/bundle/components/Button.md +93 -85
- package/bundle/components/ButtonDropdown.md +36 -70
- package/bundle/components/ButtonDropdownExtended.md +422 -0
- package/bundle/components/ButtonIcon.md +41 -48
- package/bundle/components/Caption.md +291 -0
- package/bundle/components/Checkbox.md +43 -122
- package/bundle/components/CheckboxXGroup.md +109 -46
- package/bundle/components/CheckboxYGroup.md +77 -49
- package/bundle/components/CodeText.md +197 -0
- package/bundle/components/Col.md +311 -162
- package/bundle/components/CollapsibleTree.md +584 -0
- package/bundle/components/CollapsibleTreeExtended.md +422 -0
- package/bundle/components/Dropdown.md +748 -346
- package/bundle/components/EmptyView.md +14 -15
- package/bundle/components/FooterPage.md +203 -0
- package/bundle/components/Gap.md +115 -57
- package/bundle/components/HeaderPage.md +226 -0
- package/bundle/components/HelpBox.md +45 -84
- package/bundle/components/ImageGallery.md +423 -0
- package/bundle/components/ImageGalleryExtended.md +606 -0
- package/bundle/components/LightBox.md +2252 -233
- package/bundle/components/List.md +216 -183
- package/bundle/components/ListActionItem.md +157 -0
- package/bundle/components/ListItem.md +276 -0
- package/bundle/components/ListItemControlsButton.md +103 -0
- package/bundle/components/ListItemControlsButtonDropdown.md +107 -0
- package/bundle/components/ListItemSelectable.md +99 -0
- package/bundle/components/ListItemTable.md +390 -0
- package/bundle/components/ListMaster.md +876 -156
- package/bundle/components/ListMasterFooter.md +80 -0
- package/bundle/components/ListMasterHeader.md +86 -0
- package/bundle/components/ListSortable.md +153 -0
- package/bundle/components/ListSortableItem.md +175 -0
- package/bundle/components/ListTableItem.md +391 -0
- package/bundle/components/ModalWindow.md +427 -237
- package/bundle/components/MonthYearField.md +145 -109
- package/bundle/components/Notification.md +625 -155
- package/bundle/components/Overlay.md +271 -170
- package/bundle/components/Page.md +284 -186
- package/bundle/components/Pagination.md +206 -229
- package/bundle/components/Portal.md +131 -67
- package/bundle/components/Row.md +171 -69
- package/bundle/components/Text.md +320 -0
- package/bundle/components/TextField.md +364 -97
- package/bundle/components/Title.md +296 -0
- package/bundle/guides/codestyle.md +38 -11
- package/bundle/guides/commits.md +119 -26
- package/bundle/guides/context.md +34 -30
- package/bundle/guides/stories.md +110 -40
- package/bundle/guides/template.md +3 -7
- package/bundle/guides/tests.md +48 -2
- package/bundle/manifest.json +275 -30
- package/bundle/release-notes/1.26.0.mdx +33 -2
- package/bundle/release-notes/1.27.0.mdx +43 -0
- package/bundle/release-notes/1.28.0.mdx +25 -0
- package/bundle/release-notes/1.29.0.mdx +47 -0
- package/bundle/release-notes/1.30.0.mdx +56 -0
- package/bundle/release-notes/1.31.0.mdx +52 -0
- package/bundle/release-notes/1.32.0.mdx +35 -0
- package/bundle/release-notes/1.33.0.mdx +61 -0
- package/bundle/release-notes/1.34.0.mdx +35 -0
- package/bundle/release-notes/1.35.0.mdx +21 -0
- package/bundle/release-notes/1.35.1.mdx +11 -0
- package/bundle/release-notes/1.36.0.mdx +21 -0
- package/bundle/release-notes/1.37.0.mdx +71 -0
- package/bundle/release-notes/1.38.0.mdx +36 -0
- package/bundle/release-notes/1.39.0.mdx +66 -0
- package/bundle/release-notes/1.40.0.mdx +27 -0
- package/bundle/release-notes/1.41.0.mdx +7 -0
- package/package.json +1 -1
|
@@ -1,237 +1,338 @@
|
|
|
1
1
|
---
|
|
2
2
|
component: Overlay
|
|
3
|
-
category:
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
related: [SideOverlay, ModalWindow, LightBox]
|
|
7
|
-
tokens: [triplex-next-Overlay-Panel_Background]
|
|
3
|
+
category: Overlay
|
|
4
|
+
related: [OverlayBase, OverlayMask, OverlayPanel, LightBox, TopOverlay, ModalWindow, DropdownMobile]
|
|
5
|
+
tokens: [--triplex-next-Overlay-Background, --triplex-next-Overlay-Panel_Background]
|
|
8
6
|
stories: stories/Overlay/Overlay.stories.tsx
|
|
9
|
-
version: "1.0"
|
|
7
|
+
version: "1.34.0"
|
|
10
8
|
---
|
|
11
9
|
|
|
12
10
|
# Overlay
|
|
13
11
|
|
|
14
12
|
## Назначение
|
|
15
13
|
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
`Overlay.Panel` и `Overlay.Mask`, рендерятся через render-prop'ы.
|
|
14
|
+
Затемняющий слой с выезжающей из выбранной стороны панелью контента. Применяется
|
|
15
|
+
для отображения дополнительного контента поверх элемента (`absolute`, по
|
|
16
|
+
умолчанию) или поверх всей страницы (`fixed`). Анимация открытия/закрытия —
|
|
17
|
+
CSS-transition панели (`transform`) и маски (`opacity`).
|
|
21
18
|
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
`
|
|
19
|
+
Компонент построен как семейство: корневой `Overlay` (контейнер + управление
|
|
20
|
+
жизненным циклом через `OverlayBase`) со статическими `Overlay.Mask`
|
|
21
|
+
(кликабельный фон) и `Overlay.Panel` (выезжающая панель). Контент задаётся
|
|
22
|
+
render-функцией `children`, которая получает состояние оверлея.
|
|
25
23
|
|
|
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
|
-
```
|
|
24
|
+
Используй когда: нужна выезжающая боковая/верхняя/нижняя панель (drawer,
|
|
25
|
+
off-canvas) над контейнером или страницей.
|
|
26
|
+
Не используй когда: нужно модальное окно по центру — используй `ModalWindow`;
|
|
27
|
+
нужен полноэкранный слой просмотра — `LightBox`.
|
|
39
28
|
|
|
40
29
|
---
|
|
41
30
|
|
|
42
|
-
##
|
|
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`.
|
|
31
|
+
## Варианты и props
|
|
65
32
|
|
|
66
|
-
|
|
33
|
+
### `Overlay` — обязательные props
|
|
67
34
|
|
|
68
|
-
|
|
35
|
+
| Prop | Тип | Описание |
|
|
36
|
+
|---|---|---|
|
|
37
|
+
| `direction` | `EOverlayDirection` | Сторона, из которой выезжает панель: `RIGHT` / `LEFT` / `TOP` / `BOTTOM`. По умолчанию в `OverlayBase` — `RIGHT` |
|
|
38
|
+
| `opened` | `boolean` | Управляемое состояние открытости. Изменение запускает анимацию открытия/закрытия |
|
|
39
|
+
| `setOpened` | `(opened: boolean) => void` | Сеттер `opened`, прокидывается в `provideProps` для управления изнутри панели |
|
|
40
|
+
| `children` | `(provideProps) => React.ReactElement` | Render-функция контента. См. «provideProps» ниже |
|
|
69
41
|
|
|
70
|
-
### Overlay —
|
|
42
|
+
### `Overlay` — опциональные props
|
|
71
43
|
|
|
72
|
-
| Prop | Тип |
|
|
44
|
+
| Prop | Тип | По умолчанию | Описание |
|
|
73
45
|
|---|---|---|---|
|
|
74
|
-
| `
|
|
75
|
-
| `
|
|
76
|
-
| `
|
|
77
|
-
| `
|
|
78
|
-
| `
|
|
79
|
-
| `
|
|
80
|
-
| `...HTMLAttributes` | — | — | На корневой `<div>`. |
|
|
46
|
+
| `fixed` | `boolean` | `false` | `position: fixed` (на всю страницу) вместо `absolute` (поверх родителя) |
|
|
47
|
+
| `onOpening` | `() => void` | — | Вызывается в начале открытия |
|
|
48
|
+
| `onOpen` | `() => void` | — | Вызывается по завершении анимации открытия |
|
|
49
|
+
| `onClosing` | `() => void` | — | Вызывается в начале закрытия |
|
|
50
|
+
| `onClose` | `() => void` | — | Вызывается по завершении анимации закрытия |
|
|
51
|
+
| `...HTMLDivAttributes` | — | — | Стандартные атрибуты корневого `<div>` (кроме `children`) |
|
|
81
52
|
|
|
82
|
-
###
|
|
53
|
+
### provideProps (аргумент render-функции `children`)
|
|
83
54
|
|
|
84
|
-
|
|
55
|
+
Передаются из `OverlayBase` в `children` и далее пробрасываются в `Overlay.Panel`
|
|
56
|
+
через `{...provideProps}`:
|
|
85
57
|
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
setClosing: (closing: boolean) => void;
|
|
96
|
-
setOpening: (opening: boolean) => void;
|
|
97
|
-
}
|
|
98
|
-
```
|
|
58
|
+
| Поле | Тип | Описание |
|
|
59
|
+
|---|---|---|
|
|
60
|
+
| `direction` | `EOverlayDirection` | Текущее направление |
|
|
61
|
+
| `opened` | `boolean` | Текущее состояние открытости |
|
|
62
|
+
| `opening` | `boolean` | Идёт анимация открытия |
|
|
63
|
+
| `closing` | `boolean` | Идёт анимация закрытия |
|
|
64
|
+
| `setOpened` | `(boolean) => void` | Сеттер `opened` |
|
|
65
|
+
| `setOpening` | `(boolean) => void` | Сеттер флага `opening` |
|
|
66
|
+
| `setClosing` | `(boolean) => void` | Сеттер флага `closing` |
|
|
99
67
|
|
|
100
|
-
###
|
|
68
|
+
### `Overlay.Mask` (`OverlayMask`)
|
|
101
69
|
|
|
102
|
-
|
|
70
|
+
Кликабельный полупрозрачный фон между контейнером и панелью. Props: `opened`
|
|
71
|
+
(управляет классом `overlayOpened` → `opacity`), плюс `...HTMLDivAttributes`
|
|
72
|
+
(обычно `onClick` для закрытия). `forwardRef` → `HTMLDivElement`.
|
|
103
73
|
|
|
104
|
-
###
|
|
74
|
+
### `Overlay.Panel` (`OverlayPanel`)
|
|
105
75
|
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
76
|
+
Выезжающая панель с контентом. Принимает весь `provideProps` (через спред) плюс
|
|
77
|
+
`direction`, `children` и `...HTMLDivAttributes`. По `onTransitionEnd` своего
|
|
78
|
+
корневого элемента сбрасывает `closing`/`opening` (отсекая bubbling от потомков
|
|
79
|
+
через сравнение `target === currentTarget`). `forwardRef` → `HTMLDivElement`.
|
|
109
80
|
|
|
110
|
-
###
|
|
81
|
+
### `OverlayBase`
|
|
111
82
|
|
|
112
|
-
- `forwardRef
|
|
113
|
-
|
|
114
|
-
|
|
83
|
+
Низкоуровневый headless-компонент (render-prop, **без** `forwardRef` — ничего не
|
|
84
|
+
рендерит сам, возвращает `children(provideProps)`). Единый источник истины по
|
|
85
|
+
флагам `opened`/`opening`/`closing` и оркестратор колбэков жизненного цикла.
|
|
86
|
+
Использует `useLayoutEffect` (не `useEffect`) при смене `opened`, чтобы избежать
|
|
87
|
+
мерцания при закрытии.
|
|
115
88
|
|
|
116
89
|
---
|
|
117
90
|
|
|
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]);
|
|
91
|
+
## Дизайн-токены
|
|
128
92
|
|
|
129
|
-
|
|
130
|
-
|
|
93
|
+
```
|
|
94
|
+
--triplex-next-Overlay-Background // фон маски (OverlayMask)
|
|
95
|
+
--triplex-next-Overlay-Panel_Background // фон панели (OverlayPanel)
|
|
131
96
|
```
|
|
132
97
|
|
|
133
|
-
-
|
|
134
|
-
|
|
135
|
-
- **`onClosing` / `onOpening`** вызывается **дважды** в начале (явно из `useLayoutEffect` + из `useEffect` на `closing`/`opening`). Известный нюанс — потребитель должен быть идемпотентен.
|
|
136
|
-
|
|
137
|
-
### Двойной слой `closing` (Overlay + OverlayBase)
|
|
98
|
+
Box-shadow панели и тайминги transition (`0.3s ease-in-out`) заданы константами в
|
|
99
|
+
`styles/Overlay.module.less` — токенов не имеют.
|
|
138
100
|
|
|
139
|
-
|
|
140
|
-
- `OverlayBase` хранит свой `closing` для render-prop'ов.
|
|
141
|
-
- **Причина**: `OverlayBase` без обёртки не имеет доступа к корневому `<div>` — нужно дублировать.
|
|
101
|
+
---
|
|
142
102
|
|
|
143
|
-
|
|
103
|
+
## Инварианты
|
|
144
104
|
|
|
145
|
-
|
|
146
|
-
.
|
|
147
|
-
|
|
105
|
+
- **`forwardRef`** обязателен на `Overlay` (target — корневой `HTMLDivElement`),
|
|
106
|
+
`Overlay.Mask` и `Overlay.Panel`. Не убирать. `OverlayBase` — render-prop,
|
|
107
|
+
`forwardRef` к нему неприменим.
|
|
108
|
+
- **Статические `Overlay.Mask` / `Overlay.Panel`** — часть публичного API
|
|
109
|
+
(составной компонент). Навешиваются на `forwardRef`-объект через `Object.assign`
|
|
110
|
+
(как `Badge.Dot`), тип компонента выводится автоматически — отдельный `*FC`-тип
|
|
111
|
+
не нужен.
|
|
112
|
+
- **Значения `EOverlayDirection`** (`"right"`/`"left"`/`"top"`/`"bottom"`) —
|
|
113
|
+
публичный API, не переименовывать.
|
|
114
|
+
- **Единый источник истины по `closing`/`opening`/`opened`** — `OverlayBase`.
|
|
115
|
+
Корневой `div` `Overlay` рендерится внутри render-prop и читает `closing` из
|
|
116
|
+
`provideProps` — не дублировать локальным `useState` в `Overlay`.
|
|
117
|
+
- **`provideProps` нужно пробрасывать в `Overlay.Panel`** — без них панель не
|
|
118
|
+
получит direction/флаги и не сбросит `closing`/`opening` по `transitionEnd`.
|
|
119
|
+
- **CSS-классы `bottom`/`left`/`right`/`top`/`opened`/`closing`/`fixed`/
|
|
120
|
+
`overlayOpened`** управляют анимацией — переименование ломает визуальные
|
|
121
|
+
переходы и покрыто unit-тестами.
|
|
122
|
+
- **React 17-совместимость** (ветка `release-0`): без `useId` и других React
|
|
123
|
+
18-only API.
|
|
124
|
+
|
|
125
|
+
### Гарантия однократного вызова lifecycle-колбэков
|
|
126
|
+
|
|
127
|
+
`OverlayBase` вызывает каждый из `onOpening` / `onClosing` / `onOpen` / `onClose`
|
|
128
|
+
**ровно один раз** за переход. `useLayoutEffect([opened])` только выставляет
|
|
129
|
+
флаги `opening` / `closing`, а сами колбэки вызываются исключительно в
|
|
130
|
+
`useEffect([opening])` / `useEffect([closing])`. Однократность закреплена
|
|
131
|
+
unit-тестами (`toHaveBeenCalledTimes(1)`).
|
|
132
|
+
|
|
133
|
+
> Ранее (до 2026-06-17) колбэки `onOpening` / `onClosing` вызывались дважды
|
|
134
|
+
> за переход — прямой вызов из `useLayoutEffect` дублировал эффект-driven вызов.
|
|
135
|
+
> Исправлено: прямые вызовы из layout-эффекта удалены.
|
|
148
136
|
|
|
149
|
-
|
|
137
|
+
---
|
|
150
138
|
|
|
151
|
-
|
|
139
|
+
## Accessibility
|
|
152
140
|
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
141
|
+
- `Overlay.Mask` кликабелен для закрытия — потребитель навешивает `onClick`
|
|
142
|
+
(`setOpened(false)`); компонент текст/aria не хардкодит (библиотека
|
|
143
|
+
мультиязычная). При необходимости потребитель сам задаёт `aria-label`.
|
|
144
|
+
- Скрытый оверлей выведен за пределы экрана (`left: -10000px`) и `visibility:
|
|
145
|
+
hidden`, чтобы его содержимое не получало фокус, пока он закрыт.
|
|
146
|
+
- Роль диалога (`role="dialog"`/`aria-modal`), захват и возврат фокуса, обработка
|
|
147
|
+
`Escape` — **не реализованы** в самом `Overlay`; при необходимости их
|
|
148
|
+
обеспечивает потребитель или обёртка (`LightBox`, `ModalWindow`).
|
|
160
149
|
|
|
161
|
-
|
|
162
|
-
- Анимация при закрытии не была обрезана `display: none`.
|
|
163
|
-
- Внутренние элементы не получали focus (через `visibility: hidden`).
|
|
150
|
+
---
|
|
164
151
|
|
|
165
|
-
|
|
152
|
+
## Связанные компоненты
|
|
166
153
|
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
.
|
|
170
|
-
|
|
154
|
+
- `OverlayBase` — headless-ядро (render-prop), управляет флагами и колбэками
|
|
155
|
+
жизненного цикла. Экспортируется из barrel, отдельного AI.md не имеет (описан
|
|
156
|
+
здесь).
|
|
157
|
+
- `OverlayMask` (`Overlay.Mask`) — кликабельный фон.
|
|
158
|
+
- `OverlayPanel` (`Overlay.Panel`) — выезжающая панель.
|
|
159
|
+
- `LightBox`, `TopOverlay` — построены поверх механики оверлея.
|
|
160
|
+
- `ModalWindow` — альтернатива для центрированных модальных окон.
|
|
171
161
|
|
|
172
|
-
|
|
173
|
-
- **Длительность 300ms** — захардкожена.
|
|
174
|
-
- **`box-shadow`** — захардкожен (`rgba(0,0,0,0.14)` 13px-blur 17px), не из токенов.
|
|
162
|
+
---
|
|
175
163
|
|
|
176
|
-
|
|
164
|
+
## Stories
|
|
177
165
|
|
|
178
|
-
|
|
166
|
+
Основные истории: `stories/Overlay/Overlay.stories.tsx`
|
|
167
|
+
Файлы примеров: `stories/Overlay/examples/`
|
|
179
168
|
|
|
180
|
-
|
|
169
|
+
| Story | Example file | Что демонстрирует |
|
|
170
|
+
|---|---|---|
|
|
171
|
+
| `Playground` | `Playground.tsx` | Интерактивный контроль `direction` / `fixed` / `opened`, маска и кнопки открытия/закрытия |
|
|
172
|
+
| `Default` | `Default.tsx` | Минимальный оверлей (RIGHT, не fixed) |
|
|
173
|
+
| `Directions` | `Directions.tsx` | Все 4 направления выезда панели |
|
|
174
|
+
| `Fixed` | `Fixed.tsx` | Fixed-оверлей на всю страницу |
|
|
175
|
+
| `VisualTests` | `VisualTests.tsx` | Скриншот-регрессия: `play` открывает панели всех направлений |
|
|
181
176
|
|
|
182
|
-
|
|
183
|
-
|
|
177
|
+
Visual baseline для `components-overlay--visual-tests` генерируется только в CI
|
|
178
|
+
(Linux) через `GitHub Actions → Update Visual Snapshots`.
|
|
184
179
|
|
|
185
180
|
---
|
|
186
181
|
|
|
187
|
-
##
|
|
182
|
+
## История изменений
|
|
188
183
|
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
-
|
|
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.
|
|
184
|
+
| Дата | Изменение |
|
|
185
|
+
|---|---|
|
|
186
|
+
| 2026-06-17 | Создан документ. `Overlay` переведён на `forwardRef` (ref → корневой `div`, аддитивно, release notes 1.34.0). Дедуплицирован флаг `closing` (единый источник — `OverlayBase`). Codestyle/JSDoc cleanup в 4 файлах. Добавлены 4 unit-тест-файла. Исправлен двойной вызов `onClosing`/`onOpening` за переход — теперь ровно один раз (закреплено `toHaveBeenCalledTimes(1)`). |
|
|
199
187
|
|
|
200
188
|
---
|
|
201
189
|
|
|
202
|
-
##
|
|
190
|
+
## Примеры использования
|
|
203
191
|
|
|
204
|
-
|
|
205
|
-
- **Нет ESC-обработчика** — закрытие зависит от `setOpened` через клик по Mask или явный close-button.
|
|
206
|
-
- **Нет `role="dialog"` / `aria-modal`** — overlay семантически нейтрален. Потребитель должен добавить через `...htmlDivAttributes`.
|
|
207
|
-
- `visibility: hidden` в closed-state — корректно убирает focusable-элементы из tab-последовательности.
|
|
208
|
-
- Mask — `<div>`, не интерактивный по умолчанию; клик-обработчик потребитель добавляет сам.
|
|
192
|
+
### Default
|
|
209
193
|
|
|
210
|
-
|
|
194
|
+
`stories/Overlay/examples/Default.tsx`
|
|
211
195
|
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
196
|
+
```tsx
|
|
197
|
+
import React, { useState } from "react";
|
|
198
|
+
import { Overlay, EOverlayDirection } from "@sberbusiness/triplex-next";
|
|
199
|
+
|
|
200
|
+
export const Default = () => {
|
|
201
|
+
const [opened, setOpened] = useState(false);
|
|
202
|
+
|
|
203
|
+
return (
|
|
204
|
+
<div
|
|
205
|
+
style={{
|
|
206
|
+
position: "relative",
|
|
207
|
+
width: 320,
|
|
208
|
+
height: 200,
|
|
209
|
+
border: "1px dashed #D0D4D9",
|
|
210
|
+
padding: 12,
|
|
211
|
+
}}
|
|
212
|
+
>
|
|
213
|
+
<button type="button" onClick={() => setOpened(true)} style={{ padding: "6px 12px" }}>
|
|
214
|
+
Открыть оверлей
|
|
215
|
+
</button>
|
|
216
|
+
<Overlay direction={EOverlayDirection.RIGHT} opened={opened} setOpened={setOpened}>
|
|
217
|
+
{(provideProps) => (
|
|
218
|
+
<>
|
|
219
|
+
<Overlay.Mask opened={opened} onClick={() => setOpened(false)} aria-label="Закрыть оверлей" />
|
|
220
|
+
<Overlay.Panel
|
|
221
|
+
{...provideProps}
|
|
222
|
+
direction={EOverlayDirection.RIGHT}
|
|
223
|
+
aria-label="Панель оверлея"
|
|
224
|
+
>
|
|
225
|
+
<div style={{ display: "flex", flexDirection: "column", gap: 12 }}>
|
|
226
|
+
<div>Содержимое оверлея</div>
|
|
227
|
+
<button type="button" onClick={() => setOpened(false)}>
|
|
228
|
+
Закрыть
|
|
229
|
+
</button>
|
|
230
|
+
</div>
|
|
231
|
+
</Overlay.Panel>
|
|
232
|
+
</>
|
|
233
|
+
)}
|
|
234
|
+
</Overlay>
|
|
235
|
+
</div>
|
|
236
|
+
);
|
|
237
|
+
};
|
|
238
|
+
```
|
|
216
239
|
|
|
217
|
-
|
|
240
|
+
### Directions
|
|
218
241
|
|
|
219
|
-
|
|
242
|
+
`stories/Overlay/examples/Directions.tsx`
|
|
220
243
|
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
244
|
+
```tsx
|
|
245
|
+
import React, { useState } from "react";
|
|
246
|
+
import { Overlay, EOverlayDirection } from "@sberbusiness/triplex-next";
|
|
224
247
|
|
|
225
|
-
|
|
248
|
+
interface IDirectionItemProps {
|
|
249
|
+
direction: EOverlayDirection;
|
|
250
|
+
title: string;
|
|
251
|
+
}
|
|
226
252
|
|
|
227
|
-
|
|
253
|
+
const DirectionItem = ({ direction, title }: IDirectionItemProps) => {
|
|
254
|
+
const [opened, setOpened] = useState(false);
|
|
255
|
+
|
|
256
|
+
return (
|
|
257
|
+
<div
|
|
258
|
+
style={{
|
|
259
|
+
position: "relative",
|
|
260
|
+
width: 320,
|
|
261
|
+
height: 200,
|
|
262
|
+
border: "1px dashed #D0D4D9",
|
|
263
|
+
padding: 12,
|
|
264
|
+
}}
|
|
265
|
+
>
|
|
266
|
+
<div style={{ marginBottom: 8 }}>{title}</div>
|
|
267
|
+
<button type="button" onClick={() => setOpened(true)} style={{ padding: "6px 12px" }}>
|
|
268
|
+
Открыть
|
|
269
|
+
</button>
|
|
270
|
+
<Overlay direction={direction} opened={opened} setOpened={setOpened}>
|
|
271
|
+
{(provideProps) => (
|
|
272
|
+
<>
|
|
273
|
+
<Overlay.Mask opened={opened} onClick={() => setOpened(false)} aria-label="Закрыть оверлей" />
|
|
274
|
+
<Overlay.Panel {...provideProps} direction={direction} aria-label="Панель оверлея">
|
|
275
|
+
<div style={{ display: "flex", flexDirection: "column", gap: 12 }}>
|
|
276
|
+
<div>Панель ({title})</div>
|
|
277
|
+
<button type="button" onClick={() => setOpened(false)}>
|
|
278
|
+
Закрыть
|
|
279
|
+
</button>
|
|
280
|
+
</div>
|
|
281
|
+
</Overlay.Panel>
|
|
282
|
+
</>
|
|
283
|
+
)}
|
|
284
|
+
</Overlay>
|
|
285
|
+
</div>
|
|
286
|
+
);
|
|
287
|
+
};
|
|
288
|
+
|
|
289
|
+
export const Directions = () => (
|
|
290
|
+
<div style={{ display: "grid", gridTemplateColumns: "repeat(2, 1fr)", gap: 16 }}>
|
|
291
|
+
<DirectionItem direction={EOverlayDirection.RIGHT} title="RIGHT" />
|
|
292
|
+
<DirectionItem direction={EOverlayDirection.LEFT} title="LEFT" />
|
|
293
|
+
<DirectionItem direction={EOverlayDirection.TOP} title="TOP" />
|
|
294
|
+
<DirectionItem direction={EOverlayDirection.BOTTOM} title="BOTTOM" />
|
|
295
|
+
</div>
|
|
296
|
+
);
|
|
297
|
+
```
|
|
228
298
|
|
|
229
|
-
|
|
299
|
+
### Fixed
|
|
230
300
|
|
|
231
|
-
|
|
301
|
+
`stories/Overlay/examples/Fixed.tsx`
|
|
232
302
|
|
|
233
|
-
|
|
303
|
+
```tsx
|
|
304
|
+
import React, { useState } from "react";
|
|
305
|
+
import { Overlay, EOverlayDirection } from "@sberbusiness/triplex-next";
|
|
306
|
+
|
|
307
|
+
export const Fixed = () => {
|
|
308
|
+
const [opened, setOpened] = useState(false);
|
|
309
|
+
|
|
310
|
+
return (
|
|
311
|
+
<div>
|
|
312
|
+
<button type="button" onClick={() => setOpened(true)} style={{ padding: "6px 12px" }}>
|
|
313
|
+
Открыть fixed оверлей
|
|
314
|
+
</button>
|
|
315
|
+
<Overlay fixed direction={EOverlayDirection.RIGHT} opened={opened} setOpened={setOpened}>
|
|
316
|
+
{(provideProps) => (
|
|
317
|
+
<>
|
|
318
|
+
<Overlay.Mask opened={opened} onClick={() => setOpened(false)} aria-label="Закрыть оверлей" />
|
|
319
|
+
<Overlay.Panel
|
|
320
|
+
{...provideProps}
|
|
321
|
+
direction={EOverlayDirection.RIGHT}
|
|
322
|
+
aria-label="Панель оверлея"
|
|
323
|
+
>
|
|
324
|
+
<div style={{ display: "flex", flexDirection: "column", gap: 12 }}>
|
|
325
|
+
<div>Оверлей на всю страницу (fixed)</div>
|
|
326
|
+
<button type="button" onClick={() => setOpened(false)}>
|
|
327
|
+
Закрыть
|
|
328
|
+
</button>
|
|
329
|
+
</div>
|
|
330
|
+
</Overlay.Panel>
|
|
331
|
+
</>
|
|
332
|
+
)}
|
|
333
|
+
</Overlay>
|
|
334
|
+
</div>
|
|
335
|
+
);
|
|
336
|
+
};
|
|
337
|
+
```
|
|
234
338
|
|
|
235
|
-
| Дата | Изменение |
|
|
236
|
-
|---|---|
|
|
237
|
-
| 2026-04-18 | Создан документ |
|