UNPKG

@clayui/drop-down

Version:
255 lines (254 loc) 7.98 kB
import { ClayButtonWithIcon } from "@clayui/button"; import { useControlledState, useInteractionFocus } from "@clayui/shared"; import classNames from "classnames"; import React, { useCallback, useEffect, useMemo, useRef, useState } from "react"; import { CSSTransition } from "react-transition-group"; import Caption from "./Caption"; import DropDown from "./DropDown"; import Help from "./Help"; import { ClayDropDownContext, DropDownItems, findNested } from "./Items"; import Search from "./Search"; function ClayDropDownWithDrilldown({ active: externalActive, alignmentByViewport, alignmentPosition, caption, className, containerElement, defaultActive = false, footerContent, helpText, defaultActiveMenu, initialActiveMenu, menuElementAttrs, menuHeight, menuWidth, menus, messages = { back: "Back", goTo: "Go to" }, offsetFn, onActiveChange, onSearchValueChange = () => { }, renderMenuOnClick, searchable, searchProps, searchValue = "", spritemap, trigger, triggerIcon = null }) { const [activeMenu, setActiveMenu] = useState( defaultActiveMenu ?? initialActiveMenu ); const [direction, setDirection] = useState(); const [history, setHistory] = useState([]); const triggerElementRef = useRef(null); const { isFocusVisible } = useInteractionFocus(); const [active, setActive] = useControlledState({ defaultName: "defaultActive", defaultValue: defaultActive, handleName: "onActiveChange", name: "active", onChange: onActiveChange, value: externalActive }); const focusHistoryRef = useRef([]); const innerRef = useRef(null); const menuIds = Object.keys(menus); const Wrap = footerContent ? "form" : React.Fragment; useEffect(() => { if (!isFocusVisible()) { return; } if (innerRef.current) { if (direction === "prev") { const [previous] = focusHistoryRef.current.slice(-1); focusHistoryRef.current = focusHistoryRef.current.slice( 0, focusHistoryRef.current.length - 1 ); previous?.focus(); } else { const itemEl = innerRef.current.querySelector( ".drilldown-current a.dropdown-item, .drilldown-current button.dropdown-item" ); focusHistoryRef.current = [ ...focusHistoryRef.current, document.activeElement ]; itemEl?.focus(); } } }, [activeMenu, direction, innerRef, focusHistoryRef, menus]); const onBack = useCallback(() => { const [parent] = history.slice(-1); setHistory(history.slice(0, history.length - 1)); setDirection("prev"); setActiveMenu(parent.id); }, [history]); const onForward = useCallback( (label, id) => { setHistory((history2) => [ ...history2, { id: activeMenu, title: label } ]); setDirection("next"); setActiveMenu(id); }, [activeMenu] ); const onClose = useCallback(() => { setActive(false); triggerElementRef.current?.focus(); }, []); const hasLeftSymbols = useMemo( () => menus[activeMenu] ? findNested(menus[activeMenu], "symbolLeft") : false, [activeMenu, menus] ); return /* @__PURE__ */ React.createElement( DropDown, { active, alignmentByViewport, alignmentPosition, className, containerElement, hasLeftSymbols, hasRightSymbols: true, menuElementAttrs: { ...menuElementAttrs, className: classNames(menuElementAttrs?.className, "drilldown") }, menuHeight, menuWidth, offsetFn, onActiveChange: (value) => { if (!active) { setActiveMenu(defaultActiveMenu); focusHistoryRef.current = []; setHistory([]); setDirection("prev"); } setActive(value); }, renderMenuOnClick, trigger: React.cloneElement(trigger, { ref: (node) => { if (node) { triggerElementRef.current = node; const { ref } = trigger; if (typeof ref === "function") { ref(node); } } } }), triggerIcon }, /* @__PURE__ */ React.createElement( ClayDropDownContext.Provider, { value: { close: onClose, messages, onForward } }, /* @__PURE__ */ React.createElement("div", { className: "drilldown-inner", ref: innerRef }, menuIds.map((menuKey) => { const items = menus[menuKey].map( ({ label, title, ...item }) => ({ ...item, label: label || title }) ); const header = activeMenu === menuKey && !!history.length ? history.slice(-1)[0].title : void 0; const initialClasses = classNames("transitioning", { "drilldown-prev-initial": direction === "prev" }); const active2 = activeMenu === menuKey; return /* @__PURE__ */ React.createElement( CSSTransition, { "aria-hidden": !active2, className: classNames("drilldown-item", { "drilldown-current": active2 }), classNames: { enter: initialClasses, enterActive: `drilldown-transition drilldown-${direction}-active`, exit: initialClasses, exitActive: `drilldown-transition drilldown-${direction}-active` }, in: active2, key: menuKey, timeout: 250 }, /* @__PURE__ */ React.createElement("div", { className: "drilldown-item-inner" }, header && /* @__PURE__ */ React.createElement(React.Fragment, null, /* @__PURE__ */ React.createElement( "div", { className: "dropdown-header", "data-testid": `back-button-${header}`, onClick: onBack }, /* @__PURE__ */ React.createElement( ClayButtonWithIcon, { "aria-label": messages.back, className: "component-action dropdown-item-indicator-start", onClick: onBack, spritemap, symbol: "angle-left", tabIndex: -1, title: messages.back } ), /* @__PURE__ */ React.createElement("span", { className: "dropdown-item-indicator-text-start" }, header) ), /* @__PURE__ */ React.createElement("div", { className: "dropdown-divider" })), defaultActiveMenu === menuKey ? /* @__PURE__ */ React.createElement(React.Fragment, null, helpText && /* @__PURE__ */ React.createElement(Help, null, helpText), searchable && /* @__PURE__ */ React.createElement( Search, { ...searchProps, onChange: onSearchValueChange, spritemap, value: searchValue } ), /* @__PURE__ */ React.createElement(Wrap, null, footerContent ? /* @__PURE__ */ React.createElement("div", { className: "inline-scroller" }, /* @__PURE__ */ React.createElement( DropDownItems, { items, spritemap } )) : /* @__PURE__ */ React.createElement( DropDownItems, { items, spritemap } ), caption && /* @__PURE__ */ React.createElement(Caption, null, caption), footerContent && /* @__PURE__ */ React.createElement( "div", { className: "dropdown-section", role: "presentation" }, footerContent ))) : /* @__PURE__ */ React.createElement( DropDownItems, { items, spritemap } )) ); })) ) ); } ClayDropDownWithDrilldown.displayName = "ClayDropDownWithDrilldown"; export { ClayDropDownWithDrilldown };