@umamichi-ui/common-components 0.1.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 (98) hide show
  1. package/README.md +112 -0
  2. package/dist/dialog/AboutDialog.d.ts +27 -0
  3. package/dist/dialog/AboutDialog.d.ts.map +1 -0
  4. package/dist/dialog/AboutDialog.js +61 -0
  5. package/dist/dialog/AboutDialog.js.map +1 -0
  6. package/dist/dialog/ConfirmDialog.d.ts +17 -0
  7. package/dist/dialog/ConfirmDialog.d.ts.map +1 -0
  8. package/dist/dialog/ConfirmDialog.js +38 -0
  9. package/dist/dialog/ConfirmDialog.js.map +1 -0
  10. package/dist/dialog/ConfirmDialogOverlay.d.ts +13 -0
  11. package/dist/dialog/ConfirmDialogOverlay.d.ts.map +1 -0
  12. package/dist/dialog/ConfirmDialogOverlay.js +28 -0
  13. package/dist/dialog/ConfirmDialogOverlay.js.map +1 -0
  14. package/dist/dialog/index.d.ts +4 -0
  15. package/dist/dialog/index.d.ts.map +1 -0
  16. package/dist/dialog/index.js +9 -0
  17. package/dist/dialog/index.js.map +1 -0
  18. package/dist/icons/ChevronIcons.d.ts +6 -0
  19. package/dist/icons/ChevronIcons.d.ts.map +1 -0
  20. package/dist/icons/ChevronIcons.js +19 -0
  21. package/dist/icons/ChevronIcons.js.map +1 -0
  22. package/dist/icons/DropdownMenuChevron.d.ts +6 -0
  23. package/dist/icons/DropdownMenuChevron.d.ts.map +1 -0
  24. package/dist/icons/DropdownMenuChevron.js +20 -0
  25. package/dist/icons/DropdownMenuChevron.js.map +1 -0
  26. package/dist/icons/InfoCircleIcon.d.ts +5 -0
  27. package/dist/icons/InfoCircleIcon.d.ts.map +1 -0
  28. package/dist/icons/InfoCircleIcon.js +17 -0
  29. package/dist/icons/InfoCircleIcon.js.map +1 -0
  30. package/dist/icons/index.d.ts +4 -0
  31. package/dist/icons/index.d.ts.map +1 -0
  32. package/dist/icons/index.js +10 -0
  33. package/dist/icons/index.js.map +1 -0
  34. package/dist/index.d.ts +6 -0
  35. package/dist/index.d.ts.map +1 -0
  36. package/dist/index.js +45 -0
  37. package/dist/index.js.map +1 -0
  38. package/dist/menu/FloatingMenu.d.ts +28 -0
  39. package/dist/menu/FloatingMenu.d.ts.map +1 -0
  40. package/dist/menu/FloatingMenu.js +125 -0
  41. package/dist/menu/FloatingMenu.js.map +1 -0
  42. package/dist/menu/MobileActionSheet.d.ts +9 -0
  43. package/dist/menu/MobileActionSheet.d.ts.map +1 -0
  44. package/dist/menu/MobileActionSheet.js +10 -0
  45. package/dist/menu/MobileActionSheet.js.map +1 -0
  46. package/dist/menu/MobileActionSheetContent.d.ts +31 -0
  47. package/dist/menu/MobileActionSheetContent.d.ts.map +1 -0
  48. package/dist/menu/MobileActionSheetContent.js +123 -0
  49. package/dist/menu/MobileActionSheetContent.js.map +1 -0
  50. package/dist/menu/computeFloatingMenuGeometry.d.ts +12 -0
  51. package/dist/menu/computeFloatingMenuGeometry.d.ts.map +1 -0
  52. package/dist/menu/computeFloatingMenuGeometry.js +30 -0
  53. package/dist/menu/computeFloatingMenuGeometry.js.map +1 -0
  54. package/dist/menu/index.d.ts +6 -0
  55. package/dist/menu/index.d.ts.map +1 -0
  56. package/dist/menu/index.js +13 -0
  57. package/dist/menu/index.js.map +1 -0
  58. package/dist/menu/useFloatingMenuGeometry.d.ts +13 -0
  59. package/dist/menu/useFloatingMenuGeometry.d.ts.map +1 -0
  60. package/dist/menu/useFloatingMenuGeometry.js +50 -0
  61. package/dist/menu/useFloatingMenuGeometry.js.map +1 -0
  62. package/dist/overlay/OverlayStackProvider.d.ts +17 -0
  63. package/dist/overlay/OverlayStackProvider.d.ts.map +1 -0
  64. package/dist/overlay/OverlayStackProvider.js +120 -0
  65. package/dist/overlay/OverlayStackProvider.js.map +1 -0
  66. package/dist/overlay/SiteOverlayBackdrop.d.ts +13 -0
  67. package/dist/overlay/SiteOverlayBackdrop.d.ts.map +1 -0
  68. package/dist/overlay/SiteOverlayBackdrop.js +62 -0
  69. package/dist/overlay/SiteOverlayBackdrop.js.map +1 -0
  70. package/dist/overlay/index.d.ts +6 -0
  71. package/dist/overlay/index.d.ts.map +1 -0
  72. package/dist/overlay/index.js +18 -0
  73. package/dist/overlay/index.js.map +1 -0
  74. package/dist/overlay/overlayHistory.d.ts +8 -0
  75. package/dist/overlay/overlayHistory.d.ts.map +1 -0
  76. package/dist/overlay/overlayHistory.js +14 -0
  77. package/dist/overlay/overlayHistory.js.map +1 -0
  78. package/dist/overlay/overlayStackTypes.d.ts +10 -0
  79. package/dist/overlay/overlayStackTypes.d.ts.map +1 -0
  80. package/dist/overlay/overlayStackTypes.js +9 -0
  81. package/dist/overlay/overlayStackTypes.js.map +1 -0
  82. package/dist/overlay/useOverlayStackEntry.d.ts +11 -0
  83. package/dist/overlay/useOverlayStackEntry.d.ts.map +1 -0
  84. package/dist/overlay/useOverlayStackEntry.js +55 -0
  85. package/dist/overlay/useOverlayStackEntry.js.map +1 -0
  86. package/dist/presence/index.d.ts +2 -0
  87. package/dist/presence/index.d.ts.map +1 -0
  88. package/dist/presence/index.js +7 -0
  89. package/dist/presence/index.js.map +1 -0
  90. package/dist/presence/useOverlayPresence.d.ts +10 -0
  91. package/dist/presence/useOverlayPresence.d.ts.map +1 -0
  92. package/dist/presence/useOverlayPresence.js +119 -0
  93. package/dist/presence/useOverlayPresence.js.map +1 -0
  94. package/package.json +81 -0
  95. package/src/styles/about-dialog.css +117 -0
  96. package/src/styles/index.css +3 -0
  97. package/src/styles/mobile-action-sheet.css +166 -0
  98. package/src/styles/overlay.css +61 -0
@@ -0,0 +1,120 @@
1
+ import { jsx } from "react/jsx-runtime";
2
+ import { useRef, useState, useCallback, useMemo, useEffect, useContext, createContext } from "react";
3
+ import { pushOverlayHistoryState } from "./overlayHistory.js";
4
+ import { computeOverlayZIndex } from "./overlayStackTypes.js";
5
+ const OverlayStackContext = createContext(null);
6
+ const isEditableTarget = (target) => {
7
+ if (!(target instanceof HTMLElement)) {
8
+ return false;
9
+ }
10
+ return target instanceof HTMLInputElement || target instanceof HTMLTextAreaElement || target instanceof HTMLSelectElement || target.isContentEditable;
11
+ };
12
+ function OverlayStackProvider({ children }) {
13
+ const entriesRef = useRef(/* @__PURE__ */ new Map());
14
+ const orderRef = useRef([]);
15
+ const historyActiveIdsRef = useRef(/* @__PURE__ */ new Set());
16
+ const ignoreNextPopstateRef = useRef(false);
17
+ const [order, setOrder] = useState([]);
18
+ orderRef.current = order;
19
+ const register = useCallback((entry) => {
20
+ entriesRef.current.set(entry.id, entry);
21
+ setOrder((prev) => {
22
+ const without = prev.filter((entryId) => entryId !== entry.id);
23
+ return [...without, entry.id];
24
+ });
25
+ }, []);
26
+ const unregister = useCallback((id) => {
27
+ entriesRef.current.delete(id);
28
+ setOrder((prev) => prev.filter((entryId) => entryId !== id));
29
+ }, []);
30
+ const pushOverlayHistory = useCallback((id) => {
31
+ if (historyActiveIdsRef.current.has(id)) {
32
+ return;
33
+ }
34
+ historyActiveIdsRef.current.add(id);
35
+ pushOverlayHistoryState(id);
36
+ }, []);
37
+ const syncOverlayHistoryOnUiClose = useCallback((id) => {
38
+ if (!historyActiveIdsRef.current.has(id)) {
39
+ return;
40
+ }
41
+ historyActiveIdsRef.current.delete(id);
42
+ ignoreNextPopstateRef.current = true;
43
+ history.back();
44
+ }, []);
45
+ const acknowledgeOverlayHistoryPop = useCallback((id) => {
46
+ historyActiveIdsRef.current.delete(id);
47
+ }, []);
48
+ const topId = order[order.length - 1] ?? null;
49
+ const value = useMemo(
50
+ () => ({
51
+ register,
52
+ unregister,
53
+ isTop: (id) => topId === id,
54
+ getZIndex: (id) => {
55
+ const index = order.indexOf(id);
56
+ return computeOverlayZIndex(index < 0 ? 0 : index);
57
+ },
58
+ pushOverlayHistory,
59
+ syncOverlayHistoryOnUiClose,
60
+ acknowledgeOverlayHistoryPop
61
+ }),
62
+ [
63
+ acknowledgeOverlayHistoryPop,
64
+ order,
65
+ pushOverlayHistory,
66
+ register,
67
+ syncOverlayHistoryOnUiClose,
68
+ topId,
69
+ unregister
70
+ ]
71
+ );
72
+ useEffect(() => {
73
+ const onPopState = () => {
74
+ if (ignoreNextPopstateRef.current) {
75
+ ignoreNextPopstateRef.current = false;
76
+ return;
77
+ }
78
+ const top = orderRef.current[orderRef.current.length - 1];
79
+ if (!top) {
80
+ return;
81
+ }
82
+ const entry = entriesRef.current.get(top);
83
+ if (!entry) {
84
+ return;
85
+ }
86
+ acknowledgeOverlayHistoryPop(top);
87
+ entry.onDismiss();
88
+ };
89
+ window.addEventListener("popstate", onPopState);
90
+ return () => window.removeEventListener("popstate", onPopState);
91
+ }, [acknowledgeOverlayHistoryPop]);
92
+ useEffect(() => {
93
+ const onKeyDown = (event) => {
94
+ if (event.key !== "Escape" || isEditableTarget(event.target)) {
95
+ return;
96
+ }
97
+ const top = topId ? entriesRef.current.get(topId) : void 0;
98
+ if (!top || top.dismissOnEscape === false) {
99
+ return;
100
+ }
101
+ event.preventDefault();
102
+ top.onDismiss();
103
+ };
104
+ window.addEventListener("keydown", onKeyDown);
105
+ return () => window.removeEventListener("keydown", onKeyDown);
106
+ }, [topId]);
107
+ return /* @__PURE__ */ jsx(OverlayStackContext.Provider, { value, children });
108
+ }
109
+ function useOverlayStack() {
110
+ const context = useContext(OverlayStackContext);
111
+ if (!context) {
112
+ throw new Error("useOverlayStack must be used within OverlayStackProvider");
113
+ }
114
+ return context;
115
+ }
116
+ export {
117
+ OverlayStackProvider,
118
+ useOverlayStack
119
+ };
120
+ //# sourceMappingURL=OverlayStackProvider.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"OverlayStackProvider.js","sources":["../../src/overlay/OverlayStackProvider.tsx"],"sourcesContent":["import {\n createContext,\n useCallback,\n useContext,\n useEffect,\n useMemo,\n useRef,\n useState,\n type ReactNode,\n} from 'react';\nimport { pushOverlayHistoryState } from './overlayHistory';\nimport { computeOverlayZIndex, type OverlayStackEntry } from './overlayStackTypes';\n\ntype OverlayStackContextValue = {\n register: (entry: OverlayStackEntry) => void;\n unregister: (id: string) => void;\n isTop: (id: string) => boolean;\n getZIndex: (id: string) => number;\n pushOverlayHistory: (id: string) => void;\n syncOverlayHistoryOnUiClose: (id: string) => void;\n acknowledgeOverlayHistoryPop: (id: string) => void;\n};\n\nconst OverlayStackContext = createContext<OverlayStackContextValue | null>(null);\n\nconst isEditableTarget = (target: EventTarget | null) => {\n if (!(target instanceof HTMLElement)) {\n return false;\n }\n\n return (\n target instanceof HTMLInputElement ||\n target instanceof HTMLTextAreaElement ||\n target instanceof HTMLSelectElement ||\n target.isContentEditable\n );\n};\n\nexport function OverlayStackProvider({ children }: { children: ReactNode }) {\n const entriesRef = useRef(new Map<string, OverlayStackEntry>());\n const orderRef = useRef<string[]>([]);\n const historyActiveIdsRef = useRef(new Set<string>());\n const ignoreNextPopstateRef = useRef(false);\n const [order, setOrder] = useState<string[]>([]);\n\n orderRef.current = order;\n\n const register = useCallback((entry: OverlayStackEntry) => {\n entriesRef.current.set(entry.id, entry);\n setOrder((prev) => {\n const without = prev.filter((entryId) => entryId !== entry.id);\n return [...without, entry.id];\n });\n }, []);\n\n const unregister = useCallback((id: string) => {\n entriesRef.current.delete(id);\n setOrder((prev) => prev.filter((entryId) => entryId !== id));\n }, []);\n\n const pushOverlayHistory = useCallback((id: string) => {\n if (historyActiveIdsRef.current.has(id)) {\n return;\n }\n\n historyActiveIdsRef.current.add(id);\n pushOverlayHistoryState(id);\n }, []);\n\n const syncOverlayHistoryOnUiClose = useCallback((id: string) => {\n if (!historyActiveIdsRef.current.has(id)) {\n return;\n }\n\n historyActiveIdsRef.current.delete(id);\n ignoreNextPopstateRef.current = true;\n history.back();\n }, []);\n\n const acknowledgeOverlayHistoryPop = useCallback((id: string) => {\n historyActiveIdsRef.current.delete(id);\n }, []);\n\n const topId = order[order.length - 1] ?? null;\n\n const value = useMemo<OverlayStackContextValue>(\n () => ({\n register,\n unregister,\n isTop: (id) => topId === id,\n getZIndex: (id) => {\n const index = order.indexOf(id);\n return computeOverlayZIndex(index < 0 ? 0 : index);\n },\n pushOverlayHistory,\n syncOverlayHistoryOnUiClose,\n acknowledgeOverlayHistoryPop,\n }),\n [\n acknowledgeOverlayHistoryPop,\n order,\n pushOverlayHistory,\n register,\n syncOverlayHistoryOnUiClose,\n topId,\n unregister,\n ],\n );\n\n useEffect(() => {\n const onPopState = () => {\n if (ignoreNextPopstateRef.current) {\n ignoreNextPopstateRef.current = false;\n return;\n }\n\n const top = orderRef.current[orderRef.current.length - 1];\n\n if (!top) {\n return;\n }\n\n const entry = entriesRef.current.get(top);\n\n if (!entry) {\n return;\n }\n\n acknowledgeOverlayHistoryPop(top);\n entry.onDismiss();\n };\n\n window.addEventListener('popstate', onPopState);\n return () => window.removeEventListener('popstate', onPopState);\n }, [acknowledgeOverlayHistoryPop]);\n\n useEffect(() => {\n const onKeyDown = (event: KeyboardEvent) => {\n if (event.key !== 'Escape' || isEditableTarget(event.target)) {\n return;\n }\n\n const top = topId ? entriesRef.current.get(topId) : undefined;\n\n if (!top || top.dismissOnEscape === false) {\n return;\n }\n\n event.preventDefault();\n top.onDismiss();\n };\n\n window.addEventListener('keydown', onKeyDown);\n return () => window.removeEventListener('keydown', onKeyDown);\n }, [topId]);\n\n return <OverlayStackContext.Provider value={value}>{children}</OverlayStackContext.Provider>;\n}\n\nexport function useOverlayStack() {\n const context = useContext(OverlayStackContext);\n\n if (!context) {\n throw new Error('useOverlayStack must be used within OverlayStackProvider');\n }\n\n return context;\n}\n"],"names":[],"mappings":"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"}
@@ -0,0 +1,13 @@
1
+ import type { ReactNode } from 'react';
2
+ export type SiteOverlayAlign = 'centered' | 'top' | 'bottom';
3
+ export type SiteOverlayBackdropProps = {
4
+ open: boolean;
5
+ overlayId: string;
6
+ align?: SiteOverlayAlign;
7
+ onDismiss: () => void;
8
+ onExited?: () => void;
9
+ dismissOnEscape?: boolean;
10
+ children: ReactNode;
11
+ };
12
+ export declare function SiteOverlayBackdrop({ open, overlayId, align, onDismiss, onExited, dismissOnEscape, children, }: SiteOverlayBackdropProps): import("react").ReactPortal | null;
13
+ //# sourceMappingURL=SiteOverlayBackdrop.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"SiteOverlayBackdrop.d.ts","sourceRoot":"","sources":["../../src/overlay/SiteOverlayBackdrop.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAqB,SAAS,EAAE,MAAM,OAAO,CAAC;AAM1D,MAAM,MAAM,gBAAgB,GAAG,UAAU,GAAG,KAAK,GAAG,QAAQ,CAAC;AAQ7D,MAAM,MAAM,wBAAwB,GAAG;IACrC,IAAI,EAAE,OAAO,CAAC;IACd,SAAS,EAAE,MAAM,CAAC;IAClB,KAAK,CAAC,EAAE,gBAAgB,CAAC;IACzB,SAAS,EAAE,MAAM,IAAI,CAAC;IACtB,QAAQ,CAAC,EAAE,MAAM,IAAI,CAAC;IACtB,eAAe,CAAC,EAAE,OAAO,CAAC;IAC1B,QAAQ,EAAE,SAAS,CAAC;CACrB,CAAC;AAEF,wBAAgB,mBAAmB,CAAC,EAClC,IAAI,EACJ,SAAS,EACT,KAAkB,EAClB,SAAS,EACT,QAAQ,EACR,eAAsB,EACtB,QAAQ,GACT,EAAE,wBAAwB,sCA0C1B"}
@@ -0,0 +1,62 @@
1
+ import { jsx } from "react/jsx-runtime";
2
+ import { useEffect } from "react";
3
+ import { createPortal } from "react-dom";
4
+ import { useOverlayPresence, withOverlayOpen } from "../presence/useOverlayPresence.js";
5
+ import { useOverlayStackEntry } from "./useOverlayStackEntry.js";
6
+ const alignClassName = {
7
+ centered: "site-overlay-backdrop site-overlay-backdrop--centered",
8
+ top: "site-overlay-backdrop site-overlay-backdrop--top",
9
+ bottom: "site-overlay-backdrop site-overlay-backdrop--bottom"
10
+ };
11
+ function SiteOverlayBackdrop({
12
+ open,
13
+ overlayId,
14
+ align = "centered",
15
+ onDismiss,
16
+ onExited,
17
+ dismissOnEscape = true,
18
+ children
19
+ }) {
20
+ const { mounted, isOpen, overlayRef } = useOverlayPresence(open);
21
+ const { isBackdropActive, zIndex } = useOverlayStackEntry({
22
+ id: overlayId,
23
+ open,
24
+ onDismiss,
25
+ dismissOnEscape
26
+ });
27
+ useEffect(() => {
28
+ if (!mounted) {
29
+ onExited?.();
30
+ }
31
+ }, [mounted, onExited]);
32
+ if (!mounted) {
33
+ return null;
34
+ }
35
+ const onBackdropClick = (event) => {
36
+ if (!isBackdropActive || event.target !== event.currentTarget) {
37
+ return;
38
+ }
39
+ onDismiss();
40
+ };
41
+ return createPortal(
42
+ /* @__PURE__ */ jsx(
43
+ "div",
44
+ {
45
+ ref: overlayRef,
46
+ className: withOverlayOpen(
47
+ `${alignClassName[align]}${isBackdropActive ? "" : " site-overlay-backdrop-inactive"}`,
48
+ isOpen
49
+ ),
50
+ style: { zIndex },
51
+ role: "presentation",
52
+ onClick: onBackdropClick,
53
+ children
54
+ }
55
+ ),
56
+ document.body
57
+ );
58
+ }
59
+ export {
60
+ SiteOverlayBackdrop
61
+ };
62
+ //# sourceMappingURL=SiteOverlayBackdrop.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"SiteOverlayBackdrop.js","sources":["../../src/overlay/SiteOverlayBackdrop.tsx"],"sourcesContent":["import type { MouseEventHandler, ReactNode } from 'react';\nimport { useEffect } from 'react';\nimport { createPortal } from 'react-dom';\nimport { useOverlayPresence, withOverlayOpen } from '../presence/useOverlayPresence';\nimport { useOverlayStackEntry } from './useOverlayStackEntry';\n\nexport type SiteOverlayAlign = 'centered' | 'top' | 'bottom';\n\nconst alignClassName: Record<SiteOverlayAlign, string> = {\n centered: 'site-overlay-backdrop site-overlay-backdrop--centered',\n top: 'site-overlay-backdrop site-overlay-backdrop--top',\n bottom: 'site-overlay-backdrop site-overlay-backdrop--bottom',\n};\n\nexport type SiteOverlayBackdropProps = {\n open: boolean;\n overlayId: string;\n align?: SiteOverlayAlign;\n onDismiss: () => void;\n onExited?: () => void;\n dismissOnEscape?: boolean;\n children: ReactNode;\n};\n\nexport function SiteOverlayBackdrop({\n open,\n overlayId,\n align = 'centered',\n onDismiss,\n onExited,\n dismissOnEscape = true,\n children,\n}: SiteOverlayBackdropProps) {\n const { mounted, isOpen, overlayRef } = useOverlayPresence<HTMLDivElement>(open);\n const { isBackdropActive, zIndex } = useOverlayStackEntry({\n id: overlayId,\n open,\n onDismiss,\n dismissOnEscape,\n });\n\n useEffect(() => {\n if (!mounted) {\n onExited?.();\n }\n }, [mounted, onExited]);\n\n if (!mounted) {\n return null;\n }\n\n const onBackdropClick: MouseEventHandler<HTMLDivElement> = (event) => {\n if (!isBackdropActive || event.target !== event.currentTarget) {\n return;\n }\n\n onDismiss();\n };\n\n return createPortal(\n <div\n ref={overlayRef}\n className={withOverlayOpen(\n `${alignClassName[align]}${isBackdropActive ? '' : ' site-overlay-backdrop-inactive'}`,\n isOpen,\n )}\n style={{ zIndex }}\n role=\"presentation\"\n onClick={onBackdropClick}\n >\n {children}\n </div>,\n document.body,\n );\n}\n"],"names":[],"mappings":";;;;;AAQA,MAAM,iBAAmD;AAAA,EACvD,UAAU;AAAA,EACV,KAAK;AAAA,EACL,QAAQ;AACV;AAYO,SAAS,oBAAoB;AAAA,EAClC;AAAA,EACA;AAAA,EACA,QAAQ;AAAA,EACR;AAAA,EACA;AAAA,EACA,kBAAkB;AAAA,EAClB;AACF,GAA6B;AAC3B,QAAM,EAAE,SAAS,QAAQ,WAAA,IAAe,mBAAmC,IAAI;AAC/E,QAAM,EAAE,kBAAkB,OAAA,IAAW,qBAAqB;AAAA,IACxD,IAAI;AAAA,IACJ;AAAA,IACA;AAAA,IACA;AAAA,EAAA,CACD;AAED,YAAU,MAAM;AACd,QAAI,CAAC,SAAS;AACZ,iBAAA;AAAA,IACF;AAAA,EACF,GAAG,CAAC,SAAS,QAAQ,CAAC;AAEtB,MAAI,CAAC,SAAS;AACZ,WAAO;AAAA,EACT;AAEA,QAAM,kBAAqD,CAAC,UAAU;AACpE,QAAI,CAAC,oBAAoB,MAAM,WAAW,MAAM,eAAe;AAC7D;AAAA,IACF;AAEA,cAAA;AAAA,EACF;AAEA,SAAO;AAAA,IACL;AAAA,MAAC;AAAA,MAAA;AAAA,QACC,KAAK;AAAA,QACL,WAAW;AAAA,UACT,GAAG,eAAe,KAAK,CAAC,GAAG,mBAAmB,KAAK,iCAAiC;AAAA,UACpF;AAAA,QAAA;AAAA,QAEF,OAAO,EAAE,OAAA;AAAA,QACT,MAAK;AAAA,QACL,SAAS;AAAA,QAER;AAAA,MAAA;AAAA,IAAA;AAAA,IAEH,SAAS;AAAA,EAAA;AAEb;"}
@@ -0,0 +1,6 @@
1
+ export { OverlayStackProvider, useOverlayStack } from './OverlayStackProvider';
2
+ export { isOverlayHistoryState, OVERLAY_HISTORY_STATE_KEY, pushOverlayHistoryState, type OverlayHistoryState, } from './overlayHistory';
3
+ export { computeOverlayZIndex, OVERLAY_Z_BASE, OVERLAY_Z_STEP, type OverlayStackEntry, } from './overlayStackTypes';
4
+ export { SiteOverlayBackdrop, type SiteOverlayAlign, type SiteOverlayBackdropProps } from './SiteOverlayBackdrop';
5
+ export { useOverlayStackEntry, type UseOverlayStackEntryOptions } from './useOverlayStackEntry';
6
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/overlay/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,oBAAoB,EAAE,eAAe,EAAE,MAAM,wBAAwB,CAAC;AAC/E,OAAO,EACL,qBAAqB,EACrB,yBAAyB,EACzB,uBAAuB,EACvB,KAAK,mBAAmB,GACzB,MAAM,kBAAkB,CAAC;AAC1B,OAAO,EACL,oBAAoB,EACpB,cAAc,EACd,cAAc,EACd,KAAK,iBAAiB,GACvB,MAAM,qBAAqB,CAAC;AAC7B,OAAO,EAAE,mBAAmB,EAAE,KAAK,gBAAgB,EAAE,KAAK,wBAAwB,EAAE,MAAM,uBAAuB,CAAC;AAClH,OAAO,EAAE,oBAAoB,EAAE,KAAK,2BAA2B,EAAE,MAAM,wBAAwB,CAAC"}
@@ -0,0 +1,18 @@
1
+ import { OverlayStackProvider, useOverlayStack } from "./OverlayStackProvider.js";
2
+ import { OVERLAY_HISTORY_STATE_KEY, isOverlayHistoryState, pushOverlayHistoryState } from "./overlayHistory.js";
3
+ import { OVERLAY_Z_BASE, OVERLAY_Z_STEP, computeOverlayZIndex } from "./overlayStackTypes.js";
4
+ import { SiteOverlayBackdrop } from "./SiteOverlayBackdrop.js";
5
+ import { useOverlayStackEntry } from "./useOverlayStackEntry.js";
6
+ export {
7
+ OVERLAY_HISTORY_STATE_KEY,
8
+ OVERLAY_Z_BASE,
9
+ OVERLAY_Z_STEP,
10
+ OverlayStackProvider,
11
+ SiteOverlayBackdrop,
12
+ computeOverlayZIndex,
13
+ isOverlayHistoryState,
14
+ pushOverlayHistoryState,
15
+ useOverlayStack,
16
+ useOverlayStackEntry
17
+ };
18
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;"}
@@ -0,0 +1,8 @@
1
+ /** `history.state` 上标记叠层条目的键,用于区分站内叠层与正常导航。 */
2
+ export declare const OVERLAY_HISTORY_STATE_KEY: "overlayStack";
3
+ export type OverlayHistoryState = {
4
+ [OVERLAY_HISTORY_STATE_KEY]: string;
5
+ };
6
+ export declare function isOverlayHistoryState(state: unknown): state is OverlayHistoryState;
7
+ export declare function pushOverlayHistoryState(overlayId: string): void;
8
+ //# sourceMappingURL=overlayHistory.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"overlayHistory.d.ts","sourceRoot":"","sources":["../../src/overlay/overlayHistory.ts"],"names":[],"mappings":"AAAA,+CAA+C;AAC/C,eAAO,MAAM,yBAAyB,EAAG,cAAuB,CAAC;AAEjE,MAAM,MAAM,mBAAmB,GAAG;IAChC,CAAC,yBAAyB,CAAC,EAAE,MAAM,CAAC;CACrC,CAAC;AAEF,wBAAgB,qBAAqB,CAAC,KAAK,EAAE,OAAO,GAAG,KAAK,IAAI,mBAAmB,CAOlF;AAED,wBAAgB,uBAAuB,CAAC,SAAS,EAAE,MAAM,QAGxD"}
@@ -0,0 +1,14 @@
1
+ const OVERLAY_HISTORY_STATE_KEY = "overlayStack";
2
+ function isOverlayHistoryState(state) {
3
+ return typeof state === "object" && state !== null && OVERLAY_HISTORY_STATE_KEY in state && typeof state[OVERLAY_HISTORY_STATE_KEY] === "string";
4
+ }
5
+ function pushOverlayHistoryState(overlayId) {
6
+ const next = { [OVERLAY_HISTORY_STATE_KEY]: overlayId };
7
+ history.pushState(next, "", window.location.href);
8
+ }
9
+ export {
10
+ OVERLAY_HISTORY_STATE_KEY,
11
+ isOverlayHistoryState,
12
+ pushOverlayHistoryState
13
+ };
14
+ //# sourceMappingURL=overlayHistory.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"overlayHistory.js","sources":["../../src/overlay/overlayHistory.ts"],"sourcesContent":["/** `history.state` 上标记叠层条目的键,用于区分站内叠层与正常导航。 */\nexport const OVERLAY_HISTORY_STATE_KEY = 'overlayStack' as const;\n\nexport type OverlayHistoryState = {\n [OVERLAY_HISTORY_STATE_KEY]: string;\n};\n\nexport function isOverlayHistoryState(state: unknown): state is OverlayHistoryState {\n return (\n typeof state === 'object' &&\n state !== null &&\n OVERLAY_HISTORY_STATE_KEY in state &&\n typeof (state as OverlayHistoryState)[OVERLAY_HISTORY_STATE_KEY] === 'string'\n );\n}\n\nexport function pushOverlayHistoryState(overlayId: string) {\n const next: OverlayHistoryState = { [OVERLAY_HISTORY_STATE_KEY]: overlayId };\n history.pushState(next, '', window.location.href);\n}\n"],"names":[],"mappings":"AACO,MAAM,4BAA4B;AAMlC,SAAS,sBAAsB,OAA8C;AAClF,SACE,OAAO,UAAU,YACjB,UAAU,QACV,6BAA6B,SAC7B,OAAQ,MAA8B,yBAAyB,MAAM;AAEzE;AAEO,SAAS,wBAAwB,WAAmB;AACzD,QAAM,OAA4B,EAAE,CAAC,yBAAyB,GAAG,UAAA;AACjE,UAAQ,UAAU,MAAM,IAAI,OAAO,SAAS,IAAI;AAClD;"}
@@ -0,0 +1,10 @@
1
+ /** 叠层 z-index:1010 + 栈内序号 × 10(序号从 0 起) */
2
+ export declare const OVERLAY_Z_BASE = 1010;
3
+ export declare const OVERLAY_Z_STEP = 10;
4
+ export type OverlayStackEntry = {
5
+ id: string;
6
+ onDismiss: () => void;
7
+ dismissOnEscape: boolean;
8
+ };
9
+ export declare const computeOverlayZIndex: (stackIndex: number) => number;
10
+ //# sourceMappingURL=overlayStackTypes.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"overlayStackTypes.d.ts","sourceRoot":"","sources":["../../src/overlay/overlayStackTypes.ts"],"names":[],"mappings":"AAAA,2CAA2C;AAC3C,eAAO,MAAM,cAAc,OAAO,CAAC;AACnC,eAAO,MAAM,cAAc,KAAK,CAAC;AAEjC,MAAM,MAAM,iBAAiB,GAAG;IAC9B,EAAE,EAAE,MAAM,CAAC;IACX,SAAS,EAAE,MAAM,IAAI,CAAC;IACtB,eAAe,EAAE,OAAO,CAAC;CAC1B,CAAC;AAEF,eAAO,MAAM,oBAAoB,GAAI,YAAY,MAAM,WAAiD,CAAC"}
@@ -0,0 +1,9 @@
1
+ const OVERLAY_Z_BASE = 1010;
2
+ const OVERLAY_Z_STEP = 10;
3
+ const computeOverlayZIndex = (stackIndex) => OVERLAY_Z_BASE + stackIndex * OVERLAY_Z_STEP;
4
+ export {
5
+ OVERLAY_Z_BASE,
6
+ OVERLAY_Z_STEP,
7
+ computeOverlayZIndex
8
+ };
9
+ //# sourceMappingURL=overlayStackTypes.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"overlayStackTypes.js","sources":["../../src/overlay/overlayStackTypes.ts"],"sourcesContent":["/** 叠层 z-index:1010 + 栈内序号 × 10(序号从 0 起) */\nexport const OVERLAY_Z_BASE = 1010;\nexport const OVERLAY_Z_STEP = 10;\n\nexport type OverlayStackEntry = {\n id: string;\n onDismiss: () => void;\n dismissOnEscape: boolean;\n};\n\nexport const computeOverlayZIndex = (stackIndex: number) => OVERLAY_Z_BASE + stackIndex * OVERLAY_Z_STEP;\n"],"names":[],"mappings":"AACO,MAAM,iBAAiB;AACvB,MAAM,iBAAiB;AAQvB,MAAM,uBAAuB,CAAC,eAAuB,iBAAiB,aAAa;"}
@@ -0,0 +1,11 @@
1
+ export type UseOverlayStackEntryOptions = {
2
+ id: string;
3
+ open: boolean;
4
+ onDismiss: () => void;
5
+ dismissOnEscape?: boolean;
6
+ };
7
+ export declare function useOverlayStackEntry({ id, open, onDismiss, dismissOnEscape, }: UseOverlayStackEntryOptions): {
8
+ isBackdropActive: boolean;
9
+ zIndex: number;
10
+ };
11
+ //# sourceMappingURL=useOverlayStackEntry.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"useOverlayStackEntry.d.ts","sourceRoot":"","sources":["../../src/overlay/useOverlayStackEntry.ts"],"names":[],"mappings":"AAGA,MAAM,MAAM,2BAA2B,GAAG;IACxC,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,OAAO,CAAC;IACd,SAAS,EAAE,MAAM,IAAI,CAAC;IACtB,eAAe,CAAC,EAAE,OAAO,CAAC;CAC3B,CAAC;AAEF,wBAAgB,oBAAoB,CAAC,EACnC,EAAE,EACF,IAAI,EACJ,SAAS,EACT,eAAsB,GACvB,EAAE,2BAA2B;;;EAkD7B"}
@@ -0,0 +1,55 @@
1
+ import { useRef, useEffect } from "react";
2
+ import { useOverlayStack } from "./OverlayStackProvider.js";
3
+ function useOverlayStackEntry({
4
+ id,
5
+ open,
6
+ onDismiss,
7
+ dismissOnEscape = true
8
+ }) {
9
+ const {
10
+ register,
11
+ unregister,
12
+ isTop,
13
+ getZIndex,
14
+ pushOverlayHistory,
15
+ syncOverlayHistoryOnUiClose
16
+ } = useOverlayStack();
17
+ const onDismissRef = useRef(onDismiss);
18
+ const openRef = useRef(open);
19
+ onDismissRef.current = onDismiss;
20
+ openRef.current = open;
21
+ useEffect(() => {
22
+ if (!open) {
23
+ return;
24
+ }
25
+ register({
26
+ id,
27
+ onDismiss: () => onDismissRef.current(),
28
+ dismissOnEscape
29
+ });
30
+ return () => {
31
+ unregister(id);
32
+ };
33
+ }, [dismissOnEscape, id, open, register, unregister]);
34
+ useEffect(() => {
35
+ if (!open) {
36
+ return;
37
+ }
38
+ pushOverlayHistory(id);
39
+ return () => {
40
+ queueMicrotask(() => {
41
+ if (!openRef.current) {
42
+ syncOverlayHistoryOnUiClose(id);
43
+ }
44
+ });
45
+ };
46
+ }, [id, open, pushOverlayHistory, syncOverlayHistoryOnUiClose]);
47
+ return {
48
+ isBackdropActive: open && isTop(id),
49
+ zIndex: getZIndex(id)
50
+ };
51
+ }
52
+ export {
53
+ useOverlayStackEntry
54
+ };
55
+ //# sourceMappingURL=useOverlayStackEntry.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"useOverlayStackEntry.js","sources":["../../src/overlay/useOverlayStackEntry.ts"],"sourcesContent":["import { useEffect, useRef } from 'react';\nimport { useOverlayStack } from './OverlayStackProvider';\n\nexport type UseOverlayStackEntryOptions = {\n id: string;\n open: boolean;\n onDismiss: () => void;\n dismissOnEscape?: boolean;\n};\n\nexport function useOverlayStackEntry({\n id,\n open,\n onDismiss,\n dismissOnEscape = true,\n}: UseOverlayStackEntryOptions) {\n const {\n register,\n unregister,\n isTop,\n getZIndex,\n pushOverlayHistory,\n syncOverlayHistoryOnUiClose,\n } = useOverlayStack();\n const onDismissRef = useRef(onDismiss);\n const openRef = useRef(open);\n onDismissRef.current = onDismiss;\n openRef.current = open;\n\n useEffect(() => {\n if (!open) {\n return;\n }\n\n register({\n id,\n onDismiss: () => onDismissRef.current(),\n dismissOnEscape,\n });\n\n return () => {\n unregister(id);\n };\n }, [dismissOnEscape, id, open, register, unregister]);\n\n useEffect(() => {\n if (!open) {\n return;\n }\n\n pushOverlayHistory(id);\n\n return () => {\n queueMicrotask(() => {\n if (!openRef.current) {\n syncOverlayHistoryOnUiClose(id);\n }\n });\n };\n }, [id, open, pushOverlayHistory, syncOverlayHistoryOnUiClose]);\n\n return {\n isBackdropActive: open && isTop(id),\n zIndex: getZIndex(id),\n };\n}\n"],"names":[],"mappings":";;AAUO,SAAS,qBAAqB;AAAA,EACnC;AAAA,EACA;AAAA,EACA;AAAA,EACA,kBAAkB;AACpB,GAAgC;AAC9B,QAAM;AAAA,IACJ;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,EAAA,IACE,gBAAA;AACJ,QAAM,eAAe,OAAO,SAAS;AACrC,QAAM,UAAU,OAAO,IAAI;AAC3B,eAAa,UAAU;AACvB,UAAQ,UAAU;AAElB,YAAU,MAAM;AACd,QAAI,CAAC,MAAM;AACT;AAAA,IACF;AAEA,aAAS;AAAA,MACP;AAAA,MACA,WAAW,MAAM,aAAa,QAAA;AAAA,MAC9B;AAAA,IAAA,CACD;AAED,WAAO,MAAM;AACX,iBAAW,EAAE;AAAA,IACf;AAAA,EACF,GAAG,CAAC,iBAAiB,IAAI,MAAM,UAAU,UAAU,CAAC;AAEpD,YAAU,MAAM;AACd,QAAI,CAAC,MAAM;AACT;AAAA,IACF;AAEA,uBAAmB,EAAE;AAErB,WAAO,MAAM;AACX,qBAAe,MAAM;AACnB,YAAI,CAAC,QAAQ,SAAS;AACpB,sCAA4B,EAAE;AAAA,QAChC;AAAA,MACF,CAAC;AAAA,IACH;AAAA,EACF,GAAG,CAAC,IAAI,MAAM,oBAAoB,2BAA2B,CAAC;AAE9D,SAAO;AAAA,IACL,kBAAkB,QAAQ,MAAM,EAAE;AAAA,IAClC,QAAQ,UAAU,EAAE;AAAA,EAAA;AAExB;"}
@@ -0,0 +1,2 @@
1
+ export { mergeOverlayRefs, useOverlayPresence, withOverlayOpen, } from './useOverlayPresence';
2
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/presence/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,gBAAgB,EAChB,kBAAkB,EAClB,eAAe,GAChB,MAAM,sBAAsB,CAAC"}
@@ -0,0 +1,7 @@
1
+ import { mergeOverlayRefs, useOverlayPresence, withOverlayOpen } from "./useOverlayPresence.js";
2
+ export {
3
+ mergeOverlayRefs,
4
+ useOverlayPresence,
5
+ withOverlayOpen
6
+ };
7
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sources":[],"sourcesContent":[],"names":[],"mappings":";"}
@@ -0,0 +1,10 @@
1
+ import { type RefObject } from 'react';
2
+ export declare function useOverlayPresence<T extends HTMLElement = HTMLElement>(active: boolean): {
3
+ mounted: boolean;
4
+ isOpen: boolean;
5
+ overlayRef: RefObject<T | null>;
6
+ };
7
+ export declare function withOverlayOpen(className: string, isOpen: boolean): string;
8
+ /** Attach the same DOM node to multiple refs (e.g. layout measure + transition root). */
9
+ export declare function mergeOverlayRefs<T extends HTMLElement>(overlayRef: RefObject<T | null>, ...refs: Array<RefObject<T | null> | ((node: T | null) => void) | null | undefined>): (node: T | null) => void;
10
+ //# sourceMappingURL=useOverlayPresence.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"useOverlayPresence.d.ts","sourceRoot":"","sources":["../../src/presence/useOverlayPresence.ts"],"names":[],"mappings":"AAAA,OAAO,EAA+B,KAAK,SAAS,EAAE,MAAM,OAAO,CAAC;AA0EpE,wBAAgB,kBAAkB,CAAC,CAAC,SAAS,WAAW,GAAG,WAAW,EAAE,MAAM,EAAE,OAAO;;;;EAwCtF;AAED,wBAAgB,eAAe,CAAC,SAAS,EAAE,MAAM,EAAE,MAAM,EAAE,OAAO,UAEjE;AAED,yFAAyF;AACzF,wBAAgB,gBAAgB,CAAC,CAAC,SAAS,WAAW,EACpD,UAAU,EAAE,SAAS,CAAC,CAAC,GAAG,IAAI,CAAC,EAC/B,GAAG,IAAI,EAAE,KAAK,CAAC,SAAS,CAAC,CAAC,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,CAAC,GAAG,IAAI,KAAK,IAAI,CAAC,GAAG,IAAI,GAAG,SAAS,CAAC,IAE3E,MAAM,CAAC,GAAG,IAAI,UAavB"}
@@ -0,0 +1,119 @@
1
+ import { useRef, useState, useEffect } from "react";
2
+ const FALLBACK_OVERLAY_MS = 200;
3
+ function parseCssTime(value) {
4
+ const trimmed = value.trim();
5
+ if (!trimmed || trimmed === "0") {
6
+ return 0;
7
+ }
8
+ if (trimmed.endsWith("ms")) {
9
+ return Number.parseFloat(trimmed);
10
+ }
11
+ if (trimmed.endsWith("s")) {
12
+ return Number.parseFloat(trimmed) * 1e3;
13
+ }
14
+ return Number.parseFloat(trimmed) || 0;
15
+ }
16
+ function getMaxTransitionMs(el) {
17
+ const style = getComputedStyle(el);
18
+ const durations = style.transitionDuration.split(",").map(parseCssTime);
19
+ const delays = style.transitionDelay.split(",").map(parseCssTime);
20
+ let max = 0;
21
+ const count = Math.max(durations.length, delays.length, 1);
22
+ for (let i = 0; i < count; i += 1) {
23
+ const duration = durations[i % durations.length] ?? durations[0] ?? 0;
24
+ const delay = delays[i % delays.length] ?? delays[0] ?? 0;
25
+ max = Math.max(max, duration + delay);
26
+ }
27
+ return max > 0 ? max : FALLBACK_OVERLAY_MS;
28
+ }
29
+ function waitForSubtreeTransitions(el, onDone) {
30
+ let settled = false;
31
+ let debounceId = 0;
32
+ let safetyId = 0;
33
+ const cleanup = () => {
34
+ el.removeEventListener("transitionend", onTransitionEnd);
35
+ window.clearTimeout(debounceId);
36
+ window.clearTimeout(safetyId);
37
+ };
38
+ const finish = () => {
39
+ if (settled) {
40
+ return;
41
+ }
42
+ settled = true;
43
+ cleanup();
44
+ onDone();
45
+ };
46
+ const onTransitionEnd = (event) => {
47
+ if (event.target instanceof Node && !el.contains(event.target)) {
48
+ return;
49
+ }
50
+ window.clearTimeout(debounceId);
51
+ debounceId = window.setTimeout(finish, 0);
52
+ };
53
+ safetyId = window.setTimeout(finish, getMaxTransitionMs(el) + 50);
54
+ el.addEventListener("transitionend", onTransitionEnd);
55
+ return () => {
56
+ if (!settled) {
57
+ settled = true;
58
+ cleanup();
59
+ }
60
+ };
61
+ }
62
+ function useOverlayPresence(active) {
63
+ const overlayRef = useRef(null);
64
+ const [mounted, setMounted] = useState(active);
65
+ const [isOpen, setIsOpen] = useState(false);
66
+ useEffect(() => {
67
+ if (active) {
68
+ setMounted(true);
69
+ let raf2 = 0;
70
+ const raf1 = requestAnimationFrame(() => {
71
+ raf2 = requestAnimationFrame(() => setIsOpen(true));
72
+ });
73
+ return () => {
74
+ cancelAnimationFrame(raf1);
75
+ cancelAnimationFrame(raf2);
76
+ };
77
+ }
78
+ setIsOpen(false);
79
+ let cancelWait;
80
+ let exitRaf = 0;
81
+ exitRaf = requestAnimationFrame(() => {
82
+ const el = overlayRef.current;
83
+ if (!el) {
84
+ setMounted(false);
85
+ return;
86
+ }
87
+ cancelWait = waitForSubtreeTransitions(el, () => setMounted(false));
88
+ });
89
+ return () => {
90
+ cancelAnimationFrame(exitRaf);
91
+ cancelWait?.();
92
+ };
93
+ }, [active]);
94
+ return { mounted, isOpen, overlayRef };
95
+ }
96
+ function withOverlayOpen(className, isOpen) {
97
+ return isOpen ? `${className} is-open` : className;
98
+ }
99
+ function mergeOverlayRefs(overlayRef, ...refs) {
100
+ return (node) => {
101
+ overlayRef.current = node;
102
+ for (const ref of refs) {
103
+ if (!ref) {
104
+ continue;
105
+ }
106
+ if (typeof ref === "function") {
107
+ ref(node);
108
+ continue;
109
+ }
110
+ ref.current = node;
111
+ }
112
+ };
113
+ }
114
+ export {
115
+ mergeOverlayRefs,
116
+ useOverlayPresence,
117
+ withOverlayOpen
118
+ };
119
+ //# sourceMappingURL=useOverlayPresence.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"useOverlayPresence.js","sources":["../../src/presence/useOverlayPresence.ts"],"sourcesContent":["import { useEffect, useRef, useState, type RefObject } from 'react';\n\n/** Fallback when computed transition duration is unavailable (see `--transition-overlay` in common-css). */\nconst FALLBACK_OVERLAY_MS = 200;\n\nfunction parseCssTime(value: string): number {\n const trimmed = value.trim();\n if (!trimmed || trimmed === '0') {\n return 0;\n }\n if (trimmed.endsWith('ms')) {\n return Number.parseFloat(trimmed);\n }\n if (trimmed.endsWith('s')) {\n return Number.parseFloat(trimmed) * 1000;\n }\n return Number.parseFloat(trimmed) || 0;\n}\n\nfunction getMaxTransitionMs(el: HTMLElement): number {\n const style = getComputedStyle(el);\n const durations = style.transitionDuration.split(',').map(parseCssTime);\n const delays = style.transitionDelay.split(',').map(parseCssTime);\n let max = 0;\n const count = Math.max(durations.length, delays.length, 1);\n\n for (let i = 0; i < count; i += 1) {\n const duration = durations[i % durations.length] ?? durations[0] ?? 0;\n const delay = delays[i % delays.length] ?? delays[0] ?? 0;\n max = Math.max(max, duration + delay);\n }\n\n return max > 0 ? max : FALLBACK_OVERLAY_MS;\n}\n\nfunction waitForSubtreeTransitions(el: HTMLElement, onDone: () => void): () => void {\n let settled = false;\n let debounceId = 0;\n let safetyId = 0;\n\n const cleanup = () => {\n el.removeEventListener('transitionend', onTransitionEnd);\n window.clearTimeout(debounceId);\n window.clearTimeout(safetyId);\n };\n\n const finish = () => {\n if (settled) {\n return;\n }\n settled = true;\n cleanup();\n onDone();\n };\n\n const onTransitionEnd = (event: TransitionEvent) => {\n if (event.target instanceof Node && !el.contains(event.target)) {\n return;\n }\n window.clearTimeout(debounceId);\n debounceId = window.setTimeout(finish, 0);\n };\n\n safetyId = window.setTimeout(finish, getMaxTransitionMs(el) + 50);\n el.addEventListener('transitionend', onTransitionEnd);\n\n return () => {\n if (!settled) {\n settled = true;\n cleanup();\n }\n };\n}\n\nexport function useOverlayPresence<T extends HTMLElement = HTMLElement>(active: boolean) {\n const overlayRef = useRef<T | null>(null);\n const [mounted, setMounted] = useState(active);\n const [isOpen, setIsOpen] = useState(false);\n\n useEffect(() => {\n if (active) {\n setMounted(true);\n let raf2 = 0;\n const raf1 = requestAnimationFrame(() => {\n raf2 = requestAnimationFrame(() => setIsOpen(true));\n });\n return () => {\n cancelAnimationFrame(raf1);\n cancelAnimationFrame(raf2);\n };\n }\n\n setIsOpen(false);\n\n let cancelWait: (() => void) | undefined;\n let exitRaf = 0;\n\n exitRaf = requestAnimationFrame(() => {\n const el = overlayRef.current;\n if (!el) {\n setMounted(false);\n return;\n }\n\n cancelWait = waitForSubtreeTransitions(el, () => setMounted(false));\n });\n\n return () => {\n cancelAnimationFrame(exitRaf);\n cancelWait?.();\n };\n }, [active]);\n\n return { mounted, isOpen, overlayRef };\n}\n\nexport function withOverlayOpen(className: string, isOpen: boolean) {\n return isOpen ? `${className} is-open` : className;\n}\n\n/** Attach the same DOM node to multiple refs (e.g. layout measure + transition root). */\nexport function mergeOverlayRefs<T extends HTMLElement>(\n overlayRef: RefObject<T | null>,\n ...refs: Array<RefObject<T | null> | ((node: T | null) => void) | null | undefined>\n) {\n return (node: T | null) => {\n overlayRef.current = node;\n for (const ref of refs) {\n if (!ref) {\n continue;\n }\n if (typeof ref === 'function') {\n ref(node);\n continue;\n }\n ref.current = node;\n }\n };\n}\n"],"names":[],"mappings":"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"}