@carbon/ibm-products
Version:
Carbon for IBM Products
261 lines (250 loc) • 10.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.
*/
;
var settings = require('../../../settings.js');
var checkForHoldingKey = require('./checkForHoldingKey.js');
var deepCloneObject = require('../../../global/js/utils/deepCloneObject.js');
var uuidv4 = require('../../../global/js/utils/uuidv4.js');
var removeCellSelections = require('./removeCellSelections.js');
var handleHeaderCellSelection = require('./handleHeaderCellSelection.js');
var handleMultipleKeys = require('./handleMultipleKeys.js');
var handleActiveCellInSelectionEnter = require('./handleActiveCellInSelectionEnter.js');
var handleActiveCellInSelectionTab = require('./handleActiveCellInSelectionTab.js');
var handleCellDeletion = require('./handleCellDeletion.js');
const blockClass = `${settings.pkg.prefix}--data-spreadsheet`;
// onClick fn for each cell in the data spreadsheet body,
// adds the active cell highlight
const handleBodyCellClick = function (cell, columnIndex, event) {
for (var _len = arguments.length, rest = new Array(_len > 3 ? _len - 3 : 0), _key = 3; _key < _len; _key++) {
rest[_key - 3] = arguments[_key];
}
const [currentMatcher, activeCellCoordinates, selectionAreas, setActiveCellCoordinates, setSelectionAreas, setContainerHasFocus, setClickAndHoldActive, setCurrentMatcher, ref, setSelectionAreaData, setActiveCellInsideSelectionArea, activeCellRef, setValidStartingPoint] = rest;
event.preventDefault();
const closestBodyCell = event.target.closest(`.${blockClass}__body--td`);
const isValidSelectionAreaStart = closestBodyCell.classList.contains(`${blockClass}__body--td`);
setValidStartingPoint(isValidSelectionAreaStart);
const isHoldingCommandKey = checkForHoldingKey.checkForHoldingKey(event, 'cmd');
const isHoldingShiftKey = checkForHoldingKey.checkForHoldingKey(event, 'shiftKey');
setContainerHasFocus(true);
const activeCoordinates = {
row: cell.row.index,
column: columnIndex
};
const tempMatcher = uuidv4.default();
setClickAndHoldActive(true);
// prevent multiple selections unless cmd key is held
// meaning that selectionAreas should only have one item by default
if (isHoldingCommandKey) {
const activeCellElement = ref.current.querySelector(`.${blockClass}__active-cell--highlight`);
activeCellElement.setAttribute('data-selection-id', tempMatcher);
setActiveCellInsideSelectionArea(true);
setActiveCellCoordinates(activeCoordinates);
setCurrentMatcher(tempMatcher);
setSelectionAreas(prev => [...prev, {
point1: activeCoordinates,
matcher: tempMatcher
}]);
} else if (isHoldingShiftKey) {
setContainerHasFocus(true);
const selectionAreaClone = deepCloneObject.deepCloneObject(selectionAreas);
const indexOfItemToUpdate = selectionAreaClone.findIndex(item => item.matcher === currentMatcher);
if (indexOfItemToUpdate === -1) {
// There is always a selectionArea with a point1 object that updates
// whenever the activeCellCoordinates update, we should always be able
// to find an index, but if we do not for some reason we should return
// at this point.
return;
} else {
// Update the selectionArea that was found, do not update currentMatcher
selectionAreaClone[indexOfItemToUpdate].point1 = activeCellCoordinates;
selectionAreaClone[indexOfItemToUpdate].point2 = activeCoordinates;
selectionAreaClone[indexOfItemToUpdate].areaCreated = false;
selectionAreaClone[indexOfItemToUpdate].matcher = currentMatcher;
setSelectionAreas(selectionAreaClone);
}
} else {
activeCellRef.current.style.display = 'none';
setActiveCellInsideSelectionArea(false);
setActiveCellCoordinates(activeCoordinates);
// remove all previous cell selections
removeCellSelections.removeCellSelections({
spreadsheetRef: ref
});
setSelectionAreas([{
point1: activeCoordinates,
matcher: tempMatcher
}]);
setCurrentMatcher(tempMatcher);
setSelectionAreaData([]);
}
};
const handleBodyCellHover = function (cell, columnIndex) {
for (var _len2 = arguments.length, rest = new Array(_len2 > 2 ? _len2 - 2 : 0), _key2 = 2; _key2 < _len2; _key2++) {
rest[_key2 - 2] = arguments[_key2];
}
const [clickAndHoldActive, currentMatcher, setSelectionAreas] = rest;
if (clickAndHoldActive) {
const cellCoordinates = {
row: cell.row.index,
column: columnIndex
};
setSelectionAreas(prev => {
const selectionAreaClone = deepCloneObject.deepCloneObject(prev);
const indexOfItemToUpdate = selectionAreaClone.findIndex(item => item.matcher === currentMatcher);
// No items in the array match up with the currentMatcher value
if (indexOfItemToUpdate === -1) {
return prev;
}
// Do not update state if you're still hovering on the same cell
if (selectionAreaClone[indexOfItemToUpdate].point2?.row === cellCoordinates.row && selectionAreaClone[indexOfItemToUpdate].point2?.column === cellCoordinates.column) {
return prev;
}
selectionAreaClone[indexOfItemToUpdate].point2 = cellCoordinates;
selectionAreaClone[indexOfItemToUpdate].areaCreated = false;
return selectionAreaClone;
});
}
};
const handleRowHeaderClick = function (index, event) {
for (var _len3 = arguments.length, rest = new Array(_len3 > 2 ? _len3 - 2 : 0), _key3 = 2; _key3 < _len3; _key3++) {
rest[_key3 - 2] = arguments[_key3];
}
const [columns, ref, setSelectionAreas, setCurrentMatcher, setActiveCellCoordinates, activeCellCoordinates, rows, setSelectionAreaData] = rest;
const isHoldingCommandKey = checkForHoldingKey.checkForHoldingKey(event, 'cmd');
handleHeaderCellSelection.handleHeaderCellSelection({
type: 'row',
activeCellCoordinates,
rows,
columns,
setActiveCellCoordinates,
setCurrentMatcher,
setSelectionAreas,
spreadsheetRef: ref,
index,
setSelectionAreaData,
isHoldingCommandKey
});
};
const handleKeyPress = function (event) {
for (var _len4 = arguments.length, rest = new Array(_len4 > 1 ? _len4 - 1 : 0), _key4 = 1; _key4 < _len4; _key4++) {
rest[_key4 - 1] = arguments[_key4];
}
const [activeCellInsideSelectionArea, updateActiveCellCoordinates, activeCellCoordinates, removeActiveCell, columns, rows, spreadsheetRef, currentMatcher, removeCellEditor, selectionAreas, handleHomeEndKey, keysPressedList, usingMac, updateData, checkForReturnCondition, handleArrowKeyPress, setSelectionAreas, setSelectionAreaData, setCurrentMatcher, activeCellRef, setActiveCellCoordinates, setContainerHasFocus, setActiveCellContent] = rest;
const {
key
} = event;
// Command keys need to be returned as there is default browser behavior with these keys
// Needs to be returned in editing mode
if (checkForReturnCondition(key)) {
return;
}
// Clear out all cell selection areas if user uses any arrow key, except if the shift key is being held
if (['ArrowLeft', 'ArrowUp', 'ArrowRight', 'ArrowDown'].indexOf(key) > -1) {
if (selectionAreas?.length && keysPressedList.length < 2 && !handleMultipleKeys.includesShift(keysPressedList)) {
setSelectionAreas([]);
setSelectionAreaData([]);
removeCellSelections.removeCellSelections({
spreadsheetRef
});
}
}
if (keysPressedList?.length > 1) {
handleMultipleKeys.handleMultipleKeys({
activeCellCoordinates,
event,
keysPressedList,
selectionAreas,
currentMatcher,
rows,
setSelectionAreas,
columns,
updateActiveCellCoordinates,
spreadsheetRef,
removeCellSelections: removeCellSelections.removeCellSelections,
blockClass,
setCurrentMatcher,
usingMac
});
}
// Allow arrow key navigation if there are less than two activeKeys OR
// if one of the activeCellCoordinates is in a header position
// Prevent arrow keys, home key, and end key from scrolling the page when the data spreadsheet container has focus
if (keysPressedList.length < 2 && !handleMultipleKeys.includesShift(keysPressedList) || activeCellCoordinates.row === 'header' || activeCellCoordinates.column === 'header') {
switch (key) {
case 'Backspace':
case 'Delete':
{
const deleteParams = {
selectionAreas,
currentMatcher,
rows,
setActiveCellContent,
updateData,
activeCellCoordinates
};
handleCellDeletion.handleCellDeletion(deleteParams);
break;
}
// Enter
case 'Enter':
{
handleActiveCellInSelectionEnter.handleActiveCellInSelectionEnter({
activeCellInsideSelectionArea,
activeCellCoordinates,
activeCellRef,
selectionAreas,
updateActiveCellCoordinates
});
break;
}
// HOME
case 'Home':
case 'End':
{
event.preventDefault();
if (handleMultipleKeys.includesResourceKey(keysPressedList, usingMac)) {
return;
}
handleHomeEndKey({
type: key
});
break;
}
// Tab
case 'Tab':
{
if (activeCellInsideSelectionArea) {
event.preventDefault();
return handleActiveCellInSelectionTab.handleActiveCellInSelectionTab({
activeCellInsideSelectionArea,
activeCellCoordinates,
activeCellRef,
selectionAreas,
updateActiveCellCoordinates
});
}
setSelectionAreas([]);
removeActiveCell();
removeCellEditor();
setContainerHasFocus(false);
setActiveCellCoordinates(null);
break;
}
case 'ArrowLeft':
case 'ArrowUp':
case 'ArrowRight':
case 'ArrowDown':
{
handleArrowKeyPress(key);
}
}
}
};
exports.handleBodyCellClick = handleBodyCellClick;
exports.handleBodyCellHover = handleBodyCellHover;
exports.handleKeyPress = handleKeyPress;
exports.handleRowHeaderClick = handleRowHeaderClick;