@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 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 { FC, KeyboardEvent, MouseEvent, MutableRefObject, ReactNode, Ref } from "react";
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?: FC<BackdropProps>;
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-modal__component_13am9","wrapper":"base-modal__wrapper_13am9","content":"base-modal__content_13am9","hidden":"base-modal__hidden_13am9","backdrop":"base-modal__backdrop_13am9","appear":"base-modal__appear_13am9","enter":"base-modal__enter_13am9","appearActive":"base-modal__appearActive_13am9","enterActive":"base-modal__enterActive_13am9","exit":"base-modal__exit_13am9","exitActive":"base-modal__exitActive_13am9","exitDone":"base-modal__exitDone_13am9"};
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 _q = React.useState(null), exited = _q[0], setExited = _q[1];
48
- var _r = React.useState(false), hasScroll = _r[0], setHasScroll = _r[1];
49
- var _s = React.useState(false), hasHeader = _s[0], setHasHeader = _s[1];
50
- var _t = React.useState(false), hasFooter = _t[0], setHasFooter = _t[1];
51
- var _u = React.useState(false), headerHighlighted = _u[0], setHeaderHighlighted = _u[1];
52
- var _v = React.useState(false), footerHighlighted = _v[0], setFooterHighlighted = _v[1];
53
- var _w = React.useState(0), headerOffset = _w[0], setHeaderOffset = _w[1];
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
- utils.handleContainer(el_1);
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, { autoFocus: !disableAutoFocus, disabled: disableFocusLock || !open, returnFocus: !disableRestoreFocus },
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 }),
@@ -1,7 +1,7 @@
1
1
  /// <reference types="react" />
2
2
  /// <reference types="react-transition-group" />
3
3
  import React from 'react';
4
- import { FC, KeyboardEvent, MouseEvent, MutableRefObject, ReactNode, Ref } from "react";
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?: FC<BackdropProps>;
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 _q = React.useState(null), exited = _q[0], setExited = _q[1];
47
- var _r = React.useState(false), hasScroll = _r[0], setHasScroll = _r[1];
48
- var _s = React.useState(false), hasHeader = _s[0], setHasHeader = _s[1];
49
- var _t = React.useState(false), hasFooter = _t[0], setHasFooter = _t[1];
50
- var _u = React.useState(false), headerHighlighted = _u[0], setHeaderHighlighted = _u[1];
51
- var _v = React.useState(false), footerHighlighted = _v[0], setFooterHighlighted = _v[1];
52
- var _w = React.useState(0), headerOffset = _w[0], setHeaderOffset = _w[1];
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
- utils.handleContainer(el_1);
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, { autoFocus: !disableAutoFocus, disabled: disableFocusLock || !open, returnFocus: !disableRestoreFocus },
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,4 @@
1
+ declare const lockScroll: () => void;
2
+ declare const unlockScroll: () => void;
3
+ declare const syncHeight: () => void;
4
+ export { lockScroll, unlockScroll, syncHeight };
@@ -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;
@@ -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
- scrollContainer.style.overflow = 'hidden';
85
+ if (!shouldIOSLock) {
86
+ scrollContainer.style.overflow = 'hidden';
87
+ }
85
88
  modalRestoreStyles.push({
86
89
  container: container,
87
90
  modals: 1,
@@ -1,7 +1,7 @@
1
1
  /// <reference types="react" />
2
2
  /// <reference types="react-transition-group" />
3
3
  import React from 'react';
4
- import { FC, KeyboardEvent, MouseEvent, MutableRefObject, ReactNode, Ref } from "react";
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?: FC<BackdropProps>;
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-modal__component_13am9","wrapper":"base-modal__wrapper_13am9","content":"base-modal__content_13am9","hidden":"base-modal__hidden_13am9","backdrop":"base-modal__backdrop_13am9","appear":"base-modal__appear_13am9","enter":"base-modal__enter_13am9","appearActive":"base-modal__appearActive_13am9","enterActive":"base-modal__enterActive_13am9","exit":"base-modal__exit_13am9","exitActive":"base-modal__exitActive_13am9","exitDone":"base-modal__exitDone_13am9"};
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 _q = useState(null), exited = _q[0], setExited = _q[1];
37
- var _r = useState(false), hasScroll = _r[0], setHasScroll = _r[1];
38
- var _s = useState(false), hasHeader = _s[0], setHasHeader = _s[1];
39
- var _t = useState(false), hasFooter = _t[0], setHasFooter = _t[1];
40
- var _u = useState(false), headerHighlighted = _u[0], setHeaderHighlighted = _u[1];
41
- var _v = useState(false), footerHighlighted = _v[0], setFooterHighlighted = _v[1];
42
- var _w = useState(0), headerOffset = _w[0], setHeaderOffset = _w[1];
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
- handleContainer(el_1);
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, { autoFocus: !disableAutoFocus, disabled: disableFocusLock || !open, returnFocus: !disableRestoreFocus },
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,4 @@
1
+ declare const lockScroll: () => void;
2
+ declare const unlockScroll: () => void;
3
+ declare const syncHeight: () => void;
4
+ export { lockScroll, unlockScroll, syncHeight };
@@ -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: 1npo2 */
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
- } .base-modal__component_13am9 {
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-modal__wrapper_13am9 {
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-modal__content_13am9 {
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-modal__hidden_13am9 {
49
+ } .base-modal__hidden_1apcq {
44
50
  display: none;
45
- } .base-modal__backdrop_13am9 {
51
+ } .base-modal__backdrop_1apcq {
46
52
  z-index: 0;
47
- } .base-modal__appear_13am9,
48
- .base-modal__enter_13am9 {
53
+ } .base-modal__appear_1apcq,
54
+ .base-modal__enter_1apcq {
49
55
  opacity: 0;
50
- } .base-modal__appearActive_13am9,
51
- .base-modal__enterActive_13am9 {
56
+ } .base-modal__appearActive_1apcq,
57
+ .base-modal__enterActive_1apcq {
52
58
  opacity: 1;
53
59
  transition: opacity 200ms ease-in;
54
- } .base-modal__exit_13am9 {
60
+ } .base-modal__exit_1apcq {
55
61
  opacity: 1;
56
- } .base-modal__exitActive_13am9,
57
- .base-modal__exitDone_13am9 {
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
- scrollContainer.style.overflow = 'hidden';
81
+ if (!shouldIOSLock) {
82
+ scrollContainer.style.overflow = 'hidden';
83
+ }
81
84
  modalRestoreStyles.push({
82
85
  container: container,
83
86
  modals: 1,
@@ -0,0 +1,4 @@
1
+ declare const lockScroll: () => void;
2
+ declare const unlockScroll: () => void;
3
+ declare const syncHeight: () => void;
4
+ export { lockScroll, unlockScroll, syncHeight };
@@ -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: 1npo2 */
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
- } .base-modal__component_13am9 {
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-modal__wrapper_13am9 {
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-modal__content_13am9 {
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-modal__hidden_13am9 {
49
+ } .base-modal__hidden_1apcq {
44
50
  display: none;
45
- } .base-modal__backdrop_13am9 {
51
+ } .base-modal__backdrop_1apcq {
46
52
  z-index: 0;
47
- } .base-modal__appear_13am9,
48
- .base-modal__enter_13am9 {
53
+ } .base-modal__appear_1apcq,
54
+ .base-modal__enter_1apcq {
49
55
  opacity: 0;
50
- } .base-modal__appearActive_13am9,
51
- .base-modal__enterActive_13am9 {
56
+ } .base-modal__appearActive_1apcq,
57
+ .base-modal__enterActive_1apcq {
52
58
  opacity: 1;
53
59
  transition: opacity 200ms ease-in;
54
- } .base-modal__exit_13am9 {
60
+ } .base-modal__exit_1apcq {
55
61
  opacity: 1;
56
- } .base-modal__exitActive_13am9,
57
- .base-modal__exitDone_13am9 {
62
+ } .base-modal__exitActive_1apcq,
63
+ .base-modal__exitDone_1apcq {
58
64
  opacity: 0;
59
65
  transition: opacity 200ms ease-out;
60
66
  }
@@ -1,7 +1,7 @@
1
1
  /// <reference types="react" />
2
2
  /// <reference types="react-transition-group" />
3
3
  import React from 'react';
4
- import { FC, KeyboardEvent, MouseEvent, MutableRefObject, ReactNode, Ref } from "react";
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?: FC<BackdropProps>;
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>;
@@ -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-modal__component_13am9","wrapper":"base-modal__wrapper_13am9","content":"base-modal__content_13am9","hidden":"base-modal__hidden_13am9","backdrop":"base-modal__backdrop_13am9","appear":"base-modal__appear_13am9","enter":"base-modal__enter_13am9","appearActive":"base-modal__appearActive_13am9","enterActive":"base-modal__enterActive_13am9","exit":"base-modal__exit_13am9","exitActive":"base-modal__exitActive_13am9","exitDone":"base-modal__exitDone_13am9"};
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
- handleContainer(el);
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, { autoFocus: !disableAutoFocus, disabled: disableFocusLock || !open, returnFocus: !disableRestoreFocus },
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,4 @@
1
+ declare const lockScroll: () => void;
2
+ declare const unlockScroll: () => void;
3
+ declare const syncHeight: () => void;
4
+ export { lockScroll, unlockScroll, syncHeight };
@@ -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: 1npo2 */
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
- } .base-modal__component_13am9 {
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-modal__wrapper_13am9 {
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-modal__content_13am9 {
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-modal__hidden_13am9 {
49
+ } .base-modal__hidden_1apcq {
44
50
  display: none;
45
- } .base-modal__backdrop_13am9 {
51
+ } .base-modal__backdrop_1apcq {
46
52
  z-index: 0;
47
- } .base-modal__appear_13am9,
48
- .base-modal__enter_13am9 {
53
+ } .base-modal__appear_1apcq,
54
+ .base-modal__enter_1apcq {
49
55
  opacity: 0;
50
- } .base-modal__appearActive_13am9,
51
- .base-modal__enterActive_13am9 {
56
+ } .base-modal__appearActive_1apcq,
57
+ .base-modal__enterActive_1apcq {
52
58
  opacity: 1;
53
59
  transition: opacity 200ms ease-in;
54
- } .base-modal__exit_13am9 {
60
+ } .base-modal__exit_1apcq {
55
61
  opacity: 1;
56
- } .base-modal__exitActive_13am9,
57
- .base-modal__exitDone_13am9 {
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
- scrollContainer.style.overflow = 'hidden';
77
+ if (!shouldIOSLock) {
78
+ scrollContainer.style.overflow = 'hidden';
79
+ }
78
80
  modalRestoreStyles.push({
79
81
  container,
80
82
  modals: 1,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@alfalab/core-components-base-modal",
3
- "version": "5.7.3",
3
+ "version": "5.7.4",
4
4
  "description": "BaseModal component",
5
5
  "keywords": [],
6
6
  "license": "MIT",
package/src/Component.tsx CHANGED
@@ -1,6 +1,6 @@
1
1
  /* eslint-disable jsx-a11y/no-noninteractive-element-interactions */
2
2
  import React, {
3
- FC,
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?: FC<BackdropProps>;
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
- handleContainer(el);
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
- // eslint-disable-next-line jsx-a11y/no-noninteractive-tabindex
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
+ };
@@ -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
- scrollContainer.style.overflow = 'hidden';
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
- scrollContainer.style.overflow = 'hidden';
85
+ if (!shouldIOSLock) {
86
+ scrollContainer.style.overflow = 'hidden';
87
+ }
85
88
  modalRestoreStyles.push({
86
89
  container: container,
87
90
  modals: 1,