UNPKG

rsuite

Version:

A suite of react components

100 lines 2.83 kB
'use client'; // Headless Disclosure // Ref: https://w3c.github.io/aria-practices/#disclosure import React, { useMemo, useReducer, useRef, useCallback, useContext } from 'react'; import DisclosureContext, { DisclosureActionTypes } from "./DisclosureContext.js"; import DisclosureButton from "./DisclosureButton.js"; import DisclosureContent from "./DisclosureContent.js"; import useClickOutside from "../hooks/useClickOutside.js"; const initialDisclosureState = { open: false }; function disclosureReducer(state, action) { switch (action.type) { case DisclosureActionTypes.Show: return { ...state, open: true }; case DisclosureActionTypes.Hide: return { ...state, open: false }; } return state; } const Disclosure = /*#__PURE__*/React.memo(props => { const { children, open: openProp, defaultOpen = false, hideOnClickOutside = false, onToggle, trigger = ['click'] } = props; const parentDisclosure = useContext(DisclosureContext); const [{ open: openState }, dispatch] = useReducer(disclosureReducer, { ...initialDisclosureState, open: defaultOpen }); const containerElementRef = useRef(null); const open = openProp ?? openState; useClickOutside({ enabled: hideOnClickOutside, isOutside: event => !containerElementRef.current?.contains(event.target), handle: () => dispatch({ type: DisclosureActionTypes.Hide }) }); const onMouseEnter = useCallback(event => { if (!open) { dispatch({ type: DisclosureActionTypes.Show }); onToggle?.(true, event); } }, [open, dispatch, onToggle]); const onMouseLeave = useCallback(event => { if (open) { dispatch({ type: DisclosureActionTypes.Hide }); onToggle?.(false, event); } }, [open, dispatch, onToggle]); const contextValue = useMemo(() => { const cascadeDispatch = action => { const result = dispatch(action); if ('cascade' in action) { parentDisclosure?.[1](action); } return result; }; return [{ open }, cascadeDispatch, { onToggle, trigger }]; }, [parentDisclosure, open, dispatch, onToggle, trigger]); const renderProps = useMemo(() => { const renderProps = { open }; if (trigger.includes('hover')) { renderProps.onMouseEnter = onMouseEnter; renderProps.onMouseLeave = onMouseLeave; } return renderProps; }, [open, trigger, onMouseEnter, onMouseLeave]); return /*#__PURE__*/React.createElement(DisclosureContext.Provider, { value: contextValue }, children(renderProps, containerElementRef)); }); Disclosure.displayName = 'Disclosure'; Disclosure.Button = DisclosureButton; Disclosure.Content = DisclosureContent; export default Disclosure;