fix(grid): decouple where and order by undo histories
This commit is contained in:
2 files changed
+88
-2
No files matched your search
@@ -55,6 +55,9 @@ const suggestionPosition = ref({ left: 0, top: 0, width: 180 });
|
||||
const historyPreview = ref<{ value: string; left: number; top: number; maxWidth: number; arrowTop: number; side: "left" | "right" } | null>(null);
|
||||
const pointerMovedSuggestionIndex = ref(-1);
|
||||
const editorFocused = ref(false);
|
||||
const conditionUndoStack = ref<string[]>([]);
|
||||
const conditionRedoStack = ref<string[]>([]);
|
||||
let conditionLastValue = modelValue.value;
|
||||
let collapseTimer: ReturnType<typeof setTimeout> | undefined;
|
||||
let resizeObserver: ResizeObserver | undefined;
|
||||
let expandAfterComposition = false;
|
||||
@@ -370,6 +373,43 @@ function onInput(event: Event) {
|
||||
scheduleCaretIntoView();
|
||||
}
|
||||
|
||||
function isConditionUndoRedoShortcut(event: KeyboardEvent) {
|
||||
const key = event.key.toLowerCase();
|
||||
return ((event.metaKey || event.ctrlKey) && !event.altKey && key === "z") || (event.ctrlKey && !event.metaKey && !event.altKey && key === "y");
|
||||
}
|
||||
|
||||
function isConditionRedoShortcut(event: KeyboardEvent) {
|
||||
return ((event.metaKey || event.ctrlKey) && !event.altKey && event.shiftKey && event.key.toLowerCase() === "z") || (event.ctrlKey && !event.metaKey && !event.altKey && event.key.toLowerCase() === "y");
|
||||
}
|
||||
|
||||
function applyConditionHistoryValue(value: string) {
|
||||
conditionLastValue = value;
|
||||
modelValue.value = value;
|
||||
void nextTick(() => {
|
||||
const target = activeEditor.value;
|
||||
if (!target) return;
|
||||
target.focus({ preventScroll: true });
|
||||
target.setSelectionRange(value.length, value.length);
|
||||
syncSelection(target);
|
||||
});
|
||||
}
|
||||
|
||||
function handleConditionUndoRedo(event: KeyboardEvent) {
|
||||
if (!isConditionUndoRedoShortcut(event)) return false;
|
||||
|
||||
event.preventDefault();
|
||||
event.stopPropagation();
|
||||
const source = isConditionRedoShortcut(event) ? conditionRedoStack.value : conditionUndoStack.value;
|
||||
const targetValue = source.pop();
|
||||
if (targetValue === undefined) return true;
|
||||
|
||||
const currentValue = modelValue.value;
|
||||
const destination = isConditionRedoShortcut(event) ? conditionUndoStack.value : conditionRedoStack.value;
|
||||
destination.push(currentValue);
|
||||
applyConditionHistoryValue(targetValue);
|
||||
return true;
|
||||
}
|
||||
|
||||
async function applyCondition() {
|
||||
editor.dismiss();
|
||||
const applied = props.apply ? await props.apply(modelValue.value) : emit("apply", modelValue.value);
|
||||
@@ -385,6 +425,7 @@ async function clearCondition() {
|
||||
}
|
||||
|
||||
function onKeydown(event: KeyboardEvent) {
|
||||
if (handleConditionUndoRedo(event)) return;
|
||||
if (completeQuote(event)) return;
|
||||
const action = editor.handleKeydown(event);
|
||||
if (action === "apply") void applyCondition();
|
||||
@@ -471,7 +512,14 @@ function hideHistoryPreview() {
|
||||
historyPreview.value = null;
|
||||
}
|
||||
|
||||
watch(modelValue, () => resizeEditor());
|
||||
watch(modelValue, (value) => {
|
||||
if (value !== conditionLastValue) {
|
||||
conditionUndoStack.value.push(conditionLastValue);
|
||||
conditionRedoStack.value = [];
|
||||
conditionLastValue = value;
|
||||
}
|
||||
resizeEditor();
|
||||
});
|
||||
watch(suggestionPreferredWidth, () => {
|
||||
if (editor.dropdownOpen.value) updateSuggestionPosition();
|
||||
});
|
||||
|
||||
+39
-1
@@ -30,7 +30,7 @@ function mountEditor(kind: DataGridConditionHistoryKind, initialValue: string, o
|
||||
);
|
||||
app.mount(host);
|
||||
mountedApps.push({ app, host });
|
||||
return { value, input: host.querySelector("textarea") as HTMLTextAreaElement };
|
||||
return { value, input: host.querySelector("textarea") as HTMLTextAreaElement, host };
|
||||
}
|
||||
|
||||
function mockTextareaMetrics(input: HTMLTextAreaElement, options: { clientWidth: number; scrollWidth?: number; clientHeight?: number; scrollHeight?: number }) {
|
||||
@@ -121,6 +121,44 @@ describe("DataGridConditionEditor quote completion", () => {
|
||||
expect(value.value).toBe("name");
|
||||
});
|
||||
|
||||
it.each([
|
||||
["where", "z", { ctrlKey: true }],
|
||||
["orderBy", "z", { metaKey: true }],
|
||||
["where", "z", { ctrlKey: true, shiftKey: true }],
|
||||
["orderBy", "y", { ctrlKey: true }],
|
||||
] as const)("keeps %s undo/redo shortcuts in the condition editor", (kind, key, modifiers) => {
|
||||
const { input, host } = mountEditor(kind, "id = 123");
|
||||
let bubbled = 0;
|
||||
host.addEventListener("keydown", () => bubbled++);
|
||||
|
||||
const event = new KeyboardEvent("keydown", { key, bubbles: true, cancelable: true, ...modifiers });
|
||||
input.dispatchEvent(event);
|
||||
|
||||
expect(event.defaultPrevented).toBe(true);
|
||||
expect(bubbled).toBe(0);
|
||||
});
|
||||
|
||||
it("keeps WHERE and ORDER BY undo history independent", async () => {
|
||||
const where = mountEditor("where", "id = 123");
|
||||
const orderBy = mountEditor("orderBy", "id ASC");
|
||||
|
||||
where.input.value = "id = 456";
|
||||
where.input.dispatchEvent(new Event("input", { bubbles: true }));
|
||||
orderBy.input.value = "id DESC";
|
||||
orderBy.input.dispatchEvent(new Event("input", { bubbles: true }));
|
||||
await nextTick();
|
||||
|
||||
orderBy.input.dispatchEvent(new KeyboardEvent("keydown", { key: "z", metaKey: true, bubbles: true, cancelable: true }));
|
||||
await nextTick();
|
||||
expect(where.value.value).toBe("id = 456");
|
||||
expect(orderBy.value.value).toBe("id ASC");
|
||||
|
||||
orderBy.input.dispatchEvent(new KeyboardEvent("keydown", { key: "z", metaKey: true, shiftKey: true, bubbles: true, cancelable: true }));
|
||||
await nextTick();
|
||||
expect(where.value.value).toBe("id = 456");
|
||||
expect(orderBy.value.value).toBe("id DESC");
|
||||
});
|
||||
|
||||
it("passes the textarea caret range through when accepting a suggestion", async () => {
|
||||
const { value, input } = mountEditor("where", "status = cus AND enabled = 1", { columns: ["customer_id"] });
|
||||
input.focus();
|
||||
|
||||
Reference in new issue
Block a user