fix(grid): reset filter editor caret offset on expand

This commit is contained in:
zipg authored and GitHub committed 2026-09-09 22:35:58 +08:00
1 parent b84d162049
commit f3e55a8337
2 files changed
+44 -6

No files matched your search

@@ -81,10 +81,11 @@ const hasValue = computed(() => modelValue.value.trim().length > 0);
// textarea, so keywords / fields / values are colored without losing caret
// and selection behavior.
const highlightTokens = computed(() => tokenizeDataGridCondition(modelValue.value));
const highlightScrollLeft = ref(0);
const highlightScrollTop = ref(0);
const collapsedHighlightStyle = computed<CSSProperties>(() => ({ transform: `translateX(${-highlightScrollLeft.value}px)` }));
const expandedHighlightStyle = computed<CSSProperties>(() => ({ transform: `translate(${-highlightScrollLeft.value}px, ${-highlightScrollTop.value}px)` }));
const collapsedHighlightScrollLeft = ref(0);
const expandedHighlightScrollLeft = ref(0);
const expandedHighlightScrollTop = ref(0);
const collapsedHighlightStyle = computed<CSSProperties>(() => ({ transform: `translateX(${-collapsedHighlightScrollLeft.value}px)` }));
const expandedHighlightStyle = computed<CSSProperties>(() => ({ transform: `translate(${-expandedHighlightScrollLeft.value}px, ${-expandedHighlightScrollTop.value}px)` }));
function highlightTokenClass(type: DataGridConditionTokenType): string | undefined {
if (type === "plain") return undefined;
@@ -93,8 +94,12 @@ function highlightTokenClass(type: DataGridConditionTokenType): string | undefin
function onEditorScroll(event: Event) {
const target = event.currentTarget as HTMLTextAreaElement;
highlightScrollLeft.value = target.scrollLeft;
highlightScrollTop.value = target.scrollTop;
if (target === overlayRef.value) {
expandedHighlightScrollLeft.value = target.scrollLeft;
expandedHighlightScrollTop.value = target.scrollTop;
} else {
collapsedHighlightScrollLeft.value = target.scrollLeft;
}
}
const emptyHistoryText = computed(() => (modelValue.value.trim() ? props.historyNoMatchesText : props.historyEmptyText));
const activeSuggestionId = computed(() => (editor.highlightedIndex.value >= 0 ? `${suggestionListId}-${editor.highlightedIndex.value}` : undefined));
@@ -221,10 +226,15 @@ function resizeEditor(forceExpand = false) {
expandAfterComposition = true;
return;
}
const wasExpanded = expanded.value;
const overlayFocused = document.activeElement === overlayRef.value;
const focused = document.activeElement === input || overlayFocused;
const nextExpanded = focused && shouldExpand(input) && (forceExpand || expanded.value);
if (nextExpanded) {
if (!wasExpanded) {
expandedHighlightScrollLeft.value = 0;
expandedHighlightScrollTop.value = 0;
}
const nextRect = measureExpandedRect(input);
expandedRect.value = nextRect;
expandedHeight.value = measureExpandedHeight(input, nextRect);
@@ -445,6 +445,34 @@ describe("DataGridConditionEditor quote completion", () => {
vi.unstubAllGlobals();
});
it("does not carry the collapsed horizontal scroll into the expanded highlight layer", async () => {
vi.spyOn(HTMLElement.prototype, "getBoundingClientRect").mockImplementation(function (this: HTMLElement) {
const textWidth = (this.textContent?.length ?? 0) * 8;
const width = this.classList.contains("data-grid-topbar-condition-pane--expanded") ? 160 : textWidth;
return { x: 0, y: 0, left: 0, top: 0, right: width, bottom: 24, width, height: 24, toJSON: () => ({}) } as DOMRect;
});
vi.spyOn(HTMLCanvasElement.prototype, "getContext").mockReturnValue(null);
vi.stubGlobal("requestAnimationFrame", (callback: FrameRequestCallback) => {
callback(0);
return 1;
});
const { input } = mountEditor("where", "test_item_id=12 and test_item_name=''");
mockTextareaMetrics(input, { clientWidth: 80, scrollWidth: 320 });
input.scrollLeft = 128;
input.dispatchEvent(new Event("scroll", { bubbles: true }));
input.focus();
input.setSelectionRange(input.value.length, input.value.length);
input.dispatchEvent(new Event("focus", { bubbles: true }));
await nextTick();
await nextTick();
const expandedHighlight = document.body.querySelector(".data-grid-condition-highlight--expanded") as HTMLElement | null;
expect(expandedHighlight).toBeTruthy();
expect(expandedHighlight?.style.transform).toBe("translate(0px, 0px)");
vi.unstubAllGlobals();
});
it("positions suggestions below the measured expanded editor height", () => {
const source = readFileSync(resolve(process.cwd(), "apps/desktop/src/components/grid/DataGridConditionEditor.vue"), "utf8");
const expandedPaneCss = source.match(/\.data-grid-topbar-condition-pane--expanded\s*\{(?<body>[\s\S]*?)\n\}/)?.groups?.body;