Skip to content
Source
Sign inSign up

reserved-regions.ts

50 lines1.9 KB

1export interface ReservedRegion {
2 readonly kind: "division" | "occlusion";
3 readonly x: number;
4 readonly y: number;
5 readonly width: number;
6 readonly height: number;
7}
8​
9export interface NativeLayoutMetrics {
10 readonly width: number;
11 readonly height: number;
12 readonly hasHinge?: boolean;
13 readonly horizontalSizeClass: "compact" | "regular";
14 readonly verticalBarEdge: "none" | "left" | "right";
15 readonly safeArea: {
16 readonly top: number;
17 readonly bottom: number;
18 readonly left: number;
19 readonly right: number;
20 };
21 readonly reservedRegions: ReadonlyArray<ReservedRegion>;
22 /** How far above the bottom edge UIKit floats a 48pt bottom bar, level with its tab bar. iOS 27.1+. */
23 readonly bottomBarInset?: number;
25​
26export function deriveBottomControlInsets(metrics: NativeLayoutMetrics | null) {
27 if (!metrics) return { left: 0, right: 0, bottom: 0 };
28 let left = metrics.safeArea.left;
29 let right = metrics.width - metrics.safeArea.right;
30 let bottom = metrics.height;
31 for (const region of metrics.reservedRegions) {
32 const x = Math.max(left, region.x);
33 const endX = Math.min(right, region.x + region.width);
34 const y = Math.max(0, region.y);
35 const endY = Math.min(bottom, region.y + region.height);
36 if (endX <= x || endY <= y) continue;
37 if (region.kind === "division" && region.height >= metrics.height / 2) {
38 // A native column ending at a division is already split there. Only an
39 // interior division needs to move these controls into one side of the view.
40 if (x <= left || endX >= right) continue;
41 if (right - endX >= x - left) left = endX;
42 else right = x;
43 } else if (endY >= bottom - metrics.safeArea.bottom) {
44 if (region.width >= right - left) bottom = y;
45 else if (right - endX >= x - left) left = endX;
46 else right = x;
47 }
48 }
49 return { left, right: metrics.width - right, bottom: metrics.height - bottom };