From 186ac4933b79aed57fce23ebcf3b525fcfd1c474 Mon Sep 17 00:00:00 2001 From: Guilhem Date: Thu, 2 Jul 2026 23:30:28 +0200 Subject: [PATCH] fix(frontend): home New submenus fall back below, hugging the right edge (#9894) * fix(frontend): home New submenus fall back below hugging the right edge Co-Authored-By: Claude Fable 5 * fix(frontend): re-hug submenu on window resize even without a melt reposition Co-Authored-By: Claude Fable 5 --------- Co-authored-by: Claude Fable 5 --- .../components/home/CreateActionsMenu.svelte | 41 +++++++++++++++++-- 1 file changed, 38 insertions(+), 3 deletions(-) diff --git a/frontend/src/lib/components/home/CreateActionsMenu.svelte b/frontend/src/lib/components/home/CreateActionsMenu.svelte index 146e390419..4d8adb88e9 100644 --- a/frontend/src/lib/components/home/CreateActionsMenu.svelte +++ b/frontend/src/lib/components/home/CreateActionsMenu.svelte @@ -243,7 +243,9 @@ forceVisible: true }) - // per-row fan-out submenus, anchored toward the page center (popover hugs the right edge) + // per-row fan-out submenus: open to the right when there's room. The popover hugs + // the right viewport edge on most screens, so the default flip (right → left) would + // cover the doc panel — fall back below the trigger row instead. const { elements: { subTrigger: wacSubTrigger, subMenu: wacSubMenu }, states: { subOpen: wacSubOpen } @@ -251,7 +253,7 @@ positioning: { placement: 'right-start', gutter: 4, - flip: true, + flip: { fallbackPlacements: ['bottom-end', 'bottom-start', 'top-end', 'top-start'] }, fitViewport: true, overflowPadding: 8 } @@ -263,12 +265,43 @@ positioning: { placement: 'right-end', gutter: 4, - flip: true, + flip: { fallbackPlacements: ['bottom-end', 'bottom-start', 'top-end', 'top-start'] }, fitViewport: true, overflowPadding: 8 } }) + // When a fan-out submenu falls below its row ('bottom'/'top' data-side), melt aligns + // it to the row's right edge, but free viewport space may remain beside the popover — + // slide it right until it hugs the viewport edge. Melt repositions on scroll/resize + // via style/data-side writes, so re-apply on attribute mutations. + function hugViewportRight(node: HTMLElement) { + let delta = 0 + const apply = () => { + const side = node.getAttribute('data-side') + const baseRight = node.getBoundingClientRect().right - delta + const next = + side === 'bottom' || side === 'top' + ? Math.max(0, document.documentElement.clientWidth - 8 - baseRight) + : 0 + if (Math.abs(next - delta) < 0.5) return + delta = next + node.style.transform = next ? `translateX(${next}px)` : '' + } + const observer = new MutationObserver(apply) + observer.observe(node, { attributeFilter: ['style', 'data-side'] }) + // melt only rewrites style when the reposition moves the submenu — a resize that + // changes clientWidth without moving it (scrollbar appearing, zoom) needs a direct hook + window.addEventListener('resize', apply) + apply() + return { + destroy: () => { + observer.disconnect() + window.removeEventListener('resize', apply) + } + } + } + // attach the menu trigger to the design-system