UNPKG

@hitachivantara/uikit-react-core

Version:
153 lines (152 loc) 5.91 kB
import { HvTypography } from "../Typography/Typography.js"; import { isKey, isOneOfKeys } from "../utils/keyboardUtils.js"; import { useUniqueId } from "../hooks/useUniqueId.js"; import { HvIcon } from "../icons.js"; import { useControlled } from "../hooks/useControlled.js"; import { useClasses } from "./BaseDropdown.styles.js"; import { HvDropdownPanel } from "./BaseDropdownPanel.js"; import { useDefaultProps } from "@hitachivantara/uikit-react-utils"; import { Fragment, cloneElement, forwardRef, isValidElement, useCallback, useId, useState } from "react"; import { jsx, jsxs } from "react/jsx-runtime"; import useEventCallback from "@mui/utils/useEventCallback"; import useForkRef from "@mui/utils/useForkRef"; //#region src/BaseDropdown/BaseDropdown.tsx var HvBaseDropdown = forwardRef(function BaseDropdown(props, ref) { const { id: idProp, className, classes: classesProp, children, role, placeholder, component, headerComponent: HeaderComponentProp, adornment, expanded, popperProps, variableWidth = false, placement: placementProp = "right", dropdownHeaderProps, defaultExpanded, disabled, readOnly, required, disablePortal, "aria-expanded": ariaExpandedProp, "aria-label": ariaLabelProp, "aria-labelledby": ariaLabelledByProp, dropdownHeaderRef: dropdownHeaderRefProp, onToggle, onClickOutside, onContainerCreation, ...others } = useDefaultProps("HvBaseDropdown", props); const { classes, cx } = useClasses(classesProp); const [computedPlacement, setComputedPlacement] = useState(); const [referenceElement, setReferenceElement] = useState(null); const [isOpen, setIsOpen] = useControlled(expanded, Boolean(defaultExpanded)); const headerRef = useForkRef(setReferenceElement, dropdownHeaderRefProp, dropdownHeaderProps?.ref); const customHeaderRef = useForkRef(ref, headerRef); const ariaRole = role || (component == null ? "combobox" : void 0); const ariaExpanded = ariaExpandedProp ?? (ariaRole ? !!isOpen : void 0); const id = useUniqueId(idProp); const containerId = useId(); const headerControlArias = { "aria-required": required ?? void 0, "aria-readonly": readOnly ?? void 0, "aria-disabled": disabled ?? void 0, "aria-expanded": ariaExpanded, "aria-owns": isOpen ? containerId : void 0, "aria-controls": isOpen ? containerId : void 0 }; const headerAriaLabels = { "aria-label": ariaLabelProp, "aria-labelledby": ariaLabelledByProp }; const handleToggle = useCallback((event) => { if (event && !isKey(event, "Tab")) event.preventDefault(); const notControlKey = !!event?.code && !isOneOfKeys(event, [ "Tab", "Enter", "Esc", "ArrowDown", "Space" ]); const ignoredCombinations = isKey(event, "Esc") && !isOpen || isKey(event, "ArrowDown") && isOpen || isKey(event, "Tab") && !isOpen; if (disabled || notControlKey || ignoredCombinations) return; const newOpen = !isOpen; setIsOpen(() => { if (!newOpen) referenceElement?.focus({ preventScroll: true }); return newOpen; }); onToggle?.(event, newOpen); }, [ isOpen, disabled, setIsOpen, onToggle, referenceElement ]); const defaultHeaderElement = /* @__PURE__ */ jsxs("div", { "data-popper-placement": computedPlacement, className: cx(classes.header, { [classes.headerOpen]: isOpen, [classes.headerReadOnly]: readOnly, [classes.headerDisabled]: disabled }), role: ariaRole === "combobox" ? "textbox" : void 0, ...headerAriaLabels, style: disabled || readOnly ? { pointerEvents: "none" } : void 0, tabIndex: disabled ? -1 : 0, ref: headerRef, ...dropdownHeaderProps, children: [/* @__PURE__ */ jsx("div", { className: cx(classes.selection, { [classes.selectionDisabled]: disabled }), children: placeholder && typeof placeholder === "string" ? /* @__PURE__ */ jsx(HvTypography, { noWrap: true, className: classes.placeholder, children: placeholder }) : placeholder }), /* @__PURE__ */ jsx("div", { className: classes.arrowContainer, children: adornment || /* @__PURE__ */ jsx(HvIcon, { name: "CaretDown", size: "xs", rotate: isOpen, className: classes.arrow }) })] }); const headerElement = component && isValidElement(component) ? cloneElement(component, { ref: headerRef, ...headerControlArias }) : defaultHeaderElement; const handleOutside = (event) => { if (!referenceElement?.contains(event.target)) { onClickOutside?.(event); setIsOpen(false); onToggle?.(event, false); } }; const hasCustomHeader = !!HeaderComponentProp; const HeaderComponent = HeaderComponentProp || "div"; const RootComponent = HeaderComponentProp ? Fragment : "div"; const onFirstUpdate = useEventCallback((state) => { setComputedPlacement(state.placement); onContainerCreation?.(state.elements?.popper ?? null, state); }); return /* @__PURE__ */ jsxs(RootComponent, { ...!hasCustomHeader && { className: classes.root }, children: [/* @__PURE__ */ jsx(HeaderComponent, { ref: hasCustomHeader ? customHeaderRef : ref, id, disabled: hasCustomHeader && disabled, className: cx(className, { [classes.anchor]: !hasCustomHeader, [classes.rootDisabled]: disabled }), ...!readOnly && { onKeyDown: handleToggle, onClick: handleToggle }, ...(ariaRole || hasCustomHeader) && { role: hasCustomHeader ? void 0 : ariaRole, ...headerAriaLabels, ...headerControlArias }, tabIndex: hasCustomHeader ? void 0 : -1, ...others, children: headerElement }), /* @__PURE__ */ jsx(HvDropdownPanel, { open: isOpen, classes: { container: classes.container, panel: classes.panel }, placement: `bottom-${placementProp === "right" ? "start" : "end"}`, variableWidth, containerId, onClickAway: handleOutside, disablePortal, anchorEl: referenceElement, onToggle: handleToggle, onFirstUpdate, popperOptions: popperProps, children })] }); }); //#endregion export { HvBaseDropdown };