@hitachivantara/uikit-react-core
Version:
Core React components for the NEXT Design System.
201 lines (200 loc) • 6.89 kB
JavaScript
import { jsxs, jsx } from "react/jsx-runtime";
import { useState, useMemo, useEffect } from "react";
import { useDefaultProps, mergeStyles } from "@hitachivantara/uikit-react-utils";
import { theme } from "@hitachivantara/uikit-styles";
import { useBaseDropdownContext } from "../../BaseDropdown/context.js";
import { CounterLabel } from "../../utils/CounterLabel.js";
import { setId } from "../../utils/setId.js";
import { getSelected } from "../utils.js";
import { useClasses } from "./List.styles.js";
import { staticClasses } from "./List.styles.js";
import { HvActionBar } from "../../ActionBar/ActionBar.js";
import { HvButton } from "../../Button/Button.js";
import { HvList } from "../../List/List.js";
import { HvInput } from "../../Input/Input.js";
import { HvCheckBox } from "../../CheckBox/CheckBox.js";
const clone = (values) => values.map((value) => ({ ...value }));
const cleanHidden = (lst) => lst.map((item) => ({ ...item, isHidden: false }));
const valuesExist = (values) => values != null && values?.length > 0;
const getSelectedIds = (list) => getSelected(list).map((item) => item.id || item.label);
const HvDropdownList = (props) => {
const {
id,
classes: classesProp,
values = [],
multiSelect = false,
showSearch = false,
onChange,
onCancel,
labels,
notifyChangesOnFirstRender = false,
hasTooltips = false,
singleSelectionToggle,
height: heightProp,
maxHeight: maxHeightProp,
virtualized = false,
...others
} = useDefaultProps("HvDropdownList", props);
const { classes, cx } = useClasses(classesProp);
const [searchStr, setSearchStr] = useState("");
const [list, setList] = useState(clone(values));
const [allSelected, setAllSelected] = useState(false);
const [anySelected, setAnySelected] = useState(false);
const { popper } = useBaseDropdownContext();
const { maxWidth, maxHeight } = popper?.styles.popper || {};
const hasChanges = useMemo(() => {
return String(getSelectedIds(values)) !== String(getSelectedIds(list));
}, [list, values]);
const newLabels = {
selectAll: labels?.selectAll,
selectionConjunction: labels?.multiSelectionConjunction
};
const updateSelectAll = (listValues) => {
if (!listValues) return;
const nbrSelected = getSelected(listValues).length;
const hasSelection = nbrSelected > 0;
const allSelect = nbrSelected === listValues.length;
setAnySelected(hasSelection);
setAllSelected(hasSelection && allSelect);
};
useEffect(() => {
if (!valuesExist(values)) return;
setList(clone(values));
updateSelectAll(values);
if (notifyChangesOnFirstRender) {
onChange?.(values, false, false, true);
}
}, [values, notifyChangesOnFirstRender, onChange]);
const handleSearch = (str) => {
const results = list?.filter(({ searchValue, label, value }) => {
const stringValue = typeof searchValue === "string" && searchValue || typeof label === "string" && label || typeof value === "string" && value || "";
return stringValue.toLowerCase().indexOf(str.toLowerCase()) >= 0;
});
if (results != null) {
const newList = list.map((elem) => {
const isResult = results.find((result) => result.label === elem.label);
return { ...elem, isHidden: !isResult };
});
setList(newList);
setSearchStr(str);
}
return str;
};
const renderSearch = () => /* @__PURE__ */ jsx("div", { className: classes.searchContainer, children: /* @__PURE__ */ jsx(
HvInput,
{
id: setId(id, "search"),
type: "search",
value: searchStr,
placeholder: labels?.searchPlaceholder,
"aria-label": labels?.searchPlaceholder,
onChange: (event, str) => handleSearch(str)
}
) });
const handleSelectAll = () => {
const newList = list.map((elem) => {
if (elem.disabled) return elem;
return { ...elem, selected: !anySelected };
});
setList(newList);
updateSelectAll(newList);
};
const renderSelectAll = () => {
return /* @__PURE__ */ jsx(
HvCheckBox,
{
id: setId(id, "select-all"),
label: /* @__PURE__ */ jsx(
CounterLabel,
{
selected: getSelected(list).length,
total: list.length,
conjunctionLabel: labels?.multiSelectionConjunction
}
),
onChange: handleSelectAll,
className: classes.selectAll,
indeterminate: anySelected && !allSelected,
checked: allSelected
}
);
};
const onSelection = (listValues) => {
if (!multiSelect) {
onChange(cleanHidden(listValues), true, true, true);
} else {
updateSelectAll(listValues);
setList(clone(listValues));
}
};
const renderActions = () => {
const applyLabel = labels?.applyLabel;
const cancelLabel = labels?.cancelLabel;
return /* @__PURE__ */ jsxs(HvActionBar, { id: setId(id, "actions"), children: [
/* @__PURE__ */ jsx(
HvButton,
{
id: setId(id, "actions-apply"),
disabled: !hasChanges,
onClick: () => onChange(cleanHidden(list), true, true, true),
variant: "primaryGhost",
children: applyLabel
}
),
/* @__PURE__ */ jsx(
HvButton,
{
id: setId(id, "actions-cancel"),
onClick: onCancel,
variant: "primaryGhost",
children: cancelLabel
}
)
] });
};
const showList = valuesExist(values);
const elementsSize = theme.spacing(
5 + 2 + (showSearch ? 5 : 0) + (showList && multiSelect ? 4 + 6 : 0)
);
return /* @__PURE__ */ jsxs("div", { className: classes.rootList, children: [
/* @__PURE__ */ jsx("div", { className: classes.listBorderDown }),
/* @__PURE__ */ jsxs("div", { className: classes.listContainer, children: [
showSearch && renderSearch(),
showList && multiSelect && renderSelectAll(),
showList && /* @__PURE__ */ jsx(
HvList,
{
id: setId(id, "list"),
style: mergeStyles(void 0, {
height: heightProp,
"--maxW": maxWidth,
"--maxH": maxHeightProp ?? `calc(${maxHeight} - ${elementsSize})`
}),
classes: {
root: cx(classes.dropdownListContainer, {
[classes.virtualized]: virtualized
})
},
values: list,
multiSelect,
useSelector: multiSelect,
showSelectAll: false,
onChange: onSelection,
labels: newLabels,
hasTooltips,
selectable: true,
condensed: true,
singleSelectionToggle,
height: heightProp,
virtualized,
...others
}
)
] }),
showList && multiSelect ? renderActions() : null
] });
};
export {
HvDropdownList,
staticClasses as dropdownListClasses
};