mirror of
https://github.com/bulwarkmail/webmail.git
synced 2026-11-05 08:16:22 +00:00
fix: keep the reading pane readable when a wide list meets a smaller window
This commit is contained in:
@@ -57,7 +57,7 @@ import { useSettingsStore } from "@/stores/settings-store";
|
||||
import { useEffectiveMailLayout } from "@/hooks/use-effective-mail-layout";
|
||||
import { useContactStore } from "@/stores/contact-store";
|
||||
import { useIdentityStore } from "@/stores/identity-store";
|
||||
import { useUIStore, SIDEBAR_MIN, SIDEBAR_MAX, EMAIL_LIST_MIN, EMAIL_LIST_MAX, EMAIL_LIST_HEIGHT_MIN, EMAIL_LIST_HEIGHT_MAX } from "@/stores/ui-store";
|
||||
import { useUIStore, listPaneCssSize, SIDEBAR_MIN, SIDEBAR_MAX, EMAIL_LIST_MIN, EMAIL_LIST_MAX, EMAIL_LIST_HEIGHT_MIN, EMAIL_LIST_HEIGHT_MAX, READING_PANE_MIN_WIDTH, READING_PANE_MIN_HEIGHT } from "@/stores/ui-store";
|
||||
import { useDeviceDetection } from "@/hooks/use-media-query";
|
||||
import { usePaneSize } from "@/hooks/use-pane-size";
|
||||
import { usePaneId } from "@/hooks/use-pane-context";
|
||||
@@ -283,6 +283,7 @@ export function MailApp({ linkSegments: routeSegments }: MailAppProps = {}) {
|
||||
// Column resize state (disable transitions during drag)
|
||||
const [isResizing, setIsResizing] = useState(false);
|
||||
const dragStartWidth = useRef(0);
|
||||
const listPaneRef = useRef<HTMLElement>(null);
|
||||
// Mobile conversation view state
|
||||
const [conversationBlobSource, setConversationBlobSource] = useState<EmailBlobContext | undefined>();
|
||||
const conversationOpenSeqRef = useRef(0);
|
||||
@@ -5086,6 +5087,7 @@ export function MailApp({ linkSegments: routeSegments }: MailAppProps = {}) {
|
||||
<div className={cn("relative flex flex-1 min-h-0", isHorizontalMailLayout && "md:flex-col")}>
|
||||
{/* Email List - full width on mobile, fixed width/height on tablet/desktop */}
|
||||
<section
|
||||
ref={listPaneRef}
|
||||
aria-labelledby={listHeadingId}
|
||||
className={cn(
|
||||
"relative flex flex-col bg-background min-w-0",
|
||||
@@ -5113,8 +5115,8 @@ export function MailApp({ linkSegments: routeSegments }: MailAppProps = {}) {
|
||||
isMobile
|
||||
? undefined
|
||||
: isHorizontalMailLayout
|
||||
? (!shouldHideHorizontalViewerPane ? { height: emailListHeight } : undefined)
|
||||
: (!shouldCollapseListPane && !shouldHideViewerPane ? { width: clampEmailListWidth(emailListWidth) } : undefined)
|
||||
? (!shouldHideHorizontalViewerPane ? { height: listPaneCssSize(emailListHeight, EMAIL_LIST_HEIGHT_MIN, READING_PANE_MIN_HEIGHT) } : undefined)
|
||||
: (!shouldCollapseListPane && !shouldHideViewerPane ? { width: listPaneCssSize(clampEmailListWidth(emailListWidth), EMAIL_LIST_MIN, READING_PANE_MIN_WIDTH) } : undefined)
|
||||
}
|
||||
>
|
||||
<h2 id={listHeadingId} className="sr-only">{t('shell.message_list')}</h2>
|
||||
@@ -5498,7 +5500,7 @@ export function MailApp({ linkSegments: routeSegments }: MailAppProps = {}) {
|
||||
value={emailListWidth}
|
||||
min={EMAIL_LIST_MIN}
|
||||
max={EMAIL_LIST_MAX}
|
||||
onResizeStart={() => { dragStartWidth.current = emailListWidth; setIsResizing(true); }}
|
||||
onResizeStart={() => { dragStartWidth.current = listPaneRef.current?.getBoundingClientRect().width ?? emailListWidth; setIsResizing(true); }}
|
||||
onResize={(delta) => setEmailListWidth(dragStartWidth.current + delta)}
|
||||
onResizeEnd={() => { setIsResizing(false); persistColumnWidths(); }}
|
||||
onDoubleClick={resetEmailListWidth}
|
||||
@@ -5511,7 +5513,7 @@ export function MailApp({ linkSegments: routeSegments }: MailAppProps = {}) {
|
||||
value={emailListHeight}
|
||||
min={EMAIL_LIST_HEIGHT_MIN}
|
||||
max={EMAIL_LIST_HEIGHT_MAX}
|
||||
onResizeStart={() => { dragStartWidth.current = emailListHeight; setIsResizing(true); }}
|
||||
onResizeStart={() => { dragStartWidth.current = listPaneRef.current?.getBoundingClientRect().height ?? emailListHeight; setIsResizing(true); }}
|
||||
onResize={(delta) => setEmailListHeight(dragStartWidth.current + delta)}
|
||||
onResizeEnd={() => { setIsResizing(false); persistColumnWidths(); }}
|
||||
onDoubleClick={resetEmailListHeight}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { describe, it, expect, beforeEach } from 'vitest';
|
||||
import { useUIStore } from '../ui-store';
|
||||
import { useUIStore, listPaneCssSize } from '../ui-store';
|
||||
|
||||
describe('ui-store', () => {
|
||||
beforeEach(() => {
|
||||
@@ -17,6 +17,16 @@ describe('ui-store', () => {
|
||||
localStorage.clear();
|
||||
});
|
||||
|
||||
describe('listPaneCssSize', () => {
|
||||
it('keeps the saved size only while the reading pane keeps its minimum', () => {
|
||||
expect(listPaneCssSize(600, 240, 320)).toBe('clamp(240px, calc(100% - 320px), 600px)');
|
||||
});
|
||||
|
||||
it('never puts the lower limit above a saved size smaller than it', () => {
|
||||
expect(listPaneCssSize(220, 240, 320)).toBe('clamp(220px, calc(100% - 320px), 220px)');
|
||||
});
|
||||
});
|
||||
|
||||
describe('setSidebarWidth', () => {
|
||||
it('should clamp to minimum', () => {
|
||||
useUIStore.getState().setSidebarWidth(50);
|
||||
|
||||
@@ -16,6 +16,18 @@ const EMAIL_LIST_DEFAULT = 384;
|
||||
export const EMAIL_LIST_HEIGHT_MIN = 160;
|
||||
export const EMAIL_LIST_HEIGHT_MAX = 800;
|
||||
const EMAIL_LIST_HEIGHT_DEFAULT = 320;
|
||||
// What the reading pane keeps of the window, whatever size the list was dragged to
|
||||
export const READING_PANE_MIN_WIDTH = 320;
|
||||
export const READING_PANE_MIN_HEIGHT = 240;
|
||||
|
||||
/**
|
||||
* The list pane's CSS size: the size the user dragged it to, as long as that
|
||||
* leaves the reading pane its minimum. Saved sizes outlive the window they were
|
||||
* set in, and a smaller window would squeeze the reading pane down to nothing.
|
||||
*/
|
||||
export function listPaneCssSize(saved: number, min: number, readingPaneMin: number): string {
|
||||
return `clamp(${Math.min(saved, min)}px, calc(100% - ${readingPaneMin}px), ${saved}px)`;
|
||||
}
|
||||
|
||||
interface UIState {
|
||||
// Mobile view state
|
||||
|
||||
Reference in New Issue
Block a user