feat(grid): show column comments in condition suggestions

This commit is contained in:
zipg authored and GitHub committed 2026-07-17 00:05:50 +08:00
1 parent ecec25f0f4
commit dfd47bc54c
7 files changed
+174 -21

No files matched your search

@@ -1,6 +1,6 @@
import { describe, expect, test } from "vitest";
import { getDataGridConditionSuggestionPosition } from "@/lib/dataGrid/dataGridConditionSuggestionPosition";
import { getDataGridConditionSuggestionPosition, getDataGridConditionSuggestionPreferredWidth } from "@/lib/dataGrid/dataGridConditionSuggestionPosition";
describe("getDataGridConditionSuggestionPosition", () => {
test("anchors the dropdown to the input instead of the caret", () => {
@@ -21,4 +21,35 @@ describe("getDataGridConditionSuggestionPosition", () => {
expect(position.width).toBe(180);
});
test("widens commented field suggestions within the configured maximum", () => {
const preferredWidth = getDataGridConditionSuggestionPreferredWidth([
{ value: "customer_external_identifier", kind: "column", comment: "客户外部系统唯一标识" },
{ value: "customer_name", kind: "column", comment: "客户名称" },
]);
const position = getDataGridConditionSuggestionPosition({ left: 120, bottom: 40, width: 220 }, { viewportWidth: 1000, preferredWidth, maxWidth: 520 });
expect(preferredWidth).toBeGreaterThan(220);
expect(position.width).toBe(preferredWidth);
expect(position.width).toBeLessThanOrEqual(520);
});
test("keeps normal width when suggestions have no comments or are history entries", () => {
expect(
getDataGridConditionSuggestionPreferredWidth([
{ value: "customer_id", kind: "column", comment: "" },
{ value: "status = 'active'", kind: "history" },
]),
).toBeUndefined();
const position = getDataGridConditionSuggestionPosition({ left: 120, bottom: 40, width: 220 }, { viewportWidth: 1000, preferredWidth: undefined, maxWidth: 520 });
expect(position.width).toBe(220);
});
test("caps commented suggestions to both the maximum and the viewport", () => {
const position = getDataGridConditionSuggestionPosition({ left: 760, bottom: 40, width: 180 }, { viewportWidth: 420, preferredWidth: 800, maxWidth: 520 });
expect(position.width).toBe(404);
expect(position.left + position.width).toBeLessThanOrEqual(412);
});
});
@@ -7,6 +7,8 @@ export interface DataGridConditionSuggestionRect {
export interface DataGridConditionSuggestionPositionOptions {
viewportWidth: number;
minWidth?: number;
preferredWidth?: number;
maxWidth?: number;
viewportMargin?: number;
topOffset?: number;
}
@@ -17,12 +19,33 @@ export interface DataGridConditionSuggestionPosition {
width: number;
}
export interface DataGridConditionSuggestionContent {
value: string;
kind: "column" | "history";
comment?: string | null;
}
const COMMENTED_SUGGESTION_MIN_WIDTH = 360;
const COMMENTED_SUGGESTION_MAX_WIDTH = 520;
function textWidthUnits(value: string): number {
return [...value].reduce((total, character) => total + (character.charCodeAt(0) > 0xff ? 2 : 1), 0);
}
export function getDataGridConditionSuggestionPreferredWidth(suggestions: readonly DataGridConditionSuggestionContent[]): number | undefined {
const hasComment = suggestions.some((suggestion) => suggestion.kind === "column" && !!suggestion.comment?.trim());
if (!hasComment) return undefined;
const longestFieldUnits = suggestions.reduce((longest, suggestion) => (suggestion.kind === "column" ? Math.max(longest, textWidthUnits(suggestion.value)) : longest), 0);
return Math.min(COMMENTED_SUGGESTION_MAX_WIDTH, Math.max(COMMENTED_SUGGESTION_MIN_WIDTH, Math.ceil(longestFieldUnits * 7.5 + 208)));
}
export function getDataGridConditionSuggestionPosition(inputRect: DataGridConditionSuggestionRect, options: DataGridConditionSuggestionPositionOptions): DataGridConditionSuggestionPosition {
const minWidth = options.minWidth ?? 180;
const viewportMargin = options.viewportMargin ?? 8;
const topOffset = options.topOffset ?? 2;
const availableWidth = Math.max(minWidth, options.viewportWidth - viewportMargin * 2);
const width = Math.min(Math.max(inputRect.width, minWidth), availableWidth);
const availableWidth = Math.max(0, options.viewportWidth - viewportMargin * 2);
const widthLimit = Math.min(availableWidth, options.maxWidth ?? availableWidth);
const width = Math.min(Math.max(inputRect.width, minWidth, options.preferredWidth ?? 0), widthLimit);
const maxLeft = Math.max(viewportMargin, options.viewportWidth - viewportMargin - width);
const left = Math.min(Math.max(inputRect.left, viewportMargin), maxLeft);