@carbon/ibm-products
Version:
Carbon for IBM Products
178 lines (176 loc) • 7.89 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.
*/
const require_runtime = require("../../../_virtual/_rolldown/runtime.js");
const require_index = require("../../../node_modules/classnames/index.js");
const require_context = require("./context.js");
let react = require("react");
react = require_runtime.__toESM(react);
let prop_types = require("prop-types");
prop_types = require_runtime.__toESM(prop_types);
let _carbon_react = require("@carbon/react");
//#region src/components/AddSelect/next/AddSelectBody.tsx
/**
* Copyright IBM Corp. 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.
*/
var import_classnames = /* @__PURE__ */ require_runtime.__toESM(require_index.default);
const AddSelectBody = (0, react.forwardRef)(({ children, className, layout = "vertical", itemsLabel = "", globalSearchLabel = "Search", globalSearchPlaceholder = "Search", searchResultsTitle = "Search results", itemCount, path = [], onSearch, onBreadcrumbClick, headerContent, actionsSlot, subHeaderActions, hideSearch = false, searchProps, tagProps, breadcrumbProps, breadcrumbItemProps, linkProps, ...rest }, ref) => {
const { multi } = (0, react.useContext)(require_context.AddSelectContext);
const [searchTerm, setSearchTerm] = (0, react.useState)("");
const listRef = (0, react.useRef)(null);
const focusedIndexRef = (0, react.useRef)(0);
const getItems = () => {
if (!listRef.current) return [];
return Array.from(listRef.current.querySelectorAll("[role=\"row\"]"));
};
const updateItemFocus = (focusIndex, shouldFocus = true) => {
const items = getItems();
if (items.length === 0) return;
focusedIndexRef.current = Math.max(0, Math.min(focusIndex, items.length - 1));
items.forEach((item, index) => {
if (index === focusedIndexRef.current) {
item.setAttribute("tabindex", "0");
if (shouldFocus) item.focus();
} else item.setAttribute("tabindex", "-1");
});
};
const handleKeyDown = (event) => {
const items = getItems();
if (items.length === 0) return;
const currentItem = items[focusedIndexRef.current];
let handled = false;
switch (event.key) {
case "ArrowDown":
event.preventDefault();
updateItemFocus(focusedIndexRef.current + 1);
handled = true;
break;
case "ArrowUp":
event.preventDefault();
updateItemFocus(focusedIndexRef.current - 1);
handled = true;
break;
case "ArrowRight":
if (currentItem && currentItem.hasAttribute("data-has-children")) {
event.preventDefault();
currentItem.querySelector(`.${require_context.blockClass}-row__nav-indicator`)?.click();
handled = true;
}
break;
case "Enter":
case " ":
event.preventDefault();
if (currentItem) currentItem.querySelector("input[type=\"checkbox\"], input[type=\"radio\"]")?.click();
handled = true;
break;
case "Home":
if (event.ctrlKey) {
event.preventDefault();
updateItemFocus(0);
handled = true;
}
break;
case "End":
if (event.ctrlKey) {
event.preventDefault();
updateItemFocus(items.length - 1);
handled = true;
}
break;
}
if (handled) event.stopPropagation();
};
(0, react.useEffect)(() => {
updateItemFocus(0, false);
}, [children]);
const handleSearch = (event) => {
const value = event.target.value;
setSearchTerm(value);
onSearch?.(value);
};
const bodyClasses = (0, import_classnames.default)(`${require_context.blockClass}__body`, className);
return /* @__PURE__ */ react.default.createElement("div", {
className: bodyClasses,
ref,
...rest
}, /* @__PURE__ */ react.default.createElement("div", { className: `${require_context.blockClass}__header` }, headerContent || /* @__PURE__ */ react.default.createElement(react.default.Fragment, null, (!hideSearch || actionsSlot) && /* @__PURE__ */ react.default.createElement("div", { className: (0, import_classnames.default)(`${require_context.blockClass}__search`, { [`${require_context.blockClass}__search--with-actions`]: actionsSlot }) }, !hideSearch && /* @__PURE__ */ react.default.createElement("div", { className: actionsSlot ? `${require_context.blockClass}__search-input` : "" }, /* @__PURE__ */ react.default.createElement(_carbon_react.Search, {
labelText: globalSearchLabel,
placeholder: globalSearchPlaceholder,
size: "lg",
onChange: handleSearch,
value: searchTerm,
...searchProps
})), actionsSlot && /* @__PURE__ */ react.default.createElement("div", { className: `${require_context.blockClass}__global-actions` }, actionsSlot)), /* @__PURE__ */ react.default.createElement("div", { className: `${require_context.blockClass}__sub-header` }, /* @__PURE__ */ react.default.createElement("div", { className: `${require_context.blockClass}__tags` }, searchTerm ? /* @__PURE__ */ react.default.createElement("p", { className: `${require_context.blockClass}__tags-label` }, searchResultsTitle) : path && path.length > 0 ? /* @__PURE__ */ react.default.createElement(_carbon_react.Breadcrumb, {
noTrailingSlash: true,
className: `${require_context.blockClass}__breadcrumbs`,
...breadcrumbProps
}, path.map((entry, idx) => {
const isCurrentPage = idx === path.length - 1;
return /* @__PURE__ */ react.default.createElement(_carbon_react.BreadcrumbItem, {
key: entry.id,
isCurrentPage,
...breadcrumbItemProps
}, isCurrentPage ? entry.title : /* @__PURE__ */ react.default.createElement(_carbon_react.Link, {
href: "#",
onClick: (e) => {
e.preventDefault();
onBreadcrumbClick?.(idx);
},
...linkProps
}, entry.title));
})) : /* @__PURE__ */ react.default.createElement("p", { className: `${require_context.blockClass}__tags-label` }, itemsLabel), itemCount !== void 0 && /* @__PURE__ */ react.default.createElement(_carbon_react.Tag, {
type: "gray",
size: "sm",
...tagProps
}, itemCount)), subHeaderActions && /* @__PURE__ */ react.default.createElement("div", { className: `${require_context.blockClass}__sub-header-actions` }, subHeaderActions)))), /* @__PURE__ */ react.default.createElement("div", {
className: `${require_context.blockClass}__content`,
ref: listRef,
role: "grid",
"aria-multiselectable": multi,
tabIndex: 0,
onKeyDown: handleKeyDown
}, /* @__PURE__ */ react.default.createElement("div", { className: (0, import_classnames.default)(`${require_context.blockClass}-list-body`, { [`${require_context.blockClass}-list-body--horizontal`]: layout === "horizontal" }) }, children)));
});
AddSelectBody.propTypes = {
actionsSlot: prop_types.default.node,
/**@ts-ignore */
breadcrumbItemProps: prop_types.default.object,
/**@ts-ignore */
breadcrumbProps: prop_types.default.object,
children: prop_types.default.node,
className: prop_types.default.string,
globalSearchLabel: prop_types.default.string,
globalSearchPlaceholder: prop_types.default.string,
headerContent: prop_types.default.node,
hideSearch: prop_types.default.bool,
itemCount: prop_types.default.number,
itemsLabel: prop_types.default.string,
/**@ts-ignore */
layout: prop_types.default.oneOf(["vertical", "horizontal"]),
/**@ts-ignore */
linkProps: prop_types.default.object,
/**@ts-ignore */
onBreadcrumbClick: prop_types.default.func,
/**@ts-ignore */
onSearch: prop_types.default.func,
/**@ts-ignore */
path: prop_types.default.arrayOf(prop_types.default.shape({
id: prop_types.default.string.isRequired,
title: prop_types.default.string.isRequired
})),
/**@ts-ignore */
searchProps: prop_types.default.object,
searchResultsTitle: prop_types.default.string,
subHeaderActions: prop_types.default.node,
/**@ts-ignore */
tagProps: prop_types.default.object
};
AddSelectBody.displayName = "AddSelectBody";
//#endregion
exports.default = AddSelectBody;