UNPKG

@solid-reach/disclosure

Version:
133 lines (104 loc) 3.79 kB
import { createContext, useContext, mergeProps, createMemo, createSignal, createEffect, splitProps } from 'solid-js'; import { Dynamic } from 'solid-js/web'; 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 <DisclosureContext.Provider value={context}> {props.children} </DisclosureContext.Provider>; } 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 <Dynamic aria-controls={context.panelId()} aria-expanded={context.open()} id={`button---${context.disclosureId()}`} {...others} component={local.as} onClick={handleClick} ref={composeRefs(button, props)} data-reach-disclosure-button="" data-state={context.open() ? 'open' : 'collapsed'}> {local.children} </Dynamic>; } function DisclosurePanel(props) { props = mergeProps({ as: 'div' }, props); const [local, others] = splitProps(props, ['as', 'children', 'ref']); const context = useContext(DisclosureContext); return <Dynamic hidden={!context.open()} {...others} ref={props.ref} component={local.as} data-reach-disclosure-panel="" data-state={context.open() ? 'open' : 'collapsed'} id={context.panelId()}> {local.children} </Dynamic>; } export { Disclosure, DisclosureButton, DisclosurePanel, useDisclosureContext };