mirror of
https://github.com/t8y2/dbx.git
synced 2026-10-20 00:17:44 +00:00
fix(grid): restore first WHERE suggestion default selection
This commit is contained in:
@@ -313,7 +313,7 @@ describe("DataGridConditionEditor quote completion", () => {
|
||||
expect(value.value).toBe("name");
|
||||
});
|
||||
|
||||
it("keeps the first WHERE field suggestion unselected and only applies on Enter", async () => {
|
||||
it("selects the first WHERE field suggestion with Enter", async () => {
|
||||
const { value, input } = mountEditor("where", "", { columns: ["customer_id", "customer_name"] });
|
||||
input.focus();
|
||||
input.value = "cus";
|
||||
@@ -321,12 +321,11 @@ describe("DataGridConditionEditor quote completion", () => {
|
||||
input.dispatchEvent(new Event("input", { bubbles: true }));
|
||||
|
||||
await vi.waitFor(() => expect(document.querySelectorAll('[role="option"]')).toHaveLength(2));
|
||||
// 不再默认高亮:回车只用于应用筛选,不会把列名写进输入框(issue #10595)
|
||||
expect(document.querySelector('[role="option"][aria-selected="true"]')).toBeNull();
|
||||
expect(document.querySelector('[role="option"][aria-selected="true"]')?.textContent).toContain("customer_id");
|
||||
|
||||
input.dispatchEvent(new KeyboardEvent("keydown", { key: "Enter", bubbles: true, cancelable: true }));
|
||||
await nextTick();
|
||||
expect(value.value).toBe("cus");
|
||||
expect(value.value).toBe("customer_id");
|
||||
expect(document.querySelector('[role="listbox"]')).toBeNull();
|
||||
});
|
||||
|
||||
|
||||
@@ -34,9 +34,6 @@ describe("useDataGridConditionEditor", () => {
|
||||
value.value = "status = 1 AND cust";
|
||||
await nextTick();
|
||||
await vi.waitFor(() => expect(editor.suggestions.value.map((item) => item.value)).toEqual(["customer_id", "customer_name"]));
|
||||
// 不再默认高亮第一条建议:必须显式导航/接受,避免回车应用筛选时改写输入(issue #10595)
|
||||
expect(editor.highlightedIndex.value).toBe(-1);
|
||||
expect(editor.navigate(1)).toBe(true);
|
||||
expect(editor.highlightedIndex.value).toBe(0);
|
||||
expect(editor.navigate(1)).toBe(true);
|
||||
expect(editor.highlightedIndex.value).toBe(1);
|
||||
@@ -46,6 +43,65 @@ describe("useDataGridConditionEditor", () => {
|
||||
expect(value.value).toBe("status = 1 AND customer_name");
|
||||
});
|
||||
|
||||
it.each(["cus", "status = 1 AND cus", "NOT cus", "status = 1 AND NOT cus", "(cus", "status = 1 OR (cus", "id BETWEEN 0 AND 1 AND cus", "id NOT BETWEEN 0 AND 1 OR cus", "name = 'BETWEEN' AND cus", "`BETWEEN` = 1 AND cus", "id BETWEEN abs(0) AND 1 AND cus"])(
|
||||
"accepts the first WHERE field suggestion directly with Enter: %s",
|
||||
async (condition) => {
|
||||
const value = ref("");
|
||||
const editor = useDataGridConditionEditor({ kind: "where", value, columns: ["customer_id", "customer_name"], historyScope: {} });
|
||||
value.value = condition;
|
||||
await nextTick();
|
||||
await vi.waitFor(() => expect(editor.suggestions.value).toHaveLength(2));
|
||||
|
||||
expect(editor.highlightedIndex.value).toBe(0);
|
||||
expect(editor.handleKeydown(keyboardEvent("Enter"))).toBe("accept");
|
||||
expect(value.value).toBe(condition.replace(/cus$/, "customer_id"));
|
||||
},
|
||||
);
|
||||
|
||||
it.each(["status = 1 ", "status is", "status IS nu", "status NOT betw"])("applies WHERE conditions without automatically accepting keyword suggestions: %s", async (condition) => {
|
||||
const value = ref("");
|
||||
const editor = useDataGridConditionEditor({ kind: "where", value, columns: ["status"], historyScope: {} });
|
||||
value.value = condition;
|
||||
await nextTick();
|
||||
await vi.waitFor(() => expect(editor.suggestions.value.length).toBeGreaterThan(0));
|
||||
|
||||
expect(editor.suggestions.value[0].kind).toBe("keyword");
|
||||
expect(editor.highlightedIndex.value).toBe(-1);
|
||||
expect(editor.handleKeydown(keyboardEvent("Enter"))).toBe("apply");
|
||||
expect(value.value).toBe(condition);
|
||||
});
|
||||
|
||||
it.each([
|
||||
"id = 1 + 1",
|
||||
"id = - 1",
|
||||
"id = + 1",
|
||||
"id = 2 * 1",
|
||||
"id = 2 / 1",
|
||||
"id = (1",
|
||||
"id = abs(1",
|
||||
"id = coalesce(0, 1",
|
||||
"id BETWEEN 0 AND 1",
|
||||
"id NOT BETWEEN 0 AND 1",
|
||||
"id between 0 and 1",
|
||||
"id BETWEEN abs(0) AND 1",
|
||||
"id BETWEEN 'AND' AND 1",
|
||||
"(id BETWEEN 0 AND 1",
|
||||
"id = 2 AND other_id BETWEEN 0 AND 1",
|
||||
"id BETWEEN CASE WHEN enabled = 1 AND flag = 1 THEN 0 ELSE 0 END AND 1",
|
||||
"id BETWEEN CASE WHEN enabled = 1 OR flag = 1 THEN 0 ELSE 0 END AND 1",
|
||||
"id BETWEEN CASE WHEN a = 1 THEN CASE WHEN b = 1 AND c = 1 THEN 0 ELSE 0 END ELSE 0 END AND 1",
|
||||
])("does not rewrite value expressions on Enter: %s", async (condition) => {
|
||||
const value = ref("");
|
||||
const editor = useDataGridConditionEditor({ kind: "where", value, columns: ["id", "version1"], historyScope: {} });
|
||||
value.value = condition;
|
||||
await nextTick();
|
||||
await vi.waitFor(() => expect(editor.suggestions.value).toEqual([{ value: "version1", kind: "column" }]));
|
||||
|
||||
expect(editor.highlightedIndex.value).toBe(-1);
|
||||
expect(editor.handleKeydown(keyboardEvent("Enter"))).toBe("apply");
|
||||
expect(value.value).toBe(condition);
|
||||
});
|
||||
|
||||
it("does not suggest column names while typing a value after a comparison operator", async () => {
|
||||
const value = ref("");
|
||||
const editor = useDataGridConditionEditor({
|
||||
|
||||
@@ -310,6 +310,44 @@ function whereSuggestionRole(target: DataGridConditionCompletionTarget, identifi
|
||||
return "connector";
|
||||
}
|
||||
|
||||
function isBetweenRangeSeparator(prefix: string, separatorOffset: number): boolean {
|
||||
// Ignore quoted values/identifiers and comments; BETWEEN state belongs to its parenthesis scope.
|
||||
const tokens = prefix.slice(0, separatorOffset).match(/--[^\r\n]*|\/\*[\s\S]*?(?:\*\/|$)|'(?:''|\\.|[^'\\])*'|"(?:""|\\.|[^"\\])*"|`(?:``|[^`])*`|\[(?:\]\]|[^\]])*\]|[A-Za-z_][\w$]*|[()]/g) ?? [];
|
||||
const pendingBetween = [false];
|
||||
const caseDepth = [0];
|
||||
for (const token of tokens) {
|
||||
if (token === "(") {
|
||||
pendingBetween.push(false);
|
||||
caseDepth.push(0);
|
||||
} else if (token === ")") {
|
||||
if (pendingBetween.length > 1) {
|
||||
pendingBetween.pop();
|
||||
caseDepth.pop();
|
||||
}
|
||||
} else if (token.toUpperCase() === "CASE") {
|
||||
caseDepth[caseDepth.length - 1] += 1;
|
||||
} else if (token.toUpperCase() === "END") {
|
||||
caseDepth[caseDepth.length - 1] = Math.max(0, caseDepth[caseDepth.length - 1] - 1);
|
||||
} else if (token.toUpperCase() === "BETWEEN") {
|
||||
pendingBetween[pendingBetween.length - 1] = true;
|
||||
} else if (caseDepth[caseDepth.length - 1] === 0 && (token.toUpperCase() === "AND" || token.toUpperCase() === "OR")) {
|
||||
pendingBetween[pendingBetween.length - 1] = false;
|
||||
}
|
||||
}
|
||||
return pendingBetween[pendingBetween.length - 1];
|
||||
}
|
||||
|
||||
function shouldPreselectWhereColumn(target: DataGridConditionCompletionTarget): boolean {
|
||||
if (!target.token) return false;
|
||||
const prefix = target.value.slice(0, target.from).trimEnd();
|
||||
// Only preselect at the start of a predicate, not inside arithmetic or function arguments.
|
||||
if (/^(?:NOT\b\s*|\(\s*)*$/i.test(prefix)) return true;
|
||||
const connector = /(?:^|\s)(AND|OR)\b\s*(?:NOT\b\s*|\(\s*)*$/i.exec(prefix);
|
||||
if (!connector) return false;
|
||||
const separatorOffset = connector.index + connector[0].indexOf(connector[1]);
|
||||
return connector[1].toUpperCase() !== "AND" || !isBetweenRangeSeparator(prefix, separatorOffset);
|
||||
}
|
||||
|
||||
/**
|
||||
* ORDER BY 的角色判定:开头或逗号之后是排序列位置(提示列名),
|
||||
* 已经写出排序列之后是排序方向位置(提示 ASC/DESC)。
|
||||
@@ -440,9 +478,9 @@ export function useDataGridConditionEditor(options: UseDataGridConditionEditorOp
|
||||
const providerValues = values ? [...new Set(values)] : undefined;
|
||||
suggestions.value = providerValues ? (providerValues.some((value) => value.toLowerCase() === target.token.toLowerCase()) ? [] : providerValues.slice(0, limit).map((suggestion) => ({ value: suggestion, kind: "column" }))) : defaultSuggestions(target).slice(0, limit);
|
||||
replacementRange.value = { from: target.from, to: target.to };
|
||||
// 不再默认高亮第一条建议:否则用户按回车«应用筛选»时会先把高亮项写进输入框(issue #10595)。
|
||||
// 接受补全需要显式操作(↓/↑ 后回车、Tab 或点击)。
|
||||
highlightedIndex.value = -1;
|
||||
// WHERE 条件开头默认选中首个列名,支持直接回车补全(issue #7155)。
|
||||
// 算术表达式、函数参数和关键字建议不默认选中,避免回车应用时改写条件(issue #10595)。
|
||||
highlightedIndex.value = options.kind === "where" && (role === "field" || role === "after_not") && shouldPreselectWhereColumn(target) && suggestions.value[0]?.kind === "column" ? 0 : -1;
|
||||
} catch (error) {
|
||||
if (!controller.signal.aborted && requestId === suggestionRequestId) {
|
||||
suggestions.value = [];
|
||||
|
||||
Reference in New Issue
Block a user