fix: show the file name in the tablet split view's preview header

This commit is contained in:
Linus Rath
2026-10-31 13:28:30 +01:00
parent dcca32a780
commit 65a8ac3469
3 changed files with 71 additions and 3 deletions
+37
View File
@@ -0,0 +1,37 @@
import { describe, expect, it, vi } from 'vitest';
import fs from 'node:fs';
import path from 'node:path';
vi.mock('react-native', () => ({ useWindowDimensions: () => ({ width: 800, height: 1280 }) }));
vi.mock('react-native-safe-area-context', () => ({ useSafeAreaInsets: () => ({ top: 0, bottom: 0, left: 0, right: 0 }) }));
import { FILES_LIST_MIN, PREVIEW_HEADER_CHROME, PREVIEW_TITLE_MIN, filesListWidth } from '../files-split';
// In the tablet split view the docked file preview had a fixed 45 % of the
// row. On a Pixel Tablet in portrait (about 720 dp next to the tab rail)
// that left the header's file name 2 px between its six buttons.
const titleRoom = (contentWidth: number) => contentWidth - filesListWidth(contentWidth) - PREVIEW_HEADER_CHROME;
describe('filesListWidth', () => {
it('leaves the file name room in the preview header on a tablet in portrait', () => {
expect(titleRoom(720)).toBeGreaterThanOrEqual(PREVIEW_TITLE_MIN);
expect(filesListWidth(720)).toBeGreaterThanOrEqual(FILES_LIST_MIN);
});
it('gives the list its usual pane width on a wide window', () => {
expect(filesListWidth(1200)).toBe(408);
expect(titleRoom(1200)).toBeGreaterThan(400);
});
it('keeps the list usable on a narrow split', () => {
expect(filesListWidth(600)).toBe(FILES_LIST_MIN);
});
});
describe('the Files split view', () => {
it('sizes the list column with filesListWidth and lets the preview take the rest', () => {
const source = fs.readFileSync(path.resolve(__dirname, '../../screens/FilesScreen.tsx'), 'utf8');
expect(source).toContain('width: filesListWidth(contentWidth)');
expect(source).not.toMatch(/previewPane: \{[^}]*width: '45%'/);
});
});
+24
View File
@@ -0,0 +1,24 @@
import { listPaneWidth } from './use-layout';
/**
* Fixed chrome of the docked file preview's header: 16 dp padding on each
* side and six 40 dp buttons (close, previous, next, open, download, share)
* with 8 dp gaps, the title's gap included.
*/
export const PREVIEW_HEADER_CHROME = 2 * 16 + 6 * 40 + 6 * 8;
/** Room the file name keeps in the docked preview's header. */
export const PREVIEW_TITLE_MIN = 120;
/** The file list never gets narrower than this next to a preview. */
export const FILES_LIST_MIN = 280;
/**
* Width of the file list next to the docked preview. The preview had a fixed
* 45 %: on a tablet in portrait (about 720 dp next to the tab rail) that was
* 324 dp, and the six header buttons left the file name 2 px. The list takes
* the usual list-pane width (listPaneWidth, as in Mail and Contacts) and
* gives way, down to FILES_LIST_MIN, until the name has PREVIEW_TITLE_MIN.
*/
export function filesListWidth(contentWidth: number): number {
const previewMin = PREVIEW_HEADER_CHROME + PREVIEW_TITLE_MIN;
return Math.max(FILES_LIST_MIN, Math.min(listPaneWidth(contentWidth), contentWidth - previewMin));
}
+10 -3
View File
@@ -71,6 +71,7 @@ import { useSafeAreaInsets } from 'react-native-safe-area-context';
import { formatListDate } from '../lib/date-format';
import { useLayout, useSheetInsets } from '../lib/use-layout';
import { useSheetBottomInset } from '../lib/sheet-bottom-inset';
import { filesListWidth } from '../lib/files-split';
import { useBackWhileFocused } from '../lib/use-back-while-focused';
import { filesBackStep } from '../lib/files-back';
import { driveChange, type DriveScope } from '../lib/files-drive';
@@ -258,6 +259,8 @@ export default function FilesScreen() {
const [actionsTarget, setActionsTarget] = useState<FileRow | null>(null);
// The menu of a starred folder or a recent file.
const [placeMenu, setPlaceMenu] = useState<PlaceMenu | null>(null);
// Width inside the side insets, for the list next to a docked preview.
const [contentWidth, setContentWidth] = useState(0);
const [shareTarget, setShareTarget] = useState<FileRow | null>(null);
const [moveTarget, setMoveTarget] = useState<FileRow | null>(null);
const [previewTarget, setPreviewTarget] = useState<FileRow | null>(null);
@@ -1563,8 +1566,11 @@ export default function FilesScreen() {
// On a wide window an open file shows beside the list instead of covering it.
const previewDocked = isSplit && previewTarget != null;
return (
<View style={[styles.container, previewDocked && styles.splitRow, { paddingLeft: insets.left, paddingRight: insets.right }]}>
<View style={styles.listColumn}>
<View
style={[styles.container, previewDocked && styles.splitRow, { paddingLeft: insets.left, paddingRight: insets.right }]}
onLayout={(e) => setContentWidth(e.nativeEvent.layout.width - insets.left - insets.right)}
>
<View style={[styles.listColumn, previewDocked && { flex: 0, width: filesListWidth(contentWidth) }]}>
{renderHeader()}
{body}
</View>
@@ -2177,7 +2183,8 @@ function makeStyles(c: ThemePalette) {
},
splitRow: { flexDirection: 'row' },
listColumn: { flex: 1 },
previewPane: { width: '45%', borderLeftWidth: 1, borderLeftColor: c.border },
// The rest of the row; the list column gets filesListWidth.
previewPane: { flex: 1, borderLeftWidth: 1, borderLeftColor: c.border },
header: {
paddingBottom: spacing.sm,
borderBottomWidth: 1,