UNPKG

@kobalte/core

Version:

Unstyled components and primitives for building accessible web apps and design systems with SolidJS.

397 lines (381 loc) 10.9 kB
import { createFocusScope } from "./7A3GDF4Y.jsx"; import { createHideOutside } from "./FBCYWU27.jsx"; import { DismissableLayer } from "./3VFJM5NZ.jsx"; import { createDisclosureState } from "./E53DB7BS.jsx"; import { ButtonRoot } from "./UKTBL2JL.jsx"; import { createRegisterId } from "./JNCCF6MP.jsx"; import { Polymorphic } from "./FLVHQV4A.jsx"; import { __export } from "./5WXHJDCZ.jsx"; // src/dialog/index.tsx var dialog_exports = {}; __export(dialog_exports, { CloseButton: () => DialogCloseButton, Content: () => DialogContent, Description: () => DialogDescription, Dialog: () => Dialog, Overlay: () => DialogOverlay, Portal: () => DialogPortal, Root: () => DialogRoot, Title: () => DialogTitle, Trigger: () => DialogTrigger, useDialogContext: () => useDialogContext }); // src/dialog/dialog-close-button.tsx import { callHandler } from "@kobalte/utils"; import { splitProps } from "solid-js"; // src/dialog/dialog-context.tsx import { createContext, useContext } from "solid-js"; var DialogContext = createContext(); function useDialogContext() { const context = useContext(DialogContext); if (context === void 0) { throw new Error( "[kobalte]: `useDialogContext` must be used within a `Dialog` component" ); } return context; } // src/dialog/dialog-close-button.tsx function DialogCloseButton(props) { const context = useDialogContext(); const [local, others] = splitProps(props, [ "aria-label", "onClick" ]); const onClick = (e) => { callHandler(e, local.onClick); context.close(); }; return <ButtonRoot aria-label={local["aria-label"] || context.translations().dismiss} onClick={onClick} {...others} />; } // src/dialog/dialog-content.tsx import { contains, focusWithoutScrolling, mergeDefaultProps, mergeRefs } from "@kobalte/utils"; import { Show, createEffect, onCleanup, splitProps as splitProps2 } from "solid-js"; import createPreventScroll from "solid-prevent-scroll"; function DialogContent(props) { let ref; const context = useDialogContext(); const mergedProps = mergeDefaultProps( { id: context.generateId("content") }, props ); const [local, others] = splitProps2(mergedProps, [ "ref", "onOpenAutoFocus", "onCloseAutoFocus", "onPointerDownOutside", "onFocusOutside", "onInteractOutside" ]); let hasInteractedOutside = false; let hasPointerDownOutside = false; const onPointerDownOutside = (e) => { local.onPointerDownOutside?.(e); if (context.modal() && e.detail.isContextMenu) { e.preventDefault(); } }; const onFocusOutside = (e) => { local.onFocusOutside?.(e); if (context.modal()) { e.preventDefault(); } }; const onInteractOutside = (e) => { local.onInteractOutside?.(e); if (context.modal()) { return; } if (!e.defaultPrevented) { hasInteractedOutside = true; if (e.detail.originalEvent.type === "pointerdown") { hasPointerDownOutside = true; } } if (contains(context.triggerRef(), e.target)) { e.preventDefault(); } if (e.detail.originalEvent.type === "focusin" && hasPointerDownOutside) { e.preventDefault(); } }; const onCloseAutoFocus = (e) => { local.onCloseAutoFocus?.(e); if (context.modal()) { e.preventDefault(); focusWithoutScrolling(context.triggerRef()); } else { if (!e.defaultPrevented) { if (!hasInteractedOutside) { focusWithoutScrolling(context.triggerRef()); } e.preventDefault(); } hasInteractedOutside = false; hasPointerDownOutside = false; } }; createHideOutside({ isDisabled: () => !(context.isOpen() && context.modal()), targets: () => ref ? [ref] : [] }); createPreventScroll({ element: () => ref ?? null, enabled: () => context.contentPresent() && context.preventScroll() }); createFocusScope( { trapFocus: () => context.isOpen() && context.modal(), onMountAutoFocus: local.onOpenAutoFocus, onUnmountAutoFocus: onCloseAutoFocus }, () => ref ); createEffect(() => onCleanup(context.registerContentId(others.id))); return <Show when={context.contentPresent()}><DismissableLayer ref={mergeRefs((el) => { context.setContentRef(el); ref = el; }, local.ref)} role="dialog" tabIndex={-1} disableOutsidePointerEvents={context.modal() && context.isOpen()} excludedElements={[context.triggerRef]} aria-labelledby={context.titleId()} aria-describedby={context.descriptionId()} data-expanded={context.isOpen() ? "" : void 0} data-closed={!context.isOpen() ? "" : void 0} onPointerDownOutside={onPointerDownOutside} onFocusOutside={onFocusOutside} onInteractOutside={onInteractOutside} onDismiss={context.close} {...others} /></Show>; } // src/dialog/dialog-description.tsx import { mergeDefaultProps as mergeDefaultProps2 } from "@kobalte/utils"; import { createEffect as createEffect2, onCleanup as onCleanup2, splitProps as splitProps3 } from "solid-js"; function DialogDescription(props) { const context = useDialogContext(); const mergedProps = mergeDefaultProps2( { id: context.generateId("description") }, props ); const [local, others] = splitProps3(mergedProps, ["id"]); createEffect2(() => onCleanup2(context.registerDescriptionId(local.id))); return <Polymorphic as="p" id={local.id} {...others} />; } // src/dialog/dialog-overlay.tsx import { callHandler as callHandler2, mergeRefs as mergeRefs2 } from "@kobalte/utils"; import { Show as Show2, splitProps as splitProps4 } from "solid-js"; import { combineStyle } from "@solid-primitives/props"; function DialogOverlay(props) { const context = useDialogContext(); const [local, others] = splitProps4(props, [ "ref", "style", "onPointerDown" ]); const onPointerDown = (e) => { callHandler2(e, local.onPointerDown); if (e.target === e.currentTarget) { e.preventDefault(); } }; return <Show2 when={context.overlayPresent()}><Polymorphic as="div" ref={mergeRefs2(context.setOverlayRef, local.ref)} style={combineStyle({ "pointer-events": "auto" }, local.style)} data-expanded={context.isOpen() ? "" : void 0} data-closed={!context.isOpen() ? "" : void 0} onPointerDown={onPointerDown} {...others} /></Show2>; } // src/dialog/dialog-portal.tsx import { Show as Show3 } from "solid-js"; import { Portal } from "solid-js/web"; function DialogPortal(props) { const context = useDialogContext(); return <Show3 when={context.contentPresent() || context.overlayPresent()}><Portal {...props} /></Show3>; } // src/dialog/dialog-root.tsx import { createGenerateId, mergeDefaultProps as mergeDefaultProps3 } from "@kobalte/utils"; import { createSignal, createUniqueId } from "solid-js"; import createPresence from "solid-presence"; // src/dialog/dialog.intl.ts var DIALOG_INTL_TRANSLATIONS = { // `aria-label` of Dialog.CloseButton. dismiss: "Dismiss" }; // src/dialog/dialog-root.tsx function DialogRoot(props) { const defaultId = `dialog-${createUniqueId()}`; const mergedProps = mergeDefaultProps3( { id: defaultId, modal: true, translations: DIALOG_INTL_TRANSLATIONS }, props ); const [contentId, setContentId] = createSignal(); const [titleId, setTitleId] = createSignal(); const [descriptionId, setDescriptionId] = createSignal(); const [overlayRef, setOverlayRef] = createSignal(); const [contentRef, setContentRef] = createSignal(); const [triggerRef, setTriggerRef] = createSignal(); const disclosureState = createDisclosureState({ open: () => mergedProps.open, defaultOpen: () => mergedProps.defaultOpen, onOpenChange: (isOpen) => mergedProps.onOpenChange?.(isOpen) }); const shouldMount = () => mergedProps.forceMount || disclosureState.isOpen(); const { present: overlayPresent } = createPresence({ show: shouldMount, element: () => overlayRef() ?? null }); const { present: contentPresent } = createPresence({ show: shouldMount, element: () => contentRef() ?? null }); const context = { translations: () => mergedProps.translations ?? DIALOG_INTL_TRANSLATIONS, isOpen: disclosureState.isOpen, modal: () => mergedProps.modal ?? true, preventScroll: () => mergedProps.preventScroll ?? context.modal(), contentId, titleId, descriptionId, triggerRef, overlayRef, setOverlayRef, contentRef, setContentRef, overlayPresent, contentPresent, close: disclosureState.close, toggle: disclosureState.toggle, setTriggerRef, generateId: createGenerateId(() => mergedProps.id), registerContentId: createRegisterId(setContentId), registerTitleId: createRegisterId(setTitleId), registerDescriptionId: createRegisterId(setDescriptionId) }; return <DialogContext.Provider value={context}>{mergedProps.children}</DialogContext.Provider>; } // src/dialog/dialog-title.tsx import { mergeDefaultProps as mergeDefaultProps4 } from "@kobalte/utils"; import { createEffect as createEffect3, onCleanup as onCleanup3, splitProps as splitProps5 } from "solid-js"; function DialogTitle(props) { const context = useDialogContext(); const mergedProps = mergeDefaultProps4( { id: context.generateId("title") }, props ); const [local, others] = splitProps5(mergedProps, ["id"]); createEffect3(() => onCleanup3(context.registerTitleId(local.id))); return <Polymorphic as="h2" id={local.id} {...others} />; } // src/dialog/dialog-trigger.tsx import { callHandler as callHandler3, mergeRefs as mergeRefs3 } from "@kobalte/utils"; import { splitProps as splitProps6 } from "solid-js"; function DialogTrigger(props) { const context = useDialogContext(); const [local, others] = splitProps6(props, [ "ref", "onClick" ]); const onClick = (e) => { callHandler3(e, local.onClick); context.toggle(); }; return <ButtonRoot ref={mergeRefs3(context.setTriggerRef, local.ref)} aria-haspopup="dialog" aria-expanded={context.isOpen()} aria-controls={context.isOpen() ? context.contentId() : void 0} data-expanded={context.isOpen() ? "" : void 0} data-closed={!context.isOpen() ? "" : void 0} onClick={onClick} {...others} />; } // src/dialog/index.tsx var Dialog = Object.assign(DialogRoot, { CloseButton: DialogCloseButton, Content: DialogContent, Description: DialogDescription, Overlay: DialogOverlay, Portal: DialogPortal, Title: DialogTitle, Trigger: DialogTrigger }); export { useDialogContext, DialogCloseButton, DialogContent, DialogDescription, DialogOverlay, DialogPortal, DialogRoot, DialogTitle, DialogTrigger, Dialog, dialog_exports };