mirror of
https://github.com/bulwarkmail/native.git
synced 2026-11-01 00:07:01 +00:00
fix: give the folder name its own line next to the docked file preview
This commit is contained in:
@@ -5,7 +5,10 @@ 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';
|
||||
import {
|
||||
FILES_LIST_MIN, FILES_TITLE_MIN, PREVIEW_HEADER_CHROME, PREVIEW_TITLE_MIN,
|
||||
filesHeaderTitleRoom, filesListWidth, filesTitleOnOwnRow,
|
||||
} 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)
|
||||
@@ -28,6 +31,29 @@ describe('filesListWidth', () => {
|
||||
});
|
||||
});
|
||||
|
||||
describe('the folder name in the Files header next to the docked preview', () => {
|
||||
it('had about 50 dp beside four buttons and Back in a 320 dp list column', () => {
|
||||
expect(filesHeaderTitleRoom(320, 4, true)).toBe(52);
|
||||
expect(filesHeaderTitleRoom(320, 5, false)).toBe(52);
|
||||
});
|
||||
|
||||
it('moves under the buttons when that is too little, and stays beside them when there is room', () => {
|
||||
expect(filesTitleOnOwnRow(filesListWidth(720), 4, true)).toBe(true);
|
||||
expect(filesTitleOnOwnRow(FILES_LIST_MIN, 5, false)).toBe(true);
|
||||
expect(filesTitleOnOwnRow(800, 4, true)).toBe(false);
|
||||
expect(filesHeaderTitleRoom(800, 4, true)).toBeGreaterThanOrEqual(FILES_TITLE_MIN);
|
||||
// Not measured yet: beside the buttons, as before.
|
||||
expect(filesTitleOnOwnRow(0, 5, true)).toBe(false);
|
||||
});
|
||||
|
||||
it('is how the header lays out its title', () => {
|
||||
const source = fs.readFileSync(path.resolve(__dirname, '../../screens/FilesScreen.tsx'), 'utf8');
|
||||
expect(source).toMatch(/const titleOwnRow = isSplit && previewTarget != null && filesTitleOnOwnRow\(headerWidth, headerButtons, canBack\);/);
|
||||
expect(source).toMatch(/onLayout=\{\(e\) => setHeaderWidth\(e\.nativeEvent\.layout\.width\)\}/);
|
||||
expect(source).toMatch(/\{titleOwnRow \? \(\s*<Text style=\{styles\.title\} numberOfLines=\{2\}>/);
|
||||
});
|
||||
});
|
||||
|
||||
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');
|
||||
|
||||
@@ -22,3 +22,27 @@ 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));
|
||||
}
|
||||
|
||||
/** Room the folder name keeps beside the Files header buttons before it moves to a line of its own. */
|
||||
export const FILES_TITLE_MIN = 120;
|
||||
const HEADER_PADDING = 12; // spacing.md, each side
|
||||
const HEADER_BUTTON = 44; // componentSizes.buttonLg
|
||||
const HEADER_GAP = 4; // spacing.xs, between buttons
|
||||
const HEADER_ROW_GAP = 8; // spacing.sm, title side to buttons
|
||||
|
||||
/**
|
||||
* Width left for the folder name in the Files header, `width` wide, with
|
||||
* `buttons` buttons on the right and a back button or not. Next to the docked
|
||||
* preview (280 to 420 dp) the four or five buttons left a folder name about
|
||||
* 50 dp: "…".
|
||||
*/
|
||||
export function filesHeaderTitleRoom(width: number, buttons: number, back: boolean): number {
|
||||
const actions = buttons * HEADER_BUTTON + Math.max(0, buttons - 1) * HEADER_GAP;
|
||||
const left = back ? HEADER_BUTTON + HEADER_GAP : 0;
|
||||
return width - 2 * HEADER_PADDING - actions - HEADER_ROW_GAP - left;
|
||||
}
|
||||
|
||||
/** Whether the folder name goes on its own line under the header buttons. A width not measured yet keeps it beside them. */
|
||||
export function filesTitleOnOwnRow(width: number, buttons: number, back: boolean): boolean {
|
||||
return width > 0 && filesHeaderTitleRoom(width, buttons, back) < FILES_TITLE_MIN;
|
||||
}
|
||||
|
||||
@@ -71,7 +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 { filesListWidth, filesTitleOnOwnRow } from '../lib/files-split';
|
||||
import { radioOptionA11y } from '../components/radio-a11y';
|
||||
import { useBackWhileFocused } from '../lib/use-back-while-focused';
|
||||
import { filesBackStep } from '../lib/files-back';
|
||||
@@ -232,6 +232,9 @@ export default function FilesScreen() {
|
||||
// whole screen keeps clear of the side insets in landscape.
|
||||
const insets = useSafeAreaInsets();
|
||||
const headerInsets = { paddingTop: insets.top + spacing.sm, paddingHorizontal: spacing.md };
|
||||
// The header's width: next to the docked preview the folder name moves
|
||||
// under the buttons when they leave it too little room.
|
||||
const [headerWidth, setHeaderWidth] = useState(0);
|
||||
|
||||
// Folder navigation stack. Hierarchy is real parentId nesting (#379) — each
|
||||
// entry is the folder's node id (namespaced "accountId:nodeId" inside a
|
||||
@@ -1015,8 +1018,12 @@ export default function FilesScreen() {
|
||||
</View>
|
||||
);
|
||||
}
|
||||
// Search and view toggle; New folder and Upload; Trash at the top level.
|
||||
// Only the list column next to the docked preview is that narrow.
|
||||
const headerButtons = 2 + (canCreateHere ? 2 : 0) + (path.length === 0 && !inTrash ? 1 : 0);
|
||||
const titleOwnRow = isSplit && previewTarget != null && filesTitleOnOwnRow(headerWidth, headerButtons, canBack);
|
||||
return (
|
||||
<View style={[styles.header, headerInsets]}>
|
||||
<View style={[styles.header, headerInsets]} onLayout={(e) => setHeaderWidth(e.nativeEvent.layout.width)}>
|
||||
<View style={styles.headerRow}>
|
||||
<View style={styles.headerLeft}>
|
||||
{canBack ? (
|
||||
@@ -1030,9 +1037,11 @@ export default function FilesScreen() {
|
||||
<ChevronLeft size={22} color={c.text} />
|
||||
</Pressable>
|
||||
) : null}
|
||||
<Text style={styles.title} numberOfLines={1}>
|
||||
{headerName}
|
||||
</Text>
|
||||
{!titleOwnRow ? (
|
||||
<Text style={styles.title} numberOfLines={1}>
|
||||
{headerName}
|
||||
</Text>
|
||||
) : null}
|
||||
</View>
|
||||
<View style={styles.headerActions}>
|
||||
<Pressable
|
||||
@@ -1092,6 +1101,11 @@ export default function FilesScreen() {
|
||||
) : null}
|
||||
</View>
|
||||
</View>
|
||||
{titleOwnRow ? (
|
||||
<Text style={styles.title} numberOfLines={2}>
|
||||
{headerName}
|
||||
</Text>
|
||||
) : null}
|
||||
{canBack ? (
|
||||
<Text style={styles.breadcrumb} numberOfLines={1}>
|
||||
{[rootLabel, ...(inTrash ? [trashLabel] : path.map((p) => p.name))].join(' / ')}
|
||||
|
||||
Reference in New Issue
Block a user