@carbon/ibm-products
Version:
Carbon for IBM Products
164 lines (159 loc) • 9 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 React = require('react');
var layout = require('@carbon/layout');
var settings = require('../../../settings.js');
var deepCloneObject = require('../../../global/js/utils/deepCloneObject.js');
const useSpreadsheetMouseUp = _ref => {
let {
currentMatcher,
setSelectionAreas,
selectedHeaderReorderActive,
setSelectedHeaderReorderActive,
setClickAndHoldActive,
setValidStartingPoint,
validStartingPoint,
blockClass = `${settings.pkg.prefix}--data-spreadsheet`,
ref,
setHeaderCellHoldActive,
setColumnOrder,
visibleColumns,
setActiveCellCoordinates,
activeCellCoordinates,
rows,
defaultColumn,
selectionAreas
} = _ref;
React.useEffect(() => {
const handleMouseUp = event => {
let isHoldingColumn = false;
if (selectionAreas?.[0]?.header && selectionAreas[0].header.type === 'column') {
isHoldingColumn = true;
}
// Remove the cloned selection area on mouse up
if (!validStartingPoint && isHoldingColumn) {
setHeaderCellHoldActive(false);
const selectionAreaElement = ref.current.querySelector(`.${blockClass}__selection-area--element`);
const selectionAreaCloneElement = ref.current.querySelector(`.${blockClass}__selection-area--element-cloned`);
if (!selectionAreaCloneElement) {
return;
}
// Mouse up while a cloned selection area exists/a column is being reordered
if (selectionAreaCloneElement) {
const closestCell = event.target.closest(`.${blockClass}--interactive-cell-element`);
const newColumnIndex = parseInt?.(closestCell?.getAttribute('data-column-index'));
const originalColumnIndex = parseInt?.(selectionAreaCloneElement?.getAttribute('data-column-index-original'));
const selectionAreaToClone = selectionAreas.filter(item => item?.matcher === currentMatcher);
const selectionAreaIndexArray = selectionAreaToClone[0].header.selectedIndexList;
const columnToMoveToElement = ref.current.querySelector(`[data-row-index="header"][data-column-index="${newColumnIndex}"]`);
// Mouse up element was not part of the spreadsheet component
if (!columnToMoveToElement) {
return;
}
const selectionAreaToMove = ref.current.querySelector(`[data-matcher-id="${currentMatcher}"]`);
const spreadsheetPosition = ref.current.getBoundingClientRect();
const listContainer = ref.current.querySelector(`.${blockClass}__list--container`);
const leftScrollAmount = listContainer.scrollLeft;
const newIndexLessThanStarting = newColumnIndex < originalColumnIndex;
const newIndexGreater = newColumnIndex > originalColumnIndex;
const differenceBetweenOldNewIndex = newIndexGreater ? newColumnIndex - originalColumnIndex : originalColumnIndex - newColumnIndex;
setSelectionAreas(prev => {
const selectionAreaClone = deepCloneObject.deepCloneObject(prev);
if (originalColumnIndex === newColumnIndex) {
return prev;
}
const indexOfItemToUpdate = selectionAreaClone.findIndex(item => item.matcher === currentMatcher);
if (indexOfItemToUpdate === -1) {
return prev;
}
if (!selectionAreaIndexArray.includes(newColumnIndex)) {
// We need to not add just the newColumnIndex, but an array of indexes
// if there are multiple columns
const newIndexArray = newIndexGreater ? selectionAreaIndexArray.map(num => num + differenceBetweenOldNewIndex) : selectionAreaIndexArray.map(num => num - differenceBetweenOldNewIndex);
selectionAreaClone[indexOfItemToUpdate].header.selectedIndexList = newIndexArray;
selectionAreaClone[indexOfItemToUpdate].point1.column = Math.min(...newIndexArray);
selectionAreaClone[indexOfItemToUpdate].point2.column = Math.max(...newIndexArray);
}
selectionAreaClone[indexOfItemToUpdate].areaCreated = false;
return selectionAreaClone;
});
// Only reorder columns if the new index is _not_ part of the
// selectionAreaIndexArray, meaning the new placement is outside
// of the current selection area. Similarly, the active cell position
// should only be changed under the same condition.
if (!selectionAreaIndexArray.includes(newColumnIndex)) {
const deleteCount = selectionAreaIndexArray.length;
const startIndex = Math.min(...selectionAreaIndexArray);
const columnIdArray = visibleColumns.map(column => column.id);
const columnIdArrayClone = [...columnIdArray];
const getNewColumnOrder = () => {
const newColumnList = [];
selectionAreaIndexArray.map(index => {
return newColumnList.push(columnIdArray[index]);
});
return newColumnList;
};
// Remove one element at the original index
columnIdArrayClone.splice(startIndex, deleteCount);
const originalPointIndex = selectionAreaIndexArray.findIndex(item => item === originalColumnIndex);
const updatedNewIndexWithNewOrder = newColumnIndex - originalPointIndex;
// Add one element at the new index
columnIdArrayClone.splice(updatedNewIndexWithNewOrder, 0, ...getNewColumnOrder());
setColumnOrder(columnIdArrayClone); // Function provided by useTable (react-table) hook to reorder columns
const newCellCoords = {
...activeCellCoordinates,
column: newIndexGreater ? activeCellCoordinates.column + differenceBetweenOldNewIndex : activeCellCoordinates.column - differenceBetweenOldNewIndex
};
setActiveCellCoordinates(newCellCoords);
const firstSelectedHeader = Array.from(ref.current.querySelectorAll(`.${blockClass}__th--selected-header`))[0];
const firstSelectedHeaderCoords = firstSelectedHeader.getBoundingClientRect();
const newRelativePosition = firstSelectedHeaderCoords.left - spreadsheetPosition.left + leftScrollAmount;
// console.log(firstSelectedHeaderCoords.left - spreadsheetPosition.left + leftScrollAmount);
const updatedSelectionAreaPlacement = newIndexLessThanStarting ? newRelativePosition : newColumnIndex === originalColumnIndex ? selectionAreaToMove.style.left : newRelativePosition;
selectionAreaToMove.style.left = layout.px(updatedSelectionAreaPlacement);
}
// Remove the cloned column and indicator elements
const indicatorLineElement = ref.current.querySelector(`.${blockClass}__reorder-indicator-line`);
indicatorLineElement?.remove();
selectionAreaCloneElement?.remove();
selectionAreaElement?.classList?.remove(`${blockClass}__selection-area--element`);
setSelectedHeaderReorderActive(false);
}
}
// Mouse up was on a spreadsheet body cell which is a valid
// start/end point for creating a selection area
if (validStartingPoint || event.type === 'mouseup') {
setClickAndHoldActive(false);
setValidStartingPoint(false);
const cellButton = event.target.closest(`.${blockClass}__body--td`);
if (cellButton) {
const endCellCoordinates = {
row: Number(cellButton.getAttribute('data-row-index')),
column: Number(cellButton.getAttribute('data-column-index'))
};
setSelectionAreas(prev => {
const selectionAreaClone = deepCloneObject.deepCloneObject(prev);
const indexOfItemToUpdate = selectionAreaClone.findIndex(item => item.matcher === currentMatcher);
// No items in the array have an object that matches the value of currentMatcher
if (indexOfItemToUpdate === -1) {
return prev;
}
selectionAreaClone[indexOfItemToUpdate].point2 = endCellCoordinates;
selectionAreaClone[indexOfItemToUpdate].areaCreated = false;
return selectionAreaClone;
});
}
}
};
document.addEventListener('mouseup', handleMouseUp);
return () => {
document.removeEventListener('mouseup', handleMouseUp);
};
}, [blockClass, currentMatcher, setSelectionAreas, setClickAndHoldActive, setValidStartingPoint, validStartingPoint, selectedHeaderReorderActive, setSelectedHeaderReorderActive, ref, setHeaderCellHoldActive, setColumnOrder, visibleColumns, setActiveCellCoordinates, activeCellCoordinates, rows, defaultColumn, selectionAreas]);
};
exports.useSpreadsheetMouseUp = useSpreadsheetMouseUp;