@inertiaui/modal-react 1.0.0-beta-5 → 2.0.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/CloseButton.d.ts +5 -0
- package/dist/Deferred.d.ts +11 -0
- package/dist/HeadlessModal.d.ts +64 -0
- package/dist/Modal.d.ts +48 -0
- package/dist/ModalContent.d.ts +24 -0
- package/dist/ModalLink.d.ts +29 -0
- package/dist/ModalRenderer.d.ts +4 -0
- package/dist/ModalRoot.d.ts +22 -0
- package/dist/SlideoverContent.d.ts +24 -0
- package/dist/WhenVisible.d.ts +16 -0
- package/dist/config.d.ts +21 -0
- package/dist/constants.d.ts +7 -0
- package/dist/helpers.d.ts +3 -0
- package/dist/inertiaui-modal.d.ts +2 -0
- package/dist/inertiaui-modal.js +1680 -2167
- package/dist/inertiaui-modal.js.map +1 -0
- package/dist/inertiaui-modal.umd.cjs +1854 -21
- package/dist/inertiaui-modal.umd.cjs.map +1 -0
- package/dist/inertiauiModal.d.ts +21 -0
- package/dist/types.d.ts +111 -0
- package/dist/useModal.d.ts +2 -0
- package/package.json +37 -22
- package/src/{CloseButton.jsx → CloseButton.tsx} +5 -1
- package/src/{Deferred.jsx → Deferred.tsx} +10 -3
- package/src/HeadlessModal.tsx +238 -0
- package/src/Modal.tsx +292 -0
- package/src/ModalContent.tsx +311 -0
- package/src/ModalLink.tsx +224 -0
- package/src/ModalRenderer.tsx +33 -0
- package/src/ModalRoot.tsx +880 -0
- package/src/SlideoverContent.tsx +319 -0
- package/src/{WhenVisible.jsx → WhenVisible.tsx} +20 -9
- package/src/config.ts +99 -0
- package/src/constants.ts +22 -0
- package/src/helpers.ts +17 -0
- package/src/inertiauiModal.ts +65 -0
- package/src/types.ts +150 -0
- package/src/useModal.ts +7 -0
- package/src/HeadlessModal.jsx +0 -143
- package/src/Modal.jsx +0 -136
- package/src/ModalContent.jsx +0 -56
- package/src/ModalLink.jsx +0 -123
- package/src/ModalRenderer.jsx +0 -34
- package/src/ModalRoot.jsx +0 -623
- package/src/SlideoverContent.jsx +0 -55
- package/src/config.js +0 -3
- package/src/helpers.js +0 -2
- package/src/inertiauiModal.js +0 -34
- package/src/useModal.js +0 -6
package/src/types.ts
ADDED
|
@@ -0,0 +1,150 @@
|
|
|
1
|
+
import type { AxiosResponse } from 'axios'
|
|
2
|
+
import type { ComponentType, ReactNode } from 'react'
|
|
3
|
+
import type { RequestPayload } from '@inertiajs/core'
|
|
4
|
+
|
|
5
|
+
export interface ModalResponseData {
|
|
6
|
+
id?: string
|
|
7
|
+
component: string
|
|
8
|
+
props: Record<string, unknown>
|
|
9
|
+
url?: string
|
|
10
|
+
version?: string
|
|
11
|
+
meta?: {
|
|
12
|
+
deferredProps?: Record<string, string[]>
|
|
13
|
+
}
|
|
14
|
+
baseUrl?: string
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export interface ModalConfig {
|
|
18
|
+
[key: string]: unknown
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
export interface ReloadOptions {
|
|
22
|
+
only?: string[]
|
|
23
|
+
except?: string[]
|
|
24
|
+
method?: string
|
|
25
|
+
data?: Record<string, unknown>
|
|
26
|
+
headers?: Record<string, string>
|
|
27
|
+
onStart?: () => void
|
|
28
|
+
onSuccess?: (response: AxiosResponse) => void
|
|
29
|
+
onError?: (error: unknown) => void
|
|
30
|
+
onFinish?: () => void
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
export interface VisitOptions {
|
|
34
|
+
method?: string
|
|
35
|
+
data?: RequestPayload
|
|
36
|
+
headers?: Record<string, string>
|
|
37
|
+
config?: ModalConfig
|
|
38
|
+
onClose?: () => void
|
|
39
|
+
onAfterLeave?: () => void
|
|
40
|
+
queryStringArrayFormat?: 'brackets' | 'indices'
|
|
41
|
+
navigate?: boolean
|
|
42
|
+
onStart?: () => void
|
|
43
|
+
onSuccess?: (response?: AxiosResponse) => void
|
|
44
|
+
onError?: (...args: unknown[]) => void
|
|
45
|
+
listeners?: Record<string, (...args: unknown[]) => void>
|
|
46
|
+
// Props to pass to local modals (#152)
|
|
47
|
+
props?: Record<string, unknown>
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
// Prefetch types (#146)
|
|
51
|
+
export type PrefetchOption = boolean | 'hover' | 'click' | 'mount' | Array<'hover' | 'click' | 'mount'>
|
|
52
|
+
|
|
53
|
+
export interface PrefetchOptions {
|
|
54
|
+
method?: string
|
|
55
|
+
data?: RequestPayload
|
|
56
|
+
headers?: Record<string, string>
|
|
57
|
+
queryStringArrayFormat?: 'brackets' | 'indices'
|
|
58
|
+
cacheFor?: number
|
|
59
|
+
onPrefetching?: () => void
|
|
60
|
+
onPrefetched?: () => void
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
export type EventCallback = (...args: unknown[]) => void
|
|
64
|
+
export type ComponentResolver = (name: string) => Promise<ComponentType>
|
|
65
|
+
|
|
66
|
+
export interface Modal {
|
|
67
|
+
id: string
|
|
68
|
+
isOpen: boolean
|
|
69
|
+
shouldRender: boolean
|
|
70
|
+
listeners: Record<string, EventCallback[]>
|
|
71
|
+
component: ComponentType | null
|
|
72
|
+
props: Record<string, unknown>
|
|
73
|
+
response: ModalResponseData
|
|
74
|
+
config: ModalConfig
|
|
75
|
+
onCloseCallback: (() => void) | null
|
|
76
|
+
afterLeaveCallback: (() => void) | null
|
|
77
|
+
index: number
|
|
78
|
+
onTopOfStack: boolean
|
|
79
|
+
name?: string
|
|
80
|
+
show: () => void
|
|
81
|
+
close: () => void
|
|
82
|
+
setOpen: (open: boolean) => void
|
|
83
|
+
afterLeave: () => void
|
|
84
|
+
on: (event: string, callback: EventCallback) => void
|
|
85
|
+
off: (event: string, callback?: EventCallback) => void
|
|
86
|
+
emit: (event: string, ...args: unknown[]) => void
|
|
87
|
+
registerEventListenersFromProps: (props: Record<string, unknown>) => () => void
|
|
88
|
+
reload: (options?: ReloadOptions) => void
|
|
89
|
+
updateProps: (props: Record<string, unknown>) => void
|
|
90
|
+
getParentModal: () => Modal | null
|
|
91
|
+
getChildModal: () => Modal | null
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
export interface LocalModal {
|
|
95
|
+
name: string
|
|
96
|
+
callback: (modal: Modal) => void
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
export interface ModalStackContextValue {
|
|
100
|
+
stack: Modal[]
|
|
101
|
+
localModals: Record<string, LocalModal>
|
|
102
|
+
push: (
|
|
103
|
+
component: ComponentType | null,
|
|
104
|
+
response: ModalResponseData,
|
|
105
|
+
config?: ModalConfig | null,
|
|
106
|
+
onClose?: (() => void) | null,
|
|
107
|
+
afterLeave?: (() => void) | null,
|
|
108
|
+
) => Modal
|
|
109
|
+
pushFromResponseData: (
|
|
110
|
+
responseData: ModalResponseData,
|
|
111
|
+
config?: ModalConfig,
|
|
112
|
+
onClose?: (() => void) | null,
|
|
113
|
+
onAfterLeave?: (() => void) | null,
|
|
114
|
+
) => Promise<Modal>
|
|
115
|
+
length: () => number
|
|
116
|
+
closeAll: (force?: boolean) => void
|
|
117
|
+
reset: () => void
|
|
118
|
+
visit: (
|
|
119
|
+
href: string,
|
|
120
|
+
method: string,
|
|
121
|
+
payload?: RequestPayload,
|
|
122
|
+
headers?: Record<string, string>,
|
|
123
|
+
config?: ModalConfig,
|
|
124
|
+
onClose?: (() => void) | null,
|
|
125
|
+
onAfterLeave?: (() => void) | null,
|
|
126
|
+
queryStringArrayFormat?: 'brackets' | 'indices',
|
|
127
|
+
useBrowserHistory?: boolean,
|
|
128
|
+
onStart?: (() => void) | null,
|
|
129
|
+
onSuccess?: ((response?: AxiosResponse) => void) | null,
|
|
130
|
+
onError?: ((...args: unknown[]) => void) | null,
|
|
131
|
+
) => Promise<Modal>
|
|
132
|
+
visitModal: (url: string, options?: VisitOptions) => Promise<Modal>
|
|
133
|
+
registerLocalModal: (name: string, callback: (modal: Modal) => void) => void
|
|
134
|
+
removeLocalModal: (name: string) => void
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
export interface PageProps {
|
|
138
|
+
initialPage?: {
|
|
139
|
+
version?: string
|
|
140
|
+
}
|
|
141
|
+
resolveComponent?: ComponentResolver
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
export interface ModalRootProps {
|
|
145
|
+
children?: ReactNode
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
export interface ModalRendererProps {
|
|
149
|
+
index: number
|
|
150
|
+
}
|
package/src/useModal.ts
ADDED
package/src/HeadlessModal.jsx
DELETED
|
@@ -1,143 +0,0 @@
|
|
|
1
|
-
import { useMemo, useState, forwardRef, useImperativeHandle, useEffect, useRef } from 'react'
|
|
2
|
-
import { getConfig, getConfigByType } from './config'
|
|
3
|
-
import { useModalIndex } from './ModalRenderer.jsx'
|
|
4
|
-
import { useModalStack } from './ModalRoot.jsx'
|
|
5
|
-
import ModalRenderer from './ModalRenderer'
|
|
6
|
-
|
|
7
|
-
const HeadlessModal = forwardRef(({ name, children, onFocus = null, onBlur = null, onClose = null, onSuccess = null, ...props }, ref) => {
|
|
8
|
-
const modalIndex = useModalIndex()
|
|
9
|
-
const { stack, registerLocalModal, removeLocalModal } = useModalStack()
|
|
10
|
-
|
|
11
|
-
const [localModalContext, setLocalModalContext] = useState(null)
|
|
12
|
-
const modalContext = useMemo(() => (name ? localModalContext : stack[modalIndex]), [name, localModalContext, modalIndex, stack])
|
|
13
|
-
|
|
14
|
-
const nextIndex = useMemo(() => {
|
|
15
|
-
return stack.find((m) => m.shouldRender && m.index > modalContext?.index)?.index
|
|
16
|
-
}, [modalIndex, stack])
|
|
17
|
-
|
|
18
|
-
const configSlideover = useMemo(() => modalContext?.config.slideover ?? props.slideover ?? getConfig('type') === 'slideover', [props.slideover])
|
|
19
|
-
|
|
20
|
-
const config = useMemo(
|
|
21
|
-
() => ({
|
|
22
|
-
slideover: configSlideover,
|
|
23
|
-
closeButton: props.closeButton ?? getConfigByType(configSlideover, 'closeButton'),
|
|
24
|
-
closeExplicitly: props.closeExplicitly ?? getConfigByType(configSlideover, 'closeExplicitly'),
|
|
25
|
-
maxWidth: props.maxWidth ?? getConfigByType(configSlideover, 'maxWidth'),
|
|
26
|
-
paddingClasses: props.paddingClasses ?? getConfigByType(configSlideover, 'paddingClasses'),
|
|
27
|
-
panelClasses: props.panelClasses ?? getConfigByType(configSlideover, 'panelClasses'),
|
|
28
|
-
position: props.position ?? getConfigByType(configSlideover, 'position'),
|
|
29
|
-
...modalContext?.config,
|
|
30
|
-
}),
|
|
31
|
-
[props, modalContext?.config],
|
|
32
|
-
)
|
|
33
|
-
|
|
34
|
-
useEffect(() => {
|
|
35
|
-
if (name) {
|
|
36
|
-
let removeListeners = null
|
|
37
|
-
|
|
38
|
-
registerLocalModal(name, (localContext) => {
|
|
39
|
-
removeListeners = localContext.registerEventListenersFromProps(props)
|
|
40
|
-
setLocalModalContext(localContext)
|
|
41
|
-
})
|
|
42
|
-
|
|
43
|
-
return () => {
|
|
44
|
-
removeListeners?.()
|
|
45
|
-
removeListeners = null
|
|
46
|
-
removeLocalModal(name)
|
|
47
|
-
}
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
return modalContext.registerEventListenersFromProps(props)
|
|
51
|
-
}, [name])
|
|
52
|
-
|
|
53
|
-
// Store the latest modalContext in a ref to maintain reference
|
|
54
|
-
const modalContextRef = useRef(modalContext)
|
|
55
|
-
|
|
56
|
-
// Update the ref whenever modalContext changes
|
|
57
|
-
useEffect(() => {
|
|
58
|
-
modalContextRef.current = modalContext
|
|
59
|
-
}, [modalContext])
|
|
60
|
-
|
|
61
|
-
useEffect(() => {
|
|
62
|
-
if (modalContext !== null) {
|
|
63
|
-
modalContext.isOpen ? onSuccess?.() : onClose?.()
|
|
64
|
-
}
|
|
65
|
-
}, [modalContext?.isOpen])
|
|
66
|
-
|
|
67
|
-
const [rendered, setRendered] = useState(false)
|
|
68
|
-
|
|
69
|
-
useEffect(() => {
|
|
70
|
-
if (rendered && modalContext !== null && modalContext.isOpen) {
|
|
71
|
-
modalContext.onTopOfStack ? onFocus?.() : onBlur?.()
|
|
72
|
-
}
|
|
73
|
-
|
|
74
|
-
setRendered(true)
|
|
75
|
-
}, [modalContext?.onTopOfStack])
|
|
76
|
-
|
|
77
|
-
useImperativeHandle(
|
|
78
|
-
ref,
|
|
79
|
-
() => ({
|
|
80
|
-
afterLeave: () => modalContextRef.current?.afterLeave(),
|
|
81
|
-
close: () => modalContextRef.current?.close(),
|
|
82
|
-
emit: (...args) => modalContextRef.current?.emit(...args),
|
|
83
|
-
getChildModal: () => modalContextRef.current?.getChildModal(),
|
|
84
|
-
getParentModal: () => modalContextRef.current?.getParentModal(),
|
|
85
|
-
reload: (...args) => modalContextRef.current?.reload(...args),
|
|
86
|
-
setOpen: () => modalContextRef.current?.setOpen(),
|
|
87
|
-
|
|
88
|
-
get id() {
|
|
89
|
-
return modalContextRef.current?.id
|
|
90
|
-
},
|
|
91
|
-
get index() {
|
|
92
|
-
return modalContextRef.current?.index
|
|
93
|
-
},
|
|
94
|
-
get isOpen() {
|
|
95
|
-
return modalContextRef.current?.isOpen
|
|
96
|
-
},
|
|
97
|
-
get config() {
|
|
98
|
-
return modalContextRef.current?.config
|
|
99
|
-
},
|
|
100
|
-
get modalContext() {
|
|
101
|
-
return modalContextRef.current
|
|
102
|
-
},
|
|
103
|
-
get onTopOfStack() {
|
|
104
|
-
return modalContextRef.current?.onTopOfStack
|
|
105
|
-
},
|
|
106
|
-
get shouldRender() {
|
|
107
|
-
return modalContextRef.current?.shouldRender
|
|
108
|
-
},
|
|
109
|
-
}),
|
|
110
|
-
[modalContext],
|
|
111
|
-
)
|
|
112
|
-
|
|
113
|
-
return (
|
|
114
|
-
modalContext?.shouldRender && (
|
|
115
|
-
<>
|
|
116
|
-
{typeof children === 'function'
|
|
117
|
-
? children({
|
|
118
|
-
afterLeave: modalContext.afterLeave,
|
|
119
|
-
close: modalContext.close,
|
|
120
|
-
config,
|
|
121
|
-
emit: modalContext.emit,
|
|
122
|
-
getChildModal: modalContext.getChildModal,
|
|
123
|
-
getParentModal: modalContext.getParentModal,
|
|
124
|
-
id: modalContext.id,
|
|
125
|
-
index: modalContext.index,
|
|
126
|
-
isOpen: modalContext.isOpen,
|
|
127
|
-
modalContext,
|
|
128
|
-
onTopOfStack: modalContext.onTopOfStack,
|
|
129
|
-
reload: modalContext.reload,
|
|
130
|
-
setOpen: modalContext.setOpen,
|
|
131
|
-
shouldRender: modalContext.shouldRender,
|
|
132
|
-
})
|
|
133
|
-
: children}
|
|
134
|
-
|
|
135
|
-
{/* Next modal in the stack */}
|
|
136
|
-
{nextIndex && <ModalRenderer index={nextIndex} />}
|
|
137
|
-
</>
|
|
138
|
-
)
|
|
139
|
-
)
|
|
140
|
-
})
|
|
141
|
-
|
|
142
|
-
HeadlessModal.displayName = 'HeadlessModal'
|
|
143
|
-
export default HeadlessModal
|
package/src/Modal.jsx
DELETED
|
@@ -1,136 +0,0 @@
|
|
|
1
|
-
import { Dialog, Transition, TransitionChild } from '@headlessui/react'
|
|
2
|
-
import { forwardRef, useRef, useImperativeHandle } from 'react'
|
|
3
|
-
import HeadlessModal from './HeadlessModal'
|
|
4
|
-
import ModalContent from './ModalContent'
|
|
5
|
-
import SlideoverContent from './SlideoverContent'
|
|
6
|
-
|
|
7
|
-
const Modal = forwardRef(({ name, children, onFocus = null, onBlur = null, onClose = null, onSuccess = null, onAfterLeave = null, ...props }, ref) => {
|
|
8
|
-
const renderChildren = (contentProps) => {
|
|
9
|
-
if (typeof children === 'function') {
|
|
10
|
-
return children(contentProps)
|
|
11
|
-
}
|
|
12
|
-
|
|
13
|
-
return children
|
|
14
|
-
}
|
|
15
|
-
|
|
16
|
-
const headlessModalRef = useRef(null)
|
|
17
|
-
|
|
18
|
-
useImperativeHandle(ref, () => headlessModalRef.current, [headlessModalRef])
|
|
19
|
-
|
|
20
|
-
return (
|
|
21
|
-
<HeadlessModal
|
|
22
|
-
ref={headlessModalRef}
|
|
23
|
-
name={name}
|
|
24
|
-
onFocus={onFocus}
|
|
25
|
-
onBlur={onBlur}
|
|
26
|
-
onClose={onClose}
|
|
27
|
-
onSuccess={onSuccess}
|
|
28
|
-
{...props}
|
|
29
|
-
>
|
|
30
|
-
{({
|
|
31
|
-
afterLeave,
|
|
32
|
-
close,
|
|
33
|
-
config,
|
|
34
|
-
emit,
|
|
35
|
-
getChildModal,
|
|
36
|
-
getParentModal,
|
|
37
|
-
id,
|
|
38
|
-
index,
|
|
39
|
-
isOpen,
|
|
40
|
-
modalContext,
|
|
41
|
-
onTopOfStack,
|
|
42
|
-
reload,
|
|
43
|
-
setOpen,
|
|
44
|
-
shouldRender,
|
|
45
|
-
}) => (
|
|
46
|
-
<Transition
|
|
47
|
-
appear={true}
|
|
48
|
-
show={isOpen ?? false}
|
|
49
|
-
afterLeave={onAfterLeave}
|
|
50
|
-
>
|
|
51
|
-
<Dialog
|
|
52
|
-
as="div"
|
|
53
|
-
className="im-dialog relative z-20"
|
|
54
|
-
onClose={() => (config.closeExplicitly ? null : close())}
|
|
55
|
-
data-inertiaui-modal-id={id}
|
|
56
|
-
data-inertiaui-modal-index={index}
|
|
57
|
-
>
|
|
58
|
-
{/* Only transition the backdrop for the first modal in the stack */}
|
|
59
|
-
{index === 0 ? (
|
|
60
|
-
<TransitionChild
|
|
61
|
-
enter="transition transform ease-in-out duration-300"
|
|
62
|
-
enterFrom="opacity-0"
|
|
63
|
-
enterTo="opacity-100"
|
|
64
|
-
leave="transition transform ease-in-out duration-300"
|
|
65
|
-
leaveFrom="opacity-100"
|
|
66
|
-
leaveTo="opacity-0"
|
|
67
|
-
>
|
|
68
|
-
{onTopOfStack ? (
|
|
69
|
-
<div
|
|
70
|
-
className="im-backdrop fixed inset-0 z-30 bg-black/75"
|
|
71
|
-
aria-hidden="true"
|
|
72
|
-
/>
|
|
73
|
-
) : (
|
|
74
|
-
<div />
|
|
75
|
-
)}
|
|
76
|
-
</TransitionChild>
|
|
77
|
-
) : null}
|
|
78
|
-
|
|
79
|
-
{/* On multiple modals, only show a backdrop for the modal that is on top of the stack */}
|
|
80
|
-
{index > 0 && onTopOfStack ? <div className="im-backdrop fixed inset-0 z-30 bg-black/75" /> : null}
|
|
81
|
-
|
|
82
|
-
{/* The modal/slideover content itself */}
|
|
83
|
-
{config.slideover ? (
|
|
84
|
-
<SlideoverContent
|
|
85
|
-
modalContext={modalContext}
|
|
86
|
-
config={config}
|
|
87
|
-
>
|
|
88
|
-
{renderChildren({
|
|
89
|
-
afterLeave,
|
|
90
|
-
close,
|
|
91
|
-
config,
|
|
92
|
-
emit,
|
|
93
|
-
getChildModal,
|
|
94
|
-
getParentModal,
|
|
95
|
-
id,
|
|
96
|
-
index,
|
|
97
|
-
isOpen,
|
|
98
|
-
modalContext,
|
|
99
|
-
onTopOfStack,
|
|
100
|
-
reload,
|
|
101
|
-
setOpen,
|
|
102
|
-
shouldRender,
|
|
103
|
-
})}
|
|
104
|
-
</SlideoverContent>
|
|
105
|
-
) : (
|
|
106
|
-
<ModalContent
|
|
107
|
-
modalContext={modalContext}
|
|
108
|
-
config={config}
|
|
109
|
-
>
|
|
110
|
-
{renderChildren({
|
|
111
|
-
afterLeave,
|
|
112
|
-
close,
|
|
113
|
-
config,
|
|
114
|
-
emit,
|
|
115
|
-
getChildModal,
|
|
116
|
-
getParentModal,
|
|
117
|
-
id,
|
|
118
|
-
index,
|
|
119
|
-
isOpen,
|
|
120
|
-
modalContext,
|
|
121
|
-
onTopOfStack,
|
|
122
|
-
reload,
|
|
123
|
-
setOpen,
|
|
124
|
-
shouldRender,
|
|
125
|
-
})}
|
|
126
|
-
</ModalContent>
|
|
127
|
-
)}
|
|
128
|
-
</Dialog>
|
|
129
|
-
</Transition>
|
|
130
|
-
)}
|
|
131
|
-
</HeadlessModal>
|
|
132
|
-
)
|
|
133
|
-
})
|
|
134
|
-
|
|
135
|
-
Modal.displayName = 'Modal'
|
|
136
|
-
export default Modal
|
package/src/ModalContent.jsx
DELETED
|
@@ -1,56 +0,0 @@
|
|
|
1
|
-
import { TransitionChild, DialogPanel } from '@headlessui/react'
|
|
2
|
-
import CloseButton from './CloseButton'
|
|
3
|
-
import clsx from 'clsx'
|
|
4
|
-
import { useState } from 'react'
|
|
5
|
-
|
|
6
|
-
const ModalContent = ({ modalContext, config, children }) => {
|
|
7
|
-
const [entered, setEntered] = useState(false)
|
|
8
|
-
|
|
9
|
-
return (
|
|
10
|
-
<div className="im-modal-container fixed inset-0 z-40 overflow-y-auto p-4">
|
|
11
|
-
<div
|
|
12
|
-
className={clsx('im-modal-positioner flex min-h-full justify-center', {
|
|
13
|
-
'items-start': config.position === 'top',
|
|
14
|
-
'items-center': config.position === 'center',
|
|
15
|
-
'items-end': config.position === 'bottom',
|
|
16
|
-
})}
|
|
17
|
-
>
|
|
18
|
-
<TransitionChild
|
|
19
|
-
as="div"
|
|
20
|
-
enterFrom="opacity-0 translate-y-4 sm:translate-y-0 sm:scale-95"
|
|
21
|
-
enterTo="opacity-100 translate-y-0 sm:scale-100"
|
|
22
|
-
leaveFrom="opacity-100 translate-y-0 sm:scale-100"
|
|
23
|
-
leaveTo="opacity-0 translate-y-4 sm:translate-y-0 sm:scale-95"
|
|
24
|
-
afterEnter={() => setEntered(true)}
|
|
25
|
-
afterLeave={modalContext.afterLeave}
|
|
26
|
-
className={clsx('im-modal-wrapper w-full transition duration-300 ease-in-out', modalContext.onTopOfStack ? '' : 'blur-sm', {
|
|
27
|
-
'sm:max-w-sm': config.maxWidth === 'sm',
|
|
28
|
-
'sm:max-w-md': config.maxWidth === 'md',
|
|
29
|
-
'sm:max-w-md md:max-w-lg': config.maxWidth === 'lg',
|
|
30
|
-
'sm:max-w-md md:max-w-xl': config.maxWidth === 'xl',
|
|
31
|
-
'sm:max-w-md md:max-w-xl lg:max-w-2xl': config.maxWidth === '2xl',
|
|
32
|
-
'sm:max-w-md md:max-w-xl lg:max-w-3xl': config.maxWidth === '3xl',
|
|
33
|
-
'sm:max-w-md md:max-w-xl lg:max-w-3xl xl:max-w-4xl': config.maxWidth === '4xl',
|
|
34
|
-
'sm:max-w-md md:max-w-xl lg:max-w-3xl xl:max-w-5xl': config.maxWidth === '5xl',
|
|
35
|
-
'sm:max-w-md md:max-w-xl lg:max-w-3xl xl:max-w-5xl 2xl:max-w-6xl': config.maxWidth === '6xl',
|
|
36
|
-
'sm:max-w-md md:max-w-xl lg:max-w-3xl xl:max-w-5xl 2xl:max-w-7xl': config.maxWidth === '7xl',
|
|
37
|
-
})}
|
|
38
|
-
>
|
|
39
|
-
<DialogPanel
|
|
40
|
-
className={`im-modal-content relative ${config.paddingClasses} ${config.panelClasses}`}
|
|
41
|
-
data-inertiaui-modal-entered={entered}
|
|
42
|
-
>
|
|
43
|
-
{config.closeButton && (
|
|
44
|
-
<div className="absolute right-0 top-0 pr-3 pt-3">
|
|
45
|
-
<CloseButton onClick={modalContext.close} />
|
|
46
|
-
</div>
|
|
47
|
-
)}
|
|
48
|
-
{typeof children === 'function' ? children({ modalContext, config }) : children}
|
|
49
|
-
</DialogPanel>
|
|
50
|
-
</TransitionChild>
|
|
51
|
-
</div>
|
|
52
|
-
</div>
|
|
53
|
-
)
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
export default ModalContent
|
package/src/ModalLink.jsx
DELETED
|
@@ -1,123 +0,0 @@
|
|
|
1
|
-
import { useCallback, useState, useEffect, useMemo } from 'react'
|
|
2
|
-
import { useModalStack, modalPropNames } from './ModalRoot'
|
|
3
|
-
import { only, rejectNullValues, isStandardDomEvent } from './helpers'
|
|
4
|
-
import { getConfig } from './config'
|
|
5
|
-
|
|
6
|
-
const ModalLink = ({
|
|
7
|
-
href,
|
|
8
|
-
method = 'get',
|
|
9
|
-
data = {},
|
|
10
|
-
as: Component = 'a',
|
|
11
|
-
headers = {},
|
|
12
|
-
queryStringArrayFormat = 'brackets',
|
|
13
|
-
onAfterLeave = null,
|
|
14
|
-
onBlur = null,
|
|
15
|
-
onClose = null,
|
|
16
|
-
onError = null,
|
|
17
|
-
onFocus = null,
|
|
18
|
-
onStart = null,
|
|
19
|
-
onSuccess = null,
|
|
20
|
-
navigate = null,
|
|
21
|
-
children,
|
|
22
|
-
...props
|
|
23
|
-
}) => {
|
|
24
|
-
const [loading, setLoading] = useState(false)
|
|
25
|
-
const [modalContext, setModalContext] = useState(null)
|
|
26
|
-
const { stack, visit } = useModalStack()
|
|
27
|
-
|
|
28
|
-
const shouldNavigate = useMemo(() => {
|
|
29
|
-
return navigate ?? getConfig('navigate')
|
|
30
|
-
}, [navigate])
|
|
31
|
-
|
|
32
|
-
// Separate standard props from custom event handlers
|
|
33
|
-
const standardProps = {}
|
|
34
|
-
const customEvents = {}
|
|
35
|
-
|
|
36
|
-
Object.keys(props).forEach((key) => {
|
|
37
|
-
if (modalPropNames.includes(key)) {
|
|
38
|
-
return
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
if (key.startsWith('on') && typeof props[key] === 'function') {
|
|
42
|
-
if (isStandardDomEvent(key)) {
|
|
43
|
-
standardProps[key] = props[key]
|
|
44
|
-
} else {
|
|
45
|
-
customEvents[key] = props[key]
|
|
46
|
-
}
|
|
47
|
-
} else {
|
|
48
|
-
standardProps[key] = props[key]
|
|
49
|
-
}
|
|
50
|
-
})
|
|
51
|
-
|
|
52
|
-
const [isBlurred, setIsBlurred] = useState(false)
|
|
53
|
-
|
|
54
|
-
useEffect(() => {
|
|
55
|
-
if (!modalContext) {
|
|
56
|
-
return
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
if (modalContext.onTopOfStack && isBlurred) {
|
|
60
|
-
onFocus?.()
|
|
61
|
-
} else if (!modalContext.onTopOfStack && !isBlurred) {
|
|
62
|
-
onBlur?.()
|
|
63
|
-
}
|
|
64
|
-
|
|
65
|
-
setIsBlurred(!modalContext.onTopOfStack)
|
|
66
|
-
}, [stack])
|
|
67
|
-
|
|
68
|
-
const onCloseCallback = useCallback(() => {
|
|
69
|
-
onClose?.()
|
|
70
|
-
}, [onClose])
|
|
71
|
-
|
|
72
|
-
const onAfterLeaveCallback = useCallback(() => {
|
|
73
|
-
setModalContext(null)
|
|
74
|
-
onAfterLeave?.()
|
|
75
|
-
}, [onAfterLeave])
|
|
76
|
-
|
|
77
|
-
const handle = useCallback(
|
|
78
|
-
(e) => {
|
|
79
|
-
e?.preventDefault()
|
|
80
|
-
if (loading) return
|
|
81
|
-
|
|
82
|
-
if (!href.startsWith('#')) {
|
|
83
|
-
setLoading(true)
|
|
84
|
-
onStart?.()
|
|
85
|
-
}
|
|
86
|
-
|
|
87
|
-
visit(
|
|
88
|
-
href,
|
|
89
|
-
method,
|
|
90
|
-
data,
|
|
91
|
-
headers,
|
|
92
|
-
rejectNullValues(only(props, modalPropNames)),
|
|
93
|
-
() => onCloseCallback(stack.length),
|
|
94
|
-
onAfterLeaveCallback,
|
|
95
|
-
queryStringArrayFormat,
|
|
96
|
-
shouldNavigate,
|
|
97
|
-
)
|
|
98
|
-
.then((newModalContext) => {
|
|
99
|
-
setModalContext(newModalContext)
|
|
100
|
-
newModalContext.registerEventListenersFromProps(customEvents)
|
|
101
|
-
onSuccess?.()
|
|
102
|
-
})
|
|
103
|
-
.catch((error) => {
|
|
104
|
-
console.error(error)
|
|
105
|
-
onError?.(error)
|
|
106
|
-
})
|
|
107
|
-
.finally(() => setLoading(false))
|
|
108
|
-
},
|
|
109
|
-
[href, method, data, headers, queryStringArrayFormat, props, onCloseCallback, onAfterLeaveCallback],
|
|
110
|
-
)
|
|
111
|
-
|
|
112
|
-
return (
|
|
113
|
-
<Component
|
|
114
|
-
{...standardProps}
|
|
115
|
-
href={href}
|
|
116
|
-
onClick={handle}
|
|
117
|
-
>
|
|
118
|
-
{typeof children === 'function' ? children({ loading }) : children}
|
|
119
|
-
</Component>
|
|
120
|
-
)
|
|
121
|
-
}
|
|
122
|
-
|
|
123
|
-
export default ModalLink
|
package/src/ModalRenderer.jsx
DELETED
|
@@ -1,34 +0,0 @@
|
|
|
1
|
-
import React, { useMemo } from 'react'
|
|
2
|
-
import { useModalStack } from './ModalRoot'
|
|
3
|
-
|
|
4
|
-
const ModalIndexContext = React.createContext(null)
|
|
5
|
-
ModalIndexContext.displayName = 'ModalIndexContext'
|
|
6
|
-
|
|
7
|
-
export const useModalIndex = () => {
|
|
8
|
-
const context = React.useContext(ModalIndexContext)
|
|
9
|
-
if (context === undefined) {
|
|
10
|
-
throw new Error('useModalIndex must be used within a ModalIndexProvider')
|
|
11
|
-
}
|
|
12
|
-
return context
|
|
13
|
-
}
|
|
14
|
-
|
|
15
|
-
const ModalRenderer = ({ index }) => {
|
|
16
|
-
const { stack } = useModalStack()
|
|
17
|
-
|
|
18
|
-
const modalContext = useMemo(() => {
|
|
19
|
-
return stack[index]
|
|
20
|
-
}, [stack, index])
|
|
21
|
-
|
|
22
|
-
return (
|
|
23
|
-
modalContext?.component && (
|
|
24
|
-
<ModalIndexContext.Provider value={index}>
|
|
25
|
-
<modalContext.component
|
|
26
|
-
{...modalContext.props}
|
|
27
|
-
onModalEvent={(...args) => modalContext.emit(...args)}
|
|
28
|
-
/>
|
|
29
|
-
</ModalIndexContext.Provider>
|
|
30
|
-
)
|
|
31
|
-
)
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
export default ModalRenderer
|