fix(data-grid): preserve scroll when editing cells
This commit is contained in:
parent
7961004a2c
commit
2443a550e3
|
|
@ -5793,6 +5793,9 @@ function prepareDataCellMouseDown(item: RowItem, actualColIdx: number) {
|
|||
pendingQuickEntryDraftCellFocus.value = { rowId: item.id, col: actualColIdx };
|
||||
} else {
|
||||
pendingQuickEntryDraftCellFocus.value = null;
|
||||
if (editing && (editing.rowId !== item.id || editing.col !== actualColIdx)) {
|
||||
void commitEditFromCellBlur();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@ import { ref, computed, nextTick, watch, getCurrentInstance, onActivated, onBefo
|
|||
import * as api from "@/lib/backend/api";
|
||||
import type { CellValue } from "@/lib/dataGrid/cellValue";
|
||||
import { coerceDataGridCellValue, dataGridCellEditorText } from "@/lib/dataGrid/dataGridCellCoercion";
|
||||
import { focusDataGridEditorWithoutScrolling, preserveDataGridScrollPosition } from "@/lib/dataGrid/dataGridEditorFocus";
|
||||
import { normalizeDataGridSaveError } from "@/lib/dataGrid/dataGridSql";
|
||||
import { rowStatusFilterAfterAddingRow, type RowStatusFilter } from "@/lib/dataGrid/gridRowStatus";
|
||||
import { supportsDataGridTransaction } from "@/lib/table/tableEditing";
|
||||
|
|
@ -271,9 +272,10 @@ export function useDataGridEditor(options: UseDataGridEditorOptions) {
|
|||
function focusEditInput(select = true) {
|
||||
const focusInput = () => {
|
||||
if (typeof document === "undefined") return;
|
||||
const root = getScrollerElement()?.closest("[data-grid-root]");
|
||||
const scroller = getScrollerElement();
|
||||
const root = scroller?.closest("[data-grid-root]");
|
||||
const input = (root ?? document).querySelector(".cell-edit-input") as HTMLInputElement | HTMLTextAreaElement | null;
|
||||
input?.focus();
|
||||
if (input) focusDataGridEditorWithoutScrolling(input, scroller);
|
||||
if (select && input) {
|
||||
if (input instanceof HTMLTextAreaElement && input.dataset.expandedCellEditor === "true") {
|
||||
input.setSelectionRange?.(0, 0);
|
||||
|
|
@ -399,14 +401,7 @@ export function useDataGridEditor(options: UseDataGridEditorOptions) {
|
|||
}
|
||||
|
||||
function preserveScrollPosition() {
|
||||
const el = getScrollerElement();
|
||||
if (!el) return () => {};
|
||||
const top = el.scrollTop;
|
||||
const left = el.scrollLeft;
|
||||
return () => {
|
||||
el.scrollTop = top;
|
||||
el.scrollLeft = left;
|
||||
};
|
||||
return preserveDataGridScrollPosition(getScrollerElement());
|
||||
}
|
||||
|
||||
function readScrollPosition(): PendingChangesSnapshot["scroll"] | undefined {
|
||||
|
|
@ -457,6 +452,10 @@ export function useDataGridEditor(options: UseDataGridEditorOptions) {
|
|||
restoreScroll();
|
||||
nextTick(() => {
|
||||
restoreScroll();
|
||||
if (typeof requestAnimationFrame !== "function") {
|
||||
isCancelling = false;
|
||||
return;
|
||||
}
|
||||
let attempts = 0;
|
||||
const restoreNextFrame = () => {
|
||||
restoreScroll();
|
||||
|
|
@ -731,7 +730,10 @@ export function useDataGridEditor(options: UseDataGridEditorOptions) {
|
|||
suppressNextBlurCommit = false;
|
||||
return;
|
||||
}
|
||||
await commitEditAndMaybeAutoSave(options);
|
||||
const restoreScroll = preserveScrollPosition();
|
||||
const pendingCommit = commitEditAndMaybeAutoSave(options);
|
||||
restoreScrollAcrossFrames(restoreScroll);
|
||||
await pendingCommit;
|
||||
}
|
||||
|
||||
function applyCellValue(rowId: number, col: number, value: string | null, options: ApplyCellValueOptions = {}) {
|
||||
|
|
|
|||
|
|
@ -0,0 +1,34 @@
|
|||
import { describe, expect, it, vi } from "vitest";
|
||||
import { focusDataGridEditorWithoutScrolling, preserveDataGridScrollPosition } from "@/lib/dataGrid/dataGridEditorFocus";
|
||||
|
||||
describe("preserveDataGridScrollPosition", () => {
|
||||
it("restores the viewport after an editor is removed", () => {
|
||||
const scroller = { scrollLeft: 720, scrollTop: 180 };
|
||||
const restoreScroll = preserveDataGridScrollPosition(scroller);
|
||||
|
||||
scroller.scrollLeft = 0;
|
||||
scroller.scrollTop = 0;
|
||||
restoreScroll();
|
||||
|
||||
expect(scroller).toEqual({ scrollLeft: 720, scrollTop: 180 });
|
||||
});
|
||||
});
|
||||
|
||||
describe("focusDataGridEditorWithoutScrolling", () => {
|
||||
it("prevents focus from moving the data grid viewport", () => {
|
||||
const scroller = { scrollLeft: 720, scrollTop: 180 };
|
||||
const input = {
|
||||
focus: vi.fn((options?: FocusOptions) => {
|
||||
expect(options).toEqual({ preventScroll: true });
|
||||
// Some WebViews still move a virtualized ancestor while mounting the editor.
|
||||
scroller.scrollLeft = 0;
|
||||
scroller.scrollTop = 0;
|
||||
}),
|
||||
};
|
||||
|
||||
focusDataGridEditorWithoutScrolling(input, scroller);
|
||||
|
||||
expect(input.focus).toHaveBeenCalledOnce();
|
||||
expect(scroller).toEqual({ scrollLeft: 720, scrollTop: 180 });
|
||||
});
|
||||
});
|
||||
|
|
@ -0,0 +1,19 @@
|
|||
type DataGridEditorInput = Pick<HTMLElement, "focus">;
|
||||
type DataGridEditorScroller = Pick<HTMLElement, "scrollLeft" | "scrollTop">;
|
||||
|
||||
export function preserveDataGridScrollPosition(scroller?: DataGridEditorScroller | null) {
|
||||
if (!scroller) return () => {};
|
||||
const scrollLeft = scroller.scrollLeft;
|
||||
const scrollTop = scroller.scrollTop;
|
||||
return () => {
|
||||
if (scroller.scrollLeft !== scrollLeft) scroller.scrollLeft = scrollLeft;
|
||||
if (scroller.scrollTop !== scrollTop) scroller.scrollTop = scrollTop;
|
||||
};
|
||||
}
|
||||
|
||||
export function focusDataGridEditorWithoutScrolling(input: DataGridEditorInput, scroller?: DataGridEditorScroller | null) {
|
||||
const restoreScroll = preserveDataGridScrollPosition(scroller);
|
||||
|
||||
input.focus({ preventScroll: true });
|
||||
restoreScroll();
|
||||
}
|
||||
Loading…
Reference in New Issue