From 2feab9cde8518fcec67694ee72a29e90b47632b0 Mon Sep 17 00:00:00 2001 From: DDD1542 Date: Thu, 12 Mar 2026 00:31:29 +0900 Subject: [PATCH] [agent-pipeline] pipe-20260311151253-nyk7 round-5 --- .../V2DividerLineConfigPanel.tsx | 236 ++++++++++++++++++ .../config-panels/V2SplitLineConfigPanel.tsx | 207 +++++++++++++++ .../components/v2-divider-line/index.ts | 4 +- .../components/v2-split-line/index.ts | 4 +- 4 files changed, 447 insertions(+), 4 deletions(-) create mode 100644 frontend/components/v2/config-panels/V2DividerLineConfigPanel.tsx create mode 100644 frontend/components/v2/config-panels/V2SplitLineConfigPanel.tsx diff --git a/frontend/components/v2/config-panels/V2DividerLineConfigPanel.tsx b/frontend/components/v2/config-panels/V2DividerLineConfigPanel.tsx new file mode 100644 index 00000000..f346fcb7 --- /dev/null +++ b/frontend/components/v2/config-panels/V2DividerLineConfigPanel.tsx @@ -0,0 +1,236 @@ +"use client"; + +/** + * V2DividerLine 설정 패널 + * 토스식 UX: 구분선 스타일 카드 선택 -> 텍스트 설정 -> 고급 설정(접힘) + */ + +import React, { useState } from "react"; +import { Input } from "@/components/ui/input"; +import { Switch } from "@/components/ui/switch"; +import { + Collapsible, + CollapsibleContent, + CollapsibleTrigger, +} from "@/components/ui/collapsible"; +import { Settings, ChevronDown, Minus, MoreHorizontal, Equal } from "lucide-react"; +import { cn } from "@/lib/utils"; + +const THICKNESS_CARDS = [ + { value: "1px", label: "얇게", size: "1px" }, + { value: "2px", label: "보통", size: "2px" }, + { value: "4px", label: "두껍게", size: "4px" }, +] as const; + +const COLOR_CARDS = [ + { value: "#d1d5db", label: "기본", description: "연한 회색" }, + { value: "#9ca3af", label: "진하게", description: "중간 회색" }, + { value: "#3b82f6", label: "강조", description: "파란색" }, +] as const; + +interface V2DividerLineConfigPanelProps { + config: Record; + onChange: (config: Record) => void; +} + +export const V2DividerLineConfigPanel: React.FC = ({ + config, + onChange, +}) => { + const [advancedOpen, setAdvancedOpen] = useState(false); + + const updateConfig = (field: string, value: any) => { + const newConfig = { ...config, [field]: value }; + onChange(newConfig); + + if (typeof window !== "undefined") { + window.dispatchEvent( + new CustomEvent("componentConfigChanged", { + detail: { config: newConfig }, + }) + ); + } + }; + + return ( +
+ {/* ─── 1단계: 선 두께 카드 선택 ─── */} +
+

선 두께

+
+ {THICKNESS_CARDS.map((card) => { + const isSelected = (config.thickness || "1px") === card.value; + return ( + + ); + })} +
+
+ + {/* ─── 2단계: 선 색상 카드 선택 ─── */} +
+

선 색상

+
+ {COLOR_CARDS.map((card) => { + const isSelected = (config.color || "#d1d5db") === card.value; + return ( + + ); + })} +
+ {/* 커스텀 색상 */} +
+ updateConfig("color", e.target.value)} + className="h-7 w-7 cursor-pointer rounded border" + /> + updateConfig("color", e.target.value)} + placeholder="#d1d5db" + className="h-7 flex-1 text-xs" + /> +
+
+ + {/* ─── 3단계: 구분선 텍스트 ─── */} +
+
+
+

구분 텍스트

+

+ 선 가운데에 텍스트를 표시해요 +

+
+ + updateConfig("dividerText", checked ? "구분" : "") + } + /> +
+ + {config.dividerText && ( +
+
+ 텍스트 + updateConfig("dividerText", e.target.value)} + placeholder="구분 텍스트 입력" + className="h-7 w-[160px] text-xs" + /> +
+
+ 텍스트 색상 +
+ updateConfig("textColor", e.target.value)} + className="h-6 w-6 cursor-pointer rounded border" + /> + updateConfig("textColor", e.target.value)} + className="h-7 w-[100px] text-xs" + /> +
+
+
+ )} +
+ + {/* ─── 4단계: 고급 설정 (기본 접혀있음) ─── */} + + + + + +
+
+
+

둥근 끝

+

+ 선의 양쪽 끝을 둥글게 처리해요 +

+
+ updateConfig("rounded", checked)} + /> +
+ +
+
+

비활성화

+

+ 컴포넌트를 비활성화 상태로 표시해요 +

+
+ updateConfig("disabled", checked)} + /> +
+
+
+
+
+ ); +}; + +V2DividerLineConfigPanel.displayName = "V2DividerLineConfigPanel"; + +export default V2DividerLineConfigPanel; diff --git a/frontend/components/v2/config-panels/V2SplitLineConfigPanel.tsx b/frontend/components/v2/config-panels/V2SplitLineConfigPanel.tsx new file mode 100644 index 00000000..58dcb138 --- /dev/null +++ b/frontend/components/v2/config-panels/V2SplitLineConfigPanel.tsx @@ -0,0 +1,207 @@ +"use client"; + +/** + * V2SplitLine 설정 패널 + * 토스식 UX: 리사이즈 Switch -> 두께 카드 선택 -> 색상 설정 + */ + +import React, { useState } from "react"; +import { Input } from "@/components/ui/input"; +import { Switch } from "@/components/ui/switch"; +import { + Collapsible, + CollapsibleContent, + CollapsibleTrigger, +} from "@/components/ui/collapsible"; +import { Settings, ChevronDown } from "lucide-react"; +import { cn } from "@/lib/utils"; + +const WIDTH_CARDS = [ + { value: 2, label: "얇게" }, + { value: 4, label: "보통" }, + { value: 6, label: "두껍게" }, + { value: 8, label: "넓게" }, +] as const; + +const COLOR_CARDS = [ + { value: "#e2e8f0", label: "기본", description: "연한 회색" }, + { value: "#94a3b8", label: "진하게", description: "중간 회색" }, + { value: "#3b82f6", label: "강조", description: "파란색" }, +] as const; + +interface V2SplitLineConfigPanelProps { + config: Record; + onConfigChange: (config: Record) => void; +} + +export const V2SplitLineConfigPanel: React.FC = ({ + config, + onConfigChange, +}) => { + const [advancedOpen, setAdvancedOpen] = useState(false); + const currentConfig = config || {}; + + const updateConfig = (field: string, value: any) => { + const newConfig = { ...currentConfig, [field]: value }; + onConfigChange(newConfig); + + if (typeof window !== "undefined") { + window.dispatchEvent( + new CustomEvent("componentConfigChanged", { + detail: { config: newConfig }, + }) + ); + } + }; + + return ( +
+ {/* ─── 1단계: 드래그 리사이즈 ─── */} +
+
+

드래그 리사이즈

+

+ 런타임에서 드래그로 좌우 영역을 조절할 수 있어요 +

+
+ updateConfig("resizable", checked)} + /> +
+ + {/* ─── 2단계: 분할선 두께 카드 선택 ─── */} +
+

분할선 두께

+
+ {WIDTH_CARDS.map((card) => { + const isSelected = (currentConfig.lineWidth || 4) === card.value; + return ( + + ); + })} +
+

+ 현재: {currentConfig.lineWidth || 4}px +

+
+ + {/* ─── 3단계: 분할선 색상 카드 선택 ─── */} +
+

분할선 색상

+
+ {COLOR_CARDS.map((card) => { + const isSelected = + (currentConfig.lineColor || "#e2e8f0") === card.value; + return ( + + ); + })} +
+
+ + {/* ─── 고급 설정: 커스텀 색상 입력 ─── */} + + + + + +
+ {/* 커스텀 색상 입력 */} +
+ 직접 입력 +
+ updateConfig("lineColor", e.target.value)} + className="h-7 w-7 cursor-pointer rounded border" + /> + updateConfig("lineColor", e.target.value)} + placeholder="#e2e8f0" + className="h-7 w-[100px] text-xs" + /> +
+
+ + {/* 커스텀 두께 입력 */} +
+ 두께 직접 입력 (px) + + updateConfig("lineWidth", parseInt(e.target.value) || 4) + } + className="h-7 w-[80px] text-xs" + min={1} + max={12} + /> +
+
+
+
+ +

+ 캔버스에서 스플릿선의 X 위치가 초기 분할 지점이 돼요. 런타임에서 + 드래그하면 좌우 컴포넌트가 함께 이동해요. +

+
+ ); +}; + +V2SplitLineConfigPanel.displayName = "V2SplitLineConfigPanel"; + +export default V2SplitLineConfigPanel; diff --git a/frontend/lib/registry/components/v2-divider-line/index.ts b/frontend/lib/registry/components/v2-divider-line/index.ts index 274cded1..9ade43ea 100644 --- a/frontend/lib/registry/components/v2-divider-line/index.ts +++ b/frontend/lib/registry/components/v2-divider-line/index.ts @@ -5,7 +5,7 @@ import { createComponentDefinition } from "../../utils/createComponentDefinition import { ComponentCategory } from "@/types/component"; import type { WebType } from "@/types/screen"; import { DividerLineWrapper } from "./DividerLineComponent"; -import { DividerLineConfigPanel } from "./DividerLineConfigPanel"; +import { V2DividerLineConfigPanel } from "@/components/v2/config-panels/V2DividerLineConfigPanel"; import { DividerLineConfig } from "./types"; /** @@ -25,7 +25,7 @@ export const V2DividerLineDefinition = createComponentDefinition({ maxLength: 255, }, defaultSize: { width: 400, height: 2 }, - configPanel: DividerLineConfigPanel, + configPanel: V2DividerLineConfigPanel, icon: "Layout", tags: [], version: "1.0.0", diff --git a/frontend/lib/registry/components/v2-split-line/index.ts b/frontend/lib/registry/components/v2-split-line/index.ts index 55a3a0d1..320844b3 100644 --- a/frontend/lib/registry/components/v2-split-line/index.ts +++ b/frontend/lib/registry/components/v2-split-line/index.ts @@ -4,7 +4,7 @@ import React from "react"; import { createComponentDefinition } from "../../utils/createComponentDefinition"; import { ComponentCategory } from "@/types/component"; import { SplitLineWrapper } from "./SplitLineComponent"; -import { SplitLineConfigPanel } from "./SplitLineConfigPanel"; +import { V2SplitLineConfigPanel } from "@/components/v2/config-panels/V2SplitLineConfigPanel"; import { SplitLineConfig } from "./types"; /** @@ -25,7 +25,7 @@ export const V2SplitLineDefinition = createComponentDefinition({ lineWidth: 4, } as SplitLineConfig, defaultSize: { width: 8, height: 600 }, - configPanel: SplitLineConfigPanel, + configPanel: V2SplitLineConfigPanel, icon: "SeparatorVertical", tags: ["스플릿", "분할", "분할선", "레이아웃"], version: "1.0.0",