UNPKG

@carbon/ibm-products

Version:
297 lines (295 loc) 11.3 kB
/** * Copyright IBM Corp. 2020, 2026 * * 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 { __toESM } from "../../_virtual/_rolldown/runtime.js"; import { require_classnames } from "../../node_modules/classnames/index.js"; import { pkg } from "../../settings.js"; import { usePreviousValue } from "../../global/js/hooks/usePreviousValue.js"; import { prepareProps } from "../../global/js/utils/props-helper.js"; import { selectAllCells } from "./utils/selectAllCells.js"; import { useSpreadsheetMouseMove } from "./hooks/useSpreadsheetMouseMove.js"; import { checkActiveHeaderCell } from "./utils/checkActiveHeaderCell.js"; import { checkSelectedHeaderCell } from "./utils/checkSelectedHeaderCell.js"; import { getSpreadsheetWidth } from "./utils/getSpreadsheetWidth.js"; import { checkForHoldingKey } from "./utils/checkForHoldingKey.js"; import { handleHeaderCellSelection } from "./utils/handleHeaderCellSelection.js"; import React, { forwardRef, useEffect, useState } from "react"; import PropTypes from "prop-types"; import { px } from "@carbon/layout"; //#region src/components/DataSpreadsheet/DataSpreadsheetHeader.tsx /** * Copyright IBM Corp. 2022, 2022 * * This source code is licensed under the Apache-2.0 license found in the * LICENSE file in the root directory of this source tree. */ var import_classnames = /* @__PURE__ */ __toESM(require_classnames()); const blockClass = `${pkg.prefix}--data-spreadsheet`; const DataSpreadsheetHeader = forwardRef(({ 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] = useState(0); const previousState = usePreviousValue({ cellSize }) || {}; useEffect(() => { if (previousState?.cellSize !== cellSize) { const scrollContainer = ref?.current?.querySelector(`.${blockClass}__list--container`); const scrollBarValue = scrollContainer?.scrollHeight && scrollContainer?.clientHeight && scrollContainer?.scrollHeight > scrollContainer?.clientHeight ? 0 : scrollBarSize; if (!scrollContainer?.getAttribute("tabIndex")) scrollContainer?.setAttribute("tabIndex", "0"); setScrollBarSizeValue(scrollBarValue); } }, [ cellSize, ref, scrollBarSize, previousState?.cellSize ]); const handleColumnHeaderClick = (index) => { return (event) => { handleHeaderCellSelection({ type: "column", activeCellCoordinates, rows, columns, currentMatcher, setActiveCellCoordinates, setCurrentMatcher, setSelectionAreas, spreadsheetRef: ref, index, isKeyboard: void 0, setSelectionAreaData, isHoldingCommandKey: checkForHoldingKey(event, "cmd"), isHoldingShiftKey: checkForHoldingKey(event, "shiftKey") }); }; }; const handleSelectAllClick = () => { selectAllCells({ ref, setCurrentMatcher, setSelectionAreas, rows, columns, activeCellCoordinates, updateActiveCellCoordinates }); }; const handleHeaderMouseDown = (index) => { return (event) => { if (event.shiftKey) 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 = (ref?.current ?? 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 = 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({ ref, headerCellHoldActive, defaultColumn }); return /* @__PURE__ */ React.createElement("div", { className: (0, import_classnames.default)(`${blockClass}__header--container`), role: "rowgroup" }, headerGroups?.map((headerGroup, index) => { const headerProps = prepareProps(headerGroup.getHeaderGroupProps(), "key"); return /* @__PURE__ */ React.createElement("div", { key: `header_${index}`, ...headerProps, style: { ...headerGroup.getHeaderGroupProps().style, width: getSpreadsheetWidth({ type: "header", headerGroup, scrollBarSizeValue, totalVisibleColumns, defaultColumn, totalColumnsWidth: void 0, 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: (0, import_classnames.default)(`${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 = prepareProps(column.getHeaderProps(), "key"); const selectedHeader = checkSelectedHeaderCell(index, selectionAreas, "column", rows); return /* @__PURE__ */ React.createElement("div", { 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) : void 0, onMouseUp: selectedHeader && !readOnlyTable && !disableColumnSwapping && typeof setSelectedHeaderReorderActive === "function" ? () => setSelectedHeaderReorderActive(false) : void 0, onClick: !selectedHeader ? handleColumnHeaderClick(index) : void 0, style: { height: defaultColumn?.rowHeight, width: column?.width || defaultColumn?.width }, className: (0, import_classnames.default)(`${blockClass}__th`, `${blockClass}--interactive-cell-element`, { [`${blockClass}__th--active-header`]: activeCellCoordinates?.column === index || 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: PropTypes.shape({ row: PropTypes.oneOfType([PropTypes.number, PropTypes.string]), column: PropTypes.oneOfType([PropTypes.number, PropTypes.string]) }), /** * Specifies the cell height */ cellSize: PropTypes.oneOf([ "xs", "sm", "md", "lg" ]), /** * All of the spreadsheet columns */ columns: PropTypes.array, /** * uuid that corresponds to the current selection area */ currentMatcher: PropTypes.string, /** * Default spreadsheet sizing values */ /**@ts-ignore */ defaultColumn: PropTypes.shape({ rowHeight: PropTypes.number, rowHeaderWidth: PropTypes.number, width: PropTypes.number }), /** * Disable column swapping, default false */ disableColumnSwapping: PropTypes.bool, /** * Whether or not a click/hold is active on a header cell */ headerCellHoldActive: PropTypes.bool, /** * Headers provided from useTable hook */ headerGroups: PropTypes.arrayOf(PropTypes.object), /** * Read-only table */ readOnlyTable: PropTypes.bool, /** * All of the spreadsheet row data */ /**@ts-ignore */ rows: PropTypes.arrayOf(PropTypes.object), /** * The scrollbar width */ scrollBarSize: PropTypes.number, /** * The aria label applied to the Select all button */ selectAllAriaLabel: PropTypes.string.isRequired, /** * Header reordering is active */ selectedHeaderReorderActive: PropTypes.bool, /** * All of the cell selection area items */ /**@ts-ignore */ selectionAreas: PropTypes.arrayOf(PropTypes.object), /** * Setter fn for activeCellCoordinates value */ setActiveCellCoordinates: PropTypes.func, /** * Setter fn for currentMatcher value */ setCurrentMatcher: PropTypes.func, /** * Setter fn for header cell hold active value */ setHeaderCellHoldActive: PropTypes.func, /** * Set header reordering active or not */ setSelectedHeaderReorderActive: PropTypes.func, /** * Setter fn for selectionAreaData state value */ setSelectionAreaData: PropTypes.func, /** * Setter fn for selectionAreas value */ setSelectionAreas: PropTypes.func, /** * The total number of columns to be initially visible, additional columns will be rendered and * visible via horizontal scrollbar */ totalVisibleColumns: PropTypes.number, /** * Function used to update the active cell coordinates */ updateActiveCellCoordinates: PropTypes.func, /** * Array of visible columns provided by react-table useTable hook */ /**@ts-ignore */ visibleColumns: PropTypes.array }; //#endregion export { DataSpreadsheetHeader };