@carbon/ibm-products
Version:
Carbon for IBM Products
176 lines (174 loc) • 7.11 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 { __toESM } from "../../../_virtual/_rolldown/runtime.js";
import { require_classnames } from "../../../node_modules/classnames/index.js";
import { AddSelectContext, blockClass } from "./context.js";
import React, { forwardRef, useContext, useEffect, useRef, useState } from "react";
import PropTypes from "prop-types";
import { Breadcrumb, BreadcrumbItem, Link, Search, Tag } from "@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__ */ __toESM(require_classnames());
const AddSelectBody = 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 } = useContext(AddSelectContext);
const [searchTerm, setSearchTerm] = useState("");
const listRef = useRef(null);
const focusedIndexRef = 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(`.${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();
};
useEffect(() => {
updateItemFocus(0, false);
}, [children]);
const handleSearch = (event) => {
const value = event.target.value;
setSearchTerm(value);
onSearch?.(value);
};
const bodyClasses = (0, import_classnames.default)(`${blockClass}__body`, className);
return /* @__PURE__ */ React.createElement("div", {
className: bodyClasses,
ref,
...rest
}, /* @__PURE__ */ React.createElement("div", { className: `${blockClass}__header` }, headerContent || /* @__PURE__ */ React.createElement(React.Fragment, null, (!hideSearch || actionsSlot) && /* @__PURE__ */ React.createElement("div", { className: (0, import_classnames.default)(`${blockClass}__search`, { [`${blockClass}__search--with-actions`]: actionsSlot }) }, !hideSearch && /* @__PURE__ */ React.createElement("div", { className: actionsSlot ? `${blockClass}__search-input` : "" }, /* @__PURE__ */ React.createElement(Search, {
labelText: globalSearchLabel,
placeholder: globalSearchPlaceholder,
size: "lg",
onChange: handleSearch,
value: searchTerm,
...searchProps
})), actionsSlot && /* @__PURE__ */ React.createElement("div", { className: `${blockClass}__global-actions` }, actionsSlot)), /* @__PURE__ */ React.createElement("div", { className: `${blockClass}__sub-header` }, /* @__PURE__ */ React.createElement("div", { className: `${blockClass}__tags` }, searchTerm ? /* @__PURE__ */ React.createElement("p", { className: `${blockClass}__tags-label` }, searchResultsTitle) : path && path.length > 0 ? /* @__PURE__ */ React.createElement(Breadcrumb, {
noTrailingSlash: true,
className: `${blockClass}__breadcrumbs`,
...breadcrumbProps
}, path.map((entry, idx) => {
const isCurrentPage = idx === path.length - 1;
return /* @__PURE__ */ React.createElement(BreadcrumbItem, {
key: entry.id,
isCurrentPage,
...breadcrumbItemProps
}, isCurrentPage ? entry.title : /* @__PURE__ */ React.createElement(Link, {
href: "#",
onClick: (e) => {
e.preventDefault();
onBreadcrumbClick?.(idx);
},
...linkProps
}, entry.title));
})) : /* @__PURE__ */ React.createElement("p", { className: `${blockClass}__tags-label` }, itemsLabel), itemCount !== void 0 && /* @__PURE__ */ React.createElement(Tag, {
type: "gray",
size: "sm",
...tagProps
}, itemCount)), subHeaderActions && /* @__PURE__ */ React.createElement("div", { className: `${blockClass}__sub-header-actions` }, subHeaderActions)))), /* @__PURE__ */ React.createElement("div", {
className: `${blockClass}__content`,
ref: listRef,
role: "grid",
"aria-multiselectable": multi,
tabIndex: 0,
onKeyDown: handleKeyDown
}, /* @__PURE__ */ React.createElement("div", { className: (0, import_classnames.default)(`${blockClass}-list-body`, { [`${blockClass}-list-body--horizontal`]: layout === "horizontal" }) }, children)));
});
AddSelectBody.propTypes = {
actionsSlot: PropTypes.node,
/**@ts-ignore */
breadcrumbItemProps: PropTypes.object,
/**@ts-ignore */
breadcrumbProps: PropTypes.object,
children: PropTypes.node,
className: PropTypes.string,
globalSearchLabel: PropTypes.string,
globalSearchPlaceholder: PropTypes.string,
headerContent: PropTypes.node,
hideSearch: PropTypes.bool,
itemCount: PropTypes.number,
itemsLabel: PropTypes.string,
/**@ts-ignore */
layout: PropTypes.oneOf(["vertical", "horizontal"]),
/**@ts-ignore */
linkProps: PropTypes.object,
/**@ts-ignore */
onBreadcrumbClick: PropTypes.func,
/**@ts-ignore */
onSearch: PropTypes.func,
/**@ts-ignore */
path: PropTypes.arrayOf(PropTypes.shape({
id: PropTypes.string.isRequired,
title: PropTypes.string.isRequired
})),
/**@ts-ignore */
searchProps: PropTypes.object,
searchResultsTitle: PropTypes.string,
subHeaderActions: PropTypes.node,
/**@ts-ignore */
tagProps: PropTypes.object
};
AddSelectBody.displayName = "AddSelectBody";
//#endregion
export { AddSelectBody as default };