@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
@@ -0,0 +1,6 @@
1
+ import { DrawerProps } from './types.js';
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,12 @@
1
+ import { jsx as _jsx } from "react/jsx-runtime";
2
+ import { isMobileLayout, useAdaptiveLayout } from '@cloud-ru/ds-adaptive';
3
+ import { DesktopDrawer } from '../../helperComponents/DesktopDrawer/index.js';
4
+ import { MobileDrawer } from '../../helperComponents/MobileDrawer/index.js';
5
+ /**
6
+ * Адаптивный Drawer. Раскладка из `AdaptiveProvider`: на `mobile` — `BottomSheet`, иначе —
7
+ * desktop-панель. Форс — `<AdaptiveProvider layoutType=…>` / `withLayoutType`.
8
+ */
9
+ export function Drawer(props) {
10
+ const { layoutType } = useAdaptiveLayout();
11
+ return isMobileLayout(layoutType) ? _jsx(MobileDrawer, { ...props }) : _jsx(DesktopDrawer, { ...props });
12
+ }
@@ -0,0 +1 @@
1
+ export declare const NESTED_DRAWER_PUSH_DISTANCE = 24;
@@ -0,0 +1 @@
1
+ export const NESTED_DRAWER_PUSH_DISTANCE = 24;
@@ -0,0 +1,2 @@
1
+ export * from './Drawer.js';
2
+ export * from './types.js';
@@ -0,0 +1,2 @@
1
+ export * from './Drawer.js';
2
+ export * from './types.js';
@@ -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/index.js';
5
+ import { DrawerCustomProps } from '../DrawerCustom/index.js';
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 @@
1
+ export {};
@@ -0,0 +1,15 @@
1
+ import { DialogBody, DialogBodyProps, DialogFooterProps, DialogHeaderProps } from '../../helperComponents/index.js';
2
+ import { DrawerCustomProps } from './types.js';
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-popup-private").PopupHeader;
13
+ const Body: typeof DialogBody;
14
+ const Footer: typeof import("@cloud-ru/ds-popup-private").PopupFooter;
15
+ }
@@ -0,0 +1,70 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { isMobileLayout, useAdaptiveLayout } from '@cloud-ru/ds-adaptive';
3
+ import { BottomSheetCustom, OVERLAY_SURFACE, OverlaySurfaceProvider } from '@cloud-ru/ds-bottom-sheet';
4
+ import { PopupCloseButton } from '@cloud-ru/ds-popup-private';
5
+ import { usePortalContext } from '@cloud-ru/ds-portal-context';
6
+ import { extractSupportProps, useModalOpenState } from '@cloud-ru/ds-utils';
7
+ import RcDrawerImport from '@rc-component/drawer';
8
+ import cn from 'classnames';
9
+ import { useMemo } from 'react';
10
+ import { TEST_IDS, WIDTH_AS_VALUES } from '../../constants.js';
11
+ import { DialogBody, DialogFooter, DialogHeader, } from '../../helperComponents/index.js';
12
+ import { interopDefault } from '../../utils/interopDefault.js';
13
+ import { motionProps } from './constants.js';
14
+ import { useDrawerFocusTrap } from './hooks/index.js';
15
+ import { useDrawerResize } from './hooks/useDrawerResize.js';
16
+ import styles from './styles.module.css';
17
+ const RcDrawer = interopDefault(RcDrawerImport);
18
+ /** Desktop-frame дровера: rc-drawer + position/width/push/nestedDrawer + close-button. Surface='drawer'. */
19
+ function DrawerFrame(props) {
20
+ const { showBlackout = true, showButtonClosed = true, open, onClose, rootClassName, className, push, container, children, nestedDrawer, closeOnPopstate, position, heightAuto, width: widthProp, resizable: resizableProp, disableMotions = false, ...rest } = props;
21
+ const heightAutoVertical = Boolean(heightAuto && (position === 'top' || position === 'bottom'));
22
+ const resizable = heightAutoVertical ? undefined : resizableProp;
23
+ const { tooltip, checkElement, width: userWidth, resizable: drawerResizable, } = useDrawerResize({
24
+ resizable,
25
+ position,
26
+ });
27
+ const width = resizable ? (userWidth ?? widthProp) : widthProp;
28
+ const isPredefinedWidth = typeof width === 'string' && WIDTH_AS_VALUES.includes(width);
29
+ // Wrapper height/inset задаёт CSS по `[data-height-auto]`; здесь только стили секции.
30
+ const drawerPanelStyle = useMemo(() => {
31
+ if (!heightAutoVertical) {
32
+ return undefined;
33
+ }
34
+ return {
35
+ height: 'auto',
36
+ maxHeight: '100%',
37
+ minHeight: 0,
38
+ overflow: 'hidden',
39
+ };
40
+ }, [heightAutoVertical]);
41
+ // Esc обрабатывает rc-drawer (`keyboard={showBlackout}`) — он уважает стек порталов и закрывает
42
+ // только верхний дровер. `CloseWatcher` подключаем лишь там, где rc-drawer Esc не слушает
43
+ // (`showBlackout=false`): иначе два независимых обработчика закрывают и вложенный, и родительский.
44
+ useModalOpenState(open, onClose, { closeOnPopstate, closeByCloseWatcher: !showBlackout });
45
+ const focusTrapRef = useDrawerFocusTrap(Boolean(open));
46
+ // Без явного container портал идёт через PortalContextProvider (как у Modal): скоуп к локальному
47
+ // элементу и отказ от body-level scroll lock, который rc-drawer ставит при рендере в document.body.
48
+ const portalContextRef = usePortalContext();
49
+ const resolvedContainer = container ?? (portalContextRef.current ? () => portalContextRef.current : undefined);
50
+ return (_jsx(RcDrawer, { resizable: drawerResizable, panelRef: checkElement, mask: showBlackout, maskClosable: showBlackout, keyboard: showBlackout, "data-showblackout": showBlackout, open: open, onClose: onClose, push: push, getContainer: resolvedContainer, placement: position, destroyOnHidden: true, rootClassName: cn(styles.drawerRoot, rootClassName), style: drawerPanelStyle, classNames: {
51
+ section: cn(styles.drawer, className),
52
+ }, size: isPredefinedWidth ? 'null' : width, ...extractSupportProps(rest), "data-content-wrapper": true, "data-position": position, "data-width": isPredefinedWidth ? width : undefined, "data-height-auto": heightAutoVertical ? true : undefined, "data-acrylic-appearance": 'neutral', "data-acrylic-level": '1Level', prefixCls: 'snack-rc-drawer', ...(disableMotions ? {} : motionProps), children: _jsxs("div", { ref: focusTrapRef, className: styles.focusScope, children: [showButtonClosed && (_jsx("div", { className: styles.showButtonClosedWrapper, children: _jsx(PopupCloseButton, { className: styles.showButtonClosed, "aria-label": 'close drawer', "data-test-id": TEST_IDS.closeButton, onClick: onClose }) })), _jsx(OverlaySurfaceProvider, { surface: OVERLAY_SURFACE.Drawer, bodyHeightAuto: heightAutoVertical, children: children }), tooltip, nestedDrawer] }) }));
53
+ }
54
+ /**
55
+ * Адаптивный low-level `DrawerCustom`: на `mobile` — `BottomSheetCustom`, иначе — `DrawerFrame`.
56
+ * Композиция одна (surface-aware слоты `.Header/.Body/.Footer`). Форс — `withLayoutType`.
57
+ */
58
+ export function DrawerCustom(props) {
59
+ const { layoutType } = useAdaptiveLayout();
60
+ if (isMobileLayout(layoutType)) {
61
+ const { open, onClose, children, className, rootClassName, container, closeOnPopstate, showBlackout, snapPoints, swipeEnabled, safeArea, disableMotions, ...rest } = props;
62
+ return (_jsx(BottomSheetCustom, { open: open, onClose: onClose, container: container, className: className, rootClassName: rootClassName, closeOnPopstate: closeOnPopstate, showBackdrop: showBlackout, snapPoints: snapPoints, swipeEnabled: swipeEnabled, safeArea: safeArea, disableMotions: disableMotions, ...extractSupportProps(rest), children: children }));
63
+ }
64
+ return _jsx(DrawerFrame, { ...props });
65
+ }
66
+ (function (DrawerCustom) {
67
+ DrawerCustom.Header = DialogHeader;
68
+ DrawerCustom.Body = DialogBody;
69
+ DrawerCustom.Footer = DialogFooter;
70
+ })(DrawerCustom || (DrawerCustom = {}));
@@ -0,0 +1,4 @@
1
+ import { DrawerProps } from '@rc-component/drawer';
2
+ export declare const maskMotion: DrawerProps['maskMotion'];
3
+ export declare const motion: DrawerProps['motion'];
4
+ export declare const motionProps: Partial<DrawerProps>;
@@ -0,0 +1,13 @@
1
+ export const maskMotion = {
2
+ motionAppear: true,
3
+ motionName: 'maskMotion',
4
+ };
5
+ export const motion = placement => ({
6
+ motionAppear: true,
7
+ motionDeadline: 300,
8
+ motionName: `panelMotion-${placement}`,
9
+ });
10
+ export const motionProps = {
11
+ maskMotion,
12
+ motion,
13
+ };
@@ -0,0 +1 @@
1
+ export * from './useDrawerFocusTrap.js';
@@ -0,0 +1 @@
1
+ export * from './useDrawerFocusTrap.js';
@@ -0,0 +1,9 @@
1
+ /**
2
+ * Focus scope для Drawer-панели:
3
+ * - запоминает activeElement до открытия и возвращает фокус после закрытия;
4
+ * - устанавливает фокус на контейнер при открытии (без прыжка на первый focusable);
5
+ * - цикличный Tab / Shift+Tab внутри контейнера.
6
+ *
7
+ * Возвращает ref, который нужно повесить на корневой DOM-узел панели.
8
+ */
9
+ export declare function useDrawerFocusTrap(active: boolean): import("react").RefObject<HTMLDivElement>;
@@ -0,0 +1,80 @@
1
+ import { useLayoutEffect } from '@cloud-ru/ds-utils';
2
+ import { useEffect, useRef } from 'react';
3
+ import { tabbable } from 'tabbable';
4
+ /**
5
+ * Focus scope для Drawer-панели:
6
+ * - запоминает activeElement до открытия и возвращает фокус после закрытия;
7
+ * - устанавливает фокус на контейнер при открытии (без прыжка на первый focusable);
8
+ * - цикличный Tab / Shift+Tab внутри контейнера.
9
+ *
10
+ * Возвращает ref, который нужно повесить на корневой DOM-узел панели.
11
+ */
12
+ export function useDrawerFocusTrap(active) {
13
+ const containerRef = useRef(null);
14
+ useLayoutEffect(() => {
15
+ if (!active) {
16
+ return;
17
+ }
18
+ // Панель rc-drawer монтируется отдельным коммитом, поэтому `containerRef` здесь ещё пуст —
19
+ // возврат фокуса от него не зависит и на его отсутствие завязываться нельзя.
20
+ // Начальный фокус ставит сам rc-drawer (autoFocus на panelRef).
21
+ const previous = document.activeElement instanceof HTMLElement ? document.activeElement : null;
22
+ return () => {
23
+ if (previous &&
24
+ previous !== document.body &&
25
+ previous !== document.documentElement &&
26
+ document.documentElement.contains(previous)) {
27
+ previous.focus({ preventScroll: true });
28
+ }
29
+ };
30
+ }, [active]);
31
+ useEffect(() => {
32
+ if (!active) {
33
+ return;
34
+ }
35
+ const handleKeyDown = (event) => {
36
+ if (event.key !== 'Tab') {
37
+ return;
38
+ }
39
+ const container = containerRef.current;
40
+ if (!container) {
41
+ return;
42
+ }
43
+ const activeElement = document.activeElement;
44
+ if (!(activeElement instanceof HTMLElement)) {
45
+ return;
46
+ }
47
+ const focusInsideDialog = container.contains(activeElement);
48
+ const nodes = tabbable(container);
49
+ if (nodes.length === 0) {
50
+ event.preventDefault();
51
+ return;
52
+ }
53
+ const first = nodes[0];
54
+ const last = nodes[nodes.length - 1];
55
+ if (!focusInsideDialog) {
56
+ // Фокус ушёл наружу (rc-drawer sentinel / body) — вернём внутрь.
57
+ event.preventDefault();
58
+ if (event.shiftKey) {
59
+ last?.focus();
60
+ }
61
+ else {
62
+ first?.focus();
63
+ }
64
+ return;
65
+ }
66
+ if (event.shiftKey && activeElement === first) {
67
+ event.preventDefault();
68
+ last?.focus();
69
+ return;
70
+ }
71
+ if (!event.shiftKey && activeElement === last) {
72
+ event.preventDefault();
73
+ first?.focus();
74
+ }
75
+ };
76
+ document.addEventListener('keydown', handleKeyDown, true);
77
+ return () => document.removeEventListener('keydown', handleKeyDown, true);
78
+ }, [active]);
79
+ return containerRef;
80
+ }
@@ -0,0 +1,15 @@
1
+ import { DrawerProps } from '@rc-component/drawer';
2
+ import { DrawerCustomProps } from '../types.js';
3
+ type UseDrawerResizeParams = Pick<DrawerCustomProps, 'position' | 'resizable'>;
4
+ type UseDrawerResizeResult = {
5
+ /** JSX Тултипа для ползунка изменения ширины */
6
+ tooltip: React.ReactNode;
7
+ /** Функция поиска ползунка в DOM-дереве. Стоит вызывать тогда, когда он отрендерен. */
8
+ checkElement: () => void;
9
+ /** Ресайз-хендлер для rc-drawer */
10
+ resizable: DrawerProps['resizable'];
11
+ /** Ширина ползунка в пикселях */
12
+ width?: number;
13
+ };
14
+ export declare function useDrawerResize({ position, resizable: resizableProp }: UseDrawerResizeParams): UseDrawerResizeResult;
15
+ export {};
@@ -0,0 +1,47 @@
1
+ import { jsx as _jsx } from "react/jsx-runtime";
2
+ import { Tooltip } from '@cloud-ru/ds-tooltip';
3
+ import { isBrowser } from '@cloud-ru/ds-utils';
4
+ import { useCallback, useMemo, useRef, useState } from 'react';
5
+ const DRAGGER_SELECTOR = '.rc-drawer-resizable-dragger';
6
+ export function useDrawerResize({ position, resizable: resizableProp }) {
7
+ const targetRef = useRef(null);
8
+ const targetRefCallbackRef = useRef();
9
+ const [width, setWidth] = useState(resizableProp?.default);
10
+ const [muted, setMuted] = useState(false);
11
+ const [open, setOpen] = useState(false);
12
+ const draggerTooltip = resizableProp?.draggerTooltip;
13
+ const checkElement = useCallback(() => {
14
+ if (isBrowser() && draggerTooltip) {
15
+ const element = document.querySelector(DRAGGER_SELECTOR);
16
+ if (!element)
17
+ return;
18
+ targetRefCallbackRef.current ? targetRefCallbackRef.current(element) : (targetRef.current = element);
19
+ }
20
+ }, [draggerTooltip]);
21
+ const tooltip = draggerTooltip ? (_jsx(Tooltip, { placement: position, offset: 4, tip: draggerTooltip, open: open && !muted, onOpenChange: setOpen, children: ({ ref: targetRefCallback }) => {
22
+ if (!targetRefCallback)
23
+ return null;
24
+ targetRef.current ? targetRefCallback(targetRef.current) : (targetRefCallbackRef.current = targetRefCallback);
25
+ } })) : null;
26
+ const resizable = useMemo(() => resizableProp
27
+ ? {
28
+ onResize: value => {
29
+ if (value < (resizableProp?.min || 0))
30
+ return;
31
+ if (value > (resizableProp?.max || Infinity))
32
+ return;
33
+ setWidth(value);
34
+ resizableProp?.onResize?.(value);
35
+ },
36
+ onResizeEnd: () => {
37
+ setOpen(false);
38
+ setMuted(false);
39
+ resizableProp?.onResizeEnd?.(width ?? 0);
40
+ },
41
+ onResizeStart: () => {
42
+ setMuted(true);
43
+ },
44
+ }
45
+ : undefined, [resizableProp, width]);
46
+ return { tooltip, checkElement, resizable, width };
47
+ }
@@ -0,0 +1,2 @@
1
+ export * from './DrawerCustom.js';
2
+ export type { DrawerCustomProps } from './types.js';
@@ -0,0 +1 @@
1
+ export * from './DrawerCustom.js';
@@ -0,0 +1,93 @@
1
+ .rc-drawer.rc-drawer-open .rc-drawer-mask{
2
+ opacity:unset;
3
+ transition:none;
4
+ }
5
+
6
+ .rc-drawer .rc-drawer-mask.maskMotion{
7
+ opacity:unset;
8
+ }
9
+ .rc-drawer .rc-drawer-mask.maskMotion-enter, .rc-drawer .rc-drawer-mask.maskMotion-appear{
10
+ opacity:0;
11
+ }
12
+ .rc-drawer .rc-drawer-mask.maskMotion-enter-active, .rc-drawer .rc-drawer-mask.maskMotion-appear-active{
13
+ opacity:1;
14
+ transition:opacity 0.2s;
15
+ }
16
+ .rc-drawer .rc-drawer-mask.maskMotion-leave{
17
+ opacity:1;
18
+ }
19
+ .rc-drawer .rc-drawer-mask.maskMotion-leave-active{
20
+ opacity:0;
21
+ transition:opacity 0.1s;
22
+ }
23
+ .rc-drawer .rc-drawer-content-wrapper.panelMotion{
24
+ transform:unset;
25
+ }
26
+ .rc-drawer .rc-drawer-content-wrapper.panelMotion-left-enter-start, .rc-drawer .rc-drawer-content-wrapper.panelMotion-left-appear-start, .rc-drawer .rc-drawer-content-wrapper.panelMotion-left-leave-start{
27
+ transition:none !important;
28
+ }
29
+ .rc-drawer .rc-drawer-content-wrapper.panelMotion-left-enter, .rc-drawer .rc-drawer-content-wrapper.panelMotion-left-appear{
30
+ transform:translateX(-100%);
31
+ }
32
+ .rc-drawer .rc-drawer-content-wrapper.panelMotion-left-enter-active, .rc-drawer .rc-drawer-content-wrapper.panelMotion-left-appear-active{
33
+ transform:translateX(0);
34
+ transition:transform 0.2s ease-in-out;
35
+ }
36
+ .rc-drawer .rc-drawer-content-wrapper.panelMotion-left-leave{
37
+ transform:translateX(0);
38
+ }
39
+ .rc-drawer .rc-drawer-content-wrapper.panelMotion-left-leave-active{
40
+ transform:translateX(-100%) !important;
41
+ transition:transform 0.1s ease-out;
42
+ }
43
+ .rc-drawer .rc-drawer-content-wrapper.panelMotion-right-enter-start, .rc-drawer .rc-drawer-content-wrapper.panelMotion-right-appear-start, .rc-drawer .rc-drawer-content-wrapper.panelMotion-right-leave-start{
44
+ transition:none !important;
45
+ }
46
+ .rc-drawer .rc-drawer-content-wrapper.panelMotion-right-enter, .rc-drawer .rc-drawer-content-wrapper.panelMotion-right-appear{
47
+ transform:translateX(100%);
48
+ }
49
+ .rc-drawer .rc-drawer-content-wrapper.panelMotion-right-enter-active, .rc-drawer .rc-drawer-content-wrapper.panelMotion-right-appear-active{
50
+ transform:translateX(0);
51
+ transition:transform 0.2s ease-in-out;
52
+ }
53
+ .rc-drawer .rc-drawer-content-wrapper.panelMotion-right-leave{
54
+ transform:translateX(0);
55
+ }
56
+ .rc-drawer .rc-drawer-content-wrapper.panelMotion-right-leave-active{
57
+ transform:translateX(100%) !important;
58
+ transition:transform 0.1s ease-out;
59
+ }
60
+ .rc-drawer .rc-drawer-content-wrapper.panelMotion-top-enter-start, .rc-drawer .rc-drawer-content-wrapper.panelMotion-top-appear-start, .rc-drawer .rc-drawer-content-wrapper.panelMotion-top-leave-start{
61
+ transition:none !important;
62
+ }
63
+ .rc-drawer .rc-drawer-content-wrapper.panelMotion-top-enter, .rc-drawer .rc-drawer-content-wrapper.panelMotion-top-appear{
64
+ transform:translateY(-100%);
65
+ }
66
+ .rc-drawer .rc-drawer-content-wrapper.panelMotion-top-enter-active, .rc-drawer .rc-drawer-content-wrapper.panelMotion-top-appear-active{
67
+ transform:translateY(0);
68
+ transition:transform 0.2s ease-in-out;
69
+ }
70
+ .rc-drawer .rc-drawer-content-wrapper.panelMotion-top-leave{
71
+ transform:translateY(0);
72
+ }
73
+ .rc-drawer .rc-drawer-content-wrapper.panelMotion-top-leave-active{
74
+ transform:translateY(-100%) !important;
75
+ transition:transform 0.1s ease-out;
76
+ }
77
+ .rc-drawer .rc-drawer-content-wrapper.panelMotion-bottom-enter-start, .rc-drawer .rc-drawer-content-wrapper.panelMotion-bottom-appear-start, .rc-drawer .rc-drawer-content-wrapper.panelMotion-bottom-leave-start{
78
+ transition:none !important;
79
+ }
80
+ .rc-drawer .rc-drawer-content-wrapper.panelMotion-bottom-enter, .rc-drawer .rc-drawer-content-wrapper.panelMotion-bottom-appear{
81
+ transform:translateY(100%);
82
+ }
83
+ .rc-drawer .rc-drawer-content-wrapper.panelMotion-bottom-enter-active, .rc-drawer .rc-drawer-content-wrapper.panelMotion-bottom-appear-active{
84
+ transform:translateY(0);
85
+ transition:transform 0.2s ease-in-out;
86
+ }
87
+ .rc-drawer .rc-drawer-content-wrapper.panelMotion-bottom-leave{
88
+ transform:translateY(0);
89
+ }
90
+ .rc-drawer .rc-drawer-content-wrapper.panelMotion-bottom-leave-active{
91
+ transform:translateY(100%) !important;
92
+ transition:transform 0.1s ease-out;
93
+ }