返回 presentation-ai
PresentationRoot.tsx
根目录 / src / components / notebook / presentation / editor / components / PresentationRoot.tsx
1 "use client";
2
3 import { useMemo, type ReactNode } from "react";
4
5 import { FontLoader } from "@/components/plate/utils/font-loader";
6 import { cn } from "@/lib/utils";
7 import { type PlateSlide } from "../../utils/parser";
8 import { PresentationRenderProvider } from "../context/PresentationRenderContext";
9 import ImageSlide from "../custom-elements/image-slide/ImageSlide";
10 import ImageSlideStatic from "../custom-elements/image-slide/ImageSlideStatic";
11 import {
12 getLayoutClasses,
13 getPresentingClasses,
14 getSlideCustomStyles,
15 getSlideFormatStyles,
16 } from "../utils/slide-format-styles";
17
18 interface PresentationRootProps {
19 className?: string;
20 initialContent?: PlateSlide;
21 fontsToLoad: string[];
22 isPresenting: boolean;
23 readOnly: boolean;
24 isStatic?: boolean;
25 onActivateSlide?: (slideId: string) => void;
26 children: ReactNode;
27 }
28
29 export function PresentationRoot({
30 className,
31 initialContent,
32 fontsToLoad,
33 isPresenting,
34 readOnly,
35 isStatic = false,
36 onActivateSlide,
37 children,
38 }: PresentationRootProps) {
39 // Calculate format styles using extracted utility
40 const styleByFormat = useMemo(
41 () =>
42 getSlideFormatStyles(
43 initialContent?.formatCategory,
44 initialContent?.width,
45 initialContent?.aspectRatio,
46 ),
47 [
48 initialContent?.formatCategory,
49 initialContent?.aspectRatio,
50 initialContent?.width,
51 ],
52 );
53
54 // Calculate custom inline styles
55 const customStyles = useMemo(
56 () => getSlideCustomStyles(initialContent, isPresenting, styleByFormat),
57 [initialContent, isPresenting, styleByFormat],
58 );
59
60 return (
61 <div
62 id={!isStatic ? `presentation-root-${initialContent?.id}` : undefined}
63 className={cn(
64 "group/overflow-border flex",
65 "scrollbar-thin p-0 scrollbar-thumb-muted-foreground/20 scrollbar-track-transparent hover:scrollbar-thumb-muted-foreground/30",
66 "relative",
67 "bg-(--presentation-background)",
68 "text-(--presentation-text)",
69 "focus-within:ring-opacity-50 focus-within:ring-2 focus-within:ring-primary",
70 getPresentingClasses(isPresenting, initialContent?.formatCategory),
71 className,
72 getLayoutClasses(initialContent?.layoutType),
73 )}
74 style={customStyles}
75 data-is-presenting={readOnly && isPresenting ? "true" : "false"}
76 data-slide-content="true"
77 onClickCapture={() => {
78 if (readOnly && initialContent?.id)
79 onActivateSlide?.(initialContent.id);
80 }}
81 >
82 <PresentationRenderProvider
83 layoutType={initialContent?.layoutType}
84 isStatic={isStatic}
85 >
86 <FontLoader fontsToLoad={fontsToLoad} />
87 {initialContent?.isImageSlide && initialContent.rootImage ? (
88 readOnly ? (
89 <ImageSlideStatic
90 image={initialContent.rootImage}
91 slideId={initialContent.id}
92 />
93 ) : (
94 <ImageSlide
95 image={initialContent.rootImage}
96 slideId={initialContent.id}
97 />
98 )
99 ) : (
100 children
101 )}
102 </PresentationRenderProvider>
103 </div>
104 );
105 }
106
106 lines Plain Text