UNPKG

@carbon/react

Version:

React components for the Carbon Design System

196 lines (191 loc) 6.72 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. */ import { extends as _extends } from '../../_virtual/_rollupPluginBabelHelpers.js'; import cx from 'classnames'; import PropTypes from 'prop-types'; import React, { useRef, cloneElement } from 'react'; import { ArrowUp, ArrowsVertical } from '@carbon/icons-react'; import './state/sorting.js'; import { useId } from '../../internal/useId.js'; import { usePrefix } from '../../internal/usePrefix.js'; import { AILabel } from '../AILabel/index.js'; import { isComponentElement } from '../../internal/utils.js'; import { sortStates } from './state/sortStates.js'; const defaultScope = 'col'; const translationKeys = { buttonDescription: 'carbon.table.header.icon.description' }; const translateWithId = (key, args) => { if (args && key === translationKeys.buttonDescription) { if (args.isSortHeader && sortStates) { // When transitioning, we know that the sequence of states is as follows: // NONE -> ASC -> DESC -> NONE 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 ''; }; const sortDirections = { [sortStates.NONE]: 'none', [sortStates.ASC]: 'ascending', [sortStates.DESC]: 'descending' }; const TableHeader = /*#__PURE__*/React.forwardRef(function TableHeader({ className: headerClassName, children, colSpan, decorator, isSortable = false, isSortHeader, onClick, scope = defaultScope, sortDirection, translateWithId: t = translateWithId, slug, id, ...rest }, ref) { const prefix = usePrefix(); const uniqueId = useId('table-sort'); // AILabel is always size `mini` const AILableRef = useRef(null); const candidate = slug ?? decorator; const candidateIsAILabel = isComponentElement(candidate, AILabel); const colHasAILabel = candidateIsAILabel; const normalizedDecorator = candidateIsAILabel ? /*#__PURE__*/cloneElement(candidate, { size: 'mini', ref: AILableRef }) : null; const headerLabelClassNames = cx({ [`${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__*/React.createElement("th", _extends({}, rest, { id: id, className: headerClassName, scope: scope, colSpan: colSpan, ref: ref }), children ? /*#__PURE__*/React.createElement("div", { className: headerLabelClassNames }, children, /*#__PURE__*/React.createElement("div", { className: `${prefix}--table-header-label--decorator-inner` }, normalizedDecorator)) : null); } const className = cx(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 = cx(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__*/React.createElement("th", { id: id, "aria-sort": ariaSort, className: headerClasses, colSpan: colSpan, ref: ref, scope: scope }, /*#__PURE__*/React.createElement("div", { className: `${prefix}--table-sort__description`, id: uniqueId }, sortDescription), /*#__PURE__*/React.createElement("button", _extends({ type: "button", "aria-describedby": uniqueId, className: className, onClick: handleClick }, rest), /*#__PURE__*/React.createElement("span", { className: `${prefix}--table-sort__flex` }, /*#__PURE__*/React.createElement("div", { className: `${prefix}--table-header-label` }, children), /*#__PURE__*/React.createElement(ArrowUp, { size: 20, className: `${prefix}--table-sort__icon` }), /*#__PURE__*/React.createElement(ArrowsVertical, { size: 20, className: `${prefix}--table-sort__icon-unsorted` }), /*#__PURE__*/React.createElement("div", { className: `${prefix}--table-header-label--decorator-inner` }, normalizedDecorator)))); }); TableHeader.propTypes = { /** * Pass in children that will be embedded in the table header label */ children: PropTypes.node, /** * Specify an optional className to be applied to the container node */ className: PropTypes.string, /** * Specify `colSpan` as a non-negative integer value to indicate how * many columns the TableHeader cell extends in a table */ colSpan: PropTypes.number, /** * Supply an id to the th element. */ id: PropTypes.string, /** * Specify whether this header is the header by which a table is being sorted * by */ isSortHeader: PropTypes.bool, /** * Specify whether this header is one through which a user can sort the table */ isSortable: PropTypes.bool, /** * Hook that is invoked when the header is clicked */ onClick: PropTypes.func, /** * Specify the scope of this table header. You can find more info about this * attribute at the following URL: * https://developer.mozilla.org/en-US/docs/Web/HTML/Element/th#attr-scope */ scope: PropTypes.string, /** * Specify which direction we are currently sorting by, should be one of DESC, * NONE, or ASC. */ sortDirection: PropTypes.oneOf(Object.values(sortStates)), /** * Supply a method to translate internal strings with your i18n tool of * choice. Translation keys are available on the `translationKeys` field for * this component. */ translateWithId: PropTypes.func }; TableHeader.translationKeys = Object.values(translationKeys); TableHeader.displayName = 'TableHeader'; export { TableHeader as default };