From ada356e4403d72f51efe6f20e991a9dec0e08140 Mon Sep 17 00:00:00 2001 From: Linus Rath Date: Wed, 18 Mar 2026 15:35:17 +0100 Subject: [PATCH] fix: menu overvlow fixed with submenu support for move and tag actions --- components/email/email-viewer.tsx | 209 +++++++++++++++++------------- 1 file changed, 120 insertions(+), 89 deletions(-) diff --git a/components/email/email-viewer.tsx b/components/email/email-viewer.tsx index d711a663..f5121574 100644 --- a/components/email/email-viewer.tsx +++ b/components/email/email-viewer.tsx @@ -847,6 +847,7 @@ export function EmailViewer({ const [isSendingQuickReply, setIsSendingQuickReply] = useState(false); const [showSourceModal, setShowSourceModal] = useState(false); const [moreMenuOpen, setMoreMenuOpen] = useState(false); + const [moreMenuSub, setMoreMenuSub] = useState<'move' | 'tag' | null>(null); const [tagMenuOpen, setTagMenuOpen] = useState(false); const [moveMenuOpen, setMoveMenuOpen] = useState(false); const moreMenuRef = useRef(null); @@ -922,6 +923,7 @@ export function EmailViewer({ function handleClickOutside(e: MouseEvent) { if (moreMenuOpen && moreMenuRef.current && !moreMenuRef.current.contains(e.target as Node)) { setMoreMenuOpen(false); + setMoreMenuSub(null); } if (tagMenuOpen && tagMenuRef.current && !tagMenuRef.current.contains(e.target as Node)) { setTagMenuOpen(false); @@ -961,6 +963,7 @@ export function EmailViewer({ // Temporarily prevent flex shrinking so we can measure natural widths leftGroup.style.flexShrink = '0'; rightGroup.style.flexShrink = '0'; + el.style.overflow = 'hidden'; // Iteratively hide items until content fits let count = 0; const isOverflowing = () => @@ -972,9 +975,10 @@ export function EmailViewer({ item.style.display = 'none'; count++; } - // Restore flex shrinking + // Restore layout leftGroup.style.flexShrink = ''; rightGroup.style.flexShrink = ''; + el.style.overflow = ''; setOverflowCount(prev => prev === count ? prev : count); }; const observer = new ResizeObserver(calculate); @@ -2722,121 +2726,148 @@ export function EmailViewer({ size="sm" className="flex-col items-center gap-0.5 h-auto py-1.5 px-2 sm:flex-row sm:h-8 sm:w-8 sm:gap-0 sm:py-0 sm:px-0" title={t('more_actions')} - onClick={() => { setMoreMenuOpen(!moreMenuOpen); setTagMenuOpen(false); setMoveMenuOpen(false); }} + onClick={() => { setMoreMenuOpen(!moreMenuOpen); setMoreMenuSub(null); setTagMenuOpen(false); setMoveMenuOpen(false); }} > {t('more_actions')} {moreMenuOpen && !isMobile && ( -
+
{/* Overflow: reply */} {/* Overflow: reply all */} {/* Overflow: forward */} {/* Overflow: archive */} - {/* Overflow: move to folder */} + {/* Overflow: move to folder — submenu */} {moveTree.length > 0 && onMoveToMailbox && ( -
= 6 ? "" : "sm:hidden")}> -
-
{t('move_to')}
- {(() => { - const renderMobileNodes = (nodes: MailboxNode[], depth = 0) => { - return nodes.map((node) => { - const Icon = getMoveMailboxIcon(node.role); - const isTarget = moveTargetIds.has(node.id); - return ( -
- {isTarget ? ( - - ) : ( -
- - {node.name} +
= 6 ? "" : "sm:hidden")} + onMouseEnter={() => setMoreMenuSub('move')} + onMouseLeave={() => setMoreMenuSub(null)} + > + + {moreMenuSub === 'move' && ( +
+ {(() => { + const renderMobileNodes = (nodes: MailboxNode[], depth = 0) => { + return nodes.map((node) => { + const Icon = getMoveMailboxIcon(node.role); + const isTarget = moveTargetIds.has(node.id); + return ( +
+ {isTarget ? ( + + ) : ( +
+ + {node.name} +
+ )} + {node.children.length > 0 && renderMobileNodes(node.children, depth + 1)}
- )} - {node.children.length > 0 && renderMobileNodes(node.children, depth + 1)} -
- ); - }); - }; - return renderMobileNodes(moveTree); - })()} -
+ ); + }); + }; + return renderMobileNodes(moveTree); + })()} +
+ )}
)} - {/* Overflow: tag submenu */} + {/* Overflow: tag — submenu */} {colorOptions.length > 0 && ( -
= 5 ? "" : "sm:hidden")}> -
-
{t('tag')}
- {colorOptions.map((option) => ( - + {moreMenuSub === 'tag' && ( +
+ {colorOptions.map((option) => ( + + ))} + {currentColor && ( + <> +
+ + )} - > - - {option.name} - {currentColor === option.value && } - - ))} - {currentColor && ( - +
)} -
)} {/* Overflow: spam */} {(onMarkAsSpam || onUndoSpam) && ( {/* Overflow: print */} {/* Overflow: view source */} {/* Import email */} {onShowShortcuts && (