返回 presentation-ai
DesignStep.tsx
1 "use client";
2
3 import { Shapes } from "lucide-react";
4 import { useWatch, type Control, type UseFormSetValue } from "react-hook-form";
5
6 import { cn } from "@/lib/utils";
7 import { type ThemeFormValues } from "../types";
8 import { type DesignStyle } from "./create-theme-types";
9
10 const designStyles: {
11 id: DesignStyle;
12 name: string;
13 description: string;
14 preview: string;
15 innerPreview: string;
16 }[] = [
17 {
18 id: "standard",
19 name: "Standard",
20 description: "Small border radius with minimal shadow",
21 preview: "rounded-lg shadow",
22 innerPreview: "rounded-md shadow",
23 },
24 {
25 id: "flat",
26 name: "Flat",
27 description: "No shadow and no border radius",
28 preview: "rounded-none shadow-none",
29 innerPreview: "rounded-none shadow-none",
30 },
31 {
32 id: "outline",
33 name: "Outline",
34 description: "Outline-like effect using shadow",
35 preview: "rounded-none shadow-[0_0_0_2px_rgba(37,99,235,1)]",
36 innerPreview: "rounded-none shadow-[0_0_0_2px_rgba(37,99,235,1)]",
37 },
38 {
39 id: "blocky",
40 name: "Blocky",
41 description: "3D effect using unblurred box shadow",
42 preview: "rounded-none shadow-[4px_4px_0_0_rgba(0,0,0,0.2)]",
43 innerPreview: "rounded-none shadow-[3px_3px_0_0_rgba(0,0,0,0.2)]",
44 },
45 {
46 id: "rounded",
47 name: "Rounded",
48 description: "Just rounded with gentle shadows",
49 preview: "rounded-3xl shadow",
50 innerPreview: "rounded-full shadow",
51 },
52 ];
53
54 interface DesignStepProps {
55 setValue: UseFormSetValue<ThemeFormValues>;
56 control: Control<ThemeFormValues>;
57 }
58
59 export function DesignStep({ setValue, control }: DesignStepProps) {
60 // Use useWatch instead of watch() to properly subscribe to form changes
61 const currentCardRadius = useWatch({ control, name: "borderRadius.card" });
62 const currentCardShadow = useWatch({ control, name: "shadows.card" });
63
64 // Helper to determine selected style based on current values
65 const getSelectedStyle = () => {
66 // Flat: no radius, no shadow
67 if (currentCardRadius === "0" && currentCardShadow === "none")
68 return "flat";
69
70 // Outline: no radius, outline shadow
71 if (currentCardRadius === "0" && currentCardShadow?.includes("0 0 0 2px"))
72 return "outline";
73
74 // Blocky: no radius, hard 3D shadow
75 if (currentCardRadius === "0" && currentCardShadow?.includes("4px 4px 0 0"))
76 return "blocky";
77
78 // Rounded: large radius
79 if (currentCardRadius === "1.5rem") return "rounded";
80
81 // Standard: small radius with minimal shadow
82 if (currentCardRadius === "0.5rem") return "standard";
83
84 return "standard";
85 };
86
87 const selectedStyle = getSelectedStyle();
88
89 const handleStyleSelect = (styleId: DesignStyle) => {
90 const options = { shouldDirty: true };
91 // Map style ID to actual theme values
92 switch (styleId) {
93 case "standard":
94 // Small border radius with minimal shadow
95 setValue("borderRadius.card", "0.5rem", options);
96 setValue("borderRadius.slide", "0.5rem", options);
97 setValue("borderRadius.button", "0.375rem", options);
98 setValue("shadows.card", "0 1px 3px rgba(0,0,0,0.05)", options);
99 setValue("shadows.button", "0 1px 2px rgba(0,0,0,0.03)", options);
100 setValue("shadows.slide", "0 2px 4px rgba(0,0,0,0.04)", options);
101 break;
102 case "flat":
103 // No shadow and no border radius
104 setValue("borderRadius.card", "0", options);
105 setValue("borderRadius.slide", "0", options);
106 setValue("borderRadius.button", "0", options);
107 setValue("shadows.card", "none", options);
108 setValue("shadows.button", "none", options);
109 setValue("shadows.slide", "none", options);
110 break;
111 case "outline":
112 // Outline-like effect using shadow
113 setValue("borderRadius.card", "0", options);
114 setValue("borderRadius.slide", "0", options);
115 setValue("borderRadius.button", "0", options);
116 setValue("shadows.card", "0 0 0 2px currentColor", options);
117 setValue("shadows.button", "0 0 0 2px currentColor", options);
118 setValue("shadows.slide", "0 0 0 2px currentColor", options);
119 break;
120 case "blocky":
121 // 3D effect using box shadow (not blurred)
122 setValue("borderRadius.card", "0", options);
123 setValue("borderRadius.slide", "0", options);
124 setValue("borderRadius.button", "0", options);
125 setValue("shadows.card", "4px 4px 0 0 rgba(0,0,0,0.2)", options);
126 setValue("shadows.button", "3px 3px 0 0 rgba(0,0,0,0.2)", options);
127 setValue("shadows.slide", "6px 6px 0 0 rgba(0,0,0,0.15)", options);
128 break;
129 case "rounded":
130 // Just rounded
131 setValue("borderRadius.card", "1.5rem", options);
132 setValue("borderRadius.slide", "2rem", options);
133 setValue("borderRadius.button", "9999px", options);
134 setValue("shadows.card", "0 1px 3px rgba(0,0,0,0.05)", options);
135 setValue("shadows.button", "0 1px 2px rgba(0,0,0,0.03)", options);
136 setValue("shadows.slide", "0 2px 4px rgba(0,0,0,0.04)", options);
137 break;
138 default:
139 // Default to standard
140 setValue("borderRadius.card", "0.5rem", options);
141 setValue("borderRadius.slide", "0.5rem", options);
142 setValue("borderRadius.button", "0.375rem", options);
143 setValue("shadows.card", "0 1px 3px rgba(0,0,0,0.05)", options);
144 setValue("shadows.button", "0 1px 2px rgba(0,0,0,0.03)", options);
145 setValue("shadows.slide", "0 2px 4px rgba(0,0,0,0.04)", options);
146 break;
147 }
148 };
149
150 return (
151 <div className="flex h-full flex-col">
152 <div className="border-b border-border p-6">
153 <div className="text-center">
154 <Shapes className="mx-auto mb-4 size-12 text-blue-600" />
155 <h3 className="mb-2 text-xl font-semibold text-foreground">
156 Choose a style for your content
157 </h3>
158 </div>
159 </div>
160
161 <div className="flex-1 overflow-y-auto p-6">
162 <div className="grid grid-cols-1 gap-4">
163 {designStyles.map((style) => (
164 <button
165 type="button"
166 key={style.id}
167 onClick={() => handleStyleSelect(style.id)}
168 className={cn(
169 "rounded-xl border-2 bg-card p-6 text-left transition-all hover:border-muted-foreground/50",
170 selectedStyle === style.id
171 ? "border-blue-600 bg-blue-50 dark:bg-blue-950/20"
172 : "border-border",
173 )}
174 >
175 <div className="flex items-center gap-6">
176 {/* Preview box */}
177 <div
178 className={cn(
179 "flex h-16 w-24 items-center justify-center border border-border/50 bg-background",
180 style.preview,
181 )}
182 >
183 <div
184 className={cn("h-8 w-12 bg-muted", style.innerPreview)}
185 />
186 </div>
187
188 {/* Style info */}
189 <div className="flex-1">
190 <h4 className="mb-1 text-lg font-semibold text-foreground">
191 {style.name}
192 </h4>
193 <p className="text-sm text-muted-foreground">
194 {style.description}
195 </p>
196 </div>
197 </div>
198 </button>
199 ))}
200 </div>
201 </div>
202 </div>
203 );
204 }
205
205 lines Plain Text