@alfalab/core-components-base-modal 5.2.1 → 5.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/Component.d.ts CHANGED
@@ -1,5 +1,5 @@
1
- /// <reference types="react-transition-group" />
2
1
  /// <reference types="react" />
2
+ /// <reference types="react-transition-group" />
3
3
  import React from 'react';
4
4
  import { FC, KeyboardEvent, MouseEvent, MutableRefObject, ReactNode, Ref } from "react";
5
5
  import { TransitionProps } from 'react-transition-group/Transition';
@@ -71,6 +71,14 @@ type BaseModalProps = {
71
71
  * Дополнительный класс
72
72
  */
73
73
  contentClassName?: string;
74
+ /**
75
+ * Дополнительные пропсы на обертку контента
76
+ */
77
+ contentProps?: React.DetailedHTMLProps<React.HTMLAttributes<HTMLDivElement>, HTMLDivElement>;
78
+ /**
79
+ * Дополнительные пропсы на компонентную обертку контента
80
+ */
81
+ componentDivProps?: React.DetailedHTMLProps<React.HTMLAttributes<HTMLDivElement>, HTMLDivElement>;
74
82
  /**
75
83
  * Дополнительный класс для обертки (Modal)
76
84
  */
package/Component.js CHANGED
@@ -23,7 +23,7 @@ var FocusLock__default = /*#__PURE__*/_interopDefaultCompat(FocusLock);
23
23
  var mergeRefs__default = /*#__PURE__*/_interopDefaultCompat(mergeRefs);
24
24
  var cn__default = /*#__PURE__*/_interopDefaultCompat(cn);
25
25
 
26
- var styles = {"component":"base-modal__component_10unr","wrapper":"base-modal__wrapper_10unr","content":"base-modal__content_10unr","hidden":"base-modal__hidden_10unr","backdrop":"base-modal__backdrop_10unr","appear":"base-modal__appear_10unr","enter":"base-modal__enter_10unr","appearActive":"base-modal__appearActive_10unr","enterActive":"base-modal__enterActive_10unr","exit":"base-modal__exit_10unr","exitActive":"base-modal__exitActive_10unr","exitDone":"base-modal__exitDone_10unr"};
26
+ var styles = {"component":"base-modal__component_vq09z","wrapper":"base-modal__wrapper_vq09z","content":"base-modal__content_vq09z","hidden":"base-modal__hidden_vq09z","backdrop":"base-modal__backdrop_vq09z","appear":"base-modal__appear_vq09z","enter":"base-modal__enter_vq09z","appearActive":"base-modal__appearActive_vq09z","enterActive":"base-modal__enterActive_vq09z","exit":"base-modal__exit_vq09z","exitActive":"base-modal__exitActive_vq09z","exitDone":"base-modal__exitDone_vq09z"};
27
27
  require('./index.css')
28
28
 
29
29
  // eslint-disable-next-line @typescript-eslint/no-redeclare
@@ -43,7 +43,7 @@ var BaseModalContext = React__default.default.createContext({
43
43
  onClose: function () { return null; },
44
44
  });
45
45
  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, 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 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, 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
47
  var _q = React.useState(null), exited = _q[0], setExited = _q[1];
48
48
  var _r = React.useState(false), hasScroll = _r[0], setHasScroll = _r[1];
49
49
  var _s = React.useState(false), hasHeader = _s[0], setHasHeader = _s[1];
@@ -248,8 +248,12 @@ var BaseModal = React.forwardRef(function (_a, ref) {
248
248
  zIndex: computedZIndex,
249
249
  } },
250
250
  React__default.default.createElement(reactTransitionGroup.CSSTransition, tslib.__assign({ appear: true, timeout: 200, classNames: styles }, transitionProps, { in: open, onEntered: handleEntered, onExited: handleExited }),
251
- React__default.default.createElement("div", { className: cn__default.default(styles.component, className), ref: mergeRefs__default.default([componentRef, componentNodeRef]) },
252
- React__default.default.createElement("div", { className: cn__default.default(styles.content, contentClassName) }, children)))))));
251
+ React__default.default.createElement("div", tslib.__assign({}, componentDivProps, { className: cn__default.default(styles.component, className, componentDivProps === null || componentDivProps === void 0 ? void 0 : componentDivProps.className), ref: mergeRefs__default.default([
252
+ componentRef,
253
+ componentNodeRef,
254
+ (componentDivProps === null || componentDivProps === void 0 ? void 0 : componentDivProps.ref) || null,
255
+ ]) }),
256
+ React__default.default.createElement("div", tslib.__assign({}, contentProps, { className: cn__default.default(styles.content, contentClassName, contentProps === null || contentProps === void 0 ? void 0 : contentProps.className) }), children)))))));
253
257
  })); };
254
258
  if (!shouldRender)
255
259
  return null;
@@ -1,5 +1,5 @@
1
- /// <reference types="react-transition-group" />
2
1
  /// <reference types="react" />
2
+ /// <reference types="react-transition-group" />
3
3
  import React from 'react';
4
4
  import { FC, KeyboardEvent, MouseEvent, MutableRefObject, ReactNode, Ref } from "react";
5
5
  import { TransitionProps } from 'react-transition-group/Transition';
@@ -71,6 +71,14 @@ type BaseModalProps = {
71
71
  * Дополнительный класс
72
72
  */
73
73
  contentClassName?: string;
74
+ /**
75
+ * Дополнительные пропсы на обертку контента
76
+ */
77
+ contentProps?: React.DetailedHTMLProps<React.HTMLAttributes<HTMLDivElement>, HTMLDivElement>;
78
+ /**
79
+ * Дополнительные пропсы на компонентную обертку контента
80
+ */
81
+ componentDivProps?: React.DetailedHTMLProps<React.HTMLAttributes<HTMLDivElement>, HTMLDivElement>;
74
82
  /**
75
83
  * Дополнительный класс для обертки (Modal)
76
84
  */
package/cssm/Component.js CHANGED
@@ -42,7 +42,7 @@ var BaseModalContext = React__default.default.createContext({
42
42
  onClose: function () { return null; },
43
43
  });
44
44
  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, 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;
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, 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
46
  var _q = React.useState(null), exited = _q[0], setExited = _q[1];
47
47
  var _r = React.useState(false), hasScroll = _r[0], setHasScroll = _r[1];
48
48
  var _s = React.useState(false), hasHeader = _s[0], setHasHeader = _s[1];
@@ -247,8 +247,12 @@ var BaseModal = React.forwardRef(function (_a, ref) {
247
247
  zIndex: computedZIndex,
248
248
  } },
249
249
  React__default.default.createElement(reactTransitionGroup.CSSTransition, tslib.__assign({ appear: true, timeout: 200, classNames: styles__default.default }, transitionProps, { in: open, onEntered: handleEntered, onExited: handleExited }),
250
- React__default.default.createElement("div", { className: cn__default.default(styles__default.default.component, className), ref: mergeRefs__default.default([componentRef, componentNodeRef]) },
251
- React__default.default.createElement("div", { className: cn__default.default(styles__default.default.content, contentClassName) }, children)))))));
250
+ React__default.default.createElement("div", tslib.__assign({}, componentDivProps, { className: cn__default.default(styles__default.default.component, className, componentDivProps === null || componentDivProps === void 0 ? void 0 : componentDivProps.className), ref: mergeRefs__default.default([
251
+ componentRef,
252
+ componentNodeRef,
253
+ (componentDivProps === null || componentDivProps === void 0 ? void 0 : componentDivProps.ref) || null,
254
+ ]) }),
255
+ React__default.default.createElement("div", tslib.__assign({}, contentProps, { className: cn__default.default(styles__default.default.content, contentClassName, contentProps === null || contentProps === void 0 ? void 0 : contentProps.className) }), children)))))));
252
256
  })); };
253
257
  if (!shouldRender)
254
258
  return null;
@@ -1,5 +1,5 @@
1
- /// <reference types="react-transition-group" />
2
1
  /// <reference types="react" />
2
+ /// <reference types="react-transition-group" />
3
3
  import React from 'react';
4
4
  import { FC, KeyboardEvent, MouseEvent, MutableRefObject, ReactNode, Ref } from "react";
5
5
  import { TransitionProps } from 'react-transition-group/Transition';
@@ -71,6 +71,14 @@ type BaseModalProps = {
71
71
  * Дополнительный класс
72
72
  */
73
73
  contentClassName?: string;
74
+ /**
75
+ * Дополнительные пропсы на обертку контента
76
+ */
77
+ contentProps?: React.DetailedHTMLProps<React.HTMLAttributes<HTMLDivElement>, HTMLDivElement>;
78
+ /**
79
+ * Дополнительные пропсы на компонентную обертку контента
80
+ */
81
+ componentDivProps?: React.DetailedHTMLProps<React.HTMLAttributes<HTMLDivElement>, HTMLDivElement>;
74
82
  /**
75
83
  * Дополнительный класс для обертки (Modal)
76
84
  */
package/esm/Component.js CHANGED
@@ -12,7 +12,7 @@ import { isScrolledToTop, isScrolledToBottom, handleContainer, restoreContainerS
12
12
  import './matches-polyfill.js';
13
13
  import '@alfalab/core-components-global-store/esm';
14
14
 
15
- var styles = {"component":"base-modal__component_10unr","wrapper":"base-modal__wrapper_10unr","content":"base-modal__content_10unr","hidden":"base-modal__hidden_10unr","backdrop":"base-modal__backdrop_10unr","appear":"base-modal__appear_10unr","enter":"base-modal__enter_10unr","appearActive":"base-modal__appearActive_10unr","enterActive":"base-modal__enterActive_10unr","exit":"base-modal__exit_10unr","exitActive":"base-modal__exitActive_10unr","exitDone":"base-modal__exitDone_10unr"};
15
+ var styles = {"component":"base-modal__component_vq09z","wrapper":"base-modal__wrapper_vq09z","content":"base-modal__content_vq09z","hidden":"base-modal__hidden_vq09z","backdrop":"base-modal__backdrop_vq09z","appear":"base-modal__appear_vq09z","enter":"base-modal__enter_vq09z","appearActive":"base-modal__appearActive_vq09z","enterActive":"base-modal__enterActive_vq09z","exit":"base-modal__exit_vq09z","exitActive":"base-modal__exitActive_vq09z","exitDone":"base-modal__exitDone_vq09z"};
16
16
  require('./index.css')
17
17
 
18
18
  // eslint-disable-next-line @typescript-eslint/no-redeclare
@@ -32,7 +32,7 @@ var BaseModalContext = React.createContext({
32
32
  onClose: function () { return null; },
33
33
  });
34
34
  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, 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;
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, 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
36
  var _q = useState(null), exited = _q[0], setExited = _q[1];
37
37
  var _r = useState(false), hasScroll = _r[0], setHasScroll = _r[1];
38
38
  var _s = useState(false), hasHeader = _s[0], setHasHeader = _s[1];
@@ -237,8 +237,12 @@ var BaseModal = forwardRef(function (_a, ref) {
237
237
  zIndex: computedZIndex,
238
238
  } },
239
239
  React.createElement(CSSTransition, __assign({ appear: true, timeout: 200, classNames: styles }, transitionProps, { in: open, onEntered: handleEntered, onExited: handleExited }),
240
- React.createElement("div", { className: cn(styles.component, className), ref: mergeRefs([componentRef, componentNodeRef]) },
241
- React.createElement("div", { className: cn(styles.content, contentClassName) }, children)))))));
240
+ React.createElement("div", __assign({}, componentDivProps, { className: cn(styles.component, className, componentDivProps === null || componentDivProps === void 0 ? void 0 : componentDivProps.className), ref: mergeRefs([
241
+ componentRef,
242
+ componentNodeRef,
243
+ (componentDivProps === null || componentDivProps === void 0 ? void 0 : componentDivProps.ref) || null,
244
+ ]) }),
245
+ React.createElement("div", __assign({}, contentProps, { className: cn(styles.content, contentClassName, contentProps === null || contentProps === void 0 ? void 0 : contentProps.className) }), children)))))));
242
246
  })); };
243
247
  if (!shouldRender)
244
248
  return null;
package/esm/index.css CHANGED
@@ -1,4 +1,4 @@
1
- /* hash: 10q8r */
1
+ /* hash: 184a3 */
2
2
  :root {
3
3
  } /* deprecated */ :root {
4
4
  --color-light-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,13 @@
15
15
  } :root {
16
16
  } :root {
17
17
  } :root {
18
- } .base-modal__component_10unr {
18
+ } .base-modal__component_vq09z {
19
19
  position: relative;
20
20
  box-sizing: border-box;
21
21
  background: var(--color-light-bg-primary);
22
22
  margin: auto;
23
23
  flex-shrink: 0;
24
- } .base-modal__wrapper_10unr {
24
+ } .base-modal__wrapper_vq09z {
25
25
  position: fixed;
26
26
  top: 0;
27
27
  left: 0;
@@ -33,27 +33,27 @@
33
33
  flex-direction: column;
34
34
  align-items: center;
35
35
  outline: 0;
36
- } .base-modal__content_10unr {
36
+ } .base-modal__content_vq09z {
37
37
  width: 100%;
38
38
  height: 100%;
39
39
  display: flex;
40
40
  flex-direction: column;
41
41
  flex: 1;
42
- } .base-modal__hidden_10unr {
42
+ } .base-modal__hidden_vq09z {
43
43
  display: none;
44
- } .base-modal__backdrop_10unr {
44
+ } .base-modal__backdrop_vq09z {
45
45
  z-index: 0;
46
- } .base-modal__appear_10unr,
47
- .base-modal__enter_10unr {
46
+ } .base-modal__appear_vq09z,
47
+ .base-modal__enter_vq09z {
48
48
  opacity: 0;
49
- } .base-modal__appearActive_10unr,
50
- .base-modal__enterActive_10unr {
49
+ } .base-modal__appearActive_vq09z,
50
+ .base-modal__enterActive_vq09z {
51
51
  opacity: 1;
52
52
  transition: opacity 200ms ease-in;
53
- } .base-modal__exit_10unr {
53
+ } .base-modal__exit_vq09z {
54
54
  opacity: 1;
55
- } .base-modal__exitActive_10unr,
56
- .base-modal__exitDone_10unr {
55
+ } .base-modal__exitActive_vq09z,
56
+ .base-modal__exitDone_vq09z {
57
57
  opacity: 0;
58
58
  transition: opacity 200ms ease-out;
59
59
  }
package/index.css CHANGED
@@ -1,4 +1,4 @@
1
- /* hash: 10q8r */
1
+ /* hash: 184a3 */
2
2
  :root {
3
3
  } /* deprecated */ :root {
4
4
  --color-light-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,13 @@
15
15
  } :root {
16
16
  } :root {
17
17
  } :root {
18
- } .base-modal__component_10unr {
18
+ } .base-modal__component_vq09z {
19
19
  position: relative;
20
20
  box-sizing: border-box;
21
21
  background: var(--color-light-bg-primary);
22
22
  margin: auto;
23
23
  flex-shrink: 0;
24
- } .base-modal__wrapper_10unr {
24
+ } .base-modal__wrapper_vq09z {
25
25
  position: fixed;
26
26
  top: 0;
27
27
  left: 0;
@@ -33,27 +33,27 @@
33
33
  flex-direction: column;
34
34
  align-items: center;
35
35
  outline: 0;
36
- } .base-modal__content_10unr {
36
+ } .base-modal__content_vq09z {
37
37
  width: 100%;
38
38
  height: 100%;
39
39
  display: flex;
40
40
  flex-direction: column;
41
41
  flex: 1;
42
- } .base-modal__hidden_10unr {
42
+ } .base-modal__hidden_vq09z {
43
43
  display: none;
44
- } .base-modal__backdrop_10unr {
44
+ } .base-modal__backdrop_vq09z {
45
45
  z-index: 0;
46
- } .base-modal__appear_10unr,
47
- .base-modal__enter_10unr {
46
+ } .base-modal__appear_vq09z,
47
+ .base-modal__enter_vq09z {
48
48
  opacity: 0;
49
- } .base-modal__appearActive_10unr,
50
- .base-modal__enterActive_10unr {
49
+ } .base-modal__appearActive_vq09z,
50
+ .base-modal__enterActive_vq09z {
51
51
  opacity: 1;
52
52
  transition: opacity 200ms ease-in;
53
- } .base-modal__exit_10unr {
53
+ } .base-modal__exit_vq09z {
54
54
  opacity: 1;
55
- } .base-modal__exitActive_10unr,
56
- .base-modal__exitDone_10unr {
55
+ } .base-modal__exitActive_vq09z,
56
+ .base-modal__exitDone_vq09z {
57
57
  opacity: 0;
58
58
  transition: opacity 200ms ease-out;
59
59
  }
@@ -1,5 +1,5 @@
1
- /// <reference types="react-transition-group" />
2
1
  /// <reference types="react" />
2
+ /// <reference types="react-transition-group" />
3
3
  import React from 'react';
4
4
  import { FC, KeyboardEvent, MouseEvent, MutableRefObject, ReactNode, Ref } from "react";
5
5
  import { TransitionProps } from 'react-transition-group/Transition';
@@ -71,6 +71,14 @@ type BaseModalProps = {
71
71
  * Дополнительный класс
72
72
  */
73
73
  contentClassName?: string;
74
+ /**
75
+ * Дополнительные пропсы на обертку контента
76
+ */
77
+ contentProps?: React.DetailedHTMLProps<React.HTMLAttributes<HTMLDivElement>, HTMLDivElement>;
78
+ /**
79
+ * Дополнительные пропсы на компонентную обертку контента
80
+ */
81
+ componentDivProps?: React.DetailedHTMLProps<React.HTMLAttributes<HTMLDivElement>, HTMLDivElement>;
74
82
  /**
75
83
  * Дополнительный класс для обертки (Modal)
76
84
  */
@@ -11,7 +11,7 @@ import { isScrolledToTop, isScrolledToBottom, handleContainer, restoreContainerS
11
11
  import './matches-polyfill.js';
12
12
  import '@alfalab/core-components-global-store/modern';
13
13
 
14
- const styles = {"component":"base-modal__component_10unr","wrapper":"base-modal__wrapper_10unr","content":"base-modal__content_10unr","hidden":"base-modal__hidden_10unr","backdrop":"base-modal__backdrop_10unr","appear":"base-modal__appear_10unr","enter":"base-modal__enter_10unr","appearActive":"base-modal__appearActive_10unr","enterActive":"base-modal__enterActive_10unr","exit":"base-modal__exit_10unr","exitActive":"base-modal__exitActive_10unr","exitDone":"base-modal__exitDone_10unr"};
14
+ const styles = {"component":"base-modal__component_vq09z","wrapper":"base-modal__wrapper_vq09z","content":"base-modal__content_vq09z","hidden":"base-modal__hidden_vq09z","backdrop":"base-modal__backdrop_vq09z","appear":"base-modal__appear_vq09z","enter":"base-modal__enter_vq09z","appearActive":"base-modal__appearActive_vq09z","enterActive":"base-modal__enterActive_vq09z","exit":"base-modal__exit_vq09z","exitActive":"base-modal__exitActive_vq09z","exitDone":"base-modal__exitDone_vq09z"};
15
15
  require('./index.css')
16
16
 
17
17
  /* eslint-disable jsx-a11y/no-noninteractive-element-interactions */
@@ -31,7 +31,7 @@ const BaseModalContext = React.createContext({
31
31
  setHasFooter: () => null,
32
32
  onClose: () => null,
33
33
  });
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, wrapperClassName, onBackdropClick, onClose, onEscapeKeyDown, onMount, onUnmount, dataTestId, zIndex = stackingOrder.MODAL, componentRef = null, usePortal = true, }, ref) => {
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, contentProps, componentDivProps, wrapperClassName, onBackdropClick, onClose, onEscapeKeyDown, onMount, onUnmount, dataTestId, zIndex = stackingOrder.MODAL, componentRef = null, usePortal = true, }, ref) => {
35
35
  const [exited, setExited] = useState(null);
36
36
  const [hasScroll, setHasScroll] = useState(false);
37
37
  const [hasHeader, setHasHeader] = useState(false);
@@ -233,8 +233,12 @@ const BaseModal = forwardRef(({ open, container, children, scrollHandler = 'wrap
233
233
  zIndex: computedZIndex,
234
234
  } },
235
235
  React.createElement(CSSTransition, { appear: true, timeout: 200, classNames: styles, ...transitionProps, in: open, onEntered: handleEntered, onExited: handleExited },
236
- React.createElement("div", { className: cn(styles.component, className), ref: mergeRefs([componentRef, componentNodeRef]) },
237
- React.createElement("div", { className: cn(styles.content, contentClassName) }, children)))))))));
236
+ React.createElement("div", { ...componentDivProps, className: cn(styles.component, className, componentDivProps?.className), ref: mergeRefs([
237
+ componentRef,
238
+ componentNodeRef,
239
+ componentDivProps?.ref || null,
240
+ ]) },
241
+ React.createElement("div", { ...contentProps, className: cn(styles.content, contentClassName, contentProps?.className) }, children)))))))));
238
242
  if (!shouldRender)
239
243
  return null;
240
244
  return usePortal ? (React.createElement(Portal, { getPortalContainer: container, immediateMount: true }, renderContent())) : (renderContent());
package/modern/index.css CHANGED
@@ -1,4 +1,4 @@
1
- /* hash: 10q8r */
1
+ /* hash: 184a3 */
2
2
  :root {
3
3
  } /* deprecated */ :root {
4
4
  --color-light-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,13 @@
15
15
  } :root {
16
16
  } :root {
17
17
  } :root {
18
- } .base-modal__component_10unr {
18
+ } .base-modal__component_vq09z {
19
19
  position: relative;
20
20
  box-sizing: border-box;
21
21
  background: var(--color-light-bg-primary);
22
22
  margin: auto;
23
23
  flex-shrink: 0;
24
- } .base-modal__wrapper_10unr {
24
+ } .base-modal__wrapper_vq09z {
25
25
  position: fixed;
26
26
  top: 0;
27
27
  left: 0;
@@ -33,27 +33,27 @@
33
33
  flex-direction: column;
34
34
  align-items: center;
35
35
  outline: 0;
36
- } .base-modal__content_10unr {
36
+ } .base-modal__content_vq09z {
37
37
  width: 100%;
38
38
  height: 100%;
39
39
  display: flex;
40
40
  flex-direction: column;
41
41
  flex: 1;
42
- } .base-modal__hidden_10unr {
42
+ } .base-modal__hidden_vq09z {
43
43
  display: none;
44
- } .base-modal__backdrop_10unr {
44
+ } .base-modal__backdrop_vq09z {
45
45
  z-index: 0;
46
- } .base-modal__appear_10unr,
47
- .base-modal__enter_10unr {
46
+ } .base-modal__appear_vq09z,
47
+ .base-modal__enter_vq09z {
48
48
  opacity: 0;
49
- } .base-modal__appearActive_10unr,
50
- .base-modal__enterActive_10unr {
49
+ } .base-modal__appearActive_vq09z,
50
+ .base-modal__enterActive_vq09z {
51
51
  opacity: 1;
52
52
  transition: opacity 200ms ease-in;
53
- } .base-modal__exit_10unr {
53
+ } .base-modal__exit_vq09z {
54
54
  opacity: 1;
55
- } .base-modal__exitActive_10unr,
56
- .base-modal__exitDone_10unr {
55
+ } .base-modal__exitActive_vq09z,
56
+ .base-modal__exitDone_vq09z {
57
57
  opacity: 0;
58
58
  transition: opacity 200ms ease-out;
59
59
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@alfalab/core-components-base-modal",
3
- "version": "5.2.1",
3
+ "version": "5.4.0",
4
4
  "description": "BaseModal component",
5
5
  "keywords": [],
6
6
  "license": "MIT",
@@ -11,10 +11,10 @@
11
11
  "directory": "dist"
12
12
  },
13
13
  "dependencies": {
14
- "@alfalab/core-components-backdrop": "^3.0.7",
15
- "@alfalab/core-components-global-store": "^2.0.4",
16
- "@alfalab/core-components-portal": "^3.1.5",
17
- "@alfalab/core-components-stack": "^4.0.4",
14
+ "@alfalab/core-components-backdrop": "^3.1.0",
15
+ "@alfalab/core-components-global-store": "^2.1.0",
16
+ "@alfalab/core-components-portal": "^3.2.0",
17
+ "@alfalab/core-components-stack": "^4.1.0",
18
18
  "@juggle/resize-observer": "^3.3.1",
19
19
  "classnames": "^2.3.1",
20
20
  "react-focus-lock": "^2.9.3",
@@ -0,0 +1,608 @@
1
+ /* eslint-disable jsx-a11y/no-noninteractive-element-interactions */
2
+ import React, {
3
+ FC,
4
+ forwardRef,
5
+ KeyboardEvent,
6
+ MouseEvent,
7
+ MutableRefObject,
8
+ ReactNode,
9
+ Ref,
10
+ useCallback,
11
+ useEffect,
12
+ useMemo,
13
+ useRef,
14
+ useState,
15
+ } from 'react';
16
+ import FocusLock from 'react-focus-lock';
17
+ import mergeRefs from 'react-merge-refs';
18
+ import { CSSTransition } from 'react-transition-group';
19
+ import { TransitionProps } from 'react-transition-group/Transition';
20
+ import { ResizeObserver as ResizeObserverPolyfill } from '@juggle/resize-observer';
21
+ import cn from 'classnames';
22
+
23
+ import { Backdrop as DefaultBackdrop, BackdropProps } from '@alfalab/core-components-backdrop';
24
+ import { Portal, PortalProps } from '@alfalab/core-components-portal';
25
+ import { Stack, stackingOrder } from '@alfalab/core-components-stack';
26
+
27
+ import {
28
+ getScrollbarSize,
29
+ handleContainer,
30
+ hasScrollbar,
31
+ isScrolledToBottom,
32
+ isScrolledToTop,
33
+ restoreContainerStyles,
34
+ } from './utils';
35
+
36
+ import styles from './index.module.css';
37
+
38
+ // TODO Без полифила крашится FocusLock в IE11. Выпилить в будущем!!!.
39
+ import './matches-polyfill';
40
+
41
+ export type BaseModalProps = {
42
+ /**
43
+ * Контент
44
+ */
45
+ children?: ReactNode;
46
+
47
+ /**
48
+ * Компонент бэкдропа
49
+ */
50
+ Backdrop?: FC<BackdropProps>;
51
+
52
+ /**
53
+ * Свойства для Бэкдропа
54
+ */
55
+ backdropProps?: Partial<BackdropProps> & Record<string, unknown>;
56
+
57
+ /**
58
+ * Нода, компонент или функция возвращающая их
59
+ *
60
+ * Контейнер к которому будут добавляться порталы
61
+ */
62
+ container?: PortalProps['getPortalContainer'];
63
+
64
+ /**
65
+ * Отключает автоматический перевод фокуса на модалку при открытии
66
+ * @default false
67
+ */
68
+ disableAutoFocus?: boolean;
69
+
70
+ /**
71
+ * Отключает ловушку фокуса
72
+ * @default false
73
+ */
74
+ disableFocusLock?: boolean;
75
+
76
+ /**
77
+ * Отключает восстановление фокуса на предыдущем элементе после закрытия модалки
78
+ * @default false
79
+ */
80
+ disableRestoreFocus?: boolean;
81
+
82
+ /**
83
+ * Отключает вызов `callback` при нажатии Escape
84
+ * @default false
85
+ */
86
+ disableEscapeKeyDown?: boolean;
87
+
88
+ /**
89
+ * Отключает вызов `callback` при клике на бэкдроп
90
+ * @default false
91
+ */
92
+ disableBackdropClick?: boolean;
93
+
94
+ /**
95
+ * Отключает блокировку скролла при открытии модального окна
96
+ * @default false
97
+ */
98
+ disableBlockingScroll?: boolean;
99
+
100
+ /**
101
+ * Содержимое модалки всегда в DOM
102
+ * @default false
103
+ */
104
+ keepMounted?: boolean;
105
+
106
+ /**
107
+ * Управление видимостью модалки
108
+ */
109
+ open: boolean;
110
+
111
+ /**
112
+ * Дополнительный класс
113
+ */
114
+ className?: string;
115
+
116
+ /**
117
+ * Дополнительный класс
118
+ */
119
+ contentClassName?: string;
120
+
121
+ /**
122
+ * Дополнительные пропсы на обертку контента
123
+ */
124
+ contentProps?: React.DetailedHTMLProps<React.HTMLAttributes<HTMLDivElement>, HTMLDivElement>;
125
+
126
+ /**
127
+ * Дополнительные пропсы на компонентную обертку контента
128
+ */
129
+ componentDivProps?: React.DetailedHTMLProps<
130
+ React.HTMLAttributes<HTMLDivElement>,
131
+ HTMLDivElement
132
+ >;
133
+
134
+ /**
135
+ * Дополнительный класс для обертки (Modal)
136
+ */
137
+ wrapperClassName?: string;
138
+
139
+ /**
140
+ * Обработчик скролла контента
141
+ */
142
+ scrollHandler?: 'wrapper' | 'content' | MutableRefObject<HTMLDivElement | null>;
143
+
144
+ /**
145
+ * Пропсы для анимации (CSSTransition)
146
+ */
147
+ transitionProps?: Partial<TransitionProps>;
148
+
149
+ /**
150
+ * Рендерить ли в контейнер через портал.
151
+ * @default true
152
+ */
153
+ usePortal?: boolean;
154
+
155
+ /**
156
+ * Обработчик события нажатия на бэкдроп
157
+ */
158
+ onBackdropClick?: (event: MouseEvent) => void;
159
+
160
+ /**
161
+ * Обработчик события нажатия на Escape
162
+ *
163
+ * Если `disableEscapeKeyDown` - false и модальное окно в фокусе
164
+ */
165
+ onEscapeKeyDown?: (event: KeyboardEvent) => void;
166
+
167
+ /**
168
+ * Обработчик закрытия
169
+ */
170
+ onClose?: (
171
+ event: MouseEvent<HTMLElement> | KeyboardEvent<HTMLElement>,
172
+ reason?: 'backdropClick' | 'escapeKeyDown' | 'closerClick',
173
+ ) => void;
174
+
175
+ /**
176
+ * Обработчик события onEntered компонента Transition
177
+ */
178
+ onMount?: () => void;
179
+
180
+ /**
181
+ * Обработчик события onExited компонента Transition
182
+ */
183
+ onUnmount?: () => void;
184
+
185
+ /**
186
+ * Идентификатор для систем автоматизированного тестирования
187
+ */
188
+ dataTestId?: string;
189
+
190
+ /**
191
+ * z-index компонента
192
+ */
193
+ zIndex?: number;
194
+
195
+ /**
196
+ * Реф, который должен быть установлен компонентной области
197
+ */
198
+ componentRef?: MutableRefObject<HTMLDivElement | null>;
199
+ };
200
+
201
+ export type BaseModalContext = {
202
+ parentRef: React.RefObject<HTMLDivElement>;
203
+ componentRef: React.RefObject<HTMLDivElement>;
204
+ hasFooter?: boolean;
205
+ hasHeader?: boolean;
206
+ hasScroll?: boolean;
207
+ headerHighlighted?: boolean;
208
+ footerHighlighted?: boolean;
209
+ headerOffset?: number;
210
+ setHeaderOffset: (offset: number) => void;
211
+ contentRef: Ref<HTMLElement>;
212
+ setHasHeader: (exists: boolean) => void;
213
+ setHasFooter: (exists: boolean) => void;
214
+ onClose: Required<BaseModalProps>['onClose'];
215
+ };
216
+
217
+ // eslint-disable-next-line @typescript-eslint/no-redeclare
218
+ export const BaseModalContext = React.createContext<BaseModalContext>({
219
+ parentRef: { current: null },
220
+ componentRef: { current: null },
221
+ hasFooter: false,
222
+ hasHeader: false,
223
+ hasScroll: false,
224
+ headerHighlighted: false,
225
+ footerHighlighted: false,
226
+ headerOffset: 0,
227
+ setHeaderOffset: () => null,
228
+ contentRef: () => null,
229
+ setHasHeader: () => null,
230
+ setHasFooter: () => null,
231
+ onClose: () => null,
232
+ });
233
+
234
+ export const BaseModal = forwardRef<HTMLDivElement, BaseModalProps>(
235
+ (
236
+ {
237
+ open,
238
+ container,
239
+ children,
240
+ scrollHandler = 'wrapper',
241
+ Backdrop = DefaultBackdrop,
242
+ backdropProps = {},
243
+ transitionProps = {},
244
+ disableBackdropClick,
245
+ disableAutoFocus = false,
246
+ disableFocusLock = false,
247
+ disableEscapeKeyDown = false,
248
+ disableRestoreFocus = false,
249
+ disableBlockingScroll = false,
250
+ keepMounted = false,
251
+ className,
252
+ contentClassName,
253
+ contentProps,
254
+ componentDivProps,
255
+ wrapperClassName,
256
+ onBackdropClick,
257
+ onClose,
258
+ onEscapeKeyDown,
259
+ onMount,
260
+ onUnmount,
261
+ dataTestId,
262
+ zIndex = stackingOrder.MODAL,
263
+ componentRef = null,
264
+ usePortal = true,
265
+ },
266
+ ref,
267
+ ) => {
268
+ const [exited, setExited] = useState<boolean | null>(null);
269
+ const [hasScroll, setHasScroll] = useState(false);
270
+ const [hasHeader, setHasHeader] = useState(false);
271
+ const [hasFooter, setHasFooter] = useState(false);
272
+ const [headerHighlighted, setHeaderHighlighted] = useState(false);
273
+ const [footerHighlighted, setFooterHighlighted] = useState(false);
274
+ const [headerOffset, setHeaderOffset] = useState(0);
275
+
276
+ const componentNodeRef = useRef<HTMLDivElement>(null);
277
+ const wrapperRef = useRef<HTMLDivElement>(null);
278
+ const scrollableNodeRef = useRef<HTMLDivElement | null>(null);
279
+ const contentNodeRef = useRef<HTMLDivElement | null>(null);
280
+ const restoreContainerStylesRef = useRef<null | (() => void)>(null);
281
+ const mouseDownTarget = useRef<HTMLElement>();
282
+ const resizeObserverRef = useRef<ResizeObserver>();
283
+
284
+ const checkToHasScrollBar = () => {
285
+ if (scrollableNodeRef.current) {
286
+ const scrollExists = hasScrollbar(scrollableNodeRef.current);
287
+
288
+ setFooterHighlighted(scrollExists);
289
+ setHasScroll(scrollExists);
290
+ }
291
+ };
292
+
293
+ const isExited = exited || exited === null;
294
+ const shouldRender = keepMounted || open || !isExited;
295
+
296
+ const getContainer = useCallback(
297
+ () => (container ? container() : document.body) as HTMLElement,
298
+ [container],
299
+ );
300
+
301
+ const addResizeHandle = useCallback(() => {
302
+ if (!resizeObserverRef.current) return;
303
+
304
+ if (scrollableNodeRef.current) {
305
+ resizeObserverRef.current.observe(scrollableNodeRef.current);
306
+ }
307
+ if (contentNodeRef.current) {
308
+ resizeObserverRef.current.observe(contentNodeRef.current);
309
+ }
310
+ }, []);
311
+
312
+ const removeResizeHandle = useCallback(() => resizeObserverRef.current?.disconnect(), []);
313
+
314
+ const contentRef = useCallback((node: HTMLDivElement) => {
315
+ if (node !== null) {
316
+ contentNodeRef.current = node;
317
+ if (resizeObserverRef.current) {
318
+ resizeObserverRef.current.observe(node);
319
+ }
320
+ checkToHasScrollBar();
321
+ }
322
+ }, []);
323
+
324
+ const handleScroll = useCallback(() => {
325
+ if (!scrollableNodeRef.current || !componentNodeRef.current) return;
326
+
327
+ if (hasHeader) {
328
+ setHeaderHighlighted(
329
+ !isScrolledToTop(scrollableNodeRef.current) &&
330
+ componentNodeRef.current.getBoundingClientRect().top - headerOffset <= 0,
331
+ );
332
+ }
333
+
334
+ if (hasFooter) {
335
+ setFooterHighlighted(
336
+ !isScrolledToBottom(scrollableNodeRef.current) &&
337
+ componentNodeRef.current.getBoundingClientRect().bottom >=
338
+ window.innerHeight,
339
+ );
340
+ }
341
+ }, [hasFooter, hasHeader, headerOffset]);
342
+
343
+ const handleClose = useCallback<Required<BaseModalProps>['onClose']>(
344
+ (event, reason) => {
345
+ if (onClose) {
346
+ onClose(event, reason);
347
+ }
348
+
349
+ if (reason === 'backdropClick' && onBackdropClick) {
350
+ onBackdropClick(event as MouseEvent);
351
+ }
352
+
353
+ if (reason === 'escapeKeyDown' && onEscapeKeyDown) {
354
+ onEscapeKeyDown(event as KeyboardEvent);
355
+ }
356
+
357
+ return null;
358
+ },
359
+ [onBackdropClick, onClose, onEscapeKeyDown],
360
+ );
361
+
362
+ const handleBackdropMouseDown = (event: MouseEvent<HTMLElement>) => {
363
+ let clickedOnScrollbar = false;
364
+ const clientWidth = (event.target as HTMLElement)?.clientWidth;
365
+
366
+ if (event.clientX && clientWidth) {
367
+ // Устанавливаем смещение для абсолютно спозиционированного скроллбара в OSX в 17px.
368
+ const offset = getScrollbarSize() === 0 ? 17 : 0;
369
+
370
+ clickedOnScrollbar = event.clientX + offset > clientWidth;
371
+ }
372
+
373
+ if (!disableBackdropClick && !clickedOnScrollbar) {
374
+ mouseDownTarget.current = event.target as HTMLElement;
375
+ }
376
+ };
377
+
378
+ const handleBackdropMouseUp = (event: MouseEvent<HTMLElement>) => {
379
+ if (
380
+ !disableBackdropClick &&
381
+ event.target === wrapperRef.current &&
382
+ mouseDownTarget.current === wrapperRef.current
383
+ ) {
384
+ handleClose(event, 'backdropClick');
385
+ }
386
+
387
+ mouseDownTarget.current = undefined;
388
+ };
389
+
390
+ const handleKeyDown = useCallback(
391
+ (event: KeyboardEvent<HTMLDivElement>) => {
392
+ /*
393
+ * Чтобы сохранить дефолтное поведение элементов и событий форм,
394
+ * обработчик не устанавливает event.preventDefault()
395
+ */
396
+ if (event.key !== 'Escape') {
397
+ return;
398
+ }
399
+
400
+ // Если есть обработчик escape на body
401
+ event.stopPropagation();
402
+
403
+ if (!disableEscapeKeyDown && handleClose) {
404
+ handleClose(event, 'escapeKeyDown');
405
+ }
406
+ },
407
+ [disableEscapeKeyDown, handleClose],
408
+ );
409
+
410
+ const getScrollHandler = useCallback(() => {
411
+ if (scrollHandler === 'wrapper') return wrapperRef.current;
412
+ if (scrollHandler === 'content') return componentNodeRef.current;
413
+
414
+ return scrollHandler.current || wrapperRef.current;
415
+ }, [scrollHandler]);
416
+
417
+ const handleEntered: Required<TransitionProps>['onEntered'] = useCallback(
418
+ (node, isAppearing) => {
419
+ scrollableNodeRef.current = getScrollHandler();
420
+
421
+ addResizeHandle();
422
+
423
+ if (scrollableNodeRef.current) {
424
+ scrollableNodeRef.current.addEventListener('scroll', handleScroll);
425
+ handleScroll();
426
+ }
427
+
428
+ if (transitionProps.onEntered) {
429
+ transitionProps.onEntered(node, isAppearing);
430
+ }
431
+
432
+ if (onMount) onMount();
433
+ },
434
+ [addResizeHandle, getScrollHandler, handleScroll, onMount, transitionProps],
435
+ );
436
+
437
+ const handleExited: Required<TransitionProps>['onExited'] = useCallback(
438
+ (node) => {
439
+ removeResizeHandle();
440
+
441
+ setExited(true);
442
+
443
+ if (scrollableNodeRef.current) {
444
+ scrollableNodeRef.current.removeEventListener('scroll', handleScroll);
445
+ }
446
+
447
+ if (transitionProps.onExited) {
448
+ transitionProps.onExited(node);
449
+ }
450
+
451
+ if (onUnmount) onUnmount();
452
+
453
+ if (restoreContainerStylesRef.current) {
454
+ restoreContainerStylesRef.current();
455
+ }
456
+ },
457
+ [handleScroll, onUnmount, removeResizeHandle, transitionProps],
458
+ );
459
+
460
+ useEffect(() => {
461
+ if (open && isExited) {
462
+ if (!disableBlockingScroll) {
463
+ const el = getContainer();
464
+
465
+ handleContainer(el);
466
+
467
+ restoreContainerStylesRef.current = () => {
468
+ restoreContainerStylesRef.current = null;
469
+ restoreContainerStyles(el);
470
+ };
471
+ }
472
+
473
+ setExited(false);
474
+ }
475
+ }, [getContainer, open, disableBlockingScroll, isExited]);
476
+
477
+ useEffect(() => {
478
+ const ResizeObserver = window.ResizeObserver || ResizeObserverPolyfill;
479
+
480
+ resizeObserverRef.current = new ResizeObserver(checkToHasScrollBar);
481
+
482
+ return () => {
483
+ if (restoreContainerStylesRef.current) {
484
+ restoreContainerStylesRef.current();
485
+ }
486
+
487
+ if (resizeObserverRef.current) {
488
+ resizeObserverRef.current.disconnect();
489
+ }
490
+ };
491
+ }, []);
492
+
493
+ const contextValue = useMemo<BaseModalContext>(
494
+ () => ({
495
+ parentRef: wrapperRef,
496
+ componentRef: componentNodeRef,
497
+ hasHeader,
498
+ hasFooter,
499
+ hasScroll,
500
+ headerHighlighted,
501
+ footerHighlighted,
502
+ headerOffset,
503
+ setHeaderOffset,
504
+ contentRef,
505
+ setHasHeader,
506
+ setHasFooter,
507
+ onClose: handleClose,
508
+ }),
509
+ [
510
+ contentRef,
511
+ hasHeader,
512
+ hasFooter,
513
+ hasScroll,
514
+ headerHighlighted,
515
+ footerHighlighted,
516
+ headerOffset,
517
+ setHeaderOffset,
518
+ handleClose,
519
+ ],
520
+ );
521
+
522
+ const renderContent = () => (
523
+ <Stack value={zIndex}>
524
+ {(computedZIndex) => (
525
+ <BaseModalContext.Provider value={contextValue}>
526
+ <FocusLock
527
+ autoFocus={!disableAutoFocus}
528
+ disabled={disableFocusLock || !open}
529
+ returnFocus={!disableRestoreFocus}
530
+ >
531
+ {Backdrop && (
532
+ <Backdrop
533
+ {...backdropProps}
534
+ className={cn(backdropProps.className, styles.backdrop)}
535
+ open={open}
536
+ style={{
537
+ zIndex: computedZIndex,
538
+ }}
539
+ />
540
+ )}
541
+ <div
542
+ role='dialog'
543
+ className={cn(styles.wrapper, wrapperClassName, {
544
+ [styles.hidden]: !open && isExited,
545
+ })}
546
+ ref={mergeRefs([ref, wrapperRef])}
547
+ onKeyDown={handleKeyDown}
548
+ onMouseDown={handleBackdropMouseDown}
549
+ onMouseUp={handleBackdropMouseUp}
550
+ // eslint-disable-next-line jsx-a11y/no-noninteractive-tabindex
551
+ tabIndex={0}
552
+ data-test-id={dataTestId}
553
+ style={{
554
+ zIndex: computedZIndex,
555
+ }}
556
+ >
557
+ <CSSTransition
558
+ appear={true}
559
+ timeout={200}
560
+ classNames={styles}
561
+ {...transitionProps}
562
+ in={open}
563
+ onEntered={handleEntered}
564
+ onExited={handleExited}
565
+ >
566
+ <div
567
+ {...componentDivProps}
568
+ className={cn(
569
+ styles.component,
570
+ className,
571
+ componentDivProps?.className,
572
+ )}
573
+ ref={mergeRefs([
574
+ componentRef,
575
+ componentNodeRef,
576
+ componentDivProps?.ref || null,
577
+ ])}
578
+ >
579
+ <div
580
+ {...contentProps}
581
+ className={cn(
582
+ styles.content,
583
+ contentClassName,
584
+ contentProps?.className,
585
+ )}
586
+ >
587
+ {children}
588
+ </div>
589
+ </div>
590
+ </CSSTransition>
591
+ </div>
592
+ </FocusLock>
593
+ </BaseModalContext.Provider>
594
+ )}
595
+ </Stack>
596
+ );
597
+
598
+ if (!shouldRender) return null;
599
+
600
+ return usePortal ? (
601
+ <Portal getPortalContainer={container} immediateMount={true}>
602
+ {renderContent()}
603
+ </Portal>
604
+ ) : (
605
+ renderContent()
606
+ );
607
+ },
608
+ );
@@ -0,0 +1,60 @@
1
+ @import '@alfalab/core-components-themes/src/default.css';
2
+
3
+ .component {
4
+ position: relative;
5
+ box-sizing: border-box;
6
+ background: var(--color-light-bg-primary);
7
+ margin: auto;
8
+ flex-shrink: 0;
9
+ }
10
+
11
+ .wrapper {
12
+ position: fixed;
13
+ top: 0;
14
+ left: 0;
15
+ right: 0;
16
+ bottom: 0;
17
+
18
+ overflow: auto;
19
+ display: flex;
20
+ flex-direction: column;
21
+ align-items: center;
22
+ outline: 0;
23
+ }
24
+
25
+ .content {
26
+ width: 100%;
27
+ height: 100%;
28
+ display: flex;
29
+ flex-direction: column;
30
+ flex: 1;
31
+ }
32
+
33
+ .hidden {
34
+ display: none;
35
+ }
36
+
37
+ .backdrop {
38
+ z-index: 0;
39
+ }
40
+
41
+ .appear,
42
+ .enter {
43
+ opacity: 0;
44
+ }
45
+
46
+ .appearActive,
47
+ .enterActive {
48
+ opacity: 1;
49
+ transition: opacity 200ms ease-in;
50
+ }
51
+
52
+ .exit {
53
+ opacity: 1;
54
+ }
55
+
56
+ .exitActive,
57
+ .exitDone {
58
+ opacity: 0;
59
+ transition: opacity 200ms ease-out;
60
+ }
package/src/index.ts ADDED
@@ -0,0 +1,2 @@
1
+ export * from './Component';
2
+ export * from './utils';
@@ -0,0 +1,19 @@
1
+ /* eslint-disable */
2
+ // @ts-nocheck
3
+
4
+ if (typeof window !== 'undefined') {
5
+ if (Element && !Element.prototype.matches) {
6
+ Element.prototype.matches =
7
+ Element.prototype.matchesSelector ||
8
+ Element.prototype.mozMatchesSelector ||
9
+ Element.prototype.msMatchesSelector ||
10
+ Element.prototype.oMatchesSelector ||
11
+ Element.prototype.webkitMatchesSelector ||
12
+ function (s) {
13
+ const matches = (this.document || this.ownerDocument).querySelectorAll(s);
14
+ let i = matches.length;
15
+ while (--i >= 0 && matches.item(i) !== this) {}
16
+ return i > -1;
17
+ };
18
+ }
19
+ }
package/src/utils.ts ADDED
@@ -0,0 +1,128 @@
1
+ import { getModalStore, SavedStyle } from '@alfalab/core-components-global-store';
2
+
3
+ export function isScrolledToTop(target: HTMLElement) {
4
+ return target.scrollTop <= 0;
5
+ }
6
+
7
+ export function isScrolledToBottom(target: HTMLElement) {
8
+ return target.scrollHeight - target.offsetHeight <= target.scrollTop;
9
+ }
10
+
11
+ export function hasScrollbar(target: HTMLElement) {
12
+ return target.scrollHeight > target.clientHeight;
13
+ }
14
+
15
+ export const getScrollbarSize = (() => {
16
+ let cachedSize: number;
17
+
18
+ return () => {
19
+ if (cachedSize !== undefined) return cachedSize;
20
+
21
+ const scrollDiv = document.createElement('div');
22
+
23
+ scrollDiv.style.width = '99px';
24
+ scrollDiv.style.height = '99px';
25
+ scrollDiv.style.position = 'absolute';
26
+ scrollDiv.style.top = '-9999px';
27
+ scrollDiv.style.overflow = 'scroll';
28
+
29
+ document.body.appendChild(scrollDiv);
30
+ const scrollbarSize = scrollDiv.offsetWidth - scrollDiv.clientWidth;
31
+
32
+ document.body.removeChild(scrollDiv);
33
+
34
+ cachedSize = scrollbarSize;
35
+
36
+ return scrollbarSize;
37
+ };
38
+ })();
39
+
40
+ const isOverflowing = (container: Element) => {
41
+ if (document.body === container) {
42
+ return window.innerWidth > document.documentElement.clientWidth;
43
+ }
44
+
45
+ return container.scrollHeight > container.clientHeight;
46
+ };
47
+
48
+ const getPaddingRight = (node: Element) =>
49
+ parseInt(window.getComputedStyle(node).paddingRight, 10) || 0;
50
+
51
+ export const restoreContainerStyles = (container: HTMLElement) => {
52
+ const modalRestoreStyles = getModalStore().getRestoreStyles();
53
+
54
+ const index = modalRestoreStyles.findIndex((s) => s.container === container);
55
+ const existingStyles = modalRestoreStyles[index];
56
+
57
+ if (!existingStyles) return;
58
+
59
+ existingStyles.modals -= 1;
60
+
61
+ if (existingStyles.modals <= 0) {
62
+ modalRestoreStyles.splice(index, 1);
63
+
64
+ existingStyles.styles.forEach(({ value, el, key }) => {
65
+ if (value) {
66
+ el.style.setProperty(key, value);
67
+ } else {
68
+ el.style.removeProperty(key);
69
+ }
70
+ });
71
+ }
72
+ };
73
+
74
+ export const handleContainer = (container?: HTMLElement) => {
75
+ if (!container) return;
76
+
77
+ const modalRestoreStyles = getModalStore().getRestoreStyles();
78
+
79
+ const existingStyles = modalRestoreStyles.find((s) => s.container === container);
80
+
81
+ if (existingStyles) {
82
+ existingStyles.modals += 1;
83
+
84
+ return;
85
+ }
86
+
87
+ const containerStyles: SavedStyle[] = [];
88
+
89
+ if (isOverflowing(container)) {
90
+ // Вычисляет размер до применения `overflow hidden` для избежания скачков
91
+ const scrollbarSize = getScrollbarSize();
92
+
93
+ containerStyles.push({
94
+ value: container.style.paddingRight,
95
+ key: 'padding-right',
96
+ el: container,
97
+ });
98
+ // Вычисляем стили, чтобы получить реальный `padding` c шириной сколлбара
99
+ // eslint-disable-next-line no-param-reassign
100
+ container.style.paddingRight = `${getPaddingRight(container) + scrollbarSize}px`;
101
+ }
102
+
103
+ const parent = container.parentElement;
104
+ const scrollContainer =
105
+ // TODO: заменить на optional chaining
106
+ parent &&
107
+ parent.nodeName === 'HTML' &&
108
+ window.getComputedStyle(parent).overflowY === 'scroll'
109
+ ? parent
110
+ : container;
111
+
112
+ // Блокируем скролл даже если отсутствует скроллбар
113
+ if (scrollContainer.style.overflow !== 'hidden') {
114
+ containerStyles.push({
115
+ value: scrollContainer.style.overflow,
116
+ key: 'overflow',
117
+ el: scrollContainer,
118
+ });
119
+ }
120
+
121
+ scrollContainer.style.overflow = 'hidden';
122
+
123
+ modalRestoreStyles.push({
124
+ container,
125
+ modals: 1,
126
+ styles: containerStyles,
127
+ });
128
+ };