@hitachivantara/uikit-react-core
Version:
UI Kit Core React components.
153 lines (152 loc) • 5.91 kB
JavaScript
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 };