UNPKG

@carbon/react

Version:

React components for the Carbon Design System

123 lines (121 loc) 5.21 kB
/** * 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 };