@inertiaui/modal-react 2.0.3 → 2.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.
@@ -1,1854 +1,1671 @@
1
1
  (function(global, factory) {
2
- typeof exports === "object" && typeof module !== "undefined" ? factory(exports, require("react"), require("react/jsx-runtime"), require("axios"), require("@inertiaui/vanilla"), require("@inertiajs/react"), require("@inertiajs/core"), require("react-dom")) : typeof define === "function" && define.amd ? define(["exports", "react", "react/jsx-runtime", "axios", "@inertiaui/vanilla", "@inertiajs/react", "@inertiajs/core", "react-dom"], factory) : (global = typeof globalThis !== "undefined" ? globalThis : global || self, factory(global.InertiaUIModal = {}, global.React, global.ReactJSXRuntime, global.axios, global.InertiaUIVanilla, global.InertiaReact, global.InertiaCore, global.ReactDOM));
3
- })(this, (function(exports2, React, jsxRuntime, Axios, vanilla, react, core, reactDom) {
4
- "use strict";
5
- function _interopNamespaceDefault(e) {
6
- const n = Object.create(null, { [Symbol.toStringTag]: { value: "Module" } });
7
- if (e) {
8
- for (const k in e) {
9
- if (k !== "default") {
10
- const d = Object.getOwnPropertyDescriptor(e, k);
11
- Object.defineProperty(n, k, d.get ? d : {
12
- enumerable: true,
13
- get: () => e[k]
14
- });
15
- }
16
- }
17
- }
18
- n.default = e;
19
- return Object.freeze(n);
20
- }
21
- const vanilla__namespace = /* @__PURE__ */ _interopNamespaceDefault(vanilla);
22
- const defaultConfig = {
23
- type: "modal",
24
- navigate: false,
25
- useNativeDialog: true,
26
- appElement: "#app",
27
- modal: {
28
- closeButton: true,
29
- closeExplicitly: false,
30
- closeOnClickOutside: true,
31
- maxWidth: "2xl",
32
- paddingClasses: "p-4 sm:p-6",
33
- panelClasses: "bg-white rounded",
34
- position: "center"
35
- },
36
- slideover: {
37
- closeButton: true,
38
- closeExplicitly: false,
39
- closeOnClickOutside: true,
40
- maxWidth: "md",
41
- paddingClasses: "p-4 sm:p-6",
42
- panelClasses: "bg-white min-h-screen",
43
- position: "right"
44
- }
45
- };
46
- class Config {
47
- constructor() {
48
- this.config = {};
49
- this.reset();
50
- }
51
- reset() {
52
- this.config = JSON.parse(JSON.stringify(defaultConfig));
53
- }
54
- put(key, value) {
55
- if (typeof key === "object") {
56
- this.config = {
57
- type: key.type ?? defaultConfig.type,
58
- navigate: key.navigate ?? defaultConfig.navigate,
59
- useNativeDialog: key.useNativeDialog ?? defaultConfig.useNativeDialog,
60
- appElement: key.appElement !== void 0 ? key.appElement : defaultConfig.appElement,
61
- modal: { ...defaultConfig.modal, ...key.modal ?? {} },
62
- slideover: { ...defaultConfig.slideover, ...key.slideover ?? {} }
63
- };
64
- return;
65
- }
66
- const keys = key.split(".");
67
- let current = this.config;
68
- for (let i = 0; i < keys.length - 1; i++) {
69
- current = current[keys[i]] = current[keys[i]] || {};
70
- }
71
- current[keys[keys.length - 1]] = value;
72
- }
73
- get(key) {
74
- if (typeof key === "undefined") {
75
- return this.config;
76
- }
77
- const keys = key.split(".");
78
- let current = this.config;
79
- for (const k of keys) {
80
- if (current === null || current === void 0 || typeof current !== "object") {
81
- return null;
82
- }
83
- current = current[k];
84
- }
85
- return current === void 0 ? null : current;
86
- }
87
- }
88
- const configInstance = new Config();
89
- const resetConfig = () => configInstance.reset();
90
- const putConfig = (key, value) => configInstance.put(key, value);
91
- const getConfig = (key) => configInstance.get(key);
92
- const getConfigByType = (isSlideover, key) => configInstance.get(isSlideover ? `slideover.${key}` : `modal.${key}`);
93
- function generateId(prefix = "inertiaui_") {
94
- return vanilla.generateId(prefix);
95
- }
96
- const ModalStackContext = React.createContext(null);
97
- ModalStackContext.displayName = "ModalStackContext";
98
- let pageVersion = null;
99
- let resolveComponent = null;
100
- let baseUrl = null;
101
- let closingToBaseUrlTarget = null;
102
- const prefetchCache = /* @__PURE__ */ new Map();
103
- const prefetchInFlight = /* @__PURE__ */ new Map();
104
- function getPrefetchCacheKey(url, method, data) {
105
- return `${method}:${url}:${JSON.stringify(data)}`;
106
- }
107
- function getCachedResponse(url, method, data) {
108
- const key = getPrefetchCacheKey(url, method, data);
109
- const cached = prefetchCache.get(key);
110
- if (!cached) {
111
- return null;
112
- }
113
- if (Date.now() > cached.expiresAt) {
114
- prefetchCache.delete(key);
115
- return null;
116
- }
117
- return cached.response;
118
- }
119
- function setCachedResponse(url, method, data, response, cacheFor) {
120
- const key = getPrefetchCacheKey(url, method, data);
121
- prefetchCache.set(key, {
122
- response,
123
- timestamp: Date.now(),
124
- expiresAt: Date.now() + cacheFor
125
- });
126
- }
127
- function prefetch(href, options = {}) {
128
- if (href.startsWith("#")) {
129
- return Promise.resolve();
130
- }
131
- const method = (options.method ?? "get").toLowerCase();
132
- const data = options.data ?? {};
133
- const headers = options.headers ?? {};
134
- const queryStringArrayFormat = options.queryStringArrayFormat ?? "brackets";
135
- const cacheFor = options.cacheFor ?? 3e4;
136
- const [url, mergedData] = core.mergeDataIntoQueryString(method, href || "", data, queryStringArrayFormat);
137
- const cached = getCachedResponse(url, method, mergedData);
138
- if (cached) {
139
- return Promise.resolve();
140
- }
141
- const cacheKey = getPrefetchCacheKey(url, method, mergedData);
142
- const inFlight = prefetchInFlight.get(cacheKey);
143
- if (inFlight) {
144
- return inFlight.then(() => {
145
- });
146
- }
147
- options.onPrefetching?.();
148
- const requestHeaders = {
149
- ...headers,
150
- Accept: "text/html, application/xhtml+xml",
151
- "X-Requested-With": "XMLHttpRequest",
152
- "X-Inertia": "true",
153
- "X-Inertia-Version": pageVersion ?? "",
154
- "X-InertiaUI-Modal": generateId(),
155
- "X-InertiaUI-Modal-Base-Url": baseUrl ?? ""
156
- };
157
- const request = Axios({
158
- url,
159
- method,
160
- data: mergedData,
161
- headers: requestHeaders
162
- }).then((response) => {
163
- setCachedResponse(url, method, mergedData, response, cacheFor);
164
- options.onPrefetched?.();
165
- return response;
166
- }).finally(() => {
167
- prefetchInFlight.delete(cacheKey);
168
- });
169
- prefetchInFlight.set(cacheKey, request);
170
- return request.then(() => {
171
- });
172
- }
173
- const ModalStackProvider = ({ children }) => {
174
- const stackRef = React.useRef([]);
175
- const [, forceUpdate] = React.useReducer((x) => x + 1, 0);
176
- const [localModals, setLocalModals] = React.useState({});
177
- const updateStack = (withStack) => {
178
- const newStack = withStack([...stackRef.current]);
179
- const isOnTopOfStack = (modalId) => {
180
- if (newStack.length < 2) {
181
- return true;
182
- }
183
- return newStack.map((modal) => ({ id: modal.id, shouldRender: modal.shouldRender })).reverse().find((modal) => modal.shouldRender)?.id === modalId;
184
- };
185
- newStack.forEach((modal, index) => {
186
- newStack[index].onTopOfStack = isOnTopOfStack(modal.id);
187
- newStack[index].getParentModal = () => {
188
- if (index < 1) {
189
- return null;
190
- }
191
- return stackRef.current.slice(0, index).reverse().find((m) => m.isOpen) ?? null;
192
- };
193
- newStack[index].getChildModal = () => {
194
- if (index === stackRef.current.length - 1) {
195
- return null;
196
- }
197
- return stackRef.current.slice(index + 1).find((m) => m.isOpen) ?? null;
198
- };
199
- });
200
- stackRef.current = newStack;
201
- forceUpdate();
202
- };
203
- class ModalClass {
204
- constructor(component, response, config, onClose, afterLeave) {
205
- this.show = () => {
206
- updateStack(
207
- (prevStack) => prevStack.map((modal) => {
208
- if (modal.id === this.id && !modal.isOpen) {
209
- modal.isOpen = true;
210
- modal.shouldRender = true;
211
- }
212
- return modal;
213
- })
214
- );
215
- };
216
- this.setOpen = (open) => {
217
- if (open) {
218
- this.show();
219
- } else {
220
- this.close();
221
- }
222
- };
223
- this.close = () => {
224
- updateStack((currentStack) => {
225
- let modalClosed = false;
226
- const newStack = currentStack.map((modal) => {
227
- if (modal.id === this.id && modal.isOpen) {
228
- Object.keys(modal.listeners).forEach((event) => {
229
- modal.off(event);
230
- });
231
- modal.isOpen = false;
232
- modal.onCloseCallback?.();
233
- modalClosed = true;
234
- }
235
- return modal;
236
- });
237
- return modalClosed ? newStack : currentStack;
238
- });
239
- };
240
- this.afterLeave = () => {
241
- if (this.isOpen) {
242
- return;
243
- }
244
- updateStack((prevStack) => {
245
- const updatedStack = prevStack.map((modal) => {
246
- if (modal.id === this.id && !modal.isOpen) {
247
- modal.shouldRender = false;
248
- modal.afterLeaveCallback?.();
249
- modal.afterLeaveCallback = null;
250
- }
251
- return modal;
252
- });
253
- if (this.index === 0) {
254
- const savedBaseUrl = baseUrl;
255
- baseUrl = null;
256
- closingToBaseUrlTarget = savedBaseUrl;
257
- if (savedBaseUrl && typeof window !== "undefined" && !vanilla.sameUrlPath(savedBaseUrl, window.location.href)) {
258
- react.router.push({
259
- url: savedBaseUrl,
260
- preserveScroll: true,
261
- preserveState: true,
262
- // Clear _inertiaui_modal prop to prevent modal from reopening
263
- props: (currentProps) => {
264
- const { _inertiaui_modal, ...rest } = currentProps;
265
- return { ...rest, _inertiaui_modal: void 0 };
266
- }
267
- });
268
- }
269
- return [];
270
- }
271
- return updatedStack;
272
- });
273
- };
274
- this.on = (event, callback) => {
275
- event = vanilla.kebabCase(event);
276
- this.listeners[event] = this.listeners[event] ?? [];
277
- this.listeners[event].push(callback);
278
- };
279
- this.off = (event, callback) => {
280
- event = vanilla.kebabCase(event);
281
- if (callback) {
282
- this.listeners[event] = this.listeners[event]?.filter((cb) => cb !== callback) ?? [];
283
- } else {
284
- delete this.listeners[event];
285
- }
286
- };
287
- this.emit = (event, ...args) => {
288
- this.listeners[vanilla.kebabCase(event)]?.forEach((callback) => callback(...args));
289
- };
290
- this.registerEventListenersFromProps = (props) => {
291
- const unsubscribers = [];
292
- Object.keys(props).filter((key) => key.startsWith("on")).forEach((key) => {
293
- const eventName = vanilla.kebabCase(key).replace(/^on-/, "");
294
- const callback = props[key];
295
- this.on(eventName, callback);
296
- unsubscribers.push(() => this.off(eventName, callback));
297
- });
298
- return () => unsubscribers.forEach((unsub) => unsub());
299
- };
300
- this.reload = (options = {}) => {
301
- let keys = Object.keys(this.response.props);
302
- if (options.only) {
303
- keys = options.only;
304
- }
305
- if (options.except) {
306
- keys = vanilla.except(keys, options.except);
307
- }
308
- if (!this.response?.url) {
309
- return;
310
- }
311
- const method = (options.method ?? "get").toLowerCase();
312
- const data = options.data ?? {};
313
- options.onStart?.();
314
- Axios({
315
- url: this.response.url,
316
- method,
317
- data: method === "get" ? {} : data,
318
- params: method === "get" ? data : {},
319
- headers: {
320
- ...options.headers ?? {},
321
- Accept: "text/html, application/xhtml+xml",
322
- "X-Inertia": "true",
323
- "X-Inertia-Partial-Component": this.response.component,
324
- "X-Inertia-Version": this.response.version ?? "",
325
- "X-Inertia-Partial-Data": keys.join(","),
326
- "X-InertiaUI-Modal": generateId(),
327
- "X-InertiaUI-Modal-Base-Url": baseUrl ?? ""
328
- }
329
- }).then((response2) => {
330
- this.updateProps(response2.data.props);
331
- options.onSuccess?.(response2);
332
- }).catch((error) => {
333
- options.onError?.(error);
334
- }).finally(() => {
335
- options.onFinish?.();
336
- });
337
- };
338
- this.updateProps = (props) => {
339
- Object.assign(this.props, props);
340
- updateStack((prevStack) => prevStack);
341
- };
342
- this.id = response.id ?? generateId();
343
- this.isOpen = false;
344
- this.shouldRender = false;
345
- this.listeners = {};
346
- this.component = component;
347
- this.props = response.props ?? {};
348
- this.response = response;
349
- this.config = config ?? {};
350
- this.onCloseCallback = onClose ?? null;
351
- this.afterLeaveCallback = afterLeave ?? null;
352
- this.index = -1;
353
- this.getParentModal = () => null;
354
- this.getChildModal = () => null;
355
- this.onTopOfStack = true;
356
- }
357
- }
358
- const isValidModalResponse = (data) => {
359
- return typeof data === "object" && data !== null && "component" in data && typeof data.component === "string";
360
- };
361
- const pushFromResponseData = (responseData, config = {}, onClose = null, onAfterLeave = null) => {
362
- if (!resolveComponent) {
363
- return Promise.reject(new Error("resolveComponent not set"));
364
- }
365
- if (!isValidModalResponse(responseData)) {
366
- return Promise.reject(
367
- new Error(
368
- "Invalid modal response. This usually happens when the server returns a redirect (e.g., due to session expiration). Check if the user is still authenticated."
369
- )
370
- );
371
- }
372
- return resolveComponent(responseData.component).then(
373
- (component) => push(component, responseData, config, onClose, onAfterLeave)
374
- );
375
- };
376
- const loadDeferredProps = (modal) => {
377
- const deferred = modal.response?.meta?.deferredProps;
378
- if (!deferred) {
379
- return;
380
- }
381
- Object.keys(deferred).forEach((key) => {
382
- modal.reload({ only: deferred[key] });
383
- });
384
- };
385
- const push = (component, response, config, onClose, afterLeave) => {
386
- const newModal = new ModalClass(component, response, config, onClose, afterLeave);
387
- newModal.index = stackRef.current.length;
388
- updateStack((prevStack) => [...prevStack, newModal]);
389
- loadDeferredProps(newModal);
390
- newModal.show();
391
- return newModal;
392
- };
393
- function pushLocalModal(name, config, onClose, afterLeave, props) {
394
- if (!localModals[name]) {
395
- throw new Error(`The local modal "${name}" has not been registered.`);
396
- }
397
- const responseData = { props: props ?? {} };
398
- const modal = push(null, responseData, config, onClose, afterLeave);
399
- modal.name = name;
400
- localModals[name].callback(modal);
401
- return modal;
402
- }
403
- const visitModal = (url, options = {}) => visit(
404
- url,
405
- options.method ?? "get",
406
- options.data ?? {},
407
- options.headers ?? {},
408
- options.config ?? {},
409
- options.onClose ?? null,
410
- options.onAfterLeave ?? null,
411
- options.queryStringArrayFormat ?? "brackets",
412
- options.navigate ?? getConfig("navigate"),
413
- options.onStart ?? null,
414
- options.onSuccess ?? null,
415
- options.onError ?? null,
416
- options.props ?? null
417
- ).then((modal) => {
418
- const listeners = options.listeners ?? {};
419
- Object.keys(listeners).forEach((event) => {
420
- const eventName = vanilla.kebabCase(event);
421
- modal.on(eventName, listeners[event]);
422
- });
423
- return modal;
424
- });
425
- const updateBrowserUrl = (url, useBrowserHistory, modalData) => {
426
- if (!url || !useBrowserHistory || typeof window === "undefined") {
427
- return;
428
- }
429
- react.router.push({
430
- url,
431
- preserveScroll: true,
432
- preserveState: true,
433
- // Store modal data in page props for history navigation
434
- props: modalData ? (currentProps) => ({
435
- ...currentProps,
436
- _inertiaui_modal: {
437
- ...modalData,
438
- baseUrl
439
- }
440
- }) : void 0
441
- });
442
- };
443
- const visit = (href, method, payload = {}, headers = {}, config = {}, onClose = null, onAfterLeave = null, queryStringArrayFormat = "brackets", useBrowserHistory = false, onStart = null, onSuccess = null, onError = null, props = null) => {
444
- const modalId = generateId();
445
- return new Promise((resolve, reject) => {
446
- if (href.startsWith("#")) {
447
- resolve(pushLocalModal(href.substring(1), config, onClose, onAfterLeave, props));
448
- return;
449
- }
450
- const [url, data] = core.mergeDataIntoQueryString(method, href || "", payload, queryStringArrayFormat);
451
- const cachedResponse = getCachedResponse(url, method, data);
452
- if (cachedResponse) {
453
- onSuccess?.(cachedResponse);
454
- pushFromResponseData(cachedResponse.data, config, onClose, onAfterLeave).then((modal) => {
455
- updateBrowserUrl(cachedResponse.data.url, useBrowserHistory, cachedResponse.data);
456
- resolve(modal);
457
- }).catch(reject);
458
- return;
459
- }
460
- if (stackRef.current.length === 0) {
461
- baseUrl = typeof window !== "undefined" ? window.location.href : "";
462
- }
463
- const requestHeaders = {
464
- ...headers,
465
- Accept: "text/html, application/xhtml+xml",
466
- "X-Requested-With": "XMLHttpRequest",
467
- "X-Inertia": "true",
468
- "X-Inertia-Version": pageVersion ?? "",
469
- "X-InertiaUI-Modal": modalId,
470
- "X-InertiaUI-Modal-Base-Url": baseUrl ?? ""
471
- };
472
- onStart?.();
473
- react.progress?.start();
474
- Axios({
475
- url,
476
- method,
477
- data,
478
- headers: requestHeaders
479
- }).then((response) => {
480
- onSuccess?.(response);
481
- pushFromResponseData(response.data, config, onClose, onAfterLeave).then((modal) => {
482
- updateBrowserUrl(response.data.url, useBrowserHistory, response.data);
483
- resolve(modal);
484
- }).catch(reject);
485
- }).catch((...args) => {
486
- onError?.(...args);
487
- reject(args[0]);
488
- }).finally(() => {
489
- react.progress?.finish();
490
- });
491
- });
492
- };
493
- const registerLocalModal = (name, callback) => {
494
- setLocalModals((prevLocalModals) => ({
495
- ...prevLocalModals,
496
- [name]: { name, callback }
497
- }));
498
- };
499
- const removeLocalModal = (name) => {
500
- setLocalModals((prevLocalModals) => {
501
- const newLocalModals = { ...prevLocalModals };
502
- delete newLocalModals[name];
503
- return newLocalModals;
504
- });
505
- };
506
- const value = {
507
- get stack() {
508
- return stackRef.current;
509
- },
510
- localModals,
511
- push,
512
- pushFromResponseData,
513
- length: () => stackRef.current.length,
514
- closeAll: (force = false) => {
515
- if (force) {
516
- updateStack(() => []);
517
- } else {
518
- [...stackRef.current].reverse().forEach((modal) => modal.close());
519
- }
520
- },
521
- reset: () => updateStack(() => []),
522
- visit,
523
- visitModal,
524
- registerLocalModal,
525
- removeLocalModal
526
- };
527
- return /* @__PURE__ */ jsxRuntime.jsx(ModalStackContext.Provider, { value, children });
528
- };
529
- const useModalStack = () => {
530
- const context = React.useContext(ModalStackContext);
531
- if (context === null) {
532
- throw new Error("useModalStack must be used within a ModalStackProvider");
533
- }
534
- return context;
535
- };
536
- const modalPropNames = ["closeButton", "closeExplicitly", "closeOnClickOutside", "maxWidth", "paddingClasses", "panelClasses", "position", "slideover"];
537
- const initFromPageProps = (pageProps) => {
538
- if (pageProps.initialPage) {
539
- pageVersion = pageProps.initialPage.version ?? null;
540
- }
541
- if (pageProps.resolveComponent) {
542
- resolveComponent = pageProps.resolveComponent;
543
- }
544
- };
545
- const renderApp = (App, pageProps) => {
546
- initFromPageProps(pageProps);
547
- const renderInertiaApp = ({ Component, props, key }) => {
548
- const renderComponent = () => {
549
- const child = React.createElement(Component, { key, ...props });
550
- if (typeof Component.layout === "function") {
551
- return Component.layout(child);
552
- }
553
- if (Array.isArray(Component.layout)) {
554
- return Component.layout.slice().reverse().reduce(
555
- (acc, Layout) => React.createElement(Layout, props, acc),
556
- child
557
- );
558
- }
559
- return child;
560
- };
561
- return /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
562
- renderComponent(),
563
- /* @__PURE__ */ jsxRuntime.jsx(ModalRoot, {})
564
- ] });
565
- };
566
- return /* @__PURE__ */ jsxRuntime.jsx(ModalStackProvider, { children: /* @__PURE__ */ jsxRuntime.jsx(App, { ...pageProps, children: renderInertiaApp }) });
567
- };
568
- const ModalRoot = ({ children }) => {
569
- const context = React.useContext(ModalStackContext);
570
- const $page = react.usePage();
571
- const pendingModalKeysRef = React.useRef(/* @__PURE__ */ new Set());
572
- const getModalKey = (modalData) => modalData.id || `${modalData.component}:${modalData.url}`;
573
- const isNavigatingRef = React.useRef(false);
574
- const initialModalStillOpenedRef = React.useRef(!!$page.props?._inertiaui_modal);
575
- React.useEffect(() => react.router.on("start", () => isNavigatingRef.current = true), []);
576
- React.useEffect(() => react.router.on("finish", () => isNavigatingRef.current = false), []);
577
- React.useEffect(
578
- () => react.router.on("navigate", function($event) {
579
- const modalOnBase = $event.detail.page.props._inertiaui_modal;
580
- const pageUrl = $event.detail.page.url;
581
- if (closingToBaseUrlTarget) {
582
- const targetPath = new URL(closingToBaseUrlTarget, "http://x").pathname;
583
- const pagePath = new URL(pageUrl, "http://x").pathname;
584
- if (targetPath === pagePath) {
585
- closingToBaseUrlTarget = null;
586
- context?.closeAll(true);
587
- baseUrl = null;
588
- initialModalStillOpenedRef.current = false;
589
- return;
590
- }
591
- closingToBaseUrlTarget = null;
592
- }
593
- if (!modalOnBase) {
594
- context?.closeAll(true);
595
- baseUrl = null;
596
- initialModalStillOpenedRef.current = false;
597
- return;
598
- }
599
- if (!vanilla.sameUrlPath(pageUrl, modalOnBase.url)) {
600
- context?.closeAll(true);
601
- baseUrl = null;
602
- initialModalStillOpenedRef.current = false;
603
- return;
604
- }
605
- const modalKey = getModalKey(modalOnBase);
606
- if (pendingModalKeysRef.current.has(modalKey)) {
607
- return;
608
- }
609
- if (modalOnBase.id && context?.stack.some((m) => m.id === modalOnBase.id)) {
610
- return;
611
- }
612
- if (context?.stack.some((m) => m.response?.component === modalOnBase.component && vanilla.sameUrlPath(m.response?.url, modalOnBase.url))) {
613
- return;
614
- }
615
- baseUrl = modalOnBase.baseUrl;
616
- pendingModalKeysRef.current.add(modalKey);
617
- context?.pushFromResponseData(modalOnBase, {}, () => {
618
- if (!modalOnBase.baseUrl) {
619
- console.error("No base url in modal response data so cannot navigate back");
620
- return;
621
- }
622
- if (!isNavigatingRef.current && typeof window !== "undefined" && window.location.href !== modalOnBase.baseUrl) {
623
- react.router.visit(modalOnBase.baseUrl, {
624
- preserveScroll: true,
625
- preserveState: true
626
- });
627
- }
628
- }).finally(() => {
629
- pendingModalKeysRef.current.delete(modalKey);
630
- });
631
- }),
632
- []
633
- );
634
- const axiosRequestInterceptor = (config) => {
635
- const baseUrlValue = baseUrl ?? (initialModalStillOpenedRef.current ? $page.props._inertiaui_modal?.baseUrl : null);
636
- if (baseUrlValue) {
637
- config.headers["X-InertiaUI-Modal-Base-Url"] = baseUrlValue;
638
- }
639
- return config;
640
- };
641
- React.useEffect(() => {
642
- const interceptorId = Axios.interceptors.request.use(axiosRequestInterceptor);
643
- return () => Axios.interceptors.request.eject(interceptorId);
644
- }, []);
645
- const previousModalRef = React.useRef(void 0);
646
- React.useEffect(() => {
647
- const newModal = $page.props?._inertiaui_modal;
648
- const previousModal = previousModalRef.current;
649
- previousModalRef.current = newModal;
650
- if (!newModal) {
651
- return;
652
- }
653
- if (previousModal && newModal.component === previousModal.component && vanilla.sameUrlPath(newModal.url, previousModal.url)) {
654
- context?.stack[0]?.updateProps(newModal.props ?? {});
655
- return;
656
- }
657
- if (!previousModal && context && context.stack.length > 0) {
658
- const existingModal = context.stack.find(
659
- (m) => m.response?.component === newModal.component && vanilla.sameUrlPath(m.response?.url, newModal.url)
660
- );
661
- if (existingModal) {
662
- existingModal.updateProps(newModal.props ?? {});
663
- }
664
- }
665
- }, [$page.props?._inertiaui_modal]);
666
- return /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
667
- children,
668
- context && context.stack.length > 0 && /* @__PURE__ */ jsxRuntime.jsx(ModalRenderer, { index: 0 })
669
- ] });
670
- };
671
- const ModalIndexContext = React.createContext(null);
672
- ModalIndexContext.displayName = "ModalIndexContext";
673
- const useModalIndex = () => {
674
- return React.useContext(ModalIndexContext);
675
- };
676
- const ModalRenderer = ({ index }) => {
677
- const { stack } = useModalStack();
678
- const modalContext = React.useMemo(() => {
679
- return stack[index];
680
- }, [stack, index]);
681
- if (!modalContext?.component) {
682
- return null;
683
- }
684
- return /* @__PURE__ */ jsxRuntime.jsx(ModalIndexContext.Provider, { value: index, children: React.createElement(modalContext.component, {
685
- ...modalContext.props,
686
- onModalEvent: (...args) => modalContext.emit("modal-event", ...args)
687
- }) });
688
- };
689
- function useModal() {
690
- return useModalStack().stack[useModalIndex()] ?? null;
691
- }
692
- const Deferred = ({ children, data, fallback }) => {
693
- if (!data) {
694
- throw new Error("`<Deferred>` requires a `data` prop to be a string or array of strings");
695
- }
696
- const [loaded, setLoaded] = React.useState(false);
697
- const keys = Array.isArray(data) ? data : [data];
698
- const modal = useModal();
699
- const modalProps = modal?.props ?? {};
700
- React.useEffect(() => {
701
- setLoaded(keys.every((key) => modalProps[key] !== void 0));
702
- }, [modalProps, keys]);
703
- return loaded ? children : fallback;
704
- };
705
- Deferred.displayName = "InertiaModalDeferred";
706
- const HeadlessModal = React.forwardRef(
707
- (allProps, ref) => {
708
- const { name, children, onFocus, onBlur, onClose, onSuccess, ...props } = allProps;
709
- const modalIndex = useModalIndex();
710
- const { stack, registerLocalModal, removeLocalModal } = useModalStack();
711
- const [localModalContext, setLocalModalContext] = React.useState(null);
712
- const modalContext = React.useMemo(
713
- () => name ? localModalContext : stack[modalIndex],
714
- [name, localModalContext, modalIndex, stack]
715
- );
716
- const nextIndex = React.useMemo(() => {
717
- return stack.find((m) => m.shouldRender && m.index > (modalContext?.index ?? -1))?.index;
718
- }, [modalIndex, stack]);
719
- const configSlideover = React.useMemo(
720
- () => modalContext?.config.slideover ?? props.slideover ?? getConfig("type") === "slideover",
721
- [props.slideover, modalContext?.config.slideover]
722
- );
723
- const config = React.useMemo(
724
- () => ({
725
- slideover: configSlideover,
726
- closeButton: props.closeButton ?? getConfigByType(configSlideover, "closeButton"),
727
- closeExplicitly: props.closeExplicitly ?? getConfigByType(configSlideover, "closeExplicitly"),
728
- closeOnClickOutside: props.closeOnClickOutside ?? getConfigByType(configSlideover, "closeOnClickOutside"),
729
- maxWidth: props.maxWidth ?? getConfigByType(configSlideover, "maxWidth"),
730
- paddingClasses: props.paddingClasses ?? getConfigByType(configSlideover, "paddingClasses"),
731
- panelClasses: props.panelClasses ?? getConfigByType(configSlideover, "panelClasses"),
732
- position: props.position ?? getConfigByType(configSlideover, "position"),
733
- ...modalContext?.config
734
- }),
735
- [props, modalContext?.config, configSlideover]
736
- );
737
- React.useEffect(() => {
738
- if (name) {
739
- let removeListeners = null;
740
- registerLocalModal(name, (localContext) => {
741
- removeListeners = localContext.registerEventListenersFromProps(props);
742
- setLocalModalContext(localContext);
743
- });
744
- return () => {
745
- removeListeners?.();
746
- removeListeners = null;
747
- removeLocalModal(name);
748
- };
749
- }
750
- return modalContext?.registerEventListenersFromProps(props);
751
- }, [name]);
752
- const modalContextRef = React.useRef(modalContext);
753
- React.useEffect(() => {
754
- modalContextRef.current = modalContext;
755
- }, [modalContext]);
756
- const previousIsOpenRef = React.useRef(void 0);
757
- React.useEffect(() => {
758
- if (modalContext != null) {
759
- if (modalContext.isOpen) {
760
- onSuccess?.();
761
- } else if (previousIsOpenRef.current === true) {
762
- onClose?.();
763
- }
764
- previousIsOpenRef.current = modalContext.isOpen;
765
- }
766
- }, [modalContext?.isOpen]);
767
- const [rendered, setRendered] = React.useState(false);
768
- React.useEffect(() => {
769
- if (rendered && modalContext != null && modalContext.isOpen) {
770
- if (modalContext.onTopOfStack) {
771
- onFocus?.();
772
- } else {
773
- onBlur?.();
774
- }
775
- }
776
- setRendered(true);
777
- }, [modalContext?.onTopOfStack]);
778
- React.useImperativeHandle(
779
- ref,
780
- () => ({
781
- afterLeave: () => modalContextRef.current?.afterLeave(),
782
- close: () => modalContextRef.current?.close(),
783
- emit: (...args) => modalContextRef.current?.emit(...args),
784
- getChildModal: () => modalContextRef.current?.getChildModal(),
785
- getParentModal: () => modalContextRef.current?.getParentModal(),
786
- reload: (options) => modalContextRef.current?.reload(options),
787
- setOpen: (open) => modalContextRef.current?.setOpen(open),
788
- get id() {
789
- return modalContextRef.current?.id;
790
- },
791
- get index() {
792
- return modalContextRef.current?.index;
793
- },
794
- get isOpen() {
795
- return modalContextRef.current?.isOpen;
796
- },
797
- get config() {
798
- return modalContextRef.current?.config;
799
- },
800
- get modalContext() {
801
- return modalContextRef.current;
802
- },
803
- get onTopOfStack() {
804
- return modalContextRef.current?.onTopOfStack;
805
- },
806
- get shouldRender() {
807
- return modalContextRef.current?.shouldRender;
808
- }
809
- }),
810
- [modalContext]
811
- );
812
- if (!modalContext?.shouldRender) {
813
- return null;
814
- }
815
- return /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
816
- typeof children === "function" ? children({
817
- // Spread props first so they can be overridden by built-in props
818
- ...modalContext.props,
819
- afterLeave: modalContext.afterLeave,
820
- close: modalContext.close,
821
- config,
822
- emit: modalContext.emit,
823
- getChildModal: modalContext.getChildModal,
824
- getParentModal: modalContext.getParentModal,
825
- id: modalContext.id,
826
- index: modalContext.index,
827
- isOpen: modalContext.isOpen,
828
- modalContext,
829
- onTopOfStack: modalContext.onTopOfStack,
830
- reload: modalContext.reload,
831
- setOpen: modalContext.setOpen,
832
- shouldRender: modalContext.shouldRender
833
- }) : children,
834
- nextIndex !== void 0 && /* @__PURE__ */ jsxRuntime.jsx(ModalRenderer, { index: nextIndex })
835
- ] });
836
- }
837
- );
838
- HeadlessModal.displayName = "HeadlessModal";
839
- function CloseButton({ onClick }) {
840
- return /* @__PURE__ */ jsxRuntime.jsxs(
841
- "button",
842
- {
843
- type: "button",
844
- className: "im-close-button text-gray-400 hover:text-gray-500",
845
- onClick,
846
- children: [
847
- /* @__PURE__ */ jsxRuntime.jsx("span", { className: "sr-only", children: "Close" }),
848
- /* @__PURE__ */ jsxRuntime.jsx(
849
- "svg",
850
- {
851
- className: "size-6",
852
- xmlns: "http://www.w3.org/2000/svg",
853
- fill: "none",
854
- viewBox: "0 0 24 24",
855
- strokeWidth: "2",
856
- stroke: "currentColor",
857
- "aria-hidden": "true",
858
- children: /* @__PURE__ */ jsxRuntime.jsx(
859
- "path",
860
- {
861
- strokeLinecap: "round",
862
- strokeLinejoin: "round",
863
- d: "M6 18L18 6M6 6l12 12"
864
- }
865
- )
866
- }
867
- )
868
- ]
869
- }
870
- );
871
- }
872
- function r(e) {
873
- var t, f, n = "";
874
- if ("string" == typeof e || "number" == typeof e) n += e;
875
- else if ("object" == typeof e) if (Array.isArray(e)) {
876
- var o = e.length;
877
- for (t = 0; t < o; t++) e[t] && (f = r(e[t])) && (n && (n += " "), n += f);
878
- } else for (f in e) e[f] && (n && (n += " "), n += f);
879
- return n;
880
- }
881
- function clsx() {
882
- for (var e, t, f = 0, n = "", o = arguments.length; f < o; f++) (e = arguments[f]) && (t = r(e)) && (n && (n += " "), n += t);
883
- return n;
884
- }
885
- const maxWidthClasses = {
886
- sm: "sm:max-w-sm",
887
- md: "sm:max-w-md",
888
- lg: "sm:max-w-md md:max-w-lg",
889
- xl: "sm:max-w-md md:max-w-xl",
890
- "2xl": "sm:max-w-md md:max-w-xl lg:max-w-2xl",
891
- "3xl": "sm:max-w-md md:max-w-xl lg:max-w-3xl",
892
- "4xl": "sm:max-w-md md:max-w-xl lg:max-w-3xl xl:max-w-4xl",
893
- "5xl": "sm:max-w-md md:max-w-xl lg:max-w-3xl xl:max-w-5xl",
894
- "6xl": "sm:max-w-md md:max-w-xl lg:max-w-3xl xl:max-w-5xl 2xl:max-w-6xl",
895
- "7xl": "sm:max-w-md md:max-w-xl lg:max-w-3xl xl:max-w-5xl 2xl:max-w-7xl"
896
- };
897
- const defaultMaxWidth = "2xl";
898
- function getMaxWidthClass(maxWidth) {
899
- return maxWidthClasses[maxWidth] || maxWidthClasses[defaultMaxWidth];
900
- }
901
- const ModalContent = ({ modalContext, config, useNativeDialog, isFirstModal, onAfterLeave, children }) => {
902
- const [isRendered, setIsRendered] = React.useState(false);
903
- const [isVisible, setIsVisible] = React.useState(false);
904
- const [entered, setEntered] = React.useState(false);
905
- const wrapperRef = React.useRef(null);
906
- const dialogRef = React.useRef(null);
907
- const nativeWrapperRef = React.useRef(null);
908
- const cleanupFocusTrapRef = React.useRef(null);
909
- const cleanupEscapeKeyRef = React.useRef(null);
910
- const maxWidthClass = React.useMemo(() => getMaxWidthClass(config.maxWidth), [config.maxWidth]);
911
- const animateIn = React.useCallback(async (element) => {
912
- if (!element) return;
913
- setIsVisible(true);
914
- await vanilla.animate(element, [
915
- { transform: "translate3d(0, 1rem, 0) scale(0.95)", opacity: 0 },
916
- { transform: "translate3d(0, 0, 0) scale(1)", opacity: 1 }
917
- ]);
918
- setEntered(true);
919
- }, []);
920
- const animateOut = React.useCallback(
921
- async (element) => {
922
- if (!element) return;
923
- setIsVisible(false);
924
- await vanilla.animate(element, [
925
- { transform: "translate3d(0, 0, 0) scale(1)", opacity: 1 },
926
- { transform: "translate3d(0, 1rem, 0) scale(0.95)", opacity: 0 }
927
- ]);
928
- setIsRendered(false);
929
- if (useNativeDialog && dialogRef.current) {
930
- dialogRef.current.close();
931
- }
932
- onAfterLeave?.();
933
- modalContext.afterLeave();
934
- },
935
- [useNativeDialog, onAfterLeave, modalContext]
936
- );
937
- const setupFocusTrap = React.useCallback(() => {
938
- if (useNativeDialog) return;
939
- if (!wrapperRef.current || !modalContext.onTopOfStack) return;
940
- if (cleanupFocusTrapRef.current) return;
941
- cleanupFocusTrapRef.current = vanilla.createFocusTrap(wrapperRef.current, {
942
- initialFocus: true,
943
- returnFocus: false
944
- });
945
- }, [modalContext.onTopOfStack, useNativeDialog]);
946
- const cleanupFocusTrap = React.useCallback(() => {
947
- if (cleanupFocusTrapRef.current) {
948
- cleanupFocusTrapRef.current();
949
- cleanupFocusTrapRef.current = null;
950
- }
951
- }, []);
952
- const setupEscapeKey = React.useCallback(() => {
953
- if (useNativeDialog) return;
954
- if (cleanupEscapeKeyRef.current) return;
955
- if (config?.closeExplicitly) return;
956
- cleanupEscapeKeyRef.current = vanilla.onEscapeKey(() => {
957
- if (modalContext.onTopOfStack) {
958
- modalContext.close();
959
- }
960
- });
961
- }, [config?.closeExplicitly, modalContext, useNativeDialog]);
962
- const cleanupEscapeKey = React.useCallback(() => {
963
- if (cleanupEscapeKeyRef.current) {
964
- cleanupEscapeKeyRef.current();
965
- cleanupEscapeKeyRef.current = null;
966
- }
967
- }, []);
968
- const handleClickOutside = React.useCallback(
969
- (event) => {
970
- if (useNativeDialog) return;
971
- if (!modalContext.onTopOfStack) return;
972
- if (config?.closeExplicitly) return;
973
- if (config?.closeOnClickOutside === false) return;
974
- if (!wrapperRef.current) return;
975
- if (!wrapperRef.current.contains(event.target)) {
976
- modalContext.close();
977
- }
978
- },
979
- [modalContext, config?.closeExplicitly, config?.closeOnClickOutside, useNativeDialog]
980
- );
981
- const handleCancel = React.useCallback(
982
- (event) => {
983
- event.preventDefault();
984
- if (modalContext.onTopOfStack && !config?.closeExplicitly) {
985
- modalContext.close();
986
- }
987
- },
988
- [modalContext, config?.closeExplicitly]
989
- );
990
- const handleDialogClick = React.useCallback(
991
- (event) => {
992
- if (event.target === dialogRef.current) {
993
- if (modalContext.onTopOfStack && !config?.closeExplicitly && config?.closeOnClickOutside !== false) {
994
- modalContext.close();
995
- }
996
- }
997
- },
998
- [modalContext, config?.closeExplicitly, config?.closeOnClickOutside]
999
- );
1000
- const prevIsOpenRef = React.useRef(modalContext.isOpen);
1001
- React.useEffect(() => {
1002
- if (useNativeDialog) {
1003
- if (modalContext.isOpen && !dialogRef.current?.open) {
1004
- dialogRef.current?.showModal();
1005
- animateIn(nativeWrapperRef.current);
1006
- } else if (!modalContext.isOpen && prevIsOpenRef.current) {
1007
- setEntered(false);
1008
- animateOut(nativeWrapperRef.current);
1009
- }
1010
- } else {
1011
- if (modalContext.isOpen && !isRendered) {
1012
- setIsRendered(true);
1013
- } else if (!modalContext.isOpen && prevIsOpenRef.current) {
1014
- setEntered(false);
1015
- animateOut(wrapperRef.current);
1016
- }
1017
- }
1018
- prevIsOpenRef.current = modalContext.isOpen;
1019
- }, [modalContext.isOpen, useNativeDialog, animateIn, animateOut, isRendered]);
1020
- React.useEffect(() => {
1021
- if (!useNativeDialog && isRendered && !entered && modalContext.isOpen) {
1022
- animateIn(wrapperRef.current).then(() => {
1023
- setupFocusTrap();
1024
- });
1025
- }
1026
- }, [isRendered, useNativeDialog, entered, modalContext.isOpen, animateIn, setupFocusTrap]);
1027
- React.useEffect(() => {
1028
- if (!useNativeDialog) {
1029
- setupEscapeKey();
1030
- }
1031
- return () => {
1032
- cleanupEscapeKey();
1033
- };
1034
- }, [useNativeDialog, setupEscapeKey, cleanupEscapeKey]);
1035
- React.useEffect(() => {
1036
- if (useNativeDialog) return;
1037
- if (modalContext.onTopOfStack) {
1038
- setupEscapeKey();
1039
- if (entered) {
1040
- setupFocusTrap();
1041
- }
1042
- } else {
1043
- cleanupFocusTrap();
1044
- cleanupEscapeKey();
1045
- }
1046
- }, [modalContext.onTopOfStack, entered, setupEscapeKey, setupFocusTrap, cleanupFocusTrap, cleanupEscapeKey, useNativeDialog]);
1047
- React.useEffect(() => {
1048
- return () => {
1049
- const wrapper = useNativeDialog ? nativeWrapperRef.current : wrapperRef.current;
1050
- if (wrapper) {
1051
- vanilla.cancelAnimations(wrapper);
1052
- }
1053
- if (useNativeDialog) {
1054
- if (dialogRef.current?.open) {
1055
- dialogRef.current.close();
1056
- }
1057
- } else {
1058
- cleanupFocusTrap();
1059
- cleanupEscapeKey();
1060
- }
1061
- };
1062
- }, [useNativeDialog, cleanupFocusTrap, cleanupEscapeKey]);
1063
- const renderContent = () => /* @__PURE__ */ jsxRuntime.jsxs(
1064
- "div",
1065
- {
1066
- className: `im-modal-content relative ${config.paddingClasses} ${config.panelClasses}`,
1067
- "data-inertiaui-modal-entered": entered,
1068
- children: [
1069
- config.closeButton && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "absolute right-0 top-0 pr-3 pt-3", children: /* @__PURE__ */ jsxRuntime.jsx(CloseButton, { onClick: modalContext.close }) }),
1070
- typeof children === "function" ? children({ modalContext, config }) : children
1071
- ]
1072
- }
1073
- );
1074
- if (useNativeDialog) {
1075
- return /* @__PURE__ */ jsxRuntime.jsx(
1076
- "dialog",
1077
- {
1078
- ref: dialogRef,
1079
- className: clsx(
1080
- "im-modal-dialog m-0 overflow-visible bg-transparent p-0",
1081
- "size-full max-h-none max-w-none",
1082
- "backdrop:bg-black/75 backdrop:transition-opacity backdrop:duration-300",
1083
- isVisible ? "backdrop:opacity-100" : "backdrop:opacity-0",
1084
- !isFirstModal && "backdrop:bg-transparent"
1085
- ),
1086
- onCancel: handleCancel,
1087
- onClick: handleDialogClick,
1088
- children: /* @__PURE__ */ jsxRuntime.jsx("div", { className: "im-modal-container fixed inset-0 overflow-y-auto p-4", children: /* @__PURE__ */ jsxRuntime.jsx(
1089
- "div",
1090
- {
1091
- className: clsx("im-modal-positioner flex min-h-full justify-center", {
1092
- "items-start": config.position === "top",
1093
- "items-center": config.position === "center",
1094
- "items-end": config.position === "bottom"
1095
- }),
1096
- children: /* @__PURE__ */ jsxRuntime.jsx(
1097
- "div",
1098
- {
1099
- ref: nativeWrapperRef,
1100
- className: clsx("im-modal-wrapper w-full transition-[filter] duration-300", modalContext.onTopOfStack ? "" : "blur-xs", maxWidthClass),
1101
- children: renderContent()
1102
- }
1103
- )
1104
- }
1105
- ) })
1106
- }
1107
- );
1108
- }
1109
- if (!isRendered) return null;
1110
- return /* @__PURE__ */ jsxRuntime.jsx(
1111
- "div",
1112
- {
1113
- className: "im-modal-container fixed inset-0 z-40 overflow-y-auto p-4",
1114
- onMouseDown: handleClickOutside,
1115
- children: /* @__PURE__ */ jsxRuntime.jsx(
1116
- "div",
1117
- {
1118
- className: clsx("im-modal-positioner flex min-h-full justify-center", {
1119
- "items-start": config.position === "top",
1120
- "items-center": config.position === "center",
1121
- "items-end": config.position === "bottom"
1122
- }),
1123
- onMouseDown: handleClickOutside,
1124
- children: /* @__PURE__ */ jsxRuntime.jsxs(
1125
- "div",
1126
- {
1127
- ref: wrapperRef,
1128
- role: "dialog",
1129
- "aria-modal": "true",
1130
- className: clsx("im-modal-wrapper w-full transition-[filter] duration-300", modalContext.onTopOfStack ? "" : "blur-xs", maxWidthClass),
1131
- children: [
1132
- /* @__PURE__ */ jsxRuntime.jsx("span", { className: "sr-only", children: "Dialog" }),
1133
- renderContent()
1134
- ]
1135
- }
1136
- )
1137
- }
1138
- )
1139
- }
1140
- );
1141
- };
1142
- const SlideoverContent = ({ modalContext, config, useNativeDialog, isFirstModal, onAfterLeave, children }) => {
1143
- const [isRendered, setIsRendered] = React.useState(false);
1144
- const [isVisible, setIsVisible] = React.useState(false);
1145
- const [entered, setEntered] = React.useState(false);
1146
- const wrapperRef = React.useRef(null);
1147
- const dialogRef = React.useRef(null);
1148
- const nativeWrapperRef = React.useRef(null);
1149
- const cleanupFocusTrapRef = React.useRef(null);
1150
- const cleanupEscapeKeyRef = React.useRef(null);
1151
- const isLeft = config.position === "left";
1152
- const maxWidthClass = React.useMemo(() => getMaxWidthClass(config.maxWidth), [config.maxWidth]);
1153
- const getTranslateX = React.useCallback(() => isLeft ? "-100%" : "100%", [isLeft]);
1154
- const animateIn = React.useCallback(
1155
- async (element) => {
1156
- if (!element) return;
1157
- setIsVisible(true);
1158
- const translateX = getTranslateX();
1159
- await vanilla.animate(element, [
1160
- { transform: `translate3d(${translateX}, 0, 0)`, opacity: 0 },
1161
- { transform: "translate3d(0, 0, 0)", opacity: 1 }
1162
- ]);
1163
- setEntered(true);
1164
- },
1165
- [getTranslateX]
1166
- );
1167
- const animateOut = React.useCallback(
1168
- async (element) => {
1169
- if (!element) return;
1170
- setIsVisible(false);
1171
- const translateX = getTranslateX();
1172
- await vanilla.animate(element, [
1173
- { transform: "translate3d(0, 0, 0)", opacity: 1 },
1174
- { transform: `translate3d(${translateX}, 0, 0)`, opacity: 0 }
1175
- ]);
1176
- setIsRendered(false);
1177
- if (useNativeDialog && dialogRef.current) {
1178
- dialogRef.current.close();
1179
- }
1180
- onAfterLeave?.();
1181
- modalContext.afterLeave();
1182
- },
1183
- [getTranslateX, useNativeDialog, onAfterLeave, modalContext]
1184
- );
1185
- const setupFocusTrap = React.useCallback(() => {
1186
- if (useNativeDialog) return;
1187
- if (!wrapperRef.current || !modalContext.onTopOfStack) return;
1188
- if (cleanupFocusTrapRef.current) return;
1189
- cleanupFocusTrapRef.current = vanilla.createFocusTrap(wrapperRef.current, {
1190
- initialFocus: true,
1191
- returnFocus: false
1192
- });
1193
- }, [modalContext.onTopOfStack, useNativeDialog]);
1194
- const cleanupFocusTrap = React.useCallback(() => {
1195
- if (cleanupFocusTrapRef.current) {
1196
- cleanupFocusTrapRef.current();
1197
- cleanupFocusTrapRef.current = null;
1198
- }
1199
- }, []);
1200
- const setupEscapeKey = React.useCallback(() => {
1201
- if (useNativeDialog) return;
1202
- if (cleanupEscapeKeyRef.current) return;
1203
- if (config?.closeExplicitly) return;
1204
- cleanupEscapeKeyRef.current = vanilla.onEscapeKey(() => {
1205
- if (modalContext.onTopOfStack) {
1206
- modalContext.close();
1207
- }
1208
- });
1209
- }, [config?.closeExplicitly, modalContext, useNativeDialog]);
1210
- const cleanupEscapeKey = React.useCallback(() => {
1211
- if (cleanupEscapeKeyRef.current) {
1212
- cleanupEscapeKeyRef.current();
1213
- cleanupEscapeKeyRef.current = null;
1214
- }
1215
- }, []);
1216
- const handleClickOutside = React.useCallback(
1217
- (event) => {
1218
- if (useNativeDialog) return;
1219
- if (!modalContext.onTopOfStack) return;
1220
- if (config?.closeExplicitly) return;
1221
- if (config?.closeOnClickOutside === false) return;
1222
- if (!wrapperRef.current) return;
1223
- if (!wrapperRef.current.contains(event.target)) {
1224
- modalContext.close();
1225
- }
1226
- },
1227
- [modalContext, config?.closeExplicitly, config?.closeOnClickOutside, useNativeDialog]
1228
- );
1229
- const handleCancel = React.useCallback(
1230
- (event) => {
1231
- event.preventDefault();
1232
- if (modalContext.onTopOfStack && !config?.closeExplicitly) {
1233
- modalContext.close();
1234
- }
1235
- },
1236
- [modalContext, config?.closeExplicitly]
1237
- );
1238
- const handleDialogClick = React.useCallback(
1239
- (event) => {
1240
- if (event.target === dialogRef.current) {
1241
- if (modalContext.onTopOfStack && !config?.closeExplicitly && config?.closeOnClickOutside !== false) {
1242
- modalContext.close();
1243
- }
1244
- }
1245
- },
1246
- [modalContext, config?.closeExplicitly, config?.closeOnClickOutside]
1247
- );
1248
- const prevIsOpenRef = React.useRef(modalContext.isOpen);
1249
- React.useEffect(() => {
1250
- if (useNativeDialog) {
1251
- if (modalContext.isOpen && !dialogRef.current?.open) {
1252
- dialogRef.current?.showModal();
1253
- animateIn(nativeWrapperRef.current);
1254
- } else if (!modalContext.isOpen && prevIsOpenRef.current) {
1255
- setEntered(false);
1256
- animateOut(nativeWrapperRef.current);
1257
- }
1258
- } else {
1259
- if (modalContext.isOpen && !isRendered) {
1260
- setIsRendered(true);
1261
- } else if (!modalContext.isOpen && prevIsOpenRef.current) {
1262
- setEntered(false);
1263
- animateOut(wrapperRef.current);
1264
- }
1265
- }
1266
- prevIsOpenRef.current = modalContext.isOpen;
1267
- }, [modalContext.isOpen, useNativeDialog, animateIn, animateOut, isRendered]);
1268
- React.useEffect(() => {
1269
- if (!useNativeDialog && isRendered && !entered && modalContext.isOpen) {
1270
- animateIn(wrapperRef.current).then(() => {
1271
- setupFocusTrap();
1272
- });
1273
- }
1274
- }, [isRendered, useNativeDialog, entered, modalContext.isOpen, animateIn, setupFocusTrap]);
1275
- React.useEffect(() => {
1276
- if (!useNativeDialog) {
1277
- setupEscapeKey();
1278
- }
1279
- return () => {
1280
- cleanupEscapeKey();
1281
- };
1282
- }, [useNativeDialog, setupEscapeKey, cleanupEscapeKey]);
1283
- React.useEffect(() => {
1284
- if (useNativeDialog) return;
1285
- if (modalContext.onTopOfStack) {
1286
- setupEscapeKey();
1287
- if (entered) {
1288
- setupFocusTrap();
1289
- }
1290
- } else {
1291
- cleanupFocusTrap();
1292
- cleanupEscapeKey();
1293
- }
1294
- }, [modalContext.onTopOfStack, entered, setupEscapeKey, setupFocusTrap, cleanupFocusTrap, cleanupEscapeKey, useNativeDialog]);
1295
- React.useEffect(() => {
1296
- return () => {
1297
- const wrapper = useNativeDialog ? nativeWrapperRef.current : wrapperRef.current;
1298
- if (wrapper) {
1299
- vanilla.cancelAnimations(wrapper);
1300
- }
1301
- if (useNativeDialog) {
1302
- if (dialogRef.current?.open) {
1303
- dialogRef.current.close();
1304
- }
1305
- } else {
1306
- cleanupFocusTrap();
1307
- cleanupEscapeKey();
1308
- }
1309
- };
1310
- }, [useNativeDialog, cleanupFocusTrap, cleanupEscapeKey]);
1311
- const renderContent = () => /* @__PURE__ */ jsxRuntime.jsxs(
1312
- "div",
1313
- {
1314
- className: `im-slideover-content relative ${config.paddingClasses} ${config.panelClasses}`,
1315
- "data-inertiaui-modal-entered": entered,
1316
- children: [
1317
- config.closeButton && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "absolute right-0 top-0 pr-3 pt-3", children: /* @__PURE__ */ jsxRuntime.jsx(CloseButton, { onClick: modalContext.close }) }),
1318
- typeof children === "function" ? children({ modalContext, config }) : children
1319
- ]
1320
- }
1321
- );
1322
- if (useNativeDialog) {
1323
- return /* @__PURE__ */ jsxRuntime.jsx(
1324
- "dialog",
1325
- {
1326
- ref: dialogRef,
1327
- className: clsx(
1328
- "im-slideover-dialog m-0 overflow-visible bg-transparent p-0",
1329
- "size-full max-h-none max-w-none",
1330
- "backdrop:bg-black/75 backdrop:transition-opacity backdrop:duration-300",
1331
- isVisible ? "backdrop:opacity-100" : "backdrop:opacity-0",
1332
- !isFirstModal && "backdrop:bg-transparent"
1333
- ),
1334
- onCancel: handleCancel,
1335
- onClick: handleDialogClick,
1336
- children: /* @__PURE__ */ jsxRuntime.jsx("div", { className: "im-slideover-container fixed inset-0 overflow-y-auto overflow-x-hidden", children: /* @__PURE__ */ jsxRuntime.jsx(
1337
- "div",
1338
- {
1339
- className: clsx("im-slideover-positioner flex min-h-full items-center", {
1340
- "justify-start rtl:justify-end": config?.position === "left",
1341
- "justify-end rtl:justify-start": config?.position === "right"
1342
- }),
1343
- children: /* @__PURE__ */ jsxRuntime.jsx(
1344
- "div",
1345
- {
1346
- ref: nativeWrapperRef,
1347
- className: clsx("im-slideover-wrapper w-full transition-[filter] duration-300", modalContext.onTopOfStack ? "" : "blur-xs", maxWidthClass),
1348
- children: renderContent()
1349
- }
1350
- )
1351
- }
1352
- ) })
1353
- }
1354
- );
1355
- }
1356
- if (!isRendered) return null;
1357
- return /* @__PURE__ */ jsxRuntime.jsx(
1358
- "div",
1359
- {
1360
- className: "im-slideover-container fixed inset-0 z-40 overflow-y-auto overflow-x-hidden",
1361
- onMouseDown: handleClickOutside,
1362
- children: /* @__PURE__ */ jsxRuntime.jsx(
1363
- "div",
1364
- {
1365
- className: clsx("im-slideover-positioner flex min-h-full items-center", {
1366
- "justify-start rtl:justify-end": config?.position === "left",
1367
- "justify-end rtl:justify-start": config?.position === "right"
1368
- }),
1369
- onMouseDown: handleClickOutside,
1370
- children: /* @__PURE__ */ jsxRuntime.jsxs(
1371
- "div",
1372
- {
1373
- ref: wrapperRef,
1374
- role: "dialog",
1375
- "aria-modal": "true",
1376
- className: clsx("im-slideover-wrapper w-full transition-[filter] duration-300", modalContext.onTopOfStack ? "" : "blur-xs", maxWidthClass),
1377
- children: [
1378
- /* @__PURE__ */ jsxRuntime.jsx("span", { className: "sr-only", children: "Dialog" }),
1379
- renderContent()
1380
- ]
1381
- }
1382
- )
1383
- }
1384
- )
1385
- }
1386
- );
1387
- };
1388
- const Modal = React.forwardRef(
1389
- (allProps, ref) => {
1390
- const { name, children, onFocus, onBlur, onClose, onSuccess, onAfterLeave, ...props } = allProps;
1391
- const renderChildren = (contentProps) => {
1392
- if (typeof children === "function") {
1393
- return children(contentProps);
1394
- }
1395
- return children;
1396
- };
1397
- const headlessModalRef = React.useRef(null);
1398
- const cleanupScrollLockRef = React.useRef(null);
1399
- const cleanupAriaHiddenRef = React.useRef(null);
1400
- const [rendered, setRendered] = React.useState(false);
1401
- const useNativeDialog = React.useMemo(() => getConfig("useNativeDialog"), []);
1402
- React.useImperativeHandle(ref, () => headlessModalRef.current, [headlessModalRef]);
1403
- React.useEffect(() => {
1404
- return () => {
1405
- cleanupScrollLockRef.current?.();
1406
- cleanupAriaHiddenRef.current?.();
1407
- };
1408
- }, []);
1409
- const handleSuccess = React.useCallback(() => {
1410
- onSuccess?.();
1411
- if (!cleanupScrollLockRef.current) {
1412
- cleanupScrollLockRef.current = vanilla.lockScroll();
1413
- cleanupAriaHiddenRef.current = vanilla.markAriaHidden(getConfig("appElement"));
1414
- }
1415
- }, [onSuccess]);
1416
- const handleClose = React.useCallback(() => {
1417
- onClose?.();
1418
- cleanupScrollLockRef.current?.();
1419
- cleanupAriaHiddenRef.current?.();
1420
- cleanupScrollLockRef.current = null;
1421
- cleanupAriaHiddenRef.current = null;
1422
- }, [onClose]);
1423
- const handleAfterLeave = React.useCallback(() => {
1424
- onAfterLeave?.();
1425
- }, [onAfterLeave]);
1426
- return /* @__PURE__ */ jsxRuntime.jsx(
1427
- HeadlessModal,
1428
- {
1429
- ref: headlessModalRef,
1430
- name,
1431
- onFocus: onFocus ?? void 0,
1432
- onBlur: onBlur ?? void 0,
1433
- onClose: handleClose,
1434
- onSuccess: handleSuccess,
1435
- ...props,
1436
- children: ({
1437
- afterLeave,
1438
- close,
1439
- config,
1440
- emit,
1441
- getChildModal,
1442
- getParentModal,
1443
- id,
1444
- index,
1445
- isOpen,
1446
- modalContext,
1447
- onTopOfStack,
1448
- reload,
1449
- setOpen,
1450
- shouldRender,
1451
- ...extraProps
1452
- }) => /* @__PURE__ */ jsxRuntime.jsx(ModalPortal, { children: /* @__PURE__ */ jsxRuntime.jsxs(
1453
- "div",
1454
- {
1455
- className: "im-dialog relative z-20",
1456
- "data-inertiaui-modal-id": id,
1457
- "data-inertiaui-modal-index": index,
1458
- "aria-hidden": !onTopOfStack,
1459
- children: [
1460
- index === 0 && !useNativeDialog && /* @__PURE__ */ jsxRuntime.jsx(
1461
- BackdropTransition,
1462
- {
1463
- show: isOpen,
1464
- appear: !rendered,
1465
- onAfterAppear: () => setRendered(true)
1466
- }
1467
- ),
1468
- config.slideover ? /* @__PURE__ */ jsxRuntime.jsx(
1469
- SlideoverContent,
1470
- {
1471
- modalContext,
1472
- config,
1473
- useNativeDialog,
1474
- isFirstModal: index === 0,
1475
- onAfterLeave: handleAfterLeave,
1476
- children: renderChildren({
1477
- ...extraProps,
1478
- afterLeave,
1479
- close,
1480
- config,
1481
- emit,
1482
- getChildModal,
1483
- getParentModal,
1484
- id,
1485
- index,
1486
- isOpen,
1487
- modalContext,
1488
- onTopOfStack,
1489
- reload,
1490
- setOpen,
1491
- shouldRender
1492
- })
1493
- }
1494
- ) : /* @__PURE__ */ jsxRuntime.jsx(
1495
- ModalContent,
1496
- {
1497
- modalContext,
1498
- config,
1499
- useNativeDialog,
1500
- isFirstModal: index === 0,
1501
- onAfterLeave: handleAfterLeave,
1502
- children: renderChildren({
1503
- ...extraProps,
1504
- afterLeave,
1505
- close,
1506
- config,
1507
- emit,
1508
- getChildModal,
1509
- getParentModal,
1510
- id,
1511
- index,
1512
- isOpen,
1513
- modalContext,
1514
- onTopOfStack,
1515
- reload,
1516
- setOpen,
1517
- shouldRender
1518
- })
1519
- }
1520
- )
1521
- ]
1522
- }
1523
- ) })
1524
- }
1525
- );
1526
- }
1527
- );
1528
- function ModalPortal({ children }) {
1529
- const [mounted, setMounted] = React.useState(false);
1530
- React.useEffect(() => {
1531
- setMounted(true);
1532
- }, []);
1533
- if (!mounted) return null;
1534
- return reactDom.createPortal(children, document.body);
1535
- }
1536
- function BackdropTransition({ show, appear, onAfterAppear }) {
1537
- const [state, setState] = React.useState(() => {
1538
- if (appear && show) return "entering";
1539
- return show ? "entered" : "exited";
1540
- });
1541
- const initialRender = React.useRef(true);
1542
- const backdropRef = React.useRef(null);
1543
- React.useEffect(() => {
1544
- if (initialRender.current) {
1545
- initialRender.current = false;
1546
- if (appear && show) {
1547
- requestAnimationFrame(() => {
1548
- setState("entered");
1549
- const backdrop = backdropRef.current;
1550
- if (backdrop) {
1551
- const onTransitionEnd = (e) => {
1552
- if (e.target !== backdrop) return;
1553
- backdrop.removeEventListener("transitionend", onTransitionEnd);
1554
- onAfterAppear?.();
1555
- };
1556
- backdrop.addEventListener("transitionend", onTransitionEnd);
1557
- }
1558
- });
1559
- }
1560
- return;
1561
- }
1562
- if (show) {
1563
- setState("entering");
1564
- requestAnimationFrame(() => {
1565
- setState("entered");
1566
- });
1567
- } else {
1568
- setState("leaving");
1569
- const backdrop = backdropRef.current;
1570
- if (backdrop) {
1571
- const onTransitionEnd = (e) => {
1572
- if (e.target !== backdrop) return;
1573
- backdrop.removeEventListener("transitionend", onTransitionEnd);
1574
- setState("exited");
1575
- };
1576
- backdrop.addEventListener("transitionend", onTransitionEnd);
1577
- }
1578
- }
1579
- }, [show, appear, onAfterAppear]);
1580
- if (state === "exited") return null;
1581
- const isVisible = state === "entered";
1582
- return /* @__PURE__ */ jsxRuntime.jsx(
1583
- "div",
1584
- {
1585
- ref: backdropRef,
1586
- className: `im-backdrop fixed inset-0 z-30 bg-black/75 transition-opacity duration-300 ease-in-out ${isVisible ? "opacity-100" : "opacity-0"}`,
1587
- "aria-hidden": "true"
1588
- }
1589
- );
1590
- }
1591
- Modal.displayName = "Modal";
1592
- const ModalLink = ({
1593
- href,
1594
- method = "get",
1595
- data = {},
1596
- as: Component = "a",
1597
- headers = {},
1598
- queryStringArrayFormat = "brackets",
1599
- onAfterLeave,
1600
- onBlur,
1601
- onClose,
1602
- onError,
1603
- onFocus,
1604
- onStart,
1605
- onSuccess,
1606
- onPrefetching,
1607
- onPrefetched,
1608
- navigate,
1609
- prefetch: prefetch$1 = false,
1610
- cacheFor = 3e4,
1611
- children,
1612
- ...props
1613
- }) => {
1614
- const [loading, setLoading] = React.useState(false);
1615
- const [modalContext, setModalContext] = React.useState(null);
1616
- const { stack, visit } = useModalStack();
1617
- const hoverTimeout = React.useRef(null);
1618
- const shouldNavigate = React.useMemo(() => {
1619
- return navigate ?? getConfig("navigate");
1620
- }, [navigate]);
1621
- const prefetchModes = React.useMemo(() => {
1622
- if (prefetch$1 === true) {
1623
- return ["hover"];
1624
- }
1625
- if (prefetch$1 === false) {
1626
- return [];
1627
- }
1628
- if (Array.isArray(prefetch$1)) {
1629
- return prefetch$1;
1630
- }
1631
- return [prefetch$1];
1632
- }, [prefetch$1]);
1633
- const doPrefetch = React.useCallback(() => {
1634
- prefetch(href, {
1635
- method,
1636
- data,
1637
- headers,
1638
- queryStringArrayFormat,
1639
- cacheFor,
1640
- onPrefetching: onPrefetching ?? void 0,
1641
- onPrefetched: onPrefetched ?? void 0
1642
- });
1643
- }, [href, method, data, headers, queryStringArrayFormat, cacheFor, onPrefetching, onPrefetched]);
1644
- const handleMouseEnter = React.useCallback(() => {
1645
- if (!prefetchModes.includes("hover")) return;
1646
- hoverTimeout.current = setTimeout(() => {
1647
- doPrefetch();
1648
- }, 75);
1649
- }, [prefetchModes, doPrefetch]);
1650
- const handleMouseLeave = React.useCallback(() => {
1651
- if (hoverTimeout.current) {
1652
- clearTimeout(hoverTimeout.current);
1653
- hoverTimeout.current = null;
1654
- }
1655
- }, []);
1656
- const handleMouseDown = React.useCallback(
1657
- (event) => {
1658
- if (!prefetchModes.includes("click")) return;
1659
- if (event.button !== 0) return;
1660
- doPrefetch();
1661
- },
1662
- [prefetchModes, doPrefetch]
1663
- );
1664
- React.useEffect(() => {
1665
- if (prefetchModes.includes("mount")) {
1666
- doPrefetch();
1667
- }
1668
- }, []);
1669
- React.useEffect(() => {
1670
- return () => {
1671
- if (hoverTimeout.current) {
1672
- clearTimeout(hoverTimeout.current);
1673
- }
1674
- };
1675
- }, []);
1676
- const standardProps = {};
1677
- const customEvents = {};
1678
- Object.keys(props).forEach((key) => {
1679
- if (modalPropNames.includes(key)) {
1680
- return;
1681
- }
1682
- if (key.startsWith("on") && typeof props[key] === "function") {
1683
- if (vanilla.isStandardDomEvent(key)) {
1684
- standardProps[key] = props[key];
1685
- } else {
1686
- customEvents[key] = props[key];
1687
- }
1688
- } else {
1689
- standardProps[key] = props[key];
1690
- }
1691
- });
1692
- const [isBlurred, setIsBlurred] = React.useState(false);
1693
- React.useEffect(() => {
1694
- if (!modalContext) {
1695
- return;
1696
- }
1697
- if (modalContext.onTopOfStack && isBlurred) {
1698
- onFocus?.();
1699
- } else if (!modalContext.onTopOfStack && !isBlurred) {
1700
- onBlur?.();
1701
- }
1702
- setIsBlurred(!modalContext.onTopOfStack);
1703
- }, [stack]);
1704
- const onCloseCallback = React.useCallback(() => {
1705
- onClose?.();
1706
- }, [onClose]);
1707
- const onAfterLeaveCallback = React.useCallback(() => {
1708
- setModalContext(null);
1709
- onAfterLeave?.();
1710
- }, [onAfterLeave]);
1711
- const handle = React.useCallback(
1712
- (e) => {
1713
- e?.preventDefault();
1714
- if (loading) return;
1715
- if (!href.startsWith("#")) {
1716
- setLoading(true);
1717
- onStart?.();
1718
- }
1719
- visit(
1720
- href,
1721
- method,
1722
- data,
1723
- headers,
1724
- vanilla.rejectNullValues(vanilla.only(props, modalPropNames)),
1725
- () => onCloseCallback(),
1726
- onAfterLeaveCallback,
1727
- queryStringArrayFormat,
1728
- shouldNavigate
1729
- ).then((newModalContext) => {
1730
- setModalContext(newModalContext);
1731
- newModalContext.registerEventListenersFromProps(customEvents);
1732
- onSuccess?.();
1733
- }).catch((error) => {
1734
- console.error(error);
1735
- onError?.(error);
1736
- }).finally(() => setLoading(false));
1737
- },
1738
- [href, method, data, headers, queryStringArrayFormat, props, onCloseCallback, onAfterLeaveCallback]
1739
- );
1740
- return /* @__PURE__ */ jsxRuntime.jsx(
1741
- Component,
1742
- {
1743
- ...standardProps,
1744
- href,
1745
- onClick: handle,
1746
- onMouseEnter: handleMouseEnter,
1747
- onMouseLeave: handleMouseLeave,
1748
- onMouseDown: handleMouseDown,
1749
- children: typeof children === "function" ? children({ loading }) : children
1750
- }
1751
- );
1752
- };
1753
- const WhenVisible = ({ children, data, params, buffer, as, always, fallback }) => {
1754
- always = always ?? false;
1755
- as = as ?? "div";
1756
- fallback = fallback ?? null;
1757
- const [loaded, setLoaded] = React.useState(false);
1758
- const hasFetched = React.useRef(false);
1759
- const fetching = React.useRef(false);
1760
- const ref = React.useRef(null);
1761
- const modal = useModal();
1762
- const getReloadParams = React.useCallback(() => {
1763
- if (data) {
1764
- return {
1765
- only: Array.isArray(data) ? data : [data]
1766
- };
1767
- }
1768
- if (!params) {
1769
- throw new Error("You must provide either a `data` or `params` prop.");
1770
- }
1771
- return params;
1772
- }, [params, data]);
1773
- React.useEffect(() => {
1774
- if (!ref.current) {
1775
- return;
1776
- }
1777
- const observer = new IntersectionObserver(
1778
- (entries) => {
1779
- if (!entries[0].isIntersecting) {
1780
- return;
1781
- }
1782
- if (!always && hasFetched.current) {
1783
- observer.disconnect();
1784
- }
1785
- if (fetching.current) {
1786
- return;
1787
- }
1788
- hasFetched.current = true;
1789
- fetching.current = true;
1790
- const reloadParams = getReloadParams();
1791
- modal?.reload({
1792
- ...reloadParams,
1793
- onStart: () => {
1794
- fetching.current = true;
1795
- reloadParams.onStart?.();
1796
- },
1797
- onFinish: () => {
1798
- setLoaded(true);
1799
- fetching.current = false;
1800
- reloadParams.onFinish?.();
1801
- if (!always) {
1802
- observer.disconnect();
1803
- }
1804
- }
1805
- });
1806
- },
1807
- {
1808
- rootMargin: `${buffer || 0}px`
1809
- }
1810
- );
1811
- observer.observe(ref.current);
1812
- return () => {
1813
- observer.disconnect();
1814
- };
1815
- }, [ref, getReloadParams, buffer]);
1816
- if (always || !loaded) {
1817
- return React.createElement(
1818
- as,
1819
- {
1820
- props: null,
1821
- ref
1822
- },
1823
- loaded ? children : fallback
1824
- );
1825
- }
1826
- return loaded ? children : null;
1827
- };
1828
- WhenVisible.displayName = "InertiaWhenVisible";
1829
- const setPageLayout = (layout) => (module2) => {
1830
- module2.default.layout = (page) => React.createElement(layout, { children: page });
1831
- return module2;
1832
- };
1833
- exports2.dialogUtils = vanilla__namespace;
1834
- exports2.Deferred = Deferred;
1835
- exports2.HeadlessModal = HeadlessModal;
1836
- exports2.Modal = Modal;
1837
- exports2.ModalLink = ModalLink;
1838
- exports2.ModalRoot = ModalRoot;
1839
- exports2.ModalStackProvider = ModalStackProvider;
1840
- exports2.WhenVisible = WhenVisible;
1841
- exports2.getConfig = getConfig;
1842
- exports2.initFromPageProps = initFromPageProps;
1843
- exports2.modalPropNames = modalPropNames;
1844
- exports2.prefetch = prefetch;
1845
- exports2.putConfig = putConfig;
1846
- exports2.renderApp = renderApp;
1847
- exports2.resetConfig = resetConfig;
1848
- exports2.setPageLayout = setPageLayout;
1849
- exports2.useModal = useModal;
1850
- exports2.useModalIndex = useModalIndex;
1851
- exports2.useModalStack = useModalStack;
1852
- Object.defineProperty(exports2, Symbol.toStringTag, { value: "Module" });
1853
- }));
1854
- //# sourceMappingURL=inertiaui-modal.umd.cjs.map
2
+ typeof exports === "object" && typeof module !== "undefined" ? factory(exports, require("@inertiaui/vanilla"), require("react"), require("@inertiajs/core"), require("@inertiajs/react"), require("axios"), require("react/jsx-runtime"), require("react-dom")) : typeof define === "function" && define.amd ? define([
3
+ "exports",
4
+ "@inertiaui/vanilla",
5
+ "react",
6
+ "@inertiajs/core",
7
+ "@inertiajs/react",
8
+ "axios",
9
+ "react/jsx-runtime",
10
+ "react-dom"
11
+ ], factory) : (global = typeof globalThis !== "undefined" ? globalThis : global || self, factory(global.InertiaUIModal = {}, global.InertiaUIVanilla, global.React, global.InertiaCore, global.InertiaReact, global.axios, global.ReactJSXRuntime, global.ReactDOM));
12
+ })(this, function(exports, _inertiaui_vanilla, react, _inertiajs_core, _inertiajs_react, axios, react_jsx_runtime, react_dom) {
13
+ Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
14
+ //#region \0rolldown/runtime.js
15
+ var __create = Object.create;
16
+ var __defProp = Object.defineProperty;
17
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
18
+ var __getOwnPropNames = Object.getOwnPropertyNames;
19
+ var __getProtoOf = Object.getPrototypeOf;
20
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
21
+ var __copyProps = (to, from, except, desc) => {
22
+ if (from && typeof from === "object" || typeof from === "function") for (var keys = __getOwnPropNames(from), i = 0, n = keys.length, key; i < n; i++) {
23
+ key = keys[i];
24
+ if (!__hasOwnProp.call(to, key) && key !== except) __defProp(to, key, {
25
+ get: ((k) => from[k]).bind(null, key),
26
+ enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable
27
+ });
28
+ }
29
+ return to;
30
+ };
31
+ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", {
32
+ value: mod,
33
+ enumerable: true
34
+ }) : target, mod));
35
+ //#endregion
36
+ _inertiaui_vanilla = __toESM(_inertiaui_vanilla, 1);
37
+ react = __toESM(react, 1);
38
+ axios = __toESM(axios, 1);
39
+ //#region src/config.ts
40
+ var defaultConfig = {
41
+ type: "modal",
42
+ navigate: false,
43
+ useNativeDialog: true,
44
+ appElement: "#app",
45
+ modal: {
46
+ closeButton: true,
47
+ closeExplicitly: false,
48
+ closeOnClickOutside: true,
49
+ maxWidth: "2xl",
50
+ paddingClasses: "p-4 sm:p-6",
51
+ panelClasses: "bg-white rounded",
52
+ position: "center"
53
+ },
54
+ slideover: {
55
+ closeButton: true,
56
+ closeExplicitly: false,
57
+ closeOnClickOutside: true,
58
+ maxWidth: "md",
59
+ paddingClasses: "p-4 sm:p-6",
60
+ panelClasses: "bg-white min-h-screen",
61
+ position: "right"
62
+ }
63
+ };
64
+ var Config = class {
65
+ constructor() {
66
+ this.config = {};
67
+ this.reset();
68
+ }
69
+ reset() {
70
+ this.config = JSON.parse(JSON.stringify(defaultConfig));
71
+ }
72
+ put(key, value) {
73
+ if (typeof key === "object") {
74
+ this.config = {
75
+ type: key.type ?? defaultConfig.type,
76
+ navigate: key.navigate ?? defaultConfig.navigate,
77
+ useNativeDialog: key.useNativeDialog ?? defaultConfig.useNativeDialog,
78
+ appElement: key.appElement !== void 0 ? key.appElement : defaultConfig.appElement,
79
+ modal: {
80
+ ...defaultConfig.modal,
81
+ ...key.modal
82
+ },
83
+ slideover: {
84
+ ...defaultConfig.slideover,
85
+ ...key.slideover
86
+ }
87
+ };
88
+ return;
89
+ }
90
+ const keys = key.split(".");
91
+ let current = this.config;
92
+ for (let i = 0; i < keys.length - 1; i++) current = current[keys[i]] = current[keys[i]] || {};
93
+ current[keys[keys.length - 1]] = value;
94
+ }
95
+ get(key) {
96
+ if (typeof key === "undefined") return this.config;
97
+ const keys = key.split(".");
98
+ let current = this.config;
99
+ for (const k of keys) {
100
+ if (current === null || current === void 0 || typeof current !== "object") return null;
101
+ current = current[k];
102
+ }
103
+ return current === void 0 ? null : current;
104
+ }
105
+ };
106
+ var configInstance = new Config();
107
+ var resetConfig = () => configInstance.reset();
108
+ var putConfig = (key, value) => configInstance.put(key, value);
109
+ var getConfig = (key) => configInstance.get(key);
110
+ var getConfigByType = (isSlideover, key) => configInstance.get(isSlideover ? `slideover.${key}` : `modal.${key}`);
111
+ //#endregion
112
+ //#region src/helpers.ts
113
+ var generateIdUsingCallback = null;
114
+ function generateId(prefix = "inertiaui_") {
115
+ if (generateIdUsingCallback) return generateIdUsingCallback();
116
+ return (0, _inertiaui_vanilla.generateId)(prefix);
117
+ }
118
+ //#endregion
119
+ //#region src/ModalRoot.tsx
120
+ var ModalStackContext = (0, react.createContext)(null);
121
+ ModalStackContext.displayName = "ModalStackContext";
122
+ var pageVersion = null;
123
+ var resolveComponent = null;
124
+ var baseUrl = null;
125
+ var closingToBaseUrlTarget = null;
126
+ var prefetchCache = /* @__PURE__ */ new Map();
127
+ var prefetchInFlight = /* @__PURE__ */ new Map();
128
+ function getPrefetchCacheKey(url, method, data) {
129
+ return `${method}:${url}:${JSON.stringify(data)}`;
130
+ }
131
+ function getCachedResponse(url, method, data) {
132
+ const key = getPrefetchCacheKey(url, method, data);
133
+ const cached = prefetchCache.get(key);
134
+ if (!cached) return null;
135
+ if (Date.now() > cached.expiresAt) {
136
+ prefetchCache.delete(key);
137
+ return null;
138
+ }
139
+ return cached.response;
140
+ }
141
+ function setCachedResponse(url, method, data, response, cacheFor) {
142
+ const key = getPrefetchCacheKey(url, method, data);
143
+ prefetchCache.set(key, {
144
+ response,
145
+ timestamp: Date.now(),
146
+ expiresAt: Date.now() + cacheFor
147
+ });
148
+ }
149
+ function prefetch(href, options = {}) {
150
+ if (href.startsWith("#")) return Promise.resolve();
151
+ const method = (options.method ?? "get").toLowerCase();
152
+ const data = options.data ?? {};
153
+ const headers = options.headers ?? {};
154
+ const queryStringArrayFormat = options.queryStringArrayFormat ?? "brackets";
155
+ const cacheFor = options.cacheFor ?? 3e4;
156
+ const [url, mergedData] = (0, _inertiajs_core.mergeDataIntoQueryString)(method, href || "", data, queryStringArrayFormat);
157
+ if (getCachedResponse(url, method, mergedData)) return Promise.resolve();
158
+ const cacheKey = getPrefetchCacheKey(url, method, mergedData);
159
+ const inFlight = prefetchInFlight.get(cacheKey);
160
+ if (inFlight) return inFlight.then(() => {});
161
+ options.onPrefetching?.();
162
+ const request = (0, axios.default)({
163
+ url,
164
+ method,
165
+ data: mergedData,
166
+ headers: {
167
+ ...headers,
168
+ Accept: "text/html, application/xhtml+xml",
169
+ "X-Requested-With": "XMLHttpRequest",
170
+ "X-Inertia": "true",
171
+ "X-Inertia-Version": pageVersion ?? "",
172
+ "X-InertiaUI-Modal": generateId(),
173
+ "X-InertiaUI-Modal-Base-Url": baseUrl ?? ""
174
+ }
175
+ }).then((response) => {
176
+ setCachedResponse(url, method, mergedData, response, cacheFor);
177
+ options.onPrefetched?.();
178
+ return response;
179
+ }).finally(() => {
180
+ prefetchInFlight.delete(cacheKey);
181
+ });
182
+ prefetchInFlight.set(cacheKey, request);
183
+ return request.then(() => {});
184
+ }
185
+ var ModalStackProvider = ({ children }) => {
186
+ const stackRef = (0, react.useRef)([]);
187
+ const [, forceUpdate] = (0, react.useReducer)((x) => x + 1, 0);
188
+ const [localModals, setLocalModals] = (0, react.useState)({});
189
+ const updateStack = (withStack) => {
190
+ const newStack = withStack([...stackRef.current]);
191
+ const isOnTopOfStack = (modalId) => {
192
+ if (newStack.length < 2) return true;
193
+ return newStack.map((modal) => ({
194
+ id: modal.id,
195
+ shouldRender: modal.shouldRender
196
+ })).reverse().find((modal) => modal.shouldRender)?.id === modalId;
197
+ };
198
+ newStack.forEach((modal, index) => {
199
+ newStack[index].onTopOfStack = isOnTopOfStack(modal.id);
200
+ newStack[index].getParentModal = () => {
201
+ if (index < 1) return null;
202
+ return stackRef.current.slice(0, index).reverse().find((m) => m.isOpen) ?? null;
203
+ };
204
+ newStack[index].getChildModal = () => {
205
+ if (index === stackRef.current.length - 1) return null;
206
+ return stackRef.current.slice(index + 1).find((m) => m.isOpen) ?? null;
207
+ };
208
+ });
209
+ stackRef.current = newStack;
210
+ forceUpdate();
211
+ };
212
+ class ModalClass {
213
+ constructor(component, response, config, onClose, afterLeave) {
214
+ this.show = () => {
215
+ updateStack((prevStack) => prevStack.map((modal) => {
216
+ if (modal.id === this.id && !modal.isOpen) {
217
+ modal.isOpen = true;
218
+ modal.shouldRender = true;
219
+ }
220
+ return modal;
221
+ }));
222
+ };
223
+ this.setOpen = (open) => {
224
+ if (open) this.show();
225
+ else this.close();
226
+ };
227
+ this.close = () => {
228
+ updateStack((currentStack) => {
229
+ let modalClosed = false;
230
+ const newStack = currentStack.map((modal) => {
231
+ if (modal.id === this.id && modal.isOpen) {
232
+ Object.keys(modal.listeners).forEach((event) => {
233
+ modal.off(event);
234
+ });
235
+ modal.isOpen = false;
236
+ modal.onCloseCallback?.();
237
+ modalClosed = true;
238
+ }
239
+ return modal;
240
+ });
241
+ return modalClosed ? newStack : currentStack;
242
+ });
243
+ };
244
+ this.afterLeave = () => {
245
+ if (this.isOpen) return;
246
+ updateStack((prevStack) => {
247
+ const updatedStack = prevStack.map((modal) => {
248
+ if (modal.id === this.id && !modal.isOpen) {
249
+ modal.shouldRender = false;
250
+ modal.afterLeaveCallback?.();
251
+ modal.afterLeaveCallback = null;
252
+ }
253
+ return modal;
254
+ });
255
+ if (this.index === 0) {
256
+ const savedBaseUrl = baseUrl;
257
+ baseUrl = null;
258
+ closingToBaseUrlTarget = savedBaseUrl;
259
+ if (savedBaseUrl && typeof window !== "undefined" && !(0, _inertiaui_vanilla.sameUrlPath)(savedBaseUrl, window.location.href)) _inertiajs_react.router.push({
260
+ url: savedBaseUrl,
261
+ preserveScroll: true,
262
+ preserveState: true,
263
+ props: (currentProps) => {
264
+ const { _inertiaui_modal, ...rest } = currentProps;
265
+ return {
266
+ ...rest,
267
+ _inertiaui_modal: void 0
268
+ };
269
+ }
270
+ });
271
+ return [];
272
+ }
273
+ return updatedStack;
274
+ });
275
+ };
276
+ this.on = (event, callback) => {
277
+ event = (0, _inertiaui_vanilla.kebabCase)(event);
278
+ this.listeners[event] = this.listeners[event] ?? [];
279
+ this.listeners[event].push(callback);
280
+ };
281
+ this.off = (event, callback) => {
282
+ event = (0, _inertiaui_vanilla.kebabCase)(event);
283
+ if (callback) this.listeners[event] = this.listeners[event]?.filter((cb) => cb !== callback) ?? [];
284
+ else delete this.listeners[event];
285
+ };
286
+ this.emit = (event, ...args) => {
287
+ this.listeners[(0, _inertiaui_vanilla.kebabCase)(event)]?.forEach((callback) => callback(...args));
288
+ };
289
+ this.registerEventListenersFromProps = (props) => {
290
+ const unsubscribers = [];
291
+ Object.keys(props).filter((key) => key.startsWith("on")).forEach((key) => {
292
+ const eventName = (0, _inertiaui_vanilla.kebabCase)(key).replace(/^on-/, "");
293
+ const callback = props[key];
294
+ this.on(eventName, callback);
295
+ unsubscribers.push(() => this.off(eventName, callback));
296
+ });
297
+ return () => unsubscribers.forEach((unsub) => unsub());
298
+ };
299
+ this.reload = (options = {}) => {
300
+ let keys = Object.keys(this.response.props);
301
+ if (options.only) keys = options.only;
302
+ if (options.except) keys = (0, _inertiaui_vanilla.except)(keys, options.except);
303
+ if (!this.response?.url) return;
304
+ const method = (options.method ?? "get").toLowerCase();
305
+ const data = options.data ?? {};
306
+ options.onStart?.();
307
+ (0, axios.default)({
308
+ url: this.response.url,
309
+ method,
310
+ data: method === "get" ? {} : data,
311
+ params: method === "get" ? data : {},
312
+ headers: {
313
+ ...options.headers,
314
+ Accept: "text/html, application/xhtml+xml",
315
+ "X-Inertia": "true",
316
+ "X-Inertia-Partial-Component": this.response.component,
317
+ "X-Inertia-Version": this.response.version ?? "",
318
+ "X-Inertia-Partial-Data": keys.join(","),
319
+ "X-InertiaUI-Modal": generateId(),
320
+ "X-InertiaUI-Modal-Base-Url": baseUrl ?? ""
321
+ }
322
+ }).then((response) => {
323
+ this.updateProps(response.data.props);
324
+ options.onSuccess?.(response);
325
+ }).catch((error) => {
326
+ options.onError?.(error);
327
+ }).finally(() => {
328
+ options.onFinish?.();
329
+ });
330
+ };
331
+ this.updateProps = (props) => {
332
+ Object.assign(this.props, props);
333
+ updateStack((prevStack) => prevStack);
334
+ };
335
+ this.id = response.id ?? generateId();
336
+ this.isOpen = false;
337
+ this.shouldRender = false;
338
+ this.listeners = {};
339
+ this.component = component;
340
+ this.props = response.props ?? {};
341
+ this.response = response;
342
+ this.config = config ?? {};
343
+ this.onCloseCallback = onClose ?? null;
344
+ this.afterLeaveCallback = afterLeave ?? null;
345
+ this.index = -1;
346
+ this.getParentModal = () => null;
347
+ this.getChildModal = () => null;
348
+ this.onTopOfStack = true;
349
+ }
350
+ }
351
+ const isValidModalResponse = (data) => {
352
+ return typeof data === "object" && data !== null && "component" in data && typeof data.component === "string";
353
+ };
354
+ const pushFromResponseData = (responseData, config = {}, onClose = null, onAfterLeave = null) => {
355
+ if (!resolveComponent) return Promise.reject(/* @__PURE__ */ new Error("resolveComponent not set"));
356
+ if (!isValidModalResponse(responseData)) return Promise.reject(/* @__PURE__ */ new Error("Invalid modal response. This usually happens when the server returns a redirect (e.g., due to session expiration). Check if the user is still authenticated."));
357
+ return resolveComponent(responseData.component).then((component) => push(component, responseData, config, onClose, onAfterLeave));
358
+ };
359
+ const loadDeferredProps = (modal) => {
360
+ const deferred = modal.response?.meta?.deferredProps;
361
+ if (!deferred) return;
362
+ Object.keys(deferred).forEach((key) => {
363
+ modal.reload({ only: deferred[key] });
364
+ });
365
+ };
366
+ const push = (component, response, config, onClose, afterLeave) => {
367
+ const newModal = new ModalClass(component, response, config, onClose, afterLeave);
368
+ newModal.index = stackRef.current.length;
369
+ updateStack((prevStack) => [...prevStack, newModal]);
370
+ loadDeferredProps(newModal);
371
+ newModal.show();
372
+ return newModal;
373
+ };
374
+ function pushLocalModal(name, config, onClose, afterLeave, props) {
375
+ if (!localModals[name]) throw new Error(`The local modal "${name}" has not been registered.`);
376
+ const modal = push(null, { props: props ?? {} }, config, onClose, afterLeave);
377
+ modal.name = name;
378
+ localModals[name].callback(modal);
379
+ return modal;
380
+ }
381
+ const visitModal = (url, options = {}) => visit(url, options.method ?? "get", options.data ?? {}, options.headers ?? {}, options.config ?? {}, options.onClose ?? null, options.onAfterLeave ?? null, options.queryStringArrayFormat ?? "brackets", options.navigate ?? getConfig("navigate"), options.onStart ?? null, options.onSuccess ?? null, options.onError ?? null, options.props ?? null).then((modal) => {
382
+ const listeners = options.listeners ?? {};
383
+ Object.keys(listeners).forEach((event) => {
384
+ const eventName = (0, _inertiaui_vanilla.kebabCase)(event);
385
+ modal.on(eventName, listeners[event]);
386
+ });
387
+ return modal;
388
+ });
389
+ const updateBrowserUrl = (url, useBrowserHistory, modalData) => {
390
+ if (!url || !useBrowserHistory || typeof window === "undefined") return;
391
+ _inertiajs_react.router.push({
392
+ url,
393
+ preserveScroll: true,
394
+ preserveState: true,
395
+ props: modalData ? (currentProps) => ({
396
+ ...currentProps,
397
+ _inertiaui_modal: {
398
+ ...modalData,
399
+ baseUrl
400
+ }
401
+ }) : void 0
402
+ });
403
+ };
404
+ const visit = (href, method, payload = {}, headers = {}, config = {}, onClose = null, onAfterLeave = null, queryStringArrayFormat = "brackets", useBrowserHistory = false, onStart = null, onSuccess = null, onError = null, props = null) => {
405
+ const modalId = generateId();
406
+ return new Promise((resolve, reject) => {
407
+ if (href.startsWith("#")) {
408
+ resolve(pushLocalModal(href.substring(1), config, onClose, onAfterLeave, props));
409
+ return;
410
+ }
411
+ const [url, data] = (0, _inertiajs_core.mergeDataIntoQueryString)(method, href || "", payload, queryStringArrayFormat);
412
+ const cachedResponse = getCachedResponse(url, method, data);
413
+ if (cachedResponse) {
414
+ onSuccess?.(cachedResponse);
415
+ pushFromResponseData(cachedResponse.data, config, onClose, onAfterLeave).then((modal) => {
416
+ updateBrowserUrl(cachedResponse.data.url, useBrowserHistory, cachedResponse.data);
417
+ resolve(modal);
418
+ }).catch(reject);
419
+ return;
420
+ }
421
+ if (stackRef.current.length === 0) baseUrl = typeof window !== "undefined" ? window.location.href : "";
422
+ const requestHeaders = {
423
+ ...headers,
424
+ Accept: "text/html, application/xhtml+xml",
425
+ "X-Requested-With": "XMLHttpRequest",
426
+ "X-Inertia": "true",
427
+ "X-Inertia-Version": pageVersion ?? "",
428
+ "X-InertiaUI-Modal": modalId,
429
+ "X-InertiaUI-Modal-Base-Url": baseUrl ?? ""
430
+ };
431
+ onStart?.();
432
+ _inertiajs_react.progress?.start();
433
+ (0, axios.default)({
434
+ url,
435
+ method,
436
+ data,
437
+ headers: requestHeaders
438
+ }).then((response) => {
439
+ onSuccess?.(response);
440
+ pushFromResponseData(response.data, config, onClose, onAfterLeave).then((modal) => {
441
+ updateBrowserUrl(response.data.url, useBrowserHistory, response.data);
442
+ resolve(modal);
443
+ }).catch(reject);
444
+ }).catch((...args) => {
445
+ onError?.(...args);
446
+ reject(args[0]);
447
+ }).finally(() => {
448
+ _inertiajs_react.progress?.finish();
449
+ });
450
+ });
451
+ };
452
+ const registerLocalModal = (name, callback) => {
453
+ setLocalModals((prevLocalModals) => ({
454
+ ...prevLocalModals,
455
+ [name]: {
456
+ name,
457
+ callback
458
+ }
459
+ }));
460
+ };
461
+ const removeLocalModal = (name) => {
462
+ setLocalModals((prevLocalModals) => {
463
+ const newLocalModals = { ...prevLocalModals };
464
+ delete newLocalModals[name];
465
+ return newLocalModals;
466
+ });
467
+ };
468
+ const value = {
469
+ get stack() {
470
+ return stackRef.current;
471
+ },
472
+ localModals,
473
+ push,
474
+ pushFromResponseData,
475
+ length: () => stackRef.current.length,
476
+ closeAll: (force = false) => {
477
+ if (force) updateStack(() => []);
478
+ else [...stackRef.current].reverse().forEach((modal) => modal.close());
479
+ },
480
+ reset: () => updateStack(() => []),
481
+ visit,
482
+ visitModal,
483
+ registerLocalModal,
484
+ removeLocalModal
485
+ };
486
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ModalStackContext.Provider, {
487
+ value,
488
+ children
489
+ });
490
+ };
491
+ var useModalStack = () => {
492
+ const context = (0, react.useContext)(ModalStackContext);
493
+ if (context === null) throw new Error("useModalStack must be used within a ModalStackProvider");
494
+ return context;
495
+ };
496
+ var modalPropNames = [
497
+ "closeButton",
498
+ "closeExplicitly",
499
+ "closeOnClickOutside",
500
+ "maxWidth",
501
+ "paddingClasses",
502
+ "panelClasses",
503
+ "position",
504
+ "slideover"
505
+ ];
506
+ var initFromPageProps = (pageProps) => {
507
+ if (pageProps.initialPage) pageVersion = pageProps.initialPage.version ?? null;
508
+ if (pageProps.resolveComponent) resolveComponent = pageProps.resolveComponent;
509
+ };
510
+ var renderApp = (App, pageProps) => {
511
+ initFromPageProps(pageProps);
512
+ const renderInertiaApp = ({ Component, props, key }) => {
513
+ const renderComponent = () => {
514
+ const child = (0, react.createElement)(Component, {
515
+ key,
516
+ ...props
517
+ });
518
+ if (typeof Component.layout === "function") return Component.layout(child);
519
+ if (Array.isArray(Component.layout)) return Component.layout.slice().reverse().reduce((acc, Layout) => (0, react.createElement)(Layout, props, acc), child);
520
+ return child;
521
+ };
522
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [renderComponent(), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ModalRoot, {})] });
523
+ };
524
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ModalStackProvider, { children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(App, {
525
+ ...pageProps,
526
+ children: renderInertiaApp
527
+ }) });
528
+ };
529
+ var ModalRoot = ({ children }) => {
530
+ const context = (0, react.useContext)(ModalStackContext);
531
+ const $page = (0, _inertiajs_react.usePage)();
532
+ const pendingModalKeysRef = (0, react.useRef)(/* @__PURE__ */ new Set());
533
+ const getModalKey = (modalData) => modalData.id || `${modalData.component}:${modalData.url}`;
534
+ const isNavigatingRef = (0, react.useRef)(false);
535
+ const initialModalStillOpenedRef = (0, react.useRef)(!!$page.props?._inertiaui_modal);
536
+ (0, react.useEffect)(() => _inertiajs_react.router.on("start", () => isNavigatingRef.current = true), []);
537
+ (0, react.useEffect)(() => _inertiajs_react.router.on("finish", () => isNavigatingRef.current = false), []);
538
+ (0, react.useEffect)(() => _inertiajs_react.router.on("navigate", function($event) {
539
+ const modalOnBase = $event.detail.page.props._inertiaui_modal;
540
+ const pageUrl = $event.detail.page.url;
541
+ if (closingToBaseUrlTarget) {
542
+ if (new URL(closingToBaseUrlTarget, "http://x").pathname === new URL(pageUrl, "http://x").pathname) {
543
+ closingToBaseUrlTarget = null;
544
+ context?.closeAll(true);
545
+ baseUrl = null;
546
+ initialModalStillOpenedRef.current = false;
547
+ return;
548
+ }
549
+ closingToBaseUrlTarget = null;
550
+ }
551
+ if (!modalOnBase) {
552
+ context?.closeAll(true);
553
+ baseUrl = null;
554
+ initialModalStillOpenedRef.current = false;
555
+ return;
556
+ }
557
+ if (!(0, _inertiaui_vanilla.sameUrlPath)(pageUrl, modalOnBase.url)) {
558
+ context?.closeAll(true);
559
+ baseUrl = null;
560
+ initialModalStillOpenedRef.current = false;
561
+ return;
562
+ }
563
+ const modalKey = getModalKey(modalOnBase);
564
+ if (pendingModalKeysRef.current.has(modalKey)) return;
565
+ if (modalOnBase.id && context?.stack.some((m) => m.id === modalOnBase.id)) return;
566
+ if (context?.stack.some((m) => m.response?.component === modalOnBase.component && (0, _inertiaui_vanilla.sameUrlPath)(m.response?.url, modalOnBase.url))) return;
567
+ baseUrl = modalOnBase.baseUrl;
568
+ pendingModalKeysRef.current.add(modalKey);
569
+ context?.pushFromResponseData(modalOnBase, {}, () => {
570
+ if (!modalOnBase.baseUrl) {
571
+ console.error("No base url in modal response data so cannot navigate back");
572
+ return;
573
+ }
574
+ if (!isNavigatingRef.current && typeof window !== "undefined" && window.location.href !== modalOnBase.baseUrl) _inertiajs_react.router.visit(modalOnBase.baseUrl, {
575
+ preserveScroll: true,
576
+ preserveState: true
577
+ });
578
+ }).finally(() => {
579
+ pendingModalKeysRef.current.delete(modalKey);
580
+ });
581
+ }), []);
582
+ const axiosRequestInterceptor = (config) => {
583
+ const baseUrlValue = baseUrl ?? (initialModalStillOpenedRef.current ? $page.props._inertiaui_modal?.baseUrl : null);
584
+ if (baseUrlValue) config.headers["X-InertiaUI-Modal-Base-Url"] = baseUrlValue;
585
+ return config;
586
+ };
587
+ (0, react.useEffect)(() => {
588
+ const interceptorId = axios.default.interceptors.request.use(axiosRequestInterceptor);
589
+ return () => axios.default.interceptors.request.eject(interceptorId);
590
+ }, []);
591
+ const previousModalRef = (0, react.useRef)(void 0);
592
+ (0, react.useEffect)(() => {
593
+ const newModal = $page.props?._inertiaui_modal;
594
+ const previousModal = previousModalRef.current;
595
+ previousModalRef.current = newModal;
596
+ if (!newModal) return;
597
+ if (previousModal && newModal.component === previousModal.component && (0, _inertiaui_vanilla.sameUrlPath)(newModal.url, previousModal.url)) {
598
+ context?.stack[0]?.updateProps(newModal.props ?? {});
599
+ return;
600
+ }
601
+ if (!previousModal && context && context.stack.length > 0) {
602
+ const existingModal = context.stack.find((m) => m.response?.component === newModal.component && (0, _inertiaui_vanilla.sameUrlPath)(m.response?.url, newModal.url));
603
+ if (existingModal) existingModal.updateProps(newModal.props ?? {});
604
+ }
605
+ }, [$page.props?._inertiaui_modal]);
606
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [children, context && context.stack.length > 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ModalRenderer, { index: 0 })] });
607
+ };
608
+ //#endregion
609
+ //#region src/ModalRenderer.tsx
610
+ var ModalIndexContext = react.default.createContext(null);
611
+ ModalIndexContext.displayName = "ModalIndexContext";
612
+ var useModalIndex = () => {
613
+ return react.default.useContext(ModalIndexContext);
614
+ };
615
+ var ModalRenderer = ({ index }) => {
616
+ const { stack } = useModalStack();
617
+ const modalContext = (0, react.useMemo)(() => {
618
+ return stack[index];
619
+ }, [stack, index]);
620
+ if (!modalContext?.component) return null;
621
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ModalIndexContext.Provider, {
622
+ value: index,
623
+ children: (0, react.createElement)(modalContext.component, {
624
+ ...modalContext.props,
625
+ onModalEvent: (...args) => modalContext.emit("modal-event", ...args)
626
+ })
627
+ });
628
+ };
629
+ //#endregion
630
+ //#region src/useModal.ts
631
+ function useModal() {
632
+ return useModalStack().stack[useModalIndex()] ?? null;
633
+ }
634
+ //#endregion
635
+ //#region src/Deferred.tsx
636
+ var Deferred = ({ children, data, fallback }) => {
637
+ if (!data) throw new Error("`<Deferred>` requires a `data` prop to be a string or array of strings");
638
+ const [loaded, setLoaded] = (0, react.useState)(false);
639
+ const keys = Array.isArray(data) ? data : [data];
640
+ const modalProps = useModal()?.props ?? {};
641
+ (0, react.useEffect)(() => {
642
+ setLoaded(keys.every((key) => modalProps[key] !== void 0));
643
+ }, [modalProps, keys]);
644
+ return loaded ? children : fallback;
645
+ };
646
+ Deferred.displayName = "InertiaModalDeferred";
647
+ //#endregion
648
+ //#region src/HeadlessModal.tsx
649
+ var HeadlessModal = (0, react.forwardRef)((allProps, ref) => {
650
+ const { name, children, onFocus, onBlur, onClose, onSuccess, ...props } = allProps;
651
+ const modalIndex = useModalIndex();
652
+ const { stack, registerLocalModal, removeLocalModal } = useModalStack();
653
+ const [localModalContext, setLocalModalContext] = (0, react.useState)(null);
654
+ const modalContext = (0, react.useMemo)(() => name ? localModalContext : stack[modalIndex], [
655
+ name,
656
+ localModalContext,
657
+ modalIndex,
658
+ stack
659
+ ]);
660
+ const nextIndex = (0, react.useMemo)(() => {
661
+ return stack.find((m) => m.shouldRender && m.index > (modalContext?.index ?? -1))?.index;
662
+ }, [modalIndex, stack]);
663
+ const configSlideover = (0, react.useMemo)(() => modalContext?.config.slideover ?? props.slideover ?? getConfig("type") === "slideover", [props.slideover, modalContext?.config.slideover]);
664
+ const config = (0, react.useMemo)(() => ({
665
+ slideover: configSlideover,
666
+ closeButton: props.closeButton ?? getConfigByType(configSlideover, "closeButton"),
667
+ closeExplicitly: props.closeExplicitly ?? getConfigByType(configSlideover, "closeExplicitly"),
668
+ closeOnClickOutside: props.closeOnClickOutside ?? getConfigByType(configSlideover, "closeOnClickOutside"),
669
+ maxWidth: props.maxWidth ?? getConfigByType(configSlideover, "maxWidth"),
670
+ paddingClasses: props.paddingClasses ?? getConfigByType(configSlideover, "paddingClasses"),
671
+ panelClasses: props.panelClasses ?? getConfigByType(configSlideover, "panelClasses"),
672
+ position: props.position ?? getConfigByType(configSlideover, "position"),
673
+ ...modalContext?.config
674
+ }), [
675
+ props,
676
+ modalContext?.config,
677
+ configSlideover
678
+ ]);
679
+ (0, react.useEffect)(() => {
680
+ if (name) {
681
+ let removeListeners = null;
682
+ registerLocalModal(name, (localContext) => {
683
+ removeListeners = localContext.registerEventListenersFromProps(props);
684
+ setLocalModalContext(localContext);
685
+ });
686
+ return () => {
687
+ removeListeners?.();
688
+ removeListeners = null;
689
+ removeLocalModal(name);
690
+ };
691
+ }
692
+ return modalContext?.registerEventListenersFromProps(props);
693
+ }, [name]);
694
+ const modalContextRef = (0, react.useRef)(modalContext);
695
+ (0, react.useEffect)(() => {
696
+ modalContextRef.current = modalContext;
697
+ }, [modalContext]);
698
+ const previousIsOpenRef = (0, react.useRef)(void 0);
699
+ (0, react.useEffect)(() => {
700
+ if (modalContext != null) {
701
+ if (modalContext.isOpen) onSuccess?.();
702
+ else if (previousIsOpenRef.current === true) onClose?.();
703
+ previousIsOpenRef.current = modalContext.isOpen;
704
+ }
705
+ }, [modalContext?.isOpen]);
706
+ const [rendered, setRendered] = (0, react.useState)(false);
707
+ (0, react.useEffect)(() => {
708
+ if (rendered && modalContext != null && modalContext.isOpen) if (modalContext.onTopOfStack) onFocus?.();
709
+ else onBlur?.();
710
+ setRendered(true);
711
+ }, [modalContext?.onTopOfStack]);
712
+ (0, react.useImperativeHandle)(ref, () => ({
713
+ afterLeave: () => modalContextRef.current?.afterLeave(),
714
+ close: () => modalContextRef.current?.close(),
715
+ emit: (...args) => modalContextRef.current?.emit(...args),
716
+ getChildModal: () => modalContextRef.current?.getChildModal(),
717
+ getParentModal: () => modalContextRef.current?.getParentModal(),
718
+ reload: (options) => modalContextRef.current?.reload(options),
719
+ setOpen: (open) => modalContextRef.current?.setOpen(open),
720
+ get id() {
721
+ return modalContextRef.current?.id;
722
+ },
723
+ get index() {
724
+ return modalContextRef.current?.index;
725
+ },
726
+ get isOpen() {
727
+ return modalContextRef.current?.isOpen;
728
+ },
729
+ get config() {
730
+ return modalContextRef.current?.config;
731
+ },
732
+ get modalContext() {
733
+ return modalContextRef.current;
734
+ },
735
+ get onTopOfStack() {
736
+ return modalContextRef.current?.onTopOfStack;
737
+ },
738
+ get shouldRender() {
739
+ return modalContextRef.current?.shouldRender;
740
+ }
741
+ }), [modalContext]);
742
+ if (!modalContext?.shouldRender) return null;
743
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [typeof children === "function" ? children({
744
+ ...modalContext.props,
745
+ afterLeave: modalContext.afterLeave,
746
+ close: modalContext.close,
747
+ config,
748
+ emit: modalContext.emit,
749
+ getChildModal: modalContext.getChildModal,
750
+ getParentModal: modalContext.getParentModal,
751
+ id: modalContext.id,
752
+ index: modalContext.index,
753
+ isOpen: modalContext.isOpen,
754
+ modalContext,
755
+ onTopOfStack: modalContext.onTopOfStack,
756
+ reload: modalContext.reload,
757
+ setOpen: modalContext.setOpen,
758
+ shouldRender: modalContext.shouldRender
759
+ }) : children, nextIndex !== void 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ModalRenderer, { index: nextIndex })] });
760
+ });
761
+ HeadlessModal.displayName = "HeadlessModal";
762
+ //#endregion
763
+ //#region ../node_modules/.pnpm/clsx@2.1.1/node_modules/clsx/dist/clsx.mjs
764
+ function r(e) {
765
+ var t, f, n = "";
766
+ if ("string" == typeof e || "number" == typeof e) n += e;
767
+ else if ("object" == typeof e) if (Array.isArray(e)) {
768
+ var o = e.length;
769
+ for (t = 0; t < o; t++) e[t] && (f = r(e[t])) && (n && (n += " "), n += f);
770
+ } else for (f in e) e[f] && (n && (n += " "), n += f);
771
+ return n;
772
+ }
773
+ function clsx() {
774
+ for (var e, t, f = 0, n = "", o = arguments.length; f < o; f++) (e = arguments[f]) && (t = r(e)) && (n && (n += " "), n += t);
775
+ return n;
776
+ }
777
+ //#endregion
778
+ //#region src/CloseButton.tsx
779
+ function CloseButton({ onClick }) {
780
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("button", {
781
+ type: "button",
782
+ className: "im-close-button text-gray-400 hover:text-gray-500",
783
+ onClick,
784
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
785
+ className: "sr-only",
786
+ children: "Close"
787
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("svg", {
788
+ className: "size-6",
789
+ xmlns: "http://www.w3.org/2000/svg",
790
+ fill: "none",
791
+ viewBox: "0 0 24 24",
792
+ strokeWidth: "2",
793
+ stroke: "currentColor",
794
+ "aria-hidden": "true",
795
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("path", {
796
+ strokeLinecap: "round",
797
+ strokeLinejoin: "round",
798
+ d: "M6 18L18 6M6 6l12 12"
799
+ })
800
+ })]
801
+ });
802
+ }
803
+ //#endregion
804
+ //#region src/constants.ts
805
+ /**
806
+ * Max width classes for modals and slideovers.
807
+ * Uses a map lookup for Tailwind 4 compatibility (scanner picks up full class strings).
808
+ */
809
+ var maxWidthClasses = {
810
+ sm: "sm:max-w-sm",
811
+ md: "sm:max-w-md",
812
+ lg: "sm:max-w-md md:max-w-lg",
813
+ xl: "sm:max-w-md md:max-w-xl",
814
+ "2xl": "sm:max-w-md md:max-w-xl lg:max-w-2xl",
815
+ "3xl": "sm:max-w-md md:max-w-xl lg:max-w-3xl",
816
+ "4xl": "sm:max-w-md md:max-w-xl lg:max-w-3xl xl:max-w-4xl",
817
+ "5xl": "sm:max-w-md md:max-w-xl lg:max-w-3xl xl:max-w-5xl",
818
+ "6xl": "sm:max-w-md md:max-w-xl lg:max-w-3xl xl:max-w-5xl 2xl:max-w-6xl",
819
+ "7xl": "sm:max-w-md md:max-w-xl lg:max-w-3xl xl:max-w-5xl 2xl:max-w-7xl"
820
+ };
821
+ function getMaxWidthClass(maxWidth) {
822
+ return maxWidthClasses[maxWidth] || maxWidthClasses["2xl"];
823
+ }
824
+ //#endregion
825
+ //#region src/ModalContent.tsx
826
+ var ModalContent = ({ modalContext, config, useNativeDialog, isFirstModal, onAfterLeave, children }) => {
827
+ const [isRendered, setIsRendered] = (0, react.useState)(false);
828
+ const [isVisible, setIsVisible] = (0, react.useState)(false);
829
+ const [entered, setEntered] = (0, react.useState)(false);
830
+ const wrapperRef = (0, react.useRef)(null);
831
+ const dialogRef = (0, react.useRef)(null);
832
+ const nativeWrapperRef = (0, react.useRef)(null);
833
+ const cleanupFocusTrapRef = (0, react.useRef)(null);
834
+ const cleanupEscapeKeyRef = (0, react.useRef)(null);
835
+ const maxWidthClass = (0, react.useMemo)(() => getMaxWidthClass(config.maxWidth), [config.maxWidth]);
836
+ const animateIn = (0, react.useCallback)(async (element) => {
837
+ if (!element) return;
838
+ setIsVisible(true);
839
+ await (0, _inertiaui_vanilla.animate)(element, [{
840
+ transform: "translate3d(0, 1rem, 0) scale(0.95)",
841
+ opacity: 0
842
+ }, {
843
+ transform: "translate3d(0, 0, 0) scale(1)",
844
+ opacity: 1
845
+ }]);
846
+ setEntered(true);
847
+ }, []);
848
+ const animateOut = (0, react.useCallback)(async (element) => {
849
+ if (!element) return;
850
+ setIsVisible(false);
851
+ await (0, _inertiaui_vanilla.animate)(element, [{
852
+ transform: "translate3d(0, 0, 0) scale(1)",
853
+ opacity: 1
854
+ }, {
855
+ transform: "translate3d(0, 1rem, 0) scale(0.95)",
856
+ opacity: 0
857
+ }]);
858
+ setIsRendered(false);
859
+ if (useNativeDialog && dialogRef.current) dialogRef.current.close();
860
+ onAfterLeave?.();
861
+ modalContext.afterLeave();
862
+ }, [
863
+ useNativeDialog,
864
+ onAfterLeave,
865
+ modalContext
866
+ ]);
867
+ const setupFocusTrap = (0, react.useCallback)(() => {
868
+ if (useNativeDialog) return;
869
+ if (!wrapperRef.current || !modalContext.onTopOfStack) return;
870
+ if (cleanupFocusTrapRef.current) return;
871
+ cleanupFocusTrapRef.current = (0, _inertiaui_vanilla.createFocusTrap)(wrapperRef.current, {
872
+ initialFocus: true,
873
+ returnFocus: false
874
+ });
875
+ }, [modalContext.onTopOfStack, useNativeDialog]);
876
+ const cleanupFocusTrap = (0, react.useCallback)(() => {
877
+ if (cleanupFocusTrapRef.current) {
878
+ cleanupFocusTrapRef.current();
879
+ cleanupFocusTrapRef.current = null;
880
+ }
881
+ }, []);
882
+ const setupEscapeKey = (0, react.useCallback)(() => {
883
+ if (useNativeDialog) return;
884
+ if (cleanupEscapeKeyRef.current) return;
885
+ if (config?.closeExplicitly) return;
886
+ cleanupEscapeKeyRef.current = (0, _inertiaui_vanilla.onEscapeKey)(() => {
887
+ if (modalContext.onTopOfStack) modalContext.close();
888
+ });
889
+ }, [
890
+ config?.closeExplicitly,
891
+ modalContext,
892
+ useNativeDialog
893
+ ]);
894
+ const cleanupEscapeKey = (0, react.useCallback)(() => {
895
+ if (cleanupEscapeKeyRef.current) {
896
+ cleanupEscapeKeyRef.current();
897
+ cleanupEscapeKeyRef.current = null;
898
+ }
899
+ }, []);
900
+ const handleClickOutside = (0, react.useCallback)((event) => {
901
+ if (useNativeDialog) return;
902
+ if (!modalContext.onTopOfStack) return;
903
+ if (config?.closeExplicitly) return;
904
+ if (config?.closeOnClickOutside === false) return;
905
+ if (!wrapperRef.current) return;
906
+ if (!wrapperRef.current.contains(event.target)) modalContext.close();
907
+ }, [
908
+ modalContext,
909
+ config?.closeExplicitly,
910
+ config?.closeOnClickOutside,
911
+ useNativeDialog
912
+ ]);
913
+ const handleCancel = (0, react.useCallback)((event) => {
914
+ event.preventDefault();
915
+ if (modalContext.onTopOfStack && !config?.closeExplicitly) modalContext.close();
916
+ }, [modalContext, config?.closeExplicitly]);
917
+ const handleDialogClick = (0, react.useCallback)((event) => {
918
+ if (event.target === dialogRef.current) {
919
+ if (modalContext.onTopOfStack && !config?.closeExplicitly && config?.closeOnClickOutside !== false) modalContext.close();
920
+ }
921
+ }, [
922
+ modalContext,
923
+ config?.closeExplicitly,
924
+ config?.closeOnClickOutside
925
+ ]);
926
+ const prevIsOpenRef = (0, react.useRef)(modalContext.isOpen);
927
+ (0, react.useEffect)(() => {
928
+ if (useNativeDialog) {
929
+ if (modalContext.isOpen && !dialogRef.current?.open) {
930
+ dialogRef.current?.showModal();
931
+ animateIn(nativeWrapperRef.current);
932
+ } else if (!modalContext.isOpen && prevIsOpenRef.current) {
933
+ setEntered(false);
934
+ animateOut(nativeWrapperRef.current);
935
+ }
936
+ } else if (modalContext.isOpen && !isRendered) setIsRendered(true);
937
+ else if (!modalContext.isOpen && prevIsOpenRef.current) {
938
+ setEntered(false);
939
+ animateOut(wrapperRef.current);
940
+ }
941
+ prevIsOpenRef.current = modalContext.isOpen;
942
+ }, [
943
+ modalContext.isOpen,
944
+ useNativeDialog,
945
+ animateIn,
946
+ animateOut,
947
+ isRendered
948
+ ]);
949
+ (0, react.useEffect)(() => {
950
+ if (!useNativeDialog && isRendered && !entered && modalContext.isOpen) animateIn(wrapperRef.current).then(() => {
951
+ setupFocusTrap();
952
+ });
953
+ }, [
954
+ isRendered,
955
+ useNativeDialog,
956
+ entered,
957
+ modalContext.isOpen,
958
+ animateIn,
959
+ setupFocusTrap
960
+ ]);
961
+ (0, react.useEffect)(() => {
962
+ if (!useNativeDialog) setupEscapeKey();
963
+ return () => {
964
+ cleanupEscapeKey();
965
+ };
966
+ }, [
967
+ useNativeDialog,
968
+ setupEscapeKey,
969
+ cleanupEscapeKey
970
+ ]);
971
+ (0, react.useEffect)(() => {
972
+ if (useNativeDialog) return;
973
+ if (modalContext.onTopOfStack) {
974
+ setupEscapeKey();
975
+ if (entered) setupFocusTrap();
976
+ } else {
977
+ cleanupFocusTrap();
978
+ cleanupEscapeKey();
979
+ }
980
+ }, [
981
+ modalContext.onTopOfStack,
982
+ entered,
983
+ setupEscapeKey,
984
+ setupFocusTrap,
985
+ cleanupFocusTrap,
986
+ cleanupEscapeKey,
987
+ useNativeDialog
988
+ ]);
989
+ (0, react.useEffect)(() => {
990
+ return () => {
991
+ const wrapper = useNativeDialog ? nativeWrapperRef.current : wrapperRef.current;
992
+ if (wrapper) (0, _inertiaui_vanilla.cancelAnimations)(wrapper);
993
+ if (useNativeDialog) {
994
+ if (dialogRef.current?.open) dialogRef.current.close();
995
+ } else {
996
+ cleanupFocusTrap();
997
+ cleanupEscapeKey();
998
+ }
999
+ };
1000
+ }, [
1001
+ useNativeDialog,
1002
+ cleanupFocusTrap,
1003
+ cleanupEscapeKey
1004
+ ]);
1005
+ const renderContent = () => /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
1006
+ className: `im-modal-content relative ${config.paddingClasses} ${config.panelClasses}`,
1007
+ "data-inertiaui-modal-entered": entered,
1008
+ children: [config.closeButton && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
1009
+ className: "absolute top-0 right-0 pt-3 pr-3",
1010
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(CloseButton, { onClick: modalContext.close })
1011
+ }), typeof children === "function" ? children({
1012
+ modalContext,
1013
+ config
1014
+ }) : children]
1015
+ });
1016
+ if (useNativeDialog) return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("dialog", {
1017
+ ref: dialogRef,
1018
+ className: clsx("im-modal-dialog m-0 overflow-visible bg-transparent p-0", "size-full max-h-none max-w-none", "backdrop:bg-black/75 backdrop:transition-opacity backdrop:duration-300", isVisible ? "backdrop:opacity-100" : "backdrop:opacity-0", !isFirstModal && "backdrop:bg-transparent"),
1019
+ onCancel: handleCancel,
1020
+ onClick: handleDialogClick,
1021
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
1022
+ className: "im-modal-container fixed inset-0 overflow-y-auto p-4",
1023
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
1024
+ className: clsx("im-modal-positioner flex min-h-full justify-center", {
1025
+ "items-start": config.position === "top",
1026
+ "items-center": config.position === "center",
1027
+ "items-end": config.position === "bottom"
1028
+ }),
1029
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
1030
+ ref: nativeWrapperRef,
1031
+ className: clsx("im-modal-wrapper w-full transition-[filter] duration-300", modalContext.onTopOfStack ? "" : "blur-xs", maxWidthClass),
1032
+ children: renderContent()
1033
+ })
1034
+ })
1035
+ })
1036
+ });
1037
+ if (!isRendered) return null;
1038
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
1039
+ className: "im-modal-container fixed inset-0 z-40 overflow-y-auto p-4",
1040
+ onMouseDown: handleClickOutside,
1041
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
1042
+ className: clsx("im-modal-positioner flex min-h-full justify-center", {
1043
+ "items-start": config.position === "top",
1044
+ "items-center": config.position === "center",
1045
+ "items-end": config.position === "bottom"
1046
+ }),
1047
+ onMouseDown: handleClickOutside,
1048
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
1049
+ ref: wrapperRef,
1050
+ role: "dialog",
1051
+ "aria-modal": "true",
1052
+ className: clsx("im-modal-wrapper w-full transition-[filter] duration-300", modalContext.onTopOfStack ? "" : "blur-xs", maxWidthClass),
1053
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
1054
+ className: "sr-only",
1055
+ children: "Dialog"
1056
+ }), renderContent()]
1057
+ })
1058
+ })
1059
+ });
1060
+ };
1061
+ //#endregion
1062
+ //#region src/SlideoverContent.tsx
1063
+ var SlideoverContent = ({ modalContext, config, useNativeDialog, isFirstModal, onAfterLeave, children }) => {
1064
+ const [isRendered, setIsRendered] = (0, react.useState)(false);
1065
+ const [isVisible, setIsVisible] = (0, react.useState)(false);
1066
+ const [entered, setEntered] = (0, react.useState)(false);
1067
+ const wrapperRef = (0, react.useRef)(null);
1068
+ const dialogRef = (0, react.useRef)(null);
1069
+ const nativeWrapperRef = (0, react.useRef)(null);
1070
+ const cleanupFocusTrapRef = (0, react.useRef)(null);
1071
+ const cleanupEscapeKeyRef = (0, react.useRef)(null);
1072
+ const isLeft = config.position === "left";
1073
+ const maxWidthClass = (0, react.useMemo)(() => getMaxWidthClass(config.maxWidth), [config.maxWidth]);
1074
+ const getTranslateX = (0, react.useCallback)(() => isLeft ? "-100%" : "100%", [isLeft]);
1075
+ const animateIn = (0, react.useCallback)(async (element) => {
1076
+ if (!element) return;
1077
+ setIsVisible(true);
1078
+ await (0, _inertiaui_vanilla.animate)(element, [{
1079
+ transform: `translate3d(${getTranslateX()}, 0, 0)`,
1080
+ opacity: 0
1081
+ }, {
1082
+ transform: "translate3d(0, 0, 0)",
1083
+ opacity: 1
1084
+ }]);
1085
+ setEntered(true);
1086
+ }, [getTranslateX]);
1087
+ const animateOut = (0, react.useCallback)(async (element) => {
1088
+ if (!element) return;
1089
+ setIsVisible(false);
1090
+ await (0, _inertiaui_vanilla.animate)(element, [{
1091
+ transform: "translate3d(0, 0, 0)",
1092
+ opacity: 1
1093
+ }, {
1094
+ transform: `translate3d(${getTranslateX()}, 0, 0)`,
1095
+ opacity: 0
1096
+ }]);
1097
+ setIsRendered(false);
1098
+ if (useNativeDialog && dialogRef.current) dialogRef.current.close();
1099
+ onAfterLeave?.();
1100
+ modalContext.afterLeave();
1101
+ }, [
1102
+ getTranslateX,
1103
+ useNativeDialog,
1104
+ onAfterLeave,
1105
+ modalContext
1106
+ ]);
1107
+ const setupFocusTrap = (0, react.useCallback)(() => {
1108
+ if (useNativeDialog) return;
1109
+ if (!wrapperRef.current || !modalContext.onTopOfStack) return;
1110
+ if (cleanupFocusTrapRef.current) return;
1111
+ cleanupFocusTrapRef.current = (0, _inertiaui_vanilla.createFocusTrap)(wrapperRef.current, {
1112
+ initialFocus: true,
1113
+ returnFocus: false
1114
+ });
1115
+ }, [modalContext.onTopOfStack, useNativeDialog]);
1116
+ const cleanupFocusTrap = (0, react.useCallback)(() => {
1117
+ if (cleanupFocusTrapRef.current) {
1118
+ cleanupFocusTrapRef.current();
1119
+ cleanupFocusTrapRef.current = null;
1120
+ }
1121
+ }, []);
1122
+ const setupEscapeKey = (0, react.useCallback)(() => {
1123
+ if (useNativeDialog) return;
1124
+ if (cleanupEscapeKeyRef.current) return;
1125
+ if (config?.closeExplicitly) return;
1126
+ cleanupEscapeKeyRef.current = (0, _inertiaui_vanilla.onEscapeKey)(() => {
1127
+ if (modalContext.onTopOfStack) modalContext.close();
1128
+ });
1129
+ }, [
1130
+ config?.closeExplicitly,
1131
+ modalContext,
1132
+ useNativeDialog
1133
+ ]);
1134
+ const cleanupEscapeKey = (0, react.useCallback)(() => {
1135
+ if (cleanupEscapeKeyRef.current) {
1136
+ cleanupEscapeKeyRef.current();
1137
+ cleanupEscapeKeyRef.current = null;
1138
+ }
1139
+ }, []);
1140
+ const handleClickOutside = (0, react.useCallback)((event) => {
1141
+ if (useNativeDialog) return;
1142
+ if (!modalContext.onTopOfStack) return;
1143
+ if (config?.closeExplicitly) return;
1144
+ if (config?.closeOnClickOutside === false) return;
1145
+ if (!wrapperRef.current) return;
1146
+ if (!wrapperRef.current.contains(event.target)) modalContext.close();
1147
+ }, [
1148
+ modalContext,
1149
+ config?.closeExplicitly,
1150
+ config?.closeOnClickOutside,
1151
+ useNativeDialog
1152
+ ]);
1153
+ const handleCancel = (0, react.useCallback)((event) => {
1154
+ event.preventDefault();
1155
+ if (modalContext.onTopOfStack && !config?.closeExplicitly) modalContext.close();
1156
+ }, [modalContext, config?.closeExplicitly]);
1157
+ const handleDialogClick = (0, react.useCallback)((event) => {
1158
+ if (event.target === dialogRef.current) {
1159
+ if (modalContext.onTopOfStack && !config?.closeExplicitly && config?.closeOnClickOutside !== false) modalContext.close();
1160
+ }
1161
+ }, [
1162
+ modalContext,
1163
+ config?.closeExplicitly,
1164
+ config?.closeOnClickOutside
1165
+ ]);
1166
+ const prevIsOpenRef = (0, react.useRef)(modalContext.isOpen);
1167
+ (0, react.useEffect)(() => {
1168
+ if (useNativeDialog) {
1169
+ if (modalContext.isOpen && !dialogRef.current?.open) {
1170
+ dialogRef.current?.showModal();
1171
+ animateIn(nativeWrapperRef.current);
1172
+ } else if (!modalContext.isOpen && prevIsOpenRef.current) {
1173
+ setEntered(false);
1174
+ animateOut(nativeWrapperRef.current);
1175
+ }
1176
+ } else if (modalContext.isOpen && !isRendered) setIsRendered(true);
1177
+ else if (!modalContext.isOpen && prevIsOpenRef.current) {
1178
+ setEntered(false);
1179
+ animateOut(wrapperRef.current);
1180
+ }
1181
+ prevIsOpenRef.current = modalContext.isOpen;
1182
+ }, [
1183
+ modalContext.isOpen,
1184
+ useNativeDialog,
1185
+ animateIn,
1186
+ animateOut,
1187
+ isRendered
1188
+ ]);
1189
+ (0, react.useEffect)(() => {
1190
+ if (!useNativeDialog && isRendered && !entered && modalContext.isOpen) animateIn(wrapperRef.current).then(() => {
1191
+ setupFocusTrap();
1192
+ });
1193
+ }, [
1194
+ isRendered,
1195
+ useNativeDialog,
1196
+ entered,
1197
+ modalContext.isOpen,
1198
+ animateIn,
1199
+ setupFocusTrap
1200
+ ]);
1201
+ (0, react.useEffect)(() => {
1202
+ if (!useNativeDialog) setupEscapeKey();
1203
+ return () => {
1204
+ cleanupEscapeKey();
1205
+ };
1206
+ }, [
1207
+ useNativeDialog,
1208
+ setupEscapeKey,
1209
+ cleanupEscapeKey
1210
+ ]);
1211
+ (0, react.useEffect)(() => {
1212
+ if (useNativeDialog) return;
1213
+ if (modalContext.onTopOfStack) {
1214
+ setupEscapeKey();
1215
+ if (entered) setupFocusTrap();
1216
+ } else {
1217
+ cleanupFocusTrap();
1218
+ cleanupEscapeKey();
1219
+ }
1220
+ }, [
1221
+ modalContext.onTopOfStack,
1222
+ entered,
1223
+ setupEscapeKey,
1224
+ setupFocusTrap,
1225
+ cleanupFocusTrap,
1226
+ cleanupEscapeKey,
1227
+ useNativeDialog
1228
+ ]);
1229
+ (0, react.useEffect)(() => {
1230
+ return () => {
1231
+ const wrapper = useNativeDialog ? nativeWrapperRef.current : wrapperRef.current;
1232
+ if (wrapper) (0, _inertiaui_vanilla.cancelAnimations)(wrapper);
1233
+ if (useNativeDialog) {
1234
+ if (dialogRef.current?.open) dialogRef.current.close();
1235
+ } else {
1236
+ cleanupFocusTrap();
1237
+ cleanupEscapeKey();
1238
+ }
1239
+ };
1240
+ }, [
1241
+ useNativeDialog,
1242
+ cleanupFocusTrap,
1243
+ cleanupEscapeKey
1244
+ ]);
1245
+ const renderContent = () => /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
1246
+ className: `im-slideover-content relative ${config.paddingClasses} ${config.panelClasses}`,
1247
+ "data-inertiaui-modal-entered": entered,
1248
+ children: [config.closeButton && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
1249
+ className: "absolute top-0 right-0 pt-3 pr-3",
1250
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(CloseButton, { onClick: modalContext.close })
1251
+ }), typeof children === "function" ? children({
1252
+ modalContext,
1253
+ config
1254
+ }) : children]
1255
+ });
1256
+ if (useNativeDialog) return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("dialog", {
1257
+ ref: dialogRef,
1258
+ className: clsx("im-slideover-dialog m-0 overflow-visible bg-transparent p-0", "size-full max-h-none max-w-none", "backdrop:bg-black/75 backdrop:transition-opacity backdrop:duration-300", isVisible ? "backdrop:opacity-100" : "backdrop:opacity-0", !isFirstModal && "backdrop:bg-transparent"),
1259
+ onCancel: handleCancel,
1260
+ onClick: handleDialogClick,
1261
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
1262
+ className: "im-slideover-container fixed inset-0 overflow-x-hidden overflow-y-auto",
1263
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
1264
+ className: clsx("im-slideover-positioner flex min-h-full items-center", {
1265
+ "justify-start rtl:justify-end": config?.position === "left",
1266
+ "justify-end rtl:justify-start": config?.position === "right"
1267
+ }),
1268
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
1269
+ ref: nativeWrapperRef,
1270
+ className: clsx("im-slideover-wrapper w-full transition-[filter] duration-300", modalContext.onTopOfStack ? "" : "blur-xs", maxWidthClass),
1271
+ children: renderContent()
1272
+ })
1273
+ })
1274
+ })
1275
+ });
1276
+ if (!isRendered) return null;
1277
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
1278
+ className: "im-slideover-container fixed inset-0 z-40 overflow-x-hidden overflow-y-auto",
1279
+ onMouseDown: handleClickOutside,
1280
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
1281
+ className: clsx("im-slideover-positioner flex min-h-full items-center", {
1282
+ "justify-start rtl:justify-end": config?.position === "left",
1283
+ "justify-end rtl:justify-start": config?.position === "right"
1284
+ }),
1285
+ onMouseDown: handleClickOutside,
1286
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
1287
+ ref: wrapperRef,
1288
+ role: "dialog",
1289
+ "aria-modal": "true",
1290
+ className: clsx("im-slideover-wrapper w-full transition-[filter] duration-300", modalContext.onTopOfStack ? "" : "blur-xs", maxWidthClass),
1291
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
1292
+ className: "sr-only",
1293
+ children: "Dialog"
1294
+ }), renderContent()]
1295
+ })
1296
+ })
1297
+ });
1298
+ };
1299
+ //#endregion
1300
+ //#region src/Modal.tsx
1301
+ var Modal = (0, react.forwardRef)((allProps, ref) => {
1302
+ const { name, children, onFocus, onBlur, onClose, onSuccess, onAfterLeave, ...props } = allProps;
1303
+ const renderChildren = (contentProps) => {
1304
+ if (typeof children === "function") return children(contentProps);
1305
+ return children;
1306
+ };
1307
+ const headlessModalRef = (0, react.useRef)(null);
1308
+ const cleanupScrollLockRef = (0, react.useRef)(null);
1309
+ const cleanupAriaHiddenRef = (0, react.useRef)(null);
1310
+ const [rendered, setRendered] = (0, react.useState)(false);
1311
+ const useNativeDialog = (0, react.useMemo)(() => getConfig("useNativeDialog"), []);
1312
+ (0, react.useImperativeHandle)(ref, () => headlessModalRef.current, [headlessModalRef]);
1313
+ (0, react.useEffect)(() => {
1314
+ return () => {
1315
+ cleanupScrollLockRef.current?.();
1316
+ cleanupAriaHiddenRef.current?.();
1317
+ };
1318
+ }, []);
1319
+ const handleSuccess = (0, react.useCallback)(() => {
1320
+ onSuccess?.();
1321
+ if (!cleanupScrollLockRef.current) {
1322
+ cleanupScrollLockRef.current = (0, _inertiaui_vanilla.lockScroll)();
1323
+ cleanupAriaHiddenRef.current = (0, _inertiaui_vanilla.markAriaHidden)(getConfig("appElement"));
1324
+ }
1325
+ }, [onSuccess]);
1326
+ const handleClose = (0, react.useCallback)(() => {
1327
+ onClose?.();
1328
+ cleanupScrollLockRef.current?.();
1329
+ cleanupAriaHiddenRef.current?.();
1330
+ cleanupScrollLockRef.current = null;
1331
+ cleanupAriaHiddenRef.current = null;
1332
+ }, [onClose]);
1333
+ const handleAfterLeave = (0, react.useCallback)(() => {
1334
+ onAfterLeave?.();
1335
+ }, [onAfterLeave]);
1336
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(HeadlessModal, {
1337
+ ref: headlessModalRef,
1338
+ name,
1339
+ onFocus: onFocus ?? void 0,
1340
+ onBlur: onBlur ?? void 0,
1341
+ onClose: handleClose,
1342
+ onSuccess: handleSuccess,
1343
+ ...props,
1344
+ children: ({ afterLeave, close, config, emit, getChildModal, getParentModal, id, index, isOpen, modalContext, onTopOfStack, reload, setOpen, shouldRender, ...extraProps }) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ModalPortal, { children: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
1345
+ className: "im-dialog relative z-20",
1346
+ "data-inertiaui-modal-id": id,
1347
+ "data-inertiaui-modal-index": index,
1348
+ "aria-hidden": !onTopOfStack,
1349
+ children: [index === 0 && !useNativeDialog && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(BackdropTransition, {
1350
+ show: isOpen,
1351
+ appear: !rendered,
1352
+ onAfterAppear: () => setRendered(true)
1353
+ }), config.slideover ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)(SlideoverContent, {
1354
+ modalContext,
1355
+ config,
1356
+ useNativeDialog,
1357
+ isFirstModal: index === 0,
1358
+ onAfterLeave: handleAfterLeave,
1359
+ children: renderChildren({
1360
+ ...extraProps,
1361
+ afterLeave,
1362
+ close,
1363
+ config,
1364
+ emit,
1365
+ getChildModal,
1366
+ getParentModal,
1367
+ id,
1368
+ index,
1369
+ isOpen,
1370
+ modalContext,
1371
+ onTopOfStack,
1372
+ reload,
1373
+ setOpen,
1374
+ shouldRender
1375
+ })
1376
+ }) : /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ModalContent, {
1377
+ modalContext,
1378
+ config,
1379
+ useNativeDialog,
1380
+ isFirstModal: index === 0,
1381
+ onAfterLeave: handleAfterLeave,
1382
+ children: renderChildren({
1383
+ ...extraProps,
1384
+ afterLeave,
1385
+ close,
1386
+ config,
1387
+ emit,
1388
+ getChildModal,
1389
+ getParentModal,
1390
+ id,
1391
+ index,
1392
+ isOpen,
1393
+ modalContext,
1394
+ onTopOfStack,
1395
+ reload,
1396
+ setOpen,
1397
+ shouldRender
1398
+ })
1399
+ })]
1400
+ }) })
1401
+ });
1402
+ });
1403
+ function ModalPortal({ children }) {
1404
+ const [mounted, setMounted] = (0, react.useState)(false);
1405
+ (0, react.useEffect)(() => {
1406
+ setMounted(true);
1407
+ }, []);
1408
+ if (!mounted) return null;
1409
+ return (0, react_dom.createPortal)(children, document.body);
1410
+ }
1411
+ function BackdropTransition({ show, appear, onAfterAppear }) {
1412
+ const [state, setState] = (0, react.useState)(() => {
1413
+ if (appear && show) return "entering";
1414
+ return show ? "entered" : "exited";
1415
+ });
1416
+ const initialRender = (0, react.useRef)(true);
1417
+ const backdropRef = (0, react.useRef)(null);
1418
+ (0, react.useEffect)(() => {
1419
+ if (initialRender.current) {
1420
+ initialRender.current = false;
1421
+ if (appear && show) requestAnimationFrame(() => {
1422
+ setState("entered");
1423
+ const backdrop = backdropRef.current;
1424
+ if (backdrop) {
1425
+ const onTransitionEnd = (e) => {
1426
+ if (e.target !== backdrop) return;
1427
+ backdrop.removeEventListener("transitionend", onTransitionEnd);
1428
+ onAfterAppear?.();
1429
+ };
1430
+ backdrop.addEventListener("transitionend", onTransitionEnd);
1431
+ }
1432
+ });
1433
+ return;
1434
+ }
1435
+ if (show) {
1436
+ setState("entering");
1437
+ requestAnimationFrame(() => {
1438
+ setState("entered");
1439
+ });
1440
+ } else {
1441
+ setState("leaving");
1442
+ const backdrop = backdropRef.current;
1443
+ if (backdrop) {
1444
+ const onTransitionEnd = (e) => {
1445
+ if (e.target !== backdrop) return;
1446
+ backdrop.removeEventListener("transitionend", onTransitionEnd);
1447
+ setState("exited");
1448
+ };
1449
+ backdrop.addEventListener("transitionend", onTransitionEnd);
1450
+ }
1451
+ }
1452
+ }, [
1453
+ show,
1454
+ appear,
1455
+ onAfterAppear
1456
+ ]);
1457
+ if (state === "exited") return null;
1458
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
1459
+ ref: backdropRef,
1460
+ className: `im-backdrop fixed inset-0 z-30 bg-black/75 transition-opacity duration-300 ease-in-out ${state === "entered" ? "opacity-100" : "opacity-0"}`,
1461
+ "aria-hidden": "true"
1462
+ });
1463
+ }
1464
+ Modal.displayName = "Modal";
1465
+ //#endregion
1466
+ //#region src/ModalLink.tsx
1467
+ var ModalLink = ({ href, method = "get", data = {}, as: Component = "a", headers = {}, queryStringArrayFormat = "brackets", onAfterLeave, onBlur, onClose, onError, onFocus, onStart, onSuccess, onPrefetching, onPrefetched, navigate, prefetch: prefetch$1 = false, cacheFor = 3e4, children, ...props }) => {
1468
+ const [loading, setLoading] = (0, react.useState)(false);
1469
+ const [modalContext, setModalContext] = (0, react.useState)(null);
1470
+ const { stack, visit } = useModalStack();
1471
+ const hoverTimeout = (0, react.useRef)(null);
1472
+ const shouldNavigate = (0, react.useMemo)(() => {
1473
+ return navigate ?? getConfig("navigate");
1474
+ }, [navigate]);
1475
+ const prefetchModes = (0, react.useMemo)(() => {
1476
+ if (prefetch$1 === true) return ["hover"];
1477
+ if (prefetch$1 === false) return [];
1478
+ if (Array.isArray(prefetch$1)) return prefetch$1;
1479
+ return [prefetch$1];
1480
+ }, [prefetch$1]);
1481
+ const doPrefetch = (0, react.useCallback)(() => {
1482
+ prefetch(href, {
1483
+ method,
1484
+ data,
1485
+ headers,
1486
+ queryStringArrayFormat,
1487
+ cacheFor,
1488
+ onPrefetching: onPrefetching ?? void 0,
1489
+ onPrefetched: onPrefetched ?? void 0
1490
+ });
1491
+ }, [
1492
+ href,
1493
+ method,
1494
+ data,
1495
+ headers,
1496
+ queryStringArrayFormat,
1497
+ cacheFor,
1498
+ onPrefetching,
1499
+ onPrefetched
1500
+ ]);
1501
+ const handleMouseEnter = (0, react.useCallback)(() => {
1502
+ if (!prefetchModes.includes("hover")) return;
1503
+ hoverTimeout.current = setTimeout(() => {
1504
+ doPrefetch();
1505
+ }, 75);
1506
+ }, [prefetchModes, doPrefetch]);
1507
+ const handleMouseLeave = (0, react.useCallback)(() => {
1508
+ if (hoverTimeout.current) {
1509
+ clearTimeout(hoverTimeout.current);
1510
+ hoverTimeout.current = null;
1511
+ }
1512
+ }, []);
1513
+ const handleMouseDown = (0, react.useCallback)((event) => {
1514
+ if (!prefetchModes.includes("click")) return;
1515
+ if (event.button !== 0) return;
1516
+ doPrefetch();
1517
+ }, [prefetchModes, doPrefetch]);
1518
+ (0, react.useEffect)(() => {
1519
+ if (prefetchModes.includes("mount")) doPrefetch();
1520
+ }, []);
1521
+ (0, react.useEffect)(() => {
1522
+ return () => {
1523
+ if (hoverTimeout.current) clearTimeout(hoverTimeout.current);
1524
+ };
1525
+ }, []);
1526
+ const standardProps = {};
1527
+ const customEvents = {};
1528
+ Object.keys(props).forEach((key) => {
1529
+ if (modalPropNames.includes(key)) return;
1530
+ if (key.startsWith("on") && typeof props[key] === "function") if ((0, _inertiaui_vanilla.isStandardDomEvent)(key)) standardProps[key] = props[key];
1531
+ else customEvents[key] = props[key];
1532
+ else standardProps[key] = props[key];
1533
+ });
1534
+ const [isBlurred, setIsBlurred] = (0, react.useState)(false);
1535
+ (0, react.useEffect)(() => {
1536
+ if (!modalContext) return;
1537
+ if (modalContext.onTopOfStack && isBlurred) onFocus?.();
1538
+ else if (!modalContext.onTopOfStack && !isBlurred) onBlur?.();
1539
+ setIsBlurred(!modalContext.onTopOfStack);
1540
+ }, [stack]);
1541
+ const onCloseCallback = (0, react.useCallback)(() => {
1542
+ onClose?.();
1543
+ }, [onClose]);
1544
+ const onAfterLeaveCallback = (0, react.useCallback)(() => {
1545
+ setModalContext(null);
1546
+ onAfterLeave?.();
1547
+ }, [onAfterLeave]);
1548
+ const handle = (0, react.useCallback)((e) => {
1549
+ e?.preventDefault();
1550
+ if (loading) return;
1551
+ if (!href.startsWith("#")) {
1552
+ setLoading(true);
1553
+ onStart?.();
1554
+ }
1555
+ visit(href, method, data, headers, (0, _inertiaui_vanilla.rejectNullValues)((0, _inertiaui_vanilla.only)(props, modalPropNames)), () => onCloseCallback(), onAfterLeaveCallback, queryStringArrayFormat, shouldNavigate).then((newModalContext) => {
1556
+ setModalContext(newModalContext);
1557
+ newModalContext.registerEventListenersFromProps(customEvents);
1558
+ onSuccess?.();
1559
+ }).catch((error) => {
1560
+ console.error(error);
1561
+ onError?.(error);
1562
+ }).finally(() => setLoading(false));
1563
+ }, [
1564
+ href,
1565
+ method,
1566
+ data,
1567
+ headers,
1568
+ queryStringArrayFormat,
1569
+ props,
1570
+ onCloseCallback,
1571
+ onAfterLeaveCallback
1572
+ ]);
1573
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Component, {
1574
+ ...standardProps,
1575
+ href,
1576
+ onClick: handle,
1577
+ onMouseEnter: handleMouseEnter,
1578
+ onMouseLeave: handleMouseLeave,
1579
+ onMouseDown: handleMouseDown,
1580
+ children: typeof children === "function" ? children({ loading }) : children
1581
+ });
1582
+ };
1583
+ //#endregion
1584
+ //#region src/WhenVisible.tsx
1585
+ var WhenVisible = ({ children, data, params, buffer, as, always, fallback }) => {
1586
+ always = always ?? false;
1587
+ as = as ?? "div";
1588
+ fallback = fallback ?? null;
1589
+ const [loaded, setLoaded] = (0, react.useState)(false);
1590
+ const hasFetched = (0, react.useRef)(false);
1591
+ const fetching = (0, react.useRef)(false);
1592
+ const ref = (0, react.useRef)(null);
1593
+ const modal = useModal();
1594
+ const getReloadParams = (0, react.useCallback)(() => {
1595
+ if (data) return { only: Array.isArray(data) ? data : [data] };
1596
+ if (!params) throw new Error("You must provide either a `data` or `params` prop.");
1597
+ return params;
1598
+ }, [params, data]);
1599
+ (0, react.useEffect)(() => {
1600
+ if (!ref.current) return;
1601
+ const observer = new IntersectionObserver((entries) => {
1602
+ if (!entries[0].isIntersecting) return;
1603
+ if (!always && hasFetched.current) observer.disconnect();
1604
+ if (fetching.current) return;
1605
+ hasFetched.current = true;
1606
+ fetching.current = true;
1607
+ const reloadParams = getReloadParams();
1608
+ modal?.reload({
1609
+ ...reloadParams,
1610
+ onStart: () => {
1611
+ fetching.current = true;
1612
+ reloadParams.onStart?.();
1613
+ },
1614
+ onFinish: () => {
1615
+ setLoaded(true);
1616
+ fetching.current = false;
1617
+ reloadParams.onFinish?.();
1618
+ if (!always) observer.disconnect();
1619
+ }
1620
+ });
1621
+ }, { rootMargin: `${buffer || 0}px` });
1622
+ observer.observe(ref.current);
1623
+ return () => {
1624
+ observer.disconnect();
1625
+ };
1626
+ }, [
1627
+ ref,
1628
+ getReloadParams,
1629
+ buffer
1630
+ ]);
1631
+ if (always || !loaded) return (0, react.createElement)(as, {
1632
+ props: null,
1633
+ ref
1634
+ }, loaded ? children : fallback);
1635
+ return loaded ? children : null;
1636
+ };
1637
+ WhenVisible.displayName = "InertiaWhenVisible";
1638
+ //#endregion
1639
+ //#region src/inertiauiModal.ts
1640
+ var setPageLayout = (layout) => (module) => {
1641
+ module.default.layout = (page) => (0, react.createElement)(layout, { children: page });
1642
+ return module;
1643
+ };
1644
+ //#endregion
1645
+ exports.Deferred = Deferred;
1646
+ exports.HeadlessModal = HeadlessModal;
1647
+ exports.Modal = Modal;
1648
+ exports.ModalLink = ModalLink;
1649
+ exports.ModalRoot = ModalRoot;
1650
+ exports.ModalStackProvider = ModalStackProvider;
1651
+ exports.WhenVisible = WhenVisible;
1652
+ Object.defineProperty(exports, "dialogUtils", {
1653
+ enumerable: true,
1654
+ get: function() {
1655
+ return _inertiaui_vanilla;
1656
+ }
1657
+ });
1658
+ exports.getConfig = getConfig;
1659
+ exports.initFromPageProps = initFromPageProps;
1660
+ exports.modalPropNames = modalPropNames;
1661
+ exports.prefetch = prefetch;
1662
+ exports.putConfig = putConfig;
1663
+ exports.renderApp = renderApp;
1664
+ exports.resetConfig = resetConfig;
1665
+ exports.setPageLayout = setPageLayout;
1666
+ exports.useModal = useModal;
1667
+ exports.useModalIndex = useModalIndex;
1668
+ exports.useModalStack = useModalStack;
1669
+ });
1670
+
1671
+ //# sourceMappingURL=inertiaui-modal.umd.cjs.map