@proyecto-viviana/solidaria-components 0.4.0 → 0.4.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.
- package/dist/ActionBar.d.ts.map +1 -1
- package/dist/ActionBar.js +2 -11
- package/dist/ActionBar.js.map +1 -1
- package/dist/ActionBar.jsx +2 -11
- package/dist/ActionBar.jsx.map +1 -1
- package/dist/ActionGroup.d.ts.map +1 -1
- package/dist/ActionGroup.js +1 -1
- package/dist/ActionGroup.js.map +1 -1
- package/dist/ActionGroup.jsx +1 -1
- package/dist/ActionGroup.jsx.map +1 -1
- package/dist/Autocomplete.d.ts +2 -2
- package/dist/Autocomplete.d.ts.map +1 -1
- package/dist/Autocomplete.js +7 -7
- package/dist/Autocomplete.js.map +1 -1
- package/dist/Autocomplete.jsx +7 -7
- package/dist/Autocomplete.jsx.map +1 -1
- package/dist/Breadcrumbs.d.ts.map +1 -1
- package/dist/Breadcrumbs.js +15 -16
- package/dist/Breadcrumbs.js.map +1 -1
- package/dist/Breadcrumbs.jsx +19 -24
- package/dist/Breadcrumbs.jsx.map +1 -1
- package/dist/Calendar.d.ts.map +1 -1
- package/dist/Calendar.js +100 -65
- package/dist/Calendar.js.map +1 -1
- package/dist/Calendar.jsx +10 -1
- package/dist/Calendar.jsx.map +1 -1
- package/dist/Color.d.ts.map +1 -1
- package/dist/Color.js +15 -18
- package/dist/Color.js.map +1 -1
- package/dist/Color.jsx +15 -19
- package/dist/Color.jsx.map +1 -1
- package/dist/ComboBox.d.ts +4 -0
- package/dist/ComboBox.d.ts.map +1 -1
- package/dist/ComboBox.js +21 -13
- package/dist/ComboBox.js.map +1 -1
- package/dist/ComboBox.jsx +23 -13
- package/dist/ComboBox.jsx.map +1 -1
- package/dist/DateField.d.ts +11 -2
- package/dist/DateField.d.ts.map +1 -1
- package/dist/DateField2.js +237 -234
- package/dist/DateField2.js.map +1 -1
- package/dist/DateField2.jsx +134 -97
- package/dist/DateField2.jsx.map +1 -1
- package/dist/DatePicker.d.ts +42 -3
- package/dist/DatePicker.d.ts.map +1 -1
- package/dist/DatePicker.js +6 -990
- package/dist/DatePicker.jsx +6 -681
- package/dist/DatePicker2.js +1171 -0
- package/dist/DatePicker2.js.map +1 -0
- package/dist/DatePicker2.jsx +820 -0
- package/dist/DatePicker2.jsx.map +1 -0
- package/dist/DateRangePickerContext.d.ts +2 -0
- package/dist/DateRangePickerContext.d.ts.map +1 -1
- package/dist/Disclosure.d.ts.map +1 -1
- package/dist/Disclosure.js +3 -0
- package/dist/Disclosure.js.map +1 -1
- package/dist/Disclosure.jsx +1 -1
- package/dist/Disclosure.jsx.map +1 -1
- package/dist/GridList.d.ts.map +1 -1
- package/dist/GridList.js +2 -2
- package/dist/GridList.js.map +1 -1
- package/dist/GridList.jsx +2 -2
- package/dist/GridList.jsx.map +1 -1
- package/dist/ListBox.d.ts +2 -2
- package/dist/ListBox.d.ts.map +1 -1
- package/dist/ListBox.js +327 -166
- package/dist/ListBox.js.map +1 -1
- package/dist/ListBox.jsx +161 -25
- package/dist/ListBox.jsx.map +1 -1
- package/dist/Modal.d.ts.map +1 -1
- package/dist/Modal.js +7 -5
- package/dist/Modal.js.map +1 -1
- package/dist/Modal.jsx +5 -5
- package/dist/Modal.jsx.map +1 -1
- package/dist/Popover.d.ts.map +1 -1
- package/dist/Popover.js +16 -9
- package/dist/Popover.js.map +1 -1
- package/dist/Popover.jsx +25 -4
- package/dist/Popover.jsx.map +1 -1
- package/dist/RangeCalendar.d.ts.map +1 -1
- package/dist/RangeCalendar.js +182 -46
- package/dist/RangeCalendar.js.map +1 -1
- package/dist/RangeCalendar.jsx +72 -2
- package/dist/RangeCalendar.jsx.map +1 -1
- package/dist/SearchField.d.ts.map +1 -1
- package/dist/SearchField.js +26 -2
- package/dist/SearchField.js.map +1 -1
- package/dist/SearchField.jsx +26 -2
- package/dist/SearchField.jsx.map +1 -1
- package/dist/Select.d.ts +7 -0
- package/dist/Select.d.ts.map +1 -1
- package/dist/Select.js +62 -49
- package/dist/Select.js.map +1 -1
- package/dist/Select.jsx +33 -18
- package/dist/Select.jsx.map +1 -1
- package/dist/SharedElementTransition.d.ts.map +1 -1
- package/dist/SharedElementTransition.js +35 -33
- package/dist/SharedElementTransition.js.map +1 -1
- package/dist/SharedElementTransition.jsx +35 -33
- package/dist/SharedElementTransition.jsx.map +1 -1
- package/dist/StepList.d.ts.map +1 -1
- package/dist/StepList.js +2 -9
- package/dist/StepList.js.map +1 -1
- package/dist/StepList.jsx +1 -6
- package/dist/StepList.jsx.map +1 -1
- package/dist/Table.d.ts +2 -0
- package/dist/Table.d.ts.map +1 -1
- package/dist/Table.js +10 -3
- package/dist/Table.js.map +1 -1
- package/dist/Table.jsx +10 -3
- package/dist/Table.jsx.map +1 -1
- package/dist/Tabs.d.ts.map +1 -1
- package/dist/Tabs.js +3 -0
- package/dist/Tabs.js.map +1 -1
- package/dist/Tabs.jsx +1 -1
- package/dist/Tabs.jsx.map +1 -1
- package/dist/TagGroup.d.ts +3 -1
- package/dist/TagGroup.d.ts.map +1 -1
- package/dist/TagGroup.js +12 -8
- package/dist/TagGroup.js.map +1 -1
- package/dist/TagGroup.jsx +11 -8
- package/dist/TagGroup.jsx.map +1 -1
- package/dist/TextField.js +4 -1
- package/dist/TextField.js.map +1 -1
- package/dist/TextField.jsx +4 -1
- package/dist/TextField.jsx.map +1 -1
- package/dist/TimeField.d.ts +14 -50
- package/dist/TimeField.d.ts.map +1 -1
- package/dist/TimeField.js +111 -205
- package/dist/TimeField.js.map +1 -1
- package/dist/TimeField.jsx +76 -82
- package/dist/TimeField.jsx.map +1 -1
- package/dist/Tree.d.ts.map +1 -1
- package/dist/Tree.js +3 -3
- package/dist/Tree.js.map +1 -1
- package/dist/Tree.jsx +3 -3
- package/dist/Tree.jsx.map +1 -1
- package/dist/index.d.ts +2 -3
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +5 -6
- package/dist/index.jsx +5 -6
- package/dist/useDragAndDrop.d.ts +0 -8
- package/dist/useDragAndDrop.d.ts.map +1 -1
- package/dist/useDragAndDrop2.js +6 -2
- package/dist/useDragAndDrop2.js.map +1 -1
- package/dist/useDragAndDrop2.jsx +6 -2
- package/dist/useDragAndDrop2.jsx.map +1 -1
- package/dist/utils.d.ts +1 -0
- package/dist/utils.d.ts.map +1 -1
- package/dist/utils.js +2 -2
- package/dist/utils.js.map +1 -1
- package/dist/utils.jsx +2 -2
- package/dist/utils.jsx.map +1 -1
- package/package.json +3 -9
- package/src/ActionBar.tsx +10 -12
- package/src/ActionGroup.tsx +5 -3
- package/src/Autocomplete.tsx +14 -9
- package/src/Breadcrumbs.tsx +55 -38
- package/src/Calendar.tsx +30 -1
- package/src/Color.tsx +42 -27
- package/src/ComboBox.tsx +49 -17
- package/src/DateField.tsx +285 -52
- package/src/DatePicker.tsx +345 -57
- package/src/DateRangePickerContext.tsx +2 -0
- package/src/Disclosure.tsx +4 -0
- package/src/GridList.tsx +8 -2
- package/src/ListBox.tsx +323 -21
- package/src/Modal.tsx +21 -5
- package/src/Popover.tsx +28 -7
- package/src/RangeCalendar.tsx +120 -1
- package/src/SearchField.tsx +53 -1
- package/src/Select.tsx +101 -20
- package/src/SharedElementTransition.tsx +92 -36
- package/src/StepList.tsx +0 -7
- package/src/Table.tsx +19 -4
- package/src/Tabs.tsx +1 -0
- package/src/TagGroup.tsx +26 -6
- package/src/TextField.tsx +1 -1
- package/src/TimeField.tsx +113 -196
- package/src/Tree.tsx +14 -4
- package/src/index.ts +1 -8
- package/src/useDragAndDrop.ts +8 -10
- package/src/utils.tsx +3 -3
- package/dist/DatePicker.js.map +0 -1
- package/dist/DatePicker.jsx.map +0 -1
- package/dist/HiddenTimeInput.d.ts +0 -25
- package/dist/HiddenTimeInput.d.ts.map +0 -1
- package/dist/HiddenTimeInput.js +0 -104
- package/dist/HiddenTimeInput.js.map +0 -1
- package/dist/HiddenTimeInput.jsx +0 -69
- package/dist/HiddenTimeInput.jsx.map +0 -1
- package/src/HiddenTimeInput.tsx +0 -133
package/dist/Modal.js
CHANGED
|
@@ -122,9 +122,9 @@ function ModalOverlay(props) {
|
|
|
122
122
|
close();
|
|
123
123
|
}
|
|
124
124
|
};
|
|
125
|
-
document.addEventListener("keydown", handleKeyDown,
|
|
125
|
+
document.addEventListener("keydown", handleKeyDown, false);
|
|
126
126
|
onCleanup(() => {
|
|
127
|
-
document.removeEventListener("keydown", handleKeyDown,
|
|
127
|
+
document.removeEventListener("keydown", handleKeyDown, false);
|
|
128
128
|
});
|
|
129
129
|
});
|
|
130
130
|
const resolveChildren = () => {
|
|
@@ -297,9 +297,9 @@ function ModalContent(props) {
|
|
|
297
297
|
close();
|
|
298
298
|
}
|
|
299
299
|
};
|
|
300
|
-
document.addEventListener("keydown", handleKeyDown,
|
|
300
|
+
document.addEventListener("keydown", handleKeyDown, false);
|
|
301
301
|
onCleanup(() => {
|
|
302
|
-
document.removeEventListener("keydown", handleKeyDown,
|
|
302
|
+
document.removeEventListener("keydown", handleKeyDown, false);
|
|
303
303
|
});
|
|
304
304
|
});
|
|
305
305
|
createEffect(() => {
|
|
@@ -332,7 +332,9 @@ function ModalContent(props) {
|
|
|
332
332
|
}, renderValues);
|
|
333
333
|
const domProps = createMemo(() => filterDOMProps(rest, { global: true }));
|
|
334
334
|
return createComponent(FocusScope, {
|
|
335
|
-
contain
|
|
335
|
+
get contain() {
|
|
336
|
+
return !isModalExiting();
|
|
337
|
+
},
|
|
336
338
|
restoreFocus: true,
|
|
337
339
|
get children() {
|
|
338
340
|
var _el$2 = getNextElement(_tmpl$3), _el$4 = _el$2.firstChild, [_el$5, _co$] = getNextMarker(_el$4.nextSibling), _el$6 = _el$5.nextSibling, [_el$7, _co$2] = getNextMarker(_el$6.nextSibling);
|
package/dist/Modal.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Modal.js","names":["JSX","createContext","createMemo","createSignal","createEffect","onCleanup","splitProps","Show","useContext","Portal","isServer","createInteractOutside","ariaHideOutside","FocusScope","useUNSAFE_PortalContext","createEnterAnimation","createExitAnimation","RenderChildren","ClassNameOrFunction","StyleOrFunction","useRenderProps","filterDOMProps","dataAttr","useIsHydrated","DialogTriggerContext","OverlayTriggerStateContext","OverlayTriggerState","VisuallyHidden","InternalModalContextValue","isDismissable","isKeyboardDismissDisabled","setModalRef","element","HTMLElement","isExiting","InternalModalContext","visibleModals","Array","Element","pruneDisconnectedModals","index","length","isConnected","splice","ModalRenderProps","isEntering","ModalOverlayProps","children","class","style","isOpen","defaultOpen","onOpenChange","ModalProps","useOverlayTriggerState","ModalContext","ModalOverlay","props","isHydrated","local","rest","dialogTriggerContext","internalOpen","setInternalOpen","undefined","state","close","open","toggle","overlayRef","HTMLDivElement","overlayEl","setOverlayEl","overlayRefAccessor","registerOverlayRef","modalEl","setModalEl","isOverlayEntering","isOverlayExiting","isModalExiting","overlayEntering","combinedExiting","renderValues","renderProps","defaultClassName","domProps","Record","global","internalModalContext","portalContext","portalContainer","getContainer","isTopMostModalInOverlay","topMostModal","contains","handleOverlayPointerDown","EventHandler","PointerEvent","event","target","currentTarget","handleKeyDown","KeyboardEvent","key","isComposing","preventDefault","stopPropagation","document","addEventListener","removeEventListener","resolveChildren","_$createComponent","when","_$memo","mount","Provider","value","_el$","_$getNextElement","_tmpl$","_$use","_$spread","_$mergeProps","data-entering","data-exiting","_$insert","_$runHydrationEvents","Modal","ModalContentWithAutoOverlay","overlayProps","modalProps","internalContext","ModalContent","modalRef","modalRefAccessor","parentState","registerModalRef","isModalEntering","includes","push","indexOf","isTopMostModal","html","documentElement","prevOverflow","overflow","ref","onInteractOutside","isDisabled","e","cleanup","cancelled","ownerWindow","ownerDocument","defaultView","window","hideOutside","shouldUseInert","queueMicrotask","contain","restoreFocus","_el$2","_tmpl$3","_el$4","firstChild","_el$5","_co$","_$getNextMarker","nextSibling","_el$6","_el$7","_co$2","elementType","_el$3","_tmpl$2","$$click","renderChildren","_$delegateEvents"],"sources":["../src/Modal.tsx"],"sourcesContent":["/**\n * Modal and ModalOverlay components for solidaria-components\n *\n * Headless modal components with overlay, focus trapping, and dismissal handling.\n * Port of react-aria-components Modal.\n */\n\nimport {\n type JSX,\n createContext,\n createMemo,\n createSignal,\n createEffect,\n onCleanup,\n splitProps,\n Show,\n useContext,\n} from \"solid-js\";\nimport { Portal, isServer } from \"solid-js/web\";\nimport {\n createInteractOutside,\n ariaHideOutside,\n FocusScope,\n useUNSAFE_PortalContext,\n createEnterAnimation,\n createExitAnimation,\n} from \"@proyecto-viviana/solidaria\";\nimport {\n type RenderChildren,\n type ClassNameOrFunction,\n type StyleOrFunction,\n useRenderProps,\n filterDOMProps,\n dataAttr,\n useIsHydrated,\n} from \"./utils\";\nimport {\n DialogTriggerContext,\n OverlayTriggerStateContext,\n type OverlayTriggerState,\n} from \"./contexts\";\nimport { VisuallyHidden } from \"./VisuallyHidden\";\n\n/**\n * Internal context to signal that Modal is wrapped in ModalOverlay.\n * When present, Modal should not create its own Portal.\n */\ninterface InternalModalContextValue {\n isDismissable?: boolean;\n isKeyboardDismissDisabled?: boolean;\n // Upstream's ModalOverlay owns the modal element's ref so it can watch the\n // modal surface's exit animation alongside the overlay's (both must finish\n // before the overlay unmounts). ModalContent registers its element here.\n setModalRef?: (element: HTMLElement | null) => void;\n // Combined exit flag (overlay OR modal still exiting). Both the overlay and\n // the modal surface carry `data-exiting` off this so they fade out together.\n isExiting?: () => boolean;\n}\n\nconst InternalModalContext = createContext<InternalModalContextValue | null>(null);\n\n// Stack of visible modals, used to ensure only the top-most modal dismisses on Escape/outside interaction.\nconst visibleModals: Array<() => Element | null> = [];\n\nfunction pruneDisconnectedModals() {\n for (let index = visibleModals.length - 1; index >= 0; index -= 1) {\n const element = visibleModals[index]?.();\n if (!element?.isConnected) {\n visibleModals.splice(index, 1);\n }\n }\n}\n\nexport interface ModalRenderProps {\n /** Whether the modal is currently entering (for animations). */\n isEntering: boolean;\n /** Whether the modal is currently exiting (for animations). */\n isExiting: boolean;\n}\n\nexport interface ModalOverlayProps {\n /** The children of the component - can be JSX or render function. */\n children?: RenderChildren<ModalRenderProps>;\n /** The CSS className for the element. */\n class?: ClassNameOrFunction<ModalRenderProps>;\n /** The inline style for the element. */\n style?: StyleOrFunction<ModalRenderProps>;\n /** Whether the modal is open (controlled). */\n isOpen?: boolean;\n /** Whether the modal opens by default (uncontrolled). */\n defaultOpen?: boolean;\n /** Handler called when the modal's open state changes. */\n onOpenChange?: (isOpen: boolean) => void;\n /** Whether clicking outside the modal closes it. */\n isDismissable?: boolean;\n /** Whether pressing Escape closes the modal. */\n isKeyboardDismissDisabled?: boolean;\n /** Whether the modal is entering (for animations). */\n isEntering?: boolean;\n /** Whether the modal is exiting (for animations). */\n isExiting?: boolean;\n}\n\nexport interface ModalProps extends ModalOverlayProps {}\n\nexport { OverlayTriggerStateContext, type OverlayTriggerState } from \"./contexts\";\nexport { useOverlayTriggerState } from \"./contexts\";\nexport const ModalContext = OverlayTriggerStateContext;\n\n/**\n * ModalOverlay is the backdrop/underlay behind a modal.\n * It handles click-outside dismissal and provides styling hooks.\n */\nexport function ModalOverlay(props: ModalOverlayProps): JSX.Element {\n // Do NOT early-return on the server: rendering children bare on the server and a\n // <Show>/<Portal> overlay on the client desyncs hydration. Run the same structure\n // on both and gate the Portal on useIsHydrated() (see Popover for the rationale).\n const isHydrated = useIsHydrated();\n\n // IMPORTANT: Don't destructure or access props.children early!\n // In SolidJS, children are lazily evaluated. Accessing them before\n // the context provider renders causes them to evaluate outside the context.\n // See: https://github.com/solidjs/solid/issues/182\n const [local, rest] = splitProps(props, [\n \"class\",\n \"style\",\n \"isOpen\",\n \"defaultOpen\",\n \"onOpenChange\",\n \"isDismissable\",\n \"isKeyboardDismissDisabled\",\n \"isEntering\",\n \"isExiting\",\n ]);\n\n // Get state from DialogTrigger context if available\n const dialogTriggerContext = useContext(DialogTriggerContext);\n\n // Internal state for uncontrolled mode\n const [internalOpen, setInternalOpen] = createSignal(local.defaultOpen ?? false);\n\n // Determine if open (controlled > DialogTrigger context > uncontrolled)\n const isOpen = (): boolean => {\n if (local.isOpen !== undefined) return local.isOpen;\n if (dialogTriggerContext) return dialogTriggerContext.state.isOpen();\n return internalOpen();\n };\n\n const close = () => {\n if (local.isOpen !== undefined) {\n local.onOpenChange?.(false);\n } else if (dialogTriggerContext) {\n dialogTriggerContext.state.close();\n local.onOpenChange?.(false);\n } else {\n setInternalOpen(false);\n local.onOpenChange?.(false);\n }\n };\n\n const open = () => {\n if (local.isOpen !== undefined) {\n local.onOpenChange?.(true);\n } else if (dialogTriggerContext) {\n dialogTriggerContext.state.open();\n local.onOpenChange?.(true);\n } else {\n setInternalOpen(true);\n local.onOpenChange?.(true);\n }\n };\n\n const toggle = () => {\n if (isOpen()) {\n close();\n } else {\n open();\n }\n };\n\n const state: OverlayTriggerState = {\n get isOpen() {\n return isOpen();\n },\n open,\n close,\n toggle,\n };\n\n // Enter/exit animation state (mirrors upstream's ModalOverlayWithForwardRef +\n // ModalOverlayInner). The overlay stays mounted here even while closed, so the\n // enter animation is gated on `isOpen` (isReady) — otherwise it would resolve\n // to \"done entering\" before the element ever mounts. Exit is watched for BOTH\n // the overlay and the modal surface; the modal registers its element via\n // InternalModalContext (setModalRef) so we can await its exit too.\n let overlayRef!: HTMLDivElement;\n // Signal-backed so the enter/exit effects re-run once the element mounts — a\n // plain closure over `overlayRef` would read `null` if the effect fired first\n // and, with no reactive dep on the ref, never recover (stuck `data-entering`).\n const [overlayEl, setOverlayEl] = createSignal<HTMLElement | null>(null);\n const overlayRefAccessor = () => overlayEl();\n const registerOverlayRef = (element: HTMLDivElement) => {\n overlayRef = element;\n setOverlayEl(element);\n };\n const [modalEl, setModalEl] = createSignal<HTMLElement | null>(null);\n\n const isOverlayEntering = createEnterAnimation(overlayRefAccessor, isOpen);\n const isOverlayExiting = createExitAnimation(overlayRefAccessor, isOpen);\n const isModalExiting = createExitAnimation(() => modalEl(), isOpen);\n const overlayEntering = () => isOverlayEntering() || (local.isEntering ?? false);\n const combinedExiting = () =>\n isOverlayExiting() || isModalExiting() || (local.isExiting ?? false);\n\n const renderValues = createMemo<ModalRenderProps>(() => ({\n isEntering: overlayEntering(),\n isExiting: combinedExiting(),\n }));\n\n // Resolve render props - don't pass children, we'll render props.children directly\n const renderProps = useRenderProps(\n {\n class: local.class,\n style: local.style,\n defaultClassName: \"solidaria-ModalOverlay\",\n },\n renderValues,\n );\n\n const domProps = createMemo(() =>\n filterDOMProps(rest as Record<string, unknown>, { global: true }),\n );\n\n // Internal context value to signal Modal that it's wrapped\n const internalModalContext: InternalModalContextValue = {\n isDismissable: local.isDismissable,\n isKeyboardDismissDisabled: local.isKeyboardDismissDisabled,\n setModalRef: setModalEl,\n isExiting: combinedExiting,\n };\n const portalContext = useUNSAFE_PortalContext();\n const portalContainer = () => portalContext.getContainer?.() ?? undefined;\n\n const isTopMostModalInOverlay = () => {\n pruneDisconnectedModals();\n const topMostModal = visibleModals[visibleModals.length - 1]?.();\n return !topMostModal || overlayRef?.contains(topMostModal);\n };\n\n const handleOverlayPointerDown: JSX.EventHandler<HTMLDivElement, PointerEvent> = (event) => {\n if (local.isDismissable && event.target === event.currentTarget && isTopMostModalInOverlay()) {\n close();\n }\n };\n\n createEffect(() => {\n if (!isOpen() || local.isKeyboardDismissDisabled) return;\n\n const handleKeyDown = (event: KeyboardEvent) => {\n if (event.key === \"Escape\" && !event.isComposing && isTopMostModalInOverlay()) {\n event.preventDefault();\n event.stopPropagation();\n close();\n }\n };\n\n document.addEventListener(\"keydown\", handleKeyDown, true);\n onCleanup(() => {\n document.removeEventListener(\"keydown\", handleKeyDown, true);\n });\n });\n\n // Resolve children - handle both static JSX and render functions\n // IMPORTANT: We access props.children directly (not local.children) to preserve\n // lazy evaluation inside context providers\n const resolveChildren = () => {\n const children = props.children;\n if (typeof children === \"function\") {\n return (children as (props: ModalRenderProps) => JSX.Element)(renderValues());\n }\n return children;\n };\n\n return (\n <Show when={isHydrated() && (isOpen() || combinedExiting())}>\n <Portal mount={portalContainer()}>\n <OverlayTriggerStateContext.Provider value={state}>\n <InternalModalContext.Provider value={internalModalContext}>\n <div\n {...domProps()}\n ref={registerOverlayRef}\n class={renderProps.class()}\n style={renderProps.style()}\n data-entering={dataAttr(overlayEntering())}\n data-exiting={dataAttr(combinedExiting())}\n onPointerDown={handleOverlayPointerDown}\n >\n {resolveChildren()}\n </div>\n </InternalModalContext.Provider>\n </OverlayTriggerStateContext.Provider>\n </Portal>\n </Show>\n );\n}\n\n/**\n * Modal is a dialog container that manages focus trapping, scroll prevention,\n * aria-hiding of content outside, and dismissal.\n *\n * Usage patterns:\n * 1. Standalone: `<Modal isOpen>...</Modal>` - Creates its own overlay\n * 2. With custom overlay: `<ModalOverlay><Modal>...</Modal></ModalOverlay>`\n *\n * Note: Due to SolidJS's eager JSX evaluation, we cannot detect at render time\n * whether Modal is wrapped in ModalOverlay. So standalone Modal always creates\n * an overlay, and wrapped Modal renders directly (relying on InternalModalContext).\n */\nexport function Modal(props: ModalProps): JSX.Element {\n // Check for InternalModalContext which signals we're inside a rendered ModalOverlay\n // This works because ModalContent is rendered INSIDE ModalOverlay's Show/Portal\n return <ModalContentWithAutoOverlay {...props} />;\n}\n\n/**\n * Helper component that handles the overlay detection.\n * By being a separate component, we can use Show to defer rendering until\n * the parent context is available.\n */\nfunction ModalContentWithAutoOverlay(props: ModalProps): JSX.Element {\n const [overlayProps, modalProps] = splitProps(props, [\n \"isOpen\",\n \"defaultOpen\",\n \"onOpenChange\",\n \"isDismissable\",\n \"isKeyboardDismissDisabled\",\n \"isEntering\",\n \"isExiting\",\n ]);\n\n // Check for InternalModalContext - if present, we're inside a ModalOverlay\n const internalContext = useContext(InternalModalContext);\n\n // If wrapped in ModalOverlay, just render the content. ModalContent reads the\n // overlay's InternalModalContext itself (via useContext), so nothing needs\n // threading — in both branches ModalContent renders inside a provider.\n if (internalContext) {\n return <ModalContent {...modalProps}>{props.children}</ModalContent>;\n }\n\n // For standalone usage, wrap in ModalOverlay.\n return (\n <ModalOverlay {...overlayProps}>\n <ModalContent {...modalProps}>{props.children}</ModalContent>\n </ModalOverlay>\n );\n}\n\n/**\n * Internal component that renders the actual modal content.\n * Used by both standalone Modal and Modal wrapped in ModalOverlay.\n */\nfunction ModalContent(props: ModalProps): JSX.Element {\n if (isServer) {\n return <>{props.children}</>;\n }\n\n const [local, rest] = splitProps(props, [\n \"children\",\n \"class\",\n \"style\",\n \"isOpen\",\n \"defaultOpen\",\n \"onOpenChange\",\n \"isDismissable\",\n \"isKeyboardDismissDisabled\",\n \"isEntering\",\n \"isExiting\",\n ]);\n\n let modalRef!: HTMLDivElement;\n const modalRefAccessor = () => modalRef ?? null;\n // Signal-backed element for the enter animation, so the effect re-runs when\n // the surface mounts (see the overlay's note); the plain `modalRef` var still\n // backs the synchronous consumers (focus stack, interact-outside, aria-hide).\n const [modalEl, setModalEl] = createSignal<HTMLElement | null>(null);\n\n // Get state from parent OverlayTriggerStateContext (provided by ModalOverlay)\n const parentState = useContext(OverlayTriggerStateContext);\n\n // The overlay (auto or user-provided) always wraps this content, so it owns\n // the InternalModalContext: dismissal settings, the modal-ref registration\n // sink, and the combined exit flag.\n const internalContext = useContext(InternalModalContext);\n\n // Get dismissable settings from internal context (set by ModalOverlay)\n const isDismissable = () => internalContext?.isDismissable ?? local.isDismissable;\n const isKeyboardDismissDisabled = () =>\n internalContext?.isKeyboardDismissDisabled ?? local.isKeyboardDismissDisabled;\n\n // Determine if open from parent state\n const isOpen = (): boolean => {\n if (local.isOpen !== undefined) return local.isOpen;\n return parentState?.isOpen ?? false;\n };\n\n // Register this element with the overlay so it can await the modal surface's\n // exit animation (upstream threads modalRef down the same way). The overlay's\n // combined exit flag drives this surface's `data-exiting`. The enter animation\n // is local — the modal fades/slides in on its own timeline (delay 160 vs the\n // overlay's 0).\n const registerModalRef = (element: HTMLDivElement) => {\n modalRef = element;\n setModalEl(element);\n internalContext?.setModalRef?.(element);\n };\n const isModalEntering = createEnterAnimation(() => modalEl(), isOpen);\n const isModalExiting = () => internalContext?.isExiting?.() ?? local.isExiting ?? false;\n\n // Keep this modal in a global stack so nested modals dismiss in top-down order.\n createEffect(() => {\n if (!isOpen()) return;\n\n pruneDisconnectedModals();\n if (!visibleModals.includes(modalRefAccessor)) {\n visibleModals.push(modalRefAccessor);\n }\n\n onCleanup(() => {\n const index = visibleModals.indexOf(modalRefAccessor);\n if (index >= 0) {\n visibleModals.splice(index, 1);\n }\n });\n });\n\n const isTopMostModal = () => {\n pruneDisconnectedModals();\n return visibleModals[visibleModals.length - 1] === modalRefAccessor;\n };\n\n const close = () => {\n if (local.isOpen !== undefined) {\n local.onOpenChange?.(false);\n } else {\n parentState?.close();\n }\n };\n\n // Prevent scroll when modal is open\n createEffect(() => {\n if (!isOpen()) return;\n\n const html = document.documentElement;\n const prevOverflow = html.style.overflow;\n html.style.overflow = \"hidden\";\n\n onCleanup(() => {\n html.style.overflow = prevOverflow;\n });\n });\n\n // Click outside to close (if dismissable)\n createEffect(() => {\n if (!isOpen() || !isDismissable()) return;\n\n createInteractOutside({\n ref: modalRefAccessor,\n onInteractOutside: () => {\n if (isTopMostModal()) {\n close();\n }\n },\n isDisabled: false,\n });\n });\n\n // Escape key to close\n createEffect(() => {\n if (!isOpen() || isKeyboardDismissDisabled()) return;\n\n const handleKeyDown = (e: KeyboardEvent) => {\n if (e.key === \"Escape\" && !e.isComposing && isTopMostModal()) {\n e.preventDefault();\n e.stopPropagation();\n close();\n }\n };\n\n document.addEventListener(\"keydown\", handleKeyDown, true);\n onCleanup(() => {\n document.removeEventListener(\"keydown\", handleKeyDown, true);\n });\n });\n\n // Aria-hide outside content\n createEffect(() => {\n if (!isOpen() || !modalRef) return;\n\n let cleanup: (() => void) | undefined;\n let cancelled = false;\n const ownerWindow = modalRef.ownerDocument.defaultView ?? window;\n\n const hideOutside = () => {\n if (cancelled || !modalRef?.isConnected) return;\n cleanup = ariaHideOutside([modalRef], { shouldUseInert: true });\n };\n\n if (modalRef.isConnected) {\n hideOutside();\n } else {\n ownerWindow.queueMicrotask(hideOutside);\n }\n\n onCleanup(() => {\n cancelled = true;\n cleanup?.();\n });\n });\n\n const renderValues = createMemo<ModalRenderProps>(() => ({\n isEntering: isModalEntering(),\n isExiting: isModalExiting(),\n }));\n\n const renderProps = useRenderProps(\n {\n // Lazy: the modal content is gated behind `<Show when={isHydrated() && …}>`\n // (Portal) below, so it must not be instantiated during the component body.\n // An eager `children: props.children` would build the content template (and\n // walk getNextElement) before the gate — content the server never emitted →\n // hydration mismatch. See Popover for the full rationale.\n get children() {\n return props.children;\n },\n class: local.class,\n style: local.style,\n defaultClassName: \"solidaria-Modal\",\n },\n renderValues,\n );\n\n const domProps = createMemo(() =>\n filterDOMProps(rest as Record<string, unknown>, { global: true }),\n );\n\n return (\n // No autoFocus: react-aria's Overlay renders FocusScope with restoreFocus +\n // contain only — initial focus is useDialog's job (it focuses the dialog\n // element itself when nothing inside holds focus).\n <FocusScope contain restoreFocus>\n <div\n {...domProps()}\n ref={registerModalRef}\n class={renderProps.class()}\n style={renderProps.style()}\n data-entering={dataAttr(isModalEntering())}\n data-exiting={dataAttr(isModalExiting())}\n >\n <Show when={isDismissable()}>\n {/* Faithful to react-aria's DismissButton: a bare button carrying\n only `width/height: 1px` inside a VisuallyHidden `div` wrapper —\n NOT the hidden styles inlined onto the button. The wrapper clips\n it out of sight while the button keeps its intrinsic UA border-box,\n which is exactly what upstream renders (and what D8 target-size\n measures against the pair oracle). Inlining the reset onto the\n button instead collapsed it to a 1x1 box, diverging from\n upstream's ~16x6 sentinel. */}\n <VisuallyHidden elementType=\"div\">\n <button\n aria-label=\"Dismiss\"\n tabIndex={-1}\n onClick={close}\n style={{ width: \"1px\", height: \"1px\" }}\n />\n </VisuallyHidden>\n </Show>\n {renderProps.renderChildren()}\n </div>\n </FocusScope>\n );\n}\n\nexport default Modal;\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"Modal.js","names":["JSX","createContext","createMemo","createSignal","createEffect","onCleanup","splitProps","Show","useContext","Portal","isServer","createInteractOutside","ariaHideOutside","FocusScope","useUNSAFE_PortalContext","createEnterAnimation","createExitAnimation","RenderChildren","ClassNameOrFunction","StyleOrFunction","useRenderProps","filterDOMProps","dataAttr","useIsHydrated","DialogTriggerContext","OverlayTriggerStateContext","OverlayTriggerState","VisuallyHidden","InternalModalContextValue","isDismissable","isKeyboardDismissDisabled","setModalRef","element","HTMLElement","isExiting","InternalModalContext","visibleModals","Array","Element","pruneDisconnectedModals","index","length","isConnected","splice","ModalRenderProps","isEntering","ModalOverlayProps","children","class","style","isOpen","defaultOpen","onOpenChange","ModalProps","useOverlayTriggerState","ModalContext","ModalOverlay","props","isHydrated","local","rest","dialogTriggerContext","internalOpen","setInternalOpen","undefined","state","close","open","toggle","overlayRef","HTMLDivElement","overlayEl","setOverlayEl","overlayRefAccessor","registerOverlayRef","modalEl","setModalEl","isOverlayEntering","isOverlayExiting","isModalExiting","overlayEntering","combinedExiting","renderValues","renderProps","defaultClassName","domProps","Record","global","internalModalContext","portalContext","portalContainer","getContainer","isTopMostModalInOverlay","topMostModal","contains","handleOverlayPointerDown","EventHandler","PointerEvent","event","target","currentTarget","handleKeyDown","KeyboardEvent","key","isComposing","preventDefault","stopPropagation","document","addEventListener","removeEventListener","resolveChildren","_$createComponent","when","_$memo","mount","Provider","value","_el$","_$getNextElement","_tmpl$","_$use","_$spread","_$mergeProps","data-entering","data-exiting","_$insert","_$runHydrationEvents","Modal","ModalContentWithAutoOverlay","overlayProps","modalProps","internalContext","ModalContent","modalRef","modalRefAccessor","parentState","registerModalRef","isModalEntering","includes","push","indexOf","isTopMostModal","html","documentElement","prevOverflow","overflow","ref","onInteractOutside","isDisabled","e","cleanup","cancelled","ownerWindow","ownerDocument","defaultView","window","hideOutside","shouldUseInert","queueMicrotask","contain","restoreFocus","_el$2","_tmpl$3","_el$4","firstChild","_el$5","_co$","_$getNextMarker","nextSibling","_el$6","_el$7","_co$2","elementType","_el$3","_tmpl$2","$$click","renderChildren","_$delegateEvents"],"sources":["../src/Modal.tsx"],"sourcesContent":["/**\n * Modal and ModalOverlay components for solidaria-components\n *\n * Headless modal components with overlay, focus trapping, and dismissal handling.\n * Port of react-aria-components Modal.\n */\n\nimport {\n type JSX,\n createContext,\n createMemo,\n createSignal,\n createEffect,\n onCleanup,\n splitProps,\n Show,\n useContext,\n} from \"solid-js\";\nimport { Portal, isServer } from \"solid-js/web\";\nimport {\n createInteractOutside,\n ariaHideOutside,\n FocusScope,\n useUNSAFE_PortalContext,\n createEnterAnimation,\n createExitAnimation,\n} from \"@proyecto-viviana/solidaria\";\nimport {\n type RenderChildren,\n type ClassNameOrFunction,\n type StyleOrFunction,\n useRenderProps,\n filterDOMProps,\n dataAttr,\n useIsHydrated,\n} from \"./utils\";\nimport {\n DialogTriggerContext,\n OverlayTriggerStateContext,\n type OverlayTriggerState,\n} from \"./contexts\";\nimport { VisuallyHidden } from \"./VisuallyHidden\";\n\n/**\n * Internal context to signal that Modal is wrapped in ModalOverlay.\n * When present, Modal should not create its own Portal.\n */\ninterface InternalModalContextValue {\n isDismissable?: boolean;\n isKeyboardDismissDisabled?: boolean;\n // Upstream's ModalOverlay owns the modal element's ref so it can watch the\n // modal surface's exit animation alongside the overlay's (both must finish\n // before the overlay unmounts). ModalContent registers its element here.\n setModalRef?: (element: HTMLElement | null) => void;\n // Combined exit flag (overlay OR modal still exiting). Both the overlay and\n // the modal surface carry `data-exiting` off this so they fade out together.\n isExiting?: () => boolean;\n}\n\nconst InternalModalContext = createContext<InternalModalContextValue | null>(null);\n\n// Stack of visible modals, used to ensure only the top-most modal dismisses on Escape/outside interaction.\nconst visibleModals: Array<() => Element | null> = [];\n\nfunction pruneDisconnectedModals() {\n for (let index = visibleModals.length - 1; index >= 0; index -= 1) {\n const element = visibleModals[index]?.();\n if (!element?.isConnected) {\n visibleModals.splice(index, 1);\n }\n }\n}\n\nexport interface ModalRenderProps {\n /** Whether the modal is currently entering (for animations). */\n isEntering: boolean;\n /** Whether the modal is currently exiting (for animations). */\n isExiting: boolean;\n}\n\nexport interface ModalOverlayProps {\n /** The children of the component - can be JSX or render function. */\n children?: RenderChildren<ModalRenderProps>;\n /** The CSS className for the element. */\n class?: ClassNameOrFunction<ModalRenderProps>;\n /** The inline style for the element. */\n style?: StyleOrFunction<ModalRenderProps>;\n /** Whether the modal is open (controlled). */\n isOpen?: boolean;\n /** Whether the modal opens by default (uncontrolled). */\n defaultOpen?: boolean;\n /** Handler called when the modal's open state changes. */\n onOpenChange?: (isOpen: boolean) => void;\n /** Whether clicking outside the modal closes it. */\n isDismissable?: boolean;\n /** Whether pressing Escape closes the modal. */\n isKeyboardDismissDisabled?: boolean;\n /** Whether the modal is entering (for animations). */\n isEntering?: boolean;\n /** Whether the modal is exiting (for animations). */\n isExiting?: boolean;\n}\n\nexport interface ModalProps extends ModalOverlayProps {}\n\nexport { OverlayTriggerStateContext, type OverlayTriggerState } from \"./contexts\";\nexport { useOverlayTriggerState } from \"./contexts\";\nexport const ModalContext = OverlayTriggerStateContext;\n\n/**\n * ModalOverlay is the backdrop/underlay behind a modal.\n * It handles click-outside dismissal and provides styling hooks.\n */\nexport function ModalOverlay(props: ModalOverlayProps): JSX.Element {\n // Do NOT early-return on the server: rendering children bare on the server and a\n // <Show>/<Portal> overlay on the client desyncs hydration. Run the same structure\n // on both and gate the Portal on useIsHydrated() (see Popover for the rationale).\n const isHydrated = useIsHydrated();\n\n // IMPORTANT: Don't destructure or access props.children early!\n // In SolidJS, children are lazily evaluated. Accessing them before\n // the context provider renders causes them to evaluate outside the context.\n // See: https://github.com/solidjs/solid/issues/182\n const [local, rest] = splitProps(props, [\n \"class\",\n \"style\",\n \"isOpen\",\n \"defaultOpen\",\n \"onOpenChange\",\n \"isDismissable\",\n \"isKeyboardDismissDisabled\",\n \"isEntering\",\n \"isExiting\",\n ]);\n\n // Get state from DialogTrigger context if available\n const dialogTriggerContext = useContext(DialogTriggerContext);\n\n // Internal state for uncontrolled mode\n const [internalOpen, setInternalOpen] = createSignal(local.defaultOpen ?? false);\n\n // Determine if open (controlled > DialogTrigger context > uncontrolled)\n const isOpen = (): boolean => {\n if (local.isOpen !== undefined) return local.isOpen;\n if (dialogTriggerContext) return dialogTriggerContext.state.isOpen();\n return internalOpen();\n };\n\n const close = () => {\n if (local.isOpen !== undefined) {\n local.onOpenChange?.(false);\n } else if (dialogTriggerContext) {\n dialogTriggerContext.state.close();\n local.onOpenChange?.(false);\n } else {\n setInternalOpen(false);\n local.onOpenChange?.(false);\n }\n };\n\n const open = () => {\n if (local.isOpen !== undefined) {\n local.onOpenChange?.(true);\n } else if (dialogTriggerContext) {\n dialogTriggerContext.state.open();\n local.onOpenChange?.(true);\n } else {\n setInternalOpen(true);\n local.onOpenChange?.(true);\n }\n };\n\n const toggle = () => {\n if (isOpen()) {\n close();\n } else {\n open();\n }\n };\n\n const state: OverlayTriggerState = {\n get isOpen() {\n return isOpen();\n },\n open,\n close,\n toggle,\n };\n\n // Enter/exit animation state (mirrors upstream's ModalOverlayWithForwardRef +\n // ModalOverlayInner). The overlay stays mounted here even while closed, so the\n // enter animation is gated on `isOpen` (isReady) — otherwise it would resolve\n // to \"done entering\" before the element ever mounts. Exit is watched for BOTH\n // the overlay and the modal surface; the modal registers its element via\n // InternalModalContext (setModalRef) so we can await its exit too.\n let overlayRef!: HTMLDivElement;\n // Signal-backed so the enter/exit effects re-run once the element mounts — a\n // plain closure over `overlayRef` would read `null` if the effect fired first\n // and, with no reactive dep on the ref, never recover (stuck `data-entering`).\n const [overlayEl, setOverlayEl] = createSignal<HTMLElement | null>(null);\n const overlayRefAccessor = () => overlayEl();\n const registerOverlayRef = (element: HTMLDivElement) => {\n overlayRef = element;\n setOverlayEl(element);\n };\n const [modalEl, setModalEl] = createSignal<HTMLElement | null>(null);\n\n const isOverlayEntering = createEnterAnimation(overlayRefAccessor, isOpen);\n const isOverlayExiting = createExitAnimation(overlayRefAccessor, isOpen);\n const isModalExiting = createExitAnimation(() => modalEl(), isOpen);\n const overlayEntering = () => isOverlayEntering() || (local.isEntering ?? false);\n const combinedExiting = () =>\n isOverlayExiting() || isModalExiting() || (local.isExiting ?? false);\n\n const renderValues = createMemo<ModalRenderProps>(() => ({\n isEntering: overlayEntering(),\n isExiting: combinedExiting(),\n }));\n\n // Resolve render props - don't pass children, we'll render props.children directly\n const renderProps = useRenderProps(\n {\n class: local.class,\n style: local.style,\n defaultClassName: \"solidaria-ModalOverlay\",\n },\n renderValues,\n );\n\n const domProps = createMemo(() =>\n filterDOMProps(rest as Record<string, unknown>, { global: true }),\n );\n\n // Internal context value to signal Modal that it's wrapped\n const internalModalContext: InternalModalContextValue = {\n isDismissable: local.isDismissable,\n isKeyboardDismissDisabled: local.isKeyboardDismissDisabled,\n setModalRef: setModalEl,\n isExiting: combinedExiting,\n };\n const portalContext = useUNSAFE_PortalContext();\n const portalContainer = () => portalContext.getContainer?.() ?? undefined;\n\n const isTopMostModalInOverlay = () => {\n pruneDisconnectedModals();\n const topMostModal = visibleModals[visibleModals.length - 1]?.();\n return !topMostModal || overlayRef?.contains(topMostModal);\n };\n\n const handleOverlayPointerDown: JSX.EventHandler<HTMLDivElement, PointerEvent> = (event) => {\n if (local.isDismissable && event.target === event.currentTarget && isTopMostModalInOverlay()) {\n close();\n }\n };\n\n createEffect(() => {\n if (!isOpen() || local.isKeyboardDismissDisabled) return;\n\n const handleKeyDown = (event: KeyboardEvent) => {\n if (event.key === \"Escape\" && !event.isComposing && isTopMostModalInOverlay()) {\n event.preventDefault();\n event.stopPropagation();\n close();\n }\n };\n\n // Bubble phase, mirroring react-aria's `useOverlay` (its `onKeyDown` is an\n // overlay-element prop, handled on bubble). Capture would preempt inner\n // widgets that dismiss on Escape (a combobox/menu inside the dialog must\n // consume Escape first) and would also fire before the event is recorded by\n // capture-phase listeners, inverting the keydown/onOpenChange order.\n document.addEventListener(\"keydown\", handleKeyDown, false);\n onCleanup(() => {\n document.removeEventListener(\"keydown\", handleKeyDown, false);\n });\n });\n\n // Resolve children - handle both static JSX and render functions\n // IMPORTANT: We access props.children directly (not local.children) to preserve\n // lazy evaluation inside context providers\n const resolveChildren = () => {\n const children = props.children;\n if (typeof children === \"function\") {\n return (children as (props: ModalRenderProps) => JSX.Element)(renderValues());\n }\n return children;\n };\n\n return (\n <Show when={isHydrated() && (isOpen() || combinedExiting())}>\n <Portal mount={portalContainer()}>\n <OverlayTriggerStateContext.Provider value={state}>\n <InternalModalContext.Provider value={internalModalContext}>\n <div\n {...domProps()}\n ref={registerOverlayRef}\n class={renderProps.class()}\n style={renderProps.style()}\n data-entering={dataAttr(overlayEntering())}\n data-exiting={dataAttr(combinedExiting())}\n onPointerDown={handleOverlayPointerDown}\n >\n {resolveChildren()}\n </div>\n </InternalModalContext.Provider>\n </OverlayTriggerStateContext.Provider>\n </Portal>\n </Show>\n );\n}\n\n/**\n * Modal is a dialog container that manages focus trapping, scroll prevention,\n * aria-hiding of content outside, and dismissal.\n *\n * Usage patterns:\n * 1. Standalone: `<Modal isOpen>...</Modal>` - Creates its own overlay\n * 2. With custom overlay: `<ModalOverlay><Modal>...</Modal></ModalOverlay>`\n *\n * Note: Due to SolidJS's eager JSX evaluation, we cannot detect at render time\n * whether Modal is wrapped in ModalOverlay. So standalone Modal always creates\n * an overlay, and wrapped Modal renders directly (relying on InternalModalContext).\n */\nexport function Modal(props: ModalProps): JSX.Element {\n // Check for InternalModalContext which signals we're inside a rendered ModalOverlay\n // This works because ModalContent is rendered INSIDE ModalOverlay's Show/Portal\n return <ModalContentWithAutoOverlay {...props} />;\n}\n\n/**\n * Helper component that handles the overlay detection.\n * By being a separate component, we can use Show to defer rendering until\n * the parent context is available.\n */\nfunction ModalContentWithAutoOverlay(props: ModalProps): JSX.Element {\n const [overlayProps, modalProps] = splitProps(props, [\n \"isOpen\",\n \"defaultOpen\",\n \"onOpenChange\",\n \"isDismissable\",\n \"isKeyboardDismissDisabled\",\n \"isEntering\",\n \"isExiting\",\n ]);\n\n // Check for InternalModalContext - if present, we're inside a ModalOverlay\n const internalContext = useContext(InternalModalContext);\n\n // If wrapped in ModalOverlay, just render the content. ModalContent reads the\n // overlay's InternalModalContext itself (via useContext), so nothing needs\n // threading — in both branches ModalContent renders inside a provider.\n if (internalContext) {\n return <ModalContent {...modalProps}>{props.children}</ModalContent>;\n }\n\n // For standalone usage, wrap in ModalOverlay.\n return (\n <ModalOverlay {...overlayProps}>\n <ModalContent {...modalProps}>{props.children}</ModalContent>\n </ModalOverlay>\n );\n}\n\n/**\n * Internal component that renders the actual modal content.\n * Used by both standalone Modal and Modal wrapped in ModalOverlay.\n */\nfunction ModalContent(props: ModalProps): JSX.Element {\n if (isServer) {\n return <>{props.children}</>;\n }\n\n const [local, rest] = splitProps(props, [\n \"children\",\n \"class\",\n \"style\",\n \"isOpen\",\n \"defaultOpen\",\n \"onOpenChange\",\n \"isDismissable\",\n \"isKeyboardDismissDisabled\",\n \"isEntering\",\n \"isExiting\",\n ]);\n\n let modalRef!: HTMLDivElement;\n const modalRefAccessor = () => modalRef ?? null;\n // Signal-backed element for the enter animation, so the effect re-runs when\n // the surface mounts (see the overlay's note); the plain `modalRef` var still\n // backs the synchronous consumers (focus stack, interact-outside, aria-hide).\n const [modalEl, setModalEl] = createSignal<HTMLElement | null>(null);\n\n // Get state from parent OverlayTriggerStateContext (provided by ModalOverlay)\n const parentState = useContext(OverlayTriggerStateContext);\n\n // The overlay (auto or user-provided) always wraps this content, so it owns\n // the InternalModalContext: dismissal settings, the modal-ref registration\n // sink, and the combined exit flag.\n const internalContext = useContext(InternalModalContext);\n\n // Get dismissable settings from internal context (set by ModalOverlay)\n const isDismissable = () => internalContext?.isDismissable ?? local.isDismissable;\n const isKeyboardDismissDisabled = () =>\n internalContext?.isKeyboardDismissDisabled ?? local.isKeyboardDismissDisabled;\n\n // Determine if open from parent state\n const isOpen = (): boolean => {\n if (local.isOpen !== undefined) return local.isOpen;\n return parentState?.isOpen ?? false;\n };\n\n // Register this element with the overlay so it can await the modal surface's\n // exit animation (upstream threads modalRef down the same way). The overlay's\n // combined exit flag drives this surface's `data-exiting`. The enter animation\n // is local — the modal fades/slides in on its own timeline (delay 160 vs the\n // overlay's 0).\n const registerModalRef = (element: HTMLDivElement) => {\n modalRef = element;\n setModalEl(element);\n internalContext?.setModalRef?.(element);\n };\n const isModalEntering = createEnterAnimation(() => modalEl(), isOpen);\n const isModalExiting = () => internalContext?.isExiting?.() ?? local.isExiting ?? false;\n\n // Keep this modal in a global stack so nested modals dismiss in top-down order.\n createEffect(() => {\n if (!isOpen()) return;\n\n pruneDisconnectedModals();\n if (!visibleModals.includes(modalRefAccessor)) {\n visibleModals.push(modalRefAccessor);\n }\n\n onCleanup(() => {\n const index = visibleModals.indexOf(modalRefAccessor);\n if (index >= 0) {\n visibleModals.splice(index, 1);\n }\n });\n });\n\n const isTopMostModal = () => {\n pruneDisconnectedModals();\n return visibleModals[visibleModals.length - 1] === modalRefAccessor;\n };\n\n const close = () => {\n if (local.isOpen !== undefined) {\n local.onOpenChange?.(false);\n } else {\n parentState?.close();\n }\n };\n\n // Prevent scroll when modal is open\n createEffect(() => {\n if (!isOpen()) return;\n\n const html = document.documentElement;\n const prevOverflow = html.style.overflow;\n html.style.overflow = \"hidden\";\n\n onCleanup(() => {\n html.style.overflow = prevOverflow;\n });\n });\n\n // Click outside to close (if dismissable)\n createEffect(() => {\n if (!isOpen() || !isDismissable()) return;\n\n createInteractOutside({\n ref: modalRefAccessor,\n onInteractOutside: () => {\n if (isTopMostModal()) {\n close();\n }\n },\n isDisabled: false,\n });\n });\n\n // Escape key to close\n createEffect(() => {\n if (!isOpen() || isKeyboardDismissDisabled()) return;\n\n const handleKeyDown = (e: KeyboardEvent) => {\n if (e.key === \"Escape\" && !e.isComposing && isTopMostModal()) {\n e.preventDefault();\n e.stopPropagation();\n close();\n }\n };\n\n // Bubble phase — see the ModalOverlay handler above for why (react-aria\n // `useOverlay` parity; inner-widget Escape precedence; event-order fidelity).\n document.addEventListener(\"keydown\", handleKeyDown, false);\n onCleanup(() => {\n document.removeEventListener(\"keydown\", handleKeyDown, false);\n });\n });\n\n // Aria-hide outside content\n createEffect(() => {\n if (!isOpen() || !modalRef) return;\n\n let cleanup: (() => void) | undefined;\n let cancelled = false;\n const ownerWindow = modalRef.ownerDocument.defaultView ?? window;\n\n const hideOutside = () => {\n if (cancelled || !modalRef?.isConnected) return;\n cleanup = ariaHideOutside([modalRef], { shouldUseInert: true });\n };\n\n if (modalRef.isConnected) {\n hideOutside();\n } else {\n ownerWindow.queueMicrotask(hideOutside);\n }\n\n onCleanup(() => {\n cancelled = true;\n cleanup?.();\n });\n });\n\n const renderValues = createMemo<ModalRenderProps>(() => ({\n isEntering: isModalEntering(),\n isExiting: isModalExiting(),\n }));\n\n const renderProps = useRenderProps(\n {\n // Lazy: the modal content is gated behind `<Show when={isHydrated() && …}>`\n // (Portal) below, so it must not be instantiated during the component body.\n // An eager `children: props.children` would build the content template (and\n // walk getNextElement) before the gate — content the server never emitted →\n // hydration mismatch. See Popover for the full rationale.\n get children() {\n return props.children;\n },\n class: local.class,\n style: local.style,\n defaultClassName: \"solidaria-Modal\",\n },\n renderValues,\n );\n\n const domProps = createMemo(() =>\n filterDOMProps(rest as Record<string, unknown>, { global: true }),\n );\n\n return (\n // No autoFocus: react-aria's Overlay renders FocusScope with restoreFocus +\n // contain only — initial focus is useDialog's job (it focuses the dialog\n // element itself when nothing inside holds focus).\n //\n // `contain` is gated on `!isExiting`, mirroring react-aria's Overlay\n // (react-aria/src/overlays/Overlay.tsx: `contain={... && !isExiting}`). The\n // overlay stays mounted through its exit transition (isOpen || isExiting), so\n // without this gate containment would remain active during the exit and yank\n // focus back into the dialog as restoreFocus moves it to the trigger —\n // producing a focus bounce (focusout→focusin→focusout) that React never\n // shows. Releasing containment the instant the exit begins lets focus settle\n // on the trigger in a single move, matching upstream's event sequence.\n <FocusScope contain={!isModalExiting()} restoreFocus>\n <div\n {...domProps()}\n ref={registerModalRef}\n class={renderProps.class()}\n style={renderProps.style()}\n data-entering={dataAttr(isModalEntering())}\n data-exiting={dataAttr(isModalExiting())}\n >\n <Show when={isDismissable()}>\n {/* Faithful to react-aria's DismissButton: a bare button carrying\n only `width/height: 1px` inside a VisuallyHidden `div` wrapper —\n NOT the hidden styles inlined onto the button. The wrapper clips\n it out of sight while the button keeps its intrinsic UA border-box,\n which is exactly what upstream renders (and what D8 target-size\n measures against the pair oracle). Inlining the reset onto the\n button instead collapsed it to a 1x1 box, diverging from\n upstream's ~16x6 sentinel. */}\n <VisuallyHidden elementType=\"div\">\n <button\n aria-label=\"Dismiss\"\n tabIndex={-1}\n onClick={close}\n style={{ width: \"1px\", height: \"1px\" }}\n />\n </VisuallyHidden>\n </Show>\n {renderProps.renderChildren()}\n </div>\n </FocusScope>\n );\n}\n\nexport default Modal;\n"],"mappings":"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"}
|
package/dist/Modal.jsx
CHANGED
|
@@ -121,9 +121,9 @@ function ModalOverlay(props) {
|
|
|
121
121
|
close();
|
|
122
122
|
}
|
|
123
123
|
};
|
|
124
|
-
document.addEventListener("keydown", handleKeyDown,
|
|
124
|
+
document.addEventListener("keydown", handleKeyDown, false);
|
|
125
125
|
onCleanup(() => {
|
|
126
|
-
document.removeEventListener("keydown", handleKeyDown,
|
|
126
|
+
document.removeEventListener("keydown", handleKeyDown, false);
|
|
127
127
|
});
|
|
128
128
|
});
|
|
129
129
|
const resolveChildren = () => {
|
|
@@ -259,9 +259,9 @@ function ModalContent(props) {
|
|
|
259
259
|
close();
|
|
260
260
|
}
|
|
261
261
|
};
|
|
262
|
-
document.addEventListener("keydown", handleKeyDown,
|
|
262
|
+
document.addEventListener("keydown", handleKeyDown, false);
|
|
263
263
|
onCleanup(() => {
|
|
264
|
-
document.removeEventListener("keydown", handleKeyDown,
|
|
264
|
+
document.removeEventListener("keydown", handleKeyDown, false);
|
|
265
265
|
});
|
|
266
266
|
});
|
|
267
267
|
createEffect(() => {
|
|
@@ -293,7 +293,7 @@ function ModalContent(props) {
|
|
|
293
293
|
defaultClassName: "solidaria-Modal"
|
|
294
294
|
}, renderValues);
|
|
295
295
|
const domProps = createMemo(() => filterDOMProps(rest, { global: true }));
|
|
296
|
-
return <FocusScope contain restoreFocus>
|
|
296
|
+
return <FocusScope contain={!isModalExiting()} restoreFocus>
|
|
297
297
|
<div {...domProps()} ref={registerModalRef} class={renderProps.class()} style={renderProps.style()} data-entering={dataAttr(isModalEntering())} data-exiting={dataAttr(isModalExiting())}>
|
|
298
298
|
<Show when={isDismissable()}>
|
|
299
299
|
{}
|
package/dist/Modal.jsx.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Modal.jsx","names":[],"sources":["../src/Modal.tsx"],"sourcesContent":["/**\n * Modal and ModalOverlay components for solidaria-components\n *\n * Headless modal components with overlay, focus trapping, and dismissal handling.\n * Port of react-aria-components Modal.\n */\n\nimport {\n type JSX,\n createContext,\n createMemo,\n createSignal,\n createEffect,\n onCleanup,\n splitProps,\n Show,\n useContext,\n} from \"solid-js\";\nimport { Portal, isServer } from \"solid-js/web\";\nimport {\n createInteractOutside,\n ariaHideOutside,\n FocusScope,\n useUNSAFE_PortalContext,\n createEnterAnimation,\n createExitAnimation,\n} from \"@proyecto-viviana/solidaria\";\nimport {\n type RenderChildren,\n type ClassNameOrFunction,\n type StyleOrFunction,\n useRenderProps,\n filterDOMProps,\n dataAttr,\n useIsHydrated,\n} from \"./utils\";\nimport {\n DialogTriggerContext,\n OverlayTriggerStateContext,\n type OverlayTriggerState,\n} from \"./contexts\";\nimport { VisuallyHidden } from \"./VisuallyHidden\";\n\n/**\n * Internal context to signal that Modal is wrapped in ModalOverlay.\n * When present, Modal should not create its own Portal.\n */\ninterface InternalModalContextValue {\n isDismissable?: boolean;\n isKeyboardDismissDisabled?: boolean;\n // Upstream's ModalOverlay owns the modal element's ref so it can watch the\n // modal surface's exit animation alongside the overlay's (both must finish\n // before the overlay unmounts). ModalContent registers its element here.\n setModalRef?: (element: HTMLElement | null) => void;\n // Combined exit flag (overlay OR modal still exiting). Both the overlay and\n // the modal surface carry `data-exiting` off this so they fade out together.\n isExiting?: () => boolean;\n}\n\nconst InternalModalContext = createContext<InternalModalContextValue | null>(null);\n\n// Stack of visible modals, used to ensure only the top-most modal dismisses on Escape/outside interaction.\nconst visibleModals: Array<() => Element | null> = [];\n\nfunction pruneDisconnectedModals() {\n for (let index = visibleModals.length - 1; index >= 0; index -= 1) {\n const element = visibleModals[index]?.();\n if (!element?.isConnected) {\n visibleModals.splice(index, 1);\n }\n }\n}\n\nexport interface ModalRenderProps {\n /** Whether the modal is currently entering (for animations). */\n isEntering: boolean;\n /** Whether the modal is currently exiting (for animations). */\n isExiting: boolean;\n}\n\nexport interface ModalOverlayProps {\n /** The children of the component - can be JSX or render function. */\n children?: RenderChildren<ModalRenderProps>;\n /** The CSS className for the element. */\n class?: ClassNameOrFunction<ModalRenderProps>;\n /** The inline style for the element. */\n style?: StyleOrFunction<ModalRenderProps>;\n /** Whether the modal is open (controlled). */\n isOpen?: boolean;\n /** Whether the modal opens by default (uncontrolled). */\n defaultOpen?: boolean;\n /** Handler called when the modal's open state changes. */\n onOpenChange?: (isOpen: boolean) => void;\n /** Whether clicking outside the modal closes it. */\n isDismissable?: boolean;\n /** Whether pressing Escape closes the modal. */\n isKeyboardDismissDisabled?: boolean;\n /** Whether the modal is entering (for animations). */\n isEntering?: boolean;\n /** Whether the modal is exiting (for animations). */\n isExiting?: boolean;\n}\n\nexport interface ModalProps extends ModalOverlayProps {}\n\nexport { OverlayTriggerStateContext, type OverlayTriggerState } from \"./contexts\";\nexport { useOverlayTriggerState } from \"./contexts\";\nexport const ModalContext = OverlayTriggerStateContext;\n\n/**\n * ModalOverlay is the backdrop/underlay behind a modal.\n * It handles click-outside dismissal and provides styling hooks.\n */\nexport function ModalOverlay(props: ModalOverlayProps): JSX.Element {\n // Do NOT early-return on the server: rendering children bare on the server and a\n // <Show>/<Portal> overlay on the client desyncs hydration. Run the same structure\n // on both and gate the Portal on useIsHydrated() (see Popover for the rationale).\n const isHydrated = useIsHydrated();\n\n // IMPORTANT: Don't destructure or access props.children early!\n // In SolidJS, children are lazily evaluated. Accessing them before\n // the context provider renders causes them to evaluate outside the context.\n // See: https://github.com/solidjs/solid/issues/182\n const [local, rest] = splitProps(props, [\n \"class\",\n \"style\",\n \"isOpen\",\n \"defaultOpen\",\n \"onOpenChange\",\n \"isDismissable\",\n \"isKeyboardDismissDisabled\",\n \"isEntering\",\n \"isExiting\",\n ]);\n\n // Get state from DialogTrigger context if available\n const dialogTriggerContext = useContext(DialogTriggerContext);\n\n // Internal state for uncontrolled mode\n const [internalOpen, setInternalOpen] = createSignal(local.defaultOpen ?? false);\n\n // Determine if open (controlled > DialogTrigger context > uncontrolled)\n const isOpen = (): boolean => {\n if (local.isOpen !== undefined) return local.isOpen;\n if (dialogTriggerContext) return dialogTriggerContext.state.isOpen();\n return internalOpen();\n };\n\n const close = () => {\n if (local.isOpen !== undefined) {\n local.onOpenChange?.(false);\n } else if (dialogTriggerContext) {\n dialogTriggerContext.state.close();\n local.onOpenChange?.(false);\n } else {\n setInternalOpen(false);\n local.onOpenChange?.(false);\n }\n };\n\n const open = () => {\n if (local.isOpen !== undefined) {\n local.onOpenChange?.(true);\n } else if (dialogTriggerContext) {\n dialogTriggerContext.state.open();\n local.onOpenChange?.(true);\n } else {\n setInternalOpen(true);\n local.onOpenChange?.(true);\n }\n };\n\n const toggle = () => {\n if (isOpen()) {\n close();\n } else {\n open();\n }\n };\n\n const state: OverlayTriggerState = {\n get isOpen() {\n return isOpen();\n },\n open,\n close,\n toggle,\n };\n\n // Enter/exit animation state (mirrors upstream's ModalOverlayWithForwardRef +\n // ModalOverlayInner). The overlay stays mounted here even while closed, so the\n // enter animation is gated on `isOpen` (isReady) — otherwise it would resolve\n // to \"done entering\" before the element ever mounts. Exit is watched for BOTH\n // the overlay and the modal surface; the modal registers its element via\n // InternalModalContext (setModalRef) so we can await its exit too.\n let overlayRef!: HTMLDivElement;\n // Signal-backed so the enter/exit effects re-run once the element mounts — a\n // plain closure over `overlayRef` would read `null` if the effect fired first\n // and, with no reactive dep on the ref, never recover (stuck `data-entering`).\n const [overlayEl, setOverlayEl] = createSignal<HTMLElement | null>(null);\n const overlayRefAccessor = () => overlayEl();\n const registerOverlayRef = (element: HTMLDivElement) => {\n overlayRef = element;\n setOverlayEl(element);\n };\n const [modalEl, setModalEl] = createSignal<HTMLElement | null>(null);\n\n const isOverlayEntering = createEnterAnimation(overlayRefAccessor, isOpen);\n const isOverlayExiting = createExitAnimation(overlayRefAccessor, isOpen);\n const isModalExiting = createExitAnimation(() => modalEl(), isOpen);\n const overlayEntering = () => isOverlayEntering() || (local.isEntering ?? false);\n const combinedExiting = () =>\n isOverlayExiting() || isModalExiting() || (local.isExiting ?? false);\n\n const renderValues = createMemo<ModalRenderProps>(() => ({\n isEntering: overlayEntering(),\n isExiting: combinedExiting(),\n }));\n\n // Resolve render props - don't pass children, we'll render props.children directly\n const renderProps = useRenderProps(\n {\n class: local.class,\n style: local.style,\n defaultClassName: \"solidaria-ModalOverlay\",\n },\n renderValues,\n );\n\n const domProps = createMemo(() =>\n filterDOMProps(rest as Record<string, unknown>, { global: true }),\n );\n\n // Internal context value to signal Modal that it's wrapped\n const internalModalContext: InternalModalContextValue = {\n isDismissable: local.isDismissable,\n isKeyboardDismissDisabled: local.isKeyboardDismissDisabled,\n setModalRef: setModalEl,\n isExiting: combinedExiting,\n };\n const portalContext = useUNSAFE_PortalContext();\n const portalContainer = () => portalContext.getContainer?.() ?? undefined;\n\n const isTopMostModalInOverlay = () => {\n pruneDisconnectedModals();\n const topMostModal = visibleModals[visibleModals.length - 1]?.();\n return !topMostModal || overlayRef?.contains(topMostModal);\n };\n\n const handleOverlayPointerDown: JSX.EventHandler<HTMLDivElement, PointerEvent> = (event) => {\n if (local.isDismissable && event.target === event.currentTarget && isTopMostModalInOverlay()) {\n close();\n }\n };\n\n createEffect(() => {\n if (!isOpen() || local.isKeyboardDismissDisabled) return;\n\n const handleKeyDown = (event: KeyboardEvent) => {\n if (event.key === \"Escape\" && !event.isComposing && isTopMostModalInOverlay()) {\n event.preventDefault();\n event.stopPropagation();\n close();\n }\n };\n\n document.addEventListener(\"keydown\", handleKeyDown, true);\n onCleanup(() => {\n document.removeEventListener(\"keydown\", handleKeyDown, true);\n });\n });\n\n // Resolve children - handle both static JSX and render functions\n // IMPORTANT: We access props.children directly (not local.children) to preserve\n // lazy evaluation inside context providers\n const resolveChildren = () => {\n const children = props.children;\n if (typeof children === \"function\") {\n return (children as (props: ModalRenderProps) => JSX.Element)(renderValues());\n }\n return children;\n };\n\n return (\n <Show when={isHydrated() && (isOpen() || combinedExiting())}>\n <Portal mount={portalContainer()}>\n <OverlayTriggerStateContext.Provider value={state}>\n <InternalModalContext.Provider value={internalModalContext}>\n <div\n {...domProps()}\n ref={registerOverlayRef}\n class={renderProps.class()}\n style={renderProps.style()}\n data-entering={dataAttr(overlayEntering())}\n data-exiting={dataAttr(combinedExiting())}\n onPointerDown={handleOverlayPointerDown}\n >\n {resolveChildren()}\n </div>\n </InternalModalContext.Provider>\n </OverlayTriggerStateContext.Provider>\n </Portal>\n </Show>\n );\n}\n\n/**\n * Modal is a dialog container that manages focus trapping, scroll prevention,\n * aria-hiding of content outside, and dismissal.\n *\n * Usage patterns:\n * 1. Standalone: `<Modal isOpen>...</Modal>` - Creates its own overlay\n * 2. With custom overlay: `<ModalOverlay><Modal>...</Modal></ModalOverlay>`\n *\n * Note: Due to SolidJS's eager JSX evaluation, we cannot detect at render time\n * whether Modal is wrapped in ModalOverlay. So standalone Modal always creates\n * an overlay, and wrapped Modal renders directly (relying on InternalModalContext).\n */\nexport function Modal(props: ModalProps): JSX.Element {\n // Check for InternalModalContext which signals we're inside a rendered ModalOverlay\n // This works because ModalContent is rendered INSIDE ModalOverlay's Show/Portal\n return <ModalContentWithAutoOverlay {...props} />;\n}\n\n/**\n * Helper component that handles the overlay detection.\n * By being a separate component, we can use Show to defer rendering until\n * the parent context is available.\n */\nfunction ModalContentWithAutoOverlay(props: ModalProps): JSX.Element {\n const [overlayProps, modalProps] = splitProps(props, [\n \"isOpen\",\n \"defaultOpen\",\n \"onOpenChange\",\n \"isDismissable\",\n \"isKeyboardDismissDisabled\",\n \"isEntering\",\n \"isExiting\",\n ]);\n\n // Check for InternalModalContext - if present, we're inside a ModalOverlay\n const internalContext = useContext(InternalModalContext);\n\n // If wrapped in ModalOverlay, just render the content. ModalContent reads the\n // overlay's InternalModalContext itself (via useContext), so nothing needs\n // threading — in both branches ModalContent renders inside a provider.\n if (internalContext) {\n return <ModalContent {...modalProps}>{props.children}</ModalContent>;\n }\n\n // For standalone usage, wrap in ModalOverlay.\n return (\n <ModalOverlay {...overlayProps}>\n <ModalContent {...modalProps}>{props.children}</ModalContent>\n </ModalOverlay>\n );\n}\n\n/**\n * Internal component that renders the actual modal content.\n * Used by both standalone Modal and Modal wrapped in ModalOverlay.\n */\nfunction ModalContent(props: ModalProps): JSX.Element {\n if (isServer) {\n return <>{props.children}</>;\n }\n\n const [local, rest] = splitProps(props, [\n \"children\",\n \"class\",\n \"style\",\n \"isOpen\",\n \"defaultOpen\",\n \"onOpenChange\",\n \"isDismissable\",\n \"isKeyboardDismissDisabled\",\n \"isEntering\",\n \"isExiting\",\n ]);\n\n let modalRef!: HTMLDivElement;\n const modalRefAccessor = () => modalRef ?? null;\n // Signal-backed element for the enter animation, so the effect re-runs when\n // the surface mounts (see the overlay's note); the plain `modalRef` var still\n // backs the synchronous consumers (focus stack, interact-outside, aria-hide).\n const [modalEl, setModalEl] = createSignal<HTMLElement | null>(null);\n\n // Get state from parent OverlayTriggerStateContext (provided by ModalOverlay)\n const parentState = useContext(OverlayTriggerStateContext);\n\n // The overlay (auto or user-provided) always wraps this content, so it owns\n // the InternalModalContext: dismissal settings, the modal-ref registration\n // sink, and the combined exit flag.\n const internalContext = useContext(InternalModalContext);\n\n // Get dismissable settings from internal context (set by ModalOverlay)\n const isDismissable = () => internalContext?.isDismissable ?? local.isDismissable;\n const isKeyboardDismissDisabled = () =>\n internalContext?.isKeyboardDismissDisabled ?? local.isKeyboardDismissDisabled;\n\n // Determine if open from parent state\n const isOpen = (): boolean => {\n if (local.isOpen !== undefined) return local.isOpen;\n return parentState?.isOpen ?? false;\n };\n\n // Register this element with the overlay so it can await the modal surface's\n // exit animation (upstream threads modalRef down the same way). The overlay's\n // combined exit flag drives this surface's `data-exiting`. The enter animation\n // is local — the modal fades/slides in on its own timeline (delay 160 vs the\n // overlay's 0).\n const registerModalRef = (element: HTMLDivElement) => {\n modalRef = element;\n setModalEl(element);\n internalContext?.setModalRef?.(element);\n };\n const isModalEntering = createEnterAnimation(() => modalEl(), isOpen);\n const isModalExiting = () => internalContext?.isExiting?.() ?? local.isExiting ?? false;\n\n // Keep this modal in a global stack so nested modals dismiss in top-down order.\n createEffect(() => {\n if (!isOpen()) return;\n\n pruneDisconnectedModals();\n if (!visibleModals.includes(modalRefAccessor)) {\n visibleModals.push(modalRefAccessor);\n }\n\n onCleanup(() => {\n const index = visibleModals.indexOf(modalRefAccessor);\n if (index >= 0) {\n visibleModals.splice(index, 1);\n }\n });\n });\n\n const isTopMostModal = () => {\n pruneDisconnectedModals();\n return visibleModals[visibleModals.length - 1] === modalRefAccessor;\n };\n\n const close = () => {\n if (local.isOpen !== undefined) {\n local.onOpenChange?.(false);\n } else {\n parentState?.close();\n }\n };\n\n // Prevent scroll when modal is open\n createEffect(() => {\n if (!isOpen()) return;\n\n const html = document.documentElement;\n const prevOverflow = html.style.overflow;\n html.style.overflow = \"hidden\";\n\n onCleanup(() => {\n html.style.overflow = prevOverflow;\n });\n });\n\n // Click outside to close (if dismissable)\n createEffect(() => {\n if (!isOpen() || !isDismissable()) return;\n\n createInteractOutside({\n ref: modalRefAccessor,\n onInteractOutside: () => {\n if (isTopMostModal()) {\n close();\n }\n },\n isDisabled: false,\n });\n });\n\n // Escape key to close\n createEffect(() => {\n if (!isOpen() || isKeyboardDismissDisabled()) return;\n\n const handleKeyDown = (e: KeyboardEvent) => {\n if (e.key === \"Escape\" && !e.isComposing && isTopMostModal()) {\n e.preventDefault();\n e.stopPropagation();\n close();\n }\n };\n\n document.addEventListener(\"keydown\", handleKeyDown, true);\n onCleanup(() => {\n document.removeEventListener(\"keydown\", handleKeyDown, true);\n });\n });\n\n // Aria-hide outside content\n createEffect(() => {\n if (!isOpen() || !modalRef) return;\n\n let cleanup: (() => void) | undefined;\n let cancelled = false;\n const ownerWindow = modalRef.ownerDocument.defaultView ?? window;\n\n const hideOutside = () => {\n if (cancelled || !modalRef?.isConnected) return;\n cleanup = ariaHideOutside([modalRef], { shouldUseInert: true });\n };\n\n if (modalRef.isConnected) {\n hideOutside();\n } else {\n ownerWindow.queueMicrotask(hideOutside);\n }\n\n onCleanup(() => {\n cancelled = true;\n cleanup?.();\n });\n });\n\n const renderValues = createMemo<ModalRenderProps>(() => ({\n isEntering: isModalEntering(),\n isExiting: isModalExiting(),\n }));\n\n const renderProps = useRenderProps(\n {\n // Lazy: the modal content is gated behind `<Show when={isHydrated() && …}>`\n // (Portal) below, so it must not be instantiated during the component body.\n // An eager `children: props.children` would build the content template (and\n // walk getNextElement) before the gate — content the server never emitted →\n // hydration mismatch. See Popover for the full rationale.\n get children() {\n return props.children;\n },\n class: local.class,\n style: local.style,\n defaultClassName: \"solidaria-Modal\",\n },\n renderValues,\n );\n\n const domProps = createMemo(() =>\n filterDOMProps(rest as Record<string, unknown>, { global: true }),\n );\n\n return (\n // No autoFocus: react-aria's Overlay renders FocusScope with restoreFocus +\n // contain only — initial focus is useDialog's job (it focuses the dialog\n // element itself when nothing inside holds focus).\n <FocusScope contain restoreFocus>\n <div\n {...domProps()}\n ref={registerModalRef}\n class={renderProps.class()}\n style={renderProps.style()}\n data-entering={dataAttr(isModalEntering())}\n data-exiting={dataAttr(isModalExiting())}\n >\n <Show when={isDismissable()}>\n {/* Faithful to react-aria's DismissButton: a bare button carrying\n only `width/height: 1px` inside a VisuallyHidden `div` wrapper —\n NOT the hidden styles inlined onto the button. The wrapper clips\n it out of sight while the button keeps its intrinsic UA border-box,\n which is exactly what upstream renders (and what D8 target-size\n measures against the pair oracle). Inlining the reset onto the\n button instead collapsed it to a 1x1 box, diverging from\n upstream's ~16x6 sentinel. */}\n <VisuallyHidden elementType=\"div\">\n <button\n aria-label=\"Dismiss\"\n tabIndex={-1}\n onClick={close}\n style={{ width: \"1px\", height: \"1px\" }}\n />\n </VisuallyHidden>\n </Show>\n {renderProps.renderChildren()}\n </div>\n </FocusScope>\n );\n}\n\nexport default Modal;\n"],"mappings":";;;;;;;;;;;;;AA2DA,MAAM,uBAAuB,cAAgD,IAAI;AAGjF,MAAM,gBAA6C,CAAC;AAEpD,SAAS,0BAA0B;CACjC,KAAK,IAAI,QAAQ,cAAc,SAAS,GAAG,SAAS,GAAG,SAAS,GAE9D,IAAI,EADY,cAAc,MAAM,GAAG,EAAA,EACzB,aACZ,cAAc,OAAO,OAAO,CAAC;AAGnC;AAoCA,MAAa,eAAe;;;;;AAM5B,SAAgB,aAAa,OAAuC;CAIlE,MAAM,aAAa,cAAc;CAMjC,MAAM,CAAC,OAAO,QAAQ,WAAW,OAAO;EACtC;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;CACF,CAAC;CAGD,MAAM,uBAAuB,WAAW,oBAAoB;CAG5D,MAAM,CAAC,cAAc,mBAAmB,aAAa,MAAM,eAAe,KAAK;CAG/E,MAAM,eAAwB;EAC5B,IAAI,MAAM,WAAW,KAAA,GAAW,OAAO,MAAM;EAC7C,IAAI,sBAAsB,OAAO,qBAAqB,MAAM,OAAO;EACnE,OAAO,aAAa;CACtB;CAEA,MAAM,cAAc;EAClB,IAAI,MAAM,WAAW,KAAA,GACnB,MAAM,eAAe,KAAK;OACrB,IAAI,sBAAsB;GAC/B,qBAAqB,MAAM,MAAM;GACjC,MAAM,eAAe,KAAK;EAC5B,OAAO;GACL,gBAAgB,KAAK;GACrB,MAAM,eAAe,KAAK;EAC5B;CACF;CAEA,MAAM,aAAa;EACjB,IAAI,MAAM,WAAW,KAAA,GACnB,MAAM,eAAe,IAAI;OACpB,IAAI,sBAAsB;GAC/B,qBAAqB,MAAM,KAAK;GAChC,MAAM,eAAe,IAAI;EAC3B,OAAO;GACL,gBAAgB,IAAI;GACpB,MAAM,eAAe,IAAI;EAC3B;CACF;CAEA,MAAM,eAAe;EACnB,IAAI,OAAO,GACT,MAAM;OAEN,KAAK;CAET;CAEA,MAAM,QAA6B;EACjC,IAAI,SAAS;GACX,OAAO,OAAO;EAChB;EACA;EACA;EACA;CACF;CAQA,IAAI;CAIJ,MAAM,CAAC,WAAW,gBAAgB,aAAiC,IAAI;CACvE,MAAM,2BAA2B,UAAU;CAC3C,MAAM,sBAAsB,YAA4B;EACtD,aAAa;EACb,aAAa,OAAO;CACtB;CACA,MAAM,CAAC,SAAS,cAAc,aAAiC,IAAI;CAEnE,MAAM,oBAAoB,qBAAqB,oBAAoB,MAAM;CACzE,MAAM,mBAAmB,oBAAoB,oBAAoB,MAAM;CACvE,MAAM,iBAAiB,0BAA0B,QAAQ,GAAG,MAAM;CAClE,MAAM,wBAAwB,kBAAkB,MAAM,MAAM,cAAc;CAC1E,MAAM,wBACJ,iBAAiB,KAAK,eAAe,MAAM,MAAM,aAAa;CAEhE,MAAM,eAAe,kBAAoC;EACvD,YAAY,gBAAgB;EAC5B,WAAW,gBAAgB;CAC7B,EAAE;CAGF,MAAM,cAAc,eAClB;EACE,OAAO,MAAM;EACb,OAAO,MAAM;EACb,kBAAkB;CACpB,GACA,YACF;CAEA,MAAM,WAAW,iBACf,eAAe,MAAiC,EAAE,QAAQ,KAAK,CAAC,CAClE;CAGA,MAAM,uBAAkD;EACtD,eAAe,MAAM;EACrB,2BAA2B,MAAM;EACjC,aAAa;EACb,WAAW;CACb;CACA,MAAM,gBAAgB,wBAAwB;CAC9C,MAAM,wBAAwB,cAAc,eAAe,KAAK,KAAA;CAEhE,MAAM,gCAAgC;EACpC,wBAAwB;EACxB,MAAM,eAAe,cAAc,cAAc,SAAS,EAAE,GAAG;EAC/D,OAAO,CAAC,gBAAgB,YAAY,SAAS,YAAY;CAC3D;CAEA,MAAM,4BAA4E,UAAU;EAC1F,IAAI,MAAM,iBAAiB,MAAM,WAAW,MAAM,iBAAiB,wBAAwB,GACzF,MAAM;CAEV;CAEA,mBAAmB;EACjB,IAAI,CAAC,OAAO,KAAK,MAAM,2BAA2B;EAElD,MAAM,iBAAiB,UAAyB;GAC9C,IAAI,MAAM,QAAQ,YAAY,CAAC,MAAM,eAAe,wBAAwB,GAAG;IAC7E,MAAM,eAAe;IACrB,MAAM,gBAAgB;IACtB,MAAM;GACR;EACF;EAEA,SAAS,iBAAiB,WAAW,eAAe,IAAI;EACxD,gBAAgB;GACd,SAAS,oBAAoB,WAAW,eAAe,IAAI;EAC7D,CAAC;CACH,CAAC;CAKD,MAAM,wBAAwB;EAC5B,MAAM,WAAW,MAAM;EACvB,IAAI,OAAO,aAAa,YACtB,OAAQ,SAAsD,aAAa,CAAC;EAE9E,OAAO;CACT;CAEA,OACE,CAAC,KAAK,MAAM,WAAW,MAAM,OAAO,KAAK,gBAAgB,IAAI;MAC3D,CAAC,OAAO,OAAO,gBAAgB,GAAG;QAChC,CAAC,2BAA2B,SAAS,OAAO,OAAO;UACjD,CAAC,qBAAqB,SAAS,OAAO,sBAAsB;YAC1D,CAAC,QACK,SAAS,GACb,KAAK,oBACL,OAAO,YAAY,MAAM,GACzB,OAAO,YAAY,MAAM,GACzB,eAAe,SAAS,gBAAgB,CAAC,GACzC,cAAc,SAAS,gBAAgB,CAAC,GACxC,eAAe,0BAChB;eACE,gBAAgB,EAAE;YACrB,EAAE,IAAI;UACR,EAAE,qBAAqB,SAAS;QAClC,EAAE,2BAA2B,SAAS;MACxC,EAAE,OAAO;IACX,EAAE;AAEN;;;;;;;;;;;;;AAcA,SAAgB,MAAM,OAAgC;CAGpD,OAAO,CAAC,gCAAgC;AAC1C;;;;;;AAOA,SAAS,4BAA4B,OAAgC;CACnE,MAAM,CAAC,cAAc,cAAc,WAAW,OAAO;EACnD;EACA;EACA;EACA;EACA;EACA;EACA;CACF,CAAC;CAQD,IALwB,WAAW,oBAKjB,GAChB,OAAO,CAAC,iBAAiB,aAAa,MAAM,SAAS,EAAE;CAIzD,OACE,CAAC,iBAAiB,cAAc;MAC9B,CAAC,iBAAiB,aAAa,MAAM,SAAS,EAAE,aAAa;IAC/D,EAAE;AAEN;;;;;AAMA,SAAS,aAAa,OAAgC;CACpD,IAAI,UACF,OAAO,GAAG,MAAM,SAAS;CAG3B,MAAM,CAAC,OAAO,QAAQ,WAAW,OAAO;EACtC;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;CACF,CAAC;CAED,IAAI;CACJ,MAAM,yBAAyB,YAAY;CAI3C,MAAM,CAAC,SAAS,cAAc,aAAiC,IAAI;CAGnE,MAAM,cAAc,WAAW,0BAA0B;CAKzD,MAAM,kBAAkB,WAAW,oBAAoB;CAGvD,MAAM,sBAAsB,iBAAiB,iBAAiB,MAAM;CACpE,MAAM,kCACJ,iBAAiB,6BAA6B,MAAM;CAGtD,MAAM,eAAwB;EAC5B,IAAI,MAAM,WAAW,KAAA,GAAW,OAAO,MAAM;EAC7C,OAAO,aAAa,UAAU;CAChC;CAOA,MAAM,oBAAoB,YAA4B;EACpD,WAAW;EACX,WAAW,OAAO;EAClB,iBAAiB,cAAc,OAAO;CACxC;CACA,MAAM,kBAAkB,2BAA2B,QAAQ,GAAG,MAAM;CACpE,MAAM,uBAAuB,iBAAiB,YAAY,KAAK,MAAM,aAAa;CAGlF,mBAAmB;EACjB,IAAI,CAAC,OAAO,GAAG;EAEf,wBAAwB;EACxB,IAAI,CAAC,cAAc,SAAS,gBAAgB,GAC1C,cAAc,KAAK,gBAAgB;EAGrC,gBAAgB;GACd,MAAM,QAAQ,cAAc,QAAQ,gBAAgB;GACpD,IAAI,SAAS,GACX,cAAc,OAAO,OAAO,CAAC;EAEjC,CAAC;CACH,CAAC;CAED,MAAM,uBAAuB;EAC3B,wBAAwB;EACxB,OAAO,cAAc,cAAc,SAAS,OAAO;CACrD;CAEA,MAAM,cAAc;EAClB,IAAI,MAAM,WAAW,KAAA,GACnB,MAAM,eAAe,KAAK;OAE1B,aAAa,MAAM;CAEvB;CAGA,mBAAmB;EACjB,IAAI,CAAC,OAAO,GAAG;EAEf,MAAM,OAAO,SAAS;EACtB,MAAM,eAAe,KAAK,MAAM;EAChC,KAAK,MAAM,WAAW;EAEtB,gBAAgB;GACd,KAAK,MAAM,WAAW;EACxB,CAAC;CACH,CAAC;CAGD,mBAAmB;EACjB,IAAI,CAAC,OAAO,KAAK,CAAC,cAAc,GAAG;EAEnC,sBAAsB;GACpB,KAAK;GACL,yBAAyB;IACvB,IAAI,eAAe,GACjB,MAAM;GAEV;GACA,YAAY;EACd,CAAC;CACH,CAAC;CAGD,mBAAmB;EACjB,IAAI,CAAC,OAAO,KAAK,0BAA0B,GAAG;EAE9C,MAAM,iBAAiB,MAAqB;GAC1C,IAAI,EAAE,QAAQ,YAAY,CAAC,EAAE,eAAe,eAAe,GAAG;IAC5D,EAAE,eAAe;IACjB,EAAE,gBAAgB;IAClB,MAAM;GACR;EACF;EAEA,SAAS,iBAAiB,WAAW,eAAe,IAAI;EACxD,gBAAgB;GACd,SAAS,oBAAoB,WAAW,eAAe,IAAI;EAC7D,CAAC;CACH,CAAC;CAGD,mBAAmB;EACjB,IAAI,CAAC,OAAO,KAAK,CAAC,UAAU;EAE5B,IAAI;EACJ,IAAI,YAAY;EAChB,MAAM,cAAc,SAAS,cAAc,eAAe;EAE1D,MAAM,oBAAoB;GACxB,IAAI,aAAa,CAAC,UAAU,aAAa;GACzC,UAAU,gBAAgB,CAAC,QAAQ,GAAG,EAAE,gBAAgB,KAAK,CAAC;EAChE;EAEA,IAAI,SAAS,aACX,YAAY;OAEZ,YAAY,eAAe,WAAW;EAGxC,gBAAgB;GACd,YAAY;GACZ,UAAU;EACZ,CAAC;CACH,CAAC;CAED,MAAM,eAAe,kBAAoC;EACvD,YAAY,gBAAgB;EAC5B,WAAW,eAAe;CAC5B,EAAE;CAEF,MAAM,cAAc,eAClB;EAME,IAAI,WAAW;GACb,OAAO,MAAM;EACf;EACA,OAAO,MAAM;EACb,OAAO,MAAM;EACb,kBAAkB;CACpB,GACA,YACF;CAEA,MAAM,WAAW,iBACf,eAAe,MAAiC,EAAE,QAAQ,KAAK,CAAC,CAClE;CAEA,OAIE,CAAC,WAAW,QAAQ,aAAa;MAC/B,CAAC,QACK,SAAS,GACb,KAAK,kBACL,OAAO,YAAY,MAAM,GACzB,OAAO,YAAY,MAAM,GACzB,eAAe,SAAS,gBAAgB,CAAC,GACzC,cAAc,SAAS,eAAe,CAAC,GACxC;QACC,CAAC,KAAK,MAAM,cAAc,GAAG;YAQO;UAClC,CAAC,eAAe,kBAAkB;YAChC,CAAC,OACC,qBACA,UAAU,IACV,SAAS,OACT,OAAO;EAAE,OAAO;EAAO,QAAQ;CAAM,KACrC;UACJ,EAAE,eAAe;QACnB,EAAE,KAAK;SACN,YAAY,eAAe,EAAE;MAChC,EAAE,IAAI;IACR,EAAE;AAEN"}
|
|
1
|
+
{"version":3,"file":"Modal.jsx","names":[],"sources":["../src/Modal.tsx"],"sourcesContent":["/**\n * Modal and ModalOverlay components for solidaria-components\n *\n * Headless modal components with overlay, focus trapping, and dismissal handling.\n * Port of react-aria-components Modal.\n */\n\nimport {\n type JSX,\n createContext,\n createMemo,\n createSignal,\n createEffect,\n onCleanup,\n splitProps,\n Show,\n useContext,\n} from \"solid-js\";\nimport { Portal, isServer } from \"solid-js/web\";\nimport {\n createInteractOutside,\n ariaHideOutside,\n FocusScope,\n useUNSAFE_PortalContext,\n createEnterAnimation,\n createExitAnimation,\n} from \"@proyecto-viviana/solidaria\";\nimport {\n type RenderChildren,\n type ClassNameOrFunction,\n type StyleOrFunction,\n useRenderProps,\n filterDOMProps,\n dataAttr,\n useIsHydrated,\n} from \"./utils\";\nimport {\n DialogTriggerContext,\n OverlayTriggerStateContext,\n type OverlayTriggerState,\n} from \"./contexts\";\nimport { VisuallyHidden } from \"./VisuallyHidden\";\n\n/**\n * Internal context to signal that Modal is wrapped in ModalOverlay.\n * When present, Modal should not create its own Portal.\n */\ninterface InternalModalContextValue {\n isDismissable?: boolean;\n isKeyboardDismissDisabled?: boolean;\n // Upstream's ModalOverlay owns the modal element's ref so it can watch the\n // modal surface's exit animation alongside the overlay's (both must finish\n // before the overlay unmounts). ModalContent registers its element here.\n setModalRef?: (element: HTMLElement | null) => void;\n // Combined exit flag (overlay OR modal still exiting). Both the overlay and\n // the modal surface carry `data-exiting` off this so they fade out together.\n isExiting?: () => boolean;\n}\n\nconst InternalModalContext = createContext<InternalModalContextValue | null>(null);\n\n// Stack of visible modals, used to ensure only the top-most modal dismisses on Escape/outside interaction.\nconst visibleModals: Array<() => Element | null> = [];\n\nfunction pruneDisconnectedModals() {\n for (let index = visibleModals.length - 1; index >= 0; index -= 1) {\n const element = visibleModals[index]?.();\n if (!element?.isConnected) {\n visibleModals.splice(index, 1);\n }\n }\n}\n\nexport interface ModalRenderProps {\n /** Whether the modal is currently entering (for animations). */\n isEntering: boolean;\n /** Whether the modal is currently exiting (for animations). */\n isExiting: boolean;\n}\n\nexport interface ModalOverlayProps {\n /** The children of the component - can be JSX or render function. */\n children?: RenderChildren<ModalRenderProps>;\n /** The CSS className for the element. */\n class?: ClassNameOrFunction<ModalRenderProps>;\n /** The inline style for the element. */\n style?: StyleOrFunction<ModalRenderProps>;\n /** Whether the modal is open (controlled). */\n isOpen?: boolean;\n /** Whether the modal opens by default (uncontrolled). */\n defaultOpen?: boolean;\n /** Handler called when the modal's open state changes. */\n onOpenChange?: (isOpen: boolean) => void;\n /** Whether clicking outside the modal closes it. */\n isDismissable?: boolean;\n /** Whether pressing Escape closes the modal. */\n isKeyboardDismissDisabled?: boolean;\n /** Whether the modal is entering (for animations). */\n isEntering?: boolean;\n /** Whether the modal is exiting (for animations). */\n isExiting?: boolean;\n}\n\nexport interface ModalProps extends ModalOverlayProps {}\n\nexport { OverlayTriggerStateContext, type OverlayTriggerState } from \"./contexts\";\nexport { useOverlayTriggerState } from \"./contexts\";\nexport const ModalContext = OverlayTriggerStateContext;\n\n/**\n * ModalOverlay is the backdrop/underlay behind a modal.\n * It handles click-outside dismissal and provides styling hooks.\n */\nexport function ModalOverlay(props: ModalOverlayProps): JSX.Element {\n // Do NOT early-return on the server: rendering children bare on the server and a\n // <Show>/<Portal> overlay on the client desyncs hydration. Run the same structure\n // on both and gate the Portal on useIsHydrated() (see Popover for the rationale).\n const isHydrated = useIsHydrated();\n\n // IMPORTANT: Don't destructure or access props.children early!\n // In SolidJS, children are lazily evaluated. Accessing them before\n // the context provider renders causes them to evaluate outside the context.\n // See: https://github.com/solidjs/solid/issues/182\n const [local, rest] = splitProps(props, [\n \"class\",\n \"style\",\n \"isOpen\",\n \"defaultOpen\",\n \"onOpenChange\",\n \"isDismissable\",\n \"isKeyboardDismissDisabled\",\n \"isEntering\",\n \"isExiting\",\n ]);\n\n // Get state from DialogTrigger context if available\n const dialogTriggerContext = useContext(DialogTriggerContext);\n\n // Internal state for uncontrolled mode\n const [internalOpen, setInternalOpen] = createSignal(local.defaultOpen ?? false);\n\n // Determine if open (controlled > DialogTrigger context > uncontrolled)\n const isOpen = (): boolean => {\n if (local.isOpen !== undefined) return local.isOpen;\n if (dialogTriggerContext) return dialogTriggerContext.state.isOpen();\n return internalOpen();\n };\n\n const close = () => {\n if (local.isOpen !== undefined) {\n local.onOpenChange?.(false);\n } else if (dialogTriggerContext) {\n dialogTriggerContext.state.close();\n local.onOpenChange?.(false);\n } else {\n setInternalOpen(false);\n local.onOpenChange?.(false);\n }\n };\n\n const open = () => {\n if (local.isOpen !== undefined) {\n local.onOpenChange?.(true);\n } else if (dialogTriggerContext) {\n dialogTriggerContext.state.open();\n local.onOpenChange?.(true);\n } else {\n setInternalOpen(true);\n local.onOpenChange?.(true);\n }\n };\n\n const toggle = () => {\n if (isOpen()) {\n close();\n } else {\n open();\n }\n };\n\n const state: OverlayTriggerState = {\n get isOpen() {\n return isOpen();\n },\n open,\n close,\n toggle,\n };\n\n // Enter/exit animation state (mirrors upstream's ModalOverlayWithForwardRef +\n // ModalOverlayInner). The overlay stays mounted here even while closed, so the\n // enter animation is gated on `isOpen` (isReady) — otherwise it would resolve\n // to \"done entering\" before the element ever mounts. Exit is watched for BOTH\n // the overlay and the modal surface; the modal registers its element via\n // InternalModalContext (setModalRef) so we can await its exit too.\n let overlayRef!: HTMLDivElement;\n // Signal-backed so the enter/exit effects re-run once the element mounts — a\n // plain closure over `overlayRef` would read `null` if the effect fired first\n // and, with no reactive dep on the ref, never recover (stuck `data-entering`).\n const [overlayEl, setOverlayEl] = createSignal<HTMLElement | null>(null);\n const overlayRefAccessor = () => overlayEl();\n const registerOverlayRef = (element: HTMLDivElement) => {\n overlayRef = element;\n setOverlayEl(element);\n };\n const [modalEl, setModalEl] = createSignal<HTMLElement | null>(null);\n\n const isOverlayEntering = createEnterAnimation(overlayRefAccessor, isOpen);\n const isOverlayExiting = createExitAnimation(overlayRefAccessor, isOpen);\n const isModalExiting = createExitAnimation(() => modalEl(), isOpen);\n const overlayEntering = () => isOverlayEntering() || (local.isEntering ?? false);\n const combinedExiting = () =>\n isOverlayExiting() || isModalExiting() || (local.isExiting ?? false);\n\n const renderValues = createMemo<ModalRenderProps>(() => ({\n isEntering: overlayEntering(),\n isExiting: combinedExiting(),\n }));\n\n // Resolve render props - don't pass children, we'll render props.children directly\n const renderProps = useRenderProps(\n {\n class: local.class,\n style: local.style,\n defaultClassName: \"solidaria-ModalOverlay\",\n },\n renderValues,\n );\n\n const domProps = createMemo(() =>\n filterDOMProps(rest as Record<string, unknown>, { global: true }),\n );\n\n // Internal context value to signal Modal that it's wrapped\n const internalModalContext: InternalModalContextValue = {\n isDismissable: local.isDismissable,\n isKeyboardDismissDisabled: local.isKeyboardDismissDisabled,\n setModalRef: setModalEl,\n isExiting: combinedExiting,\n };\n const portalContext = useUNSAFE_PortalContext();\n const portalContainer = () => portalContext.getContainer?.() ?? undefined;\n\n const isTopMostModalInOverlay = () => {\n pruneDisconnectedModals();\n const topMostModal = visibleModals[visibleModals.length - 1]?.();\n return !topMostModal || overlayRef?.contains(topMostModal);\n };\n\n const handleOverlayPointerDown: JSX.EventHandler<HTMLDivElement, PointerEvent> = (event) => {\n if (local.isDismissable && event.target === event.currentTarget && isTopMostModalInOverlay()) {\n close();\n }\n };\n\n createEffect(() => {\n if (!isOpen() || local.isKeyboardDismissDisabled) return;\n\n const handleKeyDown = (event: KeyboardEvent) => {\n if (event.key === \"Escape\" && !event.isComposing && isTopMostModalInOverlay()) {\n event.preventDefault();\n event.stopPropagation();\n close();\n }\n };\n\n // Bubble phase, mirroring react-aria's `useOverlay` (its `onKeyDown` is an\n // overlay-element prop, handled on bubble). Capture would preempt inner\n // widgets that dismiss on Escape (a combobox/menu inside the dialog must\n // consume Escape first) and would also fire before the event is recorded by\n // capture-phase listeners, inverting the keydown/onOpenChange order.\n document.addEventListener(\"keydown\", handleKeyDown, false);\n onCleanup(() => {\n document.removeEventListener(\"keydown\", handleKeyDown, false);\n });\n });\n\n // Resolve children - handle both static JSX and render functions\n // IMPORTANT: We access props.children directly (not local.children) to preserve\n // lazy evaluation inside context providers\n const resolveChildren = () => {\n const children = props.children;\n if (typeof children === \"function\") {\n return (children as (props: ModalRenderProps) => JSX.Element)(renderValues());\n }\n return children;\n };\n\n return (\n <Show when={isHydrated() && (isOpen() || combinedExiting())}>\n <Portal mount={portalContainer()}>\n <OverlayTriggerStateContext.Provider value={state}>\n <InternalModalContext.Provider value={internalModalContext}>\n <div\n {...domProps()}\n ref={registerOverlayRef}\n class={renderProps.class()}\n style={renderProps.style()}\n data-entering={dataAttr(overlayEntering())}\n data-exiting={dataAttr(combinedExiting())}\n onPointerDown={handleOverlayPointerDown}\n >\n {resolveChildren()}\n </div>\n </InternalModalContext.Provider>\n </OverlayTriggerStateContext.Provider>\n </Portal>\n </Show>\n );\n}\n\n/**\n * Modal is a dialog container that manages focus trapping, scroll prevention,\n * aria-hiding of content outside, and dismissal.\n *\n * Usage patterns:\n * 1. Standalone: `<Modal isOpen>...</Modal>` - Creates its own overlay\n * 2. With custom overlay: `<ModalOverlay><Modal>...</Modal></ModalOverlay>`\n *\n * Note: Due to SolidJS's eager JSX evaluation, we cannot detect at render time\n * whether Modal is wrapped in ModalOverlay. So standalone Modal always creates\n * an overlay, and wrapped Modal renders directly (relying on InternalModalContext).\n */\nexport function Modal(props: ModalProps): JSX.Element {\n // Check for InternalModalContext which signals we're inside a rendered ModalOverlay\n // This works because ModalContent is rendered INSIDE ModalOverlay's Show/Portal\n return <ModalContentWithAutoOverlay {...props} />;\n}\n\n/**\n * Helper component that handles the overlay detection.\n * By being a separate component, we can use Show to defer rendering until\n * the parent context is available.\n */\nfunction ModalContentWithAutoOverlay(props: ModalProps): JSX.Element {\n const [overlayProps, modalProps] = splitProps(props, [\n \"isOpen\",\n \"defaultOpen\",\n \"onOpenChange\",\n \"isDismissable\",\n \"isKeyboardDismissDisabled\",\n \"isEntering\",\n \"isExiting\",\n ]);\n\n // Check for InternalModalContext - if present, we're inside a ModalOverlay\n const internalContext = useContext(InternalModalContext);\n\n // If wrapped in ModalOverlay, just render the content. ModalContent reads the\n // overlay's InternalModalContext itself (via useContext), so nothing needs\n // threading — in both branches ModalContent renders inside a provider.\n if (internalContext) {\n return <ModalContent {...modalProps}>{props.children}</ModalContent>;\n }\n\n // For standalone usage, wrap in ModalOverlay.\n return (\n <ModalOverlay {...overlayProps}>\n <ModalContent {...modalProps}>{props.children}</ModalContent>\n </ModalOverlay>\n );\n}\n\n/**\n * Internal component that renders the actual modal content.\n * Used by both standalone Modal and Modal wrapped in ModalOverlay.\n */\nfunction ModalContent(props: ModalProps): JSX.Element {\n if (isServer) {\n return <>{props.children}</>;\n }\n\n const [local, rest] = splitProps(props, [\n \"children\",\n \"class\",\n \"style\",\n \"isOpen\",\n \"defaultOpen\",\n \"onOpenChange\",\n \"isDismissable\",\n \"isKeyboardDismissDisabled\",\n \"isEntering\",\n \"isExiting\",\n ]);\n\n let modalRef!: HTMLDivElement;\n const modalRefAccessor = () => modalRef ?? null;\n // Signal-backed element for the enter animation, so the effect re-runs when\n // the surface mounts (see the overlay's note); the plain `modalRef` var still\n // backs the synchronous consumers (focus stack, interact-outside, aria-hide).\n const [modalEl, setModalEl] = createSignal<HTMLElement | null>(null);\n\n // Get state from parent OverlayTriggerStateContext (provided by ModalOverlay)\n const parentState = useContext(OverlayTriggerStateContext);\n\n // The overlay (auto or user-provided) always wraps this content, so it owns\n // the InternalModalContext: dismissal settings, the modal-ref registration\n // sink, and the combined exit flag.\n const internalContext = useContext(InternalModalContext);\n\n // Get dismissable settings from internal context (set by ModalOverlay)\n const isDismissable = () => internalContext?.isDismissable ?? local.isDismissable;\n const isKeyboardDismissDisabled = () =>\n internalContext?.isKeyboardDismissDisabled ?? local.isKeyboardDismissDisabled;\n\n // Determine if open from parent state\n const isOpen = (): boolean => {\n if (local.isOpen !== undefined) return local.isOpen;\n return parentState?.isOpen ?? false;\n };\n\n // Register this element with the overlay so it can await the modal surface's\n // exit animation (upstream threads modalRef down the same way). The overlay's\n // combined exit flag drives this surface's `data-exiting`. The enter animation\n // is local — the modal fades/slides in on its own timeline (delay 160 vs the\n // overlay's 0).\n const registerModalRef = (element: HTMLDivElement) => {\n modalRef = element;\n setModalEl(element);\n internalContext?.setModalRef?.(element);\n };\n const isModalEntering = createEnterAnimation(() => modalEl(), isOpen);\n const isModalExiting = () => internalContext?.isExiting?.() ?? local.isExiting ?? false;\n\n // Keep this modal in a global stack so nested modals dismiss in top-down order.\n createEffect(() => {\n if (!isOpen()) return;\n\n pruneDisconnectedModals();\n if (!visibleModals.includes(modalRefAccessor)) {\n visibleModals.push(modalRefAccessor);\n }\n\n onCleanup(() => {\n const index = visibleModals.indexOf(modalRefAccessor);\n if (index >= 0) {\n visibleModals.splice(index, 1);\n }\n });\n });\n\n const isTopMostModal = () => {\n pruneDisconnectedModals();\n return visibleModals[visibleModals.length - 1] === modalRefAccessor;\n };\n\n const close = () => {\n if (local.isOpen !== undefined) {\n local.onOpenChange?.(false);\n } else {\n parentState?.close();\n }\n };\n\n // Prevent scroll when modal is open\n createEffect(() => {\n if (!isOpen()) return;\n\n const html = document.documentElement;\n const prevOverflow = html.style.overflow;\n html.style.overflow = \"hidden\";\n\n onCleanup(() => {\n html.style.overflow = prevOverflow;\n });\n });\n\n // Click outside to close (if dismissable)\n createEffect(() => {\n if (!isOpen() || !isDismissable()) return;\n\n createInteractOutside({\n ref: modalRefAccessor,\n onInteractOutside: () => {\n if (isTopMostModal()) {\n close();\n }\n },\n isDisabled: false,\n });\n });\n\n // Escape key to close\n createEffect(() => {\n if (!isOpen() || isKeyboardDismissDisabled()) return;\n\n const handleKeyDown = (e: KeyboardEvent) => {\n if (e.key === \"Escape\" && !e.isComposing && isTopMostModal()) {\n e.preventDefault();\n e.stopPropagation();\n close();\n }\n };\n\n // Bubble phase — see the ModalOverlay handler above for why (react-aria\n // `useOverlay` parity; inner-widget Escape precedence; event-order fidelity).\n document.addEventListener(\"keydown\", handleKeyDown, false);\n onCleanup(() => {\n document.removeEventListener(\"keydown\", handleKeyDown, false);\n });\n });\n\n // Aria-hide outside content\n createEffect(() => {\n if (!isOpen() || !modalRef) return;\n\n let cleanup: (() => void) | undefined;\n let cancelled = false;\n const ownerWindow = modalRef.ownerDocument.defaultView ?? window;\n\n const hideOutside = () => {\n if (cancelled || !modalRef?.isConnected) return;\n cleanup = ariaHideOutside([modalRef], { shouldUseInert: true });\n };\n\n if (modalRef.isConnected) {\n hideOutside();\n } else {\n ownerWindow.queueMicrotask(hideOutside);\n }\n\n onCleanup(() => {\n cancelled = true;\n cleanup?.();\n });\n });\n\n const renderValues = createMemo<ModalRenderProps>(() => ({\n isEntering: isModalEntering(),\n isExiting: isModalExiting(),\n }));\n\n const renderProps = useRenderProps(\n {\n // Lazy: the modal content is gated behind `<Show when={isHydrated() && …}>`\n // (Portal) below, so it must not be instantiated during the component body.\n // An eager `children: props.children` would build the content template (and\n // walk getNextElement) before the gate — content the server never emitted →\n // hydration mismatch. See Popover for the full rationale.\n get children() {\n return props.children;\n },\n class: local.class,\n style: local.style,\n defaultClassName: \"solidaria-Modal\",\n },\n renderValues,\n );\n\n const domProps = createMemo(() =>\n filterDOMProps(rest as Record<string, unknown>, { global: true }),\n );\n\n return (\n // No autoFocus: react-aria's Overlay renders FocusScope with restoreFocus +\n // contain only — initial focus is useDialog's job (it focuses the dialog\n // element itself when nothing inside holds focus).\n //\n // `contain` is gated on `!isExiting`, mirroring react-aria's Overlay\n // (react-aria/src/overlays/Overlay.tsx: `contain={... && !isExiting}`). The\n // overlay stays mounted through its exit transition (isOpen || isExiting), so\n // without this gate containment would remain active during the exit and yank\n // focus back into the dialog as restoreFocus moves it to the trigger —\n // producing a focus bounce (focusout→focusin→focusout) that React never\n // shows. Releasing containment the instant the exit begins lets focus settle\n // on the trigger in a single move, matching upstream's event sequence.\n <FocusScope contain={!isModalExiting()} restoreFocus>\n <div\n {...domProps()}\n ref={registerModalRef}\n class={renderProps.class()}\n style={renderProps.style()}\n data-entering={dataAttr(isModalEntering())}\n data-exiting={dataAttr(isModalExiting())}\n >\n <Show when={isDismissable()}>\n {/* Faithful to react-aria's DismissButton: a bare button carrying\n only `width/height: 1px` inside a VisuallyHidden `div` wrapper —\n NOT the hidden styles inlined onto the button. The wrapper clips\n it out of sight while the button keeps its intrinsic UA border-box,\n which is exactly what upstream renders (and what D8 target-size\n measures against the pair oracle). Inlining the reset onto the\n button instead collapsed it to a 1x1 box, diverging from\n upstream's ~16x6 sentinel. */}\n <VisuallyHidden elementType=\"div\">\n <button\n aria-label=\"Dismiss\"\n tabIndex={-1}\n onClick={close}\n style={{ width: \"1px\", height: \"1px\" }}\n />\n </VisuallyHidden>\n </Show>\n {renderProps.renderChildren()}\n </div>\n </FocusScope>\n );\n}\n\nexport default Modal;\n"],"mappings":"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"}
|
package/dist/Popover.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Popover.d.ts","sourceRoot":"","sources":["../src/Popover.tsx"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,EACL,KAAK,GAAG,EAUT,MAAM,UAAU,CAAC;AAElB,OAAO,
|
|
1
|
+
{"version":3,"file":"Popover.d.ts","sourceRoot":"","sources":["../src/Popover.tsx"],"names":[],"mappings":"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"}
|
package/dist/Popover.js
CHANGED
|
@@ -2,7 +2,7 @@ import { dataAttr, filterDOMProps, useIsHydrated, useRenderProps } from "./utils
|
|
|
2
2
|
import { o as usePopoverTrigger, r as PopoverTriggerContext, t as DialogTriggerContext } from "./contexts.js";
|
|
3
3
|
import { Portal, addEventListener, className, createComponent, delegateEvents, effect, getNextElement, getNextMarker, insert, memo, mergeProps, runHydrationEvents, setAttribute, spread, style, template, use } from "solid-js/web";
|
|
4
4
|
import { Show, createContext, createEffect, createMemo, createSignal, createUniqueId, onCleanup, splitProps, useContext } from "solid-js";
|
|
5
|
-
import { FocusScope, createOverlayTrigger, createPopover, useUNSAFE_PortalContext, visuallyHiddenStyles } from "@proyecto-viviana/solidaria";
|
|
5
|
+
import { FocusScope, createOverlayTrigger, createPopover, useLocale, useUNSAFE_PortalContext, visuallyHiddenStyles } from "@proyecto-viviana/solidaria";
|
|
6
6
|
import { createOverlayTriggerState } from "@proyecto-viviana/solid-stately";
|
|
7
7
|
//#region src/Popover.tsx
|
|
8
8
|
/**
|
|
@@ -100,9 +100,10 @@ function Popover(props) {
|
|
|
100
100
|
"isEntering",
|
|
101
101
|
"isExiting"
|
|
102
102
|
]);
|
|
103
|
-
|
|
103
|
+
const [popoverRef, setPopoverRef] = createSignal(null);
|
|
104
104
|
const [groupRef, setGroupRef] = createSignal(null);
|
|
105
105
|
const isHydrated = useIsHydrated();
|
|
106
|
+
const locale = useLocale();
|
|
106
107
|
const triggerContext = useContext(PopoverTriggerContext);
|
|
107
108
|
const dialogTriggerContext = useContext(DialogTriggerContext);
|
|
108
109
|
const popoverGroupContext = useContext(PopoverGroupContext);
|
|
@@ -136,7 +137,7 @@ function Popover(props) {
|
|
|
136
137
|
};
|
|
137
138
|
const popoverAria = createPopover({
|
|
138
139
|
triggerRef: getTriggerRef,
|
|
139
|
-
popoverRef: () => popoverRef
|
|
140
|
+
popoverRef: () => popoverRef(),
|
|
140
141
|
groupRef: () => isSubPopover() ? popoverGroupContext?.() ?? null : groupRef(),
|
|
141
142
|
get placement() {
|
|
142
143
|
return local.placement;
|
|
@@ -270,15 +271,16 @@ function Popover(props) {
|
|
|
270
271
|
createEffect(() => {
|
|
271
272
|
if (!isOpen() || !shouldBeDialog()) return;
|
|
272
273
|
if ((local.autoFocus ?? true) === false) return;
|
|
273
|
-
if (!popoverRef) return;
|
|
274
|
+
if (!popoverRef()) return;
|
|
274
275
|
if (resolvedTrigger() === "SubmenuTrigger") return;
|
|
275
276
|
let timeout;
|
|
276
277
|
let frame;
|
|
277
278
|
const focusIfNeeded = () => {
|
|
278
279
|
if (!isOpen() || !shouldBeDialog()) return;
|
|
279
|
-
|
|
280
|
-
if (
|
|
281
|
-
|
|
280
|
+
const node = popoverRef();
|
|
281
|
+
if (!node || resolvedTrigger() === "SubmenuTrigger") return;
|
|
282
|
+
if (document.activeElement === node || node.contains(document.activeElement)) return;
|
|
283
|
+
node.focus();
|
|
282
284
|
};
|
|
283
285
|
const scheduleFocus = () => {
|
|
284
286
|
timeout = window.setTimeout(focusIfNeeded, 0);
|
|
@@ -316,8 +318,7 @@ function Popover(props) {
|
|
|
316
318
|
restoreFocus: true,
|
|
317
319
|
get children() {
|
|
318
320
|
var _el$2 = getNextElement(_tmpl$2), _el$3 = _el$2.firstChild, [_el$4, _co$] = getNextMarker(_el$3.nextSibling), _el$5 = _el$4.nextSibling, [_el$6, _co$2] = getNextMarker(_el$5.nextSibling), _el$7 = _el$6.nextSibling, [_el$8, _co$3] = getNextMarker(_el$7.nextSibling);
|
|
319
|
-
|
|
320
|
-
typeof _ref$ === "function" ? use(_ref$, _el$2) : popoverRef = _el$2;
|
|
321
|
+
use(setPopoverRef, _el$2);
|
|
321
322
|
spread(_el$2, mergeProps(domProps, cleanPopoverProps, {
|
|
322
323
|
get id() {
|
|
323
324
|
return overlayId();
|
|
@@ -334,6 +335,12 @@ function Popover(props) {
|
|
|
334
335
|
get style() {
|
|
335
336
|
return mergedStyle();
|
|
336
337
|
},
|
|
338
|
+
get lang() {
|
|
339
|
+
return locale().locale;
|
|
340
|
+
},
|
|
341
|
+
get dir() {
|
|
342
|
+
return locale().direction;
|
|
343
|
+
},
|
|
337
344
|
get ["data-trigger"]() {
|
|
338
345
|
return resolvedTrigger();
|
|
339
346
|
},
|