2025-10-13 18:28:03 +09:00
|
|
|
"use client";
|
|
|
|
|
|
|
|
|
|
import React from "react";
|
|
|
|
|
import { Label } from "@/components/ui/label";
|
|
|
|
|
import { Input } from "@/components/ui/input";
|
|
|
|
|
import { Button } from "@/components/ui/button";
|
|
|
|
|
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@/components/ui/select";
|
|
|
|
|
import { Separator } from "@/components/ui/separator";
|
|
|
|
|
import { LayoutRow } from "@/types/grid-system";
|
|
|
|
|
import { GapPreset, GAP_PRESETS } from "@/lib/constants/columnSpans";
|
|
|
|
|
import { Rows, AlignHorizontalJustifyCenter, AlignVerticalJustifyCenter } from "lucide-react";
|
|
|
|
|
|
|
|
|
|
interface RowSettingsPanelProps {
|
|
|
|
|
row: LayoutRow;
|
|
|
|
|
onUpdateRow: (updates: Partial<LayoutRow>) => void;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
export const RowSettingsPanel: React.FC<RowSettingsPanelProps> = ({ row, onUpdateRow }) => {
|
|
|
|
|
return (
|
|
|
|
|
<div className="space-y-6 p-4">
|
|
|
|
|
{/* 헤더 */}
|
|
|
|
|
<div className="flex items-center gap-2">
|
|
|
|
|
<Rows className="text-primary h-5 w-5" />
|
|
|
|
|
<h3 className="text-lg font-semibold">행 설정</h3>
|
|
|
|
|
<span className="text-sm text-gray-500">#{row.rowIndex + 1}</span>
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
<Separator />
|
|
|
|
|
|
|
|
|
|
{/* 높이 설정 */}
|
|
|
|
|
<div className="space-y-3">
|
|
|
|
|
<Label className="text-sm font-medium">행 높이</Label>
|
|
|
|
|
<Select
|
|
|
|
|
value={row.height}
|
|
|
|
|
onValueChange={(value: "auto" | "fixed" | "min" | "max") => onUpdateRow({ height: value })}
|
|
|
|
|
>
|
|
|
|
|
<SelectTrigger>
|
|
|
|
|
<SelectValue />
|
|
|
|
|
</SelectTrigger>
|
|
|
|
|
<SelectContent>
|
|
|
|
|
<SelectItem value="auto">자동 (컨텐츠에 맞춤)</SelectItem>
|
|
|
|
|
<SelectItem value="fixed">고정 높이</SelectItem>
|
|
|
|
|
<SelectItem value="min">최소 높이</SelectItem>
|
|
|
|
|
<SelectItem value="max">최대 높이</SelectItem>
|
|
|
|
|
</SelectContent>
|
|
|
|
|
</Select>
|
|
|
|
|
|
|
|
|
|
{/* 고정 높이 입력 */}
|
|
|
|
|
{row.height === "fixed" && (
|
|
|
|
|
<div>
|
|
|
|
|
<Label className="text-xs text-gray-500">높이 (px)</Label>
|
|
|
|
|
<Input
|
|
|
|
|
type="number"
|
|
|
|
|
value={row.fixedHeight || 100}
|
|
|
|
|
onChange={(e) => onUpdateRow({ fixedHeight: parseInt(e.target.value) })}
|
|
|
|
|
className="mt-1"
|
|
|
|
|
placeholder="100"
|
|
|
|
|
min={50}
|
|
|
|
|
max={1000}
|
|
|
|
|
/>
|
|
|
|
|
</div>
|
|
|
|
|
)}
|
|
|
|
|
|
|
|
|
|
{/* 최소 높이 입력 */}
|
|
|
|
|
{row.height === "min" && (
|
|
|
|
|
<div>
|
|
|
|
|
<Label className="text-xs text-gray-500">최소 높이 (px)</Label>
|
|
|
|
|
<Input
|
|
|
|
|
type="number"
|
|
|
|
|
value={row.minHeight || 50}
|
|
|
|
|
onChange={(e) => onUpdateRow({ minHeight: parseInt(e.target.value) })}
|
|
|
|
|
className="mt-1"
|
|
|
|
|
placeholder="50"
|
|
|
|
|
min={0}
|
|
|
|
|
max={1000}
|
|
|
|
|
/>
|
|
|
|
|
</div>
|
|
|
|
|
)}
|
|
|
|
|
|
|
|
|
|
{/* 최대 높이 입력 */}
|
|
|
|
|
{row.height === "max" && (
|
|
|
|
|
<div>
|
|
|
|
|
<Label className="text-xs text-gray-500">최대 높이 (px)</Label>
|
|
|
|
|
<Input
|
|
|
|
|
type="number"
|
|
|
|
|
value={row.maxHeight || 500}
|
|
|
|
|
onChange={(e) => onUpdateRow({ maxHeight: parseInt(e.target.value) })}
|
|
|
|
|
className="mt-1"
|
|
|
|
|
placeholder="500"
|
|
|
|
|
min={0}
|
|
|
|
|
max={2000}
|
|
|
|
|
/>
|
|
|
|
|
</div>
|
|
|
|
|
)}
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
<Separator />
|
|
|
|
|
|
|
|
|
|
{/* 간격 설정 */}
|
|
|
|
|
<div className="space-y-3">
|
|
|
|
|
<Label className="text-sm font-medium">컴포넌트 간격</Label>
|
|
|
|
|
<div className="grid grid-cols-3 gap-2">
|
|
|
|
|
{(Object.keys(GAP_PRESETS) as GapPreset[]).map((preset) => (
|
|
|
|
|
<Button
|
|
|
|
|
key={preset}
|
|
|
|
|
variant={row.gap === preset ? "default" : "outline"}
|
|
|
|
|
size="sm"
|
|
|
|
|
onClick={() => onUpdateRow({ gap: preset })}
|
2025-10-28 17:33:03 +09:00
|
|
|
className="text-xs" style={{ fontSize: "12px" }}
|
2025-10-13 18:28:03 +09:00
|
|
|
>
|
|
|
|
|
{GAP_PRESETS[preset].label}
|
|
|
|
|
</Button>
|
|
|
|
|
))}
|
|
|
|
|
</div>
|
|
|
|
|
<p className="text-xs text-gray-500">현재: {GAP_PRESETS[row.gap].pixels}</p>
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
<Separator />
|
|
|
|
|
|
|
|
|
|
{/* 패딩 설정 */}
|
|
|
|
|
<div className="space-y-3">
|
|
|
|
|
<Label className="text-sm font-medium">행 패딩</Label>
|
|
|
|
|
<div className="grid grid-cols-3 gap-2">
|
|
|
|
|
{(Object.keys(GAP_PRESETS) as GapPreset[]).map((preset) => (
|
|
|
|
|
<Button
|
|
|
|
|
key={preset}
|
|
|
|
|
variant={row.padding === preset ? "default" : "outline"}
|
|
|
|
|
size="sm"
|
|
|
|
|
onClick={() => onUpdateRow({ padding: preset })}
|
2025-10-28 17:33:03 +09:00
|
|
|
className="text-xs" style={{ fontSize: "12px" }}
|
2025-10-13 18:28:03 +09:00
|
|
|
>
|
|
|
|
|
{GAP_PRESETS[preset].label}
|
|
|
|
|
</Button>
|
|
|
|
|
))}
|
|
|
|
|
</div>
|
|
|
|
|
<p className="text-xs text-gray-500">현재: {GAP_PRESETS[row.padding].pixels}</p>
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
<Separator />
|
|
|
|
|
|
|
|
|
|
{/* 수평 정렬 */}
|
|
|
|
|
<div className="space-y-3">
|
|
|
|
|
<div className="flex items-center gap-2">
|
|
|
|
|
<AlignHorizontalJustifyCenter className="h-4 w-4 text-gray-600" />
|
|
|
|
|
<Label className="text-sm font-medium">수평 정렬</Label>
|
|
|
|
|
</div>
|
|
|
|
|
<div className="grid grid-cols-2 gap-2">
|
|
|
|
|
<Button
|
|
|
|
|
variant={row.alignment === "start" ? "default" : "outline"}
|
|
|
|
|
size="sm"
|
|
|
|
|
onClick={() => onUpdateRow({ alignment: "start" })}
|
|
|
|
|
>
|
|
|
|
|
왼쪽
|
|
|
|
|
</Button>
|
|
|
|
|
<Button
|
|
|
|
|
variant={row.alignment === "center" ? "default" : "outline"}
|
|
|
|
|
size="sm"
|
|
|
|
|
onClick={() => onUpdateRow({ alignment: "center" })}
|
|
|
|
|
>
|
|
|
|
|
중앙
|
|
|
|
|
</Button>
|
|
|
|
|
<Button
|
|
|
|
|
variant={row.alignment === "end" ? "default" : "outline"}
|
|
|
|
|
size="sm"
|
|
|
|
|
onClick={() => onUpdateRow({ alignment: "end" })}
|
|
|
|
|
>
|
|
|
|
|
오른쪽
|
|
|
|
|
</Button>
|
|
|
|
|
<Button
|
|
|
|
|
variant={row.alignment === "stretch" ? "default" : "outline"}
|
|
|
|
|
size="sm"
|
|
|
|
|
onClick={() => onUpdateRow({ alignment: "stretch" })}
|
|
|
|
|
>
|
|
|
|
|
늘림
|
|
|
|
|
</Button>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
<Separator />
|
|
|
|
|
|
|
|
|
|
{/* 수직 정렬 */}
|
|
|
|
|
<div className="space-y-3">
|
|
|
|
|
<div className="flex items-center gap-2">
|
|
|
|
|
<AlignVerticalJustifyCenter className="h-4 w-4 text-gray-600" />
|
|
|
|
|
<Label className="text-sm font-medium">수직 정렬</Label>
|
|
|
|
|
</div>
|
|
|
|
|
<div className="grid grid-cols-2 gap-2">
|
|
|
|
|
<Button
|
|
|
|
|
variant={row.verticalAlignment === "top" ? "default" : "outline"}
|
|
|
|
|
size="sm"
|
|
|
|
|
onClick={() => onUpdateRow({ verticalAlignment: "top" })}
|
|
|
|
|
>
|
|
|
|
|
위
|
|
|
|
|
</Button>
|
|
|
|
|
<Button
|
|
|
|
|
variant={row.verticalAlignment === "middle" ? "default" : "outline"}
|
|
|
|
|
size="sm"
|
|
|
|
|
onClick={() => onUpdateRow({ verticalAlignment: "middle" })}
|
|
|
|
|
>
|
|
|
|
|
중앙
|
|
|
|
|
</Button>
|
|
|
|
|
<Button
|
|
|
|
|
variant={row.verticalAlignment === "bottom" ? "default" : "outline"}
|
|
|
|
|
size="sm"
|
|
|
|
|
onClick={() => onUpdateRow({ verticalAlignment: "bottom" })}
|
|
|
|
|
>
|
|
|
|
|
아래
|
|
|
|
|
</Button>
|
|
|
|
|
<Button
|
|
|
|
|
variant={row.verticalAlignment === "stretch" ? "default" : "outline"}
|
|
|
|
|
size="sm"
|
|
|
|
|
onClick={() => onUpdateRow({ verticalAlignment: "stretch" })}
|
|
|
|
|
>
|
|
|
|
|
늘림
|
|
|
|
|
</Button>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
<Separator />
|
|
|
|
|
|
|
|
|
|
{/* 배경색 */}
|
|
|
|
|
<div className="space-y-3">
|
|
|
|
|
<Label className="text-sm font-medium">배경색</Label>
|
|
|
|
|
<div className="flex gap-2">
|
|
|
|
|
<Input
|
|
|
|
|
type="color"
|
|
|
|
|
value={row.backgroundColor || "#ffffff"}
|
|
|
|
|
onChange={(e) => onUpdateRow({ backgroundColor: e.target.value })}
|
|
|
|
|
className="h-10 w-20 cursor-pointer p-1"
|
|
|
|
|
/>
|
|
|
|
|
<Input
|
|
|
|
|
type="text"
|
|
|
|
|
value={row.backgroundColor || ""}
|
|
|
|
|
onChange={(e) => onUpdateRow({ backgroundColor: e.target.value })}
|
|
|
|
|
placeholder="#ffffff"
|
|
|
|
|
className="flex-1"
|
|
|
|
|
/>
|
|
|
|
|
{row.backgroundColor && (
|
|
|
|
|
<Button variant="ghost" size="sm" onClick={() => onUpdateRow({ backgroundColor: undefined })}>
|
|
|
|
|
초기화
|
|
|
|
|
</Button>
|
|
|
|
|
)}
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
);
|
|
|
|
|
};
|