diff --git a/src/web-ui/src/app/components/NavPanel/sections/workspaces/WorkspaceItem.tsx b/src/web-ui/src/app/components/NavPanel/sections/workspaces/WorkspaceItem.tsx index 8af5e04dee..1c30defe62 100644 --- a/src/web-ui/src/app/components/NavPanel/sections/workspaces/WorkspaceItem.tsx +++ b/src/web-ui/src/app/components/NavPanel/sections/workspaces/WorkspaceItem.tsx @@ -62,6 +62,7 @@ interface WorkspaceItemProps { draggable?: boolean; isDragging?: boolean; onDragStart?: React.DragEventHandler; + onDrag?: React.DragEventHandler; onDragEnd?: React.DragEventHandler; } @@ -79,6 +80,7 @@ const WorkspaceItem: React.FC = ({ draggable = false, isDragging = false, onDragStart, + onDrag, onDragEnd, }) => { const { t } = useI18n('common'); @@ -122,7 +124,8 @@ const WorkspaceItem: React.FC = ({ const [acpClientsLoading, setAcpClientsLoading] = useState(false); const menuRef = useRef(null); const menuAnchorRef = useRef(null); - const menuPopoverRef = useRef(null); + const menuPopoverRef = useRef(null); + const popoverResizeObserverRef = useRef(null); const cardRef = useRef(null); const [menuPosition, setMenuPosition] = useState<{ top: number; left: number } | null>(null); const isDefaultAssistantWorkspace = @@ -387,6 +390,26 @@ const WorkspaceItem: React.FC = ({ requestAnimationFrame(apply); }, []); + // Callback ref for the menu popover. The popover only mounts once menuPosition + // is set (chicken-and-egg: menuPosition needs the popover's size), so a + // ResizeObserver created in the menuOpen effect would attach to a null ref. + // Attaching here ties the observer to the element's actual mount/unmount: + // on mount it fires once with the real size (fixing the stale initial height) + // and again whenever async content (ACP client rows, loading toggle, remote + // /git conditional rows, locale label width) changes the popover height. + const setMenuPopoverRef = useCallback((node: HTMLDivElement | null) => { + if (popoverResizeObserverRef.current) { + popoverResizeObserverRef.current.disconnect(); + popoverResizeObserverRef.current = null; + } + menuPopoverRef.current = node; + if (node && typeof ResizeObserver !== 'undefined') { + const ro = new ResizeObserver(() => updateMenuPosition()); + ro.observe(node); + popoverResizeObserverRef.current = ro; + } + }, [updateMenuPosition]); + const handleMenuTriggerClick = useCallback(() => { setMenuOpen(open => !open); }, []); @@ -784,6 +807,7 @@ const WorkspaceItem: React.FC = ({ className="bitfun-nav-panel__assistant-item-card" draggable={draggable} onDragStart={onDragStart} + onDrag={onDrag} onDragEnd={onDragEnd} onClick={() => { void handleCardNameClick(); }} style={{ cursor: 'pointer' }} @@ -816,7 +840,7 @@ const WorkspaceItem: React.FC = ({ - +