@clayui/drop-down
Version:
ClayDropDown component
239 lines (238 loc) • 6.86 kB
JavaScript
import { __NOT_PUBLIC_COLLECTION } from "@clayui/core";
import ClayIcon from "@clayui/icon";
import {
FOCUSABLE_ELEMENTS,
Keys,
getFocusableList,
useControlledState,
useNavigation
} from "@clayui/shared";
import classNames from "classnames";
import React, { useCallback, useMemo, useRef, useState } from "react";
import Action from "./Action";
import Caption from "./Caption";
import Divider from "./Divider";
import { DropDownContext } from "./DropDownContext";
import { FocusMenu } from "./FocusMenu";
import Group from "./Group";
import Help from "./Help";
import Item from "./Item";
import ItemList from "./ItemList";
import Menu from "./Menu";
import Search from "./Search";
import Section from "./Section";
const { Collection } = __NOT_PUBLIC_COLLECTION;
const List = React.forwardRef(function List2({ children, ...otherProps }, ref) {
return /* @__PURE__ */ React.createElement("ul", { ...otherProps, className: "list-unstyled", ref }, children);
});
let counter = 0;
function DropDown({
active,
alignmentByViewport,
alignmentPosition,
children,
className,
closeOnClick = false,
closeOnClickOutside,
containerElement: ContainerElement = "div",
defaultActive = false,
filterKey,
hasLeftSymbols,
hasRightSymbols,
items,
menuElementAttrs,
menuHeight,
menuWidth,
offsetFn,
onActiveChange,
renderMenuOnClick = false,
role = "menu",
trigger,
triggerIcon = null,
...otherProps
}) {
const triggerElementRef = useRef(null);
const menuElementRef = useRef(null);
const [initialized, setInitialized] = useState(!renderMenuOnClick);
const [internalActive, setInternalActive] = useControlledState({
defaultName: "defaultActive",
defaultValue: defaultActive,
handleName: "onActiveChange",
name: "active",
onChange: onActiveChange,
value: active
});
const [search, setSearch] = useState("");
const ariaControls = useMemo(() => {
counter++;
return `clay-dropdown-menu-${counter}`;
}, []);
const openMenu = useCallback(
(value) => {
if (!initialized) {
setInitialized(true);
}
setInternalActive(value);
},
[initialized]
);
const { navigationProps } = useNavigation({
activation: "manual",
containerRef: menuElementRef,
loop: true,
orientation: "vertical",
typeahead: true,
visible: internalActive
});
return /* @__PURE__ */ React.createElement(
ContainerElement,
{
...otherProps,
className: classNames("dropdown", className)
},
React.cloneElement(trigger, {
"aria-controls": internalActive ? ariaControls : void 0,
"aria-expanded": internalActive,
"aria-haspopup": "true",
"children": React.isValidElement(trigger) && // @ts-ignore
trigger?.type.displayName === "ClayButton" && triggerIcon ? /* @__PURE__ */ React.createElement(React.Fragment, null, trigger.props.children, " ", /* @__PURE__ */ React.createElement(ClayIcon, { className: "ml-1", symbol: triggerIcon })) : trigger.props.children,
"className": classNames(
"dropdown-toggle",
trigger.props.className
),
"onClick": (event) => {
if (trigger.props.onClick) {
trigger.props.onClick(event);
}
openMenu(!internalActive);
},
"onKeyDown": (event) => {
if (trigger.props.onKeyDown) {
trigger.props.onKeyDown(event);
}
if (event.key === Keys.Spacebar) {
openMenu(!internalActive);
}
if (event.key === Keys.Down) {
event.preventDefault();
event.stopPropagation();
openMenu(true);
}
if (internalActive && event.key === Keys.Down) {
event.preventDefault();
event.stopPropagation();
const list = getFocusableList(menuElementRef);
if (list.length) {
list[0].focus();
}
}
if ([Keys.Spacebar, Keys.Down].includes(event.key)) {
event.preventDefault();
}
},
"ref": (node) => {
triggerElementRef.current = node;
const { ref } = trigger;
if (typeof ref === "function") {
ref(node);
}
}
}),
initialized && /* @__PURE__ */ React.createElement(
Menu,
{
...menuElementAttrs,
active: internalActive,
alignElementRef: triggerElementRef,
alignmentByViewport,
alignmentPosition,
closeOnClickOutside,
hasLeftSymbols,
hasRightSymbols,
height: menuHeight,
id: ariaControls,
offsetFn,
onActiveChange: setInternalActive,
onKeyDown: (event) => {
if (menuElementAttrs?.onKeyDown) {
menuElementAttrs.onKeyDown(event);
}
if (event.key === Keys.Tab) {
event.preventDefault();
openMenu(false);
const list = Array.from(
document.querySelectorAll(
FOCUSABLE_ELEMENTS.join(",")
)
);
const position = list.indexOf(
triggerElementRef.current
);
const nextElement = list[position + 1];
if (nextElement) {
nextElement.focus();
}
}
navigationProps.onKeyDown(event);
},
ref: menuElementRef,
suppress: [triggerElementRef, menuElementRef],
triggerRef: triggerElementRef,
width: menuWidth
},
/* @__PURE__ */ React.createElement(
FocusMenu,
{
condition: internalActive,
onRender: () => {
setTimeout(() => {
const list = getFocusableList(menuElementRef);
if (list.length) {
list[0].focus();
}
}, 10);
}
},
/* @__PURE__ */ React.createElement(
DropDownContext.Provider,
{
value: {
close: () => {
setInternalActive(false);
triggerElementRef.current?.focus();
},
closeOnClick,
filterKey,
onSearch: setSearch,
search,
tabFocus: false
}
},
children instanceof Function ? /* @__PURE__ */ React.createElement(
Collection,
{
as: List,
items,
role
},
children
) : children
)
)
)
);
}
DropDown.Action = Action;
DropDown.Caption = Caption;
DropDown.Divider = Divider;
DropDown.Group = Group;
DropDown.Help = Help;
DropDown.Menu = Menu;
DropDown.Item = Item;
DropDown.ItemList = ItemList;
DropDown.Search = Search;
DropDown.Section = Section;
var DropDown_default = DropDown;
export {
DropDown_default as default
};