diff --git a/components/email/email-viewer.tsx b/components/email/email-viewer.tsx index 688b345e..d711a663 100644 --- a/components/email/email-viewer.tsx +++ b/components/email/email-viewer.tsx @@ -958,6 +958,9 @@ export function EmailViewer({ const leftGroup = el.firstElementChild as HTMLElement; const rightGroup = el.lastElementChild as HTMLElement; const mainGap = parseFloat(getComputedStyle(el).gap) || 0; + // Temporarily prevent flex shrinking so we can measure natural widths + leftGroup.style.flexShrink = '0'; + rightGroup.style.flexShrink = '0'; // Iteratively hide items until content fits let count = 0; const isOverflowing = () => @@ -969,6 +972,9 @@ export function EmailViewer({ item.style.display = 'none'; count++; } + // Restore flex shrinking + leftGroup.style.flexShrink = ''; + rightGroup.style.flexShrink = ''; setOverflowCount(prev => prev === count ? prev : count); }; const observer = new ResizeObserver(calculate); @@ -2435,6 +2441,468 @@ export function EmailViewer({ const isUnread = !email.keywords?.$seen; const isImportant = email.keywords?.["$important"]; + // Shared toolbar items used by both 'top' and 'below-subject' positions + const renderToolbarItems = (showBackButton: boolean) => ( + <> + {/* Left: Reply actions */} +
+ {showBackButton && isTablet && !tabletListVisible && onBack && ( + + )} + + + +
+ + {/* Right: Organize actions — order: archive, delete, move, star, tag, spam, read state, print, view source */} +
+ {isLoading && ( +
+ +
+ )} + {/* Archive */} + + {/* Delete */} + + {/* Move to folder */} + {moveTree.length > 0 && onMoveToMailbox && ( +
+ + {moveMenuOpen && ( +
+ {(() => { + const renderNodes = (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 && renderNodes(node.children, depth + 1)} +
+ ); + }); + }; + return renderNodes(moveTree); + })()} +
+ )} +
+ )} + {/* Star/Flag toggle */} + + + {/* Tag Picker — hidden on mobile, overflows to More menu */} +
+
+
+ + {tagMenuOpen && ( +
+ {colorOptions.map((option) => ( + + ))} + {currentColor && ( + <> +
+ + + )} +
+ )} +
+
+ + {/* Spam — hidden on mobile, overflows to More menu */} + {(onMarkAsSpam || onUndoSpam) && ( + + )} + + {/* Toggle read state — hidden on mobile, overflows to More menu */} + + + {/* Print — hidden on mobile, overflows to More menu */} + + + {/* View source — hidden on mobile, overflows to More menu */} + + + {/* More menu — click-based */} +
+ + {moreMenuOpen && !isMobile && ( +
+ {/* Overflow: reply */} + + {/* Overflow: reply all */} + + {/* Overflow: forward */} + + {/* Overflow: archive */} + + {/* Overflow: move to folder */} + {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} +
+ )} + {node.children.length > 0 && renderMobileNodes(node.children, depth + 1)} +
+ ); + }); + }; + return renderMobileNodes(moveTree); + })()} +
+
+ )} + {/* Overflow: tag submenu */} + {colorOptions.length > 0 && ( +
= 5 ? "" : "sm:hidden")}> +
+
{t('tag')}
+ {colorOptions.map((option) => ( + + ))} + {currentColor && ( + + )} +
+
+ )} + {/* Overflow: spam */} + {(onMarkAsSpam || onUndoSpam) && ( + + )} + {/* Overflow: toggle read */} + + {/* Overflow: print */} + + {/* Overflow: view source */} + +
+ {/* Export email */} + + {/* Import email */} + + {onShowShortcuts && ( + + )} +
+ )} +
+
+ + ); + return (
-
- {/* Left: Back + Reply actions */} -
- {isTablet && !tabletListVisible && onBack && ( - - )} - - - -
- - {/* Right: Organize actions — order: archive, delete, move, star, tag, spam, read state, print */} -
- {isLoading && ( -
- -
- )} - {/* Archive */} - - {/* Delete */} - - {/* Move to folder — promoted to Button, same design as archive/delete */} - {moveTree.length > 0 && onMoveToMailbox && ( -
- - {moveMenuOpen && ( -
- {(() => { - const renderNodes = (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 && renderNodes(node.children, depth + 1)} -
- ); - }); - }; - return renderNodes(moveTree); - })()} -
- )} -
- )} - {/* Star/Flag toggle */} - - - {/* Tag Picker — hidden on mobile, overflows to More menu */} -
-
-
- - {tagMenuOpen && ( -
- {colorOptions.map((option) => ( - - ))} - {currentColor && ( - <> -
- - - )} -
- )} -
-
- - {/* Spam — hidden on mobile, overflows to More menu */} - {(onMarkAsSpam || onUndoSpam) && ( - - )} - - {/* Toggle read state — hidden on mobile, overflows to More menu */} - - - {/* Print — hidden on mobile, overflows to More menu */} - - - {/* View source — hidden on mobile, overflows to More menu */} - - - {/* More menu — click-based */} -
- - {moreMenuOpen && !isMobile && ( -
- {/* Overflow: archive */} - - {/* Overflow: move to folder */} - {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} -
- )} - {node.children.length > 0 && renderMobileNodes(node.children, depth + 1)} -
- ); - }); - }; - return renderMobileNodes(moveTree); - })()} -
-
- )} - {/* Overflow: tag submenu */} - {colorOptions.length > 0 && ( -
= 5 ? "" : "sm:hidden")}> -
-
{t('tag')}
- {colorOptions.map((option) => ( - - ))} - {currentColor && ( - - )} -
-
- )} - {/* Overflow: spam */} - {(onMarkAsSpam || onUndoSpam) && ( - - )} - {/* Overflow: toggle read */} - - {/* Overflow: print */} - - {/* Overflow: view source */} - -
- {/* Export email */} - - {/* Import email */} - - {onShowShortcuts && ( - - )} -
- )} -
-
+
+ {renderToolbarItems(true)}
@@ -3118,422 +3161,8 @@ export function EmailViewer({ {toolbarPosition === 'below-subject' && (
-
- {/* Left: Reply actions */} -
- - - -
- - {/* Right: Organize actions — order: archive, delete, move, star, tag, spam, read state, print, view source */} -
- {isLoading && ( -
- -
- )} - {/* Archive */} - - {/* Delete */} - - {/* Move to folder — promoted to Button, same design as archive/delete */} - {moveTree.length > 0 && onMoveToMailbox && ( -
- - {moveMenuOpen && ( -
- {(() => { - const renderNodes = (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 && renderNodes(node.children, depth + 1)} -
- ); - }); - }; - return renderNodes(moveTree); - })()} -
- )} -
- )} - {/* Star/Flag toggle */} - - - {/* Tag Picker — hidden on mobile, overflows to More menu */} -
-
-
- - {tagMenuOpen && ( -
- {colorOptions.map((option) => ( - - ))} - {currentColor && ( - <> -
- - - )} -
- )} -
-
- - {/* Spam — hidden on mobile, overflows to More menu */} - {(onMarkAsSpam || onUndoSpam) && ( - - )} - - {/* Toggle read state — hidden on mobile, overflows to More menu */} - - - {/* Print — hidden on mobile, overflows to More menu */} - - - {/* View source — hidden on mobile, overflows to More menu */} - - - {/* More menu — click-based */} -
- - {moreMenuOpen && !isMobile && ( -
- {/* Overflow: archive */} - - {/* Overflow: move to folder */} - {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} -
- )} - {node.children.length > 0 && renderMobileNodes(node.children, depth + 1)} -
- ); - }); - }; - return renderMobileNodes(moveTree); - })()} -
-
- )} - {/* Overflow: tag submenu */} - {colorOptions.length > 0 && ( -
= 5 ? "" : "sm:hidden")}> -
-
{t('tag')}
- {colorOptions.map((option) => ( - - ))} - {currentColor && ( - - )} -
-
- )} - {/* Overflow: spam */} - {(onMarkAsSpam || onUndoSpam) && ( - - )} - {/* Overflow: toggle read */} - - {/* Overflow: print */} - - {/* Overflow: view source */} - -
- {/* Export email */} - - {/* Import email */} - - {onShowShortcuts && ( - - )} -
- )} -
-
+
+ {renderToolbarItems(false)}