UNPKG

@hitachivantara/uikit-react-core

Version:

Core React components for the NEXT Design System.

225 lines (224 loc) 7.87 kB
import { jsxs, jsx } from "react/jsx-runtime"; import { forwardRef, useMemo, Children, useRef, useEffect, useCallback, cloneElement } from "react"; import { useForkRef } from "@mui/material/utils"; import { useDefaultProps } from "@hitachivantara/uikit-react-utils"; import { useControlled } from "../hooks/useControlled.js"; import { useUniqueId } from "../hooks/useUniqueId.js"; import { isKey } from "../utils/keyboardUtils.js"; import { multiSelectionEventHandler } from "../utils/multiSelectionEventHandler.js"; import { setId } from "../utils/setId.js"; import { useClasses } from "./SelectionList.styles.js"; import { staticClasses } from "./SelectionList.styles.js"; import { HvLabel } from "../FormElement/Label/Label.js"; import { HvInfoMessage } from "../FormElement/InfoMessage/InfoMessage.js"; import { HvFormElement } from "../FormElement/FormElement.js"; import { HvListContainer } from "../ListContainer/ListContainer.js"; import { HvWarningText } from "../FormElement/WarningText/WarningText.js"; const getValueFromSelectedChildren = (children, multiple) => { const selectedValues = Children.toArray(children).map((child) => { const childIsControlled = child?.props?.selected !== void 0; const childIsSelected = child && childIsControlled ? child.props?.selected : child.props?.defaultSelected; return childIsSelected ? child?.props.value : void 0; }).filter((v) => v !== void 0); return multiple ? selectedValues : selectedValues?.[0]; }; const HvSelectionList = forwardRef(function HvSelectionList2(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( valueProp, defaultValue !== void 0 ? defaultValue : ( // when uncontrolled and no default value is given, // extract the initial selected values from the children own state () => getValueFromSelectedChildren(children, multiple) ) ); const [validationState, setValidationState] = useControlled( status, "standBy" ); const [validationMessage] = useControlled(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 } ) ] } ); }); export { HvSelectionList, staticClasses as selectionListClasses };