@cloud-ru/ds-drawer 1.0.1-preview-c048fbb9.0 → 1.0.1-preview-cd0cd6fb.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.
- package/dist/cjs/components/DrawerCustom/DrawerCustom.js +2 -1
- package/dist/cjs/components/DrawerCustom/hooks/index.d.ts +1 -0
- package/dist/cjs/components/DrawerCustom/hooks/index.js +1 -0
- package/dist/cjs/components/DrawerCustom/hooks/useOuterScrollLock.d.ts +8 -0
- package/dist/cjs/components/DrawerCustom/hooks/useOuterScrollLock.js +30 -0
- package/dist/esm/components/DrawerCustom/DrawerCustom.js +3 -2
- package/dist/esm/components/DrawerCustom/hooks/index.d.ts +1 -0
- package/dist/esm/components/DrawerCustom/hooks/index.js +1 -0
- package/dist/esm/components/DrawerCustom/hooks/useOuterScrollLock.d.ts +8 -0
- package/dist/esm/components/DrawerCustom/hooks/useOuterScrollLock.js +27 -0
- package/dist/tsconfig.cjs.tsbuildinfo +1 -1
- package/dist/tsconfig.esm.tsbuildinfo +1 -1
- package/package.json +4 -4
- package/src/components/DrawerCustom/DrawerCustom.tsx +8 -2
- package/src/components/DrawerCustom/hooks/index.ts +1 -0
- package/src/components/DrawerCustom/hooks/useOuterScrollLock.ts +36 -0
|
@@ -50,13 +50,14 @@ function DrawerFrame(props) {
|
|
|
50
50
|
// (`showBlackout=false`): иначе два независимых обработчика закрывают и вложенный, и родительский.
|
|
51
51
|
(0, utils_1.useModalOpenState)(open, onClose, { closeOnPopstate, closeByCloseWatcher: !showBlackout });
|
|
52
52
|
const focusTrapRef = (0, hooks_1.useDrawerFocusTrap)(Boolean(open));
|
|
53
|
+
const outerScrollLock = (0, hooks_1.useOuterScrollLock)(Boolean(open));
|
|
53
54
|
// Без явного container портал идёт через PortalContextProvider (как у Modal): скоуп к локальному
|
|
54
55
|
// элементу и отказ от body-level scroll lock, который rc-drawer ставит при рендере в document.body.
|
|
55
56
|
const portalContextRef = (0, portal_context_1.usePortalContext)();
|
|
56
57
|
const resolvedContainer = container ?? (portalContextRef.current ? () => portalContextRef.current : undefined);
|
|
57
58
|
return ((0, jsx_runtime_1.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: (0, classnames_1.default)(styles_module_scss_1.default.drawerRoot, rootClassName), style: drawerPanelStyle, classNames: {
|
|
58
59
|
section: (0, classnames_1.default)(styles_module_scss_1.default.drawer, className),
|
|
59
|
-
}, size: isPredefinedWidth ? 'null' : width, ...(0, utils_1.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 ? {} : constants_2.motionProps), children: (0, jsx_runtime_1.jsxs)(react_remove_scroll_1.RemoveScroll, { enabled: showBlackout, removeScrollBar: false, ref: focusTrapRef, className: styles_module_scss_1.default.focusScope, children: [showButtonClosed && ((0, jsx_runtime_1.jsx)("div", { className: styles_module_scss_1.default.showButtonClosedWrapper, children: (0, jsx_runtime_1.jsx)(popup_private_1.PopupCloseButton, { className: styles_module_scss_1.default.showButtonClosed, "aria-label": 'close drawer', "data-test-id": constants_1.TEST_IDS.closeButton, onClick: onClose }) })), (0, jsx_runtime_1.jsx)(bottom_sheet_1.OverlaySurfaceProvider, { surface: bottom_sheet_1.OVERLAY_SURFACE.Drawer, bodyHeightAuto: heightAutoVertical, children: children }), tooltip, nestedDrawer] }) }));
|
|
60
|
+
}, size: isPredefinedWidth ? 'null' : width, ...(0, utils_1.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 ? {} : constants_2.motionProps), children: (0, jsx_runtime_1.jsxs)(react_remove_scroll_1.RemoveScroll, { enabled: showBlackout || outerScrollLock, removeScrollBar: false, ref: focusTrapRef, className: styles_module_scss_1.default.focusScope, children: [showButtonClosed && ((0, jsx_runtime_1.jsx)("div", { className: styles_module_scss_1.default.showButtonClosedWrapper, children: (0, jsx_runtime_1.jsx)(popup_private_1.PopupCloseButton, { className: styles_module_scss_1.default.showButtonClosed, "aria-label": 'close drawer', "data-test-id": constants_1.TEST_IDS.closeButton, onClick: onClose }) })), (0, jsx_runtime_1.jsx)(bottom_sheet_1.OverlaySurfaceProvider, { surface: bottom_sheet_1.OVERLAY_SURFACE.Drawer, bodyHeightAuto: heightAutoVertical, children: children }), tooltip, nestedDrawer] }) }));
|
|
60
61
|
}
|
|
61
62
|
/**
|
|
62
63
|
* Адаптивный low-level `DrawerCustom`: на `mobile` — `BottomSheetCustom`, иначе — `DrawerFrame`.
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Есть ли снаружи чужой scroll-lock (модалка, из которой открыли дровер).
|
|
3
|
+
*
|
|
4
|
+
* Такой лок гасит колесо и touchmove вне своего поддерева, а дровер живёт в отдельном портале —
|
|
5
|
+
* без собственного лока поверх него тело дровера не прокручивается. Свой лок нужен только в этом
|
|
6
|
+
* случае: без блэкаута и без внешнего лока страница под дровером должна скроллиться.
|
|
7
|
+
*/
|
|
8
|
+
export declare function useOuterScrollLock(open: boolean): boolean;
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.useOuterScrollLock = useOuterScrollLock;
|
|
4
|
+
const utils_1 = require("@cloud-ru/ds-utils");
|
|
5
|
+
const react_1 = require("react");
|
|
6
|
+
/** Атрибут, которым `react-remove-scroll-bar` помечает `body` на время активного лока. */
|
|
7
|
+
const SCROLL_LOCK_ATTRIBUTE = 'data-scroll-locked';
|
|
8
|
+
/**
|
|
9
|
+
* Есть ли снаружи чужой scroll-lock (модалка, из которой открыли дровер).
|
|
10
|
+
*
|
|
11
|
+
* Такой лок гасит колесо и touchmove вне своего поддерева, а дровер живёт в отдельном портале —
|
|
12
|
+
* без собственного лока поверх него тело дровера не прокручивается. Свой лок нужен только в этом
|
|
13
|
+
* случае: без блэкаута и без внешнего лока страница под дровером должна скроллиться.
|
|
14
|
+
*/
|
|
15
|
+
function useOuterScrollLock(open) {
|
|
16
|
+
const [outerLock, setOuterLock] = (0, react_1.useState)(false);
|
|
17
|
+
(0, react_1.useEffect)(() => {
|
|
18
|
+
if (!open || !(0, utils_1.isBrowser)()) {
|
|
19
|
+
setOuterLock(false);
|
|
20
|
+
return;
|
|
21
|
+
}
|
|
22
|
+
// Свой лок дровера идёт с `removeScrollBar: false` и этот атрибут не ставит — самовозбуждения нет.
|
|
23
|
+
const readLock = () => setOuterLock(document.body.hasAttribute(SCROLL_LOCK_ATTRIBUTE));
|
|
24
|
+
readLock();
|
|
25
|
+
const observer = new MutationObserver(readLock);
|
|
26
|
+
observer.observe(document.body, { attributes: true, attributeFilter: [SCROLL_LOCK_ATTRIBUTE] });
|
|
27
|
+
return () => observer.disconnect();
|
|
28
|
+
}, [open]);
|
|
29
|
+
return outerLock;
|
|
30
|
+
}
|
|
@@ -12,7 +12,7 @@ import { TEST_IDS, WIDTH_AS_VALUES } from '../../constants.js';
|
|
|
12
12
|
import { DialogBody, DialogFooter, DialogHeader, } from '../../helperComponents/index.js';
|
|
13
13
|
import { interopDefault } from '../../utils/interopDefault.js';
|
|
14
14
|
import { motionProps } from './constants.js';
|
|
15
|
-
import { useDrawerFocusTrap } from './hooks/index.js';
|
|
15
|
+
import { useDrawerFocusTrap, useOuterScrollLock } from './hooks/index.js';
|
|
16
16
|
import { useDrawerResize } from './hooks/useDrawerResize.js';
|
|
17
17
|
import styles from './styles.module.css';
|
|
18
18
|
const RcDrawer = interopDefault(RcDrawerImport);
|
|
@@ -44,13 +44,14 @@ function DrawerFrame(props) {
|
|
|
44
44
|
// (`showBlackout=false`): иначе два независимых обработчика закрывают и вложенный, и родительский.
|
|
45
45
|
useModalOpenState(open, onClose, { closeOnPopstate, closeByCloseWatcher: !showBlackout });
|
|
46
46
|
const focusTrapRef = useDrawerFocusTrap(Boolean(open));
|
|
47
|
+
const outerScrollLock = useOuterScrollLock(Boolean(open));
|
|
47
48
|
// Без явного container портал идёт через PortalContextProvider (как у Modal): скоуп к локальному
|
|
48
49
|
// элементу и отказ от body-level scroll lock, который rc-drawer ставит при рендере в document.body.
|
|
49
50
|
const portalContextRef = usePortalContext();
|
|
50
51
|
const resolvedContainer = container ?? (portalContextRef.current ? () => portalContextRef.current : undefined);
|
|
51
52
|
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: {
|
|
52
53
|
section: cn(styles.drawer, className),
|
|
53
|
-
}, 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(RemoveScroll, { enabled: showBlackout, removeScrollBar: false, 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] }) }));
|
|
54
|
+
}, 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(RemoveScroll, { enabled: showBlackout || outerScrollLock, removeScrollBar: false, 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] }) }));
|
|
54
55
|
}
|
|
55
56
|
/**
|
|
56
57
|
* Адаптивный low-level `DrawerCustom`: на `mobile` — `BottomSheetCustom`, иначе — `DrawerFrame`.
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Есть ли снаружи чужой scroll-lock (модалка, из которой открыли дровер).
|
|
3
|
+
*
|
|
4
|
+
* Такой лок гасит колесо и touchmove вне своего поддерева, а дровер живёт в отдельном портале —
|
|
5
|
+
* без собственного лока поверх него тело дровера не прокручивается. Свой лок нужен только в этом
|
|
6
|
+
* случае: без блэкаута и без внешнего лока страница под дровером должна скроллиться.
|
|
7
|
+
*/
|
|
8
|
+
export declare function useOuterScrollLock(open: boolean): boolean;
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { isBrowser } from '@cloud-ru/ds-utils';
|
|
2
|
+
import { useEffect, useState } from 'react';
|
|
3
|
+
/** Атрибут, которым `react-remove-scroll-bar` помечает `body` на время активного лока. */
|
|
4
|
+
const SCROLL_LOCK_ATTRIBUTE = 'data-scroll-locked';
|
|
5
|
+
/**
|
|
6
|
+
* Есть ли снаружи чужой scroll-lock (модалка, из которой открыли дровер).
|
|
7
|
+
*
|
|
8
|
+
* Такой лок гасит колесо и touchmove вне своего поддерева, а дровер живёт в отдельном портале —
|
|
9
|
+
* без собственного лока поверх него тело дровера не прокручивается. Свой лок нужен только в этом
|
|
10
|
+
* случае: без блэкаута и без внешнего лока страница под дровером должна скроллиться.
|
|
11
|
+
*/
|
|
12
|
+
export function useOuterScrollLock(open) {
|
|
13
|
+
const [outerLock, setOuterLock] = useState(false);
|
|
14
|
+
useEffect(() => {
|
|
15
|
+
if (!open || !isBrowser()) {
|
|
16
|
+
setOuterLock(false);
|
|
17
|
+
return;
|
|
18
|
+
}
|
|
19
|
+
// Свой лок дровера идёт с `removeScrollBar: false` и этот атрибут не ставит — самовозбуждения нет.
|
|
20
|
+
const readLock = () => setOuterLock(document.body.hasAttribute(SCROLL_LOCK_ATTRIBUTE));
|
|
21
|
+
readLock();
|
|
22
|
+
const observer = new MutationObserver(readLock);
|
|
23
|
+
observer.observe(document.body, { attributes: true, attributeFilter: [SCROLL_LOCK_ATTRIBUTE] });
|
|
24
|
+
return () => observer.disconnect();
|
|
25
|
+
}, [open]);
|
|
26
|
+
return outerLock;
|
|
27
|
+
}
|