fix(editor): preserve editor selection during result auto refresh
This commit is contained in:
1 parent
eab1f6344b
commit
4bef571fc4
2 files changed
+26
-1
No files matched your search
@@ -2,7 +2,7 @@ import { afterEach, describe, expect, it, vi } from "vitest";
|
||||
|
||||
import { beginDataGridNativeSelectionBlock, DATA_GRID_NATIVE_SELECTION_BLOCK_CLASS, DATA_GRID_NATIVE_SELECTION_RELEASE_DELAY_MS, finishDataGridNativeSelectionBlock } from "@/lib/dataGrid/dataGridNativeSelection";
|
||||
|
||||
function selectionEnvironment() {
|
||||
function selectionEnvironment(options: { editableActiveElement?: boolean } = {}) {
|
||||
const classes = new Set<string>();
|
||||
const removeAllRanges = vi.fn();
|
||||
return {
|
||||
@@ -16,6 +16,11 @@ function selectionEnvironment() {
|
||||
remove: (className: string) => classes.delete(className),
|
||||
},
|
||||
},
|
||||
activeElement: options.editableActiveElement
|
||||
? {
|
||||
closest: vi.fn(() => ({})),
|
||||
}
|
||||
: null,
|
||||
},
|
||||
getSelection: () => ({ removeAllRanges }),
|
||||
setTimeout,
|
||||
@@ -39,6 +44,16 @@ describe("data grid native selection block", () => {
|
||||
expect(removeAllRanges).toHaveBeenCalledOnce();
|
||||
});
|
||||
|
||||
it("preserves the native selection while an editable control is focused", () => {
|
||||
const { classes, removeAllRanges, environment } = selectionEnvironment({ editableActiveElement: true });
|
||||
const owner = {};
|
||||
|
||||
beginDataGridNativeSelectionBlock(owner, environment);
|
||||
|
||||
expect(classes.has(DATA_GRID_NATIVE_SELECTION_BLOCK_CLASS)).toBe(true);
|
||||
expect(removeAllRanges).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it("keeps the application-level block across a fast component replacement", () => {
|
||||
vi.useFakeTimers();
|
||||
const { classes, environment } = selectionEnvironment();
|
||||
|
||||
@@ -9,6 +9,9 @@ interface NativeSelectionBlockEnvironment {
|
||||
remove(className: string): void;
|
||||
};
|
||||
};
|
||||
activeElement?: {
|
||||
closest?(selector: string): unknown;
|
||||
} | null;
|
||||
};
|
||||
getSelection?: () => { removeAllRanges(): void } | null;
|
||||
setTimeout(callback: () => void, delay: number): ReturnType<typeof setTimeout>;
|
||||
@@ -40,10 +43,17 @@ function hasActiveBlockForEnvironment(environment: NativeSelectionBlockEnvironme
|
||||
return [...activeSelectionBlocks.values()].some((activeBlock) => activeBlock.environment.document?.documentElement === documentElement);
|
||||
}
|
||||
|
||||
function activeElementUsesNativeSelection(environment: NativeSelectionBlockEnvironment): boolean {
|
||||
return !!environment.document?.activeElement?.closest?.("input, textarea, [contenteditable='true'], [role='textbox']");
|
||||
}
|
||||
|
||||
export function beginDataGridNativeSelectionBlock(owner: DataGridNativeSelectionBlockOwner, environment: NativeSelectionBlockEnvironment = defaultEnvironment()) {
|
||||
clearReleaseTimer(owner);
|
||||
activeSelectionBlocks.set(owner, { environment });
|
||||
environment.document?.documentElement?.classList.add(DATA_GRID_NATIVE_SELECTION_BLOCK_CLASS);
|
||||
// Clearing the document selection while a text editor is focused can leave
|
||||
// CodeMirror's caret visible but detach the browser's active editing range.
|
||||
if (activeElementUsesNativeSelection(environment)) return;
|
||||
environment.getSelection?.()?.removeAllRanges();
|
||||
}
|
||||
|
||||
|
||||
Reference in new issue
Block a user