@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.
Files changed (49) hide show
  1. package/dist/CloseButton.d.ts +5 -0
  2. package/dist/Deferred.d.ts +11 -0
  3. package/dist/HeadlessModal.d.ts +64 -0
  4. package/dist/Modal.d.ts +48 -0
  5. package/dist/ModalContent.d.ts +24 -0
  6. package/dist/ModalLink.d.ts +29 -0
  7. package/dist/ModalRenderer.d.ts +4 -0
  8. package/dist/ModalRoot.d.ts +22 -0
  9. package/dist/SlideoverContent.d.ts +24 -0
  10. package/dist/WhenVisible.d.ts +16 -0
  11. package/dist/config.d.ts +21 -0
  12. package/dist/constants.d.ts +7 -0
  13. package/dist/helpers.d.ts +3 -0
  14. package/dist/inertiaui-modal.d.ts +2 -0
  15. package/dist/inertiaui-modal.js +1680 -2167
  16. package/dist/inertiaui-modal.js.map +1 -0
  17. package/dist/inertiaui-modal.umd.cjs +1854 -21
  18. package/dist/inertiaui-modal.umd.cjs.map +1 -0
  19. package/dist/inertiauiModal.d.ts +21 -0
  20. package/dist/types.d.ts +111 -0
  21. package/dist/useModal.d.ts +2 -0
  22. package/package.json +37 -22
  23. package/src/{CloseButton.jsx → CloseButton.tsx} +5 -1
  24. package/src/{Deferred.jsx → Deferred.tsx} +10 -3
  25. package/src/HeadlessModal.tsx +238 -0
  26. package/src/Modal.tsx +292 -0
  27. package/src/ModalContent.tsx +311 -0
  28. package/src/ModalLink.tsx +224 -0
  29. package/src/ModalRenderer.tsx +33 -0
  30. package/src/ModalRoot.tsx +880 -0
  31. package/src/SlideoverContent.tsx +319 -0
  32. package/src/{WhenVisible.jsx → WhenVisible.tsx} +20 -9
  33. package/src/config.ts +99 -0
  34. package/src/constants.ts +22 -0
  35. package/src/helpers.ts +17 -0
  36. package/src/inertiauiModal.ts +65 -0
  37. package/src/types.ts +150 -0
  38. package/src/useModal.ts +7 -0
  39. package/src/HeadlessModal.jsx +0 -143
  40. package/src/Modal.jsx +0 -136
  41. package/src/ModalContent.jsx +0 -56
  42. package/src/ModalLink.jsx +0 -123
  43. package/src/ModalRenderer.jsx +0 -34
  44. package/src/ModalRoot.jsx +0 -623
  45. package/src/SlideoverContent.jsx +0 -55
  46. package/src/config.js +0 -3
  47. package/src/helpers.js +0 -2
  48. package/src/inertiauiModal.js +0 -34
  49. 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
+ }
@@ -0,0 +1,7 @@
1
+ import { useModalIndex } from './ModalRenderer'
2
+ import { useModalStack } from './ModalRoot'
3
+ import type { Modal } from './types'
4
+
5
+ export default function useModal(): Modal | null {
6
+ return useModalStack().stack[useModalIndex()] ?? null
7
+ }
@@ -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
@@ -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
@@ -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