@clayui/drop-down
Version:
ClayDropDown component
255 lines (254 loc) • 7.98 kB
JavaScript
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
};