@carbon/react
Version:
React components for the Carbon Design System
123 lines (121 loc) • 5.21 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 useIsomorphicEffect from "../../internal/useIsomorphicEffect.js";
import { useWindowEvent } from "../../internal/useEvent.js";
import { TableContext } from "./TableContext.js";
import classNames from "classnames";
import { useCallback, useContext, useRef } from "react";
import PropTypes from "prop-types";
import { jsx } from "react/jsx-runtime";
import { debounce } from "es-toolkit/compat";
//#region src/components/DataTable/Table.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 isElementWrappingContent = (element, context) => {
if (element.children.length > 0) return false;
const computedStyles = window.getComputedStyle(element);
context.font = computedStyles.font ? computedStyles.font : `${computedStyles.fontSize}" "${computedStyles.fontFamily}`;
let textWidth = (context?.measureText(element.textContent ?? "")).width ?? 0;
const letterSpacing = computedStyles.letterSpacing?.split("px");
if (letterSpacing && letterSpacing.length && !isNaN(Number(letterSpacing[0]))) textWidth += Number(letterSpacing[0]) * (element.textContent?.length ?? 0);
const paddingLeft = computedStyles.paddingLeft?.split("px");
if (paddingLeft && paddingLeft.length && !isNaN(Number(paddingLeft[0]))) textWidth += Number(paddingLeft[0]);
const paddingRight = computedStyles.paddingLeft?.split("px");
if (paddingRight && paddingRight.length && !isNaN(Number(paddingRight[0]))) textWidth += Number(paddingRight[0]);
if (textWidth > element.getBoundingClientRect().width) return true;
return false;
};
const Table = ({ className, children, useZebraStyles, size = "lg", isSortable = false, useStaticWidth, stickyHeader, overflowMenuOnHover = true, experimentalAutoAlign = false, ...other }) => {
const { titleId, descriptionId } = useContext(TableContext);
const prefix = usePrefix();
const tableRef = useRef(null);
const componentClass = classNames(`${prefix}--data-table`, className, {
[`${prefix}--data-table--${size}`]: size,
[`${prefix}--data-table--sort`]: isSortable,
[`${prefix}--data-table--zebra`]: useZebraStyles,
[`${prefix}--data-table--static`]: useStaticWidth,
[`${prefix}--data-table--sticky-header`]: stickyHeader,
[`${prefix}--data-table--visible-overflow-menu`]: !overflowMenuOnHover
});
const toggleTableBodyAlignmentClass = useCallback((alignTop = false) => {
if (alignTop) tableRef.current?.classList.add(`${prefix}--data-table--top-aligned-body`);
else tableRef.current?.classList.remove(`${prefix}--data-table--top-aligned-body`);
}, [prefix]);
const toggleTableHeaderAlignmentClass = useCallback((alignTop = false) => {
if (alignTop) tableRef.current?.classList.add(`${prefix}--data-table--top-aligned-header`);
else tableRef.current?.classList.remove(`${prefix}--data-table--top-aligned-header`);
}, [prefix]);
const setTableAlignment = useCallback(() => {
if (experimentalAutoAlign) {
const context = document.createElement("canvas").getContext("2d");
if (tableRef.current && context) {
const isBodyMultiline = Array.from(tableRef.current.querySelectorAll("td")).some((td) => isElementWrappingContent(td, context));
const isHeaderMultiline = Array.from(tableRef.current.querySelectorAll("th")).some((th) => {
const label = th.querySelector(`.${prefix}--table-header-label`);
return label instanceof HTMLElement && isElementWrappingContent(label, context);
});
toggleTableBodyAlignmentClass(isBodyMultiline);
toggleTableHeaderAlignmentClass(isHeaderMultiline);
}
} else {
toggleTableBodyAlignmentClass(false);
toggleTableHeaderAlignmentClass(false);
}
}, [
experimentalAutoAlign,
toggleTableBodyAlignmentClass,
toggleTableHeaderAlignmentClass,
prefix
]);
useWindowEvent("resize", debounce(setTableAlignment, 100));
if (typeof document !== "undefined" && document?.fonts?.status && document.fonts.status !== "loaded") document.fonts.ready.then(() => {
setTableAlignment();
});
useIsomorphicEffect(() => {
setTableAlignment();
}, [setTableAlignment, size]);
const table = /* @__PURE__ */ jsx("div", {
className: `${prefix}--data-table-content`,
children: /* @__PURE__ */ jsx("table", {
"aria-labelledby": titleId,
"aria-describedby": descriptionId,
...other,
className: componentClass,
ref: tableRef,
children
})
});
return stickyHeader ? /* @__PURE__ */ jsx("section", {
className: `${prefix}--data-table_inner-container`,
children: table
}) : table;
};
Table.propTypes = {
children: PropTypes.node,
className: PropTypes.string,
experimentalAutoAlign: PropTypes.bool,
isSortable: PropTypes.bool,
overflowMenuOnHover: PropTypes.bool,
size: PropTypes.oneOf([
"xs",
"sm",
"md",
"lg",
"xl"
]),
stickyHeader: PropTypes.bool,
useStaticWidth: PropTypes.bool,
useZebraStyles: PropTypes.bool,
tabIndex: PropTypes.number
};
//#endregion
export { Table as default };