mirror of
https://github.com/t8y2/dbx.git
synced 2026-10-20 00:17:44 +00:00
fix(grid): select cell editor text only once on focus
This commit is contained in:
@@ -1,3 +1,4 @@
|
||||
// @vitest-environment happy-dom
|
||||
import { computed, nextTick, ref, type Ref } from "vue";
|
||||
import { beforeEach, describe, expect, it, vi } from "vitest";
|
||||
import { clearDataGridPendingSnapshot, DATA_GRID_QUICK_ENTRY_DRAFT_ROW_ID, useDataGridEditor } from "@/composables/useDataGridEditor";
|
||||
@@ -874,3 +875,31 @@ describe("useDataGridEditor saveChanges reload", () => {
|
||||
expect(emit).not.toHaveBeenCalledWith("reload", expect.anything());
|
||||
});
|
||||
});
|
||||
|
||||
describe("useDataGridEditor cell edit focus", () => {
|
||||
it("selects the editor value only on the first frame so fast typing is not clobbered (#7336)", async () => {
|
||||
const editor = createEditor(undefined, true, undefined, undefined, [["long json value", "hidden", "last"]]);
|
||||
const select = vi.fn();
|
||||
const setSelectionRange = vi.fn();
|
||||
const focus = vi.fn();
|
||||
const input = { focus, select, setSelectionRange, dataset: {}, value: "long json value" };
|
||||
const rafCallbacks: Array<(time: number) => void> = [];
|
||||
vi.stubGlobal("document", { querySelector: () => input });
|
||||
vi.stubGlobal("requestAnimationFrame", (callback: (time: number) => void) => {
|
||||
rafCallbacks.push(callback);
|
||||
return rafCallbacks.length;
|
||||
});
|
||||
|
||||
editor.startEdit(0, 0);
|
||||
await nextTick();
|
||||
for (let frame = 0; frame < 3; frame += 1) {
|
||||
const callbacks = rafCallbacks.splice(0);
|
||||
callbacks.forEach((callback) => callback(0));
|
||||
await nextTick();
|
||||
}
|
||||
|
||||
expect(select).toHaveBeenCalledTimes(1);
|
||||
expect(setSelectionRange).toHaveBeenCalledTimes(1);
|
||||
vi.unstubAllGlobals();
|
||||
});
|
||||
});
|
||||
|
||||
@@ -361,13 +361,20 @@ export function useDataGridEditor(options: UseDataGridEditorOptions) {
|
||||
}
|
||||
|
||||
function focusEditInput(select = true) {
|
||||
const focusInput = () => {
|
||||
// `selectText` is tri-state: true selects the full value once when the
|
||||
// editor opens, false places the caret at the end, and undefined only
|
||||
// refocuses without touching the selection. The requestAnimationFrame
|
||||
// retry below uses the undefined mode so a user who starts typing or
|
||||
// moving the caret immediately after the editor opens is not clobbered by
|
||||
// a second select-all on a later frame.
|
||||
const focusInput = (selectText?: boolean) => {
|
||||
if (typeof document === "undefined") return;
|
||||
const scroller = getScrollerElement();
|
||||
const root = scroller?.closest("[data-grid-root]");
|
||||
const input = (root ?? document).querySelector(".cell-edit-input") as HTMLInputElement | HTMLTextAreaElement | null;
|
||||
if (input) focusDataGridEditorWithoutScrolling(input, scroller);
|
||||
if (select && input) {
|
||||
if (selectText === undefined) return;
|
||||
if (selectText && input) {
|
||||
if (input instanceof HTMLTextAreaElement && input.dataset.expandedCellEditor === "true") {
|
||||
// Expanded editors must match single-line editors: a double-click selects the whole value.
|
||||
input.select();
|
||||
@@ -382,11 +389,11 @@ export function useDataGridEditor(options: UseDataGridEditorOptions) {
|
||||
}
|
||||
};
|
||||
nextTick(() => {
|
||||
focusInput();
|
||||
focusInput(select);
|
||||
if (typeof requestAnimationFrame === "undefined") return;
|
||||
let attempts = 0;
|
||||
const focusNextFrame = () => {
|
||||
focusInput();
|
||||
focusInput(undefined);
|
||||
attempts += 1;
|
||||
if (attempts < 3) requestAnimationFrame(focusNextFrame);
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user