Skip to content
Source
Sign inSign up
Open History to see the last commit

DiffPanelShell.tsx

121 lines4.2 KB

1import type { ReactNode } from "react";
2​
3import { isElectron } from "~/env";
4import { cn } from "~/lib/utils";
5​
6import { Skeleton } from "./ui/skeleton";
7​
8export type DiffPanelMode = "inline" | "sheet" | "sidebar" | "embedded";
9​
10function getDiffPanelHeaderRowClassName(mode: DiffPanelMode) {
11 const shouldUseDragRegion = isElectron && mode !== "sheet" && mode !== "embedded";
12 return cn(
13 // diff-toolbar: DiffPanel folds its actions into a menu in narrow panes.
14 "@container/diff-toolbar flex items-center justify-between gap-2",
15 mode === "embedded" ? "px-2" : "px-4",
16 shouldUseDragRegion
17 ? "drag-region h-[var(--workspace-topbar-height)] border-b border-border wco:pr-(--workspace-native-controls-inset)"
18 : "flex h-10 min-h-10 shrink-0 items-center border-b border-border bg-background in-data-[preview-panel-mode=inline]:mb-3 in-data-[preview-panel-mode=inline]:h-7 in-data-[preview-panel-mode=inline]:min-h-7 in-data-[preview-panel-mode=inline]:border-b-transparent",
19 );
21​
22export function DiffPanelShell(props: {
23 mode: DiffPanelMode;
24 /** Null when a panel tab bar carries the header (PanelTabBreadcrumb, PanelTabActions). */
25 header: ReactNode | null;
26 children: ReactNode;
27}) {
28 const shouldUseDragRegion = isElectron && props.mode !== "sheet" && props.mode !== "embedded";
29​
30 return (
31 <div
32 className={cn(
33 // Under a pane's tab row the diff reaches up to the pane's top edge
34 // (--surface-top-inset, index.css).
35 "flex h-[calc(100%+var(--surface-top-inset,0px))] min-w-0 flex-col bg-background",
36 props.mode === "inline"
37 ? "w-[42vw] min-w-[360px] max-w-[560px] shrink-0 border-l border-border"
38 : "w-full",
39 )}
40 data-surface-reaches-under-header={props.header === null ? "" : undefined}
41 >
42 {props.header === null ? null : shouldUseDragRegion ? (
43 <div className={getDiffPanelHeaderRowClassName(props.mode)}>{props.header}</div>
44 ) : (
45 <div className={getDiffPanelHeaderRowClassName(props.mode)} data-surface-subheader>
46 {props.header}
47 </div>
48 )}
49 {props.children}
50 </div>
51 );
53​
54export function DiffFileHeaderSkeleton({
55 titleWidth,
56}: {
57 titleWidth: "short" | "medium" | "long";
58}) {
59 return (
60 <div className="flex h-8 items-center gap-2 px-2 pr-3">
61 <div className="flex size-5 shrink-0 items-center justify-center">
62 <Skeleton className="size-2.5" />
63 </div>
64 <Skeleton className="size-5 shrink-0" />
65 <Skeleton
66 shape="pill"
67 className={
68 titleWidth === "short"
69 ? "h-3 w-2/5 max-w-52"
70 : titleWidth === "medium"
71 ? "h-3 w-1/2 max-w-64"
72 : "h-3 w-3/5 max-w-72"
73 }
74 />
75 <div className="ml-auto flex shrink-0 items-center gap-2">
76 <Skeleton shape="pill" className="h-3 w-5" />
77 <Skeleton shape="pill" className="h-3 w-5" />
78 </div>
79 </div>
80 );
82​
83function DiffCodeLineSkeleton({ width }: { width: "short" | "medium" | "long" }) {
84 return (
85 <div className="flex items-center gap-3">
86 <Skeleton shape="pill" className="h-2.5 w-5 shrink-0" />
87 <Skeleton
88 shape="pill"
89 className={
90 width === "short" ? "h-2.5 w-3/5" : width === "medium" ? "h-2.5 w-2/3" : "h-2.5 w-4/5"
91 }
92 />
93 </div>
94 );
96​
97export function DiffPanelLoadingState(props: { label: string }) {
98 return (
99 <div
100 className="min-h-0 flex-1 overflow-hidden bg-background pt-under-header"
101 role="status"
102 aria-live="polite"
103 aria-label={props.label}
104 >
105 <DiffFileHeaderSkeleton titleWidth="medium" />
106 <div className="flex h-6 items-center gap-2 px-2 pr-3">
107 <div className="h-px flex-1 bg-border" />
108 <Skeleton shape="pill" className="h-2.5 w-24" />
109 <div className="h-px flex-1 bg-border" />
110 </div>
111 <div className="space-y-2 px-3 py-2">
112 <DiffCodeLineSkeleton width="medium" />
113 <DiffCodeLineSkeleton width="long" />
114 <DiffCodeLineSkeleton width="short" />
115 </div>
116 <DiffFileHeaderSkeleton titleWidth="short" />
117 <DiffFileHeaderSkeleton titleWidth="long" />
118 <span className="sr-only">{props.label}</span>
119 </div>
120 );