@inertiaui/modal-react 0.1.0 → 0.1.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/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@inertiaui/modal-react",
3
3
  "author": "Pascal Baljet <pascal@protone.media>",
4
- "version": "0.1.0",
4
+ "version": "0.1.2",
5
5
  "private": false,
6
6
  "license": "MIT",
7
7
  "type": "module",
@@ -0,0 +1,88 @@
1
+ import { useMemo, useState, forwardRef, useImperativeHandle, useEffect } 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, ...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]))
13
+
14
+ const modalPropsSlideover = useMemo(() => modalContext?.modalProps.slideover ?? props.slideover ?? getConfig('type') === 'slideover', [props.slideover])
15
+
16
+ const modalProps = useMemo(
17
+ () => ({
18
+ slideover: modalPropsSlideover,
19
+ closeButton: props.closeButton ?? getConfigByType(modalPropsSlideover, 'closeButton'),
20
+ closeExplicitly: props.closeExplicitly ?? getConfigByType(modalPropsSlideover, 'closeExplicitly'),
21
+ maxWidth: props.maxWidth ?? getConfigByType(modalPropsSlideover, 'maxWidth'),
22
+ paddingClasses: props.paddingClasses ?? getConfigByType(modalPropsSlideover, 'paddingClasses'),
23
+ panelClasses: props.panelClasses ?? getConfigByType(modalPropsSlideover, 'panelClasses'),
24
+ position: props.position ?? getConfigByType(modalPropsSlideover, 'position'),
25
+ ...modalContext?.modalProps,
26
+ }),
27
+ [props, modalContext?.modalProps],
28
+ )
29
+
30
+ useEffect(() => {
31
+ if (name) {
32
+ let removeListeners = null
33
+
34
+ registerLocalModal(name, (localContext) => {
35
+ removeListeners = localContext.registerEventListenersFromProps(props)
36
+ setLocalModalContext(localContext)
37
+ })
38
+
39
+ return () => {
40
+ removeListeners?.()
41
+ removeListeners = null
42
+ removeLocalModal(name)
43
+ }
44
+ }
45
+
46
+ return modalContext.registerEventListenersFromProps(props)
47
+ }, [name])
48
+
49
+ useImperativeHandle(
50
+ ref,
51
+ () => ({
52
+ afterLeave: () => modalContext.afterLeave(),
53
+ close: () => modalContext.close(),
54
+ emit: (...args) => modalContext.emit(...args),
55
+ getChildModal: () => modalContext.getChildModal(),
56
+ getParentModal: () => modalContext.getParentModal(),
57
+ modalContext,
58
+ modalProps,
59
+ reload: () => modalContext.reload(),
60
+ }),
61
+ [modalContext],
62
+ )
63
+
64
+ return (
65
+ modalContext && (
66
+ <>
67
+ {typeof children === 'function'
68
+ ? children({
69
+ afterLeave: modalContext.afterLeave,
70
+ close: modalContext.close,
71
+ emit: modalContext.emit,
72
+ getChildModal: modalContext.getChildModal,
73
+ getParentModal: modalContext.getParentModal,
74
+ modalContext,
75
+ modalProps,
76
+ reload: modalContext.reload,
77
+ })
78
+ : children}
79
+
80
+ {/* Next modal in the stack */}
81
+ {stack[modalContext.index + 1] && <ModalRenderer index={modalContext.index + 1} />}
82
+ </>
83
+ )
84
+ )
85
+ })
86
+
87
+ HeadlessModal.displayName = 'HeadlessModal'
88
+ export default HeadlessModal
package/src/Modal.jsx CHANGED
@@ -1,135 +1,102 @@
1
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'
2
+ import { forwardRef, useRef, useImperativeHandle } from 'react'
3
+ import HeadlessModal from './HeadlessModal'
6
4
  import ModalContent from './ModalContent'
7
- import ModalRenderer from './ModalRenderer'
8
5
  import SlideoverContent from './SlideoverContent'
9
6
 
10
7
  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
- }
8
+ const renderChildren = (contentProps) => {
9
+ if (typeof children === 'function') {
10
+ return children(contentProps)
49
11
  }
50
12
 
51
- return modalContext.registerEventListenersFromProps(props)
52
- }, [name])
13
+ return children
14
+ }
53
15
 
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
- )
16
+ const headlessModalRef = useRef(null)
66
17
 
67
- const closeDialog = () => {
68
- if (!modalProps.closeExplicitly) {
69
- modalContext.close()
70
- }
71
- }
18
+ useImperativeHandle(ref, () => headlessModalRef.current, [headlessModalRef])
72
19
 
73
20
  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}
21
+ <HeadlessModal
22
+ ref={headlessModalRef}
23
+ name={name}
24
+ {...props}
25
+ >
26
+ {({ afterLeave, close, emit, getChildModal, getParentModal, modalContext, modalProps, reload }) => (
27
+ <Transition
28
+ appear={true}
29
+ show={modalContext.open ?? false}
85
30
  >
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}
31
+ <Dialog
32
+ as="div"
33
+ className="im-dialog relative z-20"
34
+ onClose={() => (modalProps.closeExplicitly ? null : close())}
35
+ data-inertiaui-modal-id={modalContext.id}
36
+ data-inertiaui-modal-index={modalContext.index}
114
37
  >
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
- )
38
+ {/* Only transition the backdrop for the first modal in the stack */}
39
+ {modalContext.index === 0 ? (
40
+ <TransitionChild
41
+ enter="transition transform ease-in-out duration-300"
42
+ enterFrom="opacity-0"
43
+ enterTo="opacity-100"
44
+ leave="transition transform ease-in-out duration-300"
45
+ leaveFrom="opacity-100"
46
+ leaveTo="opacity-0"
47
+ >
48
+ {modalContext.onTopOfStack ? (
49
+ <div
50
+ className="im-backdrop fixed inset-0 z-30 bg-black/75"
51
+ aria-hidden="true"
52
+ />
53
+ ) : (
54
+ <div />
55
+ )}
56
+ </TransitionChild>
57
+ ) : null}
58
+
59
+ {/* On multiple modals, only show a backdrop for the modal that is on top of the stack */}
60
+ {modalContext.index > 0 && modalContext.onTopOfStack ? <div className="im-backdrop fixed inset-0 z-30 bg-black/75" /> : null}
61
+
62
+ {/* The modal/slideover content itself */}
63
+ {modalProps.slideover ? (
64
+ <SlideoverContent
65
+ modalContext={modalContext}
66
+ modalProps={modalProps}
67
+ >
68
+ {renderChildren({
69
+ afterLeave,
70
+ close,
71
+ emit,
72
+ getChildModal,
73
+ getParentModal,
74
+ modalContext,
75
+ modalProps,
76
+ reload,
77
+ })}
78
+ </SlideoverContent>
79
+ ) : (
80
+ <ModalContent
81
+ modalContext={modalContext}
82
+ modalProps={modalProps}
83
+ >
84
+ {renderChildren({
85
+ afterLeave,
86
+ close,
87
+ emit,
88
+ getChildModal,
89
+ getParentModal,
90
+ modalContext,
91
+ modalProps,
92
+ reload,
93
+ })}
94
+ </ModalContent>
95
+ )}
96
+ </Dialog>
97
+ </Transition>
98
+ )}
99
+ </HeadlessModal>
133
100
  )
134
101
  })
135
102
 
@@ -1,6 +1,8 @@
1
1
  import { getConfig, putConfig, resetConfig } from './config.js'
2
+ import { useModalIndex } from './ModalRenderer.jsx'
3
+ import { useModalStack, ModalRoot, ModalStackProvider } from './ModalRoot.jsx'
4
+ import HeadlessModal from './HeadlessModal.jsx'
2
5
  import Modal from './Modal.jsx'
3
6
  import ModalLink from './ModalLink.jsx'
4
- import { useModalStack, ModalRoot, ModalStackProvider } from './ModalRoot.jsx'
5
7
 
6
- export { useModalStack, Modal, ModalLink, ModalRoot, ModalStackProvider, getConfig, putConfig, resetConfig }
8
+ export { getConfig, putConfig, resetConfig, useModalStack, useModalIndex, HeadlessModal, Modal, ModalLink, ModalRoot, ModalStackProvider }