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