Laurent Nguyen 825a5d5257
Enable column selection and sorting in DocumentsTab (with persistence) with improvements (#1963)
* Reapply "Enable column selection and sorting in DocumentsTab (with persistence) (#1881)" (#1960)

This reverts commit fe9730206e6e7a63f23ec2e2fb51bedd53fe66b5.

* Fix logic bug: always include defaultQueryFields in query.

* Show resize column option outside of feature flag

* Improve prevention of no selected columns

* Add more unit tests

* Fix styling on table

* Update test snapshots

* Remove "sortable" property on table which makes the header cell focusable (user sorts by selecting menu item, not by clicking on cell)
2024-09-11 13:26:49 +02:00

104 lines
3.2 KiB
TypeScript

import { useEffect, useRef } from "react";
/**
* Utility class to help with selection.
* This emulates File Explorer selection behavior.
* ctrl: toggle selection of index.
* shift: select all rows between selectionStartIndex and index
* shift + ctrl: select or deselect all rows between selectionStartIndex and index depending on whether selectionStartIndex is selected
* No modifier only selects the clicked row
* ctrl: updates selection start index
* shift: do not update selection start index
*
* @param currentSelection current selection
* @param clickedIndex index of clicked row
* @param isShiftKey shift key is pressed
* @param isCtrlKey ctrl key is pressed
* @param selectionStartIndex index of current selected row
* @returns new selection and selection start
*/
export const selectionHelper = (
currentSelection: Set<number>,
clickedIndex: number,
isShiftKey: boolean,
isCtrlKey: boolean,
selectionStartIndex: number,
): {
selection: Set<number>;
selectionStartIndex: number;
} => {
if (isShiftKey) {
// Shift is about selecting range of rows
if (isCtrlKey) {
// shift + ctrl
const isSelectionStartIndexSelected = currentSelection.has(selectionStartIndex);
const min = Math.min(clickedIndex, selectionStartIndex);
const max = Math.max(clickedIndex, selectionStartIndex);
const newSelection = new Set<number>(currentSelection);
for (let i = min; i <= max; i++) {
// Select or deselect range depending on how selectionStartIndex is selected
if (isSelectionStartIndexSelected) {
// Select range
newSelection.add(i);
} else {
// Deselect range
newSelection.delete(i);
}
}
return {
selection: newSelection,
selectionStartIndex: undefined,
};
} else {
// shift only
// Shift only: enable everything between lastClickedIndex and clickedIndex and disable everything else
const min = Math.min(clickedIndex, selectionStartIndex);
const max = Math.max(clickedIndex, selectionStartIndex);
const newSelection = new Set<number>();
for (let i = min; i <= max; i++) {
newSelection.add(i);
}
return {
selection: newSelection,
selectionStartIndex: undefined, // do not change selection start
};
}
} else {
if (isCtrlKey) {
// Ctrl only: toggle selection where we clicked
const isNotSelected = !currentSelection.has(clickedIndex);
if (isNotSelected) {
return {
selection: new Set(currentSelection.add(clickedIndex)),
selectionStartIndex: clickedIndex,
};
} else {
// Remove
currentSelection.delete(clickedIndex);
return {
selection: new Set(currentSelection),
selectionStartIndex: clickedIndex,
};
}
} else {
// If no modifier keys are pressed, select only the clicked row
return {
selection: new Set<number>([clickedIndex]),
selectionStartIndex: clickedIndex,
};
}
}
};
// To get previous values of a state in useEffect
export const usePrevious = <T>(value: T): T | undefined => {
const ref = useRef<T>();
useEffect(() => {
ref.current = value;
});
return ref.current;
};