UNPKG

@carbon/ibm-products

Version:

Carbon for IBM Products

830 lines (812 loc) 32 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 reactTable = require('react-table'); var React = require('react'); var DataSpreadsheetBody = require('./DataSpreadsheetBody.js'); var DataSpreadsheetHeader = require('./DataSpreadsheetHeader.js'); var index = require('../../_virtual/index.js'); var createActiveCellFn = require('./utils/createActiveCellFn.js'); var cx = require('classnames'); var deepCloneObject = require('../../global/js/utils/deepCloneObject.js'); var getCellSize = require('./utils/getCellSize.js'); var devtools = require('../../global/js/utils/devtools.js'); var getScrollbarWidth = require('../../global/js/utils/getScrollbarWidth.js'); var handleEditSubmit = require('./utils/handleEditSubmit.js'); var handleHeaderCellSelection = require('./utils/handleHeaderCellSelection.js'); var getNodeTextContent = require('../../global/js/utils/getNodeTextContent.js'); var commonEventHandlers = require('./utils/commonEventHandlers.js'); var settings = require('../../settings.js'); var removeCellSelections = require('./utils/removeCellSelections.js'); var selectAllCells = require('./utils/selectAllCells.js'); var uuidv4 = require('../../global/js/utils/uuidv4.js'); var useActiveElement = require('../../global/js/hooks/useActiveElement.js'); var usePreviousValue = require('../../global/js/hooks/usePreviousValue.js'); var useMultipleKeyTracking = require('./hooks/useMultipleKeyTracking.js'); var useResetSpreadsheetFocus = require('./hooks/useResetSpreadsheetFocus.js'); var useSpreadsheetOutsideClick = require('./hooks/useSpreadsheetOutsideClick.js'); var useMoveActiveCell = require('./hooks/useMoveActiveCell.js'); var useSpreadsheetEdit = require('./hooks/useSpreadsheetEdit.js'); // The block part of our conventional BEM class names (blockClass__E--M). const blockClass = `${settings.pkg.prefix}--data-spreadsheet`; const componentName = 'DataSpreadsheet'; // Default values for props const defaults = { columns: Object.freeze([]), data: Object.freeze([]), defaultEmptyRowCount: 16, onDataUpdate: Object.freeze(() => {}), onColDrag: Object.freeze(() => {}), onActiveCellChange: Object.freeze(() => {}), onSelectionAreaChange: Object.freeze(() => {})}; /** * DataSpreadsheet: used to organize and display large amounts of structured data, separated by columns and rows in a grid-like format. */ exports.DataSpreadsheet = /*#__PURE__*/React.forwardRef((_ref, ref) => { let { // The component props, in alphabetical order (for consistency). cellSize = 'sm', className, columns = defaults.columns, data = defaults.data, defaultEmptyRowCount = defaults.defaultEmptyRowCount, onDataUpdate = defaults.onDataUpdate, onColDrag = defaults.onColDrag, id, onActiveCellChange = defaults.onActiveCellChange, onSelectionAreaChange = defaults.onSelectionAreaChange, renderRowHeader, renderRowHeaderDirection, disableColumnSwapping = false, readOnlyTable = false, selectAllAriaLabel, spreadsheetAriaLabel, theme, totalVisibleColumns, // Collect any other property values passed in. ...rest } = _ref; const multiKeyTrackingRef = React.useRef(null); const localRef = React.useRef(undefined); const spreadsheetRef = ref || localRef; const focusedElement = useActiveElement.useActiveElement(); const [currentColumns, setCurrentColumns] = React.useState(columns); const [pastColumns, setPastColumns] = React.useState([]); const [containerHasFocus, setContainerHasFocus] = React.useState(false); const [activeCellCoordinates, setActiveCellCoordinates] = React.useState(null); const [selectionAreas, setSelectionAreas] = React.useState([]); const [selectionAreaData, setSelectionAreaData] = React.useState([]); const [clickAndHoldActive, setClickAndHoldActive] = React.useState(false); const [currentMatcher, setCurrentMatcher] = React.useState(''); const [isEditing, setIsEditing] = React.useState(false); const [cellEditorValue, setCellEditorValue] = React.useState(''); const [headerCellHoldActive, setHeaderCellHoldActive] = React.useState(false); const [selectedHeaderReorderActive, setSelectedHeaderReorderActive] = React.useState(false); const isBlurSpreadsheet = React.useRef(false); const [isActiveHeaderCellChanged, setIsActiveHeaderCellChanged] = React.useState(false); const [activeCellInsideSelectionArea, setActiveCellInsideSelectionArea] = React.useState(false); const previousState = usePreviousValue.usePreviousValue({ activeCellCoordinates, isEditing, cellEditorValue, selectedHeaderReorderActive }) || {}; const cellSizeValue = getCellSize.getCellSize(cellSize); const cellEditorRef = React.useRef(undefined); const [activeCellContent, setActiveCellContent] = React.useState(); const activeCellRef = React.useRef(undefined); const cellEditorRulerRef = React.useRef(undefined); const hasCustomRowHeader = typeof renderRowHeader === 'function'; const maxNumRowsCount = data.length.toString().length; const defaultColumn = React.useMemo(() => ({ width: 150, rowHeaderWidth: hasCustomRowHeader ? 40 + maxNumRowsCount * 8.56 : 64, rowHeight: cellSizeValue }), [cellSizeValue, hasCustomRowHeader, maxNumRowsCount]); const { keysPressedList, usingMac } = useMultipleKeyTracking.useMultipleKeyTracking({ ref: multiKeyTrackingRef, containerHasFocus, isEditing }); const scrollBarSize = React.useMemo(() => getScrollbarWidth.getScrollbarWidth(), []); const { getTableProps, getTableBodyProps, headerGroups, rows, totalColumnsWidth, prepareRow, setColumnOrder, visibleColumns } = reactTable.useTable({ columns, data, defaultColumn }, reactTable.useBlockLayout, reactTable.useColumnOrder); // Update the spreadsheet data after editing a cell const updateData = React.useCallback((rowIndex, columnId, newValue) => { onDataUpdate(prev => prev.map((row, index) => { if (index === rowIndex) { return { ...prev[rowIndex], [columnId]: cellEditorValue || newValue }; } return row; })); }, [cellEditorValue, onDataUpdate]); React.useEffect(() => { const currentHeaders = []; if (Object.keys(currentColumns).length > 0) { Object.keys(currentColumns).forEach(itemIndex => { if (typeof currentColumns[itemIndex].Header === 'object') { if (currentColumns[itemIndex].column_name) { currentHeaders.push(currentColumns[itemIndex].column_name); } else { currentHeaders.push(getNodeTextContent.getNodeTextContent(currentColumns[itemIndex].Header)); } } else if (currentColumns[itemIndex].Header) { currentHeaders.push(currentColumns[itemIndex].Header); } }); } if (previousState.selectedHeaderReorderActive) { setPastColumns(currentHeaders); } if (!previousState.selectedHeaderReorderActive && pastColumns.length > 0 && !headerCellHoldActive && JSON.stringify(currentHeaders) !== JSON.stringify(pastColumns)) { onColDrag({ headers: currentHeaders, data: activeCellContent.props.data }); if (currentHeaders.length === 0) { setPastColumns([]); } } }, [previousState?.selectedHeaderReorderActive, currentColumns, headerCellHoldActive, columns, activeCellContent, onColDrag, pastColumns]); // Removes the active cell element const removeActiveCell = React.useCallback(() => { const activeCellHighlight = spreadsheetRef?.current?.querySelector(`.${blockClass}__active-cell--highlight`); if (activeCellHighlight) { activeCellHighlight.style.display = 'none'; } }, [spreadsheetRef]); const removeCellEditor = React.useCallback(() => { setCellEditorValue(''); setIsEditing(false); if (cellEditorRef?.current) { cellEditorRef.current.style.display = 'none'; } }, []); // Remove cell editor if the active cell coordinates change and save with new cell data, this will // happen if you click on another cell while isEditing is true React.useEffect(() => { const prevCoords = previousState?.activeCellCoordinates; if ((prevCoords?.row !== activeCellCoordinates?.row || prevCoords?.column !== activeCellCoordinates?.column) && isEditing) { const cellProps = rows[Number(prevCoords?.row)].cells[Number(prevCoords?.column)]; removeCellEditor(); updateData(prevCoords?.row, cellProps.column.id, undefined); if (cellEditorRulerRef?.current) { cellEditorRulerRef.current.textContent = ''; } } if (prevCoords?.row !== activeCellCoordinates?.row || prevCoords?.column !== activeCellCoordinates?.column) { if (activeCellCoordinates && activeCellCoordinates?.row !== 'header' && activeCellCoordinates?.column !== 'header') { const activeCellFullData = typeof activeCellCoordinates?.column === 'number' && typeof activeCellCoordinates?.row === 'number' ? rows[activeCellCoordinates?.row].cells[activeCellCoordinates?.column] : null; if (activeCellFullData) { setActiveCellContent(activeCellFullData.render('Cell')); } else { setActiveCellContent(null); } } if (activeCellCoordinates && activeCellCoordinates?.row === 'header' || activeCellCoordinates?.column === 'header') { setActiveCellContent(null); setIsActiveHeaderCellChanged(prev => !prev); } } // For when we edit and focus out of data spreadsheet if (isEditing && previousState.activeCellCoordinates && isBlurSpreadsheet.current) { setActiveCellContent(previousState.cellEditorValue); isBlurSpreadsheet.current = false; removeCellEditor(); } }, [isBlurSpreadsheet, activeCellCoordinates, previousState?.activeCellCoordinates, previousState?.cellEditorValue, updateData, rows, isEditing, removeCellEditor, activeCellContent]); const createActiveCell = React.useCallback(_ref2 => { let { placementElement, coords, addToHeader = false } = _ref2; const activeCellFullData = typeof coords?.column === 'number' && typeof coords?.row === 'number' ? rows[coords?.row].cells[coords?.column] : null; const activeCellValue = activeCellFullData ? Object.values(activeCellFullData.row.values)[coords?.column] : null; createActiveCellFn.createActiveCellFn({ placementElement, coords, addToHeader, contextRef: spreadsheetRef, blockClass, onActiveCellChange, activeCellValue, activeCellRef, cellEditorRef, defaultColumn }); }, [spreadsheetRef, rows, onActiveCellChange, defaultColumn]); useResetSpreadsheetFocus.useResetSpreadsheetFocus({ focusedElement, removeActiveCell, setContainerHasFocus }); useSpreadsheetOutsideClick.useSpreadsheetOutsideClick({ isBlurSpreadsheet, spreadsheetRef, setActiveCellCoordinates, setSelectionAreas, removeActiveCell, setContainerHasFocus, removeCellEditor }); useMoveActiveCell.useMoveActiveCell({ spreadsheetRef, activeCellCoordinates, containerHasFocus, createActiveCell, activeCellContent, isActiveHeaderCellChanged }); const handleInitialArrowPress = React.useCallback(() => { // If activeCellCoordinates is null then we need to set an initial value // which will place the activeCell on the select all cell/button setActiveCellInsideSelectionArea(false); if (!activeCellCoordinates) { setActiveCellCoordinates({ column: 'header', row: 'header' }); } return; }, [activeCellCoordinates]); const updateActiveCellCoordinates = React.useCallback(_ref3 => { let { coords = { ...activeCellCoordinates }, updatedValue, optOutOfSelectionAreaUpdate = false } = _ref3; const newActiveCell = { ...coords, ...updatedValue }; setActiveCellCoordinates(newActiveCell); // Only run if the active cell is _not_ a header cell. This will add a point1 object // to selectionAreas every time the active cell changes, allowing us to create cell // selections using keyboard. Opting out of the selection area updates here means // that the active cell is being moved within a selection area if (newActiveCell.row !== 'header' && newActiveCell.column !== 'header' && !optOutOfSelectionAreaUpdate) { const tempMatcher = uuidv4.default(); setSelectionAreas([{ point1: newActiveCell, matcher: tempMatcher }]); setCurrentMatcher(tempMatcher); } }, [activeCellCoordinates]); const handleHomeEndKey = React.useCallback(_ref4 => { let { type } = _ref4; const coordinatesClone = { ...activeCellCoordinates }; updateActiveCellCoordinates({ coords: coordinatesClone, updatedValue: { column: type === 'Home' ? 0 : columns.length - 1 } }); removeCellSelections.removeCellSelections({ matcher: undefined, spreadsheetRef }); }, [activeCellCoordinates, updateActiveCellCoordinates, spreadsheetRef, columns.length]); const checkForReturnCondition = React.useCallback(key => { return isEditing || key === 'Meta' || key === 'Control'; }, [isEditing]); const handleArrowKeyPress = React.useCallback(arrowKey => { event?.preventDefault(); handleInitialArrowPress(); const coordinatesClone = { ...activeCellCoordinates }; let updatedValue; switch (arrowKey) { // Left case 'ArrowLeft': { if (coordinatesClone.column === 'header') { return; } if (typeof coordinatesClone.column === 'number') { if (coordinatesClone.column === 0) { updatedValue = { column: 'header' }; } else { updatedValue = { column: coordinatesClone.column - 1 }; } } break; } // Up case 'ArrowUp': { if (coordinatesClone.row === 'header') { return; } if (typeof coordinatesClone.row === 'number') { // set row back to header if we are at index 0 if (coordinatesClone.row === 0) { updatedValue = { row: 'header' }; } else { // if we are at any other index than 0, subtract 1 from current row index updatedValue = { row: coordinatesClone.row - 1 }; } } break; } // Right case 'ArrowRight': { if (coordinatesClone.column === 'header') { updatedValue = { column: 0 }; } if (typeof coordinatesClone.column === 'number') { // Prevent active cell coordinates from updating if the active // cell is in the last column, ie we can't go any further to the right if (columns.length - 1 === coordinatesClone.column) { return; } else { updatedValue = { column: coordinatesClone.column + 1 }; } } break; } // Down case 'ArrowDown': { if (coordinatesClone.row === 'header') { updatedValue = { row: 0 }; } if (typeof coordinatesClone.row === 'number') { // Prevent active cell coordinates from updating if the active // cell is in the last row, ie we can't go any further down since // we are in the last row if (rows.length - 1 === coordinatesClone.row) { return; } else { updatedValue = { row: coordinatesClone.row + 1 }; } } break; } } if (updatedValue) { updateActiveCellCoordinates({ coords: coordinatesClone, updatedValue }); } }, [handleInitialArrowPress, updateActiveCellCoordinates, activeCellCoordinates, columns, rows]); const handleKeyPressEvent = React.useCallback(event => { commonEventHandlers.handleKeyPress(event, activeCellInsideSelectionArea, updateActiveCellCoordinates, activeCellCoordinates, removeActiveCell, columns, rows, spreadsheetRef, currentMatcher, removeCellEditor, selectionAreas, handleHomeEndKey, keysPressedList, usingMac, updateData, checkForReturnCondition, handleArrowKeyPress, setSelectionAreas, setSelectionAreaData, setCurrentMatcher, activeCellRef, setActiveCellCoordinates, setContainerHasFocus, setActiveCellContent, readOnlyTable); }, [activeCellInsideSelectionArea, updateActiveCellCoordinates, activeCellCoordinates, removeActiveCell, columns, rows, spreadsheetRef, currentMatcher, removeCellEditor, selectionAreas, handleHomeEndKey, keysPressedList, usingMac, updateData, checkForReturnCondition, handleArrowKeyPress, readOnlyTable]); const startEditMode = () => { setIsEditing(true); setClickAndHoldActive(false); const activeCellFullData = typeof activeCellCoordinates?.column === 'number' && typeof activeCellCoordinates?.row === 'number' ? rows[activeCellCoordinates?.row].cells[activeCellCoordinates?.column] : null; const activeCellValue = activeCellFullData?.row?.cells?.[Number(activeCellCoordinates?.column)]?.value; setCellEditorValue(activeCellValue || ''); if (cellEditorRulerRef?.current) { cellEditorRulerRef.current.textContent = activeCellValue; } if (cellEditorRef?.current && activeCellRef?.current) { cellEditorRef.current.style.width = activeCellRef?.current?.style?.width; } }; // Sets the initial placement of the cell editor cursor at the end of the text area // this is not done for us by default in Safari React.useEffect(() => { if (isEditing && !previousState?.isEditing) { cellEditorRef?.current?.setSelectionRange(Number(cellEditorRulerRef?.current?.textContent?.length), Number(cellEditorRulerRef?.current?.textContent?.length)); cellEditorRef?.current?.focus(); } }, [isEditing, previousState?.isEditing]); const handleActiveCellClick = () => { if (activeCellCoordinates?.row === 'header' || activeCellCoordinates?.column === 'header') { const indexValue = activeCellCoordinates?.row === 'header' ? activeCellCoordinates?.column : activeCellCoordinates?.row; if (activeCellCoordinates?.row === 'header' && activeCellCoordinates?.column === 'header') { return; } handleRowColumnHeaderClick({ isKeyboard: false, index: Number(indexValue) }); } return; }; // Mouse up on active cell const handleActiveCellMouseUp = () => { setClickAndHoldActive(false); }; // Mouse down on active cell const handleActiveCellMouseDown = () => { if (activeCellCoordinates?.row !== 'header' || activeCellCoordinates?.column !== 'header') { const tempMatcher = uuidv4.default(); setClickAndHoldActive(true); removeCellSelections.removeCellSelections({ matcher: null, spreadsheetRef }); setSelectionAreas([{ point1: activeCellCoordinates, matcher: tempMatcher }]); setCurrentMatcher(tempMatcher); setSelectionAreaData([]); setActiveCellInsideSelectionArea(false); } return; }; // Go into edit mode if 'Enter' key is pressed on activeCellRef const handleActiveCellKeyDown = event => { const { key } = event; if (key === 'Enter' && !activeCellInsideSelectionArea && !readOnlyTable) { if (activeCellCoordinates?.column !== 'header' && activeCellCoordinates?.row !== 'header') { startEditMode(); } if (activeCellCoordinates?.row === 'header' || activeCellCoordinates?.column === 'header') { handleRowColumnHeaderClick({ isKeyboard: true }); } } }; const handleRowColumnHeaderClick = _ref5 => { let { isKeyboard, index = -1 } = _ref5; const handleHeaderCellProps = { activeCellCoordinates, rows, columns, currentMatcher, setActiveCellCoordinates, setCurrentMatcher, setSelectionAreas, spreadsheetRef, index, isKeyboard, setSelectionAreaData, isHoldingCommandKey: null, isHoldingShiftKey: null }; // Select an entire column if (activeCellCoordinates?.row === 'header' && activeCellCoordinates?.column !== 'header') { handleHeaderCellSelection.handleHeaderCellSelection({ type: 'column', ...handleHeaderCellProps }); } // Select an entire row if (activeCellCoordinates?.column === 'header' && activeCellCoordinates?.row !== 'header') { handleHeaderCellSelection.handleHeaderCellSelection({ type: 'row', ...handleHeaderCellProps }); } if (activeCellCoordinates?.column === 'header' && activeCellCoordinates?.row === 'header') { selectAllCells.selectAllCells({ ref: spreadsheetRef, setCurrentMatcher, setSelectionAreas, rows, columns, activeCellCoordinates, updateActiveCellCoordinates }); } }; // Go into edit mode if double click is detected on activeCellRef const handleActiveCellDoubleClick = readOnlyTable => { if (!readOnlyTable) { startEditMode(); } }; useSpreadsheetEdit.useSpreadsheetEdit({ isEditing, rows, activeCellCoordinates, activeCellRef, cellEditorRef, cellEditorRulerRef, visibleColumns, defaultColumn, cellEditorValue }); // Only update if there are cell selection areas // Find point object that matches currentMatcher and remove the second point // because hovering over the active cell while clicking and holding should // remove the previously existing selection area const handleActiveCellMouseEnterCallback = React.useCallback((areas, clickHold) => { if (!currentMatcher) { return; } if (areas && areas.length && clickHold && currentMatcher) { setSelectionAreas(prev => { const selectionAreaClone = deepCloneObject.deepCloneObject(prev); const indexOfItemToUpdate = selectionAreaClone.findIndex(item => item.matcher === currentMatcher); if (indexOfItemToUpdate === -1) { return prev; } if (typeof selectionAreaClone[indexOfItemToUpdate].point2 === 'object' && selectionAreaClone[indexOfItemToUpdate].areaCreated) { selectionAreaClone[indexOfItemToUpdate].point2 = selectionAreaClone[indexOfItemToUpdate].point1; selectionAreaClone[indexOfItemToUpdate].areaCreated = false; setActiveCellInsideSelectionArea(false); removeCellSelections.removeCellSelections({ matcher: currentMatcher, spreadsheetRef }); return selectionAreaClone; } return prev; }); } }, [spreadsheetRef, currentMatcher]); const handleActiveCellMouseEnter = React.useCallback(() => { handleActiveCellMouseEnterCallback(selectionAreas, clickAndHoldActive); }, [clickAndHoldActive, selectionAreas, handleActiveCellMouseEnterCallback]); // cspell:words rowcount colcount return /*#__PURE__*/React.createElement("div", _rollupPluginBabelHelpers.extends({}, rest, getTableProps(), devtools.getDevtoolsProps(componentName), { className: cx(blockClass, className, `${blockClass}--interactive-cell-element`, { [`${blockClass}__container-has-focus`]: containerHasFocus, [`${blockClass}__${theme}`]: theme === 'dark' }), ref: spreadsheetRef, role: "grid", tabIndex: 0, "aria-rowcount": rows?.length || 0, "aria-colcount": columns?.length || 0, "aria-label": spreadsheetAriaLabel, onKeyDown: handleKeyPressEvent, onFocus: () => setContainerHasFocus(true) }), /*#__PURE__*/React.createElement("div", { ref: multiKeyTrackingRef }, /*#__PURE__*/React.createElement(DataSpreadsheetHeader.DataSpreadsheetHeader, { ref: spreadsheetRef, activeCellCoordinates: activeCellCoordinates, cellSize: cellSize, columns: columns, currentMatcher: currentMatcher, defaultColumn: defaultColumn, selectedHeaderReorderActive: selectedHeaderReorderActive, setSelectedHeaderReorderActive: setSelectedHeaderReorderActive, headerGroups: headerGroups, rows: rows, scrollBarSize: scrollBarSize, selectionAreas: selectionAreas, setActiveCellCoordinates: setActiveCellCoordinates, setSelectionAreas: setSelectionAreas, setCurrentMatcher: setCurrentMatcher, setSelectionAreaData: setSelectionAreaData, disableColumnSwapping: disableColumnSwapping, readOnlyTable: readOnlyTable, totalVisibleColumns: totalVisibleColumns, updateActiveCellCoordinates: updateActiveCellCoordinates, setHeaderCellHoldActive: setHeaderCellHoldActive, headerCellHoldActive: headerCellHoldActive, visibleColumns: visibleColumns, selectAllAriaLabel: selectAllAriaLabel }), /*#__PURE__*/React.createElement(DataSpreadsheetBody.DataSpreadsheetBody, { activeCellRef: activeCellRef, activeCellCoordinates: activeCellCoordinates, setCurrentColumns: setCurrentColumns, ref: spreadsheetRef, clickAndHoldActive: clickAndHoldActive, setClickAndHoldActive: setClickAndHoldActive, currentMatcher: currentMatcher, setCurrentMatcher: setCurrentMatcher, setContainerHasFocus: setContainerHasFocus, selectedHeaderReorderActive: selectedHeaderReorderActive, setSelectedHeaderReorderActive: setSelectedHeaderReorderActive, selectionAreas: selectionAreas, setSelectionAreas: setSelectionAreas, headerGroups: headerGroups, defaultColumn: defaultColumn, getTableBodyProps: getTableBodyProps, hasCustomRowHeader: hasCustomRowHeader, onDataUpdate: onDataUpdate, renderRowHeaderDirection: renderRowHeaderDirection, renderRowHeader: renderRowHeader, onActiveCellChange: onActiveCellChange, onSelectionAreaChange: onSelectionAreaChange, prepareRow: prepareRow, rows: rows, selectionAreaData: selectionAreaData, setSelectionAreaData: setSelectionAreaData, setActiveCellCoordinates: setActiveCellCoordinates, scrollBarSize: scrollBarSize, totalColumnsWidth: totalColumnsWidth, id: id, columns: columns, defaultEmptyRowCount: defaultEmptyRowCount, setActiveCellInsideSelectionArea: setActiveCellInsideSelectionArea, totalVisibleColumns: totalVisibleColumns, setHeaderCellHoldActive: setHeaderCellHoldActive, setColumnOrder: setColumnOrder, visibleColumns: visibleColumns }), /*#__PURE__*/React.createElement("button", { onMouseDown: handleActiveCellMouseDown, onMouseUp: handleActiveCellMouseUp, onClick: handleActiveCellClick, onKeyDown: handleActiveCellKeyDown, onDoubleClick: () => handleActiveCellDoubleClick(readOnlyTable), onMouseEnter: handleActiveCellMouseEnter, ref: activeCellRef, className: cx(`${blockClass}--interactive-cell-element`, `${blockClass}__active-cell--highlight`, { [`${blockClass}__active-cell--with-selection`]: activeCellInsideSelectionArea }), type: "button" }, activeCellContent), /*#__PURE__*/React.createElement("textarea", { id: `${blockClass}__cell-editor-text-area`, value: cellEditorValue, onKeyDown: handleEditSubmit.handleEditSubmit({ activeCellCoordinates, cellEditorRulerRef, columns, previousState, removeCellEditor, rows, setActiveCellCoordinates, setCurrentMatcher, setSelectionAreas, spreadsheetRef, updateData }), onChange: event => { if (previousState.isEditing) { setCellEditorValue(event.target.value); if (cellEditorRulerRef?.current) { cellEditorRulerRef.current.textContent = event.target.value; } } }, ref: cellEditorRef, "aria-labelledby": activeCellCoordinates ? `${blockClass}__cell--${activeCellCoordinates?.row}--${activeCellCoordinates?.column}` : '', className: cx(`${blockClass}__cell-editor`, `${blockClass}--interactive-cell-element`, `${blockClass}__cell-editor--${cellSize}`, { [`${blockClass}__cell-editor--active`]: isEditing }) }), /*#__PURE__*/React.createElement("pre", { "aria-hidden": true, ref: cellEditorRulerRef, className: `${blockClass}__cell-editor-ruler` }))); }); // Return a placeholder if not released and not enabled by feature flag exports.DataSpreadsheet = settings.pkg.checkComponentEnabled(exports.DataSpreadsheet, componentName); // The display name of the component, used by React. Note that displayName // is used in preference to relying on function.name. exports.DataSpreadsheet.displayName = componentName; // The types and DocGen commentary for the component props, // in alphabetical order (for consistency). // See https://www.npmjs.com/package/prop-types#usage. exports.DataSpreadsheet.propTypes = { /** * Specifies the cell height */ cellSize: index.default.oneOf(['xs', 'sm', 'md', 'lg']), /** * Provide an optional class to be applied to the containing node. */ className: index.default.string, /** * The data that will build the column headers */ /**@ts-ignore */ columns: index.default.arrayOf(index.default.shape({ Header: index.default.string, accessor: index.default.oneOfType([index.default.string, index.default.func]), Cell: index.default.func // optional cell formatter })), /** * The spreadsheet data that will be rendered in the body of the spreadsheet component */ /**@ts-ignore */ data: index.default.arrayOf(index.default.shape), /** * Sets the number of empty rows to be created when there is no data provided */ defaultEmptyRowCount: index.default.number, /** * Disable column swapping, default false */ disableColumnSwapping: index.default.bool, /** * Check if spreadsheet is using custom row header component attached */ hasCustomRowHeader: index.default.bool, /** * The spreadsheet id */ id: index.default.oneOfType([index.default.number, index.default.string]), /** * The event handler that is called when the active cell changes */ onActiveCellChange: index.default.func, /** * Callback for when columns are dropped after dragged */ onColDrag: index.default.func, /** * The setter fn for the data prop */ onDataUpdate: index.default.func, /** * The event handler that is called when the selection area values change */ onSelectionAreaChange: index.default.func, /** * Read-only table */ readOnlyTable: index.default.bool, /** * Component next to numbering rows */ renderRowHeader: index.default.func, /** * Component next to numbering rows */ renderRowHeaderDirection: index.default.oneOf(['left', 'right']), /** * The aria label applied to the Select all button */ selectAllAriaLabel: index.default.string.isRequired, /** * The aria label applied to the Data spreadsheet component */ spreadsheetAriaLabel: index.default.string.isRequired, /** * The theme the DataSpreadsheet should use (only used to render active cell/selection area colors on dark theme) */ theme: index.default.oneOf(['light', 'dark']), /** * The total number of columns to be initially visible, additional columns will be rendered and * visible via horizontal scrollbar */ totalVisibleColumns: index.default.number /* TODO: add types and DocGen for all props. */ };