@carbon/ibm-products
Version:
Carbon for IBM Products
167 lines (165 loc) • 7.13 kB
JavaScript
/**
* Copyright IBM Corp. 2020, 2026
*
* This source code is licensed under the Apache-2.0 license found in the
* LICENSE file in the root directory of this source tree.
*/
import { pkg } from "../../settings.js";
import { useId } from "../../global/js/utils/useId.js";
import { AddSelectList } from "./AddSelectList.js";
import { AddSelectSort } from "./AddSelectSort.js";
import { sortItems } from "./add-select-utils.js";
import { useItemSort } from "./hooks/useItemSort.js";
import useParentSelect from "./hooks/useParentSelect.js";
import React, { useState } from "react";
import PropTypes from "prop-types";
import { Checkbox, IconButton, Popover, PopoverContent, Search, Tag, unstable_FeatureFlags } from "@carbon/react";
import { Filter } from "@carbon/react/icons";
//#region src/components/AddSelect/AddSelectColumn.jsx
const blockClass = `${pkg.prefix}--add-select`;
const colClass = `${blockClass}__column`;
const componentName = "AddSelectColumn";
let AddSelectColumn = ({ columnInputPlaceholder, filterByLabel, header, items, multiSelection, parentId, path, setMultiSelection, setPath, sortByLabel, ...props }) => {
const { parentSelected, setParentSelected } = useParentSelect();
const selectAllId = useId();
const filterId = useId();
const searchId = useId();
const [searchTerm, setSearchTerm] = useState("");
const { sortDirection, setSortDirection, sortAttribute, setSortAttribute } = useItemSort();
const [filterOpen, setFilterOpen] = useState(false);
const [filters, setFilters] = useState([]);
const { entries, filterBy, sortBy } = items;
const getSelectedItem = () => {
const parentInPath = path.find((entry) => entry.parentId === parentId);
/**
* this check helps ensure that when the state of the open columns is cleared by global search
* that the columns are rebuilt from the data in the path array
*/
if (parentInPath && !parentSelected) return entries.find((item) => item.id === parentInPath.id);
return entries.find((item) => item.id === parentSelected) ?? null;
};
const selectedItem = getSelectedItem();
/**
* this check helps prevent children from already being open when you switch from parents
* on the same level. for example- using the storybook example, if you click on folder 1,
* file 1, folder 2, and then folder 1 again file 1 children shouldn't be expanded. this
* check ensures that when a user navigates to parents on the same level that the open state
* of their children is cleared by referencing the path array.
*/
const itemInPath = selectedItem && path.find((entry) => entry.id === selectedItem.id);
const allSelected = entries.every((item) => multiSelection.includes(item.id));
const filterByOpts = filterBy ? entries.map((item) => item[filterBy]) : [];
const selectAllHandler = (event, { checked }) => {
const itemIds = entries.map((item) => item.id);
if (checked) setMultiSelection([...new Set([...multiSelection, ...itemIds])]);
else setMultiSelection(multiSelection.filter((i) => !itemIds.includes(i)));
};
const filterHandler = (checked, opt) => {
if (checked) setFilters([...filters, opt]);
else setFilters(filters.filter((o) => o !== opt));
};
const filterBySearch = (item) => item.title.toLowerCase().includes(searchTerm);
const filterByAttribute = (item) => {
if (filters.length === 0) return true;
const { filterBy } = item;
const filterByValue = item[filterBy];
return filters.some((filter) => filter === filterByValue);
};
const filterBtnId = `filter-${filterId}`;
const sortFn = sortItems(sortAttribute, sortDirection);
const colItems = entries.filter(filterBySearch).filter(filterByAttribute).sort(sortFn);
const parentSelectionHandler = (id, title) => {
setParentSelected(id);
setPath(id, title, parentId);
};
return /* @__PURE__ */ React.createElement(React.Fragment, null, /* @__PURE__ */ React.createElement("div", { className: colClass }, /* @__PURE__ */ React.createElement("div", { className: `${colClass}-search-bar` }, /* @__PURE__ */ React.createElement(Search, {
value: searchTerm,
onChange: (e) => setSearchTerm(e.target.value),
placeholder: columnInputPlaceholder,
className: `${colClass}-input`,
id: searchId,
labelText: columnInputPlaceholder,
size: "md"
}), /* @__PURE__ */ React.createElement("div", { className: `${colClass}-sort-filter` }, /* @__PURE__ */ React.createElement(AddSelectSort, {
items: entries,
setSortAttribute,
setSortDirection,
sortAttribute,
sortDirection,
sortBy,
sortByLabel
}), filterByOpts.length > 0 && /* @__PURE__ */ React.createElement(unstable_FeatureFlags, { enableV12DynamicFloatingStyles: true }, /* @__PURE__ */ React.createElement(Popover, {
align: "bottom-right",
isTabTip: true,
onKeyDown: (e) => {
if (e.key === "Escape") {
setFilterOpen(false);
e.preventDefault();
e.stopPropagation();
document.querySelector(`#${filterBtnId}`)?.focus();
}
},
onRequestClose: () => setFilterOpen(false),
open: filterOpen
}, /* @__PURE__ */ React.createElement(IconButton, {
size: "md",
id: filterBtnId,
hasIconOnly: true,
"aria-expanded": filterOpen,
badgeCount: filters.length > 0 ? 0 : void 0,
kind: "ghost",
label: filterByLabel || "Filter",
onClick: () => {
setFilterOpen((prev) => !prev);
}
}, /* @__PURE__ */ React.createElement(Filter, null)), /* @__PURE__ */ React.createElement(PopoverContent, { className: `${colClass}-filter-popover` }, filterByOpts.map((opt) => /* @__PURE__ */ React.createElement(Checkbox, {
key: opt,
id: opt,
labelText: opt,
onChange: (event, { checked }) => filterHandler(checked, opt),
checked: filters.find((o) => o === opt) ? true : false
}))))))), /* @__PURE__ */ React.createElement("div", { className: `${blockClass}__tags` }, /* @__PURE__ */ React.createElement(Checkbox, {
id: `${selectAllId}-select-all`,
className: `${colClass}__select-all`,
checked: allSelected,
onChange: selectAllHandler,
labelText: /* @__PURE__ */ React.createElement(React.Fragment, null, /* @__PURE__ */ React.createElement("span", { className: `${blockClass}__tag-label` }, header), /* @__PURE__ */ React.createElement(Tag, {
type: "gray",
size: "sm"
}, colItems.length))
})), /* @__PURE__ */ React.createElement(AddSelectList, {
...props,
filteredItems: colItems,
setMultiSelection,
multiSelection,
setParentSelected: parentSelectionHandler,
parentSelected
})), selectedItem && itemInPath && /* @__PURE__ */ React.createElement(AddSelectColumn, {
columnInputPlaceholder,
header: selectedItem.title,
items: selectedItem.children,
multiSelection,
setMultiSelection,
parentId: selectedItem.id,
setPath,
path,
sortByLabel,
filterByLabel,
...props
}));
};
AddSelectColumn.propTypes = {
columnInputPlaceholder: PropTypes.string,
filterByLabel: PropTypes.string,
header: PropTypes.string,
items: PropTypes.object,
multiSelection: PropTypes.array,
parentId: PropTypes.string,
path: PropTypes.array,
setMultiSelection: PropTypes.func,
setPath: PropTypes.func,
sortByLabel: PropTypes.string
};
AddSelectColumn.displayName = componentName;
//#endregion
export { AddSelectColumn };