@solid-reach/disclosure
Version:
A disclosure component
199 lines (151 loc) • 4.49 kB
JavaScript
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 };