@cloud-ru/ds-drawer 1.0.0

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/CHANGELOG.md +8 -0
  2. package/LICENSE +201 -0
  3. package/README.md +480 -0
  4. package/dist/cjs/components/Drawer/Drawer.d.ts +6 -0
  5. package/dist/cjs/components/Drawer/Drawer.js +15 -0
  6. package/dist/cjs/components/Drawer/constants.d.ts +1 -0
  7. package/dist/cjs/components/Drawer/constants.js +4 -0
  8. package/dist/cjs/components/Drawer/index.d.ts +2 -0
  9. package/dist/cjs/components/Drawer/index.js +18 -0
  10. package/dist/cjs/components/Drawer/styles.module.css +19 -0
  11. package/dist/cjs/components/Drawer/types.d.ts +35 -0
  12. package/dist/cjs/components/Drawer/types.js +2 -0
  13. package/dist/cjs/components/DrawerCustom/DrawerCustom.d.ts +15 -0
  14. package/dist/cjs/components/DrawerCustom/DrawerCustom.js +76 -0
  15. package/dist/cjs/components/DrawerCustom/constants.d.ts +4 -0
  16. package/dist/cjs/components/DrawerCustom/constants.js +17 -0
  17. package/dist/cjs/components/DrawerCustom/hooks/index.d.ts +1 -0
  18. package/dist/cjs/components/DrawerCustom/hooks/index.js +17 -0
  19. package/dist/cjs/components/DrawerCustom/hooks/useDrawerFocusTrap.d.ts +9 -0
  20. package/dist/cjs/components/DrawerCustom/hooks/useDrawerFocusTrap.js +83 -0
  21. package/dist/cjs/components/DrawerCustom/hooks/useDrawerResize.d.ts +15 -0
  22. package/dist/cjs/components/DrawerCustom/hooks/useDrawerResize.js +50 -0
  23. package/dist/cjs/components/DrawerCustom/index.d.ts +2 -0
  24. package/dist/cjs/components/DrawerCustom/index.js +17 -0
  25. package/dist/cjs/components/DrawerCustom/motion.css +93 -0
  26. package/dist/cjs/components/DrawerCustom/styles.module.css +491 -0
  27. package/dist/cjs/components/DrawerCustom/types.d.ts +71 -0
  28. package/dist/cjs/components/DrawerCustom/types.js +2 -0
  29. package/dist/cjs/components/index.d.ts +2 -0
  30. package/dist/cjs/components/index.js +18 -0
  31. package/dist/cjs/constants.d.ts +28 -0
  32. package/dist/cjs/constants.js +31 -0
  33. package/dist/cjs/helperComponents/DesktopDrawer/DesktopDrawer.d.ts +3 -0
  34. package/dist/cjs/helperComponents/DesktopDrawer/DesktopDrawer.js +32 -0
  35. package/dist/cjs/helperComponents/DesktopDrawer/index.d.ts +1 -0
  36. package/dist/cjs/helperComponents/DesktopDrawer/index.js +17 -0
  37. package/dist/cjs/helperComponents/DialogSlots.d.ts +8 -0
  38. package/dist/cjs/helperComponents/DialogSlots.js +12 -0
  39. package/dist/cjs/helperComponents/DrawerBody/DrawerBody.d.ts +4 -0
  40. package/dist/cjs/helperComponents/DrawerBody/DrawerBody.js +17 -0
  41. package/dist/cjs/helperComponents/DrawerBody/index.d.ts +1 -0
  42. package/dist/cjs/helperComponents/DrawerBody/index.js +17 -0
  43. package/dist/cjs/helperComponents/DrawerBody/styles.module.css +30 -0
  44. package/dist/cjs/helperComponents/MobileDrawer/MobileDrawer.d.ts +7 -0
  45. package/dist/cjs/helperComponents/MobileDrawer/MobileDrawer.js +26 -0
  46. package/dist/cjs/helperComponents/MobileDrawer/index.d.ts +1 -0
  47. package/dist/cjs/helperComponents/MobileDrawer/index.js +17 -0
  48. package/dist/cjs/helperComponents/index.d.ts +8 -0
  49. package/dist/cjs/helperComponents/index.js +30 -0
  50. package/dist/cjs/index.d.ts +5 -0
  51. package/dist/cjs/index.js +23 -0
  52. package/dist/cjs/package.json +3 -0
  53. package/dist/cjs/style.css +633 -0
  54. package/dist/cjs/types.d.ts +4 -0
  55. package/dist/cjs/types.js +2 -0
  56. package/dist/cjs/utils/interopDefault.d.ts +7 -0
  57. package/dist/cjs/utils/interopDefault.js +10 -0
  58. package/dist/esm/components/Drawer/Drawer.d.ts +6 -0
  59. package/dist/esm/components/Drawer/Drawer.js +12 -0
  60. package/dist/esm/components/Drawer/constants.d.ts +1 -0
  61. package/dist/esm/components/Drawer/constants.js +1 -0
  62. package/dist/esm/components/Drawer/index.d.ts +2 -0
  63. package/dist/esm/components/Drawer/index.js +2 -0
  64. package/dist/esm/components/Drawer/styles.module.css +19 -0
  65. package/dist/esm/components/Drawer/types.d.ts +35 -0
  66. package/dist/esm/components/Drawer/types.js +1 -0
  67. package/dist/esm/components/DrawerCustom/DrawerCustom.d.ts +15 -0
  68. package/dist/esm/components/DrawerCustom/DrawerCustom.js +70 -0
  69. package/dist/esm/components/DrawerCustom/constants.d.ts +4 -0
  70. package/dist/esm/components/DrawerCustom/constants.js +13 -0
  71. package/dist/esm/components/DrawerCustom/hooks/index.d.ts +1 -0
  72. package/dist/esm/components/DrawerCustom/hooks/index.js +1 -0
  73. package/dist/esm/components/DrawerCustom/hooks/useDrawerFocusTrap.d.ts +9 -0
  74. package/dist/esm/components/DrawerCustom/hooks/useDrawerFocusTrap.js +80 -0
  75. package/dist/esm/components/DrawerCustom/hooks/useDrawerResize.d.ts +15 -0
  76. package/dist/esm/components/DrawerCustom/hooks/useDrawerResize.js +47 -0
  77. package/dist/esm/components/DrawerCustom/index.d.ts +2 -0
  78. package/dist/esm/components/DrawerCustom/index.js +1 -0
  79. package/dist/esm/components/DrawerCustom/motion.css +93 -0
  80. package/dist/esm/components/DrawerCustom/styles.module.css +491 -0
  81. package/dist/esm/components/DrawerCustom/types.d.ts +71 -0
  82. package/dist/esm/components/DrawerCustom/types.js +1 -0
  83. package/dist/esm/components/index.d.ts +2 -0
  84. package/dist/esm/components/index.js +2 -0
  85. package/dist/esm/constants.d.ts +28 -0
  86. package/dist/esm/constants.js +28 -0
  87. package/dist/esm/helperComponents/DesktopDrawer/DesktopDrawer.d.ts +3 -0
  88. package/dist/esm/helperComponents/DesktopDrawer/DesktopDrawer.js +26 -0
  89. package/dist/esm/helperComponents/DesktopDrawer/index.d.ts +1 -0
  90. package/dist/esm/helperComponents/DesktopDrawer/index.js +1 -0
  91. package/dist/esm/helperComponents/DialogSlots.d.ts +8 -0
  92. package/dist/esm/helperComponents/DialogSlots.js +8 -0
  93. package/dist/esm/helperComponents/DrawerBody/DrawerBody.d.ts +4 -0
  94. package/dist/esm/helperComponents/DrawerBody/DrawerBody.js +11 -0
  95. package/dist/esm/helperComponents/DrawerBody/index.d.ts +1 -0
  96. package/dist/esm/helperComponents/DrawerBody/index.js +1 -0
  97. package/dist/esm/helperComponents/DrawerBody/styles.module.css +30 -0
  98. package/dist/esm/helperComponents/MobileDrawer/MobileDrawer.d.ts +7 -0
  99. package/dist/esm/helperComponents/MobileDrawer/MobileDrawer.js +23 -0
  100. package/dist/esm/helperComponents/MobileDrawer/index.d.ts +1 -0
  101. package/dist/esm/helperComponents/MobileDrawer/index.js +1 -0
  102. package/dist/esm/helperComponents/index.d.ts +8 -0
  103. package/dist/esm/helperComponents/index.js +8 -0
  104. package/dist/esm/index.d.ts +5 -0
  105. package/dist/esm/index.js +5 -0
  106. package/dist/esm/style.css +633 -0
  107. package/dist/esm/types.d.ts +4 -0
  108. package/dist/esm/types.js +1 -0
  109. package/dist/esm/utils/interopDefault.d.ts +7 -0
  110. package/dist/esm/utils/interopDefault.js +7 -0
  111. package/dist/tsconfig.cjs.tsbuildinfo +1 -0
  112. package/dist/tsconfig.esm.tsbuildinfo +1 -0
  113. package/package.json +51 -0
  114. package/src/components/Drawer/Drawer.tsx +15 -0
  115. package/src/components/Drawer/constants.ts +1 -0
  116. package/src/components/Drawer/index.ts +2 -0
  117. package/src/components/Drawer/styles.module.scss +26 -0
  118. package/src/components/Drawer/types.ts +42 -0
  119. package/src/components/DrawerCustom/DrawerCustom.tsx +197 -0
  120. package/src/components/DrawerCustom/constants.ts +17 -0
  121. package/src/components/DrawerCustom/hooks/index.ts +1 -0
  122. package/src/components/DrawerCustom/hooks/useDrawerFocusTrap.ts +97 -0
  123. package/src/components/DrawerCustom/hooks/useDrawerResize.tsx +78 -0
  124. package/src/components/DrawerCustom/index.ts +2 -0
  125. package/src/components/DrawerCustom/styles.module.scss +330 -0
  126. package/src/components/DrawerCustom/types.ts +77 -0
  127. package/src/components/index.ts +2 -0
  128. package/src/constants.ts +31 -0
  129. package/src/helperComponents/DesktopDrawer/DesktopDrawer.tsx +85 -0
  130. package/src/helperComponents/DesktopDrawer/index.ts +1 -0
  131. package/src/helperComponents/DialogSlots.tsx +25 -0
  132. package/src/helperComponents/DrawerBody/DrawerBody.tsx +25 -0
  133. package/src/helperComponents/DrawerBody/index.ts +1 -0
  134. package/src/helperComponents/DrawerBody/styles.module.scss +32 -0
  135. package/src/helperComponents/MobileDrawer/MobileDrawer.tsx +75 -0
  136. package/src/helperComponents/MobileDrawer/index.ts +1 -0
  137. package/src/helperComponents/index.ts +11 -0
  138. package/src/index.ts +6 -0
  139. package/src/types.ts +6 -0
  140. package/src/utils/interopDefault.ts +7 -0
package/README.md ADDED
@@ -0,0 +1,480 @@
1
+ # Drawer
2
+
3
+ `@cloud-ru/ds-drawer` — Пакет выезжающих панелей — компоненты Drawer и DrawerCustom с едиными токенами позиции и ширины.
4
+
5
+ Пакет `@cloud-ru/ds-drawer` предоставляет два компонента для боковых/нижних панелей: готовую сборку `Drawer` с пресетной шапкой, телом и футером и низкоуровневый `DrawerCustom` для ручной композиции.
6
+
7
+ - ****Drawer**** — готовая панель с шапкой, прокручиваемым телом и опциональным футером. Покрывает 90% сценариев.
8
+ - ****DrawerCustom**** — низкоуровневая версия без предопределённой структуры: собирайте из `DrawerCustom.Header`, `.Body`, `.Footer` или собственной разметки.
9
+
10
+ ## Установка
11
+
12
+ ```bash
13
+ pnpm add @cloud-ru/ds-drawer
14
+ ```
15
+
16
+ ```ts
17
+ import { Drawer, DrawerCustom, POSITION, WIDTH } from '@cloud-ru/ds-drawer'
18
+ ```
19
+
20
+ ## Смотри также
21
+
22
+ - **Modal** — центрированное модальное окно для блокирующих подтверждений.
23
+ - **Popover** — всплывающий слой рядом с триггером.
24
+
25
+ ## Drawer
26
+
27
+ Панель, выезжающая со стороны экрана — пресетная шапка, прокручиваемое тело и опциональный футер.
28
+
29
+ Панель, выезжающая со стороны экрана — для дополнительного контекста, фильтров, форм и пошаговых сценариев. `Drawer` собирает шапку (медиа, заголовок, подзаголовок, back-button, слот после заголовка), прокручиваемое тело и опциональный футер. Для ручной композиции используйте [`DrawerCustom`](./drawer-custom).
30
+
31
+ ### Когда использовать
32
+ - Форма, фильтры или детали, которые не помещаются в основной поток и требуют отдельного контекста.
33
+ - Пошаговый сценарий с кнопкой «назад» в шапке — возвращение между экранами без потери контекста.
34
+ - Нижний лист (bottom sheet) для компактных действий и подтверждений на мобильных устройствах.
35
+ - Стек из двух-трёх связанных панелей через `nestedDrawer`.
36
+
37
+ Когда **не** нужен: критическое подтверждение, блокирующее остальной интерфейс (берите `Modal`), всплывающий поповер рядом с элементом (`Popover`), тост-уведомление (не блокирует UI).
38
+
39
+ ### Примеры использования
40
+ #### Базовое использование
41
+
42
+ Контролируемое open/onClose, footer из `ButtonGroup`.
43
+
44
+ ```tsx
45
+ import { Button, ButtonGroup } from '@cloud-ru/ds-button';
46
+ import { Drawer } from '@cloud-ru/ds-drawer';
47
+ import { useState } from 'react';
48
+
49
+ export function Basic() {
50
+ const [open, setOpen] = useState(false);
51
+ const close = () => setOpen(false);
52
+
53
+ return (
54
+ <>
55
+ <Button label='Открыть' appearance='primary' view='filled' onClick={() => setOpen(true)} />
56
+ <Drawer
57
+ open={open}
58
+ position='right'
59
+ onClose={close}
60
+ title='Заголовок'
61
+ subtitle='Короткое пояснение сценария'
62
+ content='Основной контент тела. Сюда помещается форма, предупреждение или подробный текст.'
63
+ footer={
64
+ <ButtonGroup
65
+ primaryAction={{ label: 'Продолжить', view: 'filled', onClick: close }}
66
+ secondaryAction={{ label: 'Отмена', appearance: 'neutral', view: 'outline', onClick: close }}
67
+ />
68
+ }
69
+ />
70
+ </>
71
+ );
72
+ }
73
+ ```
74
+
75
+ #### Критическое действие
76
+
77
+ Critical primary, neutral outline secondary.
78
+
79
+ ```tsx
80
+ import { Button, ButtonGroup } from '@cloud-ru/ds-button';
81
+ import { Drawer } from '@cloud-ru/ds-drawer';
82
+ import { useState } from 'react';
83
+
84
+ export function WithFooter() {
85
+ const [open, setOpen] = useState(false);
86
+ const close = () => setOpen(false);
87
+
88
+ return (
89
+ <>
90
+ <Button label='Удалить…' appearance='critical' view='outline' onClick={() => setOpen(true)} />
91
+ <Drawer
92
+ open={open}
93
+ position='right'
94
+ onClose={close}
95
+ title='Удалить запись'
96
+ subtitle='Действие необратимо.'
97
+ content='После подтверждения запись и все её ссылки исчезнут из списка.'
98
+ footer={
99
+ <ButtonGroup
100
+ primaryAction={{
101
+ label: 'Удалить',
102
+ appearance: 'critical',
103
+ view: 'filled',
104
+ onClick: close,
105
+ }}
106
+ secondaryAction={{
107
+ label: 'Отмена',
108
+ appearance: 'neutral',
109
+ view: 'outline',
110
+ onClick: close,
111
+ }}
112
+ />
113
+ }
114
+ />
115
+ </>
116
+ );
117
+ }
118
+ ```
119
+
120
+ #### С медиа-слотом
121
+
122
+ `media` рендерится над шапкой на всю ширину панели.
123
+
124
+ ```tsx
125
+ import { Button } from '@cloud-ru/ds-button';
126
+ import { Drawer } from '@cloud-ru/ds-drawer';
127
+ import { useState } from 'react';
128
+
129
+ export function WithMedia() {
130
+ const [open, setOpen] = useState(false);
131
+ const close = () => setOpen(false);
132
+
133
+ return (
134
+ <>
135
+ <Button label='Открыть онбординг' appearance='primary' view='filled' onClick={() => setOpen(true)} />
136
+ <Drawer
137
+ open={open}
138
+ position='right'
139
+ width='m'
140
+ onClose={close}
141
+ media={
142
+ <div
143
+ style={{
144
+ height: 200,
145
+ background: 'linear-gradient(135deg, rgb(102 126 234), rgb(118 75 162))',
146
+ }}
147
+ />
148
+ }
149
+ title='Добро пожаловать'
150
+ subtitle='Кратко о том, что изменилось в этой версии.'
151
+ content='Список ключевых улучшений и ссылки на подробности могут размещаться в теле.'
152
+ />
153
+ </>
154
+ );
155
+ }
156
+ ```
157
+
158
+ #### Bottom sheet
159
+
160
+ `position="bottom"` + `heightAuto` — высота по контенту.
161
+
162
+ ```tsx
163
+ import { Button } from '@cloud-ru/ds-button';
164
+ import { Drawer } from '@cloud-ru/ds-drawer';
165
+ import { useState } from 'react';
166
+
167
+ export function HeightAuto() {
168
+ const [open, setOpen] = useState(false);
169
+ const close = () => setOpen(false);
170
+
171
+ return (
172
+ <>
173
+ <Button label='Открыть bottom sheet' appearance='primary' view='filled' onClick={() => setOpen(true)} />
174
+ <Drawer
175
+ open={open}
176
+ position='bottom'
177
+ heightAuto
178
+ onClose={close}
179
+ title='Bottom sheet'
180
+ subtitle='Высота рассчитывается по контенту'
181
+ content='Подходит для компактных подтверждений на мобильных устройствах.'
182
+ />
183
+ </>
184
+ );
185
+ }
186
+ ```
187
+
188
+ #### Вложенный Drawer
189
+
190
+ Родитель сдвигается влево при открытии дочернего.
191
+
192
+ ```tsx
193
+ import { Button } from '@cloud-ru/ds-button';
194
+ import { Drawer } from '@cloud-ru/ds-drawer';
195
+ import { useState } from 'react';
196
+
197
+ export function NestedDrawer() {
198
+ const [outerOpen, setOuterOpen] = useState(false);
199
+ const [innerOpen, setInnerOpen] = useState(false);
200
+
201
+ const closeAll = () => {
202
+ setInnerOpen(false);
203
+ setOuterOpen(false);
204
+ };
205
+
206
+ return (
207
+ <>
208
+ <Button label='Открыть родительский' appearance='primary' view='filled' onClick={() => setOuterOpen(true)} />
209
+ <Drawer
210
+ open={outerOpen}
211
+ position='right'
212
+ width='m'
213
+ onClose={closeAll}
214
+ title='Родительский Drawer'
215
+ subtitle='При открытии вложенного — родитель сдвигается влево.'
216
+ content={
217
+ <Button label='Открыть вложенный' appearance='primary' view='outline' onClick={() => setInnerOpen(true)} />
218
+ }
219
+ nestedDrawer={
220
+ <Drawer
221
+ open={innerOpen}
222
+ position='right'
223
+ width='s'
224
+ onClose={() => setInnerOpen(false)}
225
+ title='Вложенный Drawer'
226
+ subtitle='Кнопка «назад» возвращает к родителю'
227
+ onBackButtonClick={() => setInnerOpen(false)}
228
+ content='Вложенный Drawer рендерится через проп nestedDrawer родителя.'
229
+ />
230
+ }
231
+ />
232
+ </>
233
+ );
234
+ }
235
+ ```
236
+
237
+ ### Props
238
+
239
+ **DrawerProps**
240
+
241
+ | Prop | Type | Default | Description |
242
+ |------|------|---------|-------------|
243
+ | `additionalButton` | `BottomSheetActionButton` | — | Дополнительная (третья) кнопка — пропсы `Button` (дефолт `view='simple'`, `appearance='neutral'`). |
244
+ | `approveButton` | `BottomSheetActionButton` | — | Основная кнопка действия — пропсы `Button` (дефолт `view='filled'`, `appearance='primary'`). |
245
+ | `cancelButton` | `BottomSheetActionButton` | — | Кнопка отмены — объект пропсов `Button` (по умолчанию `view='outline'`, `appearance='neutral'`). |
246
+ | `children` | `string \| number \| boolean \| ReactElement<any, string \| JSXElementConstructor<any>> \| Iterable<ReactNode> \| ReactPortal \| null \| undefined` | — | |
247
+ | `className` | `string` | — | CSS-класс для элемента с контентом <br/> CSS-класс |
248
+ | `closeOnPopstate` | `boolean` | — | Закрывать дровер при перемещении по истории браузера |
249
+ | `container` | `string \| HTMLElement` | — | Контейнер в котором будет рендерится Drawer. По-умолчанию - body |
250
+ | `content` | `ReactNode` | — | Содержимое body (альтернатива `children`). |
251
+ | `data-test-id` | `string` | — | |
252
+ | `disableMotions` | `boolean` | `false` | Отключить анимации |
253
+ | `footer` | `(ReactElement<any, string \| JSXElementConstructor<any>> & (string \| number \| boolean \| ReactElement<any, string \| JSXElementConstructor<any>> \| Iterable<...> \| ReactPortal \| null))` | — | Футер <br/> Произвольный футер. Приоритетнее `approveButton` / `cancelButton` / `additionalButton`. |
254
+ | `footerActionsOrientation` | `"horizontal"` \| `"vertical"` | `'horizontal'` | Ориентация кнопок футера. Применяется только при двух кнопках; игнорируется при заданном `footer`. |
255
+ | `heightAuto` | `boolean` | `false` | Высота панели по контенту (только при `position: "top" \| "bottom"`). |
256
+ | `media` | `ReactNode` | — | Медиа-контент |
257
+ | `nestedDrawer` | `DrawerProps` | — | Вложенный Drawer |
258
+ | `onBackButtonClick` | `(() => void)` | — | Callback клика на back-кнопку (слева в шапке). <br/> Наличие callback'а авто-рендерит `Button view='function' icon={<ArrowLeftSVG />}`. |
259
+ | `onClose` | `() => void` | — | Колбэк закрытия |
260
+ | `onSnapIndexChange` | `((snapIndex: number) => void)` | — | Callback изменения активного snap'а (пересечение swipe-границы или click по UI). <br/> Не вызывается при программной смене controlled `snapIndex`. |
261
+ | `open` | `boolean` | — | Управление состоянием показан/не показан. |
262
+ | `position` | `"bottom"` \| `"left"` \| `"right"` \| `"top"` | — | Расположение |
263
+ | `rootClassName` | `string` | — | CSS-класс для корневого элемента |
264
+ | `safeArea` | `boolean` | `true` | Резервировать ли место под iOS notch / home-indicator и Android nav-bar. Реализовано паддингом <br/> на `.content` через `env(safe-area-inset-*)`: на устройстве без выреза/индикатора (и на desktop) <br/> inset = 0, поэтому никакого «лишнего» отступа не появляется; на notched-устройстве — ровно нужный. <br/> Верхний отступ добавляется только когда sheet раскрыт на полный вьюпорт (его верх под notch). |
265
+ | `showBlackout` | `boolean` | `true` | Отображение темной подложки |
266
+ | `showButtonClosed` | `boolean` | `true` | Отображение кнопки закрытия в шапке дровера |
267
+ | `slotAfterTitle` | `ReactNode` | — | Slot справа от title (например, `QuestionTooltip` из `@cloud-ru/ds-tooltip`). |
268
+ | `snapIndex` | `number` | — | Controlled-индекс активного snap'а. Если задан, sheet всегда находится на этом snap'е; <br/> swipe-up/down вызывают `onSnapIndexChange`, но не меняют позицию сами — consumer должен <br/> передать новое значение. |
269
+ | `snapPoints` | `SnapPoint` | — | Массив фиксированных позиций sheet'а от меньшей к большей. По дефолту `undefined` — <br/> sheet `height: auto` с одним snap'ом по высоте контента. <br/> Пример: `[0.5, 1]` — sheet открывается на половину экрана, drag вверх раскрывает <br/> до full-viewport; drag вниз ниже `0.5` ведёт к закрытию. <br/> Контракт массива (движок не сортирует и не дедуплицирует — порядок и различимость на <br/> стороне потребителя): <br/> - строго по возрастанию: индекс `0` — самая компактная позиция, последний — top / expanded; <br/> - значения должны резолвиться в различные высоты (`['50%', 0.5]` на типичном вьюпорте дадут <br/> одну высоту → дубль-индекс будет недостижим свайпом); <br/> - `'fit-content'` имеет смысл только как ЕДИНСТВЕННЫЙ snap (без `snapPoints`); внутри массива <br/> фиксированных позиций его «контентная» высота не определена. |
270
+ | `subtitle` | `ReactNode` | — | Текстовая строка-подзаголовок под title. |
271
+ | `swipeEnabled` | `boolean` | `true` | Включает swipe-down для закрытия / swipe-up для раскрытия на следующий snap-point. <br/> При `swipeEnabled=false` snap-point по-прежнему можно переключить через controlled `snapIndex` prop'ом. |
272
+ | `title` | `ReactNode` | — | Заголовок. Типографика зависит от поверхности: `title-l` на sheet, `headline-s` на window (modal/drawer). |
273
+ | `width` | `Width` | `'s'` | Ширина (только при position: "left" \| "right") |
274
+
275
+ ##### Related types
276
+
277
+ **DrawerProps**
278
+
279
+ | Prop | Type | Default | Description |
280
+ |------|------|---------|-------------|
281
+ | `additionalButton` | `BottomSheetActionButton` | — | Дополнительная (третья) кнопка — пропсы `Button` (дефолт `view='simple'`, `appearance='neutral'`). |
282
+ | `approveButton` | `BottomSheetActionButton` | — | Основная кнопка действия — пропсы `Button` (дефолт `view='filled'`, `appearance='primary'`). |
283
+ | `cancelButton` | `BottomSheetActionButton` | — | Кнопка отмены — объект пропсов `Button` (по умолчанию `view='outline'`, `appearance='neutral'`). |
284
+ | `children` | `string \| number \| boolean \| ReactElement<any, string \| JSXElementConstructor<any>> \| Iterable<ReactNode> \| ReactPortal \| null \| undefined` | — | |
285
+ | `className` | `string \| undefined` | — | CSS-класс для элемента с контентом <br/> CSS-класс |
286
+ | `closeOnPopstate` | `boolean \| undefined` | — | Закрывать дровер при перемещении по истории браузера |
287
+ | `container` | `string \| HTMLElement \| undefined` | — | Контейнер в котором будет рендерится Drawer. По-умолчанию - body |
288
+ | `content` | `string \| number \| boolean \| ReactElement<any, string \| JSXElementConstructor<any>> \| Iterable<ReactNode> \| ReactPortal \| null \| undefined` | — | Содержимое body (альтернатива `children`). |
289
+ | `data-test-id` | `string \| undefined` | — | |
290
+ | `disableMotions` | `boolean \| undefined` | — | Отключить анимации |
291
+ | `footer` | `(ReactElement<any, string \| JSXElementConstructor<any>> & (string \| number \| boolean \| ReactElement<any, string \| JSXElementConstructor<any>> \| Iterable<ReactNode> \| ReactPortal \| null)) \| undefined` | — | Футер <br/> Произвольный футер. Приоритетнее `approveButton` / `cancelButton` / `additionalButton`. |
292
+ | `footerActionsOrientation` | `"horizontal"` \| `"vertical"` | — | Ориентация кнопок футера. Применяется только при двух кнопках; игнорируется при заданном `footer`. |
293
+ | `heightAuto` | `boolean \| undefined` | — | Высота панели по контенту (только при `position: "top" \| "bottom"`). |
294
+ | `media` | `string \| number \| boolean \| ReactElement<any, string \| JSXElementConstructor<any>> \| Iterable<ReactNode> \| ReactPortal \| null \| undefined` | — | Медиа-контент |
295
+ | `nestedDrawer` | `DrawerProps` | — | Вложенный Drawer |
296
+ | `onBackButtonClick` | `(() => void) \| undefined` | — | Callback клика на back-кнопку (слева в шапке). <br/> Наличие callback'а авто-рендерит `Button view='function' icon={<ArrowLeftSVG />}`. |
297
+ | `onClose` | `() => void` | — | Колбэк закрытия |
298
+ | `onSnapIndexChange` | `((snapIndex: number) => void) \| undefined` | — | Callback изменения активного snap'а (пересечение swipe-границы или click по UI). <br/> Не вызывается при программной смене controlled `snapIndex`. |
299
+ | `open` | `boolean` | — | Управление состоянием показан/не показан. |
300
+ | `position` | `"bottom"` \| `"left"` \| `"right"` \| `"top"` | — | Расположение |
301
+ | `rootClassName` | `string \| undefined` | — | CSS-класс для корневого элемента |
302
+ | `safeArea` | `boolean \| undefined` | — | Резервировать ли место под iOS notch / home-indicator и Android nav-bar. Реализовано паддингом <br/> на `.content` через `env(safe-area-inset-*)`: на устройстве без выреза/индикатора (и на desktop) <br/> inset = 0, поэтому никакого «лишнего» отступа не появляется; на notched-устройстве — ровно нужный. <br/> Верхний отступ добавляется только когда sheet раскрыт на полный вьюпорт (его верх под notch). |
303
+ | `showBlackout` | `boolean \| undefined` | — | Отображение темной подложки |
304
+ | `showButtonClosed` | `boolean \| undefined` | — | Отображение кнопки закрытия в шапке дровера |
305
+ | `slotAfterTitle` | `string \| number \| boolean \| ReactElement<any, string \| JSXElementConstructor<any>> \| Iterable<ReactNode> \| ReactPortal \| null \| undefined` | — | Slot справа от title (например, `QuestionTooltip` из `@cloud-ru/ds-tooltip`). |
306
+ | `snapIndex` | `number \| undefined` | — | Controlled-индекс активного snap'а. Если задан, sheet всегда находится на этом snap'е; <br/> swipe-up/down вызывают `onSnapIndexChange`, но не меняют позицию сами — consumer должен <br/> передать новое значение. |
307
+ | `snapPoints` | `SnapPoint` | — | Массив фиксированных позиций sheet'а от меньшей к большей. По дефолту `undefined` — <br/> sheet `height: auto` с одним snap'ом по высоте контента. <br/> Пример: `[0.5, 1]` — sheet открывается на половину экрана, drag вверх раскрывает <br/> до full-viewport; drag вниз ниже `0.5` ведёт к закрытию. <br/> Контракт массива (движок не сортирует и не дедуплицирует — порядок и различимость на <br/> стороне потребителя): <br/> - строго по возрастанию: индекс `0` — самая компактная позиция, последний — top / expanded; <br/> - значения должны резолвиться в различные высоты (`['50%', 0.5]` на типичном вьюпорте дадут <br/> одну высоту → дубль-индекс будет недостижим свайпом); <br/> - `'fit-content'` имеет смысл только как ЕДИНСТВЕННЫЙ snap (без `snapPoints`); внутри массива <br/> фиксированных позиций его «контентная» высота не определена. |
308
+ | `subtitle` | `string \| number \| boolean \| ReactElement<any, string \| JSXElementConstructor<any>> \| Iterable<ReactNode> \| ReactPortal \| null \| undefined` | — | Текстовая строка-подзаголовок под title. |
309
+ | `swipeEnabled` | `boolean \| undefined` | — | Включает swipe-down для закрытия / swipe-up для раскрытия на следующий snap-point. <br/> При `swipeEnabled=false` snap-point по-прежнему можно переключить через controlled `snapIndex` prop'ом. |
310
+ | `title` | `string \| number \| boolean \| ReactElement<any, string \| JSXElementConstructor<any>> \| Iterable<ReactNode> \| ReactPortal \| null \| undefined` | — | Заголовок. Типографика зависит от поверхности: `title-l` на sheet, `headline-s` на window (modal/drawer). |
311
+ | `width` | `Width` | — | Ширина (только при position: "left" \| "right") |
312
+
313
+ - `Position` = `"bottom"` \| `"left"` \| `"right"` \| `"top"`
314
+
315
+ - `Width` = `"l"` \| `"m"` \| `"s"`
316
+
317
+ ### Адаптивность
318
+
319
+ `Drawer` — адаптивный компонент с переключением поверхности (surface-swap). Раскладку он берёт из `AdaptiveProvider` (контекст `@cloud-ru/ds-adaptive`); публичный API единый для обеих платформ:
320
+
321
+ - **desktop** (по умолчанию) — панель, выезжающая со стороны экрана (`left`/`right`/`top`/`bottom`).
322
+ - **mobile** — контент рендерится в `BottomSheet` из `@cloud-ru/ds-bottom-sheet` (панель снизу со свайпом для закрытия).
323
+
324
+ Верстайте под desktop и поставьте один `<AdaptiveProvider>` в корне приложения — mobile-поверхность включается автоматически (desktop-first). Пропа `layoutType` у компонента нет: источник раскладки — только контекст.
325
+
326
+ #### Как форсировать платформу
327
+
328
+ Форс — только контекстом, не пропом:
329
+
330
+ - Поддерево — вложенный провайдер:
331
+ ```tsx
332
+ import { AdaptiveProvider } from '@cloud-ru/ds-adaptive'
333
+
334
+ <AdaptiveProvider layoutType='mobile'>
335
+ <Drawer open={open} onClose={close} content={…} />
336
+ </AdaptiveProvider>
337
+ ```
338
+ - Отдельный компонент — `withLayoutType` (module-scope, сахар над провайдером):
339
+ ```tsx
340
+ import { withLayoutType } from '@cloud-ru/ds-adaptive'
341
+ import { Drawer } from '@cloud-ru/ds-drawer'
342
+
343
+ const MobileDrawer = withLayoutType(Drawer, 'mobile')
344
+ ```
345
+
346
+ #### Платформенные пропы
347
+
348
+ Часть пропов управляет геометрией desktop-панели и на mobile молча игнорируется (у `BottomSheet` своя поверхность снизу). Таблица синхронизирована с type-level JSDoc у `DrawerProps`.
349
+
350
+ | Пропы | desktop | mobile |
351
+ |-------|---------|--------|
352
+ | `position`, `width`, `heightAuto`, `nestedDrawer` | используется | игнорируется |
353
+ | `title`, `subtitle`, `slotAfterTitle`, `onBackButtonClick`, `media`, `content`, `footer` | используется | используется |
354
+ | `open`, `onClose`, `showBlackout`, `container`, `closeOnPopstate`, `className`, `rootClassName` | используется | используется |
355
+
356
+ Подробнее о модели адаптивности — **Adaptive**.
357
+
358
+ ## DrawerCustom
359
+
360
+ Низкоуровневая сборка Drawer — полный контроль над разметкой через Header / Body / Footer субкомпоненты.
361
+
362
+ `DrawerCustom` — низкоуровневая версия `Drawer`, которая не диктует структуру содержимого. Вы сами компонуете шапку, тело и футер из субкомпонентов `DrawerCustom.Header`, `.Body`, `.Footer` или собственной разметки.
363
+
364
+ Используйте `DrawerCustom`, когда стандартной шапки из `Drawer` недостаточно — например, нужна своя раскладка заголовка с несколькими действиями, кастомный футер с группами кнопок или нестандартный порядок секций.
365
+
366
+ ### Когда использовать
367
+
368
+ - Стандартная шапка / футер из `Drawer` не подходят — нужна своя разметка.
369
+ - Сложная раскладка нескольких секций внутри одной панели.
370
+ - Кастомные слоты (например, фиксированный поиск между шапкой и телом).
371
+
372
+ Во всех остальных случаях предпочтительнее `Drawer` — он дешевле в поддержке и даёт консистентные отступы.
373
+
374
+ ### Анатомия
375
+
376
+ #### Position
377
+ Сторона, с которой выезжает панель: `right` — стандартный side-panel (по умолчанию), `left` — для навигации и фильтров, `top`/`bottom` — для уведомлений и bottom-sheets на мобильных.
378
+
379
+ #### Width
380
+ Предустановленная ширина панели для `position: left | right`: `s` — для узких форм и фильтров, `m` — дефолт, `l` — для сложных форм и просмотрщиков. Также принимает число или строку CSS для точного контроля.
381
+
382
+ ### Примеры использования
383
+
384
+ #### Ручная композиция
385
+
386
+ Header + Body + Footer собираются вручную.
387
+
388
+ ```tsx
389
+ import { Button } from '@cloud-ru/ds-button';
390
+ import { DrawerCustom } from '@cloud-ru/ds-drawer';
391
+ import { useState } from 'react';
392
+
393
+ export function CustomComposition() {
394
+ const [open, setOpen] = useState(false);
395
+ const close = () => setOpen(false);
396
+
397
+ return (
398
+ <>
399
+ <Button label='Открыть custom drawer' appearance='primary' view='filled' onClick={() => setOpen(true)} />
400
+ <DrawerCustom open={open} position='right' width='s' onClose={close}>
401
+ <DrawerCustom.Header title='Ручная композиция' subtitle='Header + Body + Footer собираются вручную.' />
402
+ <DrawerCustom.Body
403
+ content={
404
+ <div style={{ padding: 24 }}>
405
+ <p>Тело Drawer собирается из произвольной разметки.</p>
406
+ <p>Скролл включается автоматически при большом содержимом.</p>
407
+ </div>
408
+ }
409
+ />
410
+ <DrawerCustom.Footer>
411
+ <div style={{ display: 'flex', gap: 8, justifyContent: 'flex-end' }}>
412
+ <Button label='Закрыть' appearance='neutral' view='outline' onClick={close} />
413
+ <Button label='Подтвердить' appearance='primary' view='filled' onClick={close} />
414
+ </div>
415
+ </DrawerCustom.Footer>
416
+ </DrawerCustom>
417
+ </>
418
+ );
419
+ }
420
+ ```
421
+
422
+ ### Props
423
+
424
+ **DrawerCustomProps**
425
+
426
+ | Prop | Type | Default | Description |
427
+ |------|------|---------|-------------|
428
+ | `children` | `string \| number \| boolean \| ReactElement<any, string \| JSXElementConstructor<any>> \| Iterable<ReactNode> \| ReactPortal \| null \| undefined` | — | |
429
+ | `className` | `string` | — | CSS-класс для элемента с контентом |
430
+ | `closeOnPopstate` | `boolean` | — | Закрывать дровер при перемещении по истории браузера |
431
+ | `container` | `string \| HTMLElement` | — | Контейнер в котором будет рендерится Drawer. По-умолчанию - body |
432
+ | `data-test-id` | `string` | — | |
433
+ | `disableMotions` | `boolean` | `false` | Отключить анимации |
434
+ | `footer` | `ReactElement<any, string \| JSXElementConstructor<any>>` | — | Футер |
435
+ | `heightAuto` | `boolean` | `false` | Высота панели по контенту (только при `position: "top" \| "bottom"`). |
436
+ | `nestedDrawer` | `DrawerCustomProps` | — | Вложенный Drawer |
437
+ | `onClose` | `() => void` | — | Колбэк закрытия |
438
+ | `open` | `boolean` | — | Управление состоянием показан/не показан. |
439
+ | `position` | `"bottom"` \| `"left"` \| `"right"` \| `"top"` | — | Расположение |
440
+ | `push` | `boolean \| PushConfig` | — | Смещение при открытии "вложенного" компонента |
441
+ | `resizable` | `{ min: number; max?: number; default?: number; onResize?: ((width: number) => void) \| undefined; onResizeEnd?: ((width: number) => void) \| undefined; draggerTooltip?: string \| undefined; } \| undefined` | `'s'` | Ширина (только при position: "left" \| "right") |
442
+ | `rootClassName` | `string` | — | CSS-класс для корневого элемента |
443
+ | `safeArea` | `boolean` | `true` | Резервировать ли место под iOS notch / home-indicator и Android nav-bar. Реализовано паддингом <br/> на `.content` через `env(safe-area-inset-*)`: на устройстве без выреза/индикатора (и на desktop) <br/> inset = 0, поэтому никакого «лишнего» отступа не появляется; на notched-устройстве — ровно нужный. <br/> Верхний отступ добавляется только когда sheet раскрыт на полный вьюпорт (его верх под notch). |
444
+ | `showBlackout` | `boolean` | `true` | Отображение темной подложки |
445
+ | `showButtonClosed` | `boolean` | `true` | Отображение кнопки закрытия в шапке дровера |
446
+ | `snapPoints` | `SnapPoint` | — | Массив фиксированных позиций sheet'а от меньшей к большей. По дефолту `undefined` — <br/> sheet `height: auto` с одним snap'ом по высоте контента. <br/> Пример: `[0.5, 1]` — sheet открывается на половину экрана, drag вверх раскрывает <br/> до full-viewport; drag вниз ниже `0.5` ведёт к закрытию. <br/> Контракт массива (движок не сортирует и не дедуплицирует — порядок и различимость на <br/> стороне потребителя): <br/> - строго по возрастанию: индекс `0` — самая компактная позиция, последний — top / expanded; <br/> - значения должны резолвиться в различные высоты (`['50%', 0.5]` на типичном вьюпорте дадут <br/> одну высоту → дубль-индекс будет недостижим свайпом); <br/> - `'fit-content'` имеет смысл только как ЕДИНСТВЕННЫЙ snap (без `snapPoints`); внутри массива <br/> фиксированных позиций его «контентная» высота не определена. |
447
+ | `swipeEnabled` | `boolean` | `true` | Включает swipe-down для закрытия / swipe-up для раскрытия на следующий snap-point. <br/> При `swipeEnabled=false` snap-point по-прежнему можно переключить через controlled `snapIndex` prop'ом. |
448
+ | `width` | `Width` | `'s'` | Ширина (только при position: "left" \| "right") |
449
+
450
+ ##### Related types
451
+
452
+ **DrawerCustomProps**
453
+
454
+ | Prop | Type | Default | Description |
455
+ |------|------|---------|-------------|
456
+ | `children` | `string \| number \| boolean \| ReactElement<any, string \| JSXElementConstructor<any>> \| Iterable<ReactNode> \| ReactPortal \| null \| undefined` | — | |
457
+ | `className` | `string \| undefined` | — | CSS-класс для элемента с контентом |
458
+ | `closeOnPopstate` | `boolean \| undefined` | — | Закрывать дровер при перемещении по истории браузера |
459
+ | `container` | `string \| HTMLElement \| undefined` | — | Контейнер в котором будет рендерится Drawer. По-умолчанию - body |
460
+ | `data-test-id` | `string \| undefined` | — | |
461
+ | `disableMotions` | `boolean \| undefined` | — | Отключить анимации |
462
+ | `footer` | `ReactElement<any, string \| JSXElementConstructor<any>> \| undefined` | — | Футер |
463
+ | `heightAuto` | `boolean \| undefined` | — | Высота панели по контенту (только при `position: "top" \| "bottom"`). |
464
+ | `nestedDrawer` | `DrawerCustomProps` | — | Вложенный Drawer |
465
+ | `onClose` | `() => void` | — | Колбэк закрытия |
466
+ | `open` | `boolean` | — | Управление состоянием показан/не показан. |
467
+ | `position` | `"bottom"` \| `"left"` \| `"right"` \| `"top"` | — | Расположение |
468
+ | `push` | `boolean \| PushConfig \| undefined` | — | Смещение при открытии "вложенного" компонента |
469
+ | `resizable` | `{ min: number; max?: number; default?: number; onResize?: (width: number) => void; onResizeEnd?: (width: number) => void; draggerTooltip?: string; } \| undefined` | — | Ширина (только при position: "left" \| "right") |
470
+ | `rootClassName` | `string \| undefined` | — | CSS-класс для корневого элемента |
471
+ | `safeArea` | `boolean \| undefined` | — | Резервировать ли место под iOS notch / home-indicator и Android nav-bar. Реализовано паддингом <br/> на `.content` через `env(safe-area-inset-*)`: на устройстве без выреза/индикатора (и на desktop) <br/> inset = 0, поэтому никакого «лишнего» отступа не появляется; на notched-устройстве — ровно нужный. <br/> Верхний отступ добавляется только когда sheet раскрыт на полный вьюпорт (его верх под notch). |
472
+ | `showBlackout` | `boolean \| undefined` | — | Отображение темной подложки |
473
+ | `showButtonClosed` | `boolean \| undefined` | — | Отображение кнопки закрытия в шапке дровера |
474
+ | `snapPoints` | `SnapPoint` | — | Массив фиксированных позиций sheet'а от меньшей к большей. По дефолту `undefined` — <br/> sheet `height: auto` с одним snap'ом по высоте контента. <br/> Пример: `[0.5, 1]` — sheet открывается на половину экрана, drag вверх раскрывает <br/> до full-viewport; drag вниз ниже `0.5` ведёт к закрытию. <br/> Контракт массива (движок не сортирует и не дедуплицирует — порядок и различимость на <br/> стороне потребителя): <br/> - строго по возрастанию: индекс `0` — самая компактная позиция, последний — top / expanded; <br/> - значения должны резолвиться в различные высоты (`['50%', 0.5]` на типичном вьюпорте дадут <br/> одну высоту → дубль-индекс будет недостижим свайпом); <br/> - `'fit-content'` имеет смысл только как ЕДИНСТВЕННЫЙ snap (без `snapPoints`); внутри массива <br/> фиксированных позиций его «контентная» высота не определена. |
475
+ | `swipeEnabled` | `boolean \| undefined` | — | Включает swipe-down для закрытия / swipe-up для раскрытия на следующий snap-point. <br/> При `swipeEnabled=false` snap-point по-прежнему можно переключить через controlled `snapIndex` prop'ом. |
476
+ | `width` | `Width` | — | Ширина (только при position: "left" \| "right") |
477
+
478
+ - `Position` = `"bottom"` \| `"left"` \| `"right"` \| `"top"`
479
+
480
+ - `Width` = `"l"` \| `"m"` \| `"s"`
@@ -0,0 +1,6 @@
1
+ import { DrawerProps } from './types';
2
+ /**
3
+ * Адаптивный Drawer. Раскладка из `AdaptiveProvider`: на `mobile` — `BottomSheet`, иначе —
4
+ * desktop-панель. Форс — `<AdaptiveProvider layoutType=…>` / `withLayoutType`.
5
+ */
6
+ export declare function Drawer(props: DrawerProps): import("react/jsx-runtime").JSX.Element;
@@ -0,0 +1,15 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.Drawer = Drawer;
4
+ const jsx_runtime_1 = require("react/jsx-runtime");
5
+ const adaptive_1 = require("@cloud-ru/ds-adaptive");
6
+ const DesktopDrawer_1 = require("../../helperComponents/DesktopDrawer");
7
+ const MobileDrawer_1 = require("../../helperComponents/MobileDrawer");
8
+ /**
9
+ * Адаптивный Drawer. Раскладка из `AdaptiveProvider`: на `mobile` — `BottomSheet`, иначе —
10
+ * desktop-панель. Форс — `<AdaptiveProvider layoutType=…>` / `withLayoutType`.
11
+ */
12
+ function Drawer(props) {
13
+ const { layoutType } = (0, adaptive_1.useAdaptiveLayout)();
14
+ return (0, adaptive_1.isMobileLayout)(layoutType) ? (0, jsx_runtime_1.jsx)(MobileDrawer_1.MobileDrawer, { ...props }) : (0, jsx_runtime_1.jsx)(DesktopDrawer_1.DesktopDrawer, { ...props });
15
+ }
@@ -0,0 +1 @@
1
+ export declare const NESTED_DRAWER_PUSH_DISTANCE = 24;
@@ -0,0 +1,4 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.NESTED_DRAWER_PUSH_DISTANCE = void 0;
4
+ exports.NESTED_DRAWER_PUSH_DISTANCE = 24;
@@ -0,0 +1,2 @@
1
+ export * from './Drawer';
2
+ export * from './types';
@@ -0,0 +1,18 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __exportStar = (this && this.__exportStar) || function(m, exports) {
14
+ for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
15
+ };
16
+ Object.defineProperty(exports, "__esModule", { value: true });
17
+ __exportStar(require("./Drawer"), exports);
18
+ __exportStar(require("./types"), exports);
@@ -0,0 +1,19 @@
1
+ .drawer{
2
+ box-sizing:border-box;
3
+ }
4
+
5
+ .safeAreaTop{
6
+ display:flex;
7
+ flex-direction:column;
8
+ align-items:flex-start;
9
+ align-self:stretch;
10
+ padding-top:var(--sn-drawer-anatomy-container-safeArea-paddingTop, var(--sn-density-spacing-interval-l, var(--sn-primitive-dimension-24, 24px)));
11
+ }
12
+
13
+ .safeAreaBottom{
14
+ display:flex;
15
+ flex-direction:column;
16
+ align-items:flex-start;
17
+ align-self:stretch;
18
+ padding-bottom:var(--sn-drawer-anatomy-container-safeArea-paddingBottom, var(--sn-density-spacing-interval-s, var(--sn-primitive-dimension-8, 8px)));
19
+ }
@@ -0,0 +1,35 @@
1
+ import { BottomSheetActionButton, BottomSheetProps, FooterActionsOrientation } from '@cloud-ru/ds-bottom-sheet';
2
+ import { WithSupportProps } from '@cloud-ru/ds-utils';
3
+ import { ReactElement, ReactNode } from 'react';
4
+ import { DialogBodyProps, DialogHeaderProps } from '../../helperComponents';
5
+ import { DrawerCustomProps } from '../DrawerCustom';
6
+ /**
7
+ * Адаптивный Drawer: desktop — боковая/верхняя/нижняя панель, `mobile` — `BottomSheet`.
8
+ *
9
+ * Только desktop: `position`, `width`, `heightAuto`, `nestedDrawer` (на mobile игнорируются).
10
+ * Только mobile: `swipeEnabled`, `snapPoints`, `snapIndex`, `onSnapIndexChange`, `safeArea`
11
+ * (на desktop-панели игнорируются — это анатомия `BottomSheet`).
12
+ */
13
+ export type DrawerProps = WithSupportProps<Omit<DrawerCustomProps, 'nestedDrawer' | 'push' | 'resizable'> & Pick<BottomSheetProps, 'swipeEnabled' | 'snapPoints' | 'snapIndex' | 'onSnapIndexChange' | 'safeArea'> & Pick<DialogHeaderProps, 'title' | 'slotAfterTitle' | 'onBackButtonClick'> & Pick<DialogBodyProps, 'content'> & {
14
+ /** Текстовая строка-подзаголовок под title. */
15
+ subtitle?: ReactNode;
16
+ /** CSS-класс */
17
+ className?: string;
18
+ /** Медиа-контент */
19
+ media?: ReactNode;
20
+ /** Основная кнопка действия — пропсы `Button` (дефолт `view='filled'`, `appearance='primary'`). */
21
+ approveButton?: BottomSheetActionButton;
22
+ /** Кнопка отмены — объект пропсов `Button` (по умолчанию `view='outline'`, `appearance='neutral'`). */
23
+ cancelButton?: BottomSheetActionButton;
24
+ /** Дополнительная (третья) кнопка — пропсы `Button` (дефолт `view='simple'`, `appearance='neutral'`). */
25
+ additionalButton?: BottomSheetActionButton;
26
+ /**
27
+ * Ориентация кнопок футера. Применяется только при двух кнопках; игнорируется при заданном `footer`.
28
+ * @default 'horizontal'
29
+ */
30
+ footerActionsOrientation?: FooterActionsOrientation;
31
+ /** Произвольный футер. Приоритетнее `approveButton` / `cancelButton` / `additionalButton`. */
32
+ footer?: ReactNode;
33
+ /** Вложенный Drawer */
34
+ nestedDrawer?: ReactElement<DrawerProps>;
35
+ }>;
@@ -0,0 +1,2 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
@@ -0,0 +1,15 @@
1
+ import { DialogBody, DialogBodyProps, DialogFooterProps, DialogHeaderProps } from '../../helperComponents';
2
+ import { DrawerCustomProps } from './types';
3
+ /**
4
+ * Адаптивный low-level `DrawerCustom`: на `mobile` — `BottomSheetCustom`, иначе — `DrawerFrame`.
5
+ * Композиция одна (surface-aware слоты `.Header/.Body/.Footer`). Форс — `withLayoutType`.
6
+ */
7
+ export declare function DrawerCustom(props: DrawerCustomProps): import("react/jsx-runtime").JSX.Element;
8
+ export declare namespace DrawerCustom {
9
+ type HeaderProps = DialogHeaderProps;
10
+ type BodyProps = DialogBodyProps;
11
+ type FooterProps = DialogFooterProps;
12
+ const Header: typeof import("@cloud-ru/ds-bottom-sheet").SheetHeader;
13
+ const Body: typeof DialogBody;
14
+ const Footer: typeof import("@cloud-ru/ds-bottom-sheet").SheetFooter;
15
+ }