From 24630dd60b304d4458cd6b1a1a44f85f2a7f5b4b Mon Sep 17 00:00:00 2001 From: DDD1542 Date: Wed, 11 Mar 2026 21:44:12 +0900 Subject: [PATCH] [agent-pipeline] pipe-20260311122226-4dkx round-5 --- .../v2/config-panels/V2LayoutConfigPanel.tsx | 423 +++++++++++++----- 1 file changed, 301 insertions(+), 122 deletions(-) diff --git a/frontend/components/v2/config-panels/V2LayoutConfigPanel.tsx b/frontend/components/v2/config-panels/V2LayoutConfigPanel.tsx index 22a79b21..26f287b5 100644 --- a/frontend/components/v2/config-panels/V2LayoutConfigPanel.tsx +++ b/frontend/components/v2/config-panels/V2LayoutConfigPanel.tsx @@ -2,14 +2,68 @@ /** * V2Layout 설정 패널 - * 통합 레이아웃 컴포넌트의 세부 설정을 관리합니다. + * 토스식 단계별 UX: 레이아웃 타입 카드 선택 -> 타입별 설정 -> 고급 설정(접힘) */ -import React from "react"; -import { Label } from "@/components/ui/label"; +import React, { useState } from "react"; import { Input } from "@/components/ui/input"; -import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@/components/ui/select"; -import { Checkbox } from "@/components/ui/checkbox"; +import { + Select, + SelectContent, + SelectItem, + SelectTrigger, + SelectValue, +} from "@/components/ui/select"; +import { Switch } from "@/components/ui/switch"; +import { + Collapsible, + CollapsibleContent, + CollapsibleTrigger, +} from "@/components/ui/collapsible"; +import { + LayoutGrid, + PanelLeftClose, + MoveHorizontal, + Minus, + MonitorPlay, + Settings, + ChevronDown, +} from "lucide-react"; +import { cn } from "@/lib/utils"; + +// ─── 레이아웃 타입 카드 정의 ─── +const LAYOUT_TYPE_CARDS = [ + { + value: "grid", + icon: LayoutGrid, + title: "그리드", + description: "행과 열로 배치해요", + }, + { + value: "split", + icon: PanelLeftClose, + title: "분할 패널", + description: "영역을 나눠서 배치해요", + }, + { + value: "flex", + icon: MoveHorizontal, + title: "플렉스", + description: "유연하게 배치해요", + }, + { + value: "divider", + icon: Minus, + title: "구분선", + description: "영역을 구분해요", + }, + { + value: "screen-embed", + icon: MonitorPlay, + title: "화면 임베드", + description: "다른 화면을 불러와요", + }, +] as const; interface V2LayoutConfigPanelProps { config: Record; @@ -20,93 +74,125 @@ export const V2LayoutConfigPanel: React.FC = ({ config, onChange, }) => { + const [advancedOpen, setAdvancedOpen] = useState(false); + const updateConfig = (field: string, value: any) => { onChange({ ...config, [field]: value }); }; + const currentLayoutType = config.layoutType || config.type || "grid"; + const isGridType = currentLayoutType === "grid"; + const isSplitType = currentLayoutType === "split"; + const isFlexType = currentLayoutType === "flex"; + const isScreenEmbedType = currentLayoutType === "screen-embed"; + return ( -
- {/* LAYOUT TYPE 섹션 */} -
-

LAYOUT TYPE

-
- 레이아웃 타입 -
- -
+
+ {/* ─── 1단계: 레이아웃 타입 선택 (카드) ─── */} +
+

어떤 레이아웃을 사용하나요?

+
+ {LAYOUT_TYPE_CARDS.map((card) => { + const Icon = card.icon; + const isSelected = currentLayoutType === card.value; + return ( + + ); + })}
- {/* GRID SETTINGS 섹션 */} - {(config.layoutType === "grid" || !config.layoutType) && ( -
-

GRID SETTINGS

-
- 12컬럼 그리드 사용 - updateConfig("use12Column", checked)} - /> -
-
-
- + {/* ─── 2단계: 타입별 설정 ─── */} + + {/* 그리드 타입 설정 */} + {isGridType && ( +
+
+
+ + 그리드 설정 +
+ +
+ 컬럼 수
-
- + +
+ 간격 (px) updateConfig("gap", e.target.value)} placeholder="16" - className="h-7 text-xs" + className="h-8 w-[180px] text-sm" />
+ +
+
+

12컬럼 그리드

+

+ 표준 12컬럼 그리드 시스템을 사용해요 +

+
+ updateConfig("use12Column", checked)} + /> +
)} - {/* SPLIT SETTINGS 섹션 */} - {config.layoutType === "split" && ( -
-

SPLIT SETTINGS

-
- 분할 방향 -
+ {/* 분할 패널 타입 설정 */} + {isSplitType && ( +
+
+
+ + 분할 설정 +
+ +
+ 분할 방향
-
-
-
- - updateConfig("splitRatio", [Number(e.target.value), 100 - Number(e.target.value)])} - placeholder="50" - min="10" - max="90" - className="h-7 text-xs" - /> -
-
- - + +
+
+ 비율 (%) + updateConfig("splitRatio", [Number(e.target.value), 100 - Number(e.target.value)])} + placeholder="50" + min="10" + max="90" + className="mt-1 h-8 text-sm" + /> +
+
+ 나머지 + +
-
- 크기 조절 가능 - +
+

크기 조절

+

+ 사용자가 패널 크기를 드래그해서 조절할 수 있어요 +

+
+ updateConfig("resizable", checked)} /> @@ -149,18 +242,22 @@ export const V2LayoutConfigPanel: React.FC = ({
)} - {/* FLEX SETTINGS 섹션 */} - {config.layoutType === "flex" && ( -
-

FLEX SETTINGS

-
- 방향 -
+ {/* 플렉스 타입 설정 */} + {isFlexType && ( +
+
+
+ + 플렉스 설정 +
+ +
+ 방향
-
-
-
- + +
+ 정렬
-
- + +
+ 교차축 정렬
-
-
- 간격 (px) -
+ +
+ 간격 (px) updateConfig("gap", e.target.value)} placeholder="16" - className="h-7 text-xs" + className="h-8 w-[180px] text-sm" />
-
- 줄바꿈 허용 - +
+

줄바꿈 허용

+

+ 공간이 부족하면 다음 줄로 넘겨요 +

+
+ updateConfig("wrap", checked)} /> @@ -230,24 +332,101 @@ export const V2LayoutConfigPanel: React.FC = ({
)} - {/* SCREEN EMBED 섹션 */} - {config.layoutType === "screen-embed" && ( -
-

SCREEN EMBED

+ {/* 화면 임베드 타입 설정 */} + {isScreenEmbedType && ( +
+
+ + 임베드 설정 +
+
화면 ID -
- updateConfig("screenId", e.target.value ? Number(e.target.value) : undefined)} - placeholder="화면 ID" - className="h-7 text-xs" - /> -
+ updateConfig("screenId", e.target.value ? Number(e.target.value) : undefined)} + placeholder="화면 ID 입력" + className="h-8 w-[180px] text-sm" + />
)} + + {/* 구분선 타입: 별도 설정 없음 - 빈 상태 표시 */} + {currentLayoutType === "divider" && ( +
+ +

추가 설정이 없어요

+

구분선은 기본 스타일로 표시돼요

+
+ )} + + {/* ─── 3단계: 고급 설정 (그리드/플렉스 타입에서만) ─── */} + {(isGridType || isFlexType || isSplitType) && ( + + + + + +
+ {isGridType && ( +
+
+

반응형 그리드

+

+ 화면 크기에 따라 컬럼 수가 자동 조정돼요 +

+
+ updateConfig("responsive", checked)} + /> +
+ )} + + {isFlexType && ( +
+ 최소 아이템 너비 + updateConfig("minItemWidth", e.target.value)} + placeholder="자동" + className="h-8 w-[180px] text-sm" + /> +
+ )} + + {isSplitType && ( +
+ 최소 패널 크기 (px) + updateConfig("minPanelSize", e.target.value ? Number(e.target.value) : undefined)} + placeholder="자동" + className="h-8 w-[180px] text-sm" + /> +
+ )} +
+
+
+ )}
); };