react-modal-registry 0.1.0 → 0.1.1

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/dist/index.js CHANGED
@@ -1,302 +1,2 @@
1
- "use client";
2
-
3
- // src/index.tsx
4
- import {
5
- createContext,
6
- memo,
7
- Suspense,
8
- useCallback,
9
- useContext,
10
- useEffect,
11
- useMemo,
12
- useRef,
13
- useState
14
- } from "react";
15
- import { Fragment, jsx, jsxs } from "react/jsx-runtime";
16
- var ModalActionsContext = createContext(null);
17
- var ModalSlotsContext = createContext(null);
18
- var UNMOUNT_DELAY_MS = 200;
19
- function ModalProvider({ children }) {
20
- const slotsRef = useRef(/* @__PURE__ */ new Map());
21
- const pendingRef = useRef(/* @__PURE__ */ new Map());
22
- const timersRef = useRef(/* @__PURE__ */ new Map());
23
- const [slots, setSlots] = useState(/* @__PURE__ */ new Map());
24
- useEffect(() => {
25
- const timers = timersRef.current;
26
- const pendings = pendingRef.current;
27
- return () => {
28
- for (const timer of timers.values()) {
29
- clearTimeout(timer);
30
- }
31
- timers.clear();
32
- for (const pending of pendings.values()) {
33
- pending.resolve(void 0);
34
- }
35
- pendings.clear();
36
- };
37
- }, []);
38
- const sync = useCallback(() => {
39
- setSlots(new Map(slotsRef.current));
40
- }, []);
41
- const register = useCallback(
42
- (idOrModals, component) => {
43
- const items = Array.isArray(idOrModals) ? idOrModals : component ? [
44
- {
45
- id: idOrModals,
46
- component
47
- }
48
- ] : [];
49
- let changed = false;
50
- for (const { id, component: comp } of items) {
51
- const existing = slotsRef.current.get(id);
52
- const typedComp = comp;
53
- if (existing) {
54
- if (existing.component !== typedComp) {
55
- slotsRef.current.set(id, {
56
- ...existing,
57
- component: typedComp
58
- });
59
- changed = true;
60
- }
61
- } else {
62
- slotsRef.current.set(id, {
63
- id,
64
- component: typedComp,
65
- isOpen: false,
66
- isMounted: false,
67
- data: void 0
68
- });
69
- changed = true;
70
- }
71
- }
72
- if (changed) {
73
- sync();
74
- }
75
- },
76
- [sync]
77
- );
78
- const unregister = useCallback(
79
- (idOrIds) => {
80
- const ids = Array.isArray(idOrIds) ? idOrIds : [idOrIds];
81
- let changed = false;
82
- for (const id of ids) {
83
- const timer = timersRef.current.get(id);
84
- if (timer) {
85
- clearTimeout(timer);
86
- timersRef.current.delete(id);
87
- }
88
- const pending = pendingRef.current.get(id);
89
- if (pending) {
90
- pendingRef.current.delete(id);
91
- pending.resolve(void 0);
92
- }
93
- if (slotsRef.current.has(id)) {
94
- slotsRef.current.delete(id);
95
- changed = true;
96
- }
97
- }
98
- if (changed) {
99
- sync();
100
- }
101
- },
102
- [sync]
103
- );
104
- const open = useCallback(
105
- (id, ...args) => {
106
- const data = args[0];
107
- const slot = slotsRef.current.get(id);
108
- if (!slot) {
109
- return Promise.reject(
110
- new Error(`[react-modal-registry] Modal "${String(id)}" is not registered.`)
111
- );
112
- }
113
- const existing = pendingRef.current.get(id);
114
- if (slot.isOpen && slot.data === data && existing) {
115
- return existing.promise;
116
- }
117
- if (existing) {
118
- pendingRef.current.delete(id);
119
- existing.resolve(void 0);
120
- }
121
- const timer = timersRef.current.get(id);
122
- if (timer) {
123
- clearTimeout(timer);
124
- timersRef.current.delete(id);
125
- }
126
- let resolver;
127
- const promise = new Promise((resolve) => {
128
- resolver = resolve;
129
- });
130
- pendingRef.current.set(id, {
131
- promise,
132
- resolve: resolver
133
- });
134
- slotsRef.current.set(id, { ...slot, isOpen: true, isMounted: true, data });
135
- sync();
136
- return promise;
137
- },
138
- [sync]
139
- );
140
- const scheduleUnmount = useCallback(
141
- (id) => {
142
- const timer = timersRef.current.get(id);
143
- if (timer) clearTimeout(timer);
144
- timersRef.current.set(
145
- id,
146
- setTimeout(() => {
147
- timersRef.current.delete(id);
148
- const current = slotsRef.current.get(id);
149
- if (current && !current.isOpen) {
150
- slotsRef.current.set(id, { ...current, isMounted: false });
151
- sync();
152
- }
153
- }, UNMOUNT_DELAY_MS)
154
- );
155
- },
156
- [sync]
157
- );
158
- const close = useCallback(
159
- (id, result) => {
160
- const slot = slotsRef.current.get(id);
161
- if (!slot || !slot.isOpen) return;
162
- const pending = pendingRef.current.get(id);
163
- if (pending) {
164
- pendingRef.current.delete(id);
165
- pending.resolve(result);
166
- }
167
- slotsRef.current.set(id, { ...slot, isOpen: false, isMounted: true });
168
- sync();
169
- scheduleUnmount(id);
170
- },
171
- [sync, scheduleUnmount]
172
- );
173
- const closeAll = useCallback(() => {
174
- for (const [, pending] of pendingRef.current) {
175
- pending.resolve(void 0);
176
- }
177
- pendingRef.current.clear();
178
- let changed = false;
179
- for (const [id, slot] of slotsRef.current) {
180
- if (slot.isOpen) {
181
- slotsRef.current.set(id, { ...slot, isOpen: false, isMounted: true });
182
- scheduleUnmount(id);
183
- changed = true;
184
- }
185
- }
186
- if (changed) {
187
- sync();
188
- }
189
- }, [sync, scheduleUnmount]);
190
- const isOpen = useCallback((id) => slotsRef.current.get(id)?.isOpen ?? false, []);
191
- const actions = useMemo(
192
- () => ({
193
- register,
194
- unregister,
195
- open,
196
- close,
197
- closeAll,
198
- isOpen
199
- }),
200
- [register, unregister, open, close, closeAll, isOpen]
201
- );
202
- return /* @__PURE__ */ jsx(ModalActionsContext.Provider, { value: actions, children: /* @__PURE__ */ jsxs(ModalSlotsContext.Provider, { value: slots, children: [
203
- children,
204
- /* @__PURE__ */ jsx(ModalRenderer, {})
205
- ] }) });
206
- }
207
- function useRegisterModal(idOrModals, component) {
208
- const { register, unregister } = useModalContext();
209
- useEffect(() => {
210
- if (Array.isArray(idOrModals)) {
211
- register(idOrModals);
212
- return () => {
213
- unregister(idOrModals.map((m) => m.id));
214
- };
215
- }
216
- if (component) {
217
- register(idOrModals, component);
218
- return () => {
219
- unregister(idOrModals);
220
- };
221
- }
222
- return void 0;
223
- }, [idOrModals, component, register, unregister]);
224
- }
225
- function ModalRegistry({
226
- modals
227
- }) {
228
- useRegisterModal(modals);
229
- return null;
230
- }
231
- function useModal(id) {
232
- const { open, close, isOpen } = useModalContext();
233
- return useMemo(
234
- () => ({
235
- open: (...args) => open(id, ...args),
236
- close: (result) => close(id, result),
237
- isOpen: () => isOpen(id)
238
- }),
239
- [open, close, isOpen, id]
240
- );
241
- }
242
- function useIsModalOpen(id) {
243
- const slots = useContext(ModalSlotsContext);
244
- if (!slots) {
245
- throw new Error("useIsModalOpen must be used within <ModalProvider>");
246
- }
247
- return slots.get(id)?.isOpen ?? false;
248
- }
249
- function useCloseAll() {
250
- return useModalContext().closeAll;
251
- }
252
- var ModalSlotItem = memo(function ModalSlotItem2({
253
- id,
254
- component: Component,
255
- isOpen,
256
- data,
257
- close
258
- }) {
259
- const handleClose = useCallback(
260
- (result) => {
261
- close(id, result);
262
- },
263
- [close, id]
264
- );
265
- return /* @__PURE__ */ jsx(Suspense, { fallback: null, children: /* @__PURE__ */ jsx(Component, { isOpen, data, close: handleClose }) });
266
- });
267
- function ModalRenderer() {
268
- const slots = useContext(ModalSlotsContext);
269
- const { close } = useModalContext();
270
- if (!slots) {
271
- return null;
272
- }
273
- return /* @__PURE__ */ jsx(Fragment, { children: Array.from(slots.values()).map(
274
- (slot) => slot.isMounted ? /* @__PURE__ */ jsx(
275
- ModalSlotItem,
276
- {
277
- id: slot.id,
278
- component: slot.component,
279
- isOpen: slot.isOpen,
280
- data: slot.data,
281
- close
282
- },
283
- slot.id
284
- ) : null
285
- ) });
286
- }
287
- function useModalContext() {
288
- const context = useContext(ModalActionsContext);
289
- if (!context) {
290
- throw new Error("useModalContext must be used within <ModalProvider>");
291
- }
292
- return context;
293
- }
294
- export {
295
- ModalProvider,
296
- ModalRegistry,
297
- useCloseAll,
298
- useIsModalOpen,
299
- useModal,
300
- useRegisterModal
301
- };
1
+ "use client";import{createContext as z,memo as Z,Suspense as ee,useCallback as y,useContext as G,useEffect as E,useMemo as L,useRef as v,useState as te}from"react";import{Fragment as ue,jsx as C,jsxs as ie}from"react/jsx-runtime";var k={strategy:"hash",param:"modal",historyMode:"push"},N=()=>typeof window<"u",ne=200,Y=z(null),J=z(new Map);function oe(t,e){if(Object.is(t,e))return!0;if(typeof t!="object"||t===null||typeof e!="object"||e===null)return!1;if(t instanceof Date&&e instanceof Date)return t.getTime()===e.getTime();if(t instanceof RegExp&&e instanceof RegExp)return t.source===e.source&&t.flags===e.flags;if(t instanceof Map||t instanceof Set||e instanceof Map||e instanceof Set)return!1;let s=Object.keys(t),a=Object.keys(e);return s.length!==a.length?!1:s.every(l=>Object.prototype.hasOwnProperty.call(e,l)&&t[l]===e[l])}function re(t){let e=v(t);return Array.isArray(t)&&Array.isArray(e.current)&&e.current.length===t.length&&e.current.every((s,a)=>s.id===t[a].id&&s.component===t[a].component)?e.current:(e.current=t,t)}function Q(t){let e=t.replace(/^#/,""),[s,a]=e.includes("?")?e.split("?",2):e.includes("=")?["",e]:[e,""];return{anchor:s,params:new URLSearchParams(a)}}function ae(t,e){let s=e.toString();return s?`#${t?`${t}?`:""}${s}`:t?`#${t}`:""}function b(t){if(!N()||!t||t.enabled===!1)return null;let e=t.param??k.param;return(t.strategy??k.strategy)==="query"?new URLSearchParams(window.location.search).get(e):Q(window.location.hash).params.get(e)}function O(t,e,s){if(!N()||!e||e.enabled===!1||b(e)===t)return;let a=s??e.historyMode??k.historyMode,l=e.param??k.param,p=new URL(window.location.href),x=I=>t?I.set(l,t):I.delete(l);if((e.strategy??k.strategy)==="query")x(p.searchParams);else{let{anchor:I,params:M}=Q(window.location.hash);x(M),p.hash=ae(I,M)}window.history[a==="push"?"pushState":"replaceState"](window.history.state,"",p.pathname+p.search+p.hash)}function B(t,e){if(t)return typeof t=="function"?t(e):Object.prototype.hasOwnProperty.call(t,e)?t[e]:void 0}function pe({children:t,unmountDelay:e=ne,fallback:s=null,routing:a}){let l=v(new Map),[p,x]=te(new Map),I=v(p),M=y(n=>{let o=n(I.current);return I.current=o,x(o),o},[]),R=v(new Map),T=v(new Map),W=v(0),A=L(()=>a?{...k,...a,enabled:a.enabled!==!1}:null,[a?.enabled,a?.strategy,a?.param,a?.historyMode,a?.defaultData]),c=v(A);c.current=A,E(()=>()=>{T.current.forEach(clearTimeout),T.current.clear(),R.current.forEach(n=>n.resolve(void 0)),R.current.clear()},[]);let P=y(n=>{let o=l.current.get(n);return o?.[o.length-1]},[]),U=y((n,o)=>{let u=T.current.get(n);u&&clearTimeout(u);let r;r=setTimeout(()=>{T.current.get(n)===r&&T.current.delete(n),M(d=>{let i=d.get(n);if(!i||i.isOpen||i.invocationId!==o)return d;let g=new Map(d);return g.delete(n),g})},e),T.current.set(n,r)},[e,M]),D=y((n,o,u,r=!0)=>{let d=I.current.get(n);if(!d||!d.isOpen||o!==void 0&&d.invocationId!==o)return;let i=R.current.get(n);i&&(o===void 0||i.invocationId===o)&&(R.current.delete(n),i.resolve(u)),r&&c.current?.enabled&&b(c.current)===String(n)&&O(null,c.current,"replace");let g=!1;M(f=>{let m=f.get(n);if(!m||!m.isOpen||o!==void 0&&m.invocationId!==o)return f;g=!0;let h=new Map(f);return e<=0?h.delete(n):h.set(n,{...m,isOpen:!1}),h}),g&&e>0&&U(n,d.invocationId)},[U,e,M]),w=y((n,o,u=!0)=>{let r=P(n);if(!r)return Promise.reject(new Error(`[react-modal-registry] Modal "${String(n)}" is not registered.`));let d=I.current.get(n),i=R.current.get(n);if(d?.isOpen&&d.component===r&&oe(d.data,o)&&i)return i.promise;u&&c.current?.enabled&&O(String(n),c.current),i&&(R.current.delete(n),i.resolve(void 0));let g=T.current.get(n);g&&(clearTimeout(g),T.current.delete(n));let f=++W.current,m,h=new Promise(S=>{m=S});return R.current.set(n,{promise:h,resolve:m,invocationId:f}),M(S=>{let _=new Map(S);return _.set(n,{id:n,component:r,isOpen:!0,data:o,invocationId:f}),_}),h},[P,M]),$=y((n,...o)=>w(n,o[0],!0),[w]),q=y((n,o)=>{D(n,void 0,o,!0)},[D]),V=y(()=>{let n=Array.from(R.current.values());if(R.current.clear(),c.current?.enabled&&O(null,c.current,"replace"),e<=0){for(let o of T.current.values())clearTimeout(o);T.current.clear(),M(()=>new Map)}else{for(let[o,u]of I.current)u.isOpen&&U(o,u.invocationId);M(o=>{let u=!1,r=new Map(o);for(let[d,i]of o)i.isOpen&&(r.set(d,{...i,isOpen:!1}),u=!0);return u?r:o})}n.forEach(o=>o.resolve(void 0))},[U,e,M]),H=y(n=>I.current.get(n)?.isOpen??!1,[]),K=y(((n,o)=>{let u=Array.isArray(n)?n:o?[{id:n,component:o}]:[],r=c.current?.enabled?b(c.current):null;for(let d of u){let i=l.current.get(d.id);if(i||(i=[],l.current.set(d.id,i)),i.push(d.component),r&&r===String(d.id)&&!I.current.get(d.id)?.isOpen){let g=B(c.current?.defaultData,d.id);w(d.id,g,!1)}}}),[w]),F=y((n,o)=>{let u=Array.isArray(n)?n:[n];for(let r of u){let d=l.current.get(r);if(!d)continue;if(o){let f=d.lastIndexOf(o);if(f!==-1&&d.splice(f,1),d.length>0)continue}l.current.delete(r);let i=T.current.get(r);i&&(clearTimeout(i),T.current.delete(r));let g=R.current.get(r);g&&(R.current.delete(r),g.resolve(void 0)),c.current?.enabled&&b(c.current)===String(r)&&O(null,c.current,"replace"),M(f=>{if(!f.has(r))return f;let m=new Map(f);return m.delete(r),m})}},[M]);E(()=>{if(!A||!A.enabled||!N())return;let n=()=>{let o=b(c.current),u=I.current;for(let[r,d]of u)String(r)!==o&&d.isOpen&&D(r,void 0,void 0,!1);if(o){let r=o;!u.get(r)?.isOpen&&P(r)&&w(r,B(c.current?.defaultData,r),!1)}};return n(),window.addEventListener("popstate",n),window.addEventListener("hashchange",n),()=>{window.removeEventListener("popstate",n),window.removeEventListener("hashchange",n)}},[A,P,w,D]);let X=L(()=>({register:K,unregister:F,open:$,close:q,closeAll:V,isOpen:H}),[K,F,$,q,V,H]);return C(Y.Provider,{value:X,children:ie(J.Provider,{value:p,children:[t,C(de,{activeModals:p,close:D,fallback:s})]})})}function de({activeModals:t,close:e,fallback:s}){return t.size===0?null:C(ue,{children:Array.from(t.values(),a=>C(se,{active:a,onClose:e,fallback:s},`${String(a.id)}:${a.invocationId}`))})}var se=Z(function({active:e,onClose:s,fallback:a}){let l=y(x=>s(e.id,e.invocationId,x,!0),[s,e.id,e.invocationId]),p=e.component;return C(ee,{fallback:a??null,children:C(p,{isOpen:e.isOpen,data:e.data,close:l})})});function le(t,e){let{register:s,unregister:a}=j(),l=re(t);E(()=>{if(Array.isArray(l))return s(l),()=>{for(let p of l)a(p.id,p.component)};if(e)return s(l,e),()=>{a(l,e)}},[l,e,s,a])}function Me({modals:t}){return le(t),null}function fe(t){let{open:e,close:s,isOpen:a}=j();return L(()=>({open:(...l)=>e(t,...l),close:l=>s(t,l),isOpen:()=>a(t)}),[e,s,a,t])}function Ie(t){return G(J).get(t)?.isOpen??!1}function ge(){return j().closeAll}function j(){let t=G(Y);if(!t)throw new Error("useModalContext must be used within <ModalProvider>");return t}export{pe as ModalProvider,Me as ModalRegistry,N as isBrowser,ge as useCloseAll,Ie as useIsModalOpen,fe as useModal,le as useRegisterModal};
302
2
  //# sourceMappingURL=index.js.map
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/index.tsx"],"sourcesContent":["'use client'\n\nimport {\n createContext,\n memo,\n Suspense,\n useCallback,\n useContext,\n useEffect,\n useMemo,\n useRef,\n useState,\n type ComponentType,\n type ReactNode\n} from 'react'\n\n/**\n * Registry for modal payload types via module augmentation.\n *\n * @example\n * ```ts\n * declare module 'react-modal-registry' {\n * interface ModalDataMap {\n * confirmAction: { itemId: string }\n * }\n * }\n * ```\n */\nexport interface ModalDataMap {}\n\n/**\n * Registry for modal return types via module augmentation.\n *\n * @example\n * ```ts\n * declare module 'react-modal-registry' {\n * interface ModalResultMap {\n * confirmAction: boolean\n * }\n * }\n * ```\n */\nexport interface ModalResultMap {}\n\n/**\n * Registry for paired modal payload and return types via module augmentation.\n *\n * @example\n * ```ts\n * declare module 'react-modal-registry' {\n * interface ModalRegistry {\n * confirmAction: { data: { itemId: string }; result: boolean }\n * }\n * }\n * ```\n */\nexport interface ModalRegistry {}\n\nexport type RegisteredModalId = keyof ModalDataMap | keyof ModalResultMap | keyof ModalRegistry\n\n/**\n * Modal identifier supporting autocompletion of registered IDs with string fallback.\n */\nexport type ModalId = RegisteredModalId | (string & {})\n\nexport type ModalData<TId extends ModalId> = TId extends keyof ModalDataMap\n ? ModalDataMap[TId]\n : TId extends keyof ModalRegistry\n ? ModalRegistry[TId] extends { data: infer TData }\n ? TData\n : unknown\n : unknown\n\nexport type ModalResult<TId extends ModalId> = TId extends keyof ModalResultMap\n ? ModalResultMap[TId]\n : TId extends keyof ModalRegistry\n ? ModalRegistry[TId] extends { result: infer TResult }\n ? TResult\n : unknown\n : unknown\n\n/**\n * optional vs required payload arguments based on `TData`.\n */\nexport type ModalDataArg<TData> = [unknown] extends [TData]\n ? [data?: TData]\n : [TData] extends [void] | [undefined]\n ? [data?: TData]\n : undefined extends TData\n ? [data?: TData]\n : [data: TData]\n\n/**\n * Standard props passed to registered modal components.\n */\nexport interface ModalProps<TData = unknown, TResult = unknown> {\n isOpen: boolean\n data: TData\n close: (result?: TResult) => void\n}\n\nexport type ModalComponent<TData = unknown, TResult = unknown> =\n | ComponentType<ModalProps<TData, TResult>>\n | ComponentType<ModalProps<TData, unknown>>\n\nexport interface ModalRegistration<TId extends ModalId = ModalId> {\n id: TId\n component: ModalComponent<ModalData<TId>, ModalResult<TId>>\n}\n\n/**\n * Imperative modal controller returned by `useModal(id)`.\n */\nexport interface UseModalReturn<TData = unknown, TResult = unknown> {\n /**\n * Opens the modal with the specified payload and resolves when closed.\n */\n open(...args: ModalDataArg<TData>): Promise<TResult>\n\n /**\n * Closes the modal imperatively, optionally passing a resolution value.\n */\n close(result?: TResult): void\n\n /**\n * Imperatively reads whether the modal is currently open.\n */\n isOpen(): boolean\n}\n\ninterface ModalSlot {\n id: ModalId\n component: ComponentType<ModalProps<unknown, unknown>>\n isOpen: boolean\n isMounted: boolean\n data: unknown\n}\n\ninterface ModalPending {\n promise: Promise<unknown>\n resolve: (value: unknown) => void\n}\n\nexport interface ModalActionsValue {\n register<TId extends ModalId>(\n id: TId,\n component: ModalComponent<ModalData<TId>, ModalResult<TId>>\n ): void\n register<const TIds extends readonly ModalId[]>(modals: {\n [K in keyof TIds]: ModalRegistration<TIds[K]>\n }): void\n unregister(idOrIds: ModalId | readonly ModalId[]): void\n open<TId extends ModalId, TResult = ModalResult<TId>>(\n id: TId,\n ...args: ModalDataArg<ModalData<TId>>\n ): Promise<TResult>\n close<TId extends ModalId>(id: TId, result?: ModalResult<TId>): void\n closeAll(): void\n isOpen(id: ModalId): boolean\n}\n\nconst ModalActionsContext = createContext<ModalActionsValue | null>(null)\nconst ModalSlotsContext = createContext<ReadonlyMap<ModalId, ModalSlot> | null>(null)\n\n// Allows exit animation transitions to complete before unmounting slot\nconst UNMOUNT_DELAY_MS = 200\n\n/**\n * Root provider hosting the modal registry, lifecycle actions, and slot renderer.\n */\nexport function ModalProvider({ children }: { children: ReactNode }) {\n const slotsRef = useRef<Map<ModalId, ModalSlot>>(new Map())\n const pendingRef = useRef<Map<ModalId, ModalPending>>(new Map())\n const timersRef = useRef<Map<ModalId, ReturnType<typeof setTimeout>>>(new Map())\n const [slots, setSlots] = useState<ReadonlyMap<ModalId, ModalSlot>>(new Map())\n\n useEffect(() => {\n const timers = timersRef.current\n const pendings = pendingRef.current\n return () => {\n for (const timer of timers.values()) {\n clearTimeout(timer)\n }\n timers.clear()\n for (const pending of pendings.values()) {\n pending.resolve(undefined)\n }\n pendings.clear()\n }\n }, [])\n\n const sync = useCallback(() => {\n setSlots(new Map(slotsRef.current))\n }, [])\n\n const register: ModalActionsValue['register'] = useCallback(\n (\n idOrModals: ModalId | readonly ModalRegistration[],\n component?: ModalComponent<unknown, unknown>\n ) => {\n const items: readonly ModalRegistration[] = Array.isArray(idOrModals)\n ? idOrModals\n : component\n ? [\n {\n id: idOrModals,\n component: component as ModalComponent<unknown, unknown>\n }\n ]\n : []\n\n let changed = false\n for (const { id, component: comp } of items) {\n const existing = slotsRef.current.get(id)\n const typedComp = comp as ComponentType<ModalProps<unknown, unknown>>\n\n if (existing) {\n if (existing.component !== typedComp) {\n slotsRef.current.set(id, {\n ...existing,\n component: typedComp\n })\n changed = true\n }\n } else {\n slotsRef.current.set(id, {\n id,\n component: typedComp,\n isOpen: false,\n isMounted: false,\n data: undefined\n })\n changed = true\n }\n }\n\n if (changed) {\n sync()\n }\n },\n [sync]\n )\n\n const unregister = useCallback(\n (idOrIds: ModalId | readonly ModalId[]) => {\n const ids = Array.isArray(idOrIds) ? idOrIds : [idOrIds]\n let changed = false\n for (const id of ids) {\n const timer = timersRef.current.get(id)\n if (timer) {\n clearTimeout(timer)\n timersRef.current.delete(id)\n }\n\n const pending = pendingRef.current.get(id)\n if (pending) {\n pendingRef.current.delete(id)\n pending.resolve(undefined)\n }\n if (slotsRef.current.has(id)) {\n slotsRef.current.delete(id)\n changed = true\n }\n }\n if (changed) {\n sync()\n }\n },\n [sync]\n )\n\n const open = useCallback(\n <TId extends ModalId, TResult = ModalResult<TId>>(\n id: TId,\n ...args: ModalDataArg<ModalData<TId>>\n ): Promise<TResult> => {\n const data = args[0] as ModalData<TId>\n const slot = slotsRef.current.get(id)\n\n if (!slot) {\n return Promise.reject(\n new Error(`[react-modal-registry] Modal \"${String(id)}\" is not registered.`)\n )\n }\n\n // Re-use active pending promise if open call is repeated with identical payload\n const existing = pendingRef.current.get(id)\n if (slot.isOpen && slot.data === data && existing) {\n return existing.promise as Promise<TResult>\n }\n\n // Supersede any pending promise before creating a new one\n if (existing) {\n pendingRef.current.delete(id)\n existing.resolve(undefined)\n }\n\n const timer = timersRef.current.get(id)\n if (timer) {\n clearTimeout(timer)\n timersRef.current.delete(id)\n }\n\n let resolver!: (value: unknown) => void\n const promise = new Promise<TResult>(resolve => {\n resolver = resolve as (value: unknown) => void\n })\n\n pendingRef.current.set(id, {\n promise: promise as Promise<unknown>,\n resolve: resolver\n })\n\n slotsRef.current.set(id, { ...slot, isOpen: true, isMounted: true, data })\n sync()\n\n return promise\n },\n [sync]\n )\n\n const scheduleUnmount = useCallback(\n (id: ModalId) => {\n const timer = timersRef.current.get(id)\n if (timer) clearTimeout(timer)\n\n timersRef.current.set(\n id,\n setTimeout(() => {\n timersRef.current.delete(id)\n const current = slotsRef.current.get(id)\n if (current && !current.isOpen) {\n slotsRef.current.set(id, { ...current, isMounted: false })\n sync()\n }\n }, UNMOUNT_DELAY_MS)\n )\n },\n [sync]\n )\n\n const close = useCallback(\n <TId extends ModalId>(id: TId, result?: ModalResult<TId>) => {\n const slot = slotsRef.current.get(id)\n if (!slot || !slot.isOpen) return\n\n const pending = pendingRef.current.get(id)\n if (pending) {\n pendingRef.current.delete(id)\n pending.resolve(result)\n }\n\n slotsRef.current.set(id, { ...slot, isOpen: false, isMounted: true })\n sync()\n scheduleUnmount(id)\n },\n [sync, scheduleUnmount]\n )\n\n const closeAll = useCallback(() => {\n for (const [, pending] of pendingRef.current) {\n pending.resolve(undefined)\n }\n pendingRef.current.clear()\n\n let changed = false\n for (const [id, slot] of slotsRef.current) {\n if (slot.isOpen) {\n slotsRef.current.set(id, { ...slot, isOpen: false, isMounted: true })\n scheduleUnmount(id)\n changed = true\n }\n }\n\n if (changed) {\n sync()\n }\n }, [sync, scheduleUnmount])\n\n const isOpen = useCallback((id: ModalId) => slotsRef.current.get(id)?.isOpen ?? false, [])\n\n const actions = useMemo<ModalActionsValue>(\n () => ({\n register,\n unregister,\n open,\n close,\n closeAll,\n isOpen\n }),\n [register, unregister, open, close, closeAll, isOpen]\n )\n\n return (\n <ModalActionsContext.Provider value={actions}>\n <ModalSlotsContext.Provider value={slots}>\n {children}\n <ModalRenderer />\n </ModalSlotsContext.Provider>\n </ModalActionsContext.Provider>\n )\n}\n\n/**\n * Registers one or more modals for the lifetime of the calling component.\n *\n */\nexport function useRegisterModal<TId extends ModalId>(\n id: TId,\n component: ModalComponent<ModalData<TId>, ModalResult<TId>>\n): void\nexport function useRegisterModal<const TIds extends readonly ModalId[]>(modals: {\n [K in keyof TIds]: ModalRegistration<TIds[K]>\n}): void\nexport function useRegisterModal<TId extends ModalId>(\n idOrModals:\n | TId\n | readonly {\n id: ModalId\n component: ComponentType<ModalProps<unknown, unknown>>\n }[],\n component?: ModalComponent<ModalData<TId>, ModalResult<TId>>\n): void {\n const { register, unregister } = useModalContext()\n\n useEffect(() => {\n if (Array.isArray(idOrModals)) {\n register(idOrModals as Parameters<typeof register>[0])\n return () => {\n unregister(idOrModals.map(m => m.id))\n }\n }\n\n if (component) {\n register(idOrModals as TId, component)\n return () => {\n unregister(idOrModals as TId)\n }\n }\n\n return undefined\n }, [idOrModals, component, register, unregister])\n}\n\n/**\n * Declarative component for mounting static modal batches at application or layout roots.\n */\nexport function ModalRegistry<const TIds extends readonly ModalId[]>({\n modals\n}: {\n modals: {\n [K in keyof TIds]: ModalRegistration<TIds[K]>\n }\n}) {\n useRegisterModal(modals)\n return null\n}\n\n/**\n * Returns typed controls (`open`, `close`, `isOpen`) for a modal ID.\n *\n * `isOpen()` reads state imperatively without subscribing the caller to render updates.\n * Use `useIsModalOpen(id)` for reactive subscriptions.\n *\n * @example\n * ```tsx\n * function DeleteButton({ id }: { id: string }) {\n * const confirmModal = useModal('confirmation')\n *\n * const handleDelete = async () => {\n * const confirmed = await confirmModal.open({ itemId: id })\n * if (confirmed) {\n * deleteItem(id)\n * }\n * }\n *\n * return <button onClick={handleDelete}>Delete</button>\n * }\n * ```\n */\nexport function useModal<TId extends ModalId, TResult = ModalResult<TId>>(\n id: TId\n): UseModalReturn<ModalData<TId>, TResult> {\n const { open, close, isOpen } = useModalContext()\n\n return useMemo<UseModalReturn<ModalData<TId>, TResult>>(\n () => ({\n open: (...args: ModalDataArg<ModalData<TId>>) => open<TId, TResult>(id, ...args),\n close: (result?: TResult) => close(id, result as ModalResult<TId>),\n isOpen: () => isOpen(id)\n }),\n [open, close, isOpen, id]\n )\n}\n\n/**\n * Reactively subscribes to whether a modal is currently open.\n */\nexport function useIsModalOpen(id: ModalId): boolean {\n const slots = useContext(ModalSlotsContext)\n\n if (!slots) {\n throw new Error('useIsModalOpen must be used within <ModalProvider>')\n }\n\n return slots.get(id)?.isOpen ?? false\n}\n\nexport function useCloseAll(): () => void {\n return useModalContext().closeAll\n}\n\nconst ModalSlotItem = memo(function ModalSlotItem({\n id,\n component: Component,\n isOpen,\n data,\n close\n}: {\n id: ModalId\n component: ComponentType<ModalProps<unknown, unknown>>\n isOpen: boolean\n data: unknown\n close: (id: ModalId, result?: unknown) => void\n}) {\n const handleClose = useCallback(\n (result?: unknown) => {\n close(id, result)\n },\n [close, id]\n )\n\n return (\n <Suspense fallback={null}>\n <Component isOpen={isOpen} data={data} close={handleClose} />\n </Suspense>\n )\n})\n\nfunction ModalRenderer() {\n const slots = useContext(ModalSlotsContext)\n const { close } = useModalContext()\n\n if (!slots) {\n return null\n }\n\n return (\n <>\n {Array.from(slots.values()).map(slot =>\n slot.isMounted ? (\n <ModalSlotItem\n key={slot.id}\n id={slot.id}\n component={slot.component}\n isOpen={slot.isOpen}\n data={slot.data}\n close={close}\n />\n ) : null\n )}\n </>\n )\n}\n\nfunction useModalContext(): ModalActionsValue {\n const context = useContext(ModalActionsContext)\n\n if (!context) {\n throw new Error('useModalContext must be used within <ModalProvider>')\n }\n\n return context\n}\n"],"mappings":"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","names":["ModalSlotItem"]}
1
+ {"version":3,"sources":["../src/index.tsx"],"sourcesContent":["'use client'\n\nimport {\n createContext,\n memo,\n Suspense,\n useCallback,\n useContext,\n useEffect,\n useMemo,\n useRef,\n useState,\n type ComponentType,\n type ReactNode\n} from 'react'\n\n/**\n * Canonical registry for modal payloads and return types via module augmentation (e.g., `env.d.ts` or `modals.d.ts`).\n *\n * @example\n * ```ts\n * declare module 'react-modal-registry' {\n * interface ModalRegistry {\n * confirmAction: { data: { itemId: string }; result: boolean }\n * simpleAlert: void\n * }\n * }\n * ```\n */\nexport interface ModalRegistry { }\n\nexport type RegisteredModalId = keyof ModalRegistry\nexport type ModalId = [RegisteredModalId] extends [never] ? string : RegisteredModalId\n\nexport type ModalData<TId extends ModalId> = TId extends keyof ModalRegistry\n ? 'data' extends keyof ModalRegistry[TId]\n ? ModalRegistry[TId]['data']\n : void\n : unknown\n\nexport type ModalResult<TId extends ModalId> = TId extends keyof ModalRegistry\n ? 'result' extends keyof ModalRegistry[TId]\n ? ModalRegistry[TId]['result']\n : void\n : unknown\n\n/**\n * Derives optional vs. required payload arguments based on `TData`.\n */\nexport type ModalDataArg<TData> = [TData] extends [never]\n ? []\n : [unknown] extends [TData]\n ? [data?: TData]\n : [TData] extends [void] | [undefined]\n ? [data?: TData]\n : undefined extends TData\n ? [data?: TData]\n : [data: TData]\n\nexport type ModalClose<TResult = void> = [TResult] extends [void]\n ? () => void\n : (result?: TResult) => void\n\n/**\n * Standard props passed to registered modal components.\n */\nexport interface ModalProps<TData = unknown, TResult = void> {\n isOpen: boolean\n data: TData\n close: ModalClose<TResult>\n}\n\nexport type ModalComponent<TData = unknown, TResult = void> = ComponentType<\n ModalProps<TData, TResult>\n>\n\nexport type UntypedModalComponent = ComponentType<ModalProps<never, unknown>>\n\nexport type RegisteredComponent<TId extends ModalId> =\n [RegisteredModalId] extends [never]\n ? ComponentType<ModalProps<never, unknown>>\n : ModalComponent<ModalData<TId>, ModalResult<TId>>\n\nexport interface ModalRegistration<TId extends ModalId = ModalId> {\n id: TId\n component: RegisteredComponent<TId>\n}\n\n/**\n * Imperative modal controller returned by `useModal(id)`.\n */\nexport interface UseModalReturn<TData = unknown, TResult = unknown> {\n open(...args: ModalDataArg<TData>): Promise<TResult | undefined>\n close(result?: TResult): void\n isOpen(): boolean\n}\n\nexport type CanonicalResult<TId extends ModalId, TResult = ModalResult<TId>> =\n [RegisteredModalId] extends [never] ? TResult : ModalResult<TId>\n\nexport interface ModalActionsValue {\n register<TId extends ModalId>(\n id: TId,\n component: RegisteredComponent<TId>\n ): void\n register(modals: readonly ModalRegistration[]): void\n unregister(idOrIds: ModalId | readonly ModalId[], targetComponent?: UntypedModalComponent): void\n open<TId extends ModalId, TResult = ModalResult<TId>>(\n id: TId,\n ...args: ModalDataArg<ModalData<TId>>\n ): Promise<CanonicalResult<TId, TResult> | undefined>\n close<TId extends ModalId>(id: TId, result?: ModalResult<TId>): void\n closeAll(): void\n isOpen(id: ModalId): boolean\n}\n\nexport type ModalDefaultData =\n | Record<string, unknown>\n | ((id: ModalId) => unknown)\n\nexport type ModalRoutingStrategy = 'hash' | 'query'\nexport type ModalHistoryMode = 'push' | 'replace'\n\nexport interface ModalRoutingConfig {\n enabled?: boolean\n strategy?: ModalRoutingStrategy\n param?: string\n historyMode?: ModalHistoryMode\n defaultData?: ModalDefaultData\n}\n\nexport interface ModalProviderProps {\n children: ReactNode\n unmountDelay?: number\n fallback?: ReactNode\n routing?: ModalRoutingConfig\n}\n\ninterface ActiveModal {\n id: ModalId\n component: UntypedModalComponent\n isOpen: boolean\n data: unknown\n invocationId: number\n}\n\nconst DEFAULT_ROUTING = {\n strategy: 'hash',\n param: 'modal',\n historyMode: 'push'\n} as const\n\nexport const isBrowser = (): boolean => typeof window !== 'undefined'\n\nconst DEFAULT_UNMOUNT_DELAY = 200\n\nconst ModalActionsContext = createContext<ModalActionsValue | null>(null)\nconst ModalActiveContext = createContext<ReadonlyMap<ModalId, ActiveModal>>(new Map())\n\nfunction isShallowEqual(a: unknown, b: unknown): boolean {\n if (Object.is(a, b)) return true\n if (typeof a !== 'object' || a === null || typeof b !== 'object' || b === null) return false\n if (a instanceof Date && b instanceof Date) return a.getTime() === b.getTime()\n if (a instanceof RegExp && b instanceof RegExp) return a.source === b.source && a.flags === b.flags\n if (a instanceof Map || a instanceof Set || b instanceof Map || b instanceof Set) return false\n const keysA = Object.keys(a)\n const keysB = Object.keys(b)\n if (keysA.length !== keysB.length) return false\n return keysA.every(\n k =>\n Object.prototype.hasOwnProperty.call(b, k) &&\n (a as Record<string, unknown>)[k] === (b as Record<string, unknown>)[k]\n )\n}\n\nfunction useStableRegistrations<T extends ModalId | readonly ModalRegistration[]>(\n idOrModals: T\n): T {\n const ref = useRef(idOrModals)\n if (\n Array.isArray(idOrModals) &&\n Array.isArray(ref.current) &&\n ref.current.length === idOrModals.length &&\n ref.current.every((item, i) => item.id === idOrModals[i].id && item.component === idOrModals[i].component)\n ) {\n return ref.current as T\n }\n ref.current = idOrModals\n return idOrModals\n}\n\nfunction getHashParts(rawHash: string): { anchor: string; params: URLSearchParams } {\n const hash = rawHash.replace(/^#/, '')\n const [anchor, query] = hash.includes('?')\n ? hash.split('?', 2)\n : hash.includes('=')\n ? ['', hash]\n : [hash, '']\n return { anchor, params: new URLSearchParams(query) }\n}\n\nfunction formatHash(anchor: string, params: URLSearchParams): string {\n const qs = params.toString()\n return qs ? `#${anchor ? `${anchor}?` : ''}${qs}` : (anchor ? `#${anchor}` : '')\n}\n\nfunction getUrlModalId(config?: ModalRoutingConfig | null): string | null {\n if (!isBrowser() || !config || config.enabled === false) return null\n const param = config.param ?? DEFAULT_ROUTING.param\n return (config.strategy ?? DEFAULT_ROUTING.strategy) === 'query'\n ? new URLSearchParams(window.location.search).get(param)\n : getHashParts(window.location.hash).params.get(param)\n}\n\nfunction setUrlModalId(\n id: string | null,\n config?: ModalRoutingConfig | null,\n forceMode?: ModalHistoryMode\n) {\n if (!isBrowser() || !config || config.enabled === false) return\n if (getUrlModalId(config) === id) return\n\n const mode = forceMode ?? config.historyMode ?? DEFAULT_ROUTING.historyMode\n const param = config.param ?? DEFAULT_ROUTING.param\n const url = new URL(window.location.href)\n const updateParams = (sp: URLSearchParams) => (id ? sp.set(param, id) : sp.delete(param))\n\n if ((config.strategy ?? DEFAULT_ROUTING.strategy) === 'query') {\n updateParams(url.searchParams)\n } else {\n const { anchor, params } = getHashParts(window.location.hash)\n updateParams(params)\n url.hash = formatHash(anchor, params)\n }\n\n window.history[mode === 'push' ? 'pushState' : 'replaceState'](\n window.history.state,\n '',\n url.pathname + url.search + url.hash\n )\n}\n\nfunction getDefaultData(defaultData: ModalDefaultData | undefined, id: ModalId): unknown {\n if (!defaultData) return undefined\n return typeof defaultData === 'function'\n ? defaultData(id)\n : Object.prototype.hasOwnProperty.call(defaultData, id)\n ? (defaultData as Record<string, unknown>)[id]\n : undefined\n}\n\n/**\n * Root provider hosting the modal registry, URL routing, lifecycle actions, and slot renderer.\n */\nexport function ModalProvider({\n children,\n unmountDelay = DEFAULT_UNMOUNT_DELAY,\n fallback = null,\n routing\n}: ModalProviderProps) {\n // Registry stack allows multiple components to register the same modal ID safely\n const registryRef = useRef<Map<ModalId, UntypedModalComponent[]>>(new Map())\n\n // Active modals currently open or animating out\n const [activeModals, setActiveModals] = useState<ReadonlyMap<ModalId, ActiveModal>>(new Map())\n const activeModalsRef = useRef(activeModals)\n\n const updateActiveModals = useCallback(\n (updater: (prev: ReadonlyMap<ModalId, ActiveModal>) => ReadonlyMap<ModalId, ActiveModal>) => {\n const next = updater(activeModalsRef.current)\n activeModalsRef.current = next\n setActiveModals(next)\n return next\n },\n []\n )\n\n const pendingRef = useRef<\n Map<ModalId, { promise: Promise<unknown>; resolve: (value: unknown) => void; invocationId: number }>\n >(new Map())\n const timersRef = useRef<Map<ModalId, ReturnType<typeof setTimeout>>>(new Map())\n const invocationCounterRef = useRef(0)\n\n const resolvedRouting = useMemo(\n () =>\n routing\n ? {\n ...DEFAULT_ROUTING,\n ...routing,\n enabled: routing.enabled !== false\n }\n : null,\n [\n routing?.enabled,\n routing?.strategy,\n routing?.param,\n routing?.historyMode,\n routing?.defaultData\n ]\n )\n const routingRef = useRef(resolvedRouting)\n routingRef.current = resolvedRouting\n\n useEffect(() => {\n return () => {\n timersRef.current.forEach(clearTimeout)\n timersRef.current.clear()\n pendingRef.current.forEach(p => p.resolve(undefined))\n pendingRef.current.clear()\n }\n }, [])\n\n const getComponent = useCallback((id: ModalId) => {\n const stack = registryRef.current.get(id)\n return stack?.[stack.length - 1]\n }, [])\n\n const scheduleUnmount = useCallback(\n (id: ModalId, invocationId: number) => {\n const existing = timersRef.current.get(id)\n if (existing) clearTimeout(existing)\n\n let timer: ReturnType<typeof setTimeout>\n timer = setTimeout(() => {\n if (timersRef.current.get(id) === timer) {\n timersRef.current.delete(id)\n }\n updateActiveModals(prev => {\n const item = prev.get(id)\n if (!item || item.isOpen || item.invocationId !== invocationId) return prev\n const next = new Map(prev)\n next.delete(id)\n return next\n })\n }, unmountDelay)\n timersRef.current.set(id, timer)\n },\n [unmountDelay, updateActiveModals]\n )\n\n const closeModal = useCallback(\n (id: ModalId, invocationId?: number, result?: unknown, syncUrl = true) => {\n const active = activeModalsRef.current.get(id)\n if (!active || !active.isOpen) return\n if (invocationId !== undefined && active.invocationId !== invocationId) return\n\n const pending = pendingRef.current.get(id)\n if (pending && (invocationId === undefined || pending.invocationId === invocationId)) {\n pendingRef.current.delete(id)\n pending.resolve(result)\n }\n\n if (syncUrl && routingRef.current?.enabled) {\n if (getUrlModalId(routingRef.current) === String(id)) {\n setUrlModalId(null, routingRef.current, 'replace')\n }\n }\n\n let didClose = false\n updateActiveModals(prev => {\n const current = prev.get(id)\n if (!current || !current.isOpen) return prev\n if (invocationId !== undefined && current.invocationId !== invocationId) return prev\n didClose = true\n const next = new Map(prev)\n if (unmountDelay <= 0) {\n next.delete(id)\n } else {\n next.set(id, { ...current, isOpen: false })\n }\n return next\n })\n\n if (didClose && unmountDelay > 0) {\n scheduleUnmount(id, active.invocationId)\n }\n },\n [scheduleUnmount, unmountDelay, updateActiveModals]\n )\n\n const openModal = useCallback(\n (id: ModalId, data?: unknown, syncUrl = true): Promise<unknown> => {\n const comp = getComponent(id)\n if (!comp) {\n return Promise.reject(\n new Error(`[react-modal-registry] Modal \"${String(id)}\" is not registered.`)\n )\n }\n\n const current = activeModalsRef.current.get(id)\n const existing = pendingRef.current.get(id)\n\n if (\n current?.isOpen &&\n current.component === comp &&\n isShallowEqual(current.data, data) &&\n existing\n ) {\n return existing.promise\n }\n\n if (syncUrl && routingRef.current?.enabled) {\n setUrlModalId(String(id), routingRef.current)\n }\n\n if (existing) {\n pendingRef.current.delete(id)\n existing.resolve(undefined)\n }\n\n const timer = timersRef.current.get(id)\n if (timer) {\n clearTimeout(timer)\n timersRef.current.delete(id)\n }\n\n const invocationId = ++invocationCounterRef.current\n let resolver!: (val: unknown) => void\n const promise = new Promise<unknown>(resolve => {\n resolver = resolve\n })\n\n pendingRef.current.set(id, { promise, resolve: resolver, invocationId })\n\n updateActiveModals(prev => {\n const next = new Map(prev)\n next.set(id, { id, component: comp, isOpen: true, data, invocationId })\n return next\n })\n\n return promise\n },\n [getComponent, updateActiveModals]\n )\n\n const open = useCallback(\n <TId extends ModalId, TResult = ModalResult<TId>>(\n id: TId,\n ...args: ModalDataArg<ModalData<TId>>\n ): Promise<CanonicalResult<TId, TResult> | undefined> => {\n return openModal(id, args[0], true) as Promise<CanonicalResult<TId, TResult> | undefined>\n },\n [openModal]\n )\n\n const close = useCallback(\n <TId extends ModalId>(id: TId, result?: ModalResult<TId>) => {\n closeModal(id, undefined, result, true)\n },\n [closeModal]\n )\n\n const closeAll = useCallback(() => {\n const pendings = Array.from(pendingRef.current.values())\n pendingRef.current.clear()\n\n if (routingRef.current?.enabled) {\n setUrlModalId(null, routingRef.current, 'replace')\n }\n\n if (unmountDelay <= 0) {\n for (const timer of timersRef.current.values()) clearTimeout(timer)\n timersRef.current.clear()\n updateActiveModals(() => new Map())\n } else {\n for (const [id, active] of activeModalsRef.current) {\n if (active.isOpen) {\n scheduleUnmount(id, active.invocationId)\n }\n }\n\n updateActiveModals(prev => {\n let changed = false\n const next = new Map(prev)\n for (const [id, active] of prev) {\n if (active.isOpen) {\n next.set(id, { ...active, isOpen: false })\n changed = true\n }\n }\n return changed ? next : prev\n })\n }\n\n pendings.forEach(p => p.resolve(undefined))\n }, [scheduleUnmount, unmountDelay, updateActiveModals])\n\n const isOpen = useCallback(\n (id: ModalId) => activeModalsRef.current.get(id)?.isOpen ?? false,\n []\n )\n\n const register: ModalActionsValue['register'] = useCallback(\n ((\n idOrModals: ModalId | readonly ModalRegistration[],\n component?: UntypedModalComponent\n ) => {\n const items: readonly { id: ModalId; component: UntypedModalComponent }[] =\n Array.isArray(idOrModals)\n ? (idOrModals as unknown as readonly { id: ModalId; component: UntypedModalComponent }[])\n : component\n ? [{ id: idOrModals as ModalId, component }]\n : []\n\n const urlId = routingRef.current?.enabled ? getUrlModalId(routingRef.current) : null\n\n for (const item of items) {\n let stack = registryRef.current.get(item.id)\n if (!stack) {\n stack = []\n registryRef.current.set(item.id, stack)\n }\n stack.push(item.component)\n\n // If URL routing is active and specifies this modal, open it immediately\n if (urlId && urlId === String(item.id) && !activeModalsRef.current.get(item.id)?.isOpen) {\n const data = getDefaultData(routingRef.current?.defaultData, item.id)\n openModal(item.id, data, false)\n }\n }\n }) as ModalActionsValue['register'],\n [openModal]\n )\n\n const unregister = useCallback(\n (idOrIds: ModalId | readonly ModalId[], targetComponent?: UntypedModalComponent) => {\n const ids = Array.isArray(idOrIds) ? idOrIds : [idOrIds]\n\n for (const id of ids) {\n const stack = registryRef.current.get(id)\n if (!stack) continue\n\n if (targetComponent) {\n const idx = stack.lastIndexOf(targetComponent)\n if (idx !== -1) {\n stack.splice(idx, 1)\n }\n if (stack.length > 0) {\n continue\n }\n }\n\n registryRef.current.delete(id)\n\n const timer = timersRef.current.get(id)\n if (timer) {\n clearTimeout(timer)\n timersRef.current.delete(id)\n }\n\n const pending = pendingRef.current.get(id)\n if (pending) {\n pendingRef.current.delete(id)\n pending.resolve(undefined)\n }\n\n if (routingRef.current?.enabled && getUrlModalId(routingRef.current) === String(id)) {\n setUrlModalId(null, routingRef.current, 'replace')\n }\n\n updateActiveModals(prev => {\n if (!prev.has(id)) return prev\n const next = new Map(prev)\n next.delete(id)\n return next\n })\n }\n },\n [updateActiveModals]\n )\n\n // Two-way URL synchronization listener\n useEffect(() => {\n if (!resolvedRouting || !resolvedRouting.enabled || !isBrowser()) return\n\n const syncFromUrl = () => {\n const urlId = getUrlModalId(routingRef.current)\n const currentActive = activeModalsRef.current\n\n // Dismiss active modal that does not match urlId\n for (const [id, active] of currentActive) {\n if (String(id) !== urlId && active.isOpen) {\n closeModal(id, undefined, undefined, false)\n }\n }\n\n if (urlId) {\n const modalId = urlId as ModalId\n if (!currentActive.get(modalId)?.isOpen && getComponent(modalId)) {\n openModal(modalId, getDefaultData(routingRef.current?.defaultData, modalId), false)\n }\n }\n }\n\n syncFromUrl()\n\n window.addEventListener('popstate', syncFromUrl)\n window.addEventListener('hashchange', syncFromUrl)\n\n return () => {\n window.removeEventListener('popstate', syncFromUrl)\n window.removeEventListener('hashchange', syncFromUrl)\n }\n }, [\n resolvedRouting,\n getComponent,\n openModal,\n closeModal\n ])\n\n const actions = useMemo<ModalActionsValue>(\n () => ({\n register,\n unregister,\n open,\n close,\n closeAll,\n isOpen\n }),\n [register, unregister, open, close, closeAll, isOpen]\n )\n\n return (\n <ModalActionsContext.Provider value={actions}>\n <ModalActiveContext.Provider value={activeModals}>\n {children}\n <ModalRenderer\n activeModals={activeModals}\n close={closeModal}\n fallback={fallback}\n />\n </ModalActiveContext.Provider>\n </ModalActionsContext.Provider>\n )\n}\n\nfunction ModalRenderer({\n activeModals,\n close,\n fallback\n}: {\n activeModals: ReadonlyMap<ModalId, ActiveModal>\n close: (id: ModalId, invocationId: number, result?: unknown, syncUrl?: boolean) => void\n fallback?: ReactNode\n}) {\n if (activeModals.size === 0) return null\n\n return (\n <>\n {Array.from(activeModals.values(), active => (\n <ModalSlotItem\n key={`${String(active.id)}:${active.invocationId}`}\n active={active}\n onClose={close}\n fallback={fallback}\n />\n ))}\n </>\n )\n}\n\nconst ModalSlotItem = memo(function ModalSlotItem({\n active,\n onClose,\n fallback\n}: {\n active: ActiveModal\n onClose: (id: ModalId, invocationId: number, result?: unknown, syncUrl?: boolean) => void\n fallback?: ReactNode\n}) {\n const handleClose = useCallback(\n (result?: unknown) => onClose(active.id, active.invocationId, result, true),\n [onClose, active.id, active.invocationId]\n )\n\n const Component = active.component\n\n return (\n <Suspense fallback={fallback ?? null}>\n <Component\n isOpen={active.isOpen}\n data={active.data as never}\n close={handleClose}\n />\n </Suspense>\n )\n})\n\n/**\n * Registers one or more modals for the lifetime of the calling component.\n */\nexport function useRegisterModal<TId extends ModalId>(\n id: TId,\n component: RegisteredComponent<TId>\n): void\nexport function useRegisterModal<const TIds extends readonly ModalId[]>(modals: {\n [K in keyof TIds]: ModalRegistration<TIds[K]>\n} | readonly ModalRegistration[]): void\nexport function useRegisterModal<TId extends ModalId>(\n idOrModals: TId | readonly ModalRegistration[],\n component?: RegisteredComponent<TId>\n): void {\n const { register, unregister } = useModalContext()\n const stableIdOrModals = useStableRegistrations(idOrModals)\n\n useEffect(() => {\n if (Array.isArray(stableIdOrModals)) {\n register(stableIdOrModals)\n return () => {\n for (const item of stableIdOrModals) {\n unregister(item.id, item.component as unknown as UntypedModalComponent)\n }\n }\n }\n\n if (component) {\n register(stableIdOrModals as TId, component)\n return () => {\n unregister(stableIdOrModals as TId, component as unknown as UntypedModalComponent)\n }\n }\n\n return undefined\n }, [stableIdOrModals, component, register, unregister])\n}\n\n/**\n * Declarative component for mounting static modal batches at application roots.\n */\nexport function ModalRegistry<const TIds extends readonly ModalId[]>({\n modals\n}: {\n modals: {\n [K in keyof TIds]: ModalRegistration<TIds[K]>\n } | readonly ModalRegistration[]\n}) {\n useRegisterModal(modals)\n return null\n}\n\n/**\n * Returns typed controls (`open`, `close`, `isOpen`) for a modal ID.\n */\nexport function useModal<\n TId extends ModalId,\n TResult = ModalResult<TId>\n>(\n id: TId\n): UseModalReturn<ModalData<TId>, CanonicalResult<TId, TResult>> {\n const { open, close, isOpen } = useModalContext()\n\n return useMemo<UseModalReturn<ModalData<TId>, CanonicalResult<TId, TResult>>>(\n () => ({\n open: (...args: ModalDataArg<ModalData<TId>>) =>\n open<TId, TResult>(id, ...args),\n close: (result?: CanonicalResult<TId, TResult>) =>\n close(id, result as ModalResult<TId>),\n isOpen: () => isOpen(id)\n }),\n [open, close, isOpen, id]\n )\n}\n\n/**\n * Reactively subscribes to whether a modal is currently open.\n */\nexport function useIsModalOpen(id: ModalId): boolean {\n const active = useContext(ModalActiveContext)\n return active.get(id)?.isOpen ?? false\n}\n\nexport function useCloseAll(): () => void {\n return useModalContext().closeAll\n}\n\nfunction useModalContext(): ModalActionsValue {\n const context = useContext(ModalActionsContext)\n if (!context) {\n throw new Error('useModalContext must be used within <ModalProvider>')\n }\n return context\n}\n"],"mappings":"aAEA,OACE,iBAAAA,EACA,QAAAC,EACA,YAAAC,GACA,eAAAC,EACA,cAAAC,EACA,aAAAC,EACA,WAAAC,EACA,UAAAC,EACA,YAAAC,OAGK,QAkmBD,OAwBF,YAAAC,GAtBI,OAAAC,EAFF,QAAAC,OAAA,oBA9dN,IAAMC,EAAkB,CACtB,SAAU,OACV,MAAO,QACP,YAAa,MACf,EAEaC,EAAY,IAAe,OAAO,OAAW,IAEpDC,GAAwB,IAExBC,EAAsBf,EAAwC,IAAI,EAClEgB,EAAqBhB,EAAiD,IAAI,GAAK,EAErF,SAASiB,GAAeC,EAAYC,EAAqB,CACvD,GAAI,OAAO,GAAGD,EAAGC,CAAC,EAAG,MAAO,GAC5B,GAAI,OAAOD,GAAM,UAAYA,IAAM,MAAQ,OAAOC,GAAM,UAAYA,IAAM,KAAM,MAAO,GACvF,GAAID,aAAa,MAAQC,aAAa,KAAM,OAAOD,EAAE,QAAQ,IAAMC,EAAE,QAAQ,EAC7E,GAAID,aAAa,QAAUC,aAAa,OAAQ,OAAOD,EAAE,SAAWC,EAAE,QAAUD,EAAE,QAAUC,EAAE,MAC9F,GAAID,aAAa,KAAOA,aAAa,KAAOC,aAAa,KAAOA,aAAa,IAAK,MAAO,GACzF,IAAMC,EAAQ,OAAO,KAAKF,CAAC,EACrBG,EAAQ,OAAO,KAAKF,CAAC,EAC3B,OAAIC,EAAM,SAAWC,EAAM,OAAe,GACnCD,EAAM,MACXE,GACE,OAAO,UAAU,eAAe,KAAKH,EAAGG,CAAC,GACxCJ,EAA8BI,CAAC,IAAOH,EAA8BG,CAAC,CAC1E,CACF,CAEA,SAASC,GACPC,EACG,CACH,IAAMC,EAAMlB,EAAOiB,CAAU,EAC7B,OACE,MAAM,QAAQA,CAAU,GACxB,MAAM,QAAQC,EAAI,OAAO,GACzBA,EAAI,QAAQ,SAAWD,EAAW,QAClCC,EAAI,QAAQ,MAAM,CAACC,EAAMC,IAAMD,EAAK,KAAOF,EAAWG,CAAC,EAAE,IAAMD,EAAK,YAAcF,EAAWG,CAAC,EAAE,SAAS,EAElGF,EAAI,SAEbA,EAAI,QAAUD,EACPA,EACT,CAEA,SAASI,EAAaC,EAA8D,CAClF,IAAMC,EAAOD,EAAQ,QAAQ,KAAM,EAAE,EAC/B,CAACE,EAAQC,CAAK,EAAIF,EAAK,SAAS,GAAG,EACrCA,EAAK,MAAM,IAAK,CAAC,EACjBA,EAAK,SAAS,GAAG,EACjB,CAAC,GAAIA,CAAI,EACT,CAACA,EAAM,EAAE,EACb,MAAO,CAAE,OAAAC,EAAQ,OAAQ,IAAI,gBAAgBC,CAAK,CAAE,CACtD,CAEA,SAASC,GAAWF,EAAgBG,EAAiC,CACnE,IAAMC,EAAKD,EAAO,SAAS,EAC3B,OAAOC,EAAK,IAAIJ,EAAS,GAAGA,CAAM,IAAM,EAAE,GAAGI,CAAE,GAAMJ,EAAS,IAAIA,CAAM,GAAK,EAC/E,CAEA,SAASK,EAAcC,EAAmD,CACxE,GAAI,CAACxB,EAAU,GAAK,CAACwB,GAAUA,EAAO,UAAY,GAAO,OAAO,KAChE,IAAMC,EAAQD,EAAO,OAASzB,EAAgB,MAC9C,OAAQyB,EAAO,UAAYzB,EAAgB,YAAc,QACrD,IAAI,gBAAgB,OAAO,SAAS,MAAM,EAAE,IAAI0B,CAAK,EACrDV,EAAa,OAAO,SAAS,IAAI,EAAE,OAAO,IAAIU,CAAK,CACzD,CAEA,SAASC,EACPC,EACAH,EACAI,EACA,CAEA,GADI,CAAC5B,EAAU,GAAK,CAACwB,GAAUA,EAAO,UAAY,IAC9CD,EAAcC,CAAM,IAAMG,EAAI,OAElC,IAAME,EAAOD,GAAaJ,EAAO,aAAezB,EAAgB,YAC1D0B,EAAQD,EAAO,OAASzB,EAAgB,MACxC+B,EAAM,IAAI,IAAI,OAAO,SAAS,IAAI,EAClCC,EAAgBC,GAAyBL,EAAKK,EAAG,IAAIP,EAAOE,CAAE,EAAIK,EAAG,OAAOP,CAAK,EAEvF,IAAKD,EAAO,UAAYzB,EAAgB,YAAc,QACpDgC,EAAaD,EAAI,YAAY,MACxB,CACL,GAAM,CAAE,OAAAZ,EAAQ,OAAAG,CAAO,EAAIN,EAAa,OAAO,SAAS,IAAI,EAC5DgB,EAAaV,CAAM,EACnBS,EAAI,KAAOV,GAAWF,EAAQG,CAAM,CACtC,CAEA,OAAO,QAAQQ,IAAS,OAAS,YAAc,cAAc,EAC3D,OAAO,QAAQ,MACf,GACAC,EAAI,SAAWA,EAAI,OAASA,EAAI,IAClC,CACF,CAEA,SAASG,EAAeC,EAA2CP,EAAsB,CACvF,GAAKO,EACL,OAAO,OAAOA,GAAgB,WAC1BA,EAAYP,CAAE,EACd,OAAO,UAAU,eAAe,KAAKO,EAAaP,CAAE,EACnDO,EAAwCP,CAAE,EAC3C,MACN,CAKO,SAASQ,GAAc,CAC5B,SAAAC,EACA,aAAAC,EAAepC,GACf,SAAAqC,EAAW,KACX,QAAAC,CACF,EAAuB,CAErB,IAAMC,EAAc9C,EAA8C,IAAI,GAAK,EAGrE,CAAC+C,EAAcC,CAAe,EAAI/C,GAA4C,IAAI,GAAK,EACvFgD,EAAkBjD,EAAO+C,CAAY,EAErCG,EAAqBtD,EACxBuD,GAA4F,CAC3F,IAAMC,EAAOD,EAAQF,EAAgB,OAAO,EAC5C,OAAAA,EAAgB,QAAUG,EAC1BJ,EAAgBI,CAAI,EACbA,CACT,EACA,CAAC,CACH,EAEMC,EAAarD,EAEjB,IAAI,GAAK,EACLsD,EAAYtD,EAAoD,IAAI,GAAK,EACzEuD,EAAuBvD,EAAO,CAAC,EAE/BwD,EAAkBzD,EACtB,IACE8C,EACI,CACE,GAAGxC,EACH,GAAGwC,EACH,QAASA,EAAQ,UAAY,EAC/B,EACA,KACN,CACEA,GAAS,QACTA,GAAS,SACTA,GAAS,MACTA,GAAS,YACTA,GAAS,WACX,CACF,EACMY,EAAazD,EAAOwD,CAAe,EACzCC,EAAW,QAAUD,EAErB1D,EAAU,IACD,IAAM,CACXwD,EAAU,QAAQ,QAAQ,YAAY,EACtCA,EAAU,QAAQ,MAAM,EACxBD,EAAW,QAAQ,QAAQK,GAAKA,EAAE,QAAQ,MAAS,CAAC,EACpDL,EAAW,QAAQ,MAAM,CAC3B,EACC,CAAC,CAAC,EAEL,IAAMM,EAAe/D,EAAaqC,GAAgB,CAChD,IAAM2B,EAAQd,EAAY,QAAQ,IAAIb,CAAE,EACxC,OAAO2B,IAAQA,EAAM,OAAS,CAAC,CACjC,EAAG,CAAC,CAAC,EAECC,EAAkBjE,EACtB,CAACqC,EAAa6B,IAAyB,CACrC,IAAMC,EAAWT,EAAU,QAAQ,IAAIrB,CAAE,EACrC8B,GAAU,aAAaA,CAAQ,EAEnC,IAAIC,EACJA,EAAQ,WAAW,IAAM,CACnBV,EAAU,QAAQ,IAAIrB,CAAE,IAAM+B,GAChCV,EAAU,QAAQ,OAAOrB,CAAE,EAE7BiB,EAAmBe,GAAQ,CACzB,IAAM9C,EAAO8C,EAAK,IAAIhC,CAAE,EACxB,GAAI,CAACd,GAAQA,EAAK,QAAUA,EAAK,eAAiB2C,EAAc,OAAOG,EACvE,IAAMb,EAAO,IAAI,IAAIa,CAAI,EACzB,OAAAb,EAAK,OAAOnB,CAAE,EACPmB,CACT,CAAC,CACH,EAAGT,CAAY,EACfW,EAAU,QAAQ,IAAIrB,EAAI+B,CAAK,CACjC,EACA,CAACrB,EAAcO,CAAkB,CACnC,EAEMgB,EAAatE,EACjB,CAACqC,EAAa6B,EAAuBK,EAAkBC,EAAU,KAAS,CACxE,IAAMC,EAASpB,EAAgB,QAAQ,IAAIhB,CAAE,EAE7C,GADI,CAACoC,GAAU,CAACA,EAAO,QACnBP,IAAiB,QAAaO,EAAO,eAAiBP,EAAc,OAExE,IAAMQ,EAAUjB,EAAW,QAAQ,IAAIpB,CAAE,EACrCqC,IAAYR,IAAiB,QAAaQ,EAAQ,eAAiBR,KACrET,EAAW,QAAQ,OAAOpB,CAAE,EAC5BqC,EAAQ,QAAQH,CAAM,GAGpBC,GAAWX,EAAW,SAAS,SAC7B5B,EAAc4B,EAAW,OAAO,IAAM,OAAOxB,CAAE,GACjDD,EAAc,KAAMyB,EAAW,QAAS,SAAS,EAIrD,IAAIc,EAAW,GACfrB,EAAmBe,GAAQ,CACzB,IAAMO,EAAUP,EAAK,IAAIhC,CAAE,EAE3B,GADI,CAACuC,GAAW,CAACA,EAAQ,QACrBV,IAAiB,QAAaU,EAAQ,eAAiBV,EAAc,OAAOG,EAChFM,EAAW,GACX,IAAMnB,EAAO,IAAI,IAAIa,CAAI,EACzB,OAAItB,GAAgB,EAClBS,EAAK,OAAOnB,CAAE,EAEdmB,EAAK,IAAInB,EAAI,CAAE,GAAGuC,EAAS,OAAQ,EAAM,CAAC,EAErCpB,CACT,CAAC,EAEGmB,GAAY5B,EAAe,GAC7BkB,EAAgB5B,EAAIoC,EAAO,YAAY,CAE3C,EACA,CAACR,EAAiBlB,EAAcO,CAAkB,CACpD,EAEMuB,EAAY7E,EAChB,CAACqC,EAAayC,EAAgBN,EAAU,KAA2B,CACjE,IAAMO,EAAOhB,EAAa1B,CAAE,EAC5B,GAAI,CAAC0C,EACH,OAAO,QAAQ,OACb,IAAI,MAAM,iCAAiC,OAAO1C,CAAE,CAAC,sBAAsB,CAC7E,EAGF,IAAMuC,EAAUvB,EAAgB,QAAQ,IAAIhB,CAAE,EACxC8B,EAAWV,EAAW,QAAQ,IAAIpB,CAAE,EAE1C,GACEuC,GAAS,QACTA,EAAQ,YAAcG,GACtBjE,GAAe8D,EAAQ,KAAME,CAAI,GACjCX,EAEA,OAAOA,EAAS,QAGdK,GAAWX,EAAW,SAAS,SACjCzB,EAAc,OAAOC,CAAE,EAAGwB,EAAW,OAAO,EAG1CM,IACFV,EAAW,QAAQ,OAAOpB,CAAE,EAC5B8B,EAAS,QAAQ,MAAS,GAG5B,IAAMC,EAAQV,EAAU,QAAQ,IAAIrB,CAAE,EAClC+B,IACF,aAAaA,CAAK,EAClBV,EAAU,QAAQ,OAAOrB,CAAE,GAG7B,IAAM6B,EAAe,EAAEP,EAAqB,QACxCqB,EACEC,EAAU,IAAI,QAAiBC,GAAW,CAC9CF,EAAWE,CACb,CAAC,EAED,OAAAzB,EAAW,QAAQ,IAAIpB,EAAI,CAAE,QAAA4C,EAAS,QAASD,EAAU,aAAAd,CAAa,CAAC,EAEvEZ,EAAmBe,GAAQ,CACzB,IAAMb,EAAO,IAAI,IAAIa,CAAI,EACzB,OAAAb,EAAK,IAAInB,EAAI,CAAE,GAAAA,EAAI,UAAW0C,EAAM,OAAQ,GAAM,KAAAD,EAAM,aAAAZ,CAAa,CAAC,EAC/DV,CACT,CAAC,EAEMyB,CACT,EACA,CAAClB,EAAcT,CAAkB,CACnC,EAEM6B,EAAOnF,EACX,CACEqC,KACG+C,IAEIP,EAAUxC,EAAI+C,EAAK,CAAC,EAAG,EAAI,EAEpC,CAACP,CAAS,CACZ,EAEMQ,EAAQrF,EACZ,CAAsBqC,EAASkC,IAA8B,CAC3DD,EAAWjC,EAAI,OAAWkC,EAAQ,EAAI,CACxC,EACA,CAACD,CAAU,CACb,EAEMgB,EAAWtF,EAAY,IAAM,CACjC,IAAMuF,EAAW,MAAM,KAAK9B,EAAW,QAAQ,OAAO,CAAC,EAOvD,GANAA,EAAW,QAAQ,MAAM,EAErBI,EAAW,SAAS,SACtBzB,EAAc,KAAMyB,EAAW,QAAS,SAAS,EAG/Cd,GAAgB,EAAG,CACrB,QAAWqB,KAASV,EAAU,QAAQ,OAAO,EAAG,aAAaU,CAAK,EAClEV,EAAU,QAAQ,MAAM,EACxBJ,EAAmB,IAAM,IAAI,GAAK,CACpC,KAAO,CACL,OAAW,CAACjB,EAAIoC,CAAM,IAAKpB,EAAgB,QACrCoB,EAAO,QACTR,EAAgB5B,EAAIoC,EAAO,YAAY,EAI3CnB,EAAmBe,GAAQ,CACzB,IAAImB,EAAU,GACRhC,EAAO,IAAI,IAAIa,CAAI,EACzB,OAAW,CAAChC,EAAIoC,CAAM,IAAKJ,EACrBI,EAAO,SACTjB,EAAK,IAAInB,EAAI,CAAE,GAAGoC,EAAQ,OAAQ,EAAM,CAAC,EACzCe,EAAU,IAGd,OAAOA,EAAUhC,EAAOa,CAC1B,CAAC,CACH,CAEAkB,EAAS,QAAQzB,GAAKA,EAAE,QAAQ,MAAS,CAAC,CAC5C,EAAG,CAACG,EAAiBlB,EAAcO,CAAkB,CAAC,EAEhDmC,EAASzF,EACZqC,GAAgBgB,EAAgB,QAAQ,IAAIhB,CAAE,GAAG,QAAU,GAC5D,CAAC,CACH,EAEMqD,EAA0C1F,GAC7C,CACCqB,EACAsE,IACG,CACH,IAAMC,EACJ,MAAM,QAAQvE,CAAU,EACnBA,EACDsE,EACA,CAAC,CAAE,GAAItE,EAAuB,UAAAsE,CAAU,CAAC,EACzC,CAAC,EAEDE,EAAQhC,EAAW,SAAS,QAAU5B,EAAc4B,EAAW,OAAO,EAAI,KAEhF,QAAWtC,KAAQqE,EAAO,CACxB,IAAI5B,EAAQd,EAAY,QAAQ,IAAI3B,EAAK,EAAE,EAQ3C,GAPKyC,IACHA,EAAQ,CAAC,EACTd,EAAY,QAAQ,IAAI3B,EAAK,GAAIyC,CAAK,GAExCA,EAAM,KAAKzC,EAAK,SAAS,EAGrBsE,GAASA,IAAU,OAAOtE,EAAK,EAAE,GAAK,CAAC8B,EAAgB,QAAQ,IAAI9B,EAAK,EAAE,GAAG,OAAQ,CACvF,IAAMuD,EAAOnC,EAAekB,EAAW,SAAS,YAAatC,EAAK,EAAE,EACpEsD,EAAUtD,EAAK,GAAIuD,EAAM,EAAK,CAChC,CACF,CACF,GACA,CAACD,CAAS,CACZ,EAEMiB,EAAa9F,EACjB,CAAC+F,EAAuCC,IAA4C,CAClF,IAAMC,EAAM,MAAM,QAAQF,CAAO,EAAIA,EAAU,CAACA,CAAO,EAEvD,QAAW1D,KAAM4D,EAAK,CACpB,IAAMjC,EAAQd,EAAY,QAAQ,IAAIb,CAAE,EACxC,GAAI,CAAC2B,EAAO,SAEZ,GAAIgC,EAAiB,CACnB,IAAME,EAAMlC,EAAM,YAAYgC,CAAe,EAI7C,GAHIE,IAAQ,IACVlC,EAAM,OAAOkC,EAAK,CAAC,EAEjBlC,EAAM,OAAS,EACjB,QAEJ,CAEAd,EAAY,QAAQ,OAAOb,CAAE,EAE7B,IAAM+B,EAAQV,EAAU,QAAQ,IAAIrB,CAAE,EAClC+B,IACF,aAAaA,CAAK,EAClBV,EAAU,QAAQ,OAAOrB,CAAE,GAG7B,IAAMqC,EAAUjB,EAAW,QAAQ,IAAIpB,CAAE,EACrCqC,IACFjB,EAAW,QAAQ,OAAOpB,CAAE,EAC5BqC,EAAQ,QAAQ,MAAS,GAGvBb,EAAW,SAAS,SAAW5B,EAAc4B,EAAW,OAAO,IAAM,OAAOxB,CAAE,GAChFD,EAAc,KAAMyB,EAAW,QAAS,SAAS,EAGnDP,EAAmBe,GAAQ,CACzB,GAAI,CAACA,EAAK,IAAIhC,CAAE,EAAG,OAAOgC,EAC1B,IAAMb,EAAO,IAAI,IAAIa,CAAI,EACzB,OAAAb,EAAK,OAAOnB,CAAE,EACPmB,CACT,CAAC,CACH,CACF,EACA,CAACF,CAAkB,CACrB,EAGApD,EAAU,IAAM,CACd,GAAI,CAAC0D,GAAmB,CAACA,EAAgB,SAAW,CAAClD,EAAU,EAAG,OAElE,IAAMyF,EAAc,IAAM,CACxB,IAAMN,EAAQ5D,EAAc4B,EAAW,OAAO,EACxCuC,EAAgB/C,EAAgB,QAGtC,OAAW,CAAChB,EAAIoC,CAAM,IAAK2B,EACrB,OAAO/D,CAAE,IAAMwD,GAASpB,EAAO,QACjCH,EAAWjC,EAAI,OAAW,OAAW,EAAK,EAI9C,GAAIwD,EAAO,CACT,IAAMQ,EAAUR,EACZ,CAACO,EAAc,IAAIC,CAAO,GAAG,QAAUtC,EAAasC,CAAO,GAC7DxB,EAAUwB,EAAS1D,EAAekB,EAAW,SAAS,YAAawC,CAAO,EAAG,EAAK,CAEtF,CACF,EAEA,OAAAF,EAAY,EAEZ,OAAO,iBAAiB,WAAYA,CAAW,EAC/C,OAAO,iBAAiB,aAAcA,CAAW,EAE1C,IAAM,CACX,OAAO,oBAAoB,WAAYA,CAAW,EAClD,OAAO,oBAAoB,aAAcA,CAAW,CACtD,CACF,EAAG,CACDvC,EACAG,EACAc,EACAP,CACF,CAAC,EAED,IAAMgC,EAAUnG,EACd,KAAO,CACL,SAAAuF,EACA,WAAAI,EACA,KAAAX,EACA,MAAAE,EACA,SAAAC,EACA,OAAAG,CACF,GACA,CAACC,EAAUI,EAAYX,EAAME,EAAOC,EAAUG,CAAM,CACtD,EAEA,OACElF,EAACK,EAAoB,SAApB,CAA6B,MAAO0F,EACnC,SAAA9F,GAACK,EAAmB,SAAnB,CAA4B,MAAOsC,EACjC,UAAAL,EACDvC,EAACgG,GAAA,CACC,aAAcpD,EACd,MAAOmB,EACP,SAAUtB,EACZ,GACF,EACF,CAEJ,CAEA,SAASuD,GAAc,CACrB,aAAApD,EACA,MAAAkC,EACA,SAAArC,CACF,EAIG,CACD,OAAIG,EAAa,OAAS,EAAU,KAGlC5C,EAAAD,GAAA,CACG,eAAM,KAAK6C,EAAa,OAAO,EAAGsB,GACjClE,EAACiG,GAAA,CAEC,OAAQ/B,EACR,QAASY,EACT,SAAUrC,GAHL,GAAG,OAAOyB,EAAO,EAAE,CAAC,IAAIA,EAAO,YAAY,EAIlD,CACD,EACH,CAEJ,CAEA,IAAM+B,GAAgB1G,EAAK,SAAuB,CAChD,OAAA2E,EACA,QAAAgC,EACA,SAAAzD,CACF,EAIG,CACD,IAAM0D,EAAc1G,EACjBuE,GAAqBkC,EAAQhC,EAAO,GAAIA,EAAO,aAAcF,EAAQ,EAAI,EAC1E,CAACkC,EAAShC,EAAO,GAAIA,EAAO,YAAY,CAC1C,EAEMkC,EAAYlC,EAAO,UAEzB,OACElE,EAACR,GAAA,CAAS,SAAUiD,GAAY,KAC9B,SAAAzC,EAACoG,EAAA,CACC,OAAQlC,EAAO,OACf,KAAMA,EAAO,KACb,MAAOiC,EACT,EACF,CAEJ,CAAC,EAYM,SAASE,GACdvF,EACAsE,EACM,CACN,GAAM,CAAE,SAAAD,EAAU,WAAAI,CAAW,EAAIe,EAAgB,EAC3CC,EAAmB1F,GAAuBC,CAAU,EAE1DnB,EAAU,IAAM,CACd,GAAI,MAAM,QAAQ4G,CAAgB,EAChC,OAAApB,EAASoB,CAAgB,EAClB,IAAM,CACX,QAAWvF,KAAQuF,EACjBhB,EAAWvE,EAAK,GAAIA,EAAK,SAA6C,CAE1E,EAGF,GAAIoE,EACF,OAAAD,EAASoB,EAAyBnB,CAAS,EACpC,IAAM,CACXG,EAAWgB,EAAyBnB,CAA6C,CACnF,CAIJ,EAAG,CAACmB,EAAkBnB,EAAWD,EAAUI,CAAU,CAAC,CACxD,CAKO,SAASiB,GAAqD,CACnE,OAAAC,CACF,EAIG,CACD,OAAAJ,GAAiBI,CAAM,EAChB,IACT,CAKO,SAASC,GAId5E,EAC+D,CAC/D,GAAM,CAAE,KAAA8C,EAAM,MAAAE,EAAO,OAAAI,CAAO,EAAIoB,EAAgB,EAEhD,OAAO1G,EACL,KAAO,CACL,KAAM,IAAIiF,IACRD,EAAmB9C,EAAI,GAAG+C,CAAI,EAChC,MAAQb,GACNc,EAAMhD,EAAIkC,CAA0B,EACtC,OAAQ,IAAMkB,EAAOpD,CAAE,CACzB,GACA,CAAC8C,EAAME,EAAOI,EAAQpD,CAAE,CAC1B,CACF,CAKO,SAAS6E,GAAe7E,EAAsB,CAEnD,OADepC,EAAWY,CAAkB,EAC9B,IAAIwB,CAAE,GAAG,QAAU,EACnC,CAEO,SAAS8E,IAA0B,CACxC,OAAON,EAAgB,EAAE,QAC3B,CAEA,SAASA,GAAqC,CAC5C,IAAMO,EAAUnH,EAAWW,CAAmB,EAC9C,GAAI,CAACwG,EACH,MAAM,IAAI,MAAM,qDAAqD,EAEvE,OAAOA,CACT","names":["createContext","memo","Suspense","useCallback","useContext","useEffect","useMemo","useRef","useState","Fragment","jsx","jsxs","DEFAULT_ROUTING","isBrowser","DEFAULT_UNMOUNT_DELAY","ModalActionsContext","ModalActiveContext","isShallowEqual","a","b","keysA","keysB","k","useStableRegistrations","idOrModals","ref","item","i","getHashParts","rawHash","hash","anchor","query","formatHash","params","qs","getUrlModalId","config","param","setUrlModalId","id","forceMode","mode","url","updateParams","sp","getDefaultData","defaultData","ModalProvider","children","unmountDelay","fallback","routing","registryRef","activeModals","setActiveModals","activeModalsRef","updateActiveModals","updater","next","pendingRef","timersRef","invocationCounterRef","resolvedRouting","routingRef","p","getComponent","stack","scheduleUnmount","invocationId","existing","timer","prev","closeModal","result","syncUrl","active","pending","didClose","current","openModal","data","comp","resolver","promise","resolve","open","args","close","closeAll","pendings","changed","isOpen","register","component","items","urlId","unregister","idOrIds","targetComponent","ids","idx","syncFromUrl","currentActive","modalId","actions","ModalRenderer","ModalSlotItem","onClose","handleClose","Component","useRegisterModal","useModalContext","stableIdOrModals","ModalRegistry","modals","useModal","useIsModalOpen","useCloseAll","context"]}
package/package.json CHANGED
@@ -1,16 +1,26 @@
1
1
  {
2
2
  "name": "react-modal-registry",
3
- "version": "0.1.0",
4
- "description": "Type-safe, headless modal registry and promise-based manager for React",
3
+ "version": "0.1.1",
4
+ "description": "Type-safe modal registry and promise-based dialog manager for React",
5
5
  "keywords": [
6
+ "react-modal",
7
+ "modal-manager",
8
+ "promise-modal",
9
+ "async-modal",
10
+ "imperative-modal",
11
+ "dialog-manager",
12
+ "modal-registry",
13
+ "use-modal",
14
+ "confirmation-dialog",
15
+ "confirmation-modal",
16
+ "alert-dialog",
17
+ "nice-modal",
18
+ "type-safe-modal",
19
+ "url-modal",
20
+ "deep-link",
21
+ "overlay-manager",
22
+ "react-dialog",
6
23
  "react",
7
- "modal",
8
- "modals",
9
- "dialog",
10
- "manager",
11
- "registry",
12
- "promise",
13
- "headless",
14
24
  "typescript"
15
25
  ],
16
26
  "author": "Saurav Hathi <https://github.com/sauravhathi>",
@@ -40,19 +50,22 @@
40
50
  "README.md",
41
51
  "LICENSE"
42
52
  ],
43
- "scripts": {
44
- "build": "tsup",
45
- "typecheck": "tsc --noEmit",
46
- "prepublishOnly": "pnpm run build"
47
- },
48
53
  "peerDependencies": {
49
54
  "react": ">=19.0.0",
50
55
  "react-dom": ">=19.0.0"
51
56
  },
52
57
  "devDependencies": {
58
+ "@testing-library/react": "^16.3.3",
53
59
  "@types/react": "^19.2.7",
54
60
  "@types/react-dom": "^19.2.3",
61
+ "happy-dom": "^20.14.5",
55
62
  "tsup": "^8.5.1",
56
- "typescript": "^6.0.2"
63
+ "typescript": "^6.0.2",
64
+ "vitest": "^5.0.3"
65
+ },
66
+ "scripts": {
67
+ "build": "tsup",
68
+ "test": "vitest run",
69
+ "typecheck": "tsc --noEmit"
57
70
  }
58
- }
71
+ }