@alfalab/core-components-base-modal 5.7.2 → 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/esm/index.css CHANGED
@@ -1,5 +1,5 @@
1
- /* hash: 1njhy */
2
- :root {
1
+ /* hash: 4eqb1 */
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 */
5
5
  } :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 */
@@ -15,13 +15,19 @@
15
15
  } :root {
16
16
  } :root {
17
17
  } :root {
18
- } .base-modal__component_1b68o {
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_1b68o {
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_1b68o {
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_1b68o {
49
+ } .base-modal__hidden_1apcq {
44
50
  display: none;
45
- } .base-modal__backdrop_1b68o {
51
+ } .base-modal__backdrop_1apcq {
46
52
  z-index: 0;
47
- } .base-modal__appear_1b68o,
48
- .base-modal__enter_1b68o {
53
+ } .base-modal__appear_1apcq,
54
+ .base-modal__enter_1apcq {
49
55
  opacity: 0;
50
- } .base-modal__appearActive_1b68o,
51
- .base-modal__enterActive_1b68o {
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_1b68o {
60
+ } .base-modal__exit_1apcq {
55
61
  opacity: 1;
56
- } .base-modal__exitActive_1b68o,
57
- .base-modal__exitDone_1b68o {
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/index.js CHANGED
@@ -1,15 +1,2 @@
1
1
  export { BaseModal, BaseModalContext } from './Component.js';
2
2
  export { handleContainer, hasScrollbar, isScrolledToBottom, isScrolledToTop, restoreContainerStyles } from './utils.js';
3
- import 'tslib';
4
- import 'react';
5
- import 'react-focus-lock';
6
- import 'react-merge-refs';
7
- import 'react-transition-group';
8
- import '@juggle/resize-observer';
9
- import 'classnames';
10
- import '@alfalab/core-components-backdrop/esm';
11
- import '@alfalab/core-components-portal/esm';
12
- import '@alfalab/core-components-shared/esm';
13
- import '@alfalab/core-components-stack/esm';
14
- import './matches-polyfill.js';
15
- import '@alfalab/core-components-global-store/esm';
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,5 +1,5 @@
1
- /* hash: 1njhy */
2
- :root {
1
+ /* hash: 4eqb1 */
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 */
5
5
  } :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 */
@@ -15,13 +15,19 @@
15
15
  } :root {
16
16
  } :root {
17
17
  } :root {
18
- } .base-modal__component_1b68o {
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_1b68o {
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_1b68o {
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_1b68o {
49
+ } .base-modal__hidden_1apcq {
44
50
  display: none;
45
- } .base-modal__backdrop_1b68o {
51
+ } .base-modal__backdrop_1apcq {
46
52
  z-index: 0;
47
- } .base-modal__appear_1b68o,
48
- .base-modal__enter_1b68o {
53
+ } .base-modal__appear_1apcq,
54
+ .base-modal__enter_1apcq {
49
55
  opacity: 0;
50
- } .base-modal__appearActive_1b68o,
51
- .base-modal__enterActive_1b68o {
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_1b68o {
60
+ } .base-modal__exit_1apcq {
55
61
  opacity: 1;
56
- } .base-modal__exitActive_1b68o,
57
- .base-modal__exitDone_1b68o {
62
+ } .base-modal__exitActive_1apcq,
63
+ .base-modal__exitDone_1apcq {
58
64
  opacity: 0;
59
65
  transition: opacity 200ms ease-out;
60
66
  }
package/index.js CHANGED
@@ -4,19 +4,6 @@ Object.defineProperty(exports, '__esModule', { value: true });
4
4
 
5
5
  var Component = require('./Component.js');
6
6
  var utils = require('./utils.js');
7
- require('tslib');
8
- require('react');
9
- require('react-focus-lock');
10
- require('react-merge-refs');
11
- require('react-transition-group');
12
- require('@juggle/resize-observer');
13
- require('classnames');
14
- require('@alfalab/core-components-backdrop');
15
- require('@alfalab/core-components-portal');
16
- require('@alfalab/core-components-shared');
17
- require('@alfalab/core-components-stack');
18
- require('./matches-polyfill.js');
19
- require('@alfalab/core-components-global-store');
20
7
 
21
8
 
22
9
 
@@ -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,13 +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
- import '@alfalab/core-components-global-store/modern';
14
14
 
15
- const styles = {"component":"base-modal__component_1b68o","wrapper":"base-modal__wrapper_1b68o","content":"base-modal__content_1b68o","hidden":"base-modal__hidden_1b68o","backdrop":"base-modal__backdrop_1b68o","appear":"base-modal__appear_1b68o","enter":"base-modal__enter_1b68o","appearActive":"base-modal__appearActive_1b68o","enterActive":"base-modal__enterActive_1b68o","exit":"base-modal__exit_1b68o","exitActive":"base-modal__exitActive_1b68o","exitDone":"base-modal__exitDone_1b68o"};
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"};
16
16
  require('./index.css')
17
17
 
18
18
  /* eslint-disable jsx-a11y/no-noninteractive-element-interactions */
@@ -32,7 +32,7 @@ const BaseModalContext = React.createContext({
32
32
  setHasFooter: () => null,
33
33
  onClose: () => null,
34
34
  });
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, }, 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) => {
36
36
  const [exited, setExited] = useState(null);
37
37
  const [hasScroll, setHasScroll] = useState(false);
38
38
  const [hasHeader, setHasHeader] = useState(false);
@@ -91,6 +91,9 @@ const BaseModal = forwardRef(({ open, container, children, scrollHandler = 'wrap
91
91
  }
92
92
  }, [hasFooter, hasHeader, headerOffset]);
93
93
  const handleClose = useCallback((event, reason) => {
94
+ if (iOSLock && os.isIOS()) {
95
+ unlockScroll();
96
+ }
94
97
  if (onClose) {
95
98
  onClose(event, reason);
96
99
  }
@@ -101,7 +104,7 @@ const BaseModal = forwardRef(({ open, container, children, scrollHandler = 'wrap
101
104
  onEscapeKeyDown(event);
102
105
  }
103
106
  return null;
104
- }, [onBackdropClick, onClose, onEscapeKeyDown]);
107
+ }, [onBackdropClick, onClose, onEscapeKeyDown, iOSLock]);
105
108
  const handleBackdropMouseDown = (event) => {
106
109
  let clickedOnScrollbar = false;
107
110
  const clientWidth = event.target?.clientWidth;
@@ -175,7 +178,12 @@ const BaseModal = forwardRef(({ open, container, children, scrollHandler = 'wrap
175
178
  if (open && isExited) {
176
179
  if (!disableBlockingScroll) {
177
180
  const el = getContainer();
178
- handleContainer(el);
181
+ const shouldIOSLock = iOSLock && os.isIOS();
182
+ handleContainer(el, shouldIOSLock);
183
+ if (shouldIOSLock) {
184
+ syncHeight();
185
+ lockScroll();
186
+ }
179
187
  restoreContainerStylesRef.current = () => {
180
188
  restoreContainerStylesRef.current = null;
181
189
  restoreContainerStyles(el);
@@ -183,7 +191,7 @@ const BaseModal = forwardRef(({ open, container, children, scrollHandler = 'wrap
183
191
  }
184
192
  setExited(false);
185
193
  }
186
- }, [getContainer, open, disableBlockingScroll, isExited]);
194
+ }, [getContainer, open, disableBlockingScroll, isExited, iOSLock]);
187
195
  useEffect(() => {
188
196
  const ResizeObserver$1 = window.ResizeObserver || ResizeObserver;
189
197
  resizeObserverRef.current = new ResizeObserver$1(checkToHasScrollBar);
@@ -196,6 +204,11 @@ const BaseModal = forwardRef(({ open, container, children, scrollHandler = 'wrap
196
204
  }
197
205
  };
198
206
  }, []);
207
+ useEffect(() => {
208
+ if (disableAutoFocus || !open)
209
+ return;
210
+ wrapperRef.current?.focus();
211
+ }, [open, disableAutoFocus]);
199
212
  const contextValue = useMemo(() => ({
200
213
  parentRef: wrapperRef,
201
214
  componentRef: componentNodeRef,
@@ -222,7 +235,7 @@ const BaseModal = forwardRef(({ open, container, children, scrollHandler = 'wrap
222
235
  handleClose,
223
236
  ]);
224
237
  const renderContent = () => (React.createElement(Stack, { value: zIndex }, (computedZIndex) => (React.createElement(BaseModalContext.Provider, { value: contextValue },
225
- React.createElement(FocusLock, { autoFocus: !disableAutoFocus, disabled: disableFocusLock || !open, returnFocus: !disableRestoreFocus },
238
+ React.createElement(FocusLock, { disabled: disableFocusLock || !open, returnFocus: !disableRestoreFocus },
226
239
  Backdrop$1 && (React.createElement(Backdrop$1, { ...backdropProps, className: cn(backdropProps.className, styles.backdrop), open: open, style: {
227
240
  zIndex: computedZIndex,
228
241
  } })),
@@ -232,9 +245,7 @@ const BaseModal = forwardRef(({ open, container, children, scrollHandler = 'wrap
232
245
  ref,
233
246
  wrapperRef,
234
247
  wrapperProps?.ref,
235
- ]), onKeyDown: handleKeyDown, onMouseDown: handleBackdropMouseDown, onMouseUp: handleBackdropMouseUp,
236
- // eslint-disable-next-line jsx-a11y/no-noninteractive-tabindex
237
- tabIndex: 0, "data-test-id": dataTestId, style: {
248
+ ]), onKeyDown: handleKeyDown, onMouseDown: handleBackdropMouseDown, onMouseUp: handleBackdropMouseUp, tabIndex: -1, "data-test-id": dataTestId, style: {
238
249
  zIndex: computedZIndex,
239
250
  } },
240
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,5 +1,5 @@
1
- /* hash: 1njhy */
2
- :root {
1
+ /* hash: 4eqb1 */
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 */
5
5
  } :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 */
@@ -15,13 +15,19 @@
15
15
  } :root {
16
16
  } :root {
17
17
  } :root {
18
- } .base-modal__component_1b68o {
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_1b68o {
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_1b68o {
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_1b68o {
49
+ } .base-modal__hidden_1apcq {
44
50
  display: none;
45
- } .base-modal__backdrop_1b68o {
51
+ } .base-modal__backdrop_1apcq {
46
52
  z-index: 0;
47
- } .base-modal__appear_1b68o,
48
- .base-modal__enter_1b68o {
53
+ } .base-modal__appear_1apcq,
54
+ .base-modal__enter_1apcq {
49
55
  opacity: 0;
50
- } .base-modal__appearActive_1b68o,
51
- .base-modal__enterActive_1b68o {
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_1b68o {
60
+ } .base-modal__exit_1apcq {
55
61
  opacity: 1;
56
- } .base-modal__exitActive_1b68o,
57
- .base-modal__exitDone_1b68o {
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/index.js CHANGED
@@ -1,14 +1,2 @@
1
1
  export { BaseModal, BaseModalContext } from './Component.js';
2
2
  export { handleContainer, hasScrollbar, isScrolledToBottom, isScrolledToTop, restoreContainerStyles } from './utils.js';
3
- import 'react';
4
- import 'react-focus-lock';
5
- import 'react-merge-refs';
6
- import 'react-transition-group';
7
- import '@juggle/resize-observer';
8
- import 'classnames';
9
- import '@alfalab/core-components-backdrop/modern';
10
- import '@alfalab/core-components-portal/modern';
11
- import '@alfalab/core-components-shared/modern';
12
- import '@alfalab/core-components-stack/modern';
13
- import './matches-polyfill.js';
14
- import '@alfalab/core-components-global-store/modern';
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.2",
3
+ "version": "5.7.4",
4
4
  "description": "BaseModal component",
5
5
  "keywords": [],
6
6
  "license": "MIT",
@@ -18,7 +18,7 @@
18
18
  "@alfalab/core-components-shared": "^0.9.1",
19
19
  "@juggle/resize-observer": "^3.3.1",
20
20
  "classnames": "^2.3.1",
21
- "react-focus-lock": "^2.9.3",
21
+ "react-focus-lock": "^2.12.1",
22
22
  "react-merge-refs": "^1.1.0",
23
23
  "react-transition-group": "^4.4.5",
24
24
  "tslib": "^2.4.0"
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,