From 1e688419b44b5ad409346df0126f01838acb3efa Mon Sep 17 00:00:00 2001 From: JYC333 <22962980+JYC333@users.noreply.github.com> Date: Mon, 9 Mar 2026 00:48:08 +0000 Subject: [PATCH] fix: dropdown menu not follow the input when attribute detail dialog height changed --- .../src/services/attribute_autocomplete.ts | 37 +++++++++++++++---- 1 file changed, 30 insertions(+), 7 deletions(-) diff --git a/apps/client/src/services/attribute_autocomplete.ts b/apps/client/src/services/attribute_autocomplete.ts index a59cf94646..d1f29adbd1 100644 --- a/apps/client/src/services/attribute_autocomplete.ts +++ b/apps/client/src/services/attribute_autocomplete.ts @@ -95,11 +95,15 @@ function renderItems(panelEl: HTMLElement, items: NameItem[], activeItemId: numb function positionPanel(panelEl: HTMLElement, inputEl: HTMLElement): void { const rect = inputEl.getBoundingClientRect(); + const top = `${rect.bottom}px`; + const left = `${rect.left}px`; + const width = `${rect.width}px`; + panelEl.style.position = "fixed"; - panelEl.style.top = `${rect.bottom}px`; - panelEl.style.left = `${rect.left}px`; - panelEl.style.width = `${rect.width}px`; - panelEl.style.display = "block"; + if (panelEl.style.top !== top) panelEl.style.top = top; + if (panelEl.style.left !== left) panelEl.style.left = left; + if (panelEl.style.width !== width) panelEl.style.width = width; + if (panelEl.style.display !== "block") panelEl.style.display = "block"; } // --------------------------------------------------------------------------- @@ -115,7 +119,6 @@ function initAttributeNameNew({ $el, attributeType, open, onValueChange }: NewIn const inst = instanceMap.get(inputEl)!; inst.autocomplete.setIsOpen(true); inst.autocomplete.refresh(); - positionPanel(inst.panelEl, inputEl); } return; } @@ -125,6 +128,22 @@ function initAttributeNameNew({ $el, attributeType, open, onValueChange }: NewIn let isPanelOpen = false; let hasActiveItem = false; + let rafId: number | null = null; + function startPositioning() { + if (rafId !== null) return; + const update = () => { + positionPanel(panelEl, inputEl); + rafId = requestAnimationFrame(update); + }; + update(); + } + function stopPositioning() { + if (rafId !== null) { + cancelAnimationFrame(rafId); + rafId = null; + } + } + const autocomplete = createAutocomplete({ openOnFocus: true, defaultActiveItemId: 0, @@ -171,13 +190,15 @@ function initAttributeNameNew({ $el, attributeType, open, onValueChange }: NewIn autocomplete.setIsOpen(false); onValueChange?.(item.name); }); - positionPanel(panelEl, inputEl); + startPositioning(); } else { panelEl.style.display = "none"; + stopPositioning(); } if (!state.isOpen) { panelEl.style.display = "none"; + stopPositioning(); } }, }); @@ -195,6 +216,7 @@ function initAttributeNameNew({ $el, attributeType, open, onValueChange }: NewIn setTimeout(() => { autocomplete.setIsOpen(false); panelEl.style.display = "none"; + stopPositioning(); onValueChange?.(inputEl.value); }, 200); }; @@ -219,6 +241,7 @@ function initAttributeNameNew({ $el, attributeType, open, onValueChange }: NewIn inputEl.removeEventListener("focus", onFocus); inputEl.removeEventListener("blur", onBlur); inputEl.removeEventListener("keydown", onKeyDown); + stopPositioning(); }; instanceMap.set(inputEl, { autocomplete, panelEl, cleanup }); @@ -226,7 +249,7 @@ function initAttributeNameNew({ $el, attributeType, open, onValueChange }: NewIn if (open) { autocomplete.setIsOpen(true); autocomplete.refresh(); - positionPanel(panelEl, inputEl); + startPositioning(); } }