다국어 지원 및 테이블 설정 현황 문서를 업데이트하고, 컴포넌트 패널에서 UI 개선을 통해 사용자 경험을 향상시켰습니다. 또한, 통합 패널의 너비를 조정하고, 패널 토글 기능을 추가하여 인터페이스의 유연성을 높였습니다.

This commit is contained in:
kjs
2026-01-16 09:59:16 +09:00
parent c71641c32c
commit f160ba2a1b
5 changed files with 97 additions and 165 deletions

View File

@@ -101,7 +101,6 @@ import { ScreenPreviewProvider } from "@/contexts/ScreenPreviewContext";
import { TableOptionsProvider } from "@/contexts/TableOptionsContext";
// 새로운 통합 UI 컴포넌트
import { LeftUnifiedToolbar, defaultToolbarButtons } from "./toolbar/LeftUnifiedToolbar";
import { SlimToolbar } from "./toolbar/SlimToolbar";
import { UnifiedPropertiesPanel } from "./panels/UnifiedPropertiesPanel";
@@ -4464,15 +4463,14 @@ export default function ScreenDesigner({ selectedScreen, onBackToList }: ScreenD
onGenerateMultilang={handleGenerateMultilang}
isGeneratingMultilang={isGeneratingMultilang}
onOpenMultilangSettings={() => setShowMultilangSettingsModal(true)}
isPanelOpen={panelStates.unified?.isOpen || false}
onTogglePanel={() => togglePanel("unified")}
/>
{/* 메인 컨테이너 (좌측 툴바 + 패널들 + 캔버스) */}
{/* 메인 컨테이너 (패널들 + 캔버스) */}
<div className="flex flex-1 overflow-hidden">
{/* 좌측 통합 툴바 */}
<LeftUnifiedToolbar buttons={defaultToolbarButtons} panelStates={panelStates} onTogglePanel={togglePanel} />
{/* 통합 패널 */}
{/* 통합 패널 - 좌측 사이드바 제거 후 너비 300px로 확장 */}
{panelStates.unified?.isOpen && (
<div className="border-border bg-card flex h-full w-[240px] flex-col border-r shadow-sm overflow-hidden">
<div className="border-border bg-card flex h-full w-[300px] flex-col border-r shadow-sm overflow-hidden">
<div className="border-border flex items-center justify-between border-b px-4 py-3 shrink-0">
<h3 className="text-foreground text-sm font-semibold"></h3>
<button

View File

@@ -5,7 +5,7 @@ import { Input } from "@/components/ui/input";
import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs";
import { ComponentRegistry } from "@/lib/registry/ComponentRegistry";
import { ComponentDefinition, ComponentCategory } from "@/types/component";
import { Search, Package, Grid, Layers, Palette, Zap, MousePointer, Database } from "lucide-react";
import { Search, Package, Grid, Layers, Palette, Zap, MousePointer, Database, GripVertical } from "lucide-react";
import { TableInfo, ColumnInfo } from "@/types/screen";
import TablesPanel from "./TablesPanel";
@@ -72,8 +72,8 @@ export function ComponentsPanel({
// unified-hierarchy 제거 - 현재 미사용
{
id: "unified-repeater",
name: "통합 반복 데이터",
description: "반복 데이터 관리 (인라인/모달/버튼 모드)",
name: "리피터 그리드",
description: "행 단위로 데이터를 추가/수정/삭제",
category: "data" as ComponentCategory,
tags: ["repeater", "table", "modal", "button", "unified"],
defaultSize: { width: 600, height: 300 },
@@ -188,7 +188,25 @@ export function ComponentsPanel({
e.dataTransfer.effectAllowed = "copy";
};
// 컴포넌트 카드 렌더링 함수
// 카테고리별 배경색 매핑
const getCategoryColor = (category: string) => {
switch (category) {
case "data":
return "from-blue-500/10 to-blue-600/10 text-blue-600 group-hover:from-blue-500/20 group-hover:to-blue-600/20";
case "display":
return "from-emerald-500/10 to-emerald-600/10 text-emerald-600 group-hover:from-emerald-500/20 group-hover:to-emerald-600/20";
case "input":
return "from-violet-500/10 to-violet-600/10 text-violet-600 group-hover:from-violet-500/20 group-hover:to-violet-600/20";
case "layout":
return "from-amber-500/10 to-amber-600/10 text-amber-600 group-hover:from-amber-500/20 group-hover:to-amber-600/20";
case "action":
return "from-rose-500/10 to-rose-600/10 text-rose-600 group-hover:from-rose-500/20 group-hover:to-rose-600/20";
default:
return "from-slate-500/10 to-slate-600/10 text-slate-600 group-hover:from-slate-500/20 group-hover:to-slate-600/20";
}
};
// 컴포넌트 카드 렌더링 함수 (컴팩트 버전)
const renderComponentCard = (component: ComponentDefinition) => (
<div
key={component.id}
@@ -202,21 +220,27 @@ export function ComponentsPanel({
e.currentTarget.style.opacity = "1";
e.currentTarget.style.transform = "none";
}}
className="group bg-card hover:border-primary/50 cursor-grab rounded-lg border p-3 shadow-sm transition-all duration-200 hover:-translate-y-0.5 hover:shadow-md active:translate-y-0 active:scale-[0.98] active:cursor-grabbing"
className="group bg-card hover:border-primary/40 cursor-grab rounded-lg border px-3 py-2.5 transition-all duration-200 hover:shadow-sm active:scale-[0.98] active:cursor-grabbing"
>
<div className="flex items-start gap-3">
<div className="bg-primary/10 text-primary group-hover:bg-primary/20 flex h-10 w-10 items-center justify-center rounded-md transition-all duration-200">
<div className="flex items-center gap-2.5">
<div
className={`flex h-8 w-8 shrink-0 items-center justify-center rounded-md bg-gradient-to-br transition-all duration-200 ${getCategoryColor(component.category)}`}
>
{getCategoryIcon(component.category)}
</div>
<div className="min-w-0 flex-1">
<h4 className="mb-1 text-xs leading-tight font-semibold">{component.name}</h4>
<p className="text-muted-foreground mb-1.5 line-clamp-2 text-xs leading-relaxed">{component.description}</p>
<div className="flex items-center">
<span className="bg-muted text-muted-foreground rounded-full px-2 py-0.5 text-xs font-medium">
<span className="text-foreground block truncate text-xs font-medium">{component.name}</span>
<div className="flex items-center gap-2">
<span className="text-muted-foreground text-[10px] capitalize">{component.category}</span>
<span className="text-muted-foreground/60 text-[10px]">|</span>
<span className="text-muted-foreground text-[10px]">
{component.defaultSize.width}×{component.defaultSize.height}
</span>
</div>
</div>
<div className="text-muted-foreground/40 group-hover:text-muted-foreground/60 transition-colors">
<GripVertical className="h-3.5 w-3.5" />
</div>
</div>
</div>
);
@@ -275,13 +299,13 @@ export function ComponentsPanel({
{/* 테이블 컬럼 탭 */}
<TabsContent value="tables" className="mt-0 flex-1 overflow-y-auto">
<TablesPanel
tables={tables}
searchTerm={searchTerm}
onSearchChange={onSearchChange || (() => {})}
<TablesPanel
tables={tables}
searchTerm={searchTerm}
onSearchChange={onSearchChange || (() => {})}
onDragStart={onTableDragStart || (() => {})}
selectedTableName={selectedTableName}
placedColumns={placedColumns}
selectedTableName={selectedTableName}
placedColumns={placedColumns}
onTableSelect={onTableSelect}
showTableSelector={showTableSelector}
/>

View File

@@ -20,6 +20,8 @@ import {
Zap,
Languages,
Settings2,
PanelLeft,
PanelLeftClose,
} from "lucide-react";
import { ScreenResolution, SCREEN_RESOLUTIONS } from "@/types/screen";
import {
@@ -60,6 +62,9 @@ interface SlimToolbarProps {
onGenerateMultilang?: () => void;
isGeneratingMultilang?: boolean;
onOpenMultilangSettings?: () => void;
// 패널 토글 기능
isPanelOpen?: boolean;
onTogglePanel?: () => void;
}
export const SlimToolbar: React.FC<SlimToolbarProps> = ({
@@ -76,6 +81,8 @@ export const SlimToolbar: React.FC<SlimToolbarProps> = ({
onGenerateMultilang,
isGeneratingMultilang = false,
onOpenMultilangSettings,
isPanelOpen = false,
onTogglePanel,
}) => {
// 사용자 정의 해상도 상태
const [customWidth, setCustomWidth] = useState("");
@@ -121,13 +128,33 @@ export const SlimToolbar: React.FC<SlimToolbarProps> = ({
return (
<div className="flex h-14 items-center justify-between border-b border-gray-200 bg-gradient-to-r from-gray-50 to-white px-4 shadow-sm">
{/* 좌측: 네비게이션 화면 정보 */}
{/* 좌측: 네비게이션 + 패널 토글 + 화면 정보 */}
<div className="flex items-center space-x-4">
<Button variant="ghost" size="sm" onClick={onBack} className="flex items-center space-x-2">
<ArrowLeft className="h-4 w-4" />
<span></span>
</Button>
{onTogglePanel && <div className="h-6 w-px bg-gray-300" />}
{/* 패널 토글 버튼 */}
{onTogglePanel && (
<Button
variant={isPanelOpen ? "default" : "outline"}
size="sm"
onClick={onTogglePanel}
className="flex items-center space-x-2"
title="패널 열기/닫기 (P)"
>
{isPanelOpen ? (
<PanelLeftClose className="h-4 w-4" />
) : (
<PanelLeft className="h-4 w-4" />
)}
<span></span>
</Button>
)}
<div className="h-6 w-px bg-gray-300" />
<div className="flex items-center space-x-3">