@carbon/react
Version:
React components for the Carbon Design System
175 lines (168 loc) • 6.61 kB
JavaScript
/**
* 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.
*/
;
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.js');
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,
...other
}) => {
const {
titleId,
descriptionId
} = React.useContext(TableContext.TableContext);
const prefix = usePrefix.usePrefix();
const tableRef = React.useRef(null);
const componentClass = cx(`${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) => {
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 = React.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 = 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);
// 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.createElement("div", {
className: `${prefix}--data-table-content`
}, /*#__PURE__*/React.createElement("table", _rollupPluginBabelHelpers.extends({
"aria-labelledby": titleId,
"aria-describedby": descriptionId
}, other, {
className: componentClass,
ref: tableRef
}), children));
return stickyHeader ? /*#__PURE__*/React.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.node,
className: PropTypes.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.bool,
/**
* `false` If true, will apply sorting styles
*/
isSortable: PropTypes.bool,
/**
* Specify whether the overflow menu (if it exists) should be shown always, or only on hover
*/
overflowMenuOnHover: PropTypes.bool,
/**
* Change the row height of table. Currently supports `xs`, `sm`, `md`, `lg`, and `xl`.
*/
size: PropTypes.oneOf(['xs', 'sm', 'md', 'lg', 'xl']),
/**
* `false` If true, will keep the header sticky (only data rows will scroll)
*/
stickyHeader: PropTypes.bool,
/**
* If `true`, sets the table width to `auto` instead of `100%`.
*/
useStaticWidth: PropTypes.bool,
/**
* `true` to add useZebraStyles striping.
*/
useZebraStyles: PropTypes.bool,
/**
* Specify the table tabIndex
*/
tabIndex: PropTypes.number
};
exports.Table = Table;
exports.default = Table;