@hitachivantara/uikit-react-core
Version:
UI Kit Core React components.
173 lines (172 loc) • 6.32 kB
JavaScript
import { fixedForwardRef } from "../types/generic.js";
import { HvListContainer } from "../ListContainer/ListContainer.js";
import { setId } from "../utils/setId.js";
import { useUniqueId } from "../hooks/useUniqueId.js";
import { HvFormElement } from "../FormElement/FormElement.js";
import { HvWarningText } from "../FormElement/WarningText/WarningText.js";
import { HvDropdownPanel } from "../BaseDropdown/BaseDropdownPanel.js";
import { HvDropdownButton } from "../DropdownButton/DropdownButton.js";
import { HvLabelContainer } from "../FormElement/LabelContainer.js";
import { HvOption } from "./Option.js";
import { useClasses } from "./Select.styles.js";
import { useDefaultProps } from "@hitachivantara/uikit-react-utils";
import { useRef, useState } from "react";
import { Fragment as Fragment$1, jsx, jsxs } from "react/jsx-runtime";
import { SelectProvider, useSelect } from "@mui/base";
import { useControlled, useForkRef } from "@mui/material/utils";
import { clsx } from "clsx";
//#region src/Select/Select.tsx
var noop = () => {};
function defaultRenderValue(options) {
if (Array.isArray(options)) return /* @__PURE__ */ jsx(Fragment$1, { children: options.map((o) => o.label).join(", ") });
return options?.label ?? null;
}
var mergeIds = (...ids) => clsx(ids) || void 0;
function renderOptions(options) {
return options?.map((option) => /* @__PURE__ */ jsx(HvOption, {
...option,
children: option.label
}, option.value));
}
/**
* The `HvSelect` component is a form control for choosing an option from a list.
*
* It aligns with the native `<select>` and `<option>` APIs, making it easy to integrate into forms.
*
* @example
* <HvSelect name="pets">
* <HvOption value="dog">Dog</HvOption>
* <HvOption value="cat">Cat</HvOption>
* </HvSelect>
* */
var HvSelect = fixedForwardRef(function HvSelect(props, ref) {
const { children: childrenProp, classes: classesProp, className, id: idProp, size, variant = "secondarySubtle", name, required, disabled: disabledProp, readOnly, label, open: openProp, defaultOpen, multiple, autoComplete, renderValue: renderValueProp, options: optionsProp, variableWidth, value: valueProp, defaultValue, placeholder, inputProps, enablePortal, "aria-label": ariaLabel, "aria-labelledby": ariaLabelledBy, description, "aria-describedby": ariaDescribedBy, status, statusMessage, "aria-errormessage": ariaErrorMessage, getSerializedValue, onClick, onChange, onOpenChange, ...others } = useDefaultProps("HvSelect", props);
const { classes, cx } = useClasses(classesProp);
const [placement, setPlacement] = useState("bottom-start");
const buttonRef = useRef(null);
const { contextValue, disabled, getButtonProps, getListboxProps, getHiddenInputProps, getOptionMetadata, value, open } = useSelect({
componentName: "HvSelect",
name,
required,
disabled: disabledProp,
multiple,
open: openProp,
defaultOpen,
value: valueProp,
defaultValue,
options: optionsProp,
buttonRef: useForkRef(ref, buttonRef),
getSerializedValue,
onChange,
onOpenChange: handleOpenChange
});
const renderValue = renderValueProp ?? defaultRenderValue;
const id = useUniqueId(idProp);
const labelId = useUniqueId(setId(idProp, "label"));
const descriptionId = useUniqueId(setId(idProp, "description"));
const errorMessageId = useUniqueId(setId(idProp, "error"));
const [validationMessage] = useControlled({
name: "HvSelect.statusMessage",
controlled: statusMessage,
default: "Required"
});
const [validationState, setValidationState] = useControlled({
name: "HvSelect.status",
controlled: status,
default: "standBy"
});
function handleOpenChange(newOpen) {
if (!newOpen) {
const hasValue = multiple ? value.length > 0 : !!value;
setValidationState(required && !hasValue ? "invalid" : "valid");
}
onOpenChange?.(newOpen);
}
const canShowError = ariaErrorMessage == null && (status !== void 0 && statusMessage !== void 0 || status === void 0 && required);
const isInvalid = validationState === "invalid";
const actualValue = multiple ? value.map((v) => getOptionMetadata(v)).filter((v) => v !== void 0) : getOptionMetadata(value) ?? null;
const children = childrenProp ?? renderOptions(optionsProp);
const isOpen = open && !!children;
return /* @__PURE__ */ jsxs(HvFormElement, {
name,
required,
disabled,
readOnly,
status: validationState,
className: cx(classes.root, className, {
[classes.readOnly]: readOnly,
[classes.disabled]: disabled
}),
...others,
children: [
/* @__PURE__ */ jsx(HvLabelContainer, {
label,
description,
inputId: id,
labelId,
descriptionId,
classes: {
root: classes.labelContainer,
label: classes.label,
description: classes.description
}
}),
/* @__PURE__ */ jsx(HvDropdownButton, {
id,
open: isOpen,
disabled,
readOnly,
className: cx(classes.select, { [classes.invalid]: validationState === "invalid" }),
"data-popper-placement": placement,
size,
variant,
"aria-label": ariaLabel,
"aria-labelledby": mergeIds(ariaLabelledBy, { [labelId]: label }),
"aria-invalid": isInvalid ? true : void 0,
"aria-errormessage": errorMessageId,
"aria-describedby": mergeIds(ariaDescribedBy, { [descriptionId]: description }),
...getButtonProps(),
children: renderValue(actualValue) ?? placeholder
}),
/* @__PURE__ */ jsx(HvDropdownPanel, {
role: "none",
open: isOpen,
keepMounted: true,
variableWidth,
disablePortal: !enablePortal,
anchorEl: buttonRef.current,
classes: {
container: classes.popper,
panel: classes.panel
},
placement,
onFirstUpdate: (state) => setPlacement(state.placement),
onClickAway: noop,
onToggle: noop,
children: /* @__PURE__ */ jsx(HvListContainer, {
condensed: true,
selectable: true,
"data-is-dropdown": true,
...getListboxProps(),
children: /* @__PURE__ */ jsx(SelectProvider, {
value: contextValue,
children
})
})
}),
/* @__PURE__ */ jsx("input", {
...getHiddenInputProps(),
autoComplete,
...inputProps
}),
canShowError && /* @__PURE__ */ jsx(HvWarningText, {
id: errorMessageId,
disableBorder: true,
className: classes.error,
children: validationMessage
})
]
});
});
//#endregion
export { HvSelect };