UNPKG

@corvu/disclosure

Version:

Unstyled, accessible and customizable UI primitives for SolidJS

277 lines (272 loc) 9.51 kB
import { createComponent, mergeProps as mergeProps$1 } from 'solid-js/web'; import { combineStyle, callEventHandler } from '@corvu/utils/dom'; import { createContext, mergeProps, createUniqueId, createSignal, createEffect, on, untrack, createMemo, useContext, splitProps, Show } from 'solid-js'; import { Dynamic, DynamicButton } from '@corvu/utils/dynamic'; import { some, mergeRefs } from '@corvu/utils/reactivity'; import { dataIf, isFunction } from '@corvu/utils'; import { useKeyedContext, createKeyedContext } from '@corvu/utils/create/keyedContext'; import createControllableSignal from '@corvu/utils/create/controllableSignal'; import createOnce from '@corvu/utils/create/once'; import createPresence from 'solid-presence'; // src/Content.tsx var DisclosureContext = createContext(); var createDisclosureContext = (contextId) => { if (contextId === void 0) return DisclosureContext; const context = createKeyedContext( `disclosure-${contextId}` ); return context; }; var useDisclosureContext = (contextId) => { if (contextId === void 0) { const context2 = useContext(DisclosureContext); if (!context2) { throw new Error( "[corvu]: Disclosure context not found. Make sure to wrap Disclosure components in <Disclosure.Root>" ); } return context2; } const context = useKeyedContext( `disclosure-${contextId}` ); if (!context) { throw new Error( `[corvu]: Disclosure context with id "${contextId}" not found. Make sure to wrap Disclosure components in <Disclosure.Root contextId="${contextId}">` ); } return context; }; var InternalDisclosureContext = createContext(); var createInternalDisclosureContext = (contextId) => { if (contextId === void 0) return InternalDisclosureContext; const context = createKeyedContext( `disclosure-internal-${contextId}` ); return context; }; var useInternalDisclosureContext = (contextId) => { if (contextId === void 0) { const context2 = useContext(InternalDisclosureContext); if (!context2) { throw new Error( "[corvu]: Disclosure context not found. Make sure to wrap Disclosure components in <Disclosure.Root>" ); } return context2; } const context = useKeyedContext( `disclosure-internal-${contextId}` ); if (!context) { throw new Error( `[corvu]: Disclosure context with id "${contextId}" not found. Make sure to wrap Disclosure components in <Disclosure.Root contextId="${contextId}">` ); } return context; }; // src/Content.tsx var DisclosureContent = (props) => { const [localProps, otherProps] = splitProps(props, ["forceMount", "contextId", "ref", "style"]); const context = createMemo(() => useInternalDisclosureContext(localProps.contextId)); const show = () => some(context().expanded, () => localProps.forceMount, context().contentPresent); const contentWidth = createMemo(() => { const contentSize = context().contentSize(); return contentSize ? contentSize[0] : void 0; }); const contentHeight = createMemo(() => { const contentSize = context().contentSize(); return contentSize ? contentSize[1] : void 0; }); return createComponent(Show, { get when() { return show() || context().collapseBehavior() === "hide"; }, get children() { return createComponent(Dynamic, mergeProps$1({ as: "div", ref(r$) { var _ref$ = mergeRefs(context().setContentRef, localProps.ref); typeof _ref$ === "function" && _ref$(r$); }, get style() { return combineStyle({ display: !show() ? "none" : void 0, "--corvu-disclosure-content-width": `${contentWidth()}px`, "--corvu-disclosure-content-height": `${contentHeight()}px` }, localProps.style); }, get id() { return context().disclosureId(); }, get ["data-expanded"]() { return dataIf(context().expanded()); }, get ["data-collapsed"]() { return dataIf(!context().expanded()); }, "data-corvu-disclosure-content": "" }, otherProps)); } }); }; var Content_default = DisclosureContent; var DisclosureRoot = (props) => { const defaultedProps = mergeProps({ initialExpanded: false, collapseBehavior: "remove", preventInitialContentAnimation: false, disclosureId: createUniqueId() }, props); const [expanded, setExpanded] = createControllableSignal({ value: () => defaultedProps.expanded, initialValue: defaultedProps.initialExpanded, onChange: defaultedProps.onExpandedChange }); const [contentRef, setContentRef] = createSignal(null); const [contentSize, setContentSize] = createSignal(null); const { present: contentPresent } = createPresence({ show: expanded, element: contentRef, onStateChange: (state) => { if (state === "present") { defaultedProps.onContentPresentChange?.(true); } else if (state === "hidden") { defaultedProps.onContentPresentChange?.(false); } } }); let initialOpen = defaultedProps.preventInitialContentAnimation && expanded(); createEffect((animationName) => { expanded(); const element = contentRef(); if (!element) return; if (initialOpen) { const origionalAnimationName = element.style.animationName; element.style.animationName = "none"; initialOpen = false; return origionalAnimationName; } if (animationName === void 0) return; element.style.animationName = animationName; }); createEffect(on([contentRef, contentPresent], ([element]) => { if (!element) { setContentSize(null); return; } untrack(() => { const origionalAnimationName = element.style.animationName; element.style.animationName = "none"; const width = element.offsetWidth; const height = element.offsetHeight; const currentSize = contentSize(); if (currentSize === null || width !== currentSize[0] || height !== currentSize[1]) { setContentSize([element.offsetWidth, element.offsetHeight]); } element.style.animationName = origionalAnimationName; }); })); const childrenProps = { get expanded() { return expanded(); }, setExpanded, get collapseBehavior() { return defaultedProps.collapseBehavior; }, get preventInitialContentAnimation() { return defaultedProps.preventInitialContentAnimation; }, get disclosureId() { return defaultedProps.disclosureId; }, get contentPresent() { return contentPresent(); }, get contentRef() { return contentRef(); }, get contentSize() { return contentSize(); } }; const memoizedChildren = createOnce(() => defaultedProps.children); const resolveChildren = () => { const children = memoizedChildren()(); if (isFunction(children)) { return children(childrenProps); } return children; }; const memoizedDisclosureRoot = createMemo(() => { const DisclosureContext2 = createDisclosureContext(defaultedProps.contextId); const InternalDisclosureContext2 = createInternalDisclosureContext(defaultedProps.contextId); return createComponent(DisclosureContext2.Provider, { value: { expanded, setExpanded, collapseBehavior: () => defaultedProps.collapseBehavior, preventInitialContentAnimation: () => defaultedProps.preventInitialContentAnimation, disclosureId: () => defaultedProps.disclosureId, contentPresent, contentRef, contentSize }, get children() { return createComponent(InternalDisclosureContext2.Provider, { value: { expanded, setExpanded, collapseBehavior: () => defaultedProps.collapseBehavior, preventInitialContentAnimation: () => defaultedProps.preventInitialContentAnimation, disclosureId: () => defaultedProps.disclosureId, contentPresent, contentRef, setContentRef, contentSize }, get children() { return untrack(() => resolveChildren()); } }); } }); }); return memoizedDisclosureRoot; }; var Root_default = DisclosureRoot; var DisclosureTrigger = (props) => { const [localProps, otherProps] = splitProps(props, ["contextId", "onClick"]); const context = createMemo(() => useInternalDisclosureContext(localProps.contextId)); const onClick = (e) => { !callEventHandler(localProps.onClick, e) && context().setExpanded((expanded) => !expanded); }; return createComponent(DynamicButton, mergeProps$1({ onClick, get ["aria-controls"]() { return context().disclosureId(); }, get ["aria-expanded"]() { return context().expanded() ? "true" : "false"; }, get ["data-collapsed"]() { return dataIf(!context().expanded()); }, get ["data-expanded"]() { return dataIf(context().expanded()); }, "data-corvu-disclosure-trigger": "" }, otherProps)); }; var Trigger_default = DisclosureTrigger; // src/index.ts var Disclosure = Object.assign(Root_default, { Trigger: Trigger_default, Content: Content_default, useContext: useDisclosureContext }); var index_default = Disclosure; export { Content_default as Content, Root_default as Root, Trigger_default as Trigger, index_default as default, useDisclosureContext as useContext };