UNPKG

@carbon/ibm-products

Version:

Carbon for IBM Products

261 lines (250 loc) 10.3 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 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;