UNPKG

@solid-reach/disclosure

Version:
199 lines (151 loc) 4.49 kB
import { createComponent, Dynamic, mergeProps as mergeProps$1 } from 'solid-js/web'; import { createContext, useContext, mergeProps, createMemo, createSignal, createEffect, splitProps } from 'solid-js'; function isFunction(value) { // eslint-disable-next-line eqeqeq return !!(value && {}.toString.call(value) == '[object Function]'); } function composeRefs(ownRef, ...props) { return ref => { ownRef.current = ref; for (const prop of props) { if (!prop.ref) continue; if (isFunction(prop.ref)) prop.ref(ref); } }; } let id = 0; function createId(propsId) { if (propsId) return String(propsId); return String(id++); } const DisclosureContext = /*#__PURE__*/createContext({}); function useDisclosureContext() { return useContext(DisclosureContext); } var isProduction = process.env.NODE_ENV === 'production'; function warning(condition, message) { if (!isProduction) { if (condition) { return; } var text = "Warning: " + message; if (typeof console !== 'undefined') { console.warn(text); } try { throw Error(text); } catch (x) {} } } function Disclosure(props) { props = mergeProps({ defaultOpen: false }, props); const isControlled = () => props.open != null; const id = createMemo(() => createId(props.id) || 'disclosure'); const panelId = () => `panel---${id()}`; const [isOpen, setIsOpen] = createSignal(isControlled() ? props.open : props.defaultOpen); if (process.env.NODE_ENV !== "production") { const wasControlled = isControlled(); createEffect(() => { process.env.NODE_ENV !== "production" ? warning(!(isControlled() && !wasControlled || !isControlled() && wasControlled), 'Disclosure is changing from controlled to uncontrolled. Disclosure should not switch from controlled to uncontrolled (or vice versa). Decide between using a controlled or uncontrolled Disclosure for the lifetime of the component. Check the `open` prop being passed in.') : void 0; }); } function onSelect() { var _a; (_a = props.onChange) === null || _a === void 0 ? void 0 : _a.call(props); if (!isControlled()) { setIsOpen(isOpen => !isOpen); } } const context = { disclosureId: id, onSelect, open: isOpen, panelId }; if (isControlled() && props.open !== isOpen()) { /* * If the component is controlled, we'll sync internal state with the * controlled state */ setIsOpen(props.open); } return createComponent(DisclosureContext.Provider, { value: context, get children() { return props.children; } }); } function DisclosureButton(props) { props = mergeProps({ as: 'button' }, props); const [local, others] = splitProps(props, ['as', 'children', 'ref']); const button = {}; const context = useContext(DisclosureContext); function handleClick(event) { event.preventDefault(); button.current && button.current.focus(); context.onSelect(); } return createComponent(Dynamic, mergeProps$1({ get ["aria-controls"]() { return context.panelId(); }, get ["aria-expanded"]() { return context.open(); }, get id() { return `button---${context.disclosureId()}`; } }, others, { get component() { return local.as; }, onClick: handleClick, ref(r$) { const _ref$ = composeRefs(button, props); typeof _ref$ === "function" && _ref$(r$); }, "data-reach-disclosure-button": "", get ["data-state"]() { return context.open() ? 'open' : 'collapsed'; }, get children() { return local.children; } })); } function DisclosurePanel(props) { props = mergeProps({ as: 'div' }, props); const [local, others] = splitProps(props, ['as', 'children', 'ref']); const context = useContext(DisclosureContext); return createComponent(Dynamic, mergeProps$1({ get hidden() { return !context.open(); } }, others, { ref(r$) { const _ref$ = props.ref; typeof _ref$ === "function" ? _ref$(r$) : props.ref = r$; }, get component() { return local.as; }, "data-reach-disclosure-panel": "", get ["data-state"]() { return context.open() ? 'open' : 'collapsed'; }, get id() { return context.panelId(); }, get children() { return local.children; } })); } export { Disclosure, DisclosureButton, DisclosurePanel, useDisclosureContext };