@carbon/ibm-products
Version:
Carbon for IBM Products
241 lines (233 loc) • 8.68 kB
JavaScript
/**
* Copyright IBM Corp. 2020, 2025
*
* This source code is licensed under the Apache-2.0 license found in the
* LICENSE file in the root directory of this source tree.
*/
'use strict';
var _rollupPluginBabelHelpers = require('../../_virtual/_rollupPluginBabelHelpers.js');
var React = require('react');
var index = require('../../_virtual/index.js');
var react = require('@carbon/react');
var icons = require('@carbon/react/icons');
var settings = require('../../settings.js');
var AddSelectList = require('./AddSelectList.js');
var AddSelectSort = require('./AddSelectSort.js');
var addSelectUtils = require('./add-select-utils.js');
var useItemSort = require('./hooks/useItemSort.js');
var useId = require('../../global/js/utils/useId.js');
var useParentSelect = require('./hooks/useParentSelect.js');
var _Filter;
const blockClass = `${settings.pkg.prefix}--add-select`;
const colClass = `${blockClass}__column`;
const componentName = 'AddSelectColumn';
let AddSelectColumn = _ref => {
let {
columnInputPlaceholder,
filterByLabel,
header,
items,
multiSelection,
parentId,
path,
setMultiSelection,
setPath,
sortByLabel,
...props
} = _ref;
const {
parentSelected,
setParentSelected
} = useParentSelect.default();
const selectAllId = useId.useId();
const filterId = useId.useId();
const searchId = useId.useId();
const [searchTerm, setSearchTerm] = React.useState('');
const {
sortDirection,
setSortDirection,
sortAttribute,
setSortAttribute
} = useItemSort.useItemSort();
const [filterOpen, setFilterOpen] = React.useState(false);
const [filters, setFilters] = React.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));
// filtering
const filterByOpts = filterBy ? entries.map(item => item[filterBy]) : [];
const selectAllHandler = (event, _ref2) => {
let {
checked
} = _ref2;
const itemIds = entries.map(item => item.id);
if (checked) {
const newSelections = [...new Set([...multiSelection, ...itemIds])];
setMultiSelection(newSelections);
} else {
const newItems = multiSelection.filter(i => !itemIds.includes(i));
setMultiSelection(newItems);
}
};
const filterHandler = (checked, opt) => {
if (checked) {
const newFilters = [...filters, opt];
setFilters(newFilters);
} else {
const newFilters = filters.filter(o => o !== opt);
setFilters(newFilters);
}
};
// filter and sort array functions
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 = addSelectUtils.sortItems(sortAttribute, sortDirection);
const colItems = entries.filter(filterBySearch) // first check if the item meets the search
.filter(filterByAttribute) // then check if the item is included in the filter
.sort(sortFn); // then sort the items by whatever criteria
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(react.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.AddSelectSort, {
items: entries,
setSortAttribute: setSortAttribute,
setSortDirection: setSortDirection,
sortAttribute: sortAttribute,
sortDirection: sortDirection,
sortBy: sortBy,
sortByLabel: sortByLabel
}), filterByOpts.length > 0 && /*#__PURE__*/React.createElement(react.unstable_FeatureFlags, {
enableV12DynamicFloatingStyles: true
}, /*#__PURE__*/React.createElement(react.Popover, {
align: "bottom-right",
isTabTip: true,
onKeyDown: e => {
if (e.key === 'Escape') {
setFilterOpen(false);
e.preventDefault(); // prevent modal close
e.stopPropagation();
const filterBtn = document.querySelector(`#${filterBtnId}`);
filterBtn?.focus(); // workaround to return focus, refs not possible on button inside popover for some reason
}
},
onRequestClose: () => setFilterOpen(false),
open: filterOpen
}, /*#__PURE__*/React.createElement(react.IconButton, {
size: "md",
id: filterBtnId,
hasIconOnly: true,
"aria-expanded": filterOpen,
badgeCount: filters.length > 0 ? 0 : undefined // setting to 0 renders an empty dot
,
kind: "ghost",
label: filterByLabel || 'Filter',
onClick: () => {
setFilterOpen(prev => !prev);
}
}, _Filter || (_Filter = /*#__PURE__*/React.createElement(icons.Filter, null))), /*#__PURE__*/React.createElement(react.PopoverContent, {
className: `${colClass}-filter-popover`
}, filterByOpts.map(opt => /*#__PURE__*/React.createElement(react.Checkbox, {
key: opt,
id: opt,
labelText: opt,
onChange: (event, _ref3) => {
let {
checked
} = _ref3;
return filterHandler(checked, opt);
},
checked: filters.find(o => o === opt) ? true : false
}))))))), /*#__PURE__*/React.createElement("div", {
className: `${blockClass}__tags`
}, /*#__PURE__*/React.createElement(react.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(react.Tag, {
type: "gray",
size: "sm"
}, colItems.length))
})), /*#__PURE__*/React.createElement(AddSelectList.AddSelectList, _rollupPluginBabelHelpers.extends({}, props, {
filteredItems: colItems,
setMultiSelection: setMultiSelection,
multiSelection: multiSelection,
setParentSelected: parentSelectionHandler,
parentSelected: parentSelected
}))), selectedItem && itemInPath && /*#__PURE__*/React.createElement(AddSelectColumn, _rollupPluginBabelHelpers.extends({
columnInputPlaceholder: columnInputPlaceholder,
header: selectedItem.title,
items: selectedItem.children,
multiSelection: multiSelection,
setMultiSelection: setMultiSelection,
parentId: selectedItem.id,
setPath: setPath,
path: path,
sortByLabel: sortByLabel,
filterByLabel: filterByLabel
}, props)));
};
AddSelectColumn.propTypes = {
columnInputPlaceholder: index.default.string,
filterByLabel: index.default.string,
header: index.default.string,
items: index.default.object,
multiSelection: index.default.array,
parentId: index.default.string,
path: index.default.array,
setMultiSelection: index.default.func,
setPath: index.default.func,
sortByLabel: index.default.string
};
AddSelectColumn.displayName = componentName;
exports.AddSelectColumn = AddSelectColumn;