@alfalab/core-components-base-modal 5.7.3 → 5.7.4
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/Component.d.ts +6 -2
- package/Component.js +29 -16
- package/cssm/Component.d.ts +6 -2
- package/cssm/Component.js +28 -15
- package/cssm/helpers/lockScroll.d.ts +4 -0
- package/cssm/helpers/lockScroll.js +20 -0
- package/cssm/index.module.css +6 -0
- package/cssm/utils.d.ts +1 -1
- package/cssm/utils.js +5 -2
- package/esm/Component.d.ts +6 -2
- package/esm/Component.js +30 -17
- package/esm/helpers/lockScroll.d.ts +4 -0
- package/esm/helpers/lockScroll.js +14 -0
- package/esm/index.css +19 -13
- package/esm/utils.d.ts +1 -1
- package/esm/utils.js +5 -2
- package/helpers/lockScroll.d.ts +4 -0
- package/helpers/lockScroll.js +20 -0
- package/index.css +19 -13
- package/modern/Component.d.ts +6 -2
- package/modern/Component.js +22 -10
- package/modern/helpers/lockScroll.d.ts +4 -0
- package/modern/helpers/lockScroll.js +14 -0
- package/modern/index.css +19 -13
- package/modern/utils.d.ts +1 -1
- package/modern/utils.js +4 -2
- package/package.json +1 -1
- package/src/Component.tsx +30 -9
- package/src/helpers/lockScroll.ts +15 -0
- package/src/index.module.css +10 -0
- package/src/utils.ts +4 -2
- package/utils.d.ts +1 -1
- package/utils.js +5 -2
package/Component.d.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/// <reference types="react" />
|
|
2
2
|
/// <reference types="react-transition-group" />
|
|
3
3
|
import React from 'react';
|
|
4
|
-
import {
|
|
4
|
+
import { ComponentType, KeyboardEvent, MouseEvent, MutableRefObject, ReactNode, Ref } from "react";
|
|
5
5
|
import { TransitionProps } from 'react-transition-group/Transition';
|
|
6
6
|
import { BackdropProps } from "@alfalab/core-components-backdrop";
|
|
7
7
|
import { PortalProps } from "@alfalab/core-components-portal";
|
|
@@ -13,7 +13,7 @@ type BaseModalProps = {
|
|
|
13
13
|
/**
|
|
14
14
|
* Компонент бэкдропа
|
|
15
15
|
*/
|
|
16
|
-
Backdrop?:
|
|
16
|
+
Backdrop?: ComponentType<BackdropProps>;
|
|
17
17
|
/**
|
|
18
18
|
* Свойства для Бэкдропа
|
|
19
19
|
*/
|
|
@@ -134,6 +134,10 @@ type BaseModalProps = {
|
|
|
134
134
|
* Реф, который должен быть установлен компонентной области
|
|
135
135
|
*/
|
|
136
136
|
componentRef?: MutableRefObject<HTMLDivElement | null>;
|
|
137
|
+
/**
|
|
138
|
+
* Блокирует скролл когда модальное окно открыто. Работает только на iOS.
|
|
139
|
+
*/
|
|
140
|
+
iOSLock?: boolean;
|
|
137
141
|
};
|
|
138
142
|
type BaseModalContext = {
|
|
139
143
|
parentRef: React.RefObject<HTMLDivElement>;
|
package/Component.js
CHANGED
|
@@ -13,6 +13,7 @@ var coreComponentsBackdrop = require('@alfalab/core-components-backdrop');
|
|
|
13
13
|
var coreComponentsPortal = require('@alfalab/core-components-portal');
|
|
14
14
|
var coreComponentsShared = require('@alfalab/core-components-shared');
|
|
15
15
|
var coreComponentsStack = require('@alfalab/core-components-stack');
|
|
16
|
+
var helpers_lockScroll = require('./helpers/lockScroll.js');
|
|
16
17
|
var utils = require('./utils.js');
|
|
17
18
|
require('./matches-polyfill.js');
|
|
18
19
|
|
|
@@ -23,7 +24,7 @@ var FocusLock__default = /*#__PURE__*/_interopDefaultCompat(FocusLock);
|
|
|
23
24
|
var mergeRefs__default = /*#__PURE__*/_interopDefaultCompat(mergeRefs);
|
|
24
25
|
var cn__default = /*#__PURE__*/_interopDefaultCompat(cn);
|
|
25
26
|
|
|
26
|
-
var styles = {"component":"base-
|
|
27
|
+
var styles = {"component":"base-modal__component_1apcq","wrapper":"base-modal__wrapper_1apcq","content":"base-modal__content_1apcq","hidden":"base-modal__hidden_1apcq","backdrop":"base-modal__backdrop_1apcq","appear":"base-modal__appear_1apcq","enter":"base-modal__enter_1apcq","appearActive":"base-modal__appearActive_1apcq","enterActive":"base-modal__enterActive_1apcq","exit":"base-modal__exit_1apcq","exitActive":"base-modal__exitActive_1apcq","exitDone":"base-modal__exitDone_1apcq"};
|
|
27
28
|
require('./index.css')
|
|
28
29
|
|
|
29
30
|
// eslint-disable-next-line @typescript-eslint/no-redeclare
|
|
@@ -43,14 +44,14 @@ var BaseModalContext = React__default.default.createContext({
|
|
|
43
44
|
onClose: function () { return null; },
|
|
44
45
|
});
|
|
45
46
|
var BaseModal = React.forwardRef(function (_a, ref) {
|
|
46
|
-
var open = _a.open, container = _a.container, children = _a.children, _b = _a.scrollHandler, scrollHandler = _b === void 0 ? 'wrapper' : _b, _c = _a.Backdrop, Backdrop = _c === void 0 ? coreComponentsBackdrop.Backdrop : _c, _d = _a.backdropProps, backdropProps = _d === void 0 ? {} : _d, _e = _a.transitionProps, transitionProps = _e === void 0 ? {} : _e, disableBackdropClick = _a.disableBackdropClick, _f = _a.disableAutoFocus, disableAutoFocus = _f === void 0 ? false : _f, _g = _a.disableFocusLock, disableFocusLock = _g === void 0 ? false : _g, _h = _a.disableEscapeKeyDown, disableEscapeKeyDown = _h === void 0 ? false : _h, _j = _a.disableRestoreFocus, disableRestoreFocus = _j === void 0 ? false : _j, _k = _a.disableBlockingScroll, disableBlockingScroll = _k === void 0 ? false : _k, _l = _a.keepMounted, keepMounted = _l === void 0 ? false : _l, className = _a.className, contentClassName = _a.contentClassName, wrapperProps = _a.wrapperProps, contentProps = _a.contentProps, componentDivProps = _a.componentDivProps, wrapperClassName = _a.wrapperClassName, onBackdropClick = _a.onBackdropClick, onClose = _a.onClose, onEscapeKeyDown = _a.onEscapeKeyDown, onMount = _a.onMount, onUnmount = _a.onUnmount, dataTestId = _a.dataTestId, _m = _a.zIndex, zIndex = _m === void 0 ? coreComponentsStack.stackingOrder.MODAL : _m, _o = _a.componentRef, componentRef = _o === void 0 ? null : _o, _p = _a.usePortal, usePortal = _p === void 0 ? true : _p;
|
|
47
|
-
var
|
|
48
|
-
var
|
|
49
|
-
var
|
|
50
|
-
var
|
|
51
|
-
var
|
|
52
|
-
var
|
|
53
|
-
var
|
|
47
|
+
var open = _a.open, container = _a.container, children = _a.children, _b = _a.scrollHandler, scrollHandler = _b === void 0 ? 'wrapper' : _b, _c = _a.Backdrop, Backdrop = _c === void 0 ? coreComponentsBackdrop.Backdrop : _c, _d = _a.backdropProps, backdropProps = _d === void 0 ? {} : _d, _e = _a.transitionProps, transitionProps = _e === void 0 ? {} : _e, disableBackdropClick = _a.disableBackdropClick, _f = _a.disableAutoFocus, disableAutoFocus = _f === void 0 ? false : _f, _g = _a.disableFocusLock, disableFocusLock = _g === void 0 ? false : _g, _h = _a.disableEscapeKeyDown, disableEscapeKeyDown = _h === void 0 ? false : _h, _j = _a.disableRestoreFocus, disableRestoreFocus = _j === void 0 ? false : _j, _k = _a.disableBlockingScroll, disableBlockingScroll = _k === void 0 ? false : _k, _l = _a.keepMounted, keepMounted = _l === void 0 ? false : _l, className = _a.className, contentClassName = _a.contentClassName, wrapperProps = _a.wrapperProps, contentProps = _a.contentProps, componentDivProps = _a.componentDivProps, wrapperClassName = _a.wrapperClassName, onBackdropClick = _a.onBackdropClick, onClose = _a.onClose, onEscapeKeyDown = _a.onEscapeKeyDown, onMount = _a.onMount, onUnmount = _a.onUnmount, dataTestId = _a.dataTestId, _m = _a.zIndex, zIndex = _m === void 0 ? coreComponentsStack.stackingOrder.MODAL : _m, _o = _a.componentRef, componentRef = _o === void 0 ? null : _o, _p = _a.usePortal, usePortal = _p === void 0 ? true : _p, _q = _a.iOSLock, iOSLock = _q === void 0 ? false : _q;
|
|
48
|
+
var _r = React.useState(null), exited = _r[0], setExited = _r[1];
|
|
49
|
+
var _s = React.useState(false), hasScroll = _s[0], setHasScroll = _s[1];
|
|
50
|
+
var _t = React.useState(false), hasHeader = _t[0], setHasHeader = _t[1];
|
|
51
|
+
var _u = React.useState(false), hasFooter = _u[0], setHasFooter = _u[1];
|
|
52
|
+
var _v = React.useState(false), headerHighlighted = _v[0], setHeaderHighlighted = _v[1];
|
|
53
|
+
var _w = React.useState(false), footerHighlighted = _w[0], setFooterHighlighted = _w[1];
|
|
54
|
+
var _x = React.useState(0), headerOffset = _x[0], setHeaderOffset = _x[1];
|
|
54
55
|
var componentNodeRef = React.useRef(null);
|
|
55
56
|
var wrapperRef = React.useRef(null);
|
|
56
57
|
var scrollableNodeRef = React.useRef(null);
|
|
@@ -102,6 +103,9 @@ var BaseModal = React.forwardRef(function (_a, ref) {
|
|
|
102
103
|
}
|
|
103
104
|
}, [hasFooter, hasHeader, headerOffset]);
|
|
104
105
|
var handleClose = React.useCallback(function (event, reason) {
|
|
106
|
+
if (iOSLock && coreComponentsShared.os.isIOS()) {
|
|
107
|
+
helpers_lockScroll.unlockScroll();
|
|
108
|
+
}
|
|
105
109
|
if (onClose) {
|
|
106
110
|
onClose(event, reason);
|
|
107
111
|
}
|
|
@@ -112,7 +116,7 @@ var BaseModal = React.forwardRef(function (_a, ref) {
|
|
|
112
116
|
onEscapeKeyDown(event);
|
|
113
117
|
}
|
|
114
118
|
return null;
|
|
115
|
-
}, [onBackdropClick, onClose, onEscapeKeyDown]);
|
|
119
|
+
}, [onBackdropClick, onClose, onEscapeKeyDown, iOSLock]);
|
|
116
120
|
var handleBackdropMouseDown = function (event) {
|
|
117
121
|
var _a;
|
|
118
122
|
var clickedOnScrollbar = false;
|
|
@@ -187,7 +191,12 @@ var BaseModal = React.forwardRef(function (_a, ref) {
|
|
|
187
191
|
if (open && isExited) {
|
|
188
192
|
if (!disableBlockingScroll) {
|
|
189
193
|
var el_1 = getContainer();
|
|
190
|
-
|
|
194
|
+
var shouldIOSLock = iOSLock && coreComponentsShared.os.isIOS();
|
|
195
|
+
utils.handleContainer(el_1, shouldIOSLock);
|
|
196
|
+
if (shouldIOSLock) {
|
|
197
|
+
helpers_lockScroll.syncHeight();
|
|
198
|
+
helpers_lockScroll.lockScroll();
|
|
199
|
+
}
|
|
191
200
|
restoreContainerStylesRef.current = function () {
|
|
192
201
|
restoreContainerStylesRef.current = null;
|
|
193
202
|
utils.restoreContainerStyles(el_1);
|
|
@@ -195,7 +204,7 @@ var BaseModal = React.forwardRef(function (_a, ref) {
|
|
|
195
204
|
}
|
|
196
205
|
setExited(false);
|
|
197
206
|
}
|
|
198
|
-
}, [getContainer, open, disableBlockingScroll, isExited]);
|
|
207
|
+
}, [getContainer, open, disableBlockingScroll, isExited, iOSLock]);
|
|
199
208
|
React.useEffect(function () {
|
|
200
209
|
var ResizeObserver = window.ResizeObserver || resizeObserver.ResizeObserver;
|
|
201
210
|
resizeObserverRef.current = new ResizeObserver(checkToHasScrollBar);
|
|
@@ -208,6 +217,12 @@ var BaseModal = React.forwardRef(function (_a, ref) {
|
|
|
208
217
|
}
|
|
209
218
|
};
|
|
210
219
|
}, []);
|
|
220
|
+
React.useEffect(function () {
|
|
221
|
+
var _a;
|
|
222
|
+
if (disableAutoFocus || !open)
|
|
223
|
+
return;
|
|
224
|
+
(_a = wrapperRef.current) === null || _a === void 0 ? void 0 : _a.focus();
|
|
225
|
+
}, [open, disableAutoFocus]);
|
|
211
226
|
var contextValue = React.useMemo(function () { return ({
|
|
212
227
|
parentRef: wrapperRef,
|
|
213
228
|
componentRef: componentNodeRef,
|
|
@@ -236,7 +251,7 @@ var BaseModal = React.forwardRef(function (_a, ref) {
|
|
|
236
251
|
var renderContent = function () { return (React__default.default.createElement(coreComponentsStack.Stack, { value: zIndex }, function (computedZIndex) {
|
|
237
252
|
var _a;
|
|
238
253
|
return (React__default.default.createElement(BaseModalContext.Provider, { value: contextValue },
|
|
239
|
-
React__default.default.createElement(FocusLock__default.default, {
|
|
254
|
+
React__default.default.createElement(FocusLock__default.default, { disabled: disableFocusLock || !open, returnFocus: !disableRestoreFocus },
|
|
240
255
|
Backdrop && (React__default.default.createElement(Backdrop, tslib.__assign({}, backdropProps, { className: cn__default.default(backdropProps.className, styles.backdrop), open: open, style: {
|
|
241
256
|
zIndex: computedZIndex,
|
|
242
257
|
} }))),
|
|
@@ -246,9 +261,7 @@ var BaseModal = React.forwardRef(function (_a, ref) {
|
|
|
246
261
|
ref,
|
|
247
262
|
wrapperRef,
|
|
248
263
|
wrapperProps === null || wrapperProps === void 0 ? void 0 : wrapperProps.ref,
|
|
249
|
-
]), onKeyDown: handleKeyDown, onMouseDown: handleBackdropMouseDown, onMouseUp: handleBackdropMouseUp,
|
|
250
|
-
// eslint-disable-next-line jsx-a11y/no-noninteractive-tabindex
|
|
251
|
-
tabIndex: 0, "data-test-id": dataTestId, style: {
|
|
264
|
+
]), onKeyDown: handleKeyDown, onMouseDown: handleBackdropMouseDown, onMouseUp: handleBackdropMouseUp, tabIndex: -1, "data-test-id": dataTestId, style: {
|
|
252
265
|
zIndex: computedZIndex,
|
|
253
266
|
} }),
|
|
254
267
|
React__default.default.createElement(reactTransitionGroup.CSSTransition, tslib.__assign({ appear: true, timeout: 200, classNames: styles, nodeRef: componentNodeRef }, transitionProps, { in: open, onEntered: handleEntered, onExited: handleExited }),
|
package/cssm/Component.d.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/// <reference types="react" />
|
|
2
2
|
/// <reference types="react-transition-group" />
|
|
3
3
|
import React from 'react';
|
|
4
|
-
import {
|
|
4
|
+
import { ComponentType, KeyboardEvent, MouseEvent, MutableRefObject, ReactNode, Ref } from "react";
|
|
5
5
|
import { TransitionProps } from 'react-transition-group/Transition';
|
|
6
6
|
import { BackdropProps } from "@alfalab/core-components-backdrop";
|
|
7
7
|
import { PortalProps } from "@alfalab/core-components-portal";
|
|
@@ -13,7 +13,7 @@ type BaseModalProps = {
|
|
|
13
13
|
/**
|
|
14
14
|
* Компонент бэкдропа
|
|
15
15
|
*/
|
|
16
|
-
Backdrop?:
|
|
16
|
+
Backdrop?: ComponentType<BackdropProps>;
|
|
17
17
|
/**
|
|
18
18
|
* Свойства для Бэкдропа
|
|
19
19
|
*/
|
|
@@ -134,6 +134,10 @@ type BaseModalProps = {
|
|
|
134
134
|
* Реф, который должен быть установлен компонентной области
|
|
135
135
|
*/
|
|
136
136
|
componentRef?: MutableRefObject<HTMLDivElement | null>;
|
|
137
|
+
/**
|
|
138
|
+
* Блокирует скролл когда модальное окно открыто. Работает только на iOS.
|
|
139
|
+
*/
|
|
140
|
+
iOSLock?: boolean;
|
|
137
141
|
};
|
|
138
142
|
type BaseModalContext = {
|
|
139
143
|
parentRef: React.RefObject<HTMLDivElement>;
|
package/cssm/Component.js
CHANGED
|
@@ -13,6 +13,7 @@ var coreComponentsBackdrop = require('@alfalab/core-components-backdrop/cssm');
|
|
|
13
13
|
var coreComponentsPortal = require('@alfalab/core-components-portal/cssm');
|
|
14
14
|
var coreComponentsShared = require('@alfalab/core-components-shared/cssm');
|
|
15
15
|
var coreComponentsStack = require('@alfalab/core-components-stack/cssm');
|
|
16
|
+
var helpers_lockScroll = require('./helpers/lockScroll.js');
|
|
16
17
|
var utils = require('./utils.js');
|
|
17
18
|
var styles = require('./index.module.css');
|
|
18
19
|
require('./matches-polyfill.js');
|
|
@@ -42,14 +43,14 @@ var BaseModalContext = React__default.default.createContext({
|
|
|
42
43
|
onClose: function () { return null; },
|
|
43
44
|
});
|
|
44
45
|
var BaseModal = React.forwardRef(function (_a, ref) {
|
|
45
|
-
var open = _a.open, container = _a.container, children = _a.children, _b = _a.scrollHandler, scrollHandler = _b === void 0 ? 'wrapper' : _b, _c = _a.Backdrop, Backdrop = _c === void 0 ? coreComponentsBackdrop.Backdrop : _c, _d = _a.backdropProps, backdropProps = _d === void 0 ? {} : _d, _e = _a.transitionProps, transitionProps = _e === void 0 ? {} : _e, disableBackdropClick = _a.disableBackdropClick, _f = _a.disableAutoFocus, disableAutoFocus = _f === void 0 ? false : _f, _g = _a.disableFocusLock, disableFocusLock = _g === void 0 ? false : _g, _h = _a.disableEscapeKeyDown, disableEscapeKeyDown = _h === void 0 ? false : _h, _j = _a.disableRestoreFocus, disableRestoreFocus = _j === void 0 ? false : _j, _k = _a.disableBlockingScroll, disableBlockingScroll = _k === void 0 ? false : _k, _l = _a.keepMounted, keepMounted = _l === void 0 ? false : _l, className = _a.className, contentClassName = _a.contentClassName, wrapperProps = _a.wrapperProps, contentProps = _a.contentProps, componentDivProps = _a.componentDivProps, wrapperClassName = _a.wrapperClassName, onBackdropClick = _a.onBackdropClick, onClose = _a.onClose, onEscapeKeyDown = _a.onEscapeKeyDown, onMount = _a.onMount, onUnmount = _a.onUnmount, dataTestId = _a.dataTestId, _m = _a.zIndex, zIndex = _m === void 0 ? coreComponentsStack.stackingOrder.MODAL : _m, _o = _a.componentRef, componentRef = _o === void 0 ? null : _o, _p = _a.usePortal, usePortal = _p === void 0 ? true : _p;
|
|
46
|
-
var
|
|
47
|
-
var
|
|
48
|
-
var
|
|
49
|
-
var
|
|
50
|
-
var
|
|
51
|
-
var
|
|
52
|
-
var
|
|
46
|
+
var open = _a.open, container = _a.container, children = _a.children, _b = _a.scrollHandler, scrollHandler = _b === void 0 ? 'wrapper' : _b, _c = _a.Backdrop, Backdrop = _c === void 0 ? coreComponentsBackdrop.Backdrop : _c, _d = _a.backdropProps, backdropProps = _d === void 0 ? {} : _d, _e = _a.transitionProps, transitionProps = _e === void 0 ? {} : _e, disableBackdropClick = _a.disableBackdropClick, _f = _a.disableAutoFocus, disableAutoFocus = _f === void 0 ? false : _f, _g = _a.disableFocusLock, disableFocusLock = _g === void 0 ? false : _g, _h = _a.disableEscapeKeyDown, disableEscapeKeyDown = _h === void 0 ? false : _h, _j = _a.disableRestoreFocus, disableRestoreFocus = _j === void 0 ? false : _j, _k = _a.disableBlockingScroll, disableBlockingScroll = _k === void 0 ? false : _k, _l = _a.keepMounted, keepMounted = _l === void 0 ? false : _l, className = _a.className, contentClassName = _a.contentClassName, wrapperProps = _a.wrapperProps, contentProps = _a.contentProps, componentDivProps = _a.componentDivProps, wrapperClassName = _a.wrapperClassName, onBackdropClick = _a.onBackdropClick, onClose = _a.onClose, onEscapeKeyDown = _a.onEscapeKeyDown, onMount = _a.onMount, onUnmount = _a.onUnmount, dataTestId = _a.dataTestId, _m = _a.zIndex, zIndex = _m === void 0 ? coreComponentsStack.stackingOrder.MODAL : _m, _o = _a.componentRef, componentRef = _o === void 0 ? null : _o, _p = _a.usePortal, usePortal = _p === void 0 ? true : _p, _q = _a.iOSLock, iOSLock = _q === void 0 ? false : _q;
|
|
47
|
+
var _r = React.useState(null), exited = _r[0], setExited = _r[1];
|
|
48
|
+
var _s = React.useState(false), hasScroll = _s[0], setHasScroll = _s[1];
|
|
49
|
+
var _t = React.useState(false), hasHeader = _t[0], setHasHeader = _t[1];
|
|
50
|
+
var _u = React.useState(false), hasFooter = _u[0], setHasFooter = _u[1];
|
|
51
|
+
var _v = React.useState(false), headerHighlighted = _v[0], setHeaderHighlighted = _v[1];
|
|
52
|
+
var _w = React.useState(false), footerHighlighted = _w[0], setFooterHighlighted = _w[1];
|
|
53
|
+
var _x = React.useState(0), headerOffset = _x[0], setHeaderOffset = _x[1];
|
|
53
54
|
var componentNodeRef = React.useRef(null);
|
|
54
55
|
var wrapperRef = React.useRef(null);
|
|
55
56
|
var scrollableNodeRef = React.useRef(null);
|
|
@@ -101,6 +102,9 @@ var BaseModal = React.forwardRef(function (_a, ref) {
|
|
|
101
102
|
}
|
|
102
103
|
}, [hasFooter, hasHeader, headerOffset]);
|
|
103
104
|
var handleClose = React.useCallback(function (event, reason) {
|
|
105
|
+
if (iOSLock && coreComponentsShared.os.isIOS()) {
|
|
106
|
+
helpers_lockScroll.unlockScroll();
|
|
107
|
+
}
|
|
104
108
|
if (onClose) {
|
|
105
109
|
onClose(event, reason);
|
|
106
110
|
}
|
|
@@ -111,7 +115,7 @@ var BaseModal = React.forwardRef(function (_a, ref) {
|
|
|
111
115
|
onEscapeKeyDown(event);
|
|
112
116
|
}
|
|
113
117
|
return null;
|
|
114
|
-
}, [onBackdropClick, onClose, onEscapeKeyDown]);
|
|
118
|
+
}, [onBackdropClick, onClose, onEscapeKeyDown, iOSLock]);
|
|
115
119
|
var handleBackdropMouseDown = function (event) {
|
|
116
120
|
var _a;
|
|
117
121
|
var clickedOnScrollbar = false;
|
|
@@ -186,7 +190,12 @@ var BaseModal = React.forwardRef(function (_a, ref) {
|
|
|
186
190
|
if (open && isExited) {
|
|
187
191
|
if (!disableBlockingScroll) {
|
|
188
192
|
var el_1 = getContainer();
|
|
189
|
-
|
|
193
|
+
var shouldIOSLock = iOSLock && coreComponentsShared.os.isIOS();
|
|
194
|
+
utils.handleContainer(el_1, shouldIOSLock);
|
|
195
|
+
if (shouldIOSLock) {
|
|
196
|
+
helpers_lockScroll.syncHeight();
|
|
197
|
+
helpers_lockScroll.lockScroll();
|
|
198
|
+
}
|
|
190
199
|
restoreContainerStylesRef.current = function () {
|
|
191
200
|
restoreContainerStylesRef.current = null;
|
|
192
201
|
utils.restoreContainerStyles(el_1);
|
|
@@ -194,7 +203,7 @@ var BaseModal = React.forwardRef(function (_a, ref) {
|
|
|
194
203
|
}
|
|
195
204
|
setExited(false);
|
|
196
205
|
}
|
|
197
|
-
}, [getContainer, open, disableBlockingScroll, isExited]);
|
|
206
|
+
}, [getContainer, open, disableBlockingScroll, isExited, iOSLock]);
|
|
198
207
|
React.useEffect(function () {
|
|
199
208
|
var ResizeObserver = window.ResizeObserver || resizeObserver.ResizeObserver;
|
|
200
209
|
resizeObserverRef.current = new ResizeObserver(checkToHasScrollBar);
|
|
@@ -207,6 +216,12 @@ var BaseModal = React.forwardRef(function (_a, ref) {
|
|
|
207
216
|
}
|
|
208
217
|
};
|
|
209
218
|
}, []);
|
|
219
|
+
React.useEffect(function () {
|
|
220
|
+
var _a;
|
|
221
|
+
if (disableAutoFocus || !open)
|
|
222
|
+
return;
|
|
223
|
+
(_a = wrapperRef.current) === null || _a === void 0 ? void 0 : _a.focus();
|
|
224
|
+
}, [open, disableAutoFocus]);
|
|
210
225
|
var contextValue = React.useMemo(function () { return ({
|
|
211
226
|
parentRef: wrapperRef,
|
|
212
227
|
componentRef: componentNodeRef,
|
|
@@ -235,7 +250,7 @@ var BaseModal = React.forwardRef(function (_a, ref) {
|
|
|
235
250
|
var renderContent = function () { return (React__default.default.createElement(coreComponentsStack.Stack, { value: zIndex }, function (computedZIndex) {
|
|
236
251
|
var _a;
|
|
237
252
|
return (React__default.default.createElement(BaseModalContext.Provider, { value: contextValue },
|
|
238
|
-
React__default.default.createElement(FocusLock__default.default, {
|
|
253
|
+
React__default.default.createElement(FocusLock__default.default, { disabled: disableFocusLock || !open, returnFocus: !disableRestoreFocus },
|
|
239
254
|
Backdrop && (React__default.default.createElement(Backdrop, tslib.__assign({}, backdropProps, { className: cn__default.default(backdropProps.className, styles__default.default.backdrop), open: open, style: {
|
|
240
255
|
zIndex: computedZIndex,
|
|
241
256
|
} }))),
|
|
@@ -245,9 +260,7 @@ var BaseModal = React.forwardRef(function (_a, ref) {
|
|
|
245
260
|
ref,
|
|
246
261
|
wrapperRef,
|
|
247
262
|
wrapperProps === null || wrapperProps === void 0 ? void 0 : wrapperProps.ref,
|
|
248
|
-
]), onKeyDown: handleKeyDown, onMouseDown: handleBackdropMouseDown, onMouseUp: handleBackdropMouseUp,
|
|
249
|
-
// eslint-disable-next-line jsx-a11y/no-noninteractive-tabindex
|
|
250
|
-
tabIndex: 0, "data-test-id": dataTestId, style: {
|
|
263
|
+
]), onKeyDown: handleKeyDown, onMouseDown: handleBackdropMouseDown, onMouseUp: handleBackdropMouseUp, tabIndex: -1, "data-test-id": dataTestId, style: {
|
|
251
264
|
zIndex: computedZIndex,
|
|
252
265
|
} }),
|
|
253
266
|
React__default.default.createElement(reactTransitionGroup.CSSTransition, tslib.__assign({ appear: true, timeout: 200, classNames: styles__default.default, nodeRef: componentNodeRef }, transitionProps, { in: open, onEntered: handleEntered, onExited: handleExited }),
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
Object.defineProperty(exports, '__esModule', { value: true });
|
|
4
|
+
|
|
5
|
+
var scrollY;
|
|
6
|
+
var lockScroll = function () {
|
|
7
|
+
scrollY = window.scrollY;
|
|
8
|
+
document.body.classList.add('is-locked');
|
|
9
|
+
};
|
|
10
|
+
var unlockScroll = function () {
|
|
11
|
+
document.body.classList.remove('is-locked');
|
|
12
|
+
window.scrollTo(0, scrollY);
|
|
13
|
+
};
|
|
14
|
+
var syncHeight = function () {
|
|
15
|
+
document.body.style.setProperty('--window-inner-scrollY', "".concat(window.scrollY, "px"));
|
|
16
|
+
};
|
|
17
|
+
|
|
18
|
+
exports.lockScroll = lockScroll;
|
|
19
|
+
exports.syncHeight = syncHeight;
|
|
20
|
+
exports.unlockScroll = unlockScroll;
|
package/cssm/index.module.css
CHANGED
|
@@ -14,6 +14,12 @@
|
|
|
14
14
|
} :root {
|
|
15
15
|
} :root {
|
|
16
16
|
} :root {
|
|
17
|
+
} :root {
|
|
18
|
+
--window-inner-scrollY: 10px; /* fallback value to prevent ci error */
|
|
19
|
+
} body:global(.is-locked) {
|
|
20
|
+
margin-top: calc(var(--window-inner-scrollY) * -1);
|
|
21
|
+
position: fixed;
|
|
22
|
+
overflow: hidden;
|
|
17
23
|
} .component {
|
|
18
24
|
position: relative;
|
|
19
25
|
box-sizing: border-box;
|
package/cssm/utils.d.ts
CHANGED
|
@@ -2,5 +2,5 @@ declare function isScrolledToTop(target: HTMLElement): boolean;
|
|
|
2
2
|
declare function isScrolledToBottom(target: HTMLElement): boolean;
|
|
3
3
|
declare function hasScrollbar(target: HTMLElement): boolean;
|
|
4
4
|
declare const restoreContainerStyles: (container: HTMLElement) => void;
|
|
5
|
-
declare const handleContainer: (container?: HTMLElement) => void;
|
|
5
|
+
declare const handleContainer: (container?: HTMLElement, shouldIOSLock?: boolean) => void;
|
|
6
6
|
export { isScrolledToTop, isScrolledToBottom, hasScrollbar, restoreContainerStyles, handleContainer };
|
package/cssm/utils.js
CHANGED
|
@@ -43,7 +43,8 @@ var restoreContainerStyles = function (container) {
|
|
|
43
43
|
});
|
|
44
44
|
}
|
|
45
45
|
};
|
|
46
|
-
var handleContainer = function (container) {
|
|
46
|
+
var handleContainer = function (container, shouldIOSLock) {
|
|
47
|
+
if (shouldIOSLock === void 0) { shouldIOSLock = false; }
|
|
47
48
|
if (!container)
|
|
48
49
|
return;
|
|
49
50
|
var modalRestoreStyles = coreComponentsGlobalStore.getModalStore().getRestoreStyles();
|
|
@@ -81,7 +82,9 @@ var handleContainer = function (container) {
|
|
|
81
82
|
el: scrollContainer,
|
|
82
83
|
});
|
|
83
84
|
}
|
|
84
|
-
|
|
85
|
+
if (!shouldIOSLock) {
|
|
86
|
+
scrollContainer.style.overflow = 'hidden';
|
|
87
|
+
}
|
|
85
88
|
modalRestoreStyles.push({
|
|
86
89
|
container: container,
|
|
87
90
|
modals: 1,
|
package/esm/Component.d.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/// <reference types="react" />
|
|
2
2
|
/// <reference types="react-transition-group" />
|
|
3
3
|
import React from 'react';
|
|
4
|
-
import {
|
|
4
|
+
import { ComponentType, KeyboardEvent, MouseEvent, MutableRefObject, ReactNode, Ref } from "react";
|
|
5
5
|
import { TransitionProps } from 'react-transition-group/Transition';
|
|
6
6
|
import { BackdropProps } from "@alfalab/core-components-backdrop";
|
|
7
7
|
import { PortalProps } from "@alfalab/core-components-portal";
|
|
@@ -13,7 +13,7 @@ type BaseModalProps = {
|
|
|
13
13
|
/**
|
|
14
14
|
* Компонент бэкдропа
|
|
15
15
|
*/
|
|
16
|
-
Backdrop?:
|
|
16
|
+
Backdrop?: ComponentType<BackdropProps>;
|
|
17
17
|
/**
|
|
18
18
|
* Свойства для Бэкдропа
|
|
19
19
|
*/
|
|
@@ -134,6 +134,10 @@ type BaseModalProps = {
|
|
|
134
134
|
* Реф, который должен быть установлен компонентной области
|
|
135
135
|
*/
|
|
136
136
|
componentRef?: MutableRefObject<HTMLDivElement | null>;
|
|
137
|
+
/**
|
|
138
|
+
* Блокирует скролл когда модальное окно открыто. Работает только на iOS.
|
|
139
|
+
*/
|
|
140
|
+
iOSLock?: boolean;
|
|
137
141
|
};
|
|
138
142
|
type BaseModalContext = {
|
|
139
143
|
parentRef: React.RefObject<HTMLDivElement>;
|
package/esm/Component.js
CHANGED
|
@@ -7,12 +7,13 @@ import { ResizeObserver } from '@juggle/resize-observer';
|
|
|
7
7
|
import cn from 'classnames';
|
|
8
8
|
import { Backdrop } from '@alfalab/core-components-backdrop/esm';
|
|
9
9
|
import { Portal } from '@alfalab/core-components-portal/esm';
|
|
10
|
-
import { browser } from '@alfalab/core-components-shared/esm';
|
|
10
|
+
import { os, browser } from '@alfalab/core-components-shared/esm';
|
|
11
11
|
import { stackingOrder, Stack } from '@alfalab/core-components-stack/esm';
|
|
12
|
+
import { unlockScroll, syncHeight, lockScroll } from './helpers/lockScroll.js';
|
|
12
13
|
import { isScrolledToTop, isScrolledToBottom, handleContainer, restoreContainerStyles, hasScrollbar } from './utils.js';
|
|
13
14
|
import './matches-polyfill.js';
|
|
14
15
|
|
|
15
|
-
var styles = {"component":"base-
|
|
16
|
+
var styles = {"component":"base-modal__component_1apcq","wrapper":"base-modal__wrapper_1apcq","content":"base-modal__content_1apcq","hidden":"base-modal__hidden_1apcq","backdrop":"base-modal__backdrop_1apcq","appear":"base-modal__appear_1apcq","enter":"base-modal__enter_1apcq","appearActive":"base-modal__appearActive_1apcq","enterActive":"base-modal__enterActive_1apcq","exit":"base-modal__exit_1apcq","exitActive":"base-modal__exitActive_1apcq","exitDone":"base-modal__exitDone_1apcq"};
|
|
16
17
|
require('./index.css')
|
|
17
18
|
|
|
18
19
|
// eslint-disable-next-line @typescript-eslint/no-redeclare
|
|
@@ -32,14 +33,14 @@ var BaseModalContext = React.createContext({
|
|
|
32
33
|
onClose: function () { return null; },
|
|
33
34
|
});
|
|
34
35
|
var BaseModal = forwardRef(function (_a, ref) {
|
|
35
|
-
var open = _a.open, container = _a.container, children = _a.children, _b = _a.scrollHandler, scrollHandler = _b === void 0 ? 'wrapper' : _b, _c = _a.Backdrop, Backdrop$1 = _c === void 0 ? Backdrop : _c, _d = _a.backdropProps, backdropProps = _d === void 0 ? {} : _d, _e = _a.transitionProps, transitionProps = _e === void 0 ? {} : _e, disableBackdropClick = _a.disableBackdropClick, _f = _a.disableAutoFocus, disableAutoFocus = _f === void 0 ? false : _f, _g = _a.disableFocusLock, disableFocusLock = _g === void 0 ? false : _g, _h = _a.disableEscapeKeyDown, disableEscapeKeyDown = _h === void 0 ? false : _h, _j = _a.disableRestoreFocus, disableRestoreFocus = _j === void 0 ? false : _j, _k = _a.disableBlockingScroll, disableBlockingScroll = _k === void 0 ? false : _k, _l = _a.keepMounted, keepMounted = _l === void 0 ? false : _l, className = _a.className, contentClassName = _a.contentClassName, wrapperProps = _a.wrapperProps, contentProps = _a.contentProps, componentDivProps = _a.componentDivProps, wrapperClassName = _a.wrapperClassName, onBackdropClick = _a.onBackdropClick, onClose = _a.onClose, onEscapeKeyDown = _a.onEscapeKeyDown, onMount = _a.onMount, onUnmount = _a.onUnmount, dataTestId = _a.dataTestId, _m = _a.zIndex, zIndex = _m === void 0 ? stackingOrder.MODAL : _m, _o = _a.componentRef, componentRef = _o === void 0 ? null : _o, _p = _a.usePortal, usePortal = _p === void 0 ? true : _p;
|
|
36
|
-
var
|
|
37
|
-
var
|
|
38
|
-
var
|
|
39
|
-
var
|
|
40
|
-
var
|
|
41
|
-
var
|
|
42
|
-
var
|
|
36
|
+
var open = _a.open, container = _a.container, children = _a.children, _b = _a.scrollHandler, scrollHandler = _b === void 0 ? 'wrapper' : _b, _c = _a.Backdrop, Backdrop$1 = _c === void 0 ? Backdrop : _c, _d = _a.backdropProps, backdropProps = _d === void 0 ? {} : _d, _e = _a.transitionProps, transitionProps = _e === void 0 ? {} : _e, disableBackdropClick = _a.disableBackdropClick, _f = _a.disableAutoFocus, disableAutoFocus = _f === void 0 ? false : _f, _g = _a.disableFocusLock, disableFocusLock = _g === void 0 ? false : _g, _h = _a.disableEscapeKeyDown, disableEscapeKeyDown = _h === void 0 ? false : _h, _j = _a.disableRestoreFocus, disableRestoreFocus = _j === void 0 ? false : _j, _k = _a.disableBlockingScroll, disableBlockingScroll = _k === void 0 ? false : _k, _l = _a.keepMounted, keepMounted = _l === void 0 ? false : _l, className = _a.className, contentClassName = _a.contentClassName, wrapperProps = _a.wrapperProps, contentProps = _a.contentProps, componentDivProps = _a.componentDivProps, wrapperClassName = _a.wrapperClassName, onBackdropClick = _a.onBackdropClick, onClose = _a.onClose, onEscapeKeyDown = _a.onEscapeKeyDown, onMount = _a.onMount, onUnmount = _a.onUnmount, dataTestId = _a.dataTestId, _m = _a.zIndex, zIndex = _m === void 0 ? stackingOrder.MODAL : _m, _o = _a.componentRef, componentRef = _o === void 0 ? null : _o, _p = _a.usePortal, usePortal = _p === void 0 ? true : _p, _q = _a.iOSLock, iOSLock = _q === void 0 ? false : _q;
|
|
37
|
+
var _r = useState(null), exited = _r[0], setExited = _r[1];
|
|
38
|
+
var _s = useState(false), hasScroll = _s[0], setHasScroll = _s[1];
|
|
39
|
+
var _t = useState(false), hasHeader = _t[0], setHasHeader = _t[1];
|
|
40
|
+
var _u = useState(false), hasFooter = _u[0], setHasFooter = _u[1];
|
|
41
|
+
var _v = useState(false), headerHighlighted = _v[0], setHeaderHighlighted = _v[1];
|
|
42
|
+
var _w = useState(false), footerHighlighted = _w[0], setFooterHighlighted = _w[1];
|
|
43
|
+
var _x = useState(0), headerOffset = _x[0], setHeaderOffset = _x[1];
|
|
43
44
|
var componentNodeRef = useRef(null);
|
|
44
45
|
var wrapperRef = useRef(null);
|
|
45
46
|
var scrollableNodeRef = useRef(null);
|
|
@@ -91,6 +92,9 @@ var BaseModal = forwardRef(function (_a, ref) {
|
|
|
91
92
|
}
|
|
92
93
|
}, [hasFooter, hasHeader, headerOffset]);
|
|
93
94
|
var handleClose = useCallback(function (event, reason) {
|
|
95
|
+
if (iOSLock && os.isIOS()) {
|
|
96
|
+
unlockScroll();
|
|
97
|
+
}
|
|
94
98
|
if (onClose) {
|
|
95
99
|
onClose(event, reason);
|
|
96
100
|
}
|
|
@@ -101,7 +105,7 @@ var BaseModal = forwardRef(function (_a, ref) {
|
|
|
101
105
|
onEscapeKeyDown(event);
|
|
102
106
|
}
|
|
103
107
|
return null;
|
|
104
|
-
}, [onBackdropClick, onClose, onEscapeKeyDown]);
|
|
108
|
+
}, [onBackdropClick, onClose, onEscapeKeyDown, iOSLock]);
|
|
105
109
|
var handleBackdropMouseDown = function (event) {
|
|
106
110
|
var _a;
|
|
107
111
|
var clickedOnScrollbar = false;
|
|
@@ -176,7 +180,12 @@ var BaseModal = forwardRef(function (_a, ref) {
|
|
|
176
180
|
if (open && isExited) {
|
|
177
181
|
if (!disableBlockingScroll) {
|
|
178
182
|
var el_1 = getContainer();
|
|
179
|
-
|
|
183
|
+
var shouldIOSLock = iOSLock && os.isIOS();
|
|
184
|
+
handleContainer(el_1, shouldIOSLock);
|
|
185
|
+
if (shouldIOSLock) {
|
|
186
|
+
syncHeight();
|
|
187
|
+
lockScroll();
|
|
188
|
+
}
|
|
180
189
|
restoreContainerStylesRef.current = function () {
|
|
181
190
|
restoreContainerStylesRef.current = null;
|
|
182
191
|
restoreContainerStyles(el_1);
|
|
@@ -184,7 +193,7 @@ var BaseModal = forwardRef(function (_a, ref) {
|
|
|
184
193
|
}
|
|
185
194
|
setExited(false);
|
|
186
195
|
}
|
|
187
|
-
}, [getContainer, open, disableBlockingScroll, isExited]);
|
|
196
|
+
}, [getContainer, open, disableBlockingScroll, isExited, iOSLock]);
|
|
188
197
|
useEffect(function () {
|
|
189
198
|
var ResizeObserver$1 = window.ResizeObserver || ResizeObserver;
|
|
190
199
|
resizeObserverRef.current = new ResizeObserver$1(checkToHasScrollBar);
|
|
@@ -197,6 +206,12 @@ var BaseModal = forwardRef(function (_a, ref) {
|
|
|
197
206
|
}
|
|
198
207
|
};
|
|
199
208
|
}, []);
|
|
209
|
+
useEffect(function () {
|
|
210
|
+
var _a;
|
|
211
|
+
if (disableAutoFocus || !open)
|
|
212
|
+
return;
|
|
213
|
+
(_a = wrapperRef.current) === null || _a === void 0 ? void 0 : _a.focus();
|
|
214
|
+
}, [open, disableAutoFocus]);
|
|
200
215
|
var contextValue = useMemo(function () { return ({
|
|
201
216
|
parentRef: wrapperRef,
|
|
202
217
|
componentRef: componentNodeRef,
|
|
@@ -225,7 +240,7 @@ var BaseModal = forwardRef(function (_a, ref) {
|
|
|
225
240
|
var renderContent = function () { return (React.createElement(Stack, { value: zIndex }, function (computedZIndex) {
|
|
226
241
|
var _a;
|
|
227
242
|
return (React.createElement(BaseModalContext.Provider, { value: contextValue },
|
|
228
|
-
React.createElement(FocusLock, {
|
|
243
|
+
React.createElement(FocusLock, { disabled: disableFocusLock || !open, returnFocus: !disableRestoreFocus },
|
|
229
244
|
Backdrop$1 && (React.createElement(Backdrop$1, __assign({}, backdropProps, { className: cn(backdropProps.className, styles.backdrop), open: open, style: {
|
|
230
245
|
zIndex: computedZIndex,
|
|
231
246
|
} }))),
|
|
@@ -235,9 +250,7 @@ var BaseModal = forwardRef(function (_a, ref) {
|
|
|
235
250
|
ref,
|
|
236
251
|
wrapperRef,
|
|
237
252
|
wrapperProps === null || wrapperProps === void 0 ? void 0 : wrapperProps.ref,
|
|
238
|
-
]), onKeyDown: handleKeyDown, onMouseDown: handleBackdropMouseDown, onMouseUp: handleBackdropMouseUp,
|
|
239
|
-
// eslint-disable-next-line jsx-a11y/no-noninteractive-tabindex
|
|
240
|
-
tabIndex: 0, "data-test-id": dataTestId, style: {
|
|
253
|
+
]), onKeyDown: handleKeyDown, onMouseDown: handleBackdropMouseDown, onMouseUp: handleBackdropMouseUp, tabIndex: -1, "data-test-id": dataTestId, style: {
|
|
241
254
|
zIndex: computedZIndex,
|
|
242
255
|
} }),
|
|
243
256
|
React.createElement(CSSTransition, __assign({ appear: true, timeout: 200, classNames: styles, nodeRef: componentNodeRef }, transitionProps, { in: open, onEntered: handleEntered, onExited: handleExited }),
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
var scrollY;
|
|
2
|
+
var lockScroll = function () {
|
|
3
|
+
scrollY = window.scrollY;
|
|
4
|
+
document.body.classList.add('is-locked');
|
|
5
|
+
};
|
|
6
|
+
var unlockScroll = function () {
|
|
7
|
+
document.body.classList.remove('is-locked');
|
|
8
|
+
window.scrollTo(0, scrollY);
|
|
9
|
+
};
|
|
10
|
+
var syncHeight = function () {
|
|
11
|
+
document.body.style.setProperty('--window-inner-scrollY', "".concat(window.scrollY, "px"));
|
|
12
|
+
};
|
|
13
|
+
|
|
14
|
+
export { lockScroll, syncHeight, unlockScroll };
|
package/esm/index.css
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
/* hash:
|
|
1
|
+
/* hash: 4eqb1 */
|
|
2
2
|
:root { /* deprecated */ /* deprecated */ /* deprecated */ /* deprecated */ /* deprecated */ /* deprecated */ /* deprecated */ /* deprecated */ /* deprecated */ /* deprecated */ /* deprecated */ /* deprecated */ /* deprecated */ /* deprecated */ /* deprecated */ /* deprecated */ /* deprecated */ /* deprecated */ /* deprecated */ /* deprecated */ /* deprecated */ /* deprecated */ /* deprecated */ /* deprecated */ /* deprecated */ /* deprecated */ /* deprecated */ /* deprecated */ /* deprecated */ /* deprecated */ /* deprecated */ /* deprecated */ /* deprecated */ /* deprecated */ /* deprecated */ /* deprecated */ /* deprecated */ /* deprecated */ /* deprecated */ /* deprecated */ /* deprecated */ /* deprecated */ /* deprecated */ /* deprecated */ /* deprecated */ /* deprecated */ /* deprecated */ /* deprecated */ /* deprecated */ /* deprecated */ /* deprecated */ /* deprecated */ /* deprecated */ /* deprecated */ /* deprecated */ /* deprecated */ /* deprecated */ /* deprecated */ /* deprecated */ /* deprecated */ /* deprecated */ /* deprecated */ /* deprecated */ /* deprecated */ /* deprecated */ /* deprecated */ /* deprecated */ /* deprecated */ /* deprecated */
|
|
3
3
|
} /* deprecated */ :root {
|
|
4
4
|
--color-light-modal-bg-primary: #fff; /* deprecated */ /* deprecated */ /* deprecated */ /* deprecated */ /* deprecated */ /* deprecated */ /* deprecated */ /* deprecated */ /* deprecated */ /* deprecated */ /* deprecated */ /* deprecated */ /* deprecated */ /* deprecated */ /* deprecated */ /* deprecated */ /* deprecated */ /* deprecated */ /* deprecated */ /* deprecated */ /* deprecated */ /* deprecated */ /* deprecated */ /* deprecated */ /* deprecated */ /* deprecated */ /* deprecated */ /* deprecated */ /* deprecated */ /* deprecated */ /* deprecated */ /* deprecated */ /* deprecated */ /* deprecated */ /* deprecated */ /* deprecated */ /* deprecated */ /* deprecated */
|
|
@@ -15,13 +15,19 @@
|
|
|
15
15
|
} :root {
|
|
16
16
|
} :root {
|
|
17
17
|
} :root {
|
|
18
|
-
}
|
|
18
|
+
} :root {
|
|
19
|
+
--window-inner-scrollY: 10px; /* fallback value to prevent ci error */
|
|
20
|
+
} body.is-locked {
|
|
21
|
+
margin-top: calc(var(--window-inner-scrollY) * -1);
|
|
22
|
+
position: fixed;
|
|
23
|
+
overflow: hidden;
|
|
24
|
+
} .base-modal__component_1apcq {
|
|
19
25
|
position: relative;
|
|
20
26
|
box-sizing: border-box;
|
|
21
27
|
background: var(--color-light-modal-bg-primary);
|
|
22
28
|
margin: auto;
|
|
23
29
|
flex-shrink: 0;
|
|
24
|
-
} .base-
|
|
30
|
+
} .base-modal__wrapper_1apcq {
|
|
25
31
|
position: fixed;
|
|
26
32
|
top: 0;
|
|
27
33
|
left: 0;
|
|
@@ -34,27 +40,27 @@
|
|
|
34
40
|
align-items: center;
|
|
35
41
|
outline: 0;
|
|
36
42
|
overscroll-behavior: none;
|
|
37
|
-
} .base-
|
|
43
|
+
} .base-modal__content_1apcq {
|
|
38
44
|
width: 100%;
|
|
39
45
|
height: 100%;
|
|
40
46
|
display: flex;
|
|
41
47
|
flex-direction: column;
|
|
42
48
|
flex: 1;
|
|
43
|
-
} .base-
|
|
49
|
+
} .base-modal__hidden_1apcq {
|
|
44
50
|
display: none;
|
|
45
|
-
} .base-
|
|
51
|
+
} .base-modal__backdrop_1apcq {
|
|
46
52
|
z-index: 0;
|
|
47
|
-
} .base-
|
|
48
|
-
.base-
|
|
53
|
+
} .base-modal__appear_1apcq,
|
|
54
|
+
.base-modal__enter_1apcq {
|
|
49
55
|
opacity: 0;
|
|
50
|
-
} .base-
|
|
51
|
-
.base-
|
|
56
|
+
} .base-modal__appearActive_1apcq,
|
|
57
|
+
.base-modal__enterActive_1apcq {
|
|
52
58
|
opacity: 1;
|
|
53
59
|
transition: opacity 200ms ease-in;
|
|
54
|
-
} .base-
|
|
60
|
+
} .base-modal__exit_1apcq {
|
|
55
61
|
opacity: 1;
|
|
56
|
-
} .base-
|
|
57
|
-
.base-
|
|
62
|
+
} .base-modal__exitActive_1apcq,
|
|
63
|
+
.base-modal__exitDone_1apcq {
|
|
58
64
|
opacity: 0;
|
|
59
65
|
transition: opacity 200ms ease-out;
|
|
60
66
|
}
|
package/esm/utils.d.ts
CHANGED
|
@@ -2,5 +2,5 @@ declare function isScrolledToTop(target: HTMLElement): boolean;
|
|
|
2
2
|
declare function isScrolledToBottom(target: HTMLElement): boolean;
|
|
3
3
|
declare function hasScrollbar(target: HTMLElement): boolean;
|
|
4
4
|
declare const restoreContainerStyles: (container: HTMLElement) => void;
|
|
5
|
-
declare const handleContainer: (container?: HTMLElement) => void;
|
|
5
|
+
declare const handleContainer: (container?: HTMLElement, shouldIOSLock?: boolean) => void;
|
|
6
6
|
export { isScrolledToTop, isScrolledToBottom, hasScrollbar, restoreContainerStyles, handleContainer };
|
package/esm/utils.js
CHANGED
|
@@ -39,7 +39,8 @@ var restoreContainerStyles = function (container) {
|
|
|
39
39
|
});
|
|
40
40
|
}
|
|
41
41
|
};
|
|
42
|
-
var handleContainer = function (container) {
|
|
42
|
+
var handleContainer = function (container, shouldIOSLock) {
|
|
43
|
+
if (shouldIOSLock === void 0) { shouldIOSLock = false; }
|
|
43
44
|
if (!container)
|
|
44
45
|
return;
|
|
45
46
|
var modalRestoreStyles = getModalStore().getRestoreStyles();
|
|
@@ -77,7 +78,9 @@ var handleContainer = function (container) {
|
|
|
77
78
|
el: scrollContainer,
|
|
78
79
|
});
|
|
79
80
|
}
|
|
80
|
-
|
|
81
|
+
if (!shouldIOSLock) {
|
|
82
|
+
scrollContainer.style.overflow = 'hidden';
|
|
83
|
+
}
|
|
81
84
|
modalRestoreStyles.push({
|
|
82
85
|
container: container,
|
|
83
86
|
modals: 1,
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
Object.defineProperty(exports, '__esModule', { value: true });
|
|
4
|
+
|
|
5
|
+
var scrollY;
|
|
6
|
+
var lockScroll = function () {
|
|
7
|
+
scrollY = window.scrollY;
|
|
8
|
+
document.body.classList.add('is-locked');
|
|
9
|
+
};
|
|
10
|
+
var unlockScroll = function () {
|
|
11
|
+
document.body.classList.remove('is-locked');
|
|
12
|
+
window.scrollTo(0, scrollY);
|
|
13
|
+
};
|
|
14
|
+
var syncHeight = function () {
|
|
15
|
+
document.body.style.setProperty('--window-inner-scrollY', "".concat(window.scrollY, "px"));
|
|
16
|
+
};
|
|
17
|
+
|
|
18
|
+
exports.lockScroll = lockScroll;
|
|
19
|
+
exports.syncHeight = syncHeight;
|
|
20
|
+
exports.unlockScroll = unlockScroll;
|
package/index.css
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
/* hash:
|
|
1
|
+
/* hash: 4eqb1 */
|
|
2
2
|
:root { /* deprecated */ /* deprecated */ /* deprecated */ /* deprecated */ /* deprecated */ /* deprecated */ /* deprecated */ /* deprecated */ /* deprecated */ /* deprecated */ /* deprecated */ /* deprecated */ /* deprecated */ /* deprecated */ /* deprecated */ /* deprecated */ /* deprecated */ /* deprecated */ /* deprecated */ /* deprecated */ /* deprecated */ /* deprecated */ /* deprecated */ /* deprecated */ /* deprecated */ /* deprecated */ /* deprecated */ /* deprecated */ /* deprecated */ /* deprecated */ /* deprecated */ /* deprecated */ /* deprecated */ /* deprecated */ /* deprecated */ /* deprecated */ /* deprecated */ /* deprecated */ /* deprecated */ /* deprecated */ /* deprecated */ /* deprecated */ /* deprecated */ /* deprecated */ /* deprecated */ /* deprecated */ /* deprecated */ /* deprecated */ /* deprecated */ /* deprecated */ /* deprecated */ /* deprecated */ /* deprecated */ /* deprecated */ /* deprecated */ /* deprecated */ /* deprecated */ /* deprecated */ /* deprecated */ /* deprecated */ /* deprecated */ /* deprecated */ /* deprecated */ /* deprecated */ /* deprecated */ /* deprecated */ /* deprecated */ /* deprecated */ /* deprecated */
|
|
3
3
|
} /* deprecated */ :root {
|
|
4
4
|
--color-light-modal-bg-primary: #fff; /* deprecated */ /* deprecated */ /* deprecated */ /* deprecated */ /* deprecated */ /* deprecated */ /* deprecated */ /* deprecated */ /* deprecated */ /* deprecated */ /* deprecated */ /* deprecated */ /* deprecated */ /* deprecated */ /* deprecated */ /* deprecated */ /* deprecated */ /* deprecated */ /* deprecated */ /* deprecated */ /* deprecated */ /* deprecated */ /* deprecated */ /* deprecated */ /* deprecated */ /* deprecated */ /* deprecated */ /* deprecated */ /* deprecated */ /* deprecated */ /* deprecated */ /* deprecated */ /* deprecated */ /* deprecated */ /* deprecated */ /* deprecated */ /* deprecated */ /* deprecated */
|
|
@@ -15,13 +15,19 @@
|
|
|
15
15
|
} :root {
|
|
16
16
|
} :root {
|
|
17
17
|
} :root {
|
|
18
|
-
}
|
|
18
|
+
} :root {
|
|
19
|
+
--window-inner-scrollY: 10px; /* fallback value to prevent ci error */
|
|
20
|
+
} body.is-locked {
|
|
21
|
+
margin-top: calc(var(--window-inner-scrollY) * -1);
|
|
22
|
+
position: fixed;
|
|
23
|
+
overflow: hidden;
|
|
24
|
+
} .base-modal__component_1apcq {
|
|
19
25
|
position: relative;
|
|
20
26
|
box-sizing: border-box;
|
|
21
27
|
background: var(--color-light-modal-bg-primary);
|
|
22
28
|
margin: auto;
|
|
23
29
|
flex-shrink: 0;
|
|
24
|
-
} .base-
|
|
30
|
+
} .base-modal__wrapper_1apcq {
|
|
25
31
|
position: fixed;
|
|
26
32
|
top: 0;
|
|
27
33
|
left: 0;
|
|
@@ -34,27 +40,27 @@
|
|
|
34
40
|
align-items: center;
|
|
35
41
|
outline: 0;
|
|
36
42
|
overscroll-behavior: none;
|
|
37
|
-
} .base-
|
|
43
|
+
} .base-modal__content_1apcq {
|
|
38
44
|
width: 100%;
|
|
39
45
|
height: 100%;
|
|
40
46
|
display: flex;
|
|
41
47
|
flex-direction: column;
|
|
42
48
|
flex: 1;
|
|
43
|
-
} .base-
|
|
49
|
+
} .base-modal__hidden_1apcq {
|
|
44
50
|
display: none;
|
|
45
|
-
} .base-
|
|
51
|
+
} .base-modal__backdrop_1apcq {
|
|
46
52
|
z-index: 0;
|
|
47
|
-
} .base-
|
|
48
|
-
.base-
|
|
53
|
+
} .base-modal__appear_1apcq,
|
|
54
|
+
.base-modal__enter_1apcq {
|
|
49
55
|
opacity: 0;
|
|
50
|
-
} .base-
|
|
51
|
-
.base-
|
|
56
|
+
} .base-modal__appearActive_1apcq,
|
|
57
|
+
.base-modal__enterActive_1apcq {
|
|
52
58
|
opacity: 1;
|
|
53
59
|
transition: opacity 200ms ease-in;
|
|
54
|
-
} .base-
|
|
60
|
+
} .base-modal__exit_1apcq {
|
|
55
61
|
opacity: 1;
|
|
56
|
-
} .base-
|
|
57
|
-
.base-
|
|
62
|
+
} .base-modal__exitActive_1apcq,
|
|
63
|
+
.base-modal__exitDone_1apcq {
|
|
58
64
|
opacity: 0;
|
|
59
65
|
transition: opacity 200ms ease-out;
|
|
60
66
|
}
|
package/modern/Component.d.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/// <reference types="react" />
|
|
2
2
|
/// <reference types="react-transition-group" />
|
|
3
3
|
import React from 'react';
|
|
4
|
-
import {
|
|
4
|
+
import { ComponentType, KeyboardEvent, MouseEvent, MutableRefObject, ReactNode, Ref } from "react";
|
|
5
5
|
import { TransitionProps } from 'react-transition-group/Transition';
|
|
6
6
|
import { BackdropProps } from "@alfalab/core-components-backdrop";
|
|
7
7
|
import { PortalProps } from "@alfalab/core-components-portal";
|
|
@@ -13,7 +13,7 @@ type BaseModalProps = {
|
|
|
13
13
|
/**
|
|
14
14
|
* Компонент бэкдропа
|
|
15
15
|
*/
|
|
16
|
-
Backdrop?:
|
|
16
|
+
Backdrop?: ComponentType<BackdropProps>;
|
|
17
17
|
/**
|
|
18
18
|
* Свойства для Бэкдропа
|
|
19
19
|
*/
|
|
@@ -134,6 +134,10 @@ type BaseModalProps = {
|
|
|
134
134
|
* Реф, который должен быть установлен компонентной области
|
|
135
135
|
*/
|
|
136
136
|
componentRef?: MutableRefObject<HTMLDivElement | null>;
|
|
137
|
+
/**
|
|
138
|
+
* Блокирует скролл когда модальное окно открыто. Работает только на iOS.
|
|
139
|
+
*/
|
|
140
|
+
iOSLock?: boolean;
|
|
137
141
|
};
|
|
138
142
|
type BaseModalContext = {
|
|
139
143
|
parentRef: React.RefObject<HTMLDivElement>;
|
package/modern/Component.js
CHANGED
|
@@ -6,12 +6,13 @@ import { ResizeObserver } from '@juggle/resize-observer';
|
|
|
6
6
|
import cn from 'classnames';
|
|
7
7
|
import { Backdrop } from '@alfalab/core-components-backdrop/modern';
|
|
8
8
|
import { Portal } from '@alfalab/core-components-portal/modern';
|
|
9
|
-
import { browser } from '@alfalab/core-components-shared/modern';
|
|
9
|
+
import { os, browser } from '@alfalab/core-components-shared/modern';
|
|
10
10
|
import { stackingOrder, Stack } from '@alfalab/core-components-stack/modern';
|
|
11
|
+
import { unlockScroll, syncHeight, lockScroll } from './helpers/lockScroll.js';
|
|
11
12
|
import { isScrolledToTop, isScrolledToBottom, handleContainer, restoreContainerStyles, hasScrollbar } from './utils.js';
|
|
12
13
|
import './matches-polyfill.js';
|
|
13
14
|
|
|
14
|
-
const styles = {"component":"base-
|
|
15
|
+
const styles = {"component":"base-modal__component_1apcq","wrapper":"base-modal__wrapper_1apcq","content":"base-modal__content_1apcq","hidden":"base-modal__hidden_1apcq","backdrop":"base-modal__backdrop_1apcq","appear":"base-modal__appear_1apcq","enter":"base-modal__enter_1apcq","appearActive":"base-modal__appearActive_1apcq","enterActive":"base-modal__enterActive_1apcq","exit":"base-modal__exit_1apcq","exitActive":"base-modal__exitActive_1apcq","exitDone":"base-modal__exitDone_1apcq"};
|
|
15
16
|
require('./index.css')
|
|
16
17
|
|
|
17
18
|
/* eslint-disable jsx-a11y/no-noninteractive-element-interactions */
|
|
@@ -31,7 +32,7 @@ const BaseModalContext = React.createContext({
|
|
|
31
32
|
setHasFooter: () => null,
|
|
32
33
|
onClose: () => null,
|
|
33
34
|
});
|
|
34
|
-
const BaseModal = forwardRef(({ open, container, children, scrollHandler = 'wrapper', Backdrop: Backdrop$1 = Backdrop, backdropProps = {}, transitionProps = {}, disableBackdropClick, disableAutoFocus = false, disableFocusLock = false, disableEscapeKeyDown = false, disableRestoreFocus = false, disableBlockingScroll = false, keepMounted = false, className, contentClassName, wrapperProps, contentProps, componentDivProps, wrapperClassName, onBackdropClick, onClose, onEscapeKeyDown, onMount, onUnmount, dataTestId, zIndex = stackingOrder.MODAL, componentRef = null, usePortal = true, }, ref) => {
|
|
35
|
+
const BaseModal = forwardRef(({ open, container, children, scrollHandler = 'wrapper', Backdrop: Backdrop$1 = Backdrop, backdropProps = {}, transitionProps = {}, disableBackdropClick, disableAutoFocus = false, disableFocusLock = false, disableEscapeKeyDown = false, disableRestoreFocus = false, disableBlockingScroll = false, keepMounted = false, className, contentClassName, wrapperProps, contentProps, componentDivProps, wrapperClassName, onBackdropClick, onClose, onEscapeKeyDown, onMount, onUnmount, dataTestId, zIndex = stackingOrder.MODAL, componentRef = null, usePortal = true, iOSLock = false, }, ref) => {
|
|
35
36
|
const [exited, setExited] = useState(null);
|
|
36
37
|
const [hasScroll, setHasScroll] = useState(false);
|
|
37
38
|
const [hasHeader, setHasHeader] = useState(false);
|
|
@@ -90,6 +91,9 @@ const BaseModal = forwardRef(({ open, container, children, scrollHandler = 'wrap
|
|
|
90
91
|
}
|
|
91
92
|
}, [hasFooter, hasHeader, headerOffset]);
|
|
92
93
|
const handleClose = useCallback((event, reason) => {
|
|
94
|
+
if (iOSLock && os.isIOS()) {
|
|
95
|
+
unlockScroll();
|
|
96
|
+
}
|
|
93
97
|
if (onClose) {
|
|
94
98
|
onClose(event, reason);
|
|
95
99
|
}
|
|
@@ -100,7 +104,7 @@ const BaseModal = forwardRef(({ open, container, children, scrollHandler = 'wrap
|
|
|
100
104
|
onEscapeKeyDown(event);
|
|
101
105
|
}
|
|
102
106
|
return null;
|
|
103
|
-
}, [onBackdropClick, onClose, onEscapeKeyDown]);
|
|
107
|
+
}, [onBackdropClick, onClose, onEscapeKeyDown, iOSLock]);
|
|
104
108
|
const handleBackdropMouseDown = (event) => {
|
|
105
109
|
let clickedOnScrollbar = false;
|
|
106
110
|
const clientWidth = event.target?.clientWidth;
|
|
@@ -174,7 +178,12 @@ const BaseModal = forwardRef(({ open, container, children, scrollHandler = 'wrap
|
|
|
174
178
|
if (open && isExited) {
|
|
175
179
|
if (!disableBlockingScroll) {
|
|
176
180
|
const el = getContainer();
|
|
177
|
-
|
|
181
|
+
const shouldIOSLock = iOSLock && os.isIOS();
|
|
182
|
+
handleContainer(el, shouldIOSLock);
|
|
183
|
+
if (shouldIOSLock) {
|
|
184
|
+
syncHeight();
|
|
185
|
+
lockScroll();
|
|
186
|
+
}
|
|
178
187
|
restoreContainerStylesRef.current = () => {
|
|
179
188
|
restoreContainerStylesRef.current = null;
|
|
180
189
|
restoreContainerStyles(el);
|
|
@@ -182,7 +191,7 @@ const BaseModal = forwardRef(({ open, container, children, scrollHandler = 'wrap
|
|
|
182
191
|
}
|
|
183
192
|
setExited(false);
|
|
184
193
|
}
|
|
185
|
-
}, [getContainer, open, disableBlockingScroll, isExited]);
|
|
194
|
+
}, [getContainer, open, disableBlockingScroll, isExited, iOSLock]);
|
|
186
195
|
useEffect(() => {
|
|
187
196
|
const ResizeObserver$1 = window.ResizeObserver || ResizeObserver;
|
|
188
197
|
resizeObserverRef.current = new ResizeObserver$1(checkToHasScrollBar);
|
|
@@ -195,6 +204,11 @@ const BaseModal = forwardRef(({ open, container, children, scrollHandler = 'wrap
|
|
|
195
204
|
}
|
|
196
205
|
};
|
|
197
206
|
}, []);
|
|
207
|
+
useEffect(() => {
|
|
208
|
+
if (disableAutoFocus || !open)
|
|
209
|
+
return;
|
|
210
|
+
wrapperRef.current?.focus();
|
|
211
|
+
}, [open, disableAutoFocus]);
|
|
198
212
|
const contextValue = useMemo(() => ({
|
|
199
213
|
parentRef: wrapperRef,
|
|
200
214
|
componentRef: componentNodeRef,
|
|
@@ -221,7 +235,7 @@ const BaseModal = forwardRef(({ open, container, children, scrollHandler = 'wrap
|
|
|
221
235
|
handleClose,
|
|
222
236
|
]);
|
|
223
237
|
const renderContent = () => (React.createElement(Stack, { value: zIndex }, (computedZIndex) => (React.createElement(BaseModalContext.Provider, { value: contextValue },
|
|
224
|
-
React.createElement(FocusLock, {
|
|
238
|
+
React.createElement(FocusLock, { disabled: disableFocusLock || !open, returnFocus: !disableRestoreFocus },
|
|
225
239
|
Backdrop$1 && (React.createElement(Backdrop$1, { ...backdropProps, className: cn(backdropProps.className, styles.backdrop), open: open, style: {
|
|
226
240
|
zIndex: computedZIndex,
|
|
227
241
|
} })),
|
|
@@ -231,9 +245,7 @@ const BaseModal = forwardRef(({ open, container, children, scrollHandler = 'wrap
|
|
|
231
245
|
ref,
|
|
232
246
|
wrapperRef,
|
|
233
247
|
wrapperProps?.ref,
|
|
234
|
-
]), onKeyDown: handleKeyDown, onMouseDown: handleBackdropMouseDown, onMouseUp: handleBackdropMouseUp,
|
|
235
|
-
// eslint-disable-next-line jsx-a11y/no-noninteractive-tabindex
|
|
236
|
-
tabIndex: 0, "data-test-id": dataTestId, style: {
|
|
248
|
+
]), onKeyDown: handleKeyDown, onMouseDown: handleBackdropMouseDown, onMouseUp: handleBackdropMouseUp, tabIndex: -1, "data-test-id": dataTestId, style: {
|
|
237
249
|
zIndex: computedZIndex,
|
|
238
250
|
} },
|
|
239
251
|
React.createElement(CSSTransition, { appear: true, timeout: 200, classNames: styles, nodeRef: componentNodeRef, ...transitionProps, in: open, onEntered: handleEntered, onExited: handleExited },
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
let scrollY;
|
|
2
|
+
const lockScroll = () => {
|
|
3
|
+
scrollY = window.scrollY;
|
|
4
|
+
document.body.classList.add('is-locked');
|
|
5
|
+
};
|
|
6
|
+
const unlockScroll = () => {
|
|
7
|
+
document.body.classList.remove('is-locked');
|
|
8
|
+
window.scrollTo(0, scrollY);
|
|
9
|
+
};
|
|
10
|
+
const syncHeight = () => {
|
|
11
|
+
document.body.style.setProperty('--window-inner-scrollY', `${window.scrollY}px`);
|
|
12
|
+
};
|
|
13
|
+
|
|
14
|
+
export { lockScroll, syncHeight, unlockScroll };
|
package/modern/index.css
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
/* hash:
|
|
1
|
+
/* hash: 4eqb1 */
|
|
2
2
|
:root { /* deprecated */ /* deprecated */ /* deprecated */ /* deprecated */ /* deprecated */ /* deprecated */ /* deprecated */ /* deprecated */ /* deprecated */ /* deprecated */ /* deprecated */ /* deprecated */ /* deprecated */ /* deprecated */ /* deprecated */ /* deprecated */ /* deprecated */ /* deprecated */ /* deprecated */ /* deprecated */ /* deprecated */ /* deprecated */ /* deprecated */ /* deprecated */ /* deprecated */ /* deprecated */ /* deprecated */ /* deprecated */ /* deprecated */ /* deprecated */ /* deprecated */ /* deprecated */ /* deprecated */ /* deprecated */ /* deprecated */ /* deprecated */ /* deprecated */ /* deprecated */ /* deprecated */ /* deprecated */ /* deprecated */ /* deprecated */ /* deprecated */ /* deprecated */ /* deprecated */ /* deprecated */ /* deprecated */ /* deprecated */ /* deprecated */ /* deprecated */ /* deprecated */ /* deprecated */ /* deprecated */ /* deprecated */ /* deprecated */ /* deprecated */ /* deprecated */ /* deprecated */ /* deprecated */ /* deprecated */ /* deprecated */ /* deprecated */ /* deprecated */ /* deprecated */ /* deprecated */ /* deprecated */ /* deprecated */ /* deprecated */ /* deprecated */
|
|
3
3
|
} /* deprecated */ :root {
|
|
4
4
|
--color-light-modal-bg-primary: #fff; /* deprecated */ /* deprecated */ /* deprecated */ /* deprecated */ /* deprecated */ /* deprecated */ /* deprecated */ /* deprecated */ /* deprecated */ /* deprecated */ /* deprecated */ /* deprecated */ /* deprecated */ /* deprecated */ /* deprecated */ /* deprecated */ /* deprecated */ /* deprecated */ /* deprecated */ /* deprecated */ /* deprecated */ /* deprecated */ /* deprecated */ /* deprecated */ /* deprecated */ /* deprecated */ /* deprecated */ /* deprecated */ /* deprecated */ /* deprecated */ /* deprecated */ /* deprecated */ /* deprecated */ /* deprecated */ /* deprecated */ /* deprecated */ /* deprecated */ /* deprecated */
|
|
@@ -15,13 +15,19 @@
|
|
|
15
15
|
} :root {
|
|
16
16
|
} :root {
|
|
17
17
|
} :root {
|
|
18
|
-
}
|
|
18
|
+
} :root {
|
|
19
|
+
--window-inner-scrollY: 10px; /* fallback value to prevent ci error */
|
|
20
|
+
} body.is-locked {
|
|
21
|
+
margin-top: calc(var(--window-inner-scrollY) * -1);
|
|
22
|
+
position: fixed;
|
|
23
|
+
overflow: hidden;
|
|
24
|
+
} .base-modal__component_1apcq {
|
|
19
25
|
position: relative;
|
|
20
26
|
box-sizing: border-box;
|
|
21
27
|
background: var(--color-light-modal-bg-primary);
|
|
22
28
|
margin: auto;
|
|
23
29
|
flex-shrink: 0;
|
|
24
|
-
} .base-
|
|
30
|
+
} .base-modal__wrapper_1apcq {
|
|
25
31
|
position: fixed;
|
|
26
32
|
top: 0;
|
|
27
33
|
left: 0;
|
|
@@ -34,27 +40,27 @@
|
|
|
34
40
|
align-items: center;
|
|
35
41
|
outline: 0;
|
|
36
42
|
overscroll-behavior: none;
|
|
37
|
-
} .base-
|
|
43
|
+
} .base-modal__content_1apcq {
|
|
38
44
|
width: 100%;
|
|
39
45
|
height: 100%;
|
|
40
46
|
display: flex;
|
|
41
47
|
flex-direction: column;
|
|
42
48
|
flex: 1;
|
|
43
|
-
} .base-
|
|
49
|
+
} .base-modal__hidden_1apcq {
|
|
44
50
|
display: none;
|
|
45
|
-
} .base-
|
|
51
|
+
} .base-modal__backdrop_1apcq {
|
|
46
52
|
z-index: 0;
|
|
47
|
-
} .base-
|
|
48
|
-
.base-
|
|
53
|
+
} .base-modal__appear_1apcq,
|
|
54
|
+
.base-modal__enter_1apcq {
|
|
49
55
|
opacity: 0;
|
|
50
|
-
} .base-
|
|
51
|
-
.base-
|
|
56
|
+
} .base-modal__appearActive_1apcq,
|
|
57
|
+
.base-modal__enterActive_1apcq {
|
|
52
58
|
opacity: 1;
|
|
53
59
|
transition: opacity 200ms ease-in;
|
|
54
|
-
} .base-
|
|
60
|
+
} .base-modal__exit_1apcq {
|
|
55
61
|
opacity: 1;
|
|
56
|
-
} .base-
|
|
57
|
-
.base-
|
|
62
|
+
} .base-modal__exitActive_1apcq,
|
|
63
|
+
.base-modal__exitDone_1apcq {
|
|
58
64
|
opacity: 0;
|
|
59
65
|
transition: opacity 200ms ease-out;
|
|
60
66
|
}
|
package/modern/utils.d.ts
CHANGED
|
@@ -2,5 +2,5 @@ declare function isScrolledToTop(target: HTMLElement): boolean;
|
|
|
2
2
|
declare function isScrolledToBottom(target: HTMLElement): boolean;
|
|
3
3
|
declare function hasScrollbar(target: HTMLElement): boolean;
|
|
4
4
|
declare const restoreContainerStyles: (container: HTMLElement) => void;
|
|
5
|
-
declare const handleContainer: (container?: HTMLElement) => void;
|
|
5
|
+
declare const handleContainer: (container?: HTMLElement, shouldIOSLock?: boolean) => void;
|
|
6
6
|
export { isScrolledToTop, isScrolledToBottom, hasScrollbar, restoreContainerStyles, handleContainer };
|
package/modern/utils.js
CHANGED
|
@@ -36,7 +36,7 @@ const restoreContainerStyles = (container) => {
|
|
|
36
36
|
});
|
|
37
37
|
}
|
|
38
38
|
};
|
|
39
|
-
const handleContainer = (container) => {
|
|
39
|
+
const handleContainer = (container, shouldIOSLock = false) => {
|
|
40
40
|
if (!container)
|
|
41
41
|
return;
|
|
42
42
|
const modalRestoreStyles = getModalStore().getRestoreStyles();
|
|
@@ -74,7 +74,9 @@ const handleContainer = (container) => {
|
|
|
74
74
|
el: scrollContainer,
|
|
75
75
|
});
|
|
76
76
|
}
|
|
77
|
-
|
|
77
|
+
if (!shouldIOSLock) {
|
|
78
|
+
scrollContainer.style.overflow = 'hidden';
|
|
79
|
+
}
|
|
78
80
|
modalRestoreStyles.push({
|
|
79
81
|
container,
|
|
80
82
|
modals: 1,
|
package/package.json
CHANGED
package/src/Component.tsx
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
/* eslint-disable jsx-a11y/no-noninteractive-element-interactions */
|
|
2
2
|
import React, {
|
|
3
|
-
|
|
3
|
+
ComponentType,
|
|
4
4
|
forwardRef,
|
|
5
5
|
KeyboardEvent,
|
|
6
6
|
MouseEvent,
|
|
@@ -22,9 +22,10 @@ import cn from 'classnames';
|
|
|
22
22
|
|
|
23
23
|
import { Backdrop as DefaultBackdrop, BackdropProps } from '@alfalab/core-components-backdrop';
|
|
24
24
|
import { Portal, PortalProps } from '@alfalab/core-components-portal';
|
|
25
|
-
import { browser } from '@alfalab/core-components-shared';
|
|
25
|
+
import { browser, os } from '@alfalab/core-components-shared';
|
|
26
26
|
import { Stack, stackingOrder } from '@alfalab/core-components-stack';
|
|
27
27
|
|
|
28
|
+
import { lockScroll, syncHeight, unlockScroll } from './helpers/lockScroll';
|
|
28
29
|
import {
|
|
29
30
|
handleContainer,
|
|
30
31
|
hasScrollbar,
|
|
@@ -47,7 +48,7 @@ export type BaseModalProps = {
|
|
|
47
48
|
/**
|
|
48
49
|
* Компонент бэкдропа
|
|
49
50
|
*/
|
|
50
|
-
Backdrop?:
|
|
51
|
+
Backdrop?: ComponentType<BackdropProps>;
|
|
51
52
|
|
|
52
53
|
/**
|
|
53
54
|
* Свойства для Бэкдропа
|
|
@@ -201,6 +202,11 @@ export type BaseModalProps = {
|
|
|
201
202
|
* Реф, который должен быть установлен компонентной области
|
|
202
203
|
*/
|
|
203
204
|
componentRef?: MutableRefObject<HTMLDivElement | null>;
|
|
205
|
+
|
|
206
|
+
/**
|
|
207
|
+
* Блокирует скролл когда модальное окно открыто. Работает только на iOS.
|
|
208
|
+
*/
|
|
209
|
+
iOSLock?: boolean;
|
|
204
210
|
};
|
|
205
211
|
|
|
206
212
|
export type BaseModalContext = {
|
|
@@ -268,6 +274,7 @@ export const BaseModal = forwardRef<HTMLDivElement, BaseModalProps>(
|
|
|
268
274
|
zIndex = stackingOrder.MODAL,
|
|
269
275
|
componentRef = null,
|
|
270
276
|
usePortal = true,
|
|
277
|
+
iOSLock = false,
|
|
271
278
|
},
|
|
272
279
|
ref,
|
|
273
280
|
) => {
|
|
@@ -348,6 +355,10 @@ export const BaseModal = forwardRef<HTMLDivElement, BaseModalProps>(
|
|
|
348
355
|
|
|
349
356
|
const handleClose = useCallback<Required<BaseModalProps>['onClose']>(
|
|
350
357
|
(event, reason) => {
|
|
358
|
+
if (iOSLock && os.isIOS()) {
|
|
359
|
+
unlockScroll();
|
|
360
|
+
}
|
|
361
|
+
|
|
351
362
|
if (onClose) {
|
|
352
363
|
onClose(event, reason);
|
|
353
364
|
}
|
|
@@ -362,7 +373,7 @@ export const BaseModal = forwardRef<HTMLDivElement, BaseModalProps>(
|
|
|
362
373
|
|
|
363
374
|
return null;
|
|
364
375
|
},
|
|
365
|
-
[onBackdropClick, onClose, onEscapeKeyDown],
|
|
376
|
+
[onBackdropClick, onClose, onEscapeKeyDown, iOSLock],
|
|
366
377
|
);
|
|
367
378
|
|
|
368
379
|
const handleBackdropMouseDown = (event: MouseEvent<HTMLElement>) => {
|
|
@@ -468,7 +479,13 @@ export const BaseModal = forwardRef<HTMLDivElement, BaseModalProps>(
|
|
|
468
479
|
if (!disableBlockingScroll) {
|
|
469
480
|
const el = getContainer();
|
|
470
481
|
|
|
471
|
-
|
|
482
|
+
const shouldIOSLock = iOSLock && os.isIOS();
|
|
483
|
+
|
|
484
|
+
handleContainer(el, shouldIOSLock);
|
|
485
|
+
if (shouldIOSLock) {
|
|
486
|
+
syncHeight();
|
|
487
|
+
lockScroll();
|
|
488
|
+
}
|
|
472
489
|
|
|
473
490
|
restoreContainerStylesRef.current = () => {
|
|
474
491
|
restoreContainerStylesRef.current = null;
|
|
@@ -478,7 +495,7 @@ export const BaseModal = forwardRef<HTMLDivElement, BaseModalProps>(
|
|
|
478
495
|
|
|
479
496
|
setExited(false);
|
|
480
497
|
}
|
|
481
|
-
}, [getContainer, open, disableBlockingScroll, isExited]);
|
|
498
|
+
}, [getContainer, open, disableBlockingScroll, isExited, iOSLock]);
|
|
482
499
|
|
|
483
500
|
useEffect(() => {
|
|
484
501
|
const ResizeObserver = window.ResizeObserver || ResizeObserverPolyfill;
|
|
@@ -496,6 +513,12 @@ export const BaseModal = forwardRef<HTMLDivElement, BaseModalProps>(
|
|
|
496
513
|
};
|
|
497
514
|
}, []);
|
|
498
515
|
|
|
516
|
+
useEffect(() => {
|
|
517
|
+
if (disableAutoFocus || !open) return;
|
|
518
|
+
|
|
519
|
+
wrapperRef.current?.focus();
|
|
520
|
+
}, [open, disableAutoFocus]);
|
|
521
|
+
|
|
499
522
|
const contextValue = useMemo<BaseModalContext>(
|
|
500
523
|
() => ({
|
|
501
524
|
parentRef: wrapperRef,
|
|
@@ -530,7 +553,6 @@ export const BaseModal = forwardRef<HTMLDivElement, BaseModalProps>(
|
|
|
530
553
|
{(computedZIndex) => (
|
|
531
554
|
<BaseModalContext.Provider value={contextValue}>
|
|
532
555
|
<FocusLock
|
|
533
|
-
autoFocus={!disableAutoFocus}
|
|
534
556
|
disabled={disableFocusLock || !open}
|
|
535
557
|
returnFocus={!disableRestoreFocus}
|
|
536
558
|
>
|
|
@@ -563,8 +585,7 @@ export const BaseModal = forwardRef<HTMLDivElement, BaseModalProps>(
|
|
|
563
585
|
onKeyDown={handleKeyDown}
|
|
564
586
|
onMouseDown={handleBackdropMouseDown}
|
|
565
587
|
onMouseUp={handleBackdropMouseUp}
|
|
566
|
-
|
|
567
|
-
tabIndex={0}
|
|
588
|
+
tabIndex={-1}
|
|
568
589
|
data-test-id={dataTestId}
|
|
569
590
|
style={{
|
|
570
591
|
zIndex: computedZIndex,
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
let scrollY: number;
|
|
2
|
+
|
|
3
|
+
export const lockScroll = () => {
|
|
4
|
+
scrollY = window.scrollY;
|
|
5
|
+
document.body.classList.add('is-locked');
|
|
6
|
+
};
|
|
7
|
+
|
|
8
|
+
export const unlockScroll = () => {
|
|
9
|
+
document.body.classList.remove('is-locked');
|
|
10
|
+
window.scrollTo(0, scrollY);
|
|
11
|
+
};
|
|
12
|
+
|
|
13
|
+
export const syncHeight = () => {
|
|
14
|
+
document.body.style.setProperty('--window-inner-scrollY', `${window.scrollY}px`);
|
|
15
|
+
};
|
package/src/index.module.css
CHANGED
|
@@ -1,5 +1,15 @@
|
|
|
1
1
|
@import '@alfalab/core-components-themes/src/default.css';
|
|
2
2
|
|
|
3
|
+
:root {
|
|
4
|
+
--window-inner-scrollY: 10px; /* fallback value to prevent ci error */
|
|
5
|
+
}
|
|
6
|
+
|
|
7
|
+
body:global(.is-locked) {
|
|
8
|
+
margin-top: calc(var(--window-inner-scrollY) * -1);
|
|
9
|
+
position: fixed;
|
|
10
|
+
overflow: hidden;
|
|
11
|
+
}
|
|
12
|
+
|
|
3
13
|
.component {
|
|
4
14
|
position: relative;
|
|
5
15
|
box-sizing: border-box;
|
package/src/utils.ts
CHANGED
|
@@ -47,7 +47,7 @@ export const restoreContainerStyles = (container: HTMLElement) => {
|
|
|
47
47
|
}
|
|
48
48
|
};
|
|
49
49
|
|
|
50
|
-
export const handleContainer = (container?: HTMLElement) => {
|
|
50
|
+
export const handleContainer = (container?: HTMLElement, shouldIOSLock = false) => {
|
|
51
51
|
if (!container) return;
|
|
52
52
|
|
|
53
53
|
const modalRestoreStyles = getModalStore().getRestoreStyles();
|
|
@@ -94,7 +94,9 @@ export const handleContainer = (container?: HTMLElement) => {
|
|
|
94
94
|
});
|
|
95
95
|
}
|
|
96
96
|
|
|
97
|
-
|
|
97
|
+
if (!shouldIOSLock) {
|
|
98
|
+
scrollContainer.style.overflow = 'hidden';
|
|
99
|
+
}
|
|
98
100
|
|
|
99
101
|
modalRestoreStyles.push({
|
|
100
102
|
container,
|
package/utils.d.ts
CHANGED
|
@@ -2,5 +2,5 @@ declare function isScrolledToTop(target: HTMLElement): boolean;
|
|
|
2
2
|
declare function isScrolledToBottom(target: HTMLElement): boolean;
|
|
3
3
|
declare function hasScrollbar(target: HTMLElement): boolean;
|
|
4
4
|
declare const restoreContainerStyles: (container: HTMLElement) => void;
|
|
5
|
-
declare const handleContainer: (container?: HTMLElement) => void;
|
|
5
|
+
declare const handleContainer: (container?: HTMLElement, shouldIOSLock?: boolean) => void;
|
|
6
6
|
export { isScrolledToTop, isScrolledToBottom, hasScrollbar, restoreContainerStyles, handleContainer };
|
package/utils.js
CHANGED
|
@@ -43,7 +43,8 @@ var restoreContainerStyles = function (container) {
|
|
|
43
43
|
});
|
|
44
44
|
}
|
|
45
45
|
};
|
|
46
|
-
var handleContainer = function (container) {
|
|
46
|
+
var handleContainer = function (container, shouldIOSLock) {
|
|
47
|
+
if (shouldIOSLock === void 0) { shouldIOSLock = false; }
|
|
47
48
|
if (!container)
|
|
48
49
|
return;
|
|
49
50
|
var modalRestoreStyles = coreComponentsGlobalStore.getModalStore().getRestoreStyles();
|
|
@@ -81,7 +82,9 @@ var handleContainer = function (container) {
|
|
|
81
82
|
el: scrollContainer,
|
|
82
83
|
});
|
|
83
84
|
}
|
|
84
|
-
|
|
85
|
+
if (!shouldIOSLock) {
|
|
86
|
+
scrollContainer.style.overflow = 'hidden';
|
|
87
|
+
}
|
|
85
88
|
modalRestoreStyles.push({
|
|
86
89
|
container: container,
|
|
87
90
|
modals: 1,
|