@carbon/ibm-products
Version:
Carbon for IBM Products
364 lines (358 loc) • 12.5 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.
*/
import { extends as _extends } from '../../_virtual/_rollupPluginBabelHelpers.js';
import React__default, { forwardRef, useState } from 'react';
import PropTypes from '../../_virtual/index.js';
import cx from 'classnames';
import { Tag } from '@carbon/react';
import { Tearsheet } from '../Tearsheet/Tearsheet.js';
import { TearsheetNarrow } from '../Tearsheet/TearsheetNarrow.js';
import '../EmptyStates/EmptyState.js';
import '../EmptyStates/EmptyStateV2.js';
import '../EmptyStates/ErrorEmptyState/ErrorEmptyState.js';
import '../EmptyStates/NoDataEmptyState/NoDataEmptyState.js';
import '../EmptyStates/NoTagsEmptyState/NoTagsEmptyState.js';
import { NotFoundEmptyState } from '../EmptyStates/NotFoundEmptyState/NotFoundEmptyState.js';
import '../EmptyStates/NotificationsEmptyState/NotificationsEmptyState.js';
import '../EmptyStates/UnauthorizedEmptyState/UnauthorizedEmptyState.js';
import { AddSelectSidebar } from './AddSelectSidebar.js';
import { AddSelectBreadcrumbs } from './AddSelectBreadcrumbs.js';
import { AddSelectList } from './AddSelectList.js';
import { AddSelectColumn } from './AddSelectColumn.js';
import { AddSelectFilter } from './AddSelectFilter.js';
import { AddSelectSort } from './AddSelectSort.js';
import { getFilteredItems, sortItems } from './add-select-utils.js';
import { useItemSort } from './hooks/useItemSort.js';
import useParentSelect from './hooks/useParentSelect.js';
import usePath from './hooks/usePath.js';
import { pkg } from '../../settings.js';
const blockClass = `${pkg.prefix}--add-select`;
const componentName = 'AddSelectBody';
const AddSelectBody = /*#__PURE__*/forwardRef((_ref, ref) => {
let {
className,
clearFiltersText,
closeIconDescription,
columnInputPlaceholder,
defaultModifiers,
description,
filterByLabel,
globalFilterOpts,
globalFiltersLabel,
globalFiltersIconDescription,
globalFiltersPlaceholderText,
globalFiltersPrimaryButtonText,
globalFiltersSecondaryButtonText,
globalSearchLabel,
globalSearchPlaceholder,
globalSortBy,
illustrationTheme,
influencerTitle,
items,
itemsLabel,
metaIconDescription,
metaPanelTitle,
multi,
navIconDescription,
noResultsDescription,
noResultsTitle,
noSelectionDescription,
noSelectionTitle,
normalizedItems,
onClose,
onCloseButtonText,
onSubmit,
onSubmitButtonText,
open,
portalTarget,
searchResultsTitle,
sortByLabel,
title,
useNormalizedItems,
...rest
} = _ref;
// hooks
const [singleSelection, setSingleSelection] = useState('');
const [multiSelection, setMultiSelection] = useState([]);
const [searchTerm, setSearchTerm] = useState('');
const [appliedGlobalFilters, setAppliedGlobalFilters] = useState({});
const [displayMetalPanel, setDisplayMetaPanel] = useState({});
const [appliedModifiers, setAppliedModifiers] = useState(defaultModifiers);
const {
sortDirection,
setSortDirection,
sortAttribute,
setSortAttribute
} = useItemSort();
const {
parentSelected,
setParentSelected
} = useParentSelect();
const {
path,
setPath,
pathOnclick,
resetPath
} = usePath(itemsLabel);
const resetState = () => {
setSingleSelection('');
setMultiSelection([]);
setSearchTerm('');
setAppliedGlobalFilters({});
setDisplayMetaPanel({});
setAppliedModifiers(defaultModifiers);
setSortAttribute('');
setSortDirection('');
setParentSelected(null);
resetPath();
};
const onCloseHandler = () => {
resetState();
onClose?.();
};
const tearsheetClassnames = cx(className, blockClass, {
[`${blockClass}__single`]: !multi,
[`${blockClass}__multi`]: multi
});
const globalFilterKeys = Object.keys(appliedGlobalFilters);
const globalFiltersApplied = globalFilterKeys.length > 0;
// handlers
const handleSearch = term => {
setSearchTerm(term);
};
const handleFilter = filters => {
setAppliedGlobalFilters(filters);
};
const submitHandler = () => {
if (multi && appliedModifiers && appliedModifiers?.length > 0) {
const selections = multiSelection.map(item => {
return appliedModifiers.find(mod => mod.id === item);
});
onSubmit?.(selections);
} else if (multi && appliedModifiers?.length === 0) {
onSubmit?.(multiSelection);
} else {
onSubmit?.(singleSelection);
}
onCloseHandler();
};
const setShowBreadsCrumbs = () => {
if (useNormalizedItems === false || searchTerm || globalFiltersApplied) {
return false;
}
return true;
};
const setShowTags = () => {
if (searchTerm) {
return true;
}
if (useNormalizedItems && multi) {
return false;
}
return true;
};
const parentSelectionHandler = (id, title, parentId) => {
setParentSelected(id);
setPath(id, title, parentId);
};
const sortFn = sortItems(sortAttribute, sortDirection);
const itemsToDisplay = getFilteredItems(useNormalizedItems, normalizedItems, searchTerm, globalFiltersApplied, globalFilterKeys, appliedGlobalFilters, sortFn, multi, items, path);
const hasResults = itemsToDisplay.length > 0;
const showBreadsCrumbs = setShowBreadsCrumbs();
const showSort = (searchTerm || globalFiltersApplied) && hasResults;
const showTags = setShowTags();
const commonListProps = {
displayMetalPanel,
metaIconDescription,
multi,
multiSelection,
navIconDescription,
path,
setMultiSelection,
setPath,
setSingleSelection,
singleSelection,
setDisplayMetaPanel,
parentId: path[0].id
};
const commonTearsheetProps = {
...rest,
className: tearsheetClassnames,
open,
title,
description,
actions: [{
label: onCloseButtonText,
kind: 'secondary',
onClick: onCloseHandler
}, {
label: onSubmitButtonText,
kind: 'primary',
onClick: submitHandler,
disabled: multi ? multiSelection.length === 0 : !singleSelection
}],
portalTarget,
ref
};
const sidebarProps = {
appliedModifiers,
closeIconDescription,
displayMetalPanel,
illustrationTheme,
influencerTitle,
items: useNormalizedItems ? normalizedItems : items?.entries,
metaPanelTitle,
modifiers: items?.modifiers,
multiSelection,
noSelectionDescription,
noSelectionTitle,
setDisplayMetaPanel
};
const displayColumnView = multi && useNormalizedItems && !searchTerm && !globalFiltersApplied;
// main content
const body = /*#__PURE__*/React__default.createElement(React__default.Fragment, null, /*#__PURE__*/React__default.createElement("div", {
id: "add-select",
className: `${blockClass}__header`
}, /*#__PURE__*/React__default.createElement(AddSelectFilter, {
inputLabel: globalSearchLabel,
inputPlaceholder: globalSearchPlaceholder,
searchTerm: searchTerm,
handleSearch: handleSearch,
multi: multi,
filterOpts: globalFilterOpts,
filtersLabel: globalFiltersLabel,
handleFilter: handleFilter,
primaryButtonText: globalFiltersPrimaryButtonText,
secondaryButtonText: globalFiltersSecondaryButtonText,
placeholder: globalFiltersPlaceholderText,
iconDescription: globalFiltersIconDescription,
appliedFilters: appliedGlobalFilters,
hasFiltersApplied: globalFiltersApplied,
clearFiltersText: clearFiltersText
}), /*#__PURE__*/React__default.createElement("div", {
className: cx(`${blockClass}__sub-header`, {
[`${blockClass}__sub-header-multi`]: multi
})
}, /*#__PURE__*/React__default.createElement("div", {
className: `${blockClass}__tags`
}, showBreadsCrumbs ? /*#__PURE__*/React__default.createElement(AddSelectBreadcrumbs, {
path: path,
onClick: pathOnclick,
multi: multi
}) : /*#__PURE__*/React__default.createElement("p", {
className: `${blockClass}__tags-label`
}, searchTerm ? searchResultsTitle : itemsLabel), showTags && /*#__PURE__*/React__default.createElement(Tag, {
type: "gray",
size: "sm"
}, itemsToDisplay.length)), showSort && /*#__PURE__*/React__default.createElement(AddSelectSort, {
setSortAttribute: setSortAttribute,
setSortDirection: setSortDirection,
sortBy: globalSortBy
}))), displayColumnView ? /*#__PURE__*/React__default.createElement("div", {
className: `${blockClass}__columns`
}, /*#__PURE__*/React__default.createElement(AddSelectColumn, _extends({}, commonListProps, {
items: itemsToDisplay,
columnInputPlaceholder: columnInputPlaceholder,
header: path[0]?.title,
filterByLabel: filterByLabel,
sortByLabel: sortByLabel
}))) : /*#__PURE__*/React__default.createElement("div", null, hasResults ? /*#__PURE__*/React__default.createElement(AddSelectList, _extends({}, commonListProps, {
filteredItems: itemsToDisplay,
modifiers: items?.modifiers,
appliedModifiers: appliedModifiers,
setAppliedModifiers: setAppliedModifiers,
setParentSelected: parentSelectionHandler,
parentSelected: parentSelected,
parentId: parentSelected || path[0].id
})) : /*#__PURE__*/React__default.createElement("div", {
className: `${blockClass}__body`
}, /*#__PURE__*/React__default.createElement(NotFoundEmptyState, {
subtitle: noResultsDescription,
title: noResultsTitle,
illustrationTheme: illustrationTheme
}))));
if (multi) {
return /*#__PURE__*/React__default.createElement(Tearsheet, _extends({}, commonTearsheetProps, {
influencer: multi && /*#__PURE__*/React__default.createElement(AddSelectSidebar, sidebarProps),
influencerPosition: "right"
}), body);
}
return /*#__PURE__*/React__default.createElement(TearsheetNarrow, commonTearsheetProps, body);
});
AddSelectBody.propTypes = {
className: PropTypes.string,
clearFiltersText: PropTypes.string,
closeIconDescription: PropTypes.string,
columnInputPlaceholder: PropTypes.string,
defaultModifiers: PropTypes.array,
description: PropTypes.string,
filterByLabel: PropTypes.string,
globalFilterOpts: PropTypes.array,
globalFiltersIconDescription: PropTypes.string,
globalFiltersLabel: PropTypes.string,
globalFiltersPlaceholderText: PropTypes.string,
globalFiltersPrimaryButtonText: PropTypes.string,
globalFiltersSecondaryButtonText: PropTypes.string,
globalSearchLabel: PropTypes.string.isRequired,
globalSearchPlaceholder: PropTypes.string,
globalSortBy: PropTypes.array,
illustrationTheme: PropTypes.oneOf(['light', 'dark']),
influencerTitle: PropTypes.string,
/**@ts-ignore */
items: PropTypes.shape({
modifiers: PropTypes.shape({
id: PropTypes.string,
label: PropTypes.string,
options: PropTypes.array,
multiSelect: PropTypes.bool
}),
sortBy: PropTypes.array,
filterBy: PropTypes.array,
entries: PropTypes.arrayOf(PropTypes.shape({
avatar: PropTypes.shape({
alt: PropTypes.string,
icon: PropTypes.oneOfType([PropTypes.func, PropTypes.object]),
src: PropTypes.string,
theme: PropTypes.oneOf(['light', 'dark'])
}),
children: PropTypes.object,
icon: PropTypes.oneOfType([PropTypes.func, PropTypes.object]),
id: PropTypes.string.isRequired,
meta: PropTypes.oneOfType([PropTypes.arrayOf(PropTypes.shape({
id: PropTypes.string,
title: PropTypes.string,
value: PropTypes.string
})), PropTypes.node]),
subtitle: PropTypes.string,
title: PropTypes.string.isRequired,
value: PropTypes.string.isRequired
}))
}),
itemsLabel: PropTypes.string,
metaIconDescription: PropTypes.string,
metaPanelTitle: PropTypes.string,
multi: PropTypes.bool,
navIconDescription: PropTypes.string,
noResultsDescription: PropTypes.string,
noResultsTitle: PropTypes.string,
noSelectionDescription: PropTypes.string,
noSelectionTitle: PropTypes.string,
normalizedItems: PropTypes.object,
onClose: PropTypes.func,
onCloseButtonText: PropTypes.string,
onSubmit: PropTypes.func,
onSubmitButtonText: PropTypes.string,
open: PropTypes.bool,
/**@ts-ignore */
portalTarget: PropTypes.node,
searchResultsTitle: PropTypes.string,
sortByLabel: PropTypes.string,
title: PropTypes.string,
useNormalizedItems: PropTypes.bool
};
AddSelectBreadcrumbs.displayName = componentName;
export { AddSelectBody };