@okyrychenko-dev/react-modal-manager 0.1.0 → 0.2.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/CHANGELOG.md +32 -0
- package/README.md +143 -49
- package/dist/index.cjs +234 -275
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +8 -6
- package/dist/index.d.ts +8 -6
- package/dist/index.js +235 -276
- package/dist/index.js.map +1 -1
- package/package.json +43 -28
package/dist/index.js
CHANGED
|
@@ -1,7 +1,6 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { isDefined, isInstanceOf, isNonEmptyArray, assertTrue, hasProperty, assertDefined, isObject, isFunction, isError } from '@okyrychenko-dev/type-utils';
|
|
2
|
+
import { createContext, useId, useRef, useEffect, useCallback, useMemo, useContext, useSyncExternalStore, useState, createElement } from 'react';
|
|
2
3
|
import { jsxs, jsx, Fragment } from 'react/jsx-runtime';
|
|
3
|
-
import { useStore } from 'zustand';
|
|
4
|
-
import { createStore } from 'zustand/vanilla';
|
|
5
4
|
|
|
6
5
|
// src/registry/createModal.ts
|
|
7
6
|
function createGeneratedModalId() {
|
|
@@ -14,73 +13,56 @@ function createModal(options) {
|
|
|
14
13
|
};
|
|
15
14
|
return {
|
|
16
15
|
...definition,
|
|
17
|
-
open: (manager,
|
|
18
|
-
};
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
// src/registry/createModalController.constants.ts
|
|
22
|
-
var MODAL_CONTROLLER_UNBOUND_ERROR = "Modal registry is not bound to a mounted ModalProvider";
|
|
23
|
-
|
|
24
|
-
// src/registry/createModalController.ts
|
|
25
|
-
function createModalController() {
|
|
26
|
-
const managerStack = [];
|
|
27
|
-
const getActiveManager = () => {
|
|
28
|
-
if (managerStack.length === 0) {
|
|
29
|
-
throw new Error(MODAL_CONTROLLER_UNBOUND_ERROR);
|
|
30
|
-
}
|
|
31
|
-
return managerStack[managerStack.length - 1];
|
|
32
|
-
};
|
|
33
|
-
return {
|
|
34
|
-
bind: (manager) => {
|
|
35
|
-
managerStack.push(manager);
|
|
36
|
-
return () => {
|
|
37
|
-
const managerIndex = managerStack.lastIndexOf(manager);
|
|
38
|
-
if (managerIndex >= 0) {
|
|
39
|
-
managerStack.splice(managerIndex, 1);
|
|
40
|
-
}
|
|
41
|
-
};
|
|
42
|
-
},
|
|
43
|
-
closeAll: (reason) => {
|
|
44
|
-
getActiveManager().closeAll(reason);
|
|
45
|
-
},
|
|
46
|
-
confirm: (params) => getActiveManager().confirm(params),
|
|
47
|
-
dismiss: (instanceId, reason) => {
|
|
48
|
-
getActiveManager().dismiss(instanceId, reason);
|
|
49
|
-
},
|
|
50
|
-
isReady: () => managerStack.length > 0,
|
|
51
|
-
open: (modal, input) => getActiveManager().open(modal, input)
|
|
16
|
+
open: (manager, ...args) => manager.open(definition, ...args)
|
|
52
17
|
};
|
|
53
18
|
}
|
|
54
19
|
|
|
55
20
|
// src/registry/createModalRegistry.constants.ts
|
|
56
21
|
var MODAL_REGISTRY_UNKNOWN_KEY_ERROR = "Modal registry does not contain modal";
|
|
22
|
+
var MODAL_CONTROLLER_UNBOUND_ERROR = "Modal registry is not bound to a mounted ModalProvider";
|
|
57
23
|
|
|
58
|
-
// src/registry/
|
|
59
|
-
var
|
|
60
|
-
function
|
|
61
|
-
|
|
24
|
+
// src/registry/createModalRegistry.ts
|
|
25
|
+
var MODAL_REGISTRY_ATTACH = /* @__PURE__ */ Symbol("modal-registry-attach");
|
|
26
|
+
function isModalRegistryAttachable(value) {
|
|
27
|
+
return isObject(value) && isFunction(value[MODAL_REGISTRY_ATTACH]);
|
|
62
28
|
}
|
|
63
|
-
function
|
|
64
|
-
return
|
|
29
|
+
function attachModalRegistry(registry, manager) {
|
|
30
|
+
return registry[MODAL_REGISTRY_ATTACH](manager);
|
|
65
31
|
}
|
|
66
|
-
|
|
67
|
-
// src/registry/createModalRegistry.ts
|
|
68
32
|
function createModalRegistry(definitions) {
|
|
69
|
-
|
|
33
|
+
let attachments = [];
|
|
34
|
+
const activeManager = () => {
|
|
35
|
+
assertTrue(isNonEmptyArray(attachments), MODAL_CONTROLLER_UNBOUND_ERROR);
|
|
36
|
+
return attachments[attachments.length - 1].manager;
|
|
37
|
+
};
|
|
38
|
+
const attach = (manager) => {
|
|
39
|
+
const attachment = { manager };
|
|
40
|
+
attachments.push(attachment);
|
|
41
|
+
return () => {
|
|
42
|
+
attachments = attachments.filter((candidate) => candidate !== attachment);
|
|
43
|
+
};
|
|
44
|
+
};
|
|
70
45
|
function open(key, input) {
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
46
|
+
assertTrue(
|
|
47
|
+
hasProperty(definitions, key),
|
|
48
|
+
() => `${MODAL_REGISTRY_UNKNOWN_KEY_ERROR}: ${key}`
|
|
49
|
+
);
|
|
50
|
+
return definitions[key].open(activeManager(), input);
|
|
75
51
|
}
|
|
76
52
|
const registry = {
|
|
77
|
-
closeAll:
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
53
|
+
closeAll: (reason) => {
|
|
54
|
+
activeManager().closeAll(reason);
|
|
55
|
+
},
|
|
56
|
+
confirm: (params) => {
|
|
57
|
+
return activeManager().confirm(params);
|
|
58
|
+
},
|
|
59
|
+
dismiss: (instanceId, reason) => {
|
|
60
|
+
activeManager().dismiss(instanceId, reason);
|
|
61
|
+
},
|
|
62
|
+
isReady: () => isNonEmptyArray(attachments),
|
|
63
|
+
open,
|
|
64
|
+
[MODAL_REGISTRY_ATTACH]: attach
|
|
82
65
|
};
|
|
83
|
-
bindModalRegistry(registry, controller);
|
|
84
66
|
return registry;
|
|
85
67
|
}
|
|
86
68
|
|
|
@@ -108,7 +90,7 @@ function ConfirmModal(props) {
|
|
|
108
90
|
const confirmText = input.confirmText ?? DEFAULT_CONFIRM_TEXT;
|
|
109
91
|
const variant = input.variant ?? "default";
|
|
110
92
|
const dismissible = input.dismissible !== false;
|
|
111
|
-
const hasDescription = input.description
|
|
93
|
+
const hasDescription = isDefined(input.description);
|
|
112
94
|
const titleId = useId();
|
|
113
95
|
const descriptionId = useId();
|
|
114
96
|
const dialogRef = useRef(null);
|
|
@@ -122,7 +104,7 @@ function ConfirmModal(props) {
|
|
|
122
104
|
}
|
|
123
105
|
initialFocusRef.current?.focus();
|
|
124
106
|
return () => {
|
|
125
|
-
if (previouslyFocused
|
|
107
|
+
if (isInstanceOf(previouslyFocused, HTMLElement)) {
|
|
126
108
|
previouslyFocused.focus();
|
|
127
109
|
}
|
|
128
110
|
};
|
|
@@ -144,11 +126,11 @@ function ConfirmModal(props) {
|
|
|
144
126
|
handleDismiss();
|
|
145
127
|
};
|
|
146
128
|
const handleTab = (event) => {
|
|
147
|
-
if (dialogRef.current
|
|
129
|
+
if (!isDefined(dialogRef.current)) {
|
|
148
130
|
return;
|
|
149
131
|
}
|
|
150
132
|
const focusable = getFocusableElements(dialogRef.current);
|
|
151
|
-
if (focusable
|
|
133
|
+
if (!isNonEmptyArray(focusable)) {
|
|
152
134
|
return;
|
|
153
135
|
}
|
|
154
136
|
const first = focusable[0];
|
|
@@ -223,117 +205,173 @@ var ModalRejectError = class extends Error {
|
|
|
223
205
|
this.value = value;
|
|
224
206
|
}
|
|
225
207
|
};
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
208
|
+
function createModalLifecycle(options) {
|
|
209
|
+
let nextInstanceIndex = 0;
|
|
210
|
+
let instances = [];
|
|
211
|
+
let disposed = false;
|
|
212
|
+
let closeDelayMs = options.closeDelayMs;
|
|
213
|
+
const serverSnapshot = { instances: [] };
|
|
214
|
+
let snapshot = serverSnapshot;
|
|
215
|
+
const observers = /* @__PURE__ */ new Set();
|
|
216
|
+
const removalTimers = /* @__PURE__ */ new Map();
|
|
217
|
+
const dismissers = /* @__PURE__ */ new Map();
|
|
218
|
+
const notifyObservers = () => {
|
|
219
|
+
for (const observer of observers) {
|
|
220
|
+
observer();
|
|
221
|
+
}
|
|
222
|
+
};
|
|
223
|
+
const publishState = () => {
|
|
224
|
+
if (disposed) {
|
|
225
|
+
return;
|
|
226
|
+
}
|
|
227
|
+
snapshot = { instances: [...instances] };
|
|
228
|
+
notifyObservers();
|
|
229
|
+
};
|
|
230
|
+
const removeInstance = (instanceId) => {
|
|
231
|
+
const timer = removalTimers.get(instanceId);
|
|
232
|
+
if (isDefined(timer)) {
|
|
233
|
+
globalThis.clearTimeout(timer);
|
|
234
|
+
removalTimers.delete(instanceId);
|
|
235
|
+
}
|
|
236
|
+
dismissers.delete(instanceId);
|
|
237
|
+
instances = instances.filter(
|
|
238
|
+
(candidate) => candidate.instanceId !== instanceId
|
|
239
|
+
);
|
|
240
|
+
publishState();
|
|
241
|
+
};
|
|
242
|
+
const settleInstance = (instanceId, settle) => {
|
|
243
|
+
const instance = instances.find(
|
|
244
|
+
(candidate) => candidate.instanceId === instanceId
|
|
245
|
+
);
|
|
246
|
+
if (!isDefined(instance) || instance.status === "closing") {
|
|
246
247
|
return;
|
|
247
248
|
}
|
|
248
|
-
|
|
249
|
-
|
|
249
|
+
instances = instances.map((candidate) => {
|
|
250
|
+
if (candidate.instanceId === instanceId) {
|
|
251
|
+
return { ...candidate, status: "closing" };
|
|
252
|
+
}
|
|
253
|
+
return candidate;
|
|
254
|
+
});
|
|
255
|
+
publishState();
|
|
256
|
+
settle();
|
|
257
|
+
if (closeDelayMs <= 0) {
|
|
258
|
+
removeInstance(instanceId);
|
|
259
|
+
return;
|
|
260
|
+
}
|
|
261
|
+
const timer = globalThis.setTimeout(() => {
|
|
262
|
+
removeInstance(instanceId);
|
|
263
|
+
}, closeDelayMs);
|
|
264
|
+
removalTimers.set(instanceId, timer);
|
|
265
|
+
};
|
|
266
|
+
const dismissOpenInstances = (reason) => {
|
|
267
|
+
const openInstanceIds = instances.filter((instance) => instance.status === "open").map((instance) => instance.instanceId);
|
|
268
|
+
for (const instanceId of openInstanceIds) {
|
|
269
|
+
dismissers.get(instanceId)?.(reason);
|
|
270
|
+
}
|
|
250
271
|
};
|
|
251
272
|
return {
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
modals: [...state.modals, { ...modal, status: "open" }]
|
|
255
|
-
}));
|
|
256
|
-
},
|
|
257
|
-
allocateInstanceId: () => {
|
|
258
|
-
const { nextInstanceIndex } = get();
|
|
259
|
-
set({ nextInstanceIndex: nextInstanceIndex + 1 });
|
|
260
|
-
return `modal-${String(nextInstanceIndex)}`;
|
|
273
|
+
closeAll: (reason = "close-all") => {
|
|
274
|
+
dismissOpenInstances(reason);
|
|
261
275
|
},
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
276
|
+
dispose: () => {
|
|
277
|
+
if (disposed) {
|
|
278
|
+
return;
|
|
279
|
+
}
|
|
280
|
+
disposed = true;
|
|
281
|
+
dismissOpenInstances("provider-unmount");
|
|
282
|
+
for (const timer of removalTimers.values()) {
|
|
283
|
+
globalThis.clearTimeout(timer);
|
|
266
284
|
}
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
285
|
+
removalTimers.clear();
|
|
286
|
+
dismissers.clear();
|
|
287
|
+
instances = [];
|
|
288
|
+
snapshot = { instances: [] };
|
|
289
|
+
notifyObservers();
|
|
290
|
+
observers.clear();
|
|
273
291
|
},
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
modals: state.modals.map((modal) => {
|
|
277
|
-
if (modal.instanceId === instanceId) {
|
|
278
|
-
return { ...modal, status: "closing" };
|
|
279
|
-
}
|
|
280
|
-
return modal;
|
|
281
|
-
})
|
|
282
|
-
}));
|
|
292
|
+
dismiss: (instanceId, reason) => {
|
|
293
|
+
dismissers.get(instanceId)?.(reason);
|
|
283
294
|
},
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
295
|
+
getSnapshot: () => snapshot,
|
|
296
|
+
getServerSnapshot: () => serverSnapshot,
|
|
297
|
+
open: (modal, ...args) => {
|
|
298
|
+
const input = args[0];
|
|
299
|
+
const instanceId = `modal-${String(nextInstanceIndex)}`;
|
|
300
|
+
nextInstanceIndex += 1;
|
|
301
|
+
if (disposed) {
|
|
302
|
+
const result2 = Promise.reject(
|
|
303
|
+
new ModalDismissError("provider-unmount")
|
|
304
|
+
);
|
|
305
|
+
return Object.assign(result2, {
|
|
306
|
+
dismiss: () => void 0,
|
|
307
|
+
instanceId
|
|
308
|
+
});
|
|
289
309
|
}
|
|
290
|
-
const
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
310
|
+
const result = new Promise((resolve, reject) => {
|
|
311
|
+
const dismiss = (reason = "dismiss") => {
|
|
312
|
+
settleInstance(instanceId, () => {
|
|
313
|
+
reject(new ModalDismissError(reason));
|
|
314
|
+
});
|
|
315
|
+
};
|
|
316
|
+
dismissers.set(instanceId, dismiss);
|
|
317
|
+
const instance = {
|
|
318
|
+
definitionId: modal.id,
|
|
319
|
+
instanceId,
|
|
320
|
+
render: () => createElement(modal.component, {
|
|
321
|
+
close: (modalResult) => {
|
|
322
|
+
settleInstance(instanceId, () => {
|
|
323
|
+
resolve(modalResult);
|
|
324
|
+
});
|
|
325
|
+
},
|
|
326
|
+
dismiss,
|
|
327
|
+
input,
|
|
328
|
+
instanceId,
|
|
329
|
+
reject: (error) => {
|
|
330
|
+
settleInstance(instanceId, () => {
|
|
331
|
+
reject(isError(error) ? error : new ModalRejectError(error));
|
|
332
|
+
});
|
|
333
|
+
}
|
|
334
|
+
}),
|
|
335
|
+
status: "open"
|
|
336
|
+
};
|
|
337
|
+
instances = [...instances, instance];
|
|
338
|
+
publishState();
|
|
339
|
+
});
|
|
340
|
+
return Object.assign(result, {
|
|
341
|
+
dismiss: (reason) => {
|
|
342
|
+
dismissers.get(instanceId)?.(reason);
|
|
343
|
+
},
|
|
344
|
+
instanceId
|
|
345
|
+
});
|
|
301
346
|
},
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
set({ modals: [] });
|
|
347
|
+
setCloseDelayMs: (nextCloseDelayMs) => {
|
|
348
|
+
closeDelayMs = nextCloseDelayMs;
|
|
305
349
|
},
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
350
|
+
subscribe: (observer) => {
|
|
351
|
+
if (disposed) {
|
|
352
|
+
return () => void 0;
|
|
309
353
|
}
|
|
310
|
-
|
|
354
|
+
observers.add(observer);
|
|
355
|
+
return () => {
|
|
356
|
+
observers.delete(observer);
|
|
357
|
+
};
|
|
311
358
|
}
|
|
312
359
|
};
|
|
313
360
|
}
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
};
|
|
320
|
-
|
|
321
|
-
// src/store/modalStore.ts
|
|
322
|
-
function createModalStore() {
|
|
323
|
-
return createStore((set, get) => ({
|
|
324
|
-
...MODAL_STORE_DEFAULTS,
|
|
325
|
-
...createModalStoreActions(set, get)
|
|
326
|
-
}));
|
|
361
|
+
var ModalLifecycleContext = createContext(void 0);
|
|
362
|
+
function useModalLifecycle() {
|
|
363
|
+
const lifecycle = useContext(ModalLifecycleContext);
|
|
364
|
+
assertDefined(lifecycle, "Modal lifecycle must be used within ModalProvider");
|
|
365
|
+
return lifecycle;
|
|
327
366
|
}
|
|
328
|
-
function
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
367
|
+
function useModalLifecycleState() {
|
|
368
|
+
const lifecycle = useModalLifecycle();
|
|
369
|
+
return useSyncExternalStore(
|
|
370
|
+
lifecycle.subscribe,
|
|
371
|
+
lifecycle.getSnapshot,
|
|
372
|
+
lifecycle.getServerSnapshot
|
|
373
|
+
);
|
|
333
374
|
}
|
|
334
|
-
|
|
335
|
-
// src/store/modalStore.selectors.ts
|
|
336
|
-
var useModals = () => useModalStore((state) => state.modals);
|
|
337
375
|
var ModalRuntimeConfigContext = createContext({
|
|
338
376
|
closeDelayMs: 0,
|
|
339
377
|
confirmModal
|
|
@@ -344,104 +382,22 @@ function useModalRuntimeConfig() {
|
|
|
344
382
|
return useContext(ModalRuntimeConfigContext);
|
|
345
383
|
}
|
|
346
384
|
|
|
347
|
-
// src/hooks/useModalManager/useModalManager.
|
|
348
|
-
var DEFAULT_DISMISS_REASON = "dismiss";
|
|
385
|
+
// src/hooks/useModalManager/useModalManager.tsx
|
|
349
386
|
function useModalManager() {
|
|
350
|
-
const
|
|
351
|
-
const {
|
|
352
|
-
const scheduleRemove = useCallback(
|
|
353
|
-
(instanceId) => {
|
|
354
|
-
store.getState().scheduleRemove(instanceId, closeDelayMs);
|
|
355
|
-
},
|
|
356
|
-
[closeDelayMs, store]
|
|
357
|
-
);
|
|
358
|
-
const settleInstance = useCallback(
|
|
359
|
-
(instanceId, settle) => {
|
|
360
|
-
const instance = store.getState().modals.find((modal) => modal.instanceId === instanceId);
|
|
361
|
-
if (instance === void 0 || instance.status === "closing") {
|
|
362
|
-
return;
|
|
363
|
-
}
|
|
364
|
-
store.getState().markModalClosing(instanceId);
|
|
365
|
-
settle(instance);
|
|
366
|
-
scheduleRemove(instanceId);
|
|
367
|
-
},
|
|
368
|
-
[scheduleRemove, store]
|
|
369
|
-
);
|
|
370
|
-
const dismiss = useCallback(
|
|
371
|
-
(instanceId, reason = DEFAULT_DISMISS_REASON) => {
|
|
372
|
-
settleInstance(instanceId, (instance) => {
|
|
373
|
-
instance.dismiss(reason);
|
|
374
|
-
});
|
|
375
|
-
},
|
|
376
|
-
[settleInstance]
|
|
377
|
-
);
|
|
378
|
-
const closeAll = useCallback(
|
|
379
|
-
(reason) => {
|
|
380
|
-
const dismissReason = reason ?? "close-all";
|
|
381
|
-
const openInstanceIds = store.getState().modals.filter((modal) => modal.status === "open").map((modal) => modal.instanceId);
|
|
382
|
-
store.getState().dismissAll(dismissReason);
|
|
383
|
-
for (const instanceId of openInstanceIds) {
|
|
384
|
-
scheduleRemove(instanceId);
|
|
385
|
-
}
|
|
386
|
-
},
|
|
387
|
-
[scheduleRemove, store]
|
|
388
|
-
);
|
|
389
|
-
const open = useCallback(
|
|
390
|
-
(modal, input) => {
|
|
391
|
-
const storeState = store.getState();
|
|
392
|
-
const instanceId = storeState.allocateInstanceId();
|
|
393
|
-
const result = new Promise((resolve, reject) => {
|
|
394
|
-
const Component = modal.component;
|
|
395
|
-
const handleClose = (modalResult) => {
|
|
396
|
-
settleInstance(instanceId, () => {
|
|
397
|
-
resolve(modalResult);
|
|
398
|
-
});
|
|
399
|
-
};
|
|
400
|
-
const handleDismiss = (reason = DEFAULT_DISMISS_REASON) => {
|
|
401
|
-
settleInstance(instanceId, () => {
|
|
402
|
-
reject(new ModalDismissError(reason));
|
|
403
|
-
});
|
|
404
|
-
};
|
|
405
|
-
const handleReject = (error) => {
|
|
406
|
-
settleInstance(instanceId, () => {
|
|
407
|
-
reject(
|
|
408
|
-
error instanceof Error ? error : new ModalRejectError(error)
|
|
409
|
-
);
|
|
410
|
-
});
|
|
411
|
-
};
|
|
412
|
-
storeState.addModal({
|
|
413
|
-
instanceId,
|
|
414
|
-
status: "open",
|
|
415
|
-
definitionId: modal.id,
|
|
416
|
-
render: () => /* @__PURE__ */ jsx(
|
|
417
|
-
Component,
|
|
418
|
-
{
|
|
419
|
-
close: handleClose,
|
|
420
|
-
dismiss: handleDismiss,
|
|
421
|
-
instanceId,
|
|
422
|
-
input,
|
|
423
|
-
reject: handleReject
|
|
424
|
-
}
|
|
425
|
-
),
|
|
426
|
-
dismiss: (reason) => {
|
|
427
|
-
reject(new ModalDismissError(reason));
|
|
428
|
-
}
|
|
429
|
-
});
|
|
430
|
-
});
|
|
431
|
-
const dismissHandle = (reason) => {
|
|
432
|
-
dismiss(instanceId, reason);
|
|
433
|
-
};
|
|
434
|
-
return Object.assign(result, { dismiss: dismissHandle, instanceId });
|
|
435
|
-
},
|
|
436
|
-
[dismiss, settleInstance, store]
|
|
437
|
-
);
|
|
387
|
+
const lifecycle = useModalLifecycle();
|
|
388
|
+
const { confirmModal: confirmModal2 } = useModalRuntimeConfig();
|
|
438
389
|
const confirm = useCallback(
|
|
439
|
-
(params) => open(confirmModal2, params),
|
|
440
|
-
[confirmModal2,
|
|
390
|
+
(params) => lifecycle.open(confirmModal2, params),
|
|
391
|
+
[confirmModal2, lifecycle]
|
|
441
392
|
);
|
|
442
393
|
return useMemo(
|
|
443
|
-
() => ({
|
|
444
|
-
|
|
394
|
+
() => ({
|
|
395
|
+
closeAll: lifecycle.closeAll,
|
|
396
|
+
confirm,
|
|
397
|
+
dismiss: lifecycle.dismiss,
|
|
398
|
+
open: lifecycle.open
|
|
399
|
+
}),
|
|
400
|
+
[confirm, lifecycle]
|
|
445
401
|
);
|
|
446
402
|
}
|
|
447
403
|
function DefaultModalRenderer({ children }) {
|
|
@@ -450,7 +406,7 @@ function DefaultModalRenderer({ children }) {
|
|
|
450
406
|
function ModalViewport({
|
|
451
407
|
renderer: Renderer = DefaultModalRenderer
|
|
452
408
|
}) {
|
|
453
|
-
const modals =
|
|
409
|
+
const { instances: modals } = useModalLifecycleState();
|
|
454
410
|
return /* @__PURE__ */ jsx(Fragment, { children: modals.map((modal) => /* @__PURE__ */ jsx(
|
|
455
411
|
Renderer,
|
|
456
412
|
{
|
|
@@ -465,32 +421,35 @@ function ModalViewport({
|
|
|
465
421
|
)) });
|
|
466
422
|
}
|
|
467
423
|
function ModalRegistryBinder(props) {
|
|
468
|
-
const {
|
|
424
|
+
const { registry } = props;
|
|
469
425
|
const manager = useModalManager();
|
|
470
426
|
useEffect(() => {
|
|
471
|
-
const unbind =
|
|
427
|
+
const unbind = attachModalRegistry(registry, manager);
|
|
472
428
|
return () => {
|
|
473
429
|
unbind();
|
|
474
430
|
};
|
|
475
|
-
}, [
|
|
431
|
+
}, [registry, manager]);
|
|
476
432
|
return null;
|
|
477
433
|
}
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
434
|
+
function useModalLifecycleOwner(closeDelayMs) {
|
|
435
|
+
const [lifecycle] = useState(() => createModalLifecycle({ closeDelayMs }));
|
|
436
|
+
const mountGeneration = useRef(0);
|
|
437
|
+
useEffect(() => {
|
|
438
|
+
lifecycle.setCloseDelayMs(closeDelayMs);
|
|
439
|
+
}, [closeDelayMs, lifecycle]);
|
|
440
|
+
useEffect(() => {
|
|
441
|
+
mountGeneration.current += 1;
|
|
442
|
+
return () => {
|
|
443
|
+
mountGeneration.current += 1;
|
|
444
|
+
const cleanupGeneration = mountGeneration.current;
|
|
445
|
+
globalThis.queueMicrotask(() => {
|
|
446
|
+
if (mountGeneration.current === cleanupGeneration) {
|
|
447
|
+
lifecycle.dispose();
|
|
448
|
+
}
|
|
449
|
+
});
|
|
450
|
+
};
|
|
451
|
+
}, [lifecycle]);
|
|
452
|
+
return lifecycle;
|
|
494
453
|
}
|
|
495
454
|
function ModalProvider(props) {
|
|
496
455
|
const {
|
|
@@ -500,9 +459,9 @@ function ModalProvider(props) {
|
|
|
500
459
|
registry,
|
|
501
460
|
renderer
|
|
502
461
|
} = props;
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
462
|
+
if (isDefined(registry)) {
|
|
463
|
+
assertTrue(
|
|
464
|
+
isModalRegistryAttachable(registry),
|
|
506
465
|
"ModalProvider registry must be created by createModalRegistry"
|
|
507
466
|
);
|
|
508
467
|
}
|
|
@@ -510,11 +469,11 @@ function ModalProvider(props) {
|
|
|
510
469
|
() => ({ closeDelayMs, confirmModal: confirmModal2 }),
|
|
511
470
|
[closeDelayMs, confirmModal2]
|
|
512
471
|
);
|
|
513
|
-
|
|
472
|
+
const lifecycle = useModalLifecycleOwner(closeDelayMs);
|
|
473
|
+
return /* @__PURE__ */ jsx(ModalLifecycleContext.Provider, { value: lifecycle, children: /* @__PURE__ */ jsxs(ModalRuntimeConfigContext.Provider, { value: runtimeConfig, children: [
|
|
514
474
|
children,
|
|
515
|
-
|
|
516
|
-
/* @__PURE__ */ jsx(ModalViewport, { renderer })
|
|
517
|
-
/* @__PURE__ */ jsx(ModalStoreCleanup, {})
|
|
475
|
+
isDefined(registry) && /* @__PURE__ */ jsx(ModalRegistryBinder, { registry }),
|
|
476
|
+
/* @__PURE__ */ jsx(ModalViewport, { renderer })
|
|
518
477
|
] }) });
|
|
519
478
|
}
|
|
520
479
|
|