@blockle/blocks-react 3.2.0 → 3.3.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.
Files changed (75) hide show
  1. package/dist/components/display/Divider/divider.css.js +3 -3
  2. package/dist/components/form/Label/Label.d.ts +1 -1
  3. package/dist/components/form/Label/Label.d.ts.map +1 -1
  4. package/dist/components/form/Select/select.css.js +1 -1
  5. package/dist/components/navigation/Tabs/Tab.d.ts +21 -0
  6. package/dist/components/navigation/Tabs/Tab.d.ts.map +1 -0
  7. package/dist/components/navigation/Tabs/Tab.js +138 -0
  8. package/dist/components/navigation/Tabs/TabIndicator.d.ts +6 -0
  9. package/dist/components/navigation/Tabs/TabIndicator.d.ts.map +1 -0
  10. package/dist/components/navigation/Tabs/TabIndicator.js +19 -0
  11. package/dist/components/navigation/Tabs/TabList.d.ts +10 -0
  12. package/dist/components/navigation/Tabs/TabList.d.ts.map +1 -0
  13. package/dist/components/navigation/Tabs/TabList.js +101 -0
  14. package/dist/components/navigation/Tabs/TabListContext.d.ts +13 -0
  15. package/dist/components/navigation/Tabs/TabListContext.d.ts.map +1 -0
  16. package/dist/components/navigation/Tabs/TabListContext.js +6 -0
  17. package/dist/components/navigation/Tabs/TabNav.d.ts +15 -0
  18. package/dist/components/navigation/Tabs/TabNav.d.ts.map +1 -0
  19. package/dist/components/navigation/Tabs/TabNav.js +44 -0
  20. package/dist/components/navigation/Tabs/TabPanel.d.ts +14 -0
  21. package/dist/components/navigation/Tabs/TabPanel.d.ts.map +1 -0
  22. package/dist/components/navigation/Tabs/TabPanel.js +31 -0
  23. package/dist/components/navigation/Tabs/Tabs.d.ts +13 -0
  24. package/dist/components/navigation/Tabs/Tabs.d.ts.map +1 -0
  25. package/dist/components/navigation/Tabs/Tabs.js +33 -0
  26. package/dist/components/navigation/Tabs/TabsContext.d.ts +13 -0
  27. package/dist/components/navigation/Tabs/TabsContext.d.ts.map +1 -0
  28. package/dist/components/navigation/Tabs/TabsContext.js +27 -0
  29. package/dist/components/navigation/Tabs/tabs.css.d.ts +7 -0
  30. package/dist/components/navigation/Tabs/tabs.css.d.ts.map +1 -0
  31. package/dist/components/navigation/Tabs/tabs.css.js +122 -0
  32. package/dist/components/overlay/Dialog/Dialog.d.ts +1 -1
  33. package/dist/components/overlay/Dialog/Dialog.d.ts.map +1 -1
  34. package/dist/components/overlay/Dialog/Dialog.js +9 -7
  35. package/dist/components/overlay/Drawer/Drawer.d.ts +1 -1
  36. package/dist/components/overlay/Drawer/Drawer.d.ts.map +1 -1
  37. package/dist/components/overlay/Drawer/Drawer.js +9 -7
  38. package/dist/components/overlay/Menu/Menu.d.ts +52 -0
  39. package/dist/components/overlay/Menu/Menu.d.ts.map +1 -0
  40. package/dist/components/overlay/Menu/Menu.js +273 -0
  41. package/dist/components/overlay/Menu/menu-utils.d.ts +33 -0
  42. package/dist/components/overlay/Menu/menu-utils.d.ts.map +1 -0
  43. package/dist/components/overlay/Menu/menu-utils.js +54 -0
  44. package/dist/components/overlay/Popover/Popover.d.ts +9 -0
  45. package/dist/components/overlay/Popover/Popover.d.ts.map +1 -1
  46. package/dist/components/overlay/Popover/Popover.js +70 -31
  47. package/dist/components/overlay/Popover/popover-utils.d.ts +32 -1
  48. package/dist/components/overlay/Popover/popover-utils.d.ts.map +1 -1
  49. package/dist/components/overlay/Popover/popover-utils.js +77 -22
  50. package/dist/components/overlay/modalOverlayStack.d.ts +8 -0
  51. package/dist/components/overlay/modalOverlayStack.d.ts.map +1 -0
  52. package/dist/components/overlay/modalOverlayStack.js +37 -0
  53. package/dist/components/overlay/useModalOverlay.d.ts +6 -14
  54. package/dist/components/overlay/useModalOverlay.d.ts.map +1 -1
  55. package/dist/components/overlay/useModalOverlay.js +8 -31
  56. package/dist/components/typography/textOverflow.css.d.ts.map +1 -1
  57. package/dist/hooks/useModalDialog/useModalDialog.d.ts +5 -0
  58. package/dist/hooks/useModalDialog/useModalDialog.d.ts.map +1 -1
  59. package/dist/hooks/useModalDialog/useModalDialog.js +3 -2
  60. package/dist/hooks/usePreventBodyScroll/usePreventBodyScroll.d.ts +5 -0
  61. package/dist/hooks/usePreventBodyScroll/usePreventBodyScroll.d.ts.map +1 -1
  62. package/dist/hooks/usePreventBodyScroll/usePreventBodyScroll.js +31 -16
  63. package/dist/index.d.ts +6 -0
  64. package/dist/index.d.ts.map +1 -1
  65. package/dist/index.js +12 -0
  66. package/dist/testUtils/browser/contrast.d.ts +6 -2
  67. package/dist/testUtils/browser/contrast.d.ts.map +1 -1
  68. package/dist/testUtils/browser/emulateNoAnchorPositioning.d.ts +8 -0
  69. package/dist/testUtils/browser/emulateNoAnchorPositioning.d.ts.map +1 -0
  70. package/dist/testUtils/browser/render.d.ts +6 -2
  71. package/dist/testUtils/browser/render.d.ts.map +1 -1
  72. package/dist/testUtils/browser/screenshot.d.ts +2 -0
  73. package/dist/testUtils/browser/screenshot.d.ts.map +1 -0
  74. package/dist/testUtils/testTheme.css.d.ts.map +1 -1
  75. package/package.json +2 -2
@@ -17,7 +17,16 @@ export type PopoverProps = {
17
17
  * @deprecated Use `placement`, which wins when both are passed
18
18
  */
19
19
  position?: 'top' | 'bottom' | 'left' | 'right';
20
+ /**
21
+ * @deprecated Has no effect, an open popover always keeps its layout up to
22
+ * date
23
+ */
20
24
  sticky?: boolean;
25
+ /**
26
+ * Caps the popover's height to the room on the side it opens on, so a tall
27
+ * popover stops at the viewport's edge. Give it `overflow: auto` to scroll.
28
+ */
29
+ limitHeight?: boolean;
21
30
  } & HTMLElementProps<HTMLDivElement>;
22
31
  export declare const Popover: React.FC<PopoverProps>;
23
32
  //# sourceMappingURL=Popover.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"Popover.d.ts","sourceRoot":"","sources":["../../../../src/components/overlay/Popover/Popover.tsx"],"names":[],"mappings":"AAEA,OAAO,EAGL,KAAK,gBAAgB,EAEtB,MAAM,sBAAsB,CAAC;AAQ9B,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,0BAA0B,CAAC;AACnE,OAAO,EAAsB,KAAK,gBAAgB,EAAE,MAAM,oBAAoB,CAAC;AAE/E,YAAY,EAAE,gBAAgB,EAAE,CAAC;AAIjC,MAAM,MAAM,YAAY,GAAG;IACzB,aAAa,EAAE,KAAK,CAAC,SAAS,CAAC,WAAW,GAAG,IAAI,CAAC,CAAC;IACnD,cAAc,EAAE,CAAC,MAAM,EAAE,kBAAkB,KAAK,IAAI,CAAC;IACrD,IAAI,EAAE,OAAO,CAAC;IACd;;;;;OAKG;IACH,SAAS,CAAC,EAAE,gBAAgB,CAAC;IAC7B;;OAEG;IACH,QAAQ,CAAC,EAAE,KAAK,GAAG,QAAQ,GAAG,MAAM,GAAG,OAAO,CAAC;IAC/C,MAAM,CAAC,EAAE,OAAO,CAAC;CAElB,GAAG,gBAAgB,CAAC,cAAc,CAAC,CAAC;AAErC,eAAO,MAAM,OAAO,EAAE,KAAK,CAAC,EAAE,CAAC,YAAY,CAkJ1C,CAAC"}
1
+ {"version":3,"file":"Popover.d.ts","sourceRoot":"","sources":["../../../../src/components/overlay/Popover/Popover.tsx"],"names":[],"mappings":"AAEA,OAAO,EAGL,KAAK,gBAAgB,EAEtB,MAAM,sBAAsB,CAAC;AAQ9B,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,0BAA0B,CAAC;AACnE,OAAO,EAGL,KAAK,gBAAgB,EACtB,MAAM,oBAAoB,CAAC;AAE5B,YAAY,EAAE,gBAAgB,EAAE,CAAC;AAIjC,MAAM,MAAM,YAAY,GAAG;IACzB,aAAa,EAAE,KAAK,CAAC,SAAS,CAAC,WAAW,GAAG,IAAI,CAAC,CAAC;IACnD,cAAc,EAAE,CAAC,MAAM,EAAE,kBAAkB,KAAK,IAAI,CAAC;IACrD,IAAI,EAAE,OAAO,CAAC;IACd;;;;;OAKG;IACH,SAAS,CAAC,EAAE,gBAAgB,CAAC;IAC7B;;OAEG;IACH,QAAQ,CAAC,EAAE,KAAK,GAAG,QAAQ,GAAG,MAAM,GAAG,OAAO,CAAC;IAC/C;;;OAGG;IACH,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB;;;OAGG;IACH,WAAW,CAAC,EAAE,OAAO,CAAC;CAEvB,GAAG,gBAAgB,CAAC,cAAc,CAAC,CAAC;AAErC,eAAO,MAAM,OAAO,EAAE,KAAK,CAAC,EAAE,CAAC,YAAY,CAsN1C,CAAC"}
@@ -7,23 +7,24 @@ import { useComponentStyles } from "../../../hooks/useComponentStyles/useCompone
7
7
  import { useIsomorphicLayoutEffect } from "../../../hooks/useIsomorphicLayoutEffect/useIsomorphicLayoutEffect.js";
8
8
  import { useKeyboard } from "../../../hooks/useKeyboard/useKeyboard.js";
9
9
  import { Box } from "../../layout/Box/Box.js";
10
- import { getPopoverPosition } from "./popover-utils.js";
10
+ import { getPopoverLayout } from "./popover-utils.js";
11
11
  const Popover = ({
12
12
  anchorElement,
13
13
  children,
14
14
  className,
15
+ limitHeight = false,
15
16
  onRequestClose,
16
17
  open,
17
18
  placement: placementProp,
18
19
  position,
19
20
  ref,
20
- sticky,
21
+ sticky: _sticky,
21
22
  style,
22
23
  ...restProps
23
24
  }) => {
24
25
  const placement = placementProp ?? position ?? "top";
25
26
  const popoverRef = useRef(null);
26
- const [popoverPosition, setPopoverPosition] = useState({ x: 0, y: 0 });
27
+ const [layout, setLayout] = useState({ x: 0, y: 0 });
27
28
  const [visible, setVisible] = useState(open);
28
29
  const popoverClassName = useComponentStyles("popover", { root: true }, false);
29
30
  const onEscape = useCallback(
@@ -50,8 +51,9 @@ const Popover = ({
50
51
  return;
51
52
  }
52
53
  element.showPopover();
53
- const [x, y] = getPopoverPosition(placement, anchorElement, popoverRef);
54
- setPopoverPosition({ x, y });
54
+ setLayout(
55
+ getPopoverLayout(placement, anchorElement, popoverRef, limitHeight)
56
+ );
55
57
  } else if (open) {
56
58
  setVisible(true);
57
59
  } else {
@@ -71,36 +73,70 @@ const Popover = ({
71
73
  }
72
74
  }, [open, visible]);
73
75
  useEffect(() => {
74
- if (open) {
75
- const [x, y] = getPopoverPosition(placement, anchorElement, popoverRef);
76
- setPopoverPosition({ x, y });
77
- }
78
- }, [open, anchorElement, placement]);
79
- useEffect(() => {
80
- if (!open || !sticky) {
76
+ if (!open || !visible) {
81
77
  return;
82
78
  }
83
- function updatePopoverPosition() {
84
- const [x, y] = getPopoverPosition(placement, anchorElement, popoverRef);
85
- setPopoverPosition((prev) => {
86
- if (prev.x === x && prev.y === y) {
79
+ let frame = 0;
80
+ function updateLayout() {
81
+ frame = 0;
82
+ if (!popoverRef.current) {
83
+ return;
84
+ }
85
+ const next = getPopoverLayout(
86
+ placement,
87
+ anchorElement,
88
+ popoverRef,
89
+ limitHeight
90
+ );
91
+ setLayout((prev) => {
92
+ if (prev.x === next.x && prev.y === next.y && prev.maxWidth === next.maxWidth && prev.maxHeight === next.maxHeight) {
87
93
  return prev;
88
94
  }
89
- return { x, y };
95
+ return next;
90
96
  });
91
97
  }
92
- window.addEventListener("resize", updatePopoverPosition);
93
- window.addEventListener("scroll", updatePopoverPosition);
98
+ function scheduleLayoutUpdate() {
99
+ if (!frame) {
100
+ frame = requestAnimationFrame(updateLayout);
101
+ }
102
+ }
103
+ function onScroll(event) {
104
+ if (event.target instanceof Node && popoverRef.current?.contains(event.target)) {
105
+ return;
106
+ }
107
+ scheduleLayoutUpdate();
108
+ }
109
+ updateLayout();
110
+ const { visualViewport } = window;
111
+ const observer = typeof ResizeObserver === "undefined" ? void 0 : new ResizeObserver(scheduleLayoutUpdate);
112
+ if (anchorElement.current) {
113
+ observer?.observe(anchorElement.current);
114
+ }
115
+ if (popoverRef.current) {
116
+ observer?.observe(popoverRef.current);
117
+ }
118
+ window.addEventListener("resize", scheduleLayoutUpdate);
119
+ document.addEventListener("scroll", onScroll, {
120
+ capture: true,
121
+ passive: true
122
+ });
123
+ visualViewport?.addEventListener("resize", scheduleLayoutUpdate);
94
124
  return () => {
95
- window.removeEventListener("resize", updatePopoverPosition);
96
- window.removeEventListener("scroll", updatePopoverPosition);
125
+ cancelAnimationFrame(frame);
126
+ observer?.disconnect();
127
+ window.removeEventListener("resize", scheduleLayoutUpdate);
128
+ document.removeEventListener("scroll", onScroll, { capture: true });
129
+ visualViewport?.removeEventListener("resize", scheduleLayoutUpdate);
97
130
  };
98
- }, [placement, anchorElement, open, sticky]);
99
- const onAnimationEnd = useCallback(() => {
100
- if (!open) {
101
- setVisible(false);
102
- }
103
- }, [open]);
131
+ }, [open, visible, placement, anchorElement, limitHeight]);
132
+ const onClosingEnd = useCallback(
133
+ (event) => {
134
+ if (!open && event.target === event.currentTarget) {
135
+ setVisible(false);
136
+ }
137
+ },
138
+ [open]
139
+ );
104
140
  if (!visible) {
105
141
  return null;
106
142
  }
@@ -112,12 +148,15 @@ const Popover = ({
112
148
  popover: "manual",
113
149
  className: classnames(popoverClassName, className),
114
150
  position: "absolute",
115
- onAnimationEnd,
116
- onTransitionEnd: onAnimationEnd,
151
+ onAnimationEnd: onClosingEnd,
152
+ onTransitionEnd: onClosingEnd,
153
+ onTransitionCancel: onClosingEnd,
117
154
  style: {
118
155
  ...style,
119
- left: popoverPosition.x,
120
- top: popoverPosition.y
156
+ left: layout.x,
157
+ top: layout.y,
158
+ maxWidth: layout.maxWidth,
159
+ maxHeight: layout.maxHeight
121
160
  },
122
161
  ...restProps,
123
162
  children
@@ -35,6 +35,37 @@ export type ComputePopoverPositionOptions = {
35
35
  * Returns the popover's `left` and `top`, relative to the document
36
36
  */
37
37
  export declare function computePopoverPosition({ placement, anchor, popover, viewport, }: ComputePopoverPositionOptions): PopoverPositions;
38
- export declare function getPopoverPosition(placement: PopoverPlacement, anchorRef: React.RefObject<HTMLElement | null>, popoverRef: React.RefObject<HTMLElement | null>): PopoverPositions;
38
+ /**
39
+ * Returns the most height the popover's border box can take on the side it
40
+ * opens on, keeping it VIEWPORT_PADDING inside the viewport
41
+ */
42
+ export declare function computeAvailableHeight({ placement, anchor, popover, viewport, }: ComputePopoverPositionOptions): number;
43
+ /**
44
+ * Returns the most width the popover's border box can take, keeping it
45
+ * VIEWPORT_PADDING inside the viewport on both sides
46
+ */
47
+ export declare function computeAvailableWidth(viewport: ComputePopoverPositionOptions['viewport']): number;
48
+ export type PopoverLayout = {
49
+ x: number;
50
+ y: number;
51
+ /**
52
+ * The popover's `max-width`: the viewport's width, VIEWPORT_PADDING inside
53
+ * both edges. It follows the popover's `box-sizing`, so padding and border
54
+ * fit too.
55
+ */
56
+ maxWidth?: number;
57
+ /**
58
+ * The popover's `max-height`, when it is limited to the room it has. It
59
+ * follows the popover's `box-sizing`, so padding and border fit too.
60
+ */
61
+ maxHeight?: number;
62
+ };
63
+ /**
64
+ * Measures the anchor and popover and returns where to put the popover. A
65
+ * popover wider than the viewport is capped to it, and placed by its capped
66
+ * width. With `limitHeight`, a popover taller than the room on its side is
67
+ * capped to that room, and placed by its capped height.
68
+ */
69
+ export declare function getPopoverLayout(placement: PopoverPlacement, anchorRef: React.RefObject<HTMLElement | null>, popoverRef: React.RefObject<HTMLElement | null>, limitHeight?: boolean): PopoverLayout;
39
70
  export {};
40
71
  //# sourceMappingURL=popover-utils.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"popover-utils.d.ts","sourceRoot":"","sources":["../../../../src/components/overlay/Popover/popover-utils.ts"],"names":[],"mappings":"AAEA,MAAM,MAAM,gBAAgB,GAAG,CAAC,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,MAAM,CAAC,CAAC;AAEtD,KAAK,WAAW,GAAG,KAAK,GAAG,QAAQ,GAAG,MAAM,GAAG,OAAO,CAAC;AAEvD,KAAK,WAAW,GAAG,OAAO,GAAG,KAAK,CAAC;AAEnC,MAAM,MAAM,gBAAgB,GAAG,WAAW,GAAG,GAAG,WAAW,IAAI,WAAW,EAAE,CAAC;AAE7E,KAAK,IAAI,GAAG;IAAE,GAAG,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,CAAC;AAEzE,KAAK,MAAM,GAAG;IAAE,GAAG,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAA;CAAE,CAAC;AAE3E,MAAM,MAAM,6BAA6B,GAAG;IAC1C,SAAS,EAAE,gBAAgB,CAAC;IAC5B,wDAAwD;IACxD,MAAM,EAAE,IAAI,CAAC;IACb,kEAAkE;IAClE,OAAO,EAAE;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAA;KAAE,CAAC;IAC3D,QAAQ,EAAE;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAC;QAAC,OAAO,EAAE,MAAM,CAAC;QAAC,OAAO,EAAE,MAAM,CAAA;KAAE,CAAC;CAC/E,CAAC;AAEF;;GAEG;AACH,wBAAgB,sBAAsB,CAAC,EACrC,SAAS,EACT,MAAM,EACN,OAAO,EACP,QAAQ,GACT,EAAE,6BAA6B,GAAG,gBAAgB,CAyClD;AAoED,wBAAgB,kBAAkB,CAChC,SAAS,EAAE,gBAAgB,EAC3B,SAAS,EAAE,KAAK,CAAC,SAAS,CAAC,WAAW,GAAG,IAAI,CAAC,EAC9C,UAAU,EAAE,KAAK,CAAC,SAAS,CAAC,WAAW,GAAG,IAAI,CAAC,GAC9C,gBAAgB,CAyClB"}
1
+ {"version":3,"file":"popover-utils.d.ts","sourceRoot":"","sources":["../../../../src/components/overlay/Popover/popover-utils.ts"],"names":[],"mappings":"AAEA,MAAM,MAAM,gBAAgB,GAAG,CAAC,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,MAAM,CAAC,CAAC;AAEtD,KAAK,WAAW,GAAG,KAAK,GAAG,QAAQ,GAAG,MAAM,GAAG,OAAO,CAAC;AAEvD,KAAK,WAAW,GAAG,OAAO,GAAG,KAAK,CAAC;AAEnC,MAAM,MAAM,gBAAgB,GAAG,WAAW,GAAG,GAAG,WAAW,IAAI,WAAW,EAAE,CAAC;AAE7E,KAAK,IAAI,GAAG;IAAE,GAAG,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,CAAC;AAEzE,KAAK,MAAM,GAAG;IAAE,GAAG,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAA;CAAE,CAAC;AAE3E,MAAM,MAAM,6BAA6B,GAAG;IAC1C,SAAS,EAAE,gBAAgB,CAAC;IAC5B,wDAAwD;IACxD,MAAM,EAAE,IAAI,CAAC;IACb,kEAAkE;IAClE,OAAO,EAAE;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAA;KAAE,CAAC;IAC3D,QAAQ,EAAE;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAC;QAAC,OAAO,EAAE,MAAM,CAAC;QAAC,OAAO,EAAE,MAAM,CAAA;KAAE,CAAC;CAC/E,CAAC;AAEF;;GAEG;AACH,wBAAgB,sBAAsB,CAAC,EACrC,SAAS,EACT,MAAM,EACN,OAAO,EACP,QAAQ,GACT,EAAE,6BAA6B,GAAG,gBAAgB,CAgDlD;AA0CD;;;GAGG;AACH,wBAAgB,sBAAsB,CAAC,EACrC,SAAS,EACT,MAAM,EACN,OAAO,EACP,QAAQ,GACT,EAAE,6BAA6B,GAAG,MAAM,CAgBxC;AAED;;;GAGG;AACH,wBAAgB,qBAAqB,CACnC,QAAQ,EAAE,6BAA6B,CAAC,UAAU,CAAC,GAClD,MAAM,CAER;AAyCD,MAAM,MAAM,aAAa,GAAG;IAC1B,CAAC,EAAE,MAAM,CAAC;IACV,CAAC,EAAE,MAAM,CAAC;IACV;;;;OAIG;IACH,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB;;;OAGG;IACH,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB,CAAC;AAEF;;;;;GAKG;AACH,wBAAgB,gBAAgB,CAC9B,SAAS,EAAE,gBAAgB,EAC3B,SAAS,EAAE,KAAK,CAAC,SAAS,CAAC,WAAW,GAAG,IAAI,CAAC,EAC9C,UAAU,EAAE,KAAK,CAAC,SAAS,CAAC,WAAW,GAAG,IAAI,CAAC,EAC/C,WAAW,UAAQ,GAClB,aAAa,CAyFf"}
@@ -11,7 +11,11 @@ function computePopoverPosition({
11
11
  let left;
12
12
  let top;
13
13
  if (side === "top" || side === "bottom") {
14
- top = side === "top" ? anchor.top - margin.bottom - popover.height : anchor.top + anchor.height + margin.top;
14
+ top = holdInViewport(
15
+ side === "top" ? anchor.top - margin.bottom - popover.height : anchor.top + anchor.height + margin.top,
16
+ popover.height,
17
+ viewport.height
18
+ );
15
19
  left = alignOnCrossAxis(
16
20
  edge,
17
21
  anchor.left,
@@ -20,7 +24,11 @@ function computePopoverPosition({
20
24
  viewport.width
21
25
  );
22
26
  } else {
23
- left = side === "left" ? anchor.left - margin.right - popover.width : anchor.left + anchor.width + margin.left;
27
+ left = holdInViewport(
28
+ side === "left" ? anchor.left - margin.right - popover.width : anchor.left + anchor.width + margin.left,
29
+ popover.width,
30
+ viewport.width
31
+ );
24
32
  top = alignOnCrossAxis(
25
33
  edge,
26
34
  anchor.top,
@@ -56,6 +64,25 @@ function flipSide(side, anchor, popover, viewport) {
56
64
  }
57
65
  return side;
58
66
  }
67
+ function computeAvailableHeight({
68
+ placement,
69
+ anchor,
70
+ popover,
71
+ viewport
72
+ }) {
73
+ const [preferredSide] = placement.split("-");
74
+ const side = flipSide(preferredSide, anchor, popover, viewport);
75
+ let available = viewport.height - VIEWPORT_PADDING * 2;
76
+ if (side === "top") {
77
+ available = anchor.top - popover.margin.bottom - VIEWPORT_PADDING;
78
+ } else if (side === "bottom") {
79
+ available = viewport.height - VIEWPORT_PADDING - (anchor.top + anchor.height) - popover.margin.top;
80
+ }
81
+ return Math.max(0, available);
82
+ }
83
+ function computeAvailableWidth(viewport) {
84
+ return Math.max(0, viewport.width - VIEWPORT_PADDING * 2);
85
+ }
59
86
  function alignOnCrossAxis(edge, anchorStart, anchorSize, popoverSize, viewportSize) {
60
87
  let start = anchorStart + (anchorSize - popoverSize) / 2;
61
88
  if (edge === "start") {
@@ -63,46 +90,74 @@ function alignOnCrossAxis(edge, anchorStart, anchorSize, popoverSize, viewportSi
63
90
  } else if (edge === "end") {
64
91
  start = anchorStart + anchorSize - popoverSize;
65
92
  }
93
+ return holdInViewport(start, popoverSize, viewportSize);
94
+ }
95
+ function holdInViewport(start, popoverSize, viewportSize) {
66
96
  const max = viewportSize - VIEWPORT_PADDING - popoverSize;
67
97
  return Math.max(VIEWPORT_PADDING, Math.min(start, max));
68
98
  }
69
- function getPopoverPosition(placement, anchorRef, popoverRef) {
99
+ function getPopoverLayout(placement, anchorRef, popoverRef, limitHeight = false) {
70
100
  if (!anchorRef.current || !popoverRef.current) {
71
- return [0, 0];
101
+ return { x: 0, y: 0 };
72
102
  }
103
+ const element = popoverRef.current;
73
104
  const anchorRect = anchorRef.current.getBoundingClientRect();
74
- const popoverRect = popoverRef.current.getBoundingClientRect();
75
- const popoverStyles = getComputedStyle(popoverRef.current);
76
- const [width, height] = getOriginalElementSize(
105
+ const popoverRect = element.getBoundingClientRect();
106
+ const popoverStyles = getComputedStyle(element);
107
+ const [measuredWidth, measuredHeight] = getOriginalElementSize(
77
108
  popoverStyles,
78
109
  popoverRect.width,
79
110
  popoverRect.height
80
111
  );
112
+ const style = (property) => cssValueToNumber(popoverStyles.getPropertyValue(property));
113
+ const borderY = style("border-top-width") + style("border-bottom-width");
114
+ const isContentBox = popoverStyles.boxSizing === "content-box";
115
+ const height = limitHeight ? Math.max(measuredHeight, element.scrollHeight + borderY) : measuredHeight;
81
116
  const { documentElement } = document;
82
- return computePopoverPosition({
117
+ const viewport = {
118
+ width: documentElement.clientWidth,
119
+ height: documentElement.clientHeight,
120
+ scrollX: documentElement.scrollLeft,
121
+ scrollY: documentElement.scrollTop
122
+ };
123
+ const availableWidth = computeAvailableWidth(viewport);
124
+ const options = {
83
125
  placement,
84
126
  anchor: anchorRect,
85
127
  popover: {
86
- width,
128
+ width: Math.min(measuredWidth, availableWidth),
87
129
  height,
88
130
  margin: {
89
- top: cssValueToNumber(popoverStyles.getPropertyValue("margin-top")),
90
- right: cssValueToNumber(popoverStyles.getPropertyValue("margin-right")),
91
- bottom: cssValueToNumber(
92
- popoverStyles.getPropertyValue("margin-bottom")
93
- ),
94
- left: cssValueToNumber(popoverStyles.getPropertyValue("margin-left"))
131
+ top: style("margin-top"),
132
+ right: style("margin-right"),
133
+ bottom: style("margin-bottom"),
134
+ left: style("margin-left")
95
135
  }
96
136
  },
97
- viewport: {
98
- width: documentElement.clientWidth,
99
- height: documentElement.clientHeight,
100
- scrollX: documentElement.scrollLeft,
101
- scrollY: documentElement.scrollTop
102
- }
137
+ viewport
138
+ };
139
+ const paddingAndBorderX = isContentBox ? style("padding-left") + style("padding-right") + style("border-left-width") + style("border-right-width") : 0;
140
+ const maxWidth = Math.max(0, availableWidth - paddingAndBorderX);
141
+ if (!limitHeight) {
142
+ const [x2, y2] = computePopoverPosition(options);
143
+ return { x: x2, y: y2, maxWidth };
144
+ }
145
+ const availableHeight = computeAvailableHeight(options);
146
+ const [x, y] = computePopoverPosition({
147
+ ...options,
148
+ popover: { ...options.popover, height: Math.min(height, availableHeight) }
103
149
  });
150
+ const paddingAndBorderY = isContentBox ? style("padding-top") + style("padding-bottom") + borderY : 0;
151
+ return {
152
+ x,
153
+ y,
154
+ maxWidth,
155
+ maxHeight: Math.max(0, availableHeight - paddingAndBorderY)
156
+ };
104
157
  }
105
158
  export {
159
+ computeAvailableHeight,
160
+ computeAvailableWidth,
106
161
  computePopoverPosition,
107
- getPopoverPosition
162
+ getPopoverLayout
108
163
  };
@@ -0,0 +1,8 @@
1
+ /**
2
+ * Puts a modal overlay on the stack of open ones while `open` is true, and
3
+ * returns whether it is the top one: the one opened last, wherever it is
4
+ * mounted. Like the browser's top layer, which shows each modal dialog above
5
+ * the ones shown before it, the order does not follow the React tree.
6
+ */
7
+ export declare const useIsTopModalOverlay: (open: boolean) => boolean;
8
+ //# sourceMappingURL=modalOverlayStack.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"modalOverlayStack.d.ts","sourceRoot":"","sources":["../../../src/components/overlay/modalOverlayStack.ts"],"names":[],"mappings":"AA8BA;;;;;GAKG;AACH,eAAO,MAAM,oBAAoB,GAAI,MAAM,OAAO,KAAG,OAoBpD,CAAC"}
@@ -0,0 +1,37 @@
1
+ "use client";
2
+ import { useState, useSyncExternalStore } from "react";
3
+ import { useIsomorphicLayoutEffect } from "../../hooks/useIsomorphicLayoutEffect/useIsomorphicLayoutEffect.js";
4
+ let stack = [];
5
+ const listeners = /* @__PURE__ */ new Set();
6
+ const setStack = (next) => {
7
+ stack = next;
8
+ for (const listener of listeners) {
9
+ listener();
10
+ }
11
+ };
12
+ const subscribe = (listener) => {
13
+ listeners.add(listener);
14
+ return () => {
15
+ listeners.delete(listener);
16
+ };
17
+ };
18
+ const useIsTopModalOverlay = (open) => {
19
+ const [token] = useState(() => ({}));
20
+ useIsomorphicLayoutEffect(() => {
21
+ if (!open) {
22
+ return;
23
+ }
24
+ setStack([...stack, token]);
25
+ return () => {
26
+ setStack(stack.filter((item) => item !== token));
27
+ };
28
+ }, [open, token]);
29
+ return useSyncExternalStore(
30
+ subscribe,
31
+ () => stack.at(-1) === token,
32
+ () => false
33
+ );
34
+ };
35
+ export {
36
+ useIsTopModalOverlay
37
+ };
@@ -1,9 +1,4 @@
1
1
  import { RequestCloseReason } from './requestCloseReason.js';
2
- type DialogContextValue = {
3
- setEnabled: (active: boolean) => void;
4
- };
5
- export declare const DialogContext: import('react').Context<DialogContextValue | undefined>;
6
- export declare const useNestedDialog: (open: boolean) => boolean;
7
2
  type UseDialogCloseRequestsOptions = {
8
3
  enabled: boolean;
9
4
  onRequestClose: (reason: RequestCloseReason) => void;
@@ -19,17 +14,14 @@ type UseModalOverlayOptions = Omit<UseDialogCloseRequestsOptions, 'enabled'> & {
19
14
  * closed, the body does not scroll, and an Escape, close request or click
20
15
  * outside requests to close.
21
16
  *
22
- * While a nested Dialog or Drawer is open, the outer one ignores Escape and
23
- * clicks outside, and leaves the body scroll lock to the outermost one.
24
- * Provide `contextValue` through DialogContext to the dialog's content.
17
+ * Only the modal overlay opened last handles Escape and clicks outside, so a
18
+ * click in a Dialog or Drawer does not close the ones below it, whether it is
19
+ * nested in them or mounted elsewhere.
25
20
  */
26
21
  export declare const useModalOverlay: (dialogRef: React.RefObject<HTMLDialogElement | null>, { open, onRequestClose, ...handlers }: UseModalOverlayOptions) => {
27
- contextValue: DialogContextValue;
28
- closeRequestProps: {
29
- onKeyDown: (event: React.KeyboardEvent<HTMLDialogElement>) => void;
30
- onKeyDownCapture: (event: React.KeyboardEvent<HTMLDialogElement>) => void;
31
- onCancel: (event: React.SyntheticEvent<HTMLDialogElement>) => void;
32
- };
22
+ onKeyDown: (event: React.KeyboardEvent<HTMLDialogElement>) => void;
23
+ onKeyDownCapture: (event: React.KeyboardEvent<HTMLDialogElement>) => void;
24
+ onCancel: (event: React.SyntheticEvent<HTMLDialogElement>) => void;
33
25
  };
34
26
  export {};
35
27
  //# sourceMappingURL=useModalOverlay.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"useModalOverlay.d.ts","sourceRoot":"","sources":["../../../src/components/overlay/useModalOverlay.ts"],"names":[],"mappings":"AAOA,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,yBAAyB,CAAC;AAElE,KAAK,kBAAkB,GAAG;IACxB,UAAU,EAAE,CAAC,MAAM,EAAE,OAAO,KAAK,IAAI,CAAC;CACvC,CAAC;AAEF,eAAO,MAAM,aAAa,yDAEzB,CAAC;AAEF,eAAO,MAAM,eAAe,GAAI,MAAM,OAAO,KAAG,OAgB/C,CAAC;AAEF,KAAK,6BAA6B,GAAG;IACnC,OAAO,EAAE,OAAO,CAAC;IACjB,cAAc,EAAE,CAAC,MAAM,EAAE,kBAAkB,KAAK,IAAI,CAAC;IACrD,SAAS,CAAC,EAAE,KAAK,CAAC,oBAAoB,CAAC,iBAAiB,CAAC,CAAC;IAC1D,gBAAgB,CAAC,EAAE,KAAK,CAAC,oBAAoB,CAAC,iBAAiB,CAAC,CAAC;IACjE,QAAQ,CAAC,EAAE,KAAK,CAAC,iBAAiB,CAAC,iBAAiB,CAAC,CAAC;CACvD,CAAC;AA6EF,KAAK,sBAAsB,GAAG,IAAI,CAAC,6BAA6B,EAAE,SAAS,CAAC,GAAG;IAC7E,IAAI,EAAE,OAAO,CAAC;CACf,CAAC;AAEF;;;;;;;;GAQG;AACH,eAAO,MAAM,eAAe,GAC1B,WAAW,KAAK,CAAC,SAAS,CAAC,iBAAiB,GAAG,IAAI,CAAC,EACpD,uCAAuC,sBAAsB;;;2BA/D/B,KAAK,CAAC,aAAa,CAAC,iBAAiB,CAAC;kCAR3D,KAAK,CAAC,aAAa,CAAC,iBAAiB,CAAC;0BA2BlB,KAAK,CAAC,cAAc,CAAC,iBAAiB,CAAC;;CAqErE,CAAC"}
1
+ {"version":3,"file":"useModalOverlay.d.ts","sourceRoot":"","sources":["../../../src/components/overlay/useModalOverlay.ts"],"names":[],"mappings":"AAQA,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,yBAAyB,CAAC;AAElE,KAAK,6BAA6B,GAAG;IACnC,OAAO,EAAE,OAAO,CAAC;IACjB,cAAc,EAAE,CAAC,MAAM,EAAE,kBAAkB,KAAK,IAAI,CAAC;IACrD,SAAS,CAAC,EAAE,KAAK,CAAC,oBAAoB,CAAC,iBAAiB,CAAC,CAAC;IAC1D,gBAAgB,CAAC,EAAE,KAAK,CAAC,oBAAoB,CAAC,iBAAiB,CAAC,CAAC;IACjE,QAAQ,CAAC,EAAE,KAAK,CAAC,iBAAiB,CAAC,iBAAiB,CAAC,CAAC;CACvD,CAAC;AA6EF,KAAK,sBAAsB,GAAG,IAAI,CAAC,6BAA6B,EAAE,SAAS,CAAC,GAAG;IAC7E,IAAI,EAAE,OAAO,CAAC;CACf,CAAC;AAEF;;;;;;;;GAQG;AACH,eAAO,MAAM,eAAe,GAC1B,WAAW,KAAK,CAAC,SAAS,CAAC,iBAAiB,GAAG,IAAI,CAAC,EACpD,uCAAuC,sBAAsB;uBA/D/B,KAAK,CAAC,aAAa,CAAC,iBAAiB,CAAC;8BAR3D,KAAK,CAAC,aAAa,CAAC,iBAAiB,CAAC;sBA2BlB,KAAK,CAAC,cAAc,CAAC,iBAAiB,CAAC;CA2DrE,CAAC"}
@@ -1,24 +1,9 @@
1
1
  "use client";
2
- import { useState, createContext, useContext, useEffect, useRef } from "react";
2
+ import { useRef } from "react";
3
3
  import { useClickOutside } from "../../hooks/useClickOutside/useClickOutside.js";
4
4
  import { usePreventBodyScroll } from "../../hooks/usePreventBodyScroll/usePreventBodyScroll.js";
5
5
  import { useRestoreFocus } from "../../hooks/useRestoreFocus/useRestoreFocus.js";
6
- const DialogContext = createContext(
7
- void 0
8
- );
9
- const useNestedDialog = (open) => {
10
- const parentDialog = useContext(DialogContext);
11
- useEffect(() => {
12
- if (!parentDialog || !open) {
13
- return;
14
- }
15
- parentDialog.setEnabled(false);
16
- return () => {
17
- parentDialog.setEnabled(true);
18
- };
19
- }, [parentDialog, open]);
20
- return !!parentDialog;
21
- };
6
+ import { useIsTopModalOverlay } from "./modalOverlayStack.js";
22
7
  const useDialogCloseRequests = ({
23
8
  enabled,
24
9
  onRequestClose,
@@ -63,23 +48,15 @@ const useDialogCloseRequests = ({
63
48
  };
64
49
  };
65
50
  const useModalOverlay = (dialogRef, { open, onRequestClose, ...handlers }) => {
66
- const [enabled, setEnabled] = useState(true);
67
- const [contextValue] = useState(() => ({ setEnabled }));
68
51
  useRestoreFocus(open);
69
- const isNested = useNestedDialog(open);
70
- usePreventBodyScroll(open && !isNested);
52
+ usePreventBodyScroll(open);
53
+ const isTop = useIsTopModalOverlay(open);
54
+ const enabled = open && isTop;
71
55
  useClickOutside(dialogRef, () => onRequestClose("outside-click"), {
72
- enabled: open && enabled
73
- });
74
- const closeRequestProps = useDialogCloseRequests({
75
- enabled: open && enabled,
76
- onRequestClose,
77
- ...handlers
56
+ enabled
78
57
  });
79
- return { contextValue, closeRequestProps };
58
+ return useDialogCloseRequests({ enabled, onRequestClose, ...handlers });
80
59
  };
81
60
  export {
82
- DialogContext,
83
- useModalOverlay,
84
- useNestedDialog
61
+ useModalOverlay
85
62
  };
@@ -1 +1 @@
1
- {"version":3,"file":"textOverflow.css.d.ts","sourceRoot":"","sources":["../../../src/components/typography/textOverflow.css.ts"],"names":[],"mappings":"AAIA,eAAO,MAAM,SAAS,mCAYrB,CAAC;AAEF,eAAO,MAAM,QAAQ,QASnB,CAAC"}
1
+ {"version":3,"file":"textOverflow.css.d.ts","sourceRoot":"","sources":["../../../src/components/typography/textOverflow.css.ts"],"names":[],"mappings":"AAKA,eAAO,MAAM,SAAS,mCAYrB,CAAC;AAEF,eAAO,MAAM,QAAQ,QASnB,CAAC"}
@@ -5,9 +5,14 @@
5
5
  * The dialog also closes whenever the effect is cleaned up while open, such as
6
6
  * when React hides it in an <Activity> or unmounts it, so it never lingers in
7
7
  * the top layer. Revealing it again shows it as modal.
8
+ *
9
+ * Render `openAttribute` as the dialog's `open` attribute. It is `open` on the
10
+ * first render, so a server-rendered open dialog hydrates without a mismatch,
11
+ * and never changes after it, so React leaves the attribute to this hook.
8
12
  */
9
13
  export declare const useModalDialog: (dialogRef: React.RefObject<HTMLDialogElement | null>, open: boolean) => {
10
14
  visible: boolean;
11
15
  onAnimationEnd: () => void;
16
+ openAttribute: boolean;
12
17
  };
13
18
  //# sourceMappingURL=useModalDialog.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"useModalDialog.d.ts","sourceRoot":"","sources":["../../../src/hooks/useModalDialog/useModalDialog.ts"],"names":[],"mappings":"AAOA;;;;;;;GAOG;AACH,eAAO,MAAM,cAAc,GACzB,WAAW,KAAK,CAAC,SAAS,CAAC,iBAAiB,GAAG,IAAI,CAAC,EACpD,MAAM,OAAO;;;CA+Cd,CAAC"}
1
+ {"version":3,"file":"useModalDialog.d.ts","sourceRoot":"","sources":["../../../src/hooks/useModalDialog/useModalDialog.ts"],"names":[],"mappings":"AAOA;;;;;;;;;;;GAWG;AACH,eAAO,MAAM,cAAc,GACzB,WAAW,KAAK,CAAC,SAAS,CAAC,iBAAiB,GAAG,IAAI,CAAC,EACpD,MAAM,OAAO;;;;CAiDd,CAAC"}
@@ -4,6 +4,7 @@ import { useState, useCallback } from "react";
4
4
  import { useIsomorphicLayoutEffect } from "../useIsomorphicLayoutEffect/useIsomorphicLayoutEffect.js";
5
5
  const useModalDialog = (dialogRef, open) => {
6
6
  const [visible, setVisible] = useState(open);
7
+ const [openAttribute] = useState(open);
7
8
  useIsomorphicLayoutEffect(() => {
8
9
  const dialog = dialogRef.current;
9
10
  if (open && visible) {
@@ -11,7 +12,7 @@ const useModalDialog = (dialogRef, open) => {
11
12
  return;
12
13
  }
13
14
  if (dialog.open) {
14
- dialog.close();
15
+ dialog.removeAttribute("open");
15
16
  }
16
17
  dialog.showModal();
17
18
  return () => {
@@ -32,7 +33,7 @@ const useModalDialog = (dialogRef, open) => {
32
33
  setVisible(false);
33
34
  }
34
35
  }, [open]);
35
- return { visible, onAnimationEnd };
36
+ return { visible, onAnimationEnd, openAttribute };
36
37
  };
37
38
  export {
38
39
  useModalDialog
@@ -1,2 +1,7 @@
1
+ /**
2
+ * Prevents the body from scrolling while enabled. Any number of callers can
3
+ * be enabled at once: the first locks the body, and the body's styles and
4
+ * scroll position are restored once the last one is disabled or unmounts.
5
+ */
1
6
  export declare const usePreventBodyScroll: (enabled?: boolean) => void;
2
7
  //# sourceMappingURL=usePreventBodyScroll.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"usePreventBodyScroll.d.ts","sourceRoot":"","sources":["../../../src/hooks/usePreventBodyScroll/usePreventBodyScroll.ts"],"names":[],"mappings":"AAIA,eAAO,MAAM,oBAAoB,GAAI,iBAAc,SAmClD,CAAC"}
1
+ {"version":3,"file":"usePreventBodyScroll.d.ts","sourceRoot":"","sources":["../../../src/hooks/usePreventBodyScroll/usePreventBodyScroll.ts"],"names":[],"mappings":"AAuCA;;;;GAIG;AACH,eAAO,MAAM,oBAAoB,GAAI,iBAAc,SAqBlD,CAAC"}
@@ -1,28 +1,43 @@
1
1
  "use client";
2
2
  import { useIsomorphicLayoutEffect } from "../useIsomorphicLayoutEffect/useIsomorphicLayoutEffect.js";
3
+ let lockCount = 0;
4
+ let unlock = null;
5
+ const lock = () => {
6
+ const prevOverflow = document.body.style.getPropertyValue("overflow");
7
+ const prevScrollTop = document.documentElement.scrollTop;
8
+ if (document.body.scrollHeight > window.innerHeight) {
9
+ document.body.style.position = "fixed";
10
+ document.body.style.overflow = "hidden";
11
+ document.body.style.overflowY = "scroll";
12
+ document.body.style.width = "100%";
13
+ document.body.style.top = `-${prevScrollTop}px`;
14
+ } else {
15
+ document.body.style.overflow = "hidden";
16
+ }
17
+ return () => {
18
+ document.body.style.position = "";
19
+ document.body.style.overflow = prevOverflow;
20
+ document.body.style.overflowY = "";
21
+ document.body.style.width = "";
22
+ document.body.style.top = "";
23
+ document.documentElement.scrollTop = prevScrollTop;
24
+ };
25
+ };
3
26
  const usePreventBodyScroll = (enabled = true) => {
4
27
  useIsomorphicLayoutEffect(() => {
5
28
  if (!enabled) {
6
29
  return;
7
30
  }
8
- const prevOverflow = document.body.style.getPropertyValue("overflow");
9
- const prevScrollTop = document.documentElement.scrollTop;
10
- if (document.body.scrollHeight > window.innerHeight) {
11
- document.body.style.position = "fixed";
12
- document.body.style.overflow = "hidden";
13
- document.body.style.overflowY = "scroll";
14
- document.body.style.width = "100%";
15
- document.body.style.top = `-${prevScrollTop}px`;
16
- } else {
17
- document.body.style.overflow = "hidden";
31
+ lockCount += 1;
32
+ if (lockCount === 1) {
33
+ unlock = lock();
18
34
  }
19
35
  return () => {
20
- document.body.style.position = "";
21
- document.body.style.overflow = prevOverflow;
22
- document.body.style.overflowY = "";
23
- document.body.style.width = "";
24
- document.body.style.top = "";
25
- document.documentElement.scrollTop = prevScrollTop;
36
+ lockCount -= 1;
37
+ if (lockCount === 0) {
38
+ unlock?.();
39
+ unlock = null;
40
+ }
26
41
  };
27
42
  }, [enabled]);
28
43
  };