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/README.md +157 -0
- package/dist/index.cjs +912 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +618 -0
- package/dist/index.d.ts +618 -0
- package/dist/index.js +874 -0
- package/dist/index.js.map +1 -0
- package/package.json +76 -0
- package/src/adapters.ts +567 -0
- package/src/api.ts +418 -0
- package/src/context.tsx +206 -0
- package/src/core.ts +226 -0
- package/src/hoc.tsx +114 -0
- package/src/hooks.ts +250 -0
- package/src/index.ts +81 -0
- package/src/modal-manager.ts +136 -0
- package/src/types.ts +243 -0
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
|