Skip to content
File

Blob: src/shared/editor/presentation/document-layout.tsx

typescript78 lines
1import type { ReactNode, Ref } from "react";
2 
3export const PAGE_SHELL_CLASS = "relative animate-fade-in px-4 py-10 sm:px-8";
4export const PAGE_STAGE_CLASS = "mx-auto w-full max-w-[72rem]";
5export const PAGE_STAGE_TRACKS_CLASS =
6 "min-[1440px]:grid min-[1440px]:grid-cols-[minmax(0,48rem)_12rem] min-[1440px]:justify-center min-[1440px]:gap-x-8";
7export const PAGE_STAGE_WITH_TRACKS_CLASS = `${PAGE_STAGE_CLASS} ${PAGE_STAGE_TRACKS_CLASS}`;
8export const PAGE_BALANCED_STAGE_CLASS = "mx-auto w-full max-w-[76rem]";
9export const PAGE_BALANCED_STAGE_TRACKS_CLASS =
10 "min-[1280px]:grid min-[1280px]:grid-cols-[12rem_minmax(0,48rem)_12rem] min-[1280px]:justify-center min-[1280px]:gap-x-8";
11export const PAGE_BALANCED_STAGE_WITH_TRACKS_CLASS = `${PAGE_BALANCED_STAGE_CLASS} ${PAGE_BALANCED_STAGE_TRACKS_CLASS}`;
12export const PAGE_CONTENT_COLUMN_CLASS = "mx-auto min-w-0 max-w-3xl";
13export const PAGE_ICON_ROW_CLASS = "mb-4 flex min-h-9 items-center gap-3 pl-7";
14export const DOC_PAGE_MAIN_CLASS = "min-w-0";
15export const DOC_PAGE_INLINE_OUTLINE_CLASS = "mx-auto mt-8 w-full min-w-0 max-w-3xl min-[1440px]:hidden";
16export const DOC_PAGE_BALANCED_INLINE_OUTLINE_CLASS = "mx-auto mt-8 w-full min-w-0 max-w-3xl min-[1280px]:hidden";
17export const DOC_PAGE_BALANCED_INLINE_OUTLINE_BEFORE_CLASS =
18 "mx-auto mb-8 w-full min-w-0 max-w-3xl min-[1280px]:hidden";
19export const DOC_PAGE_RAIL_CLASS = "hidden min-w-0 min-[1440px]:block min-[1440px]:pt-[5.5rem]";
20export const DOC_PAGE_BALANCED_RAIL_CLASS =
21 "hidden min-w-0 min-[1280px]:col-start-3 min-[1280px]:block min-[1280px]:pt-[5.5rem]";
22export const DOC_PAGE_RAIL_INNER_CLASS = "min-[1440px]:sticky min-[1440px]:top-8";
23export const DOC_PAGE_BALANCED_RAIL_INNER_CLASS = "min-[1280px]:sticky min-[1280px]:top-8";
24 
25// Must mirror the literal `min-[1440px]:` used in the rail class strings above.
26// JS gating that depends on the rail layout being realized should read this constant.
27export const OUTLINE_RAIL_MEDIA_QUERY = "(min-width: 1440px)";
28 
29export type DocumentOutlineMode = "rail" | "inline";
30 
31interface DocumentFrameProps {
32 main: ReactNode;
33 rail?: ReactNode;
34 inlineOutline?: ReactNode;
35 railBalance?: "none" | "content";
36 railLabel?: string;
37 railRef?: Ref<HTMLDivElement>;
38}
39 
40export function DocumentFrame({
41 main,
42 rail,
43 inlineOutline,
44 railBalance = "none",
45 railLabel = "Document outline",
46 railRef,
47}: DocumentFrameProps) {
48 const hasRail = rail !== null && rail !== undefined;
49 const balanceContent = hasRail && railBalance === "content";
50 const stageClass = hasRail
51 ? balanceContent
52 ? PAGE_BALANCED_STAGE_WITH_TRACKS_CLASS
53 : PAGE_STAGE_WITH_TRACKS_CLASS
54 : PAGE_STAGE_CLASS;
55 const mainClass = balanceContent ? `${DOC_PAGE_MAIN_CLASS} min-[1280px]:col-start-2` : DOC_PAGE_MAIN_CLASS;
56 const inlineClass = balanceContent ? DOC_PAGE_BALANCED_INLINE_OUTLINE_CLASS : DOC_PAGE_INLINE_OUTLINE_CLASS;
57 const railClass = balanceContent ? DOC_PAGE_BALANCED_RAIL_CLASS : DOC_PAGE_RAIL_CLASS;
58 const railInnerClass = balanceContent ? DOC_PAGE_BALANCED_RAIL_INNER_CLASS : DOC_PAGE_RAIL_INNER_CLASS;
59 
60 return (
61 <div className={stageClass}>
62 <div className={mainClass}>
63 {main}
64 {inlineOutline !== null && inlineOutline !== undefined ? (
65 <div className={inlineClass}>{inlineOutline}</div>
66 ) : null}
67 </div>
68 {hasRail ? (
69 <aside className={railClass} aria-label={railLabel}>
70 <div ref={railRef} className={railInnerClass}>
71 {rail}
72 </div>
73 </aside>
74 ) : null}
75 </div>
76 );
77}