fix(grid): preserve selection across column reordering
This commit is contained in:
1 parent
b406c0a52c
commit
d63a407931
4 files changed
+189
-18
No files matched your search
@@ -45,7 +45,7 @@ describe("useDataGridSelection", () => {
|
||||
|
||||
selection.selectColumn(0);
|
||||
selection.selectColumn(2, rowEvent({ meta: true }));
|
||||
selection.remapColumnSelection([0, 1, 2], [1, 2, 0]);
|
||||
selection.reconcileSelectionAfterColumnReorder([0, 1, 2], [1, 2, 0]);
|
||||
|
||||
expect(selection.selectedColumnIndexes.value).toEqual(new Set([1, 2]));
|
||||
|
||||
@@ -53,6 +53,92 @@ describe("useDataGridSelection", () => {
|
||||
expect(selection.selectedColumnIndexes.value).toEqual(new Set([0, 1, 2]));
|
||||
});
|
||||
|
||||
it("moves a single-column rectangular cell selection with its column after reordering", () => {
|
||||
const selection = createSelection();
|
||||
|
||||
selection.selectSingleCell(1, 0);
|
||||
selection.extendCellSelectionTo(2, 0);
|
||||
selection.reconcileSelectionAfterColumnReorder([0, 1, 2], [1, 2, 0]);
|
||||
|
||||
expect(selection.selectedRange.value).toEqual({ startRow: 1, endRow: 2, startCol: 2, endCol: 2 });
|
||||
expect(selection.cellIsSelected(1, 2)).toBe(true);
|
||||
expect(selection.cellIsSelected(2, 0)).toBe(false);
|
||||
});
|
||||
|
||||
it("moves a discrete single-column cell selection with its column after reordering", () => {
|
||||
const selection = createSelection();
|
||||
|
||||
selection.selectedCellKeys.value = new Set(["0:0", "2:0"]);
|
||||
selection.reconcileSelectionAfterColumnReorder([0, 1, 2], [1, 2, 0]);
|
||||
|
||||
expect(selection.selectedCellKeys.value).toEqual(new Set(["0:2", "2:2"]));
|
||||
expect(selection.cellIsSelected(0, 2)).toBe(true);
|
||||
expect(selection.cellIsSelected(2, 0)).toBe(false);
|
||||
});
|
||||
|
||||
it("keeps a multi-column rectangular cell selection when its columns remain contiguous", () => {
|
||||
const selection = createSelection();
|
||||
|
||||
selection.selectSingleCell(1, 0);
|
||||
selection.extendCellSelectionTo(2, 1);
|
||||
selection.reconcileSelectionAfterColumnReorder([0, 1, 2], [2, 0, 1]);
|
||||
|
||||
expect(selection.selectedRange.value).toEqual({ startRow: 1, endRow: 2, startCol: 1, endCol: 2 });
|
||||
expect(selection.cellIsSelected(1, 1)).toBe(true);
|
||||
expect(selection.cellIsSelected(2, 2)).toBe(true);
|
||||
expect(selection.cellIsSelected(1, 0)).toBe(false);
|
||||
});
|
||||
|
||||
it.each([
|
||||
{ edge: "left", nextColumnIndexes: [1, 0, 2] },
|
||||
{ edge: "right", nextColumnIndexes: [0, 2, 1] },
|
||||
])("keeps the whole rectangular selection when an inner column moves to its $edge edge", ({ nextColumnIndexes }) => {
|
||||
const selection = createSelection();
|
||||
|
||||
selection.selectSingleCell(1, 0);
|
||||
selection.extendCellSelectionTo(2, 2);
|
||||
selection.reconcileSelectionAfterColumnReorder([0, 1, 2], nextColumnIndexes);
|
||||
|
||||
expect(selection.selectedRange.value).toEqual({ startRow: 1, endRow: 2, startCol: 0, endCol: 2 });
|
||||
expect(selection.cellIsSelected(1, nextColumnIndexes.indexOf(1))).toBe(true);
|
||||
expect(selection.selectedCellCount.value).toBe(6);
|
||||
});
|
||||
|
||||
it("clears a multi-column rectangular cell selection when its columns split apart", () => {
|
||||
const selection = createSelection();
|
||||
|
||||
selection.selectSingleCell(1, 0);
|
||||
selection.extendCellSelectionTo(2, 1);
|
||||
selection.reconcileSelectionAfterColumnReorder([0, 1, 2], [1, 2, 0]);
|
||||
|
||||
expect(selection.selectedRange.value).toBeNull();
|
||||
expect(selection.selectedCellKeys.value).toEqual(new Set());
|
||||
expect(selection.hasCellSelection.value).toBe(false);
|
||||
});
|
||||
|
||||
it("keeps a discrete multi-column cell selection when its columns become contiguous", () => {
|
||||
const selection = createSelection();
|
||||
|
||||
selection.selectedCellKeys.value = new Set(["0:0", "0:2", "2:0", "2:2"]);
|
||||
selection.reconcileSelectionAfterColumnReorder([0, 1, 2], [1, 2, 0]);
|
||||
|
||||
expect(selection.selectedCellKeys.value).toEqual(new Set(["0:2", "0:1", "2:2", "2:1"]));
|
||||
expect(selection.cellIsSelected(0, 1)).toBe(true);
|
||||
expect(selection.cellIsSelected(2, 2)).toBe(true);
|
||||
expect(selection.cellIsSelected(0, 0)).toBe(false);
|
||||
});
|
||||
|
||||
it("clears a discrete multi-column cell selection when its columns split apart", () => {
|
||||
const selection = createSelection();
|
||||
|
||||
selection.selectedCellKeys.value = new Set(["0:0", "0:1", "2:0", "2:1"]);
|
||||
selection.reconcileSelectionAfterColumnReorder([0, 1, 2], [1, 2, 0]);
|
||||
|
||||
expect(selection.selectedRange.value).toBeNull();
|
||||
expect(selection.selectedCellKeys.value).toEqual(new Set());
|
||||
expect(selection.hasCellSelection.value).toBe(false);
|
||||
});
|
||||
|
||||
it("invalidates synthetic context state for ordinary, Ctrl, and Cmd cell selection", () => {
|
||||
const originalDocument = globalThis.document;
|
||||
const fakeDocument = {
|
||||
|
||||
@@ -247,13 +247,63 @@ export function useDataGridSelection(options: UseDataGridSelectionOptions) {
|
||||
lastClickedColumnIndex.value = colIndex;
|
||||
}
|
||||
|
||||
function remapColumnSelection(previousColumnIndexes: readonly number[], nextColumnIndexes: readonly number[]) {
|
||||
const selectedColumns = new Set([...selectedColumnIndexes.value].map((index) => previousColumnIndexes[index]).filter((index): index is number => index !== undefined));
|
||||
selectedColumnIndexes.value = new Set(nextColumnIndexes.flatMap((columnIndex, index) => (selectedColumns.has(columnIndex) ? [index] : [])));
|
||||
|
||||
function reconcileSelectionAfterColumnReorder(previousColumnIndexes: readonly number[], nextColumnIndexes: readonly number[]) {
|
||||
const lastClickedColumn = lastClickedColumnIndex.value === null ? undefined : previousColumnIndexes[lastClickedColumnIndex.value];
|
||||
const nextLastClickedColumnIndex = lastClickedColumn === undefined ? -1 : nextColumnIndexes.indexOf(lastClickedColumn);
|
||||
lastClickedColumnIndex.value = nextLastClickedColumnIndex >= 0 ? nextLastClickedColumnIndex : null;
|
||||
|
||||
if (hasColumnSelection.value) {
|
||||
const selectedColumns = new Set([...selectedColumnIndexes.value].map((index) => previousColumnIndexes[index]).filter((index): index is number => index !== undefined));
|
||||
selectedColumnIndexes.value = new Set(nextColumnIndexes.flatMap((columnIndex, index) => (selectedColumns.has(columnIndex) ? [index] : [])));
|
||||
return;
|
||||
}
|
||||
|
||||
const selectedCellColumnIndexes = cellSelectionColumnIndexes();
|
||||
const remappedCellColumnIndexes = selectedCellColumnIndexes.map((index) => {
|
||||
const selectedColumn = previousColumnIndexes[index];
|
||||
return selectedColumn === undefined ? -1 : nextColumnIndexes.indexOf(selectedColumn);
|
||||
});
|
||||
const sortedRemappedCellColumnIndexes = [...remappedCellColumnIndexes].sort((a, b) => a - b);
|
||||
const selectionRemainsContiguous = sortedRemappedCellColumnIndexes.length > 0 && sortedRemappedCellColumnIndexes.every((index, position) => index >= 0 && index === sortedRemappedCellColumnIndexes[0]! + position);
|
||||
if (selectionRemainsContiguous) {
|
||||
const remappedColumnIndexByPreviousIndex = new Map(selectedCellColumnIndexes.map((index, position) => [index, remappedCellColumnIndexes[position]!]));
|
||||
if (selectedCellKeys.value.size > 0) {
|
||||
selectedCellKeys.value = new Set(
|
||||
[...selectedCellKeys.value].map((key) => {
|
||||
const position = parseCellKey(key)!;
|
||||
return cellKey(position.rowIndex, remappedColumnIndexByPreviousIndex.get(position.colIndex)!);
|
||||
}),
|
||||
);
|
||||
if (selectionAnchor.value) selectionAnchor.value = { ...selectionAnchor.value, colIndex: remappedColumnIndexByPreviousIndex.get(selectionAnchor.value.colIndex)! };
|
||||
if (selectionFocus.value) selectionFocus.value = { ...selectionFocus.value, colIndex: remappedColumnIndexByPreviousIndex.get(selectionFocus.value.colIndex)! };
|
||||
} else if (selectionAnchor.value && selectionFocus.value) {
|
||||
const selectionStartsLeft = selectionAnchor.value.colIndex <= selectionFocus.value.colIndex;
|
||||
const startCol = sortedRemappedCellColumnIndexes[0]!;
|
||||
const endCol = sortedRemappedCellColumnIndexes[sortedRemappedCellColumnIndexes.length - 1]!;
|
||||
selectionAnchor.value = { ...selectionAnchor.value, colIndex: selectionStartsLeft ? startCol : endCol };
|
||||
selectionFocus.value = { ...selectionFocus.value, colIndex: selectionStartsLeft ? endCol : startCol };
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
clearCellSelection();
|
||||
lastClickedColumnIndex.value = null;
|
||||
}
|
||||
|
||||
function cellSelectionColumnIndexes(): number[] {
|
||||
if (selectedCellKeys.value.size > 0) {
|
||||
const selectedColumnIndexes = new Set<number>();
|
||||
for (const key of selectedCellKeys.value) {
|
||||
const position = parseCellKey(key);
|
||||
if (!position) return [];
|
||||
selectedColumnIndexes.add(position.colIndex);
|
||||
}
|
||||
return [...selectedColumnIndexes];
|
||||
}
|
||||
|
||||
const range = selectedRange.value;
|
||||
if (!range) return [];
|
||||
return Array.from({ length: range.endCol - range.startCol + 1 }, (_, index) => range.startCol + index);
|
||||
}
|
||||
|
||||
function selectAllCells() {
|
||||
@@ -588,7 +638,7 @@ export function useDataGridSelection(options: UseDataGridSelectionOptions) {
|
||||
selectRow,
|
||||
selectColumn,
|
||||
selectColumns,
|
||||
remapColumnSelection,
|
||||
reconcileSelectionAfterColumnReorder,
|
||||
selectAllCells,
|
||||
extendCellSelectionTo,
|
||||
finishCellSelection,
|
||||
|
||||
Reference in new issue
Block a user