UNPKG

@carbon/react

Version:

React components for the Carbon Design System

194 lines (184 loc) 7.94 kB
/** * Copyright IBM Corp. 2016, 2023 * * This source code is licensed under the Apache-2.0 license found in the * LICENSE file in the root directory of this source tree. */ 'use strict'; Object.defineProperty(exports, '__esModule', { value: true }); var _rollupPluginBabelHelpers = require('../../_virtual/_rollupPluginBabelHelpers.js'); var React = require('react'); var PropTypes = require('prop-types'); var cx = require('classnames'); var usePrefix = require('../../internal/usePrefix.js'); var TableContext = require('./TableContext.js'); var useEvent = require('../../internal/useEvent.js'); var useIsomorphicEffect = require('../../internal/useIsomorphicEffect.js'); var debounce = require('../../node_modules/es-toolkit/dist/compat/function/debounce.mjs.js'); function _interopDefaultLegacy (e) { return e && typeof e === 'object' && 'default' in e ? e : { 'default': e }; } var React__default = /*#__PURE__*/_interopDefaultLegacy(React); var PropTypes__default = /*#__PURE__*/_interopDefaultLegacy(PropTypes); var cx__default = /*#__PURE__*/_interopDefaultLegacy(cx); 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}`; const measuredText = context?.measureText(element.textContent ?? ''); let textWidth = measuredText.width ?? 0; // account for letter spacing const letterSpacing = computedStyles.letterSpacing?.split('px'); if (letterSpacing && letterSpacing.length && !isNaN(Number(letterSpacing[0]))) { textWidth += Number(letterSpacing[0]) * (element.textContent?.length ?? 0); } // account for padding 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 measured textWidth is larger than the cell's width, then the content is being wrapped if (textWidth > element.getBoundingClientRect().width) { return true; } return false; }; const Table = ({ className, children, useZebraStyles, size = 'lg', isSortable = false, useStaticWidth, stickyHeader, overflowMenuOnHover = true, experimentalAutoAlign = false, tabIndex, ...other }) => { const { titleId, descriptionId } = React.useContext(TableContext.TableContext); const prefix = usePrefix.usePrefix(); const [isScrollable, setIsScrollable] = React.useState(false); const tableRef = React.useRef(null); const componentClass = cx__default["default"](`${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 = React.useCallback((alignTop = false) => { alignTop ? tableRef.current?.classList.add(`${prefix}--data-table--top-aligned-body`) : tableRef.current?.classList.remove(`${prefix}--data-table--top-aligned-body`); }, [prefix]); const toggleTableHeaderAlignmentClass = React.useCallback((alignTop = false) => { alignTop ? tableRef.current?.classList.add(`${prefix}--data-table--top-aligned-header`) : tableRef.current?.classList.remove(`${prefix}--data-table--top-aligned-header`); }, [prefix]); const setTableAlignment = React.useCallback(() => { if (experimentalAutoAlign) { const canvas = document.createElement('canvas'); const context = 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 && isElementWrappingContent(label, context); }); toggleTableBodyAlignmentClass(isBodyMultiline); toggleTableHeaderAlignmentClass(isHeaderMultiline); } } else { toggleTableBodyAlignmentClass(false); toggleTableHeaderAlignmentClass(false); } }, [experimentalAutoAlign, toggleTableBodyAlignmentClass, toggleTableHeaderAlignmentClass, prefix]); const debouncedSetTableAlignment = debounce.debounce(setTableAlignment, 100); useEvent.useWindowEvent('resize', debouncedSetTableAlignment); // Used to set a tabIndex when the Table is horizontally scrollable const setTabIndex = React.useCallback(() => { const tableContainer = tableRef?.current?.parentNode; const tableHeader = tableRef?.current?.firstChild; if (tableHeader?.scrollWidth > tableContainer?.clientWidth) { setIsScrollable(true); } else { setIsScrollable(false); } }, []); const debouncedSetTabIndex = debounce.debounce(setTabIndex, 100); useEvent.useWindowEvent('resize', debouncedSetTabIndex); useIsomorphicEffect["default"](() => { setTabIndex(); }, [setTabIndex]); // recalculate table alignment once fonts have loaded if (typeof document !== 'undefined' && document?.fonts?.status && document.fonts.status !== 'loaded') { document.fonts.ready.then(() => { setTableAlignment(); }); } useIsomorphicEffect["default"](() => { setTableAlignment(); }, [setTableAlignment, size]); const table = /*#__PURE__*/React__default["default"].createElement("div", { className: `${prefix}--data-table-content` // eslint-disable-next-line jsx-a11y/no-noninteractive-tabindex , tabIndex: tabIndex ?? (isScrollable ? 0 : undefined) }, /*#__PURE__*/React__default["default"].createElement("table", _rollupPluginBabelHelpers["extends"]({ "aria-labelledby": titleId, "aria-describedby": descriptionId }, other, { className: componentClass, ref: tableRef }), children)); return stickyHeader ? /*#__PURE__*/React__default["default"].createElement("section", { className: `${prefix}--data-table_inner-container` }, table) : table; }; Table.propTypes = { /** * Pass in the children that will be rendered within the Table */ children: PropTypes__default["default"].node, className: PropTypes__default["default"].string, /** * Experimental property. Allows table to align cell contents to the top if there is text wrapping in the content. Might have performance issues, intended for smaller tables */ experimentalAutoAlign: PropTypes__default["default"].bool, /** * `false` If true, will apply sorting styles */ isSortable: PropTypes__default["default"].bool, /** * Specify whether the overflow menu (if it exists) should be shown always, or only on hover */ overflowMenuOnHover: PropTypes__default["default"].bool, /** * Change the row height of table. Currently supports `xs`, `sm`, `md`, `lg`, and `xl`. */ size: PropTypes__default["default"].oneOf(['xs', 'sm', 'md', 'lg', 'xl']), /** * `false` If true, will keep the header sticky (only data rows will scroll) */ stickyHeader: PropTypes__default["default"].bool, /** * `false` If true, will use a width of 'auto' instead of 100% */ useStaticWidth: PropTypes__default["default"].bool, /** * `true` to add useZebraStyles striping. */ useZebraStyles: PropTypes__default["default"].bool, /** * Specify the table tabIndex */ tabIndex: PropTypes__default["default"].number }; exports.Table = Table; exports["default"] = Table;