fix(app): avoid settings header offset

This commit is contained in:
Mohamed Boudra
2026-04-22 16:19:31 +07:00
parent 7993dc02dc
commit b6afc66131
4 changed files with 105 additions and 18 deletions
@@ -18,6 +18,7 @@ interface ScreenHeaderProps {
leftStyle?: StyleProp<ViewStyle>;
rightStyle?: StyleProp<ViewStyle>;
borderless?: boolean;
windowControlsPaddingRole?: "header" | "detailHeader";
onRowLayout?: (event: LayoutChangeEvent) => void;
}
@@ -31,12 +32,13 @@ export function ScreenHeader({
leftStyle,
rightStyle,
borderless,
windowControlsPaddingRole = "header",
onRowLayout,
}: ScreenHeaderProps) {
const { theme } = useUnistyles();
const insets = useSafeAreaInsets();
const isMobile = useIsCompactFormFactor();
const padding = useWindowControlsPadding("header");
const padding = useWindowControlsPadding(windowControlsPaddingRole);
// Only add extra padding on mobile for better touch targets; on desktop, only use safe area insets
const topPadding = isMobile ? HEADER_TOP_PADDING_MOBILE : 0;
const baseHorizontalPadding = theme.spacing[2];
@@ -898,6 +898,7 @@ export default function SettingsScreen({ view }: SettingsScreenProps) {
<View style={desktopStyles.contentPane}>
<ScreenHeader
borderless={!detailHeader}
windowControlsPaddingRole="detailHeader"
left={
detailHeader ? (
<>
@@ -0,0 +1,42 @@
import { describe, expect, it } from "vitest";
import { resolveWindowControlsPadding } from "@/utils/desktop-window";
const rawPadding = {
left: 80,
right: 48,
top: 28,
};
describe("resolveWindowControlsPadding", () => {
it("pads the main header for window controls when the app sidebar is closed", () => {
expect(
resolveWindowControlsPadding({
role: "header",
rawPadding,
sidebarClosed: true,
explorerOpen: false,
focusModeEnabled: false,
}),
).toEqual({
left: 80,
right: 48,
top: 0,
});
});
it("does not add left padding to detail headers with their own sidebar", () => {
expect(
resolveWindowControlsPadding({
role: "detailHeader",
rawPadding,
sidebarClosed: true,
explorerOpen: false,
focusModeEnabled: false,
}),
).toEqual({
left: 0,
right: 48,
top: 0,
});
});
});
+59 -17
View File
@@ -17,7 +17,12 @@ type RawWindowControlsPadding = {
top: number;
};
type WindowControlsPaddingRole = "sidebar" | "header" | "tabRow" | "explorerSidebar";
type WindowControlsPaddingRole =
| "sidebar"
| "header"
| "detailHeader"
| "tabRow"
| "explorerSidebar";
// Module-level cache so hook remounts (e.g., on navigation) don't briefly
// fall back to the default `false` while the async fullscreen check resolves.
@@ -114,22 +119,59 @@ export function useWindowControlsPadding(role: WindowControlsPaddingRole): {
const rawPadding = useRawWindowControlsPadding();
const sidebarClosed = !sidebarOpen;
let left = 0;
let right = 0;
let top = 0;
if (role === "sidebar") {
left = rawPadding.left;
top = rawPadding.top;
} else if (role === "header") {
left = sidebarClosed ? rawPadding.left : 0;
right = explorerOpen ? 0 : rawPadding.right;
} else if (role === "tabRow") {
left = sidebarClosed && focusModeEnabled ? rawPadding.left : 0;
right = focusModeEnabled && !explorerOpen ? rawPadding.right : 0;
} else if (role === "explorerSidebar") {
right = rawPadding.right;
}
const { left, right, top } = resolveWindowControlsPadding({
role,
rawPadding,
sidebarClosed,
explorerOpen,
focusModeEnabled,
});
return useMemo(() => ({ left, right, top }), [left, right, top]);
}
export function resolveWindowControlsPadding(input: {
role: WindowControlsPaddingRole;
rawPadding: RawWindowControlsPadding;
sidebarClosed: boolean;
explorerOpen: boolean;
focusModeEnabled: boolean;
}): RawWindowControlsPadding {
if (input.role === "sidebar") {
return {
left: input.rawPadding.left,
right: 0,
top: input.rawPadding.top,
};
}
if (input.role === "header") {
return {
left: input.sidebarClosed ? input.rawPadding.left : 0,
right: input.explorerOpen ? 0 : input.rawPadding.right,
top: 0,
};
}
if (input.role === "detailHeader") {
return {
left: 0,
right: input.rawPadding.right,
top: 0,
};
}
if (input.role === "tabRow") {
return {
left: input.sidebarClosed && input.focusModeEnabled ? input.rawPadding.left : 0,
right: input.focusModeEnabled && !input.explorerOpen ? input.rawPadding.right : 0,
top: 0,
};
}
return {
left: 0,
right: input.rawPadding.right,
top: 0,
};
}