UNPKG

@hitachivantara/uikit-react-core

Version:
171 lines (170 loc) 6.85 kB
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 };