返回 presentation-ai
slide-format-styles.ts
根目录 / src / components / notebook / presentation / editor / utils / slide-format-styles.ts
1 import type React from "react";
2
3 import {
4 calculateHeightFromRatio,
5 getSlideBaseWidth,
6 } from "@/config/slideFormats";
7 import { type PlateSlide } from "../../utils/parser";
8 import { getTypographyCSSVariables } from "./typography";
9
10 /**
11 * Get layout flex direction classes based on layout type.
12 */
13 export function getLayoutClasses(layoutType: PlateSlide["layoutType"]): string {
14 switch (layoutType) {
15 case "right":
16 return "flex-row";
17 case "vertical":
18 return "flex-col-reverse justify-end";
19 case "left":
20 return "flex-row-reverse";
21 case "background":
22 return "flex-col";
23 case "none":
24 case undefined:
25 return "flex-col";
26 default:
27 return "flex-col";
28 }
29 }
30
31 /**
32 * Get present mode specific classes.
33 * @param isPresenting - Whether in present mode
34 * @param formatCategory - The slide format category (affects how width is handled)
35 */
36 export function getPresentingClasses(
37 isPresenting: boolean,
38 formatCategory?: PlateSlide["formatCategory"],
39 ): string {
40 if (!isPresenting) {
41 return "min-h-125 border border-(--presentation-accent)";
42 }
43
44 // For social format, don't force w-full so the aspect ratio is preserved
45 // The width will come from the slide's defined dimensions (via styleByFormat)
46 if (formatCategory === "social") {
47 return "border-0";
48 }
49
50 // For presentation/fluid formats, take full width.
51 // On larger screens (md+), force min-h-dvh so slide fills the viewport.
52 // On phones (below md), maintain a 16:9 aspect ratio so the slide acts
53 // like `object-fit: contain` and preserves its layout.
54 return "w-full border-0 md:min-h-dvh max-md:aspect-video";
55 }
56
57 /**
58 * Calculate slide format styles (width/height) for the presentation root.
59 */
60 export function getSlideFormatStyles(
61 category: PlateSlide["formatCategory"],
62 width: PlateSlide["width"],
63 aspectRatio: PlateSlide["aspectRatio"],
64 ): React.CSSProperties {
65 const slideWidthSize = width ?? "M";
66 const formatCategory = category ?? "presentation";
67 const aspect = aspectRatio ?? { type: "fluid", value: "" };
68
69 const baseWidth = getSlideBaseWidth(
70 formatCategory,
71 slideWidthSize as "S" | "M" | "L",
72 aspect,
73 );
74
75 const heightConfig = calculateHeightFromRatio(baseWidth, aspect);
76
77 return {
78 ...(aspect.type !== "fluid" ? { width: `${baseWidth}px` } : {}),
79 ...(heightConfig.minHeightCSS
80 ? { minHeight: heightConfig.minHeightCSS }
81 : {}),
82 };
83 }
84
85 /**
86 * Get custom inline styles for the slide based on content settings.
87 */
88 export function getSlideCustomStyles(
89 initialContent: PlateSlide | undefined,
90 isPresenting: boolean,
91 styleByFormat: React.CSSProperties,
92 ): React.CSSProperties {
93 // Determine if we should apply format dimensions in present mode
94 // For social format, we WANT strict dimensions to preserve aspect ratio
95 // For presentation format, we rely on the CSS classes (min-h-dvh! w-full)
96 const shouldApplyDimensions =
97 !isPresenting || initialContent?.formatCategory === "social";
98
99 return {
100 borderRadius: isPresenting
101 ? "0"
102 : "var(--presentation-slide-border-radius)",
103 boxShadow: isPresenting ? "none" : "var(--presentation-slide-shadow)",
104
105 // Apply dimensions if not presenting OR if it's social format
106 ...(shouldApplyDimensions && styleByFormat),
107
108 // Background color override
109 ...(initialContent?.bgColor && {
110 "--presentation-background": initialContent.bgColor as string,
111 backgroundColor: initialContent.bgColor as string,
112 }),
113 // Background image for background layout
114 backgroundImage:
115 initialContent?.layoutType === "background" &&
116 initialContent?.rootImage?.url
117 ? `url(${initialContent.rootImage.url})`
118 : undefined,
119 backgroundSize: "cover",
120 backgroundPosition: "center",
121 backgroundRepeat: "no-repeat",
122 // Typography
123 ...getTypographyCSSVariables(initialContent?.fontSize),
124 ...(initialContent?.fontFamily?.body && {
125 "--presentation-body-font": initialContent.fontFamily.body as string,
126 }),
127 ...(initialContent?.fontFamily?.heading && {
128 "--presentation-heading-font": initialContent.fontFamily
129 .heading as string,
130 }),
131 fontFamily: "var(--presentation-body-font)",
132 } as React.CSSProperties;
133 }
134
134 lines TYPESCRIPT