fix(app): avoid settings header offset
This commit is contained in:
@@ -18,6 +18,7 @@ interface ScreenHeaderProps {
|
|||||||
leftStyle?: StyleProp<ViewStyle>;
|
leftStyle?: StyleProp<ViewStyle>;
|
||||||
rightStyle?: StyleProp<ViewStyle>;
|
rightStyle?: StyleProp<ViewStyle>;
|
||||||
borderless?: boolean;
|
borderless?: boolean;
|
||||||
|
windowControlsPaddingRole?: "header" | "detailHeader";
|
||||||
onRowLayout?: (event: LayoutChangeEvent) => void;
|
onRowLayout?: (event: LayoutChangeEvent) => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -31,12 +32,13 @@ export function ScreenHeader({
|
|||||||
leftStyle,
|
leftStyle,
|
||||||
rightStyle,
|
rightStyle,
|
||||||
borderless,
|
borderless,
|
||||||
|
windowControlsPaddingRole = "header",
|
||||||
onRowLayout,
|
onRowLayout,
|
||||||
}: ScreenHeaderProps) {
|
}: ScreenHeaderProps) {
|
||||||
const { theme } = useUnistyles();
|
const { theme } = useUnistyles();
|
||||||
const insets = useSafeAreaInsets();
|
const insets = useSafeAreaInsets();
|
||||||
const isMobile = useIsCompactFormFactor();
|
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
|
// 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 topPadding = isMobile ? HEADER_TOP_PADDING_MOBILE : 0;
|
||||||
const baseHorizontalPadding = theme.spacing[2];
|
const baseHorizontalPadding = theme.spacing[2];
|
||||||
|
|||||||
@@ -898,6 +898,7 @@ export default function SettingsScreen({ view }: SettingsScreenProps) {
|
|||||||
<View style={desktopStyles.contentPane}>
|
<View style={desktopStyles.contentPane}>
|
||||||
<ScreenHeader
|
<ScreenHeader
|
||||||
borderless={!detailHeader}
|
borderless={!detailHeader}
|
||||||
|
windowControlsPaddingRole="detailHeader"
|
||||||
left={
|
left={
|
||||||
detailHeader ? (
|
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,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
@@ -17,7 +17,12 @@ type RawWindowControlsPadding = {
|
|||||||
top: number;
|
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
|
// 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.
|
// 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 rawPadding = useRawWindowControlsPadding();
|
||||||
const sidebarClosed = !sidebarOpen;
|
const sidebarClosed = !sidebarOpen;
|
||||||
|
|
||||||
let left = 0;
|
const { left, right, top } = resolveWindowControlsPadding({
|
||||||
let right = 0;
|
role,
|
||||||
let top = 0;
|
rawPadding,
|
||||||
|
sidebarClosed,
|
||||||
if (role === "sidebar") {
|
explorerOpen,
|
||||||
left = rawPadding.left;
|
focusModeEnabled,
|
||||||
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;
|
|
||||||
}
|
|
||||||
|
|
||||||
return useMemo(() => ({ left, right, top }), [left, right, top]);
|
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,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user