@carbon/ibm-products
Version:
Carbon for IBM Products
830 lines (812 loc) • 32 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 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. */
};