UNPKG

@hitachivantara/uikit-react-core

Version:

Core React components for the NEXT Design System.

253 lines (252 loc) 8.42 kB
import { jsx, jsxs, Fragment } from "react/jsx-runtime"; import { useRef, useEffect, useMemo, forwardRef, isValidElement } from "react"; import { FixedSizeList } from "react-window"; import { useDefaultProps } from "@hitachivantara/uikit-react-utils"; import { HvIcon } from "../icons.js"; import { CounterLabel } from "../utils/CounterLabel.js"; import { setId } from "../utils/setId.js"; import { useClasses } from "./List.styles.js"; import { staticClasses } from "./List.styles.js"; import { useSelectableList } from "./useSelectableList.js"; import { parseList } from "./utils.js"; import { HvCheckBox } from "../CheckBox/CheckBox.js"; import { HvRadio } from "../Radio/Radio.js"; import { HvOverflowTooltip } from "../OverflowTooltip/OverflowTooltip.js"; import { HvLink } from "../Link/Link.js"; import { HvListContainer } from "../ListContainer/ListContainer.js"; import { HvListItem } from "../ListContainer/ListItem/ListItem.js"; const DEFAULT_LABELS = { /** The label used for the All checkbox action. @deprecated no longer used */ selectAll: "Select All", /** The label used in the middle of the multi-selection count. */ selectionConjunction: "/" }; const HvList = (props) => { const { id, classes: classesProp, className, multiSelect = false, // eslint-disable-next-line @typescript-eslint/no-unused-vars hasTooltips = false, showSelectAll = false, labels = DEFAULT_LABELS, useSelector = false, selectable = true, singleSelectionToggle = true, condensed = false, onChange, onClick, values: valuesProp = [], height, virtualized = false, ...others } = useDefaultProps("HvList", props); const { classes, cx } = useClasses(classesProp); const [list, setList, selection] = useSelectableList(valuesProp); const listRef = useRef(null); useEffect(() => { const passedProps = { multiSelect, selectable, singleSelectionToggle }; const parsedList = parseList( void 0, passedProps, void 0, valuesProp ); setList(parsedList); }, [valuesProp, multiSelect, selectable, singleSelectionToggle, setList]); const [role, itemRole] = useMemo(() => { if (selectable && useSelector) return ["list", "listitem"]; if (selectable) return ["listbox", "option"]; return ["menu", "menuitem"]; }, [selectable, useSelector]); const handleSelect = (evt, item) => { if (!item.path) evt.preventDefault(); if (item.disabled) return; const passedProps = { multiSelect, selectable, singleSelectionToggle }; const parsedList = parseList(item, passedProps, void 0, list); setList(parsedList); onClick?.(evt, item); onChange?.(parsedList); }; const handleSelectAll = () => { const passedProps = { multiSelect, selectable, singleSelectionToggle }; const anySelectableSelected = list.some( (elem) => elem.selected || elem.disabled ); const parsedList = parseList( void 0, passedProps, !anySelectableSelected, list ); setList(parsedList); onChange?.(parsedList); }; const renderLeftIcon = (item) => { return isValidElement(item.icon) ? item.icon : typeof item.icon === "function" ? item.icon?.({ isSelected: item.selected, isDisabled: item.disabled }) : null; }; const renderSelectAll = () => { const anySelected2 = !!selection?.length; const allSelected = selection.length === list.length; return /* @__PURE__ */ jsx( HvCheckBox, { id: setId(id, "select-all"), label: /* @__PURE__ */ jsx( CounterLabel, { selected: selection.length, total: list.length, conjunctionLabel: labels.selectionConjunction } ), onChange: handleSelectAll, className: classes.selectAllSelector, indeterminate: anySelected2 && !allSelected, checked: allSelected } ); }; const renderItemText = (item) => { return !multiSelect && item.path ? /* @__PURE__ */ jsx(HvLink, { route: item.path, classes: { a: classes.link }, children: /* @__PURE__ */ jsx(HvOverflowTooltip, { data: item.label }) }) : /* @__PURE__ */ jsx(HvOverflowTooltip, { data: item.label }); }; const renderSelectItem = (item, itemId) => { if (!useSelector) return renderItemText(item); const Component = multiSelect ? HvCheckBox : HvRadio; return /* @__PURE__ */ jsx( Component, { id: setId(itemId, "selector"), label: /* @__PURE__ */ jsx(HvOverflowTooltip, { data: item.label }), checked: item.selected || false, disabled: item.disabled, onChange: multiSelect ? (evt) => handleSelect(evt, item) : void 0, classes: { root: classes.selectorRoot, container: classes.selectorContainer, label: classes.truncate } } ); }; const renderListItem = (item, i, otherProps = {}) => { const itemId = setId(id, "item", i); const selected = item.selected || false; const startAdornment = !useSelector && item.icon ? renderLeftIcon(item) : null; return /* @__PURE__ */ jsx( HvListItem, { id: itemId, role: itemRole, disabled: item.disabled || void 0, className: classes.item, classes: { selected: cx({ [classes.itemSelector]: useSelector || multiSelect }) }, selected: multiSelect || selected ? selected : void 0, onClick: (evt) => handleSelect(evt, item), startAdornment, endAdornment: item.showNavIcon && /* @__PURE__ */ jsx(HvIcon, { name: "CaretRight", className: classes.box, size: "xs" }), ...otherProps, children: renderSelectItem(item, itemId) }, i ); }; const filteredList = list.filter((it) => !it.isHidden); const anySelected = list.map((item) => item.selected && !item.disabled).reduce((result, selected) => result || selected, false); const selectedItemIndex = list.findIndex((item) => item.selected); useEffect(() => { if (selectedItemIndex >= 0 && listRef.current !== null) { listRef.current.scrollToItem(selectedItemIndex); } }, [listRef, selectedItemIndex]); const renderVirtualizedListItem = ({ index, style }) => { const item = filteredList[index]; const tabIndex = item.tabIndex || !anySelected && index === 0 || item.selected && !item.disabled ? 0 : -1; return renderListItem(item, index, { style: { ...style, top: `${parseFloat(style.top) + 5}px`, left: `${parseFloat(style.left) + 5}px`, width: `calc(${parseFloat(style.width)}% - 10px)` }, tabIndex, interactive: true, condensed, disableGutters: useSelector }); }; const ariaMultiSelectable = role === "listbox" && multiSelect || void 0; const ListContainer = useMemo(() => { return forwardRef( ({ ...rest }, ref) => /* @__PURE__ */ jsx( HvListContainer, { id, className: cx(classes.root, className), role, interactive: true, condensed, disableGutters: useSelector, "aria-multiselectable": ariaMultiSelectable, ref, ...rest } ) ); }, [ cx, id, useSelector, className, classes.root, role, condensed, ariaMultiSelectable ]); if (filteredList.length === 0) return null; return /* @__PURE__ */ jsxs(Fragment, { children: [ multiSelect && useSelector && showSelectAll && renderSelectAll(), !virtualized ? /* @__PURE__ */ jsx( HvListContainer, { id, className: cx(classes.root, className), role, interactive: true, condensed, disableGutters: useSelector, "aria-multiselectable": ariaMultiSelectable, ...others, children: filteredList.map((item, i) => renderListItem(item, i)) } ) : /* @__PURE__ */ jsx( FixedSizeList, { ref: listRef, className: classes.virtualizedRoot, height: (height || 0) + 5, width: "100%", itemCount: filteredList.length, itemSize: condensed ? 32 : 40, innerElementType: ListContainer, ...others, children: renderVirtualizedListItem } ) ] }); }; export { HvList, staticClasses as listClasses };