UNPKG

@carbon/ibm-products

Version:

Carbon for IBM Products

326 lines (320 loc) 12.3 kB
/** * Copyright IBM Corp. 2020, 2025 * * 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'; var _rollupPluginBabelHelpers = require('../../_virtual/_rollupPluginBabelHelpers.js'); var React = require('react'); var index = require('../../_virtual/index.js'); var cx = require('classnames'); var layout = require('@carbon/layout'); var settings = require('../../settings.js'); var checkActiveHeaderCell = require('./utils/checkActiveHeaderCell.js'); var checkSelectedHeaderCell = require('./utils/checkSelectedHeaderCell.js'); var handleHeaderCellSelection = require('./utils/handleHeaderCellSelection.js'); var selectAllCells = require('./utils/selectAllCells.js'); var getSpreadsheetWidth = require('./utils/getSpreadsheetWidth.js'); var checkForHoldingKey = require('./utils/checkForHoldingKey.js'); var propsHelper = require('../../global/js/utils/props-helper.js'); var usePreviousValue = require('../../global/js/hooks/usePreviousValue.js'); var useSpreadsheetMouseMove = require('./hooks/useSpreadsheetMouseMove.js'); const blockClass = `${settings.pkg.prefix}--data-spreadsheet`; const DataSpreadsheetHeader = /*#__PURE__*/React.forwardRef((_ref, ref) => { let { activeCellCoordinates, cellSize, columns, currentMatcher, defaultColumn, headerGroups, scrollBarSize, selectionAreas, selectedHeaderReorderActive, setSelectedHeaderReorderActive, setActiveCellCoordinates, setCurrentMatcher, setSelectionAreas, readOnlyTable, disableColumnSwapping, setSelectionAreaData, rows, totalVisibleColumns, updateActiveCellCoordinates, setHeaderCellHoldActive, headerCellHoldActive, selectAllAriaLabel, visibleColumns } = _ref; const [scrollBarSizeValue, setScrollBarSizeValue] = React.useState(0); const previousState = usePreviousValue.usePreviousValue({ cellSize }) || {}; React.useEffect(() => { if (previousState?.cellSize !== cellSize) { const scrollContainer = ref?.current?.querySelector(`.${blockClass}__list--container`); const hasScrollBar = scrollContainer?.scrollHeight && scrollContainer?.clientHeight && scrollContainer?.scrollHeight > scrollContainer?.clientHeight; const scrollBarValue = hasScrollBar ? 0 : scrollBarSize; // fix for a11y violation element_scrollable_tabbable if (!scrollContainer?.getAttribute('tabIndex')) { scrollContainer?.setAttribute('tabIndex', '0'); } setScrollBarSizeValue(scrollBarValue); } }, [cellSize, ref, scrollBarSize, previousState?.cellSize]); const handleColumnHeaderClick = index => { return event => { const isHoldingCommandKey = checkForHoldingKey.checkForHoldingKey(event, 'cmd'); const isHoldingShiftKey = checkForHoldingKey.checkForHoldingKey(event, 'shiftKey'); handleHeaderCellSelection.handleHeaderCellSelection({ type: 'column', activeCellCoordinates, rows, columns, currentMatcher, setActiveCellCoordinates, setCurrentMatcher, setSelectionAreas, spreadsheetRef: ref, index, isKeyboard: undefined, setSelectionAreaData, isHoldingCommandKey, isHoldingShiftKey }); }; }; const handleSelectAllClick = () => { selectAllCells.selectAllCells({ ref, setCurrentMatcher, setSelectionAreas, rows, columns, activeCellCoordinates, updateActiveCellCoordinates }); }; const handleHeaderMouseDown = index => { return event => { if (event.shiftKey) { // Remove columns, need to call handleHeaderCellSelection return; } const selectionAreaToClone = selectionAreas?.filter(item => item?.matcher === currentMatcher); const selectionAreaElement = ref.current.querySelector(`[data-matcher-id="${selectionAreaToClone?.[0]?.matcher}"]`); if (selectionAreaElement) { selectionAreaElement.classList.add(`${blockClass}__selection-area--element`); } if (typeof setSelectedHeaderReorderActive === 'function') { setSelectedHeaderReorderActive(true); } const clickXPosition = event.clientX; const headerButtonCoords = event.target.getBoundingClientRect(); const headerIndex = event.target.getAttribute('data-column-index'); const offsetXValue = clickXPosition - headerButtonCoords.left; const lowestColumnIndexFromSelectionArea = Math.min(selectionAreaToClone?.[0]?.point1?.column || 0, selectionAreaToClone?.[0]?.point2?.column || 0); const selectionAreaCoords = selectionAreaElement?.getBoundingClientRect() || new DOMRect(); const updatedOffsetDifference = lowestColumnIndexFromSelectionArea < parseInt(headerIndex) ? offsetXValue + (headerButtonCoords.left - selectionAreaCoords?.left) : offsetXValue; const bodyContainer = document?.querySelector(`.${blockClass}__list--container`)?.firstElementChild; const selectionAreaClonedElement = selectionAreaElement?.cloneNode(); const reorderIndicatorLine = selectionAreaElement?.cloneNode(); if (reorderIndicatorLine) { reorderIndicatorLine.className = `${blockClass}__reorder-indicator-line`; reorderIndicatorLine.style.width = layout.px(2); } selectionAreaClonedElement.classList.add(`${blockClass}__selection-area--element-cloned`); selectionAreaClonedElement.setAttribute('data-clone-offset-x', `${updatedOffsetDifference}`); selectionAreaClonedElement.setAttribute('data-column-index-original', index); bodyContainer?.appendChild(selectionAreaClonedElement); bodyContainer?.appendChild(reorderIndicatorLine); setHeaderCellHoldActive?.(true); }; }; useSpreadsheetMouseMove.useSpreadsheetMouseMove({ ref, headerCellHoldActive, defaultColumn }); return /*#__PURE__*/React.createElement("div", { className: cx(`${blockClass}__header--container`), role: "rowgroup" }, headerGroups?.map((headerGroup, index) => { const headerProps = propsHelper.prepareProps(headerGroup.getHeaderGroupProps(), 'key'); return /*#__PURE__*/React.createElement("div", _rollupPluginBabelHelpers.extends({ key: `header_${index}` }, headerProps, { style: { ...headerGroup.getHeaderGroupProps().style, width: getSpreadsheetWidth.getSpreadsheetWidth({ type: 'header', headerGroup, scrollBarSizeValue, totalVisibleColumns, defaultColumn, totalColumnsWidth: undefined, visibleColumns }), overflow: 'hidden' }, className: `${blockClass}__tr` }), /*#__PURE__*/React.createElement("div", { role: "columnheader", className: `${blockClass}__select-all-cell-container`, style: { width: defaultColumn?.rowHeaderWidth, height: defaultColumn?.rowHeight } }, /*#__PURE__*/React.createElement("button", { id: `${blockClass}__cell--header--header`, "data-row-index": "header", "data-column-index": "header", type: "button", style: { width: defaultColumn?.rowHeaderWidth }, tabIndex: -1, "aria-label": selectAllAriaLabel, onClick: handleSelectAllClick, className: cx(`${blockClass}__th`, `${blockClass}--interactive-cell-element`, `${blockClass}__th--select-all`, { [`${blockClass}__th--active-header`]: activeCellCoordinates?.column === 'header' && activeCellCoordinates?.row === 'header' }) }, "\xA0")), headerGroup.headers.map((column, index) => { const colProps = propsHelper.prepareProps(column.getHeaderProps(), 'key'); const selectedHeader = checkSelectedHeaderCell.checkSelectedHeaderCell(index, selectionAreas, 'column', rows); return /*#__PURE__*/React.createElement("div", _rollupPluginBabelHelpers.extends({ key: `column_${index}`, role: "columnheader", className: `${blockClass}__columnheader` }, colProps), /*#__PURE__*/React.createElement("button", { id: `${blockClass}__cell--header--${index}`, "data-row-index": "header", "data-column-index": index, tabIndex: -1, onMouseDown: selectedHeader && !readOnlyTable && !disableColumnSwapping ? handleHeaderMouseDown(index) : undefined, onMouseUp: selectedHeader && !readOnlyTable && !disableColumnSwapping && typeof setSelectedHeaderReorderActive === 'function' ? () => setSelectedHeaderReorderActive(false) : undefined, onClick: !selectedHeader ? handleColumnHeaderClick(index) : undefined, style: { height: defaultColumn?.rowHeight, width: column?.width || defaultColumn?.width }, className: cx(`${blockClass}__th`, `${blockClass}--interactive-cell-element`, { [`${blockClass}__th--active-header`]: activeCellCoordinates?.column === index || checkActiveHeaderCell.checkActiveHeaderCell(index, selectionAreas, 'column'), [`${blockClass}__th--active-header-disabledSwapping`]: disableColumnSwapping || readOnlyTable, [`${blockClass}__th--selected-header`]: selectedHeader, [`${blockClass}__th--selected-header-reorder-active`]: selectedHeaderReorderActive }), type: "button" }, column.render('Header'))); })); })); }); DataSpreadsheetHeader.propTypes = { /** * Object containing the active cell coordinates */ /**@ts-ignore */ activeCellCoordinates: index.default.shape({ row: index.default.oneOfType([index.default.number, index.default.string]), column: index.default.oneOfType([index.default.number, index.default.string]) }), /** * Specifies the cell height */ cellSize: index.default.oneOf(['xs', 'sm', 'md', 'lg']), /** * All of the spreadsheet columns */ columns: index.default.array, /** * uuid that corresponds to the current selection area */ currentMatcher: index.default.string, /** * Default spreadsheet sizing values */ /**@ts-ignore */ defaultColumn: index.default.shape({ rowHeight: index.default.number, rowHeaderWidth: index.default.number, width: index.default.number }), /** * Disable column swapping, default false */ disableColumnSwapping: index.default.bool, /** * Whether or not a click/hold is active on a header cell */ headerCellHoldActive: index.default.bool, /** * Headers provided from useTable hook */ headerGroups: index.default.arrayOf(index.default.object), /** * Read-only table */ readOnlyTable: index.default.bool, /** * All of the spreadsheet row data */ /**@ts-ignore */ rows: index.default.arrayOf(index.default.object), /** * The scrollbar width */ scrollBarSize: index.default.number, /** * The aria label applied to the Select all button */ selectAllAriaLabel: index.default.string.isRequired, /** * Header reordering is active */ selectedHeaderReorderActive: index.default.bool, /** * All of the cell selection area items */ /**@ts-ignore */ selectionAreas: index.default.arrayOf(index.default.object), /** * Setter fn for activeCellCoordinates value */ setActiveCellCoordinates: index.default.func, /** * Setter fn for currentMatcher value */ setCurrentMatcher: index.default.func, /** * Setter fn for header cell hold active value */ setHeaderCellHoldActive: index.default.func, /** * Set header reordering active or not */ setSelectedHeaderReorderActive: index.default.func, /** * Setter fn for selectionAreaData state value */ setSelectionAreaData: index.default.func, /** * Setter fn for selectionAreas value */ setSelectionAreas: index.default.func, /** * The total number of columns to be initially visible, additional columns will be rendered and * visible via horizontal scrollbar */ totalVisibleColumns: index.default.number, /** * Function used to update the active cell coordinates */ updateActiveCellCoordinates: index.default.func, /** * Array of visible columns provided by react-table useTable hook */ /**@ts-ignore */ visibleColumns: index.default.array }; exports.DataSpreadsheetHeader = DataSpreadsheetHeader;