返回 presentation-ai
box-static.tsx
1 "use client";
2
3 import { SlateElement, type SlateElementProps } from "platejs/static";
4 import { useId } from "react";
5
6 import { cn } from "@/lib/utils";
7 import { columnSizeVariant, getDefaultColumnSize } from "../../utils";
8 import {
9 ALTERNATING_BOX_COLUMN_GAP_PX,
10 ALTERNATING_BOX_MIN_ROW_HEIGHT_PX,
11 ALTERNATING_BOX_ROW_GAP_PX,
12 getAlternatingBoxPlacementCss,
13 useAlternatingBoxFitScale,
14 } from "../alternating-box-layout";
15 import { getDiagramFitFrameStyle } from "../diagram-fit";
16 import { getStaticDiagramJustifyClass } from "./diagram-static-utils";
17
18 export default function BoxGroupStatic(props: SlateElementProps) {
19 const alternatingLayoutId = useId();
20 const { columnSize: explicitColumnSize, boxType = "solid" } =
21 props.element as {
22 columnSize?: "sm" | "md" | "lg" | "xl";
23 boxType?: string;
24 alignment?: "left" | "center" | "right";
25 };
26 const element = props.element as {
27 alignment?: "left" | "center" | "right";
28 children?: unknown[];
29 orientation?: "horizontal" | "vertical";
30 };
31 const alignment = element.alignment ?? "center";
32 const columnSize =
33 explicitColumnSize ?? getDefaultColumnSize(element.children?.length ?? 0);
34 const orientation = element.orientation ?? "horizontal";
35 const isAlternating = boxType === "alternating";
36 const alternatingItemCount = Math.max(element.children?.length ?? 0, 1);
37 const alternatingPlacementCss = getAlternatingBoxPlacementCss(
38 alternatingLayoutId,
39 alternatingItemCount,
40 orientation,
41 );
42 const { containerRef, fitStyle, frameStyle, layoutRef } =
43 useAlternatingBoxFitScale<HTMLDivElement>(
44 alternatingItemCount,
45 orientation,
46 );
47
48 if (isAlternating) {
49 return (
50 <SlateElement {...props} className="mb-4">
51 <div
52 ref={containerRef}
53 className={cn(
54 "flex w-full overflow-visible",
55 getStaticDiagramJustifyClass(alignment),
56 )}
57 >
58 <div
59 style={{
60 ...frameStyle,
61 ...getDiagramFitFrameStyle(alignment),
62 }}
63 >
64 <style>{alternatingPlacementCss}</style>
65 <div ref={layoutRef} className="relative" style={fitStyle}>
66 <div
67 aria-hidden="true"
68 className="pointer-events-none absolute rounded-full"
69 data-decor="true"
70 style={{
71 background:
72 "color-mix(in oklch, var(--presentation-smart-layout, var(--presentation-primary)) 72%, transparent)",
73 boxShadow:
74 "0 0 0 1px color-mix(in oklch, var(--presentation-background) 65%, transparent)",
75 ...(orientation === "vertical"
76 ? {
77 bottom: 0,
78 left: "50%",
79 top: 0,
80 transform: "translateX(-50%)",
81 width: 2,
82 zIndex: 20,
83 }
84 : {
85 height: 2,
86 left: 0,
87 right: 0,
88 top: "50%",
89 transform: "translateY(-50%)",
90 zIndex: 20,
91 }),
92 }}
93 />
94 <div
95 className="relative z-10 grid items-stretch *:min-w-0"
96 data-alternating-box-layout={alternatingLayoutId}
97 style={{
98 columnGap: ALTERNATING_BOX_COLUMN_GAP_PX,
99 gridTemplateColumns:
100 orientation === "vertical"
101 ? "repeat(2, minmax(0, 1fr))"
102 : `repeat(${alternatingItemCount}, minmax(0, 1fr))`,
103 gridTemplateRows:
104 orientation === "vertical"
105 ? `repeat(${alternatingItemCount}, minmax(${ALTERNATING_BOX_MIN_ROW_HEIGHT_PX}px, auto))`
106 : `repeat(2, minmax(${ALTERNATING_BOX_MIN_ROW_HEIGHT_PX}px, 1fr))`,
107 rowGap: ALTERNATING_BOX_ROW_GAP_PX,
108 }}
109 >
110 {props.children}
111 </div>
112 </div>
113 </div>
114 </div>
115 </SlateElement>
116 );
117 }
118
119 return (
120 <SlateElement {...props} className="mb-4">
121 <div
122 className={cn(
123 "max-w-full items-stretch *:flex *:min-w-0",
124 columnSizeVariant({ columnSize }),
125 boxType === "joined" || boxType === "joined-icon" ? "" : "gap-4",
126 )}
127 >
128 {props.children}
129 </div>
130 </SlateElement>
131 );
132 }
133
133 lines Plain Text