@carbon/react
Version:
React components for the Carbon Design System
152 lines (150 loc) • 5.54 kB
JavaScript
/**
* Copyright IBM Corp. 2016, 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 { usePrefix } from "../../internal/usePrefix.js";
import { useId } from "../../internal/useId.js";
import { isComponentElement } from "../../internal/utils.js";
import { AILabel } from "../AILabel/index.js";
import { sortStates } from "./state/sortStates.js";
import classNames from "classnames";
import { cloneElement, forwardRef, useRef } from "react";
import PropTypes from "prop-types";
import { jsx, jsxs } from "react/jsx-runtime";
import { ArrowUp, ArrowsVertical } from "@carbon/icons-react";
//#region src/components/DataTable/TableHeader.tsx
/**
* Copyright IBM Corp. 2016, 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 defaultScope = "col";
const translationIds = { "carbon.table.header.icon.description": "carbon.table.header.icon.description" };
const defaultTranslations = { [translationIds["carbon.table.header.icon.description"]]: "Click to sort rows by header in ascending order" };
const defaultTranslateWithId = (messageId, args) => {
if (args && messageId === translationIds["carbon.table.header.icon.description"]) {
if (args.isSortHeader && sortStates) {
if (args.sortDirection === sortStates.NONE) return `Click to sort rows by ${args.header} header in ascending order`;
if (args.sortDirection === sortStates.ASC) return `Click to sort rows by ${args.header} header in descending order`;
return `Click to unsort rows by ${args.header} header`;
}
return `Click to sort rows by ${args.header} header in ascending order`;
}
return defaultTranslations[messageId];
};
const sortDirections = {
[sortStates.NONE]: "none",
[sortStates.ASC]: "ascending",
[sortStates.DESC]: "descending"
};
const TableHeader = forwardRef((props, ref) => {
const { className: headerClassName, children, colSpan, decorator, isSortable = false, isSortHeader, onClick, scope = defaultScope, sortDirection, translateWithId: t = defaultTranslateWithId, slug, id, ...rest } = props;
const prefix = usePrefix();
const uniqueId = useId("table-sort");
const AILableRef = useRef(null);
const candidate = slug ?? decorator;
const candidateIsAILabel = isComponentElement(candidate, AILabel);
const colHasAILabel = candidateIsAILabel;
const normalizedDecorator = candidateIsAILabel ? cloneElement(candidate, {
size: "mini",
ref: AILableRef
}) : candidate;
const headerLabelClassNames = classNames({
[`${prefix}--table-header-label`]: true,
[`${prefix}--table-header-label--slug ${prefix}--table-header-label--ai-label`]: colHasAILabel,
[`${prefix}--table-header-label--decorator`]: decorator
});
if (!isSortable) return /* @__PURE__ */ jsx("th", {
...rest,
id,
className: headerClassName,
scope,
colSpan,
ref,
children: children ? /* @__PURE__ */ jsxs("div", {
className: headerLabelClassNames,
children: [children, /* @__PURE__ */ jsx("div", {
className: `${prefix}--table-header-label--decorator-inner`,
children: normalizedDecorator
})]
}) : null
});
const className = classNames(headerClassName, {
[`${prefix}--table-sort`]: true,
[`${prefix}--table-sort--active`]: isSortHeader && sortDirection !== sortStates.NONE,
[`${prefix}--table-sort--descending`]: isSortHeader && sortDirection === sortStates.DESC
});
const ariaSort = !isSortHeader || !sortDirection ? "none" : sortDirections[sortDirection];
const sortDescription = t && t("carbon.table.header.icon.description", {
header: children,
sortDirection,
isSortHeader,
sortStates
});
const headerClasses = classNames(headerClassName, `${prefix}--table-sort__header`, {
[`${prefix}--table-sort__header--ai-label`]: colHasAILabel,
[`${prefix}--table-sort__header--decorator`]: decorator
});
const handleClick = (evt) => {
if (colHasAILabel && AILableRef.current && AILableRef.current.contains(evt.target)) return;
else if (onClick) return onClick(evt);
};
return /* @__PURE__ */ jsxs("th", {
id,
"aria-sort": ariaSort,
className: headerClasses,
colSpan,
ref,
scope,
children: [/* @__PURE__ */ jsx("div", {
className: `${prefix}--table-sort__description`,
id: uniqueId,
children: sortDescription
}), /* @__PURE__ */ jsx("button", {
type: "button",
"aria-describedby": uniqueId,
className,
onClick: handleClick,
...rest,
children: /* @__PURE__ */ jsxs("span", {
className: `${prefix}--table-sort__flex`,
children: [
/* @__PURE__ */ jsx("div", {
className: `${prefix}--table-header-label`,
children
}),
/* @__PURE__ */ jsx(ArrowUp, {
size: 20,
className: `${prefix}--table-sort__icon`
}),
/* @__PURE__ */ jsx(ArrowsVertical, {
size: 20,
className: `${prefix}--table-sort__icon-unsorted`
}),
/* @__PURE__ */ jsx("div", {
className: `${prefix}--table-header-label--decorator-inner`,
children: normalizedDecorator
})
]
})
})]
});
});
TableHeader.propTypes = {
children: PropTypes.node,
className: PropTypes.string,
colSpan: PropTypes.number,
id: PropTypes.string,
isSortHeader: PropTypes.bool,
isSortable: PropTypes.bool,
onClick: PropTypes.func,
scope: PropTypes.string,
sortDirection: PropTypes.oneOf(Object.values(sortStates)),
translateWithId: PropTypes.func
};
TableHeader.displayName = "TableHeader";
//#endregion
export { TableHeader as default };