UNPKG

@clayui/drop-down

Version:
239 lines (238 loc) 6.86 kB
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 };