@inertiaui/modal-react 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/src/Modal.jsx ADDED
@@ -0,0 +1,137 @@
1
+ import { Dialog, Transition, TransitionChild } from '@headlessui/react'
2
+ import { useMemo, useState, forwardRef, useImperativeHandle, useEffect } from 'react'
3
+ import { getConfig, getConfigByType } from './config'
4
+ import { useModalIndex } from './ModalRenderer.jsx'
5
+ import { useModalStack } from './ModalRoot.jsx'
6
+ import ModalContent from './ModalContent'
7
+ import ModalRenderer from './ModalRenderer'
8
+ import SlideoverContent from './SlideoverContent'
9
+
10
+ const Modal = forwardRef(({ name, children, ...props }, ref) => {
11
+ const modalIndex = useModalIndex()
12
+ const { stack, registerLocalModal, removeLocalModal } = useModalStack()
13
+
14
+ const [localModalContext, setLocalModalContext] = useState(null)
15
+ const modalContext = useMemo(() => (name ? localModalContext : stack[modalIndex]))
16
+
17
+ const modalPropsSlideover = useMemo(() => modalContext?.modalProps.slideover ?? props.slideover ?? getConfig('type') === 'slideover', [props.slideover])
18
+
19
+ const modalProps = useMemo(
20
+ () => ({
21
+ slideover: modalPropsSlideover,
22
+ closeButton: props.closeButton ?? getConfigByType(modalPropsSlideover, 'closeButton'),
23
+ closeExplicitly: props.closeExplicitly ?? getConfigByType(modalPropsSlideover, 'closeExplicitly'),
24
+ maxWidth: props.maxWidth ?? getConfigByType(modalPropsSlideover, 'maxWidth'),
25
+ paddingClasses: props.paddingClasses ?? getConfigByType(modalPropsSlideover, 'paddingClasses'),
26
+ panelClasses: props.panelClasses ?? getConfigByType(modalPropsSlideover, 'panelClasses'),
27
+ position: props.position ?? getConfigByType(modalPropsSlideover, 'position'),
28
+ ...modalContext?.modalProps,
29
+ }),
30
+ [props, modalContext?.modalProps],
31
+ )
32
+
33
+ const Content = useMemo(() => (modalProps.slideover ? SlideoverContent : ModalContent), [modalProps.slideover])
34
+
35
+ useEffect(() => {
36
+ if (name) {
37
+ let removeListeners = null
38
+
39
+ registerLocalModal(name, (localContext) => {
40
+ removeListeners = localContext.registerEventListenersFromProps(props)
41
+ setLocalModalContext(localContext)
42
+ })
43
+
44
+ return () => {
45
+ removeListeners?.()
46
+ removeListeners = null
47
+ removeLocalModal(name)
48
+ }
49
+ }
50
+
51
+ return modalContext.registerEventListenersFromProps(props)
52
+ }, [name])
53
+
54
+ useImperativeHandle(
55
+ ref,
56
+ () => ({
57
+ close: () => modalContext.close(),
58
+ emit: (...args) => modalContext.emit(...args),
59
+ getChildModal: () => modalContext.getChildModal(),
60
+ getParentModal: () => modalContext.getParentModal(),
61
+ modalContext: modalContext,
62
+ reload: () => modalContext.reload(),
63
+ }),
64
+ [modalContext],
65
+ )
66
+
67
+ const closeDialog = () => {
68
+ if (!modalProps.closeExplicitly) {
69
+ modalContext.close()
70
+ }
71
+ }
72
+
73
+ return (
74
+ modalContext && (
75
+ <Transition
76
+ appear={true}
77
+ show={modalContext.open ?? false}
78
+ >
79
+ <Dialog
80
+ as="div"
81
+ className="im-dialog relative z-20"
82
+ onClose={closeDialog}
83
+ data-inertiaui-modal-id={modalContext.id}
84
+ data-inertiaui-modal-index={modalContext.index}
85
+ >
86
+ {/* Only transition the backdrop for the first modal in the stack */}
87
+ {modalContext.index === 0 ? (
88
+ <TransitionChild
89
+ enter="transition transform ease-in-out duration-300"
90
+ enterFrom="opacity-0"
91
+ enterTo="opacity-100"
92
+ leave="transition transform ease-in-out duration-300"
93
+ leaveFrom="opacity-100"
94
+ leaveTo="opacity-0"
95
+ >
96
+ {modalContext.onTopOfStack ? (
97
+ <div
98
+ className="im-backdrop fixed inset-0 z-30 bg-black/75"
99
+ aria-hidden="true"
100
+ />
101
+ ) : (
102
+ <div />
103
+ )}
104
+ </TransitionChild>
105
+ ) : null}
106
+
107
+ {/* On multiple modals, only show a backdrop for the modal that is on top of the stack */}
108
+ {modalContext.index > 0 && modalContext.onTopOfStack ? <div className="im-backdrop fixed inset-0 z-30 bg-black/75" /> : null}
109
+
110
+ {/* The modal/slideover content itself */}
111
+ <Content
112
+ modalContext={modalContext}
113
+ modalProps={modalProps}
114
+ >
115
+ {typeof children === 'function'
116
+ ? children({
117
+ close: modalContext.close,
118
+ emit: modalContext.emit,
119
+ getChildModal: modalContext.getChildModal,
120
+ getParentModal: modalContext.getParentModal,
121
+ modalContext,
122
+ modalProps,
123
+ reload: modalContext.reload,
124
+ })
125
+ : children}
126
+ </Content>
127
+
128
+ {/* Next modal in the stack */}
129
+ {stack[modalContext.index + 1] && <ModalRenderer index={modalContext.index + 1} />}
130
+ </Dialog>
131
+ </Transition>
132
+ )
133
+ )
134
+ })
135
+
136
+ Modal.displayName = 'Modal'
137
+ export default Modal
@@ -0,0 +1,48 @@
1
+ import { TransitionChild, DialogPanel } from '@headlessui/react'
2
+ import CloseButton from './CloseButton'
3
+ import clsx from 'clsx'
4
+
5
+ const ModalContent = ({ modalContext, modalProps, children }) => {
6
+ return (
7
+ <div className="im-modal-container fixed inset-0 z-40 overflow-y-auto p-4">
8
+ <div
9
+ className={clsx('im-modal-positioner flex min-h-full justify-center', {
10
+ 'items-start': modalProps.position === 'top',
11
+ 'items-center': modalProps.position === 'center',
12
+ 'items-end': modalProps.position === 'bottom',
13
+ })}
14
+ >
15
+ <TransitionChild
16
+ enterFrom="opacity-0 translate-y-4 sm:translate-y-0 sm:scale-95"
17
+ enterTo="opacity-100 translate-y-0 sm:scale-100"
18
+ leaveFrom="opacity-100 translate-y-0 sm:scale-100"
19
+ leaveTo="opacity-0 translate-y-4 sm:translate-y-0 sm:scale-95"
20
+ afterLeave={modalContext.afterLeave}
21
+ className={clsx('im-modal-wrapper w-full transition duration-300 ease-in-out', modalContext.onTopOfStack ? '' : 'blur-sm', {
22
+ 'sm:max-w-sm': modalProps.maxWidth === 'sm',
23
+ 'sm:max-w-md': modalProps.maxWidth === 'md',
24
+ 'sm:max-w-md md:max-w-lg': modalProps.maxWidth === 'lg',
25
+ 'sm:max-w-md md:max-w-xl': modalProps.maxWidth === 'xl',
26
+ 'sm:max-w-md md:max-w-xl lg:max-w-2xl': modalProps.maxWidth === '2xl',
27
+ 'sm:max-w-md md:max-w-xl lg:max-w-3xl': modalProps.maxWidth === '3xl',
28
+ 'sm:max-w-md md:max-w-xl lg:max-w-3xl xl:max-w-4xl': modalProps.maxWidth === '4xl',
29
+ 'sm:max-w-md md:max-w-xl lg:max-w-3xl xl:max-w-5xl': modalProps.maxWidth === '5xl',
30
+ 'sm:max-w-md md:max-w-xl lg:max-w-3xl xl:max-w-5xl 2xl:max-w-6xl': modalProps.maxWidth === '6xl',
31
+ 'sm:max-w-md md:max-w-xl lg:max-w-3xl xl:max-w-5xl 2xl:max-w-7xl': modalProps.maxWidth === '7xl',
32
+ })}
33
+ >
34
+ <DialogPanel className={`im-modal-content relative ${modalProps.paddingClasses} ${modalProps.panelClasses}`}>
35
+ {modalProps.closeButton && (
36
+ <div className="absolute right-0 top-0 pr-3 pt-3">
37
+ <CloseButton onClick={modalContext.close} />
38
+ </div>
39
+ )}
40
+ {typeof children === 'function' ? children({ modalContext, modalProps }) : children}
41
+ </DialogPanel>
42
+ </TransitionChild>
43
+ </div>
44
+ </div>
45
+ )
46
+ }
47
+
48
+ export default ModalContent
@@ -0,0 +1,132 @@
1
+ import { useCallback, useState, useEffect } from 'react'
2
+ import { useModalStack, modalPropNames } from './ModalRoot'
3
+ import { only, rejectNullValues } from './helpers'
4
+
5
+ const ModalLink = ({
6
+ href,
7
+ method = 'get',
8
+ data = {},
9
+ as: Component = 'a',
10
+ fragment = null,
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
+ children,
21
+ ...props
22
+ }) => {
23
+ const [loading, setLoading] = useState(false)
24
+ const [modalContext, setModalContext] = useState(null)
25
+ const { stack, visit } = useModalStack()
26
+
27
+ // Separate standard props from custom event handlers
28
+ const standardProps = {}
29
+ const customEvents = {}
30
+
31
+ Object.keys(props).forEach((key) => {
32
+ if (modalPropNames.includes(key)) {
33
+ return
34
+ }
35
+
36
+ if (key.startsWith('on') && typeof props[key] === 'function') {
37
+ if (key.toLowerCase() in window) {
38
+ standardProps[key] = props[key]
39
+ } else {
40
+ customEvents[key] = props[key]
41
+ }
42
+ } else {
43
+ standardProps[key] = props[key]
44
+ }
45
+ })
46
+
47
+ useEffect(() => {
48
+ if (fragment && window.location.hash === `#${fragment}`) {
49
+ handle()
50
+ }
51
+ }, [fragment])
52
+
53
+ const [isBlurred, setIsBlurred] = useState(false)
54
+
55
+ useEffect(() => {
56
+ if (!modalContext) {
57
+ return
58
+ }
59
+
60
+ if (modalContext.onTopOfStack && isBlurred) {
61
+ onFocus?.()
62
+ } else if (!modalContext.onTopOfStack && !isBlurred) {
63
+ onBlur?.()
64
+ }
65
+
66
+ setIsBlurred(!modalContext.onTopOfStack)
67
+ }, [stack])
68
+
69
+ const onCloseCallback = useCallback(
70
+ (index) => {
71
+ if (fragment && index === 0) {
72
+ window.location.hash = ''
73
+ }
74
+ onClose?.()
75
+ },
76
+ [onClose, fragment],
77
+ )
78
+
79
+ const onAfterLeaveCallback = useCallback(() => {
80
+ setModalContext(null)
81
+ onAfterLeave?.()
82
+ }, [onAfterLeave])
83
+
84
+ const handle = useCallback(
85
+ (e) => {
86
+ e?.preventDefault()
87
+ if (loading) return
88
+
89
+ if (!href.startsWith('#')) {
90
+ setLoading(true)
91
+ onStart?.()
92
+ }
93
+
94
+ visit(
95
+ href,
96
+ method,
97
+ data,
98
+ headers,
99
+ rejectNullValues(only(props, modalPropNames)),
100
+ () => onCloseCallback(stack.length),
101
+ onAfterLeaveCallback,
102
+ queryStringArrayFormat,
103
+ )
104
+ .then((newModalContext) => {
105
+ setModalContext(newModalContext)
106
+ if (fragment && newModalContext.index === 0) {
107
+ window.location.hash = fragment
108
+ }
109
+ newModalContext.registerEventListenersFromProps(customEvents)
110
+ onSuccess?.()
111
+ })
112
+ .catch((error) => {
113
+ console.error(error)
114
+ onError?.(error)
115
+ })
116
+ .finally(() => setLoading(false))
117
+ },
118
+ [href, method, data, headers, queryStringArrayFormat, props, onCloseCallback, onAfterLeaveCallback],
119
+ )
120
+
121
+ return (
122
+ <Component
123
+ {...standardProps}
124
+ href={href}
125
+ onClick={handle}
126
+ >
127
+ {typeof children === 'function' ? children({ loading }) : children}
128
+ </Component>
129
+ )
130
+ }
131
+
132
+ export default ModalLink
@@ -0,0 +1,34 @@
1
+ import React 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 = stack[index]
19
+
20
+ if (!modalContext || !modalContext.component) {
21
+ return null
22
+ }
23
+
24
+ return (
25
+ <ModalIndexContext.Provider value={index}>
26
+ <modalContext.component
27
+ {...modalContext.componentProps}
28
+ onModalEvent={(...args) => modalContext.emit(...args)}
29
+ />
30
+ </ModalIndexContext.Provider>
31
+ )
32
+ }
33
+
34
+ export default ModalRenderer
@@ -0,0 +1,282 @@
1
+ import { useState } from 'react'
2
+ import { default as Axios } from 'axios'
3
+ import { except, only, resolveInteriaPageFromRouter } from './helpers'
4
+ import { router } from '@inertiajs/react'
5
+ import { mergeDataIntoQueryString } from '@inertiajs/core'
6
+ import { createContext, useContext } from 'react'
7
+ import ModalRenderer from './ModalRenderer'
8
+
9
+ const ModalStackContext = createContext(null)
10
+ ModalStackContext.displayName = 'ModalStackContext'
11
+
12
+ export const ModalStackProvider = ({ children }) => {
13
+ const [stack, setStack] = useState([])
14
+ const [localModals, setLocalModals] = useState({})
15
+
16
+ class Modal {
17
+ constructor(component, response, modalProps, onClose, afterLeave) {
18
+ this.id = Modal.generateId()
19
+ this.open = true
20
+ this.listeners = {}
21
+
22
+ this.component = component
23
+ this.componentProps = response.props
24
+ this.response = response
25
+ this.modalProps = modalProps
26
+ this.onCloseCallback = onClose
27
+ this.afterLeaveCallback = afterLeave
28
+
29
+ this.index = -1 // Will be set when added to the stack
30
+ this.getParentModal = () => null // Will be set in push()
31
+ this.getChildModal = () => null // Will be set in push()
32
+ this.onTopOfStack = true // Will be updated in push()
33
+ }
34
+
35
+ static generateId() {
36
+ if (typeof crypto !== 'undefined' && typeof crypto.randomUUID === 'function') {
37
+ return `inertiaui_modal_${crypto.randomUUID()}`
38
+ }
39
+ // Fallback for environments where crypto.randomUUID is not available
40
+ return `inertiaui_modal_${Date.now().toString(36)}_${Math.random().toString(36).substr(2, 9)}`
41
+ }
42
+
43
+ updateStack = (withStack) => {
44
+ setStack((prevStack) => {
45
+ const newStack = withStack([...prevStack])
46
+
47
+ newStack.forEach((modal, index) => {
48
+ newStack[index].index = index
49
+ newStack[index].onTopOfStack = index === newStack.length - 1
50
+ })
51
+
52
+ return newStack
53
+ })
54
+ }
55
+
56
+ close = () => {
57
+ this.updateStack((prevStack) =>
58
+ prevStack.map((modal) => {
59
+ if (modal.id === this.id) {
60
+ Object.keys(modal.listeners).forEach((event) => {
61
+ modal.off(event)
62
+ })
63
+
64
+ modal.open = false
65
+ modal.onCloseCallback?.()
66
+ }
67
+ return modal
68
+ }),
69
+ )
70
+ }
71
+
72
+ afterLeave = () => {
73
+ this.updateStack((prevStack) =>
74
+ prevStack.filter((modal) => {
75
+ if (modal.id !== this.id) {
76
+ return true
77
+ }
78
+
79
+ modal.afterLeaveCallback?.()
80
+ return false
81
+ }),
82
+ )
83
+ }
84
+
85
+ on = (event, callback) => {
86
+ this.listeners[event] = this.listeners[event] ?? []
87
+ this.listeners[event].push(callback)
88
+ }
89
+
90
+ off = (event, callback) => {
91
+ if (callback) {
92
+ this.listeners[event] = this.listeners[event]?.filter((cb) => cb !== callback) ?? []
93
+ } else {
94
+ delete this.listeners[event]
95
+ }
96
+ }
97
+
98
+ emit = (event, ...args) => {
99
+ console.log('Emitting', event, 'with args', args)
100
+ this.listeners[event]?.forEach((callback) => callback(...args))
101
+ return 'OK'
102
+ }
103
+
104
+ registerEventListenersFromProps = (props) => {
105
+ const unsubscribers = []
106
+
107
+ Object.keys(props)
108
+ .filter((key) => key.startsWith('on'))
109
+ .forEach((key) => {
110
+ // e.g. onRefreshKey -> refresh-key
111
+ const snakeCaseKey = key
112
+ .replace(/^on/, '')
113
+ .replace(/^./, (firstLetter) => firstLetter.toLowerCase())
114
+ .replace(/([A-Z])/g, '-$1')
115
+ .toLowerCase()
116
+
117
+ this.on(snakeCaseKey, props[key])
118
+ unsubscribers.push(() => this.off(snakeCaseKey, props[key]))
119
+ })
120
+
121
+ return () => unsubscribers.forEach((unsub) => unsub())
122
+ }
123
+
124
+ reload = (options = {}) => {
125
+ let keys = Object.keys(this.response.props)
126
+
127
+ if (options.only) {
128
+ keys = only(keys, options.only)
129
+ }
130
+
131
+ if (options.except) {
132
+ keys = except(keys, options.except)
133
+ }
134
+
135
+ Axios.get(this.response.url, {
136
+ headers: {
137
+ Accept: 'text/html, application/xhtml+xml',
138
+ 'X-Inertia': true,
139
+ 'X-Inertia-Partial-Component': this.response.component,
140
+ 'X-Inertia-Version': this.response.version,
141
+ 'X-Inertia-Partial-Data': keys.join(','),
142
+ },
143
+ }).then((response) => {
144
+ Object.assign(this.componentProps, response.data.props)
145
+ setStack((prevStack) => [...prevStack]) // Trigger re-render
146
+ })
147
+ }
148
+ }
149
+
150
+ const push = (component, response, modalProps, onClose, afterLeave) => {
151
+ const newModal = new Modal(component, response, modalProps, onClose, afterLeave)
152
+
153
+ setStack((prevStack) => {
154
+ const updatedStack = [...prevStack, newModal]
155
+
156
+ // Set index and update onTopOfStack for all modals
157
+ updatedStack.forEach((modal, index) => {
158
+ modal.index = index
159
+ modal.onTopOfStack = index === updatedStack.length - 1
160
+ })
161
+
162
+ // Set getParentModal and getChildModal
163
+ updatedStack.forEach((modal, index) => {
164
+ modal.getParentModal = () => (index > 0 ? updatedStack[index - 1] : null)
165
+ modal.getChildModal = () => (index < updatedStack.length - 1 ? updatedStack[index + 1] : null)
166
+ })
167
+
168
+ return updatedStack
169
+ })
170
+
171
+ return newModal
172
+ }
173
+
174
+ function pushLocalModal(name, modalProps, onClose, afterLeave) {
175
+ if (!localModals[name]) {
176
+ throw new Error(`The local modal "${name}" has not been registered.`)
177
+ }
178
+
179
+ const modal = push(null, {}, modalProps, onClose, afterLeave)
180
+ modal.name = name
181
+ localModals[name].callback(modal)
182
+ return modal
183
+ }
184
+
185
+ const visitModal = (url, options = {}) => {
186
+ return visit(
187
+ url,
188
+ options.method ?? 'get',
189
+ options.data ?? {},
190
+ options.headers ?? {},
191
+ options.config ?? {},
192
+ options.onClose,
193
+ options.onAfterLeave,
194
+ options.queryStringArrayFormat ?? 'brackets',
195
+ )
196
+ }
197
+
198
+ const visit = (href, method, payload = {}, headers = {}, modalProps = {}, onClose = null, onAfterLeave = null, queryStringArrayFormat = 'brackets') => {
199
+ return new Promise((resolve, reject) => {
200
+ if (href.startsWith('#')) {
201
+ const localModal = pushLocalModal(href.substring(1), modalProps, onClose, onAfterLeave)
202
+ resolve(localModal)
203
+ return
204
+ }
205
+
206
+ const [url, data] = mergeDataIntoQueryString(method, href || '', payload, queryStringArrayFormat)
207
+
208
+ resolveInteriaPageFromRouter().then((inertiaPage) => {
209
+ Axios({
210
+ url,
211
+ method,
212
+ data,
213
+ headers: {
214
+ ...headers,
215
+ Accept: 'text/html, application/xhtml+xml',
216
+ 'X-Requested-With': 'XMLHttpRequest',
217
+ 'X-Inertia': true,
218
+ 'X-Inertia-Version': inertiaPage.version,
219
+ 'X-InertiaUI-Modal': true,
220
+ },
221
+ })
222
+ .then((response) => {
223
+ router.resolveComponent(response.data.component).then((component) => {
224
+ resolve(push(component, response.data, modalProps, onClose, onAfterLeave))
225
+ })
226
+ })
227
+ .catch((error) => {
228
+ reject(error)
229
+ })
230
+ })
231
+ })
232
+ }
233
+
234
+ const registerLocalModal = (name, callback) => {
235
+ setLocalModals((prevLocalModals) => ({
236
+ ...prevLocalModals,
237
+ [name]: { name, callback },
238
+ }))
239
+ }
240
+
241
+ const removeLocalModal = (name) => {
242
+ setLocalModals((prevLocalModals) => {
243
+ const newLocalModals = { ...prevLocalModals }
244
+ delete newLocalModals[name]
245
+ return newLocalModals
246
+ })
247
+ }
248
+
249
+ const value = {
250
+ stack,
251
+ localModals,
252
+ push,
253
+ reset: () => setStack([]),
254
+ visit,
255
+ visitModal,
256
+ registerLocalModal,
257
+ removeLocalModal,
258
+ }
259
+
260
+ return <ModalStackContext.Provider value={value}>{children}</ModalStackContext.Provider>
261
+ }
262
+
263
+ export const useModalStack = () => {
264
+ const context = useContext(ModalStackContext)
265
+ if (context === null) {
266
+ throw new Error('useModalStack must be used within a ModalStackProvider')
267
+ }
268
+ return context
269
+ }
270
+
271
+ export const modalPropNames = ['closeButton', 'closeExplicitly', 'maxWidth', 'paddingClasses', 'panelClasses', 'position', 'slideover']
272
+
273
+ export const ModalRoot = ({ children }) => {
274
+ const stack = useContext(ModalStackContext).stack
275
+
276
+ return (
277
+ <>
278
+ {children}
279
+ {stack.length > 0 && <ModalRenderer index={0} />}
280
+ </>
281
+ )
282
+ }
@@ -0,0 +1,47 @@
1
+ import { TransitionChild, DialogPanel } from '@headlessui/react'
2
+ import CloseButton from './CloseButton'
3
+ import clsx from 'clsx'
4
+
5
+ const SlideoverContent = ({ modalContext, modalProps, children }) => {
6
+ return (
7
+ <div className="im-slideover-container fixed inset-0 z-40 overflow-y-auto overflow-x-hidden">
8
+ <div
9
+ className={clsx('im-slideover-positioner flex min-h-full items-center', {
10
+ 'justify-start': modalProps.position === 'left',
11
+ 'justify-end': modalProps.position === 'right',
12
+ })}
13
+ >
14
+ <TransitionChild
15
+ enterFrom={`opacity-0 ${modalProps.position === 'left' ? '-translate-x-full' : 'translate-x-full'}`}
16
+ enterTo="opacity-100 translate-x-0"
17
+ leaveFrom="opacity-100 translate-x-0"
18
+ leaveTo={`opacity-0 ${modalProps.position === 'left' ? '-translate-x-full' : 'translate-x-full'}`}
19
+ afterLeave={modalContext.afterLeave}
20
+ className={clsx('im-slideover-wrapper w-full transition duration-300 ease-in-out', modalContext.onTopOfStack ? '' : 'blur-sm', {
21
+ 'sm:max-w-sm': modalProps.maxWidth === 'sm',
22
+ 'sm:max-w-md': modalProps.maxWidth === 'md',
23
+ 'sm:max-w-md md:max-w-lg': modalProps.maxWidth === 'lg',
24
+ 'sm:max-w-md md:max-w-xl': modalProps.maxWidth === 'xl',
25
+ 'sm:max-w-md md:max-w-xl lg:max-w-2xl': modalProps.maxWidth === '2xl',
26
+ 'sm:max-w-md md:max-w-xl lg:max-w-3xl': modalProps.maxWidth === '3xl',
27
+ 'sm:max-w-md md:max-w-xl lg:max-w-3xl xl:max-w-4xl': modalProps.maxWidth === '4xl',
28
+ 'sm:max-w-md md:max-w-xl lg:max-w-3xl xl:max-w-5xl': modalProps.maxWidth === '5xl',
29
+ 'sm:max-w-md md:max-w-xl lg:max-w-3xl xl:max-w-5xl 2xl:max-w-6xl': modalProps.maxWidth === '6xl',
30
+ 'sm:max-w-md md:max-w-xl lg:max-w-3xl xl:max-w-5xl 2xl:max-w-7xl': modalProps.maxWidth === '7xl',
31
+ })}
32
+ >
33
+ <DialogPanel className={`im-slideover-content relative ${modalProps.paddingClasses} ${modalProps.panelClasses}`}>
34
+ {modalProps.closeButton && (
35
+ <div className="absolute right-0 top-0 pr-3 pt-3">
36
+ <CloseButton onClick={modalContext.close} />
37
+ </div>
38
+ )}
39
+ {typeof children === 'function' ? children({ modalContext, modalProps }) : children}
40
+ </DialogPanel>
41
+ </TransitionChild>
42
+ </div>
43
+ </div>
44
+ )
45
+ }
46
+
47
+ export default SlideoverContent
package/src/config.js ADDED
@@ -0,0 +1,3 @@
1
+ import { resetConfig, putConfig, getConfig, getConfigByType } from './../../vue/src/config.js'
2
+
3
+ export { resetConfig, putConfig, getConfig, getConfigByType }