modalio 0.9.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js ADDED
@@ -0,0 +1,874 @@
1
+ import { createContext, useEffect, useContext, useRef, useCallback, useMemo, useReducer } from 'react';
2
+ import { jsxs, jsx, Fragment } from 'react/jsx-runtime';
3
+
4
+ // src/adapters.ts
5
+ var radixUiDialog = (modal, options) => ({
6
+ open: modal.isOpen,
7
+ onOpenChange: (open) => {
8
+ if (!(open || options?.disableClose)) {
9
+ modal.dismiss();
10
+ }
11
+ }
12
+ });
13
+ var radixUiDialogContent = (modal, options) => ({
14
+ onAnimationEndCapture: () => {
15
+ modal.onAnimationEnd();
16
+ },
17
+ onEscapeKeyDown: (e) => {
18
+ if (options?.disableClose) {
19
+ e?.preventDefault();
20
+ } else {
21
+ modal.dismiss();
22
+ }
23
+ },
24
+ onPointerDownOutside: (e) => {
25
+ if (options?.disableClose) {
26
+ e?.preventDefault();
27
+ } else {
28
+ modal.dismiss();
29
+ }
30
+ }
31
+ });
32
+ var radixUiAlertDialog = (modal, options) => ({
33
+ open: modal.isOpen,
34
+ onOpenChange: (open) => {
35
+ if (!(open || options?.disableClose)) {
36
+ modal.dismiss();
37
+ }
38
+ }
39
+ });
40
+ var radixUiAlertDialogContent = (modal, options) => ({
41
+ onAnimationEndCapture: () => {
42
+ modal.onAnimationEnd();
43
+ },
44
+ onEscapeKeyDown: (e) => {
45
+ if (options?.disableClose) {
46
+ e?.preventDefault();
47
+ } else {
48
+ modal.dismiss();
49
+ }
50
+ },
51
+ onPointerDownOutside: (e) => {
52
+ if (options?.disableClose) {
53
+ e?.preventDefault();
54
+ } else {
55
+ modal.dismiss();
56
+ }
57
+ }
58
+ });
59
+ var shadcnUiDialog = (modal, options) => ({
60
+ open: modal.isOpen,
61
+ onOpenChange: (open) => {
62
+ if (!(open || options?.disableClose)) {
63
+ modal.dismiss();
64
+ }
65
+ },
66
+ onClose: () => {
67
+ if (!options?.disableClose) {
68
+ modal.dismiss();
69
+ }
70
+ },
71
+ onAnimationEndCapture: () => {
72
+ modal.onAnimationEnd();
73
+ }
74
+ });
75
+ var shadcnUiDialogContent = (modal, options) => ({
76
+ onAnimationEndCapture: () => {
77
+ modal.onAnimationEnd();
78
+ },
79
+ onEscapeKeyDown: (e) => {
80
+ if (options?.disableClose) {
81
+ e?.preventDefault();
82
+ } else {
83
+ modal.dismiss();
84
+ }
85
+ },
86
+ onPointerDownOutside: (e) => {
87
+ if (options?.disableClose) {
88
+ e?.preventDefault();
89
+ } else {
90
+ modal.dismiss();
91
+ }
92
+ }
93
+ });
94
+ var shadcnUiAlertDialog = (modal, options) => ({
95
+ open: modal.isOpen,
96
+ onOpenChange: (open) => {
97
+ if (!(open || options?.disableClose)) {
98
+ modal.dismiss();
99
+ }
100
+ },
101
+ onClose: () => {
102
+ if (!options?.disableClose) {
103
+ modal.dismiss();
104
+ }
105
+ },
106
+ onAnimationEndCapture: () => {
107
+ modal.onAnimationEnd();
108
+ }
109
+ });
110
+ var shadcnUiAlertDialogContent = (modal, options) => ({
111
+ onAnimationEndCapture: () => {
112
+ modal.onAnimationEnd();
113
+ },
114
+ onEscapeKeyDown: (e) => {
115
+ if (options?.disableClose) {
116
+ e?.preventDefault();
117
+ } else {
118
+ modal.dismiss();
119
+ }
120
+ },
121
+ onPointerDownOutside: (e) => {
122
+ if (options?.disableClose) {
123
+ e?.preventDefault();
124
+ } else {
125
+ modal.dismiss();
126
+ }
127
+ }
128
+ });
129
+ var shadcnUiSheet = (modal, options) => ({
130
+ open: modal.isOpen,
131
+ onOpenChange: (open) => {
132
+ if (!(open || options?.disableClose)) {
133
+ modal.dismiss();
134
+ }
135
+ },
136
+ onClose: () => {
137
+ if (!options?.disableClose) {
138
+ modal.dismiss();
139
+ }
140
+ },
141
+ onAnimationEndCapture: () => {
142
+ modal.onAnimationEnd();
143
+ }
144
+ });
145
+ var shadcnUiSheetContent = (modal, options) => ({
146
+ onAnimationEndCapture: () => {
147
+ modal.onAnimationEnd();
148
+ },
149
+ onEscapeKeyDown: (e) => {
150
+ if (options?.disableClose) {
151
+ e?.preventDefault();
152
+ } else {
153
+ modal.dismiss();
154
+ }
155
+ },
156
+ onPointerDownOutside: (e) => {
157
+ if (options?.disableClose) {
158
+ e?.preventDefault();
159
+ } else {
160
+ modal.dismiss();
161
+ }
162
+ }
163
+ });
164
+ var shadcnUiPopover = (modal, options) => ({
165
+ open: modal.isOpen,
166
+ onOpenChange: (open) => {
167
+ if (!(open || options?.disableClose)) {
168
+ modal.dismiss();
169
+ modal.onAnimationEnd();
170
+ }
171
+ }
172
+ });
173
+ var shadcnUiDrawer = (modal, options) => ({
174
+ open: modal.isOpen,
175
+ onOpenChange: (open) => {
176
+ if (!(open || options?.disableClose)) {
177
+ modal.dismiss();
178
+ }
179
+ },
180
+ dismissible: !options?.disableClose
181
+ });
182
+ var shadcnUiDrawerContent = (modal) => ({
183
+ onAnimationEnd: () => {
184
+ modal.onAnimationEnd();
185
+ }
186
+ });
187
+ var radixUiSheet = (modal, options) => ({
188
+ open: modal.isOpen,
189
+ onOpenChange: (open) => {
190
+ if (!(open || options?.disableClose)) {
191
+ modal.dismiss();
192
+ }
193
+ }
194
+ });
195
+ var radixUiSheetContent = (modal, options) => ({
196
+ onAnimationEndCapture: () => {
197
+ modal.onAnimationEnd();
198
+ },
199
+ onEscapeKeyDown: (e) => {
200
+ if (options?.disableClose) {
201
+ e?.preventDefault();
202
+ } else {
203
+ modal.dismiss();
204
+ }
205
+ },
206
+ onPointerDownOutside: (e) => {
207
+ if (options?.disableClose) {
208
+ e?.preventDefault();
209
+ } else {
210
+ modal.dismiss();
211
+ }
212
+ }
213
+ });
214
+ var radixUiPopover = (modal, options) => ({
215
+ open: modal.isOpen,
216
+ onOpenChange: (open) => {
217
+ if (!(open || options?.disableClose)) {
218
+ modal.dismiss();
219
+ modal.onAnimationEnd();
220
+ }
221
+ }
222
+ });
223
+ var baseUiDialog = (modal, options) => ({
224
+ open: modal.isOpen,
225
+ onOpenChange: (open) => {
226
+ if (!(open || options?.disableClose)) {
227
+ modal.dismiss();
228
+ }
229
+ },
230
+ dismissible: !options?.disableClose
231
+ });
232
+ var baseUiDialogPortal = (modal) => ({
233
+ keepMounted: modal.keepMounted
234
+ });
235
+ var baseUiDialogPopup = (modal) => ({
236
+ onAnimationEnd: () => {
237
+ modal.onAnimationEnd();
238
+ }
239
+ });
240
+ var baseUiAlertDialog = (modal, options) => ({
241
+ open: modal.isOpen,
242
+ onOpenChange: (open) => {
243
+ if (!(open || options?.disableClose)) {
244
+ modal.dismiss();
245
+ }
246
+ },
247
+ dismissible: !options?.disableClose
248
+ });
249
+ var baseUiAlertDialogPortal = (modal) => ({
250
+ keepMounted: modal.keepMounted
251
+ });
252
+ var baseUiAlertDialogPopup = (modal) => ({
253
+ onAnimationEnd: () => {
254
+ modal.onAnimationEnd();
255
+ }
256
+ });
257
+ var baseUiPopover = (modal, options) => ({
258
+ open: modal.isOpen,
259
+ onOpenChange: (open) => {
260
+ if (!(open || options?.disableClose)) {
261
+ modal.dismiss();
262
+ }
263
+ }
264
+ });
265
+ var baseUiPopoverPortal = (modal) => ({
266
+ keepMounted: modal.keepMounted
267
+ });
268
+ var baseUiPopoverPopup = (modal) => ({
269
+ onAnimationEnd: () => {
270
+ modal.onAnimationEnd();
271
+ }
272
+ });
273
+ var baseUiSheet = (modal, options) => ({
274
+ open: modal.isOpen,
275
+ onOpenChange: (open) => {
276
+ if (!(open || options?.disableClose)) {
277
+ modal.dismiss();
278
+ }
279
+ },
280
+ dismissible: !options?.disableClose
281
+ });
282
+ var baseUiSheetPortal = (modal) => ({
283
+ keepMounted: modal.keepMounted
284
+ });
285
+ var baseUiSheetPopup = (modal) => ({
286
+ onAnimationEnd: () => {
287
+ modal.onAnimationEnd();
288
+ }
289
+ });
290
+
291
+ // src/core.ts
292
+ var symModalId = /* @__PURE__ */ Symbol("ModalManagerId");
293
+ var initialState = {};
294
+ var MODAL_REGISTRY = {};
295
+ var ALREADY_MOUNTED = {};
296
+ var modalCallbacks = {};
297
+ var hideModalCallbacks = {};
298
+ var uidSeed = 0;
299
+ var dispatchFn = () => {
300
+ throw new Error(
301
+ "No dispatch method detected. Did you wrap your app with ModalManager.Provider?"
302
+ );
303
+ };
304
+ var getUid = () => `_modal_${uidSeed++}`;
305
+ var getModalId = (modal) => {
306
+ if (typeof modal === "string") {
307
+ return modal;
308
+ }
309
+ const modalWithId = modal;
310
+ if (!modalWithId[symModalId]) {
311
+ modalWithId[symModalId] = getUid();
312
+ }
313
+ return modalWithId[symModalId];
314
+ };
315
+ var setDispatch = (fn) => {
316
+ dispatchFn = fn;
317
+ };
318
+ var getDispatch = () => dispatchFn;
319
+ var actions = {
320
+ open: (modalId, data) => ({
321
+ type: "modalio/show",
322
+ payload: { modalId, data }
323
+ }),
324
+ close: (modalId) => ({
325
+ type: "modalio/hide",
326
+ payload: { modalId }
327
+ }),
328
+ remove: (modalId) => ({
329
+ type: "modalio/remove",
330
+ payload: { modalId }
331
+ }),
332
+ setFlags: (modalId, flags) => ({
333
+ type: "modalio/set-flags",
334
+ payload: { modalId, flags }
335
+ })
336
+ };
337
+ var reducer = (state, action) => {
338
+ switch (action.type) {
339
+ case "modalio/show": {
340
+ const { modalId, data } = action.payload;
341
+ return {
342
+ ...state,
343
+ [modalId]: {
344
+ ...state[modalId],
345
+ modalId,
346
+ data,
347
+ // If already mounted, show immediately; otherwise delay
348
+ isOpen: !!ALREADY_MOUNTED[modalId],
349
+ delayOpen: !ALREADY_MOUNTED[modalId]
350
+ }
351
+ };
352
+ }
353
+ case "modalio/hide": {
354
+ const { modalId } = action.payload;
355
+ const modalState = state[modalId];
356
+ if (!modalState) {
357
+ return state;
358
+ }
359
+ return {
360
+ ...state,
361
+ [modalId]: {
362
+ ...modalState,
363
+ isOpen: false
364
+ }
365
+ };
366
+ }
367
+ case "modalio/remove": {
368
+ const { modalId } = action.payload;
369
+ const newState = { ...state };
370
+ delete newState[modalId];
371
+ return newState;
372
+ }
373
+ case "modalio/set-flags": {
374
+ const { modalId, flags } = action.payload;
375
+ const existingState = state[modalId];
376
+ if (!existingState) {
377
+ return state;
378
+ }
379
+ return {
380
+ ...state,
381
+ [modalId]: {
382
+ ...existingState,
383
+ ...flags
384
+ }
385
+ };
386
+ }
387
+ default:
388
+ return state;
389
+ }
390
+ };
391
+ var register = (id, comp, props) => {
392
+ if (MODAL_REGISTRY[id]) {
393
+ MODAL_REGISTRY[id].props = props;
394
+ } else {
395
+ MODAL_REGISTRY[id] = {
396
+ comp,
397
+ props
398
+ };
399
+ }
400
+ };
401
+ var unregister = (id) => {
402
+ delete MODAL_REGISTRY[id];
403
+ };
404
+
405
+ // src/types.ts
406
+ var MODAL_CONFIG_KEY = "__modalConfig";
407
+
408
+ // src/api.ts
409
+ var CLEANUP_DELAY_MS = 5e3;
410
+ function createDeferredPromise() {
411
+ let resolve;
412
+ let reject;
413
+ const promise = new Promise((res, rej) => {
414
+ resolve = res;
415
+ reject = rej;
416
+ });
417
+ return { resolve, reject, promise };
418
+ }
419
+ var openedCallbacks = {};
420
+ var beforeClosedCallbacks = {};
421
+ var modalStates = {};
422
+ var closedPromises = {};
423
+ var cleanupModal = (modalId) => {
424
+ delete modalCallbacks[modalId];
425
+ delete hideModalCallbacks[modalId];
426
+ delete openedCallbacks[modalId];
427
+ delete beforeClosedCallbacks[modalId];
428
+ delete modalStates[modalId];
429
+ delete closedPromises[modalId];
430
+ };
431
+ var cleanupAllModals = () => {
432
+ const allIds = /* @__PURE__ */ new Set([
433
+ ...Object.keys(modalCallbacks),
434
+ ...Object.keys(hideModalCallbacks),
435
+ ...Object.keys(openedCallbacks),
436
+ ...Object.keys(beforeClosedCallbacks),
437
+ ...Object.keys(modalStates),
438
+ ...Object.keys(closedPromises)
439
+ ]);
440
+ for (const id of allIds) {
441
+ cleanupModal(id);
442
+ }
443
+ };
444
+ function closeModal(modal) {
445
+ const modalId = getModalId(modal);
446
+ modalStates[modalId] = "closing";
447
+ getDispatch()(actions.close(modalId));
448
+ if (modalCallbacks[modalId]) {
449
+ modalCallbacks[modalId].resolve(void 0);
450
+ delete modalCallbacks[modalId];
451
+ }
452
+ if (!hideModalCallbacks[modalId]) {
453
+ hideModalCallbacks[modalId] = createDeferredPromise();
454
+ }
455
+ return hideModalCallbacks[modalId].promise;
456
+ }
457
+ var removeModal = (modal) => {
458
+ const modalId = getModalId(modal);
459
+ getDispatch()(actions.remove(modalId));
460
+ modalCallbacks[modalId]?.resolve(void 0);
461
+ hideModalCallbacks[modalId]?.resolve(void 0);
462
+ beforeClosedCallbacks[modalId]?.resolve(void 0);
463
+ openedCallbacks[modalId]?.resolve();
464
+ cleanupModal(modalId);
465
+ };
466
+ var setFlags = (modalId, flags) => {
467
+ getDispatch()(actions.setFlags(modalId, flags));
468
+ };
469
+ function openModal(modal, config = {}) {
470
+ const modalId = config.modalId ?? getModalId(modal);
471
+ if (typeof modal !== "string" && !MODAL_REGISTRY[modalId]) {
472
+ register(modalId, modal);
473
+ }
474
+ let disableClose = config.disableClose ?? false;
475
+ const modalConfig = {
476
+ disableClose,
477
+ keepMounted: config.keepMounted
478
+ };
479
+ const data = {
480
+ ...config.data,
481
+ [MODAL_CONFIG_KEY]: modalConfig
482
+ };
483
+ modalStates[modalId] = "open";
484
+ getDispatch()(actions.open(modalId, data));
485
+ if (config.keepMounted) {
486
+ getDispatch()(actions.setFlags(modalId, { keepMounted: true }));
487
+ }
488
+ const mainCallbacks = createDeferredPromise();
489
+ modalCallbacks[modalId] = mainCallbacks;
490
+ closedPromises[modalId] = mainCallbacks.promise;
491
+ const openedDeferred = createDeferredPromise();
492
+ openedCallbacks[modalId] = {
493
+ resolve: openedDeferred.resolve,
494
+ promise: openedDeferred.promise
495
+ };
496
+ const beforeClosedDeferred = createDeferredPromise();
497
+ beforeClosedCallbacks[modalId] = {
498
+ resolve: beforeClosedDeferred.resolve,
499
+ promise: beforeClosedDeferred.promise
500
+ };
501
+ const modalRef = {
502
+ modalId,
503
+ data: config.data,
504
+ get disableClose() {
505
+ return disableClose;
506
+ },
507
+ set disableClose(value) {
508
+ disableClose = value;
509
+ const currentConfig = data[MODAL_CONFIG_KEY] ?? {};
510
+ const updatedData = {
511
+ ...data,
512
+ [MODAL_CONFIG_KEY]: {
513
+ ...currentConfig,
514
+ disableClose: value
515
+ }
516
+ };
517
+ getDispatch()(actions.setFlags(modalId, { data: updatedData }));
518
+ },
519
+ close: (result) => {
520
+ if (modalStates[modalId] === "closed") {
521
+ return;
522
+ }
523
+ modalStates[modalId] = "closing";
524
+ beforeClosedCallbacks[modalId]?.resolve(result);
525
+ delete beforeClosedCallbacks[modalId];
526
+ modalCallbacks[modalId]?.resolve(result);
527
+ delete modalCallbacks[modalId];
528
+ closeModal(modalId);
529
+ },
530
+ afterOpened: () => {
531
+ return openedCallbacks[modalId]?.promise ?? Promise.resolve();
532
+ },
533
+ afterClosed: () => {
534
+ return closedPromises[modalId] ?? Promise.resolve(void 0);
535
+ },
536
+ beforeClosed: () => {
537
+ return beforeClosedCallbacks[modalId]?.promise ?? Promise.resolve(void 0);
538
+ },
539
+ updateData: (newData) => {
540
+ Object.assign(data, newData);
541
+ getDispatch()(actions.open(modalId, data));
542
+ },
543
+ getState: () => {
544
+ return modalStates[modalId] ?? "closed";
545
+ }
546
+ };
547
+ return modalRef;
548
+ }
549
+ var markClosed = (modalId) => {
550
+ modalStates[modalId] = "closed";
551
+ setTimeout(() => {
552
+ if (modalStates[modalId] === "closed") {
553
+ delete modalStates[modalId];
554
+ delete closedPromises[modalId];
555
+ }
556
+ }, CLEANUP_DELAY_MS);
557
+ };
558
+ var getOpenModals = () => {
559
+ return Object.entries(modalStates).filter(([_, state]) => state === "open" || state === "closing").map(([id]) => id);
560
+ };
561
+ var closeAllModals = async () => {
562
+ const openModals = getOpenModals();
563
+ const closePromises = [];
564
+ for (const modalId of openModals) {
565
+ modalStates[modalId] = "closing";
566
+ beforeClosedCallbacks[modalId]?.resolve(void 0);
567
+ delete beforeClosedCallbacks[modalId];
568
+ modalCallbacks[modalId]?.resolve(void 0);
569
+ delete modalCallbacks[modalId];
570
+ closePromises.push(closeModal(modalId));
571
+ }
572
+ await Promise.all(closePromises);
573
+ };
574
+ var hasOpenModals = () => {
575
+ return getOpenModals().length > 0;
576
+ };
577
+ var notifyOpened = (modalId) => {
578
+ openedCallbacks[modalId]?.resolve();
579
+ delete openedCallbacks[modalId];
580
+ };
581
+ var ModalContext = createContext(initialState);
582
+ var ModalIdContext = createContext(null);
583
+ function ModalPlaceholder() {
584
+ const modals = useContext(ModalContext);
585
+ const visibleModalIds = Object.keys(modals).filter(
586
+ (id) => modals[id] !== void 0
587
+ );
588
+ const toRender = [];
589
+ for (const id of visibleModalIds) {
590
+ const entry = MODAL_REGISTRY[id];
591
+ if (entry) {
592
+ toRender.push({ id, comp: entry.comp, props: entry.props });
593
+ } else if (!ALREADY_MOUNTED[id]) {
594
+ console.warn(
595
+ `[ModalManager] No modal found for id: ${id}. Please check if it is registered or declared via JSX.`
596
+ );
597
+ }
598
+ }
599
+ return /* @__PURE__ */ jsx(Fragment, { children: toRender.map(({ id, comp: Comp, props }) => /* @__PURE__ */ jsx(Comp, { modalId: id, ...props }, id)) });
600
+ }
601
+ function InnerContextProvider({
602
+ children
603
+ }) {
604
+ const [modals, dispatch] = useReducer(reducer, initialState);
605
+ setDispatch(dispatch);
606
+ return /* @__PURE__ */ jsxs(ModalContext.Provider, { value: modals, children: [
607
+ children,
608
+ /* @__PURE__ */ jsx(ModalPlaceholder, {})
609
+ ] });
610
+ }
611
+ function ModalProvider({
612
+ children,
613
+ dispatch: givenDispatch,
614
+ modals: givenModals
615
+ }) {
616
+ if (givenDispatch && givenModals) {
617
+ setDispatch(givenDispatch);
618
+ return /* @__PURE__ */ jsxs(ModalContext.Provider, { value: givenModals, children: [
619
+ children,
620
+ /* @__PURE__ */ jsx(ModalPlaceholder, {})
621
+ ] });
622
+ }
623
+ return /* @__PURE__ */ jsx(InnerContextProvider, { children });
624
+ }
625
+ function ModalDef({
626
+ id,
627
+ component
628
+ }) {
629
+ useEffect(() => {
630
+ register(id, component);
631
+ return () => {
632
+ unregister(id);
633
+ };
634
+ }, [id, component]);
635
+ return null;
636
+ }
637
+ function useModal(modal, initialData) {
638
+ const modals = useContext(ModalContext);
639
+ const contextModalId = useContext(ModalIdContext);
640
+ const modalId = modal ? getModalId(modal) : contextModalId;
641
+ if (!modalId) {
642
+ throw new Error(
643
+ "[ModalManager] No modal id found in useModal. Either pass a modal component/id or use inside a modal component."
644
+ );
645
+ }
646
+ const isComponentRef = modal !== void 0 && typeof modal !== "string";
647
+ useEffect(() => {
648
+ if (isComponentRef && !MODAL_REGISTRY[modalId]) {
649
+ register(
650
+ modalId,
651
+ modal,
652
+ initialData
653
+ );
654
+ }
655
+ }, [isComponentRef, modalId, modal, initialData]);
656
+ const modalInfo = modals[modalId];
657
+ const modalInfoRef = useRef(modalInfo);
658
+ useEffect(() => {
659
+ modalInfoRef.current = modalInfo;
660
+ }, [modalInfo]);
661
+ const openCallback = useCallback(
662
+ (data) => {
663
+ const ref = openModal(modalId, { data });
664
+ return ref.afterClosed();
665
+ },
666
+ [modalId]
667
+ );
668
+ const closeCallback = useCallback(
669
+ (result) => {
670
+ modalCallbacks[modalId]?.resolve(result);
671
+ delete modalCallbacks[modalId];
672
+ closeModal(modalId);
673
+ },
674
+ [modalId]
675
+ );
676
+ const dismissCallback = useCallback(() => {
677
+ modalCallbacks[modalId]?.resolve(void 0);
678
+ delete modalCallbacks[modalId];
679
+ closeModal(modalId);
680
+ }, [modalId]);
681
+ const removeCallback = useCallback(() => removeModal(modalId), [modalId]);
682
+ const onAnimationEnd = useCallback(() => {
683
+ const current = modalInfoRef.current;
684
+ if (current?.isOpen) {
685
+ notifyOpened(modalId);
686
+ } else {
687
+ markClosed(modalId);
688
+ hideModalCallbacks[modalId]?.resolve(void 0);
689
+ delete hideModalCallbacks[modalId];
690
+ if (!current?.keepMounted) {
691
+ removeModal(modalId);
692
+ }
693
+ }
694
+ }, [modalId]);
695
+ return useMemo(
696
+ () => ({
697
+ // State
698
+ modalId,
699
+ data: modalInfo?.data,
700
+ isOpen: !!modalInfo?.isOpen,
701
+ keepMounted: !!modalInfo?.keepMounted,
702
+ // Controls
703
+ open: openCallback,
704
+ close: closeCallback,
705
+ dismiss: dismissCallback,
706
+ remove: removeCallback,
707
+ // Animation handler
708
+ onAnimationEnd
709
+ }),
710
+ [
711
+ modalId,
712
+ modalInfo?.data,
713
+ modalInfo?.isOpen,
714
+ modalInfo?.keepMounted,
715
+ openCallback,
716
+ closeCallback,
717
+ dismissCallback,
718
+ removeCallback,
719
+ onAnimationEnd
720
+ ]
721
+ );
722
+ }
723
+ function useModalData() {
724
+ const modals = useContext(ModalContext);
725
+ const modalId = useContext(ModalIdContext);
726
+ if (!modalId) {
727
+ throw new Error(
728
+ "[ModalManager] useModalData must be used inside a modal component. Make sure you're using createModal() to wrap your modal."
729
+ );
730
+ }
731
+ const modalInfo = modals[modalId];
732
+ const data = modalInfo?.data;
733
+ if (!data) {
734
+ return void 0;
735
+ }
736
+ const { [MODAL_CONFIG_KEY]: _, ...userData } = data;
737
+ return userData;
738
+ }
739
+ function useModalConfig() {
740
+ const modals = useContext(ModalContext);
741
+ const modalId = useContext(ModalIdContext);
742
+ if (!modalId) {
743
+ throw new Error(
744
+ "[ModalManager] useModalConfig must be used inside a modal component."
745
+ );
746
+ }
747
+ const modalInfo = modals[modalId];
748
+ const config = modalInfo?.data?.[MODAL_CONFIG_KEY];
749
+ return config ?? {};
750
+ }
751
+ var createModal = (Comp) => {
752
+ function WrappedComponent(allProps) {
753
+ const { defaultOpen, keepMounted, modalId, ref, ...restProps } = allProps;
754
+ const componentProps = restProps;
755
+ const { data, open: openModal2 } = useModal(modalId);
756
+ const modals = useContext(ModalContext);
757
+ const shouldMount = modalId in modals;
758
+ useEffect(() => {
759
+ if (defaultOpen) {
760
+ openModal2();
761
+ }
762
+ ALREADY_MOUNTED[modalId] = true;
763
+ return () => {
764
+ delete ALREADY_MOUNTED[modalId];
765
+ };
766
+ }, [modalId, openModal2, defaultOpen]);
767
+ useEffect(() => {
768
+ if (keepMounted) {
769
+ setFlags(modalId, { keepMounted: true });
770
+ }
771
+ }, [modalId, keepMounted]);
772
+ const delayOpen = modals[modalId]?.delayOpen;
773
+ useEffect(() => {
774
+ if (delayOpen) {
775
+ openModal2(data);
776
+ }
777
+ }, [delayOpen, data, openModal2]);
778
+ if (!shouldMount) {
779
+ return null;
780
+ }
781
+ const mergedProps = {
782
+ ...componentProps,
783
+ ...data,
784
+ ref
785
+ };
786
+ return /* @__PURE__ */ jsx(ModalIdContext.Provider, { value: modalId, children: /* @__PURE__ */ jsx(Comp, { ...mergedProps }) });
787
+ }
788
+ WrappedComponent.displayName = `ModalManager(${Comp.displayName || Comp.name || "Component"})`;
789
+ return WrappedComponent;
790
+ };
791
+
792
+ // src/modal-manager.ts
793
+ var ModalManager = {
794
+ /**
795
+ * Create a modal component with lifecycle management.
796
+ * Wraps your component with automatic registration and state handling.
797
+ */
798
+ create: createModal,
799
+ /**
800
+ * Open a modal and return a ModalRef for controlling it.
801
+ * @returns ModalRef with afterOpened(), afterClosed(), beforeClosed() promises
802
+ */
803
+ open: openModal,
804
+ /**
805
+ * Close a specific modal by component or ID.
806
+ * @returns Promise that resolves when the close animation completes
807
+ */
808
+ close: closeModal,
809
+ /**
810
+ * Close all open modals.
811
+ * @returns Promise that resolves when all close animations complete
812
+ */
813
+ closeAll: closeAllModals,
814
+ /**
815
+ * Remove a modal from the DOM completely.
816
+ */
817
+ remove: removeModal,
818
+ /**
819
+ * Check if any modals are currently open.
820
+ */
821
+ hasOpen: hasOpenModals,
822
+ /**
823
+ * Get array of currently open modal IDs.
824
+ */
825
+ getOpen: getOpenModals,
826
+ /**
827
+ * Register a modal component with an ID for later use.
828
+ */
829
+ register,
830
+ /**
831
+ * Unregister a previously registered modal.
832
+ */
833
+ unregister,
834
+ /**
835
+ * Clean up all internal state for a specific modal.
836
+ */
837
+ cleanup: cleanupModal,
838
+ /**
839
+ * Clean up all modal state (useful for testing).
840
+ */
841
+ cleanupAll: cleanupAllModals,
842
+ /**
843
+ * Context provider for modal management.
844
+ * Must wrap your application.
845
+ *
846
+ * @example
847
+ * ```tsx
848
+ * function App() {
849
+ * return (
850
+ * <ModalManager.Provider>
851
+ * <YourApp />
852
+ * </ModalManager.Provider>
853
+ * );
854
+ * }
855
+ * ```
856
+ */
857
+ Provider: ModalProvider,
858
+ /**
859
+ * Declaratively define a modal in JSX.
860
+ *
861
+ * @example
862
+ * ```tsx
863
+ * <ModalManager.Def component={MyModal} id="my-modal" />
864
+ *
865
+ * // Later: open by ID
866
+ * ModalManager.open("my-modal");
867
+ * ```
868
+ */
869
+ Def: ModalDef
870
+ };
871
+
872
+ export { MODAL_CONFIG_KEY, ModalContext, ModalIdContext, ModalManager, baseUiAlertDialog, baseUiAlertDialogPopup, baseUiAlertDialogPortal, baseUiDialog, baseUiDialogPopup, baseUiDialogPortal, baseUiPopover, baseUiPopoverPopup, baseUiPopoverPortal, baseUiSheet, baseUiSheetPopup, baseUiSheetPortal, getModalId, radixUiAlertDialog, radixUiAlertDialogContent, radixUiDialog, radixUiDialogContent, radixUiPopover, radixUiSheet, radixUiSheetContent, reducer, shadcnUiAlertDialog, shadcnUiAlertDialogContent, shadcnUiDialog, shadcnUiDialogContent, shadcnUiDrawer, shadcnUiDrawerContent, shadcnUiPopover, shadcnUiSheet, shadcnUiSheetContent, useModal, useModalConfig, useModalData };
873
+ //# sourceMappingURL=index.js.map
874
+ //# sourceMappingURL=index.js.map