288 lines
11 KiB
TypeScript
288 lines
11 KiB
TypeScript
"use client";
|
|
|
|
import { useState, useEffect } from "react";
|
|
import { Input } from "@/components/ui/input";
|
|
import { Label } from "@/components/ui/label";
|
|
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@/components/ui/select";
|
|
import { Collapsible, CollapsibleContent, CollapsibleTrigger } from "@/components/ui/collapsible";
|
|
import { Palette, Type, Square, ChevronDown } from "lucide-react";
|
|
import { ComponentStyle } from "@/types/screen";
|
|
import { ColorPickerWithTransparent } from "./common/ColorPickerWithTransparent";
|
|
|
|
interface StyleEditorProps {
|
|
style: ComponentStyle;
|
|
onStyleChange: (style: ComponentStyle) => void;
|
|
className?: string;
|
|
}
|
|
|
|
export default function StyleEditor({ style, onStyleChange, className }: StyleEditorProps) {
|
|
const [localStyle, setLocalStyle] = useState<ComponentStyle>(style || {});
|
|
const [openSections, setOpenSections] = useState<Record<string, boolean>>({
|
|
border: false,
|
|
background: false,
|
|
text: false,
|
|
});
|
|
|
|
useEffect(() => {
|
|
setLocalStyle(style || {});
|
|
}, [style]);
|
|
|
|
const handleStyleChange = (property: keyof ComponentStyle, value: any) => {
|
|
const newStyle = { ...localStyle, [property]: value };
|
|
setLocalStyle(newStyle);
|
|
onStyleChange(newStyle);
|
|
};
|
|
|
|
const toggleSection = (section: string) => {
|
|
setOpenSections((prev) => ({
|
|
...prev,
|
|
[section]: !prev[section],
|
|
}));
|
|
};
|
|
|
|
return (
|
|
<div className={`space-y-2 ${className}`}>
|
|
{/* 테두리 섹션 */}
|
|
<Collapsible open={openSections.border} onOpenChange={() => toggleSection("border")}>
|
|
<CollapsibleTrigger className="flex w-full items-center justify-between rounded-md bg-gray-50 px-2 py-1.5 hover:bg-gray-100">
|
|
<div className="flex items-center gap-1.5">
|
|
<Square className="text-primary h-3 w-3" />
|
|
<span className="text-xs font-medium">테두리</span>
|
|
</div>
|
|
<ChevronDown
|
|
className={`h-3 w-3 text-gray-500 transition-transform ${openSections.border ? "rotate-180" : ""}`}
|
|
/>
|
|
</CollapsibleTrigger>
|
|
<CollapsibleContent className="pt-2">
|
|
<div className="space-y-2 pl-1">
|
|
<div className="grid grid-cols-2 gap-2">
|
|
<div className="space-y-1">
|
|
<Label htmlFor="borderWidth" className="text-xs font-medium">
|
|
두께
|
|
</Label>
|
|
<Input
|
|
id="borderWidth"
|
|
type="text"
|
|
placeholder="1px"
|
|
value={localStyle.borderWidth || ""}
|
|
onChange={(e) => handleStyleChange("borderWidth", e.target.value)}
|
|
className="h-6 w-full px-2 py-0 text-xs"
|
|
/>
|
|
</div>
|
|
<div className="space-y-1">
|
|
<Label htmlFor="borderStyle" className="text-xs font-medium">
|
|
스타일
|
|
</Label>
|
|
<Select
|
|
value={localStyle.borderStyle || "solid"}
|
|
onValueChange={(value) => handleStyleChange("borderStyle", value)}
|
|
>
|
|
<SelectTrigger className="h-6 w-full px-2 py-0 text-xs">
|
|
<SelectValue />
|
|
</SelectTrigger>
|
|
<SelectContent>
|
|
<SelectItem value="solid" className="text-xs">
|
|
실선
|
|
</SelectItem>
|
|
<SelectItem value="dashed" className="text-xs">
|
|
파선
|
|
</SelectItem>
|
|
<SelectItem value="dotted" className="text-xs">
|
|
점선
|
|
</SelectItem>
|
|
<SelectItem value="none" className="text-xs">
|
|
없음
|
|
</SelectItem>
|
|
</SelectContent>
|
|
</Select>
|
|
</div>
|
|
</div>
|
|
|
|
<div className="space-y-2">
|
|
<div className="space-y-1">
|
|
<Label htmlFor="borderColor" className="text-xs font-medium">
|
|
색상
|
|
</Label>
|
|
<ColorPickerWithTransparent
|
|
id="borderColor"
|
|
value={localStyle.borderColor}
|
|
onChange={(value) => handleStyleChange("borderColor", value)}
|
|
defaultColor="#e5e7eb"
|
|
placeholder="#e5e7eb"
|
|
/>
|
|
</div>
|
|
<div className="space-y-1">
|
|
<Label htmlFor="borderRadius" className="text-xs font-medium">
|
|
모서리
|
|
</Label>
|
|
<Input
|
|
id="borderRadius"
|
|
type="text"
|
|
placeholder="5px"
|
|
value={localStyle.borderRadius || ""}
|
|
onChange={(e) => handleStyleChange("borderRadius", e.target.value)}
|
|
className="h-6 w-full px-2 py-0 text-xs"
|
|
/>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</CollapsibleContent>
|
|
</Collapsible>
|
|
|
|
{/* 배경 섹션 */}
|
|
<Collapsible open={openSections.background} onOpenChange={() => toggleSection("background")}>
|
|
<CollapsibleTrigger className="flex w-full items-center justify-between rounded-md bg-gray-50 px-2 py-1.5 hover:bg-gray-100">
|
|
<div className="flex items-center gap-1.5">
|
|
<Palette className="text-primary h-3 w-3" />
|
|
<span className="text-xs font-medium">배경</span>
|
|
</div>
|
|
<ChevronDown
|
|
className={`h-3 w-3 text-gray-500 transition-transform ${openSections.background ? "rotate-180" : ""}`}
|
|
/>
|
|
</CollapsibleTrigger>
|
|
<CollapsibleContent className="pt-2">
|
|
<div className="space-y-2 pl-1">
|
|
<div className="space-y-1">
|
|
<Label htmlFor="backgroundColor" className="text-xs font-medium">
|
|
색상
|
|
</Label>
|
|
<ColorPickerWithTransparent
|
|
id="backgroundColor"
|
|
value={localStyle.backgroundColor}
|
|
onChange={(value) => handleStyleChange("backgroundColor", value)}
|
|
defaultColor="#ffffff"
|
|
placeholder="#ffffff"
|
|
/>
|
|
</div>
|
|
|
|
<div className="space-y-1">
|
|
<Label htmlFor="backgroundImage" className="text-xs font-medium">
|
|
배경 이미지 (CSS)
|
|
</Label>
|
|
<Input
|
|
id="backgroundImage"
|
|
type="text"
|
|
placeholder="url('image.jpg')"
|
|
value={localStyle.backgroundImage || ""}
|
|
onChange={(e) => handleStyleChange("backgroundImage", e.target.value)}
|
|
className="h-6 w-full px-2 py-0 text-xs"
|
|
/>
|
|
<p className="text-muted-foreground text-[10px]">위젯 배경 꾸미기용 (고급 사용자 전용)</p>
|
|
</div>
|
|
</div>
|
|
</CollapsibleContent>
|
|
</Collapsible>
|
|
|
|
{/* 텍스트 섹션 */}
|
|
<Collapsible open={openSections.text} onOpenChange={() => toggleSection("text")}>
|
|
<CollapsibleTrigger className="flex w-full items-center justify-between rounded-md bg-gray-50 px-2 py-1.5 hover:bg-gray-100">
|
|
<div className="flex items-center gap-1.5">
|
|
<Type className="text-primary h-3 w-3" />
|
|
<span className="text-xs font-medium">텍스트</span>
|
|
</div>
|
|
<ChevronDown
|
|
className={`h-3 w-3 text-gray-500 transition-transform ${openSections.text ? "rotate-180" : ""}`}
|
|
/>
|
|
</CollapsibleTrigger>
|
|
<CollapsibleContent className="pt-2">
|
|
<div className="space-y-2 pl-1">
|
|
<div className="space-y-2">
|
|
<div className="space-y-1">
|
|
<Label htmlFor="color" className="text-xs font-medium">
|
|
색상
|
|
</Label>
|
|
<ColorPickerWithTransparent
|
|
id="color"
|
|
value={localStyle.color}
|
|
onChange={(value) => handleStyleChange("color", value)}
|
|
defaultColor="#000000"
|
|
placeholder="#000000"
|
|
/>
|
|
</div>
|
|
<div className="space-y-1">
|
|
<Label htmlFor="fontSize" className="text-xs font-medium">
|
|
크기
|
|
</Label>
|
|
<Input
|
|
id="fontSize"
|
|
type="text"
|
|
placeholder="14px"
|
|
value={localStyle.fontSize || ""}
|
|
onChange={(e) => handleStyleChange("fontSize", e.target.value)}
|
|
className="h-6 w-full px-2 py-0 text-xs"
|
|
/>
|
|
</div>
|
|
</div>
|
|
|
|
<div className="grid grid-cols-2 gap-2">
|
|
<div className="space-y-1">
|
|
<Label htmlFor="fontWeight" className="text-xs font-medium">
|
|
굵기
|
|
</Label>
|
|
<Select
|
|
value={localStyle.fontWeight || "normal"}
|
|
onValueChange={(value) => handleStyleChange("fontWeight", value)}
|
|
>
|
|
<SelectTrigger className="h-6 w-full px-2 py-0 text-xs">
|
|
<SelectValue />
|
|
</SelectTrigger>
|
|
<SelectContent>
|
|
<SelectItem value="normal" className="text-xs">
|
|
보통
|
|
</SelectItem>
|
|
<SelectItem value="bold" className="text-xs">
|
|
굵게
|
|
</SelectItem>
|
|
<SelectItem value="100" className="text-xs">
|
|
100
|
|
</SelectItem>
|
|
<SelectItem value="400" className="text-xs">
|
|
400
|
|
</SelectItem>
|
|
<SelectItem value="500" className="text-xs">
|
|
500
|
|
</SelectItem>
|
|
<SelectItem value="600" className="text-xs">
|
|
600
|
|
</SelectItem>
|
|
<SelectItem value="700" className="text-xs">
|
|
700
|
|
</SelectItem>
|
|
</SelectContent>
|
|
</Select>
|
|
</div>
|
|
<div className="space-y-1">
|
|
<Label htmlFor="textAlign" className="text-xs font-medium">
|
|
정렬
|
|
</Label>
|
|
<Select
|
|
value={localStyle.textAlign || "left"}
|
|
onValueChange={(value) => handleStyleChange("textAlign", value)}
|
|
>
|
|
<SelectTrigger className="h-6 w-full px-2 py-0 text-xs">
|
|
<SelectValue />
|
|
</SelectTrigger>
|
|
<SelectContent>
|
|
<SelectItem value="left" className="text-xs">
|
|
왼쪽
|
|
</SelectItem>
|
|
<SelectItem value="center" className="text-xs">
|
|
가운데
|
|
</SelectItem>
|
|
<SelectItem value="right" className="text-xs">
|
|
오른쪽
|
|
</SelectItem>
|
|
<SelectItem value="justify" className="text-xs">
|
|
양쪽
|
|
</SelectItem>
|
|
</SelectContent>
|
|
</Select>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</CollapsibleContent>
|
|
</Collapsible>
|
|
</div>
|
|
);
|
|
}
|