@hitachivantara/uikit-react-core
Version:
UI Kit Core React components.
171 lines (170 loc) • 6.85 kB
JavaScript
import { HvListContainer } from "../ListContainer/ListContainer.js";
import { isKey } from "../utils/keyboardUtils.js";
import { setId } from "../utils/setId.js";
import { useUniqueId } from "../hooks/useUniqueId.js";
import { HvFormElement } from "../FormElement/FormElement.js";
import { HvLabel } from "../FormElement/Label/Label.js";
import { HvInfoMessage } from "../FormElement/InfoMessage/InfoMessage.js";
import { HvWarningText } from "../FormElement/WarningText/WarningText.js";
import { useControlled as useControlled$1 } from "../hooks/useControlled.js";
import { multiSelectionEventHandler } from "../utils/multiSelectionEventHandler.js";
import { useClasses } from "./SelectionList.styles.js";
import { useDefaultProps } from "@hitachivantara/uikit-react-utils";
import { Children, cloneElement, forwardRef, useCallback, useEffect, useMemo, useRef } from "react";
import { jsx, jsxs } from "react/jsx-runtime";
import { useForkRef } from "@mui/material/utils";
//#region src/SelectionList/SelectionList.tsx
var getValueFromSelectedChildren = (children, multiple) => {
const selectedValues = Children.toArray(children).map((child) => {
const childIsControlled = child?.props?.selected !== void 0;
return (child && childIsControlled ? child.props?.selected : child.props?.defaultSelected) ? child?.props.value : void 0;
}).filter((v) => v !== void 0);
return multiple ? selectedValues : selectedValues?.[0];
};
/**
* SelectionList allows users to select one or more items from a list.
*
* While it supports multi-selection, it’s recommended to use it when it’s clear that only a single option should be selected.
*/
var HvSelectionList = forwardRef(function HvSelectionList(props, ref) {
const { id, classes: classesProp, className, children, name, value: valueProp, defaultValue, required, readOnly, disabled, label, "aria-label": ariaLabel, "aria-labelledby": ariaLabelledBy, description, "aria-describedby": ariaDescribedBy, onChange, status, statusMessage, "aria-errormessage": ariaErrorMessage, orientation = "vertical", multiple = false, singleSelectionToggle = false, ...others } = useDefaultProps("HvSelectionList", props);
const { classes, cx } = useClasses(classesProp);
const elementId = useUniqueId(id);
const [value, setValue] = useControlled$1(valueProp, defaultValue !== void 0 ? defaultValue : () => getValueFromSelectedChildren(children, multiple));
const [validationState, setValidationState] = useControlled$1(status, "standBy");
const [validationMessage] = useControlled$1(statusMessage, "Required");
const [allValues, selectedState] = useMemo(() => {
const childValues = [];
const childSelectedState = [];
Children.toArray(children).forEach((child, i) => {
const childValue = child?.props?.value;
const childIsSelected = multiple ? value.indexOf(childValue) !== -1 : value === childValue;
childValues[i] = childValue;
childSelectedState[i] = childIsSelected;
});
return [childValues, childSelectedState];
}, [
children,
multiple,
value
]);
const selectionAnchor = useRef(void 0);
const listRef = useRef(null);
const listForkedRef = useForkRef(ref, listRef);
useEffect(() => {
const handleMeta = (event) => {
const tempArray = [];
if (isKey(event, "ArrowUp") && event.shiftKey && listRef.current.contains(event.target) || isKey(event, "ArrowDown") && event.shiftKey && listRef.current.contains(event.target)) {
selectedState.forEach((isSelected, i) => {
if (i === event.target.value - 1) {
if (!isSelected) tempArray.push(allValues[i]);
} else if (isSelected) tempArray.push(allValues[i]);
});
setValue(tempArray);
}
};
window.addEventListener("keyup", handleMeta);
return () => {
window.removeEventListener("keyup", handleMeta);
};
}, [
allValues,
selectedState,
setValue
]);
const onChildChangeInterceptor = useCallback((index, childOnClick, evt) => {
childOnClick?.(evt);
if (!readOnly && !disabled) {
let newValue;
if (multiple) newValue = multiSelectionEventHandler(evt, index, selectionAnchor, allValues, selectedState, void 0);
else newValue = singleSelectionToggle && selectedState[index] ? null : allValues[index];
onChange?.(evt, newValue);
setValue(() => {
if (required && newValue.length === 0) setValidationState("invalid");
else setValidationState("valid");
return newValue;
});
}
}, [
allValues,
disabled,
multiple,
onChange,
readOnly,
required,
selectedState,
setValidationState,
setValue,
singleSelectionToggle,
selectionAnchor
]);
const modifiedChildren = useMemo(() => {
return Children.map(children, (child, i) => {
const childIsSelected = selectedState[i];
return cloneElement(child, {
role: "option",
selected: childIsSelected,
onClick: (evt) => onChildChangeInterceptor(i, child?.props?.onClick, evt),
disabled: disabled || child?.props?.disabled
});
});
}, [
children,
disabled,
onChildChangeInterceptor,
selectedState
]);
const canShowError = ariaErrorMessage == null && (status !== void 0 && statusMessage !== void 0 || status === void 0 && required);
const errorMessageId = canShowError ? setId(elementId, "error") : ariaErrorMessage;
const listId = label && setId(elementId, "listbox") || "";
return /* @__PURE__ */ jsxs(HvFormElement, {
id,
name,
status: validationState,
disabled,
required,
readOnly,
className: cx(classes.root, className),
children: [
label && /* @__PURE__ */ jsx(HvLabel, {
showGutter: true,
id: setId(elementId, "label"),
label,
className: classes.label
}),
description && /* @__PURE__ */ jsx(HvInfoMessage, {
id: setId(elementId, "description"),
className: classes.description,
children: description
}),
/* @__PURE__ */ jsx(HvListContainer, {
id: listId,
interactive: true,
condensed: true,
role: "listbox",
"aria-multiselectable": multiple || void 0,
"aria-label": ariaLabel,
"aria-labelledby": [label && setId(elementId, "label"), ariaLabelledBy].join(" ").trim() || void 0,
"aria-invalid": validationState === "invalid" ? true : void 0,
"aria-errormessage": validationState === "invalid" ? errorMessageId : void 0,
"aria-describedby": [description && setId(elementId, "description"), ariaDescribedBy].join(" ").trim() || void 0,
className: cx(classes.listbox, {
[classes.vertical]: orientation === "vertical",
[classes.horizontal]: orientation === "horizontal",
[classes.invalid]: validationState === "invalid"
}),
ref: listForkedRef,
...others,
children: modifiedChildren
}),
canShowError && /* @__PURE__ */ jsx(HvWarningText, {
id: setId(elementId, "error"),
disableBorder: true,
className: classes.error,
children: validationMessage
})
]
});
});
//#endregion
export { HvSelectionList };