@carbon/ibm-products
Version:
Carbon for IBM Products
326 lines (320 loc) • 12.3 kB
JavaScript
/**
* 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;