457 lines
12 KiB
TypeScript
457 lines
12 KiB
TypeScript
// 레이아웃 기능 타입 정의
|
|
|
|
import { ComponentStyle, ComponentData, ComponentType } from "./screen";
|
|
|
|
// 레이아웃 타입 정의
|
|
export type LayoutType =
|
|
| "grid" // 그리드 레이아웃 (n x m 격자)
|
|
| "flexbox" // 플렉스박스 레이아웃
|
|
| "split" // 분할 레이아웃 (수직/수평)
|
|
| "card" // 카드 레이아웃
|
|
| "tabs" // 탭 레이아웃
|
|
| "accordion" // 아코디언 레이아웃
|
|
| "sidebar" // 사이드바 레이아웃
|
|
| "header-footer" // 헤더-푸터 레이아웃
|
|
| "three-column" // 3단 레이아웃
|
|
| "dashboard" // 대시보드 레이아웃
|
|
| "form" // 폼 레이아웃
|
|
| "table" // 테이블 레이아웃
|
|
| "custom"; // 커스텀 레이아웃
|
|
|
|
// 레이아웃 카테고리
|
|
export const LAYOUT_CATEGORIES = {
|
|
BASIC: "basic", // 기본 레이아웃
|
|
FORM: "form", // 폼 레이아웃
|
|
TABLE: "table", // 테이블 레이아웃
|
|
DASHBOARD: "dashboard", // 대시보드 레이아웃
|
|
NAVIGATION: "navigation", // 네비게이션 레이아웃
|
|
CONTENT: "content", // 컨텐츠 레이아웃
|
|
BUSINESS: "business", // 업무용 레이아웃
|
|
} as const;
|
|
|
|
export type LayoutCategory = (typeof LAYOUT_CATEGORIES)[keyof typeof LAYOUT_CATEGORIES];
|
|
|
|
// 레이아웃 존 정의
|
|
export interface LayoutZone {
|
|
id: string;
|
|
name: string;
|
|
position: {
|
|
row?: number;
|
|
column?: number;
|
|
x?: number;
|
|
y?: number;
|
|
};
|
|
size: {
|
|
width: number | string;
|
|
height: number | string;
|
|
min_width?: number;
|
|
min_height?: number;
|
|
max_width?: number;
|
|
max_height?: number;
|
|
};
|
|
style?: ComponentStyle;
|
|
allowed_components?: ComponentType[];
|
|
is_resizable?: boolean;
|
|
is_required?: boolean; // 필수 영역 여부
|
|
}
|
|
|
|
// 레이아웃 설정
|
|
export interface LayoutConfig {
|
|
// 그리드 레이아웃 설정
|
|
grid?: {
|
|
rows: number;
|
|
columns: number;
|
|
gap: number;
|
|
row_gap?: number;
|
|
column_gap?: number;
|
|
auto_rows?: string;
|
|
auto_columns?: string;
|
|
};
|
|
|
|
// 플렉스박스 설정
|
|
flexbox?: {
|
|
direction: "row" | "column" | "row-reverse" | "column-reverse";
|
|
justify: "flex-start" | "flex-end" | "center" | "space-between" | "space-around" | "space-evenly";
|
|
align: "flex-start" | "flex-end" | "center" | "stretch" | "baseline";
|
|
wrap: "nowrap" | "wrap" | "wrap-reverse";
|
|
gap: number;
|
|
};
|
|
|
|
// 분할 레이아웃 설정
|
|
split?: {
|
|
direction: "horizontal" | "vertical";
|
|
ratio: number[]; // 각 영역의 비율 [30, 70]
|
|
min_size: number[]; // 각 영역의 최소 크기
|
|
resizable: boolean; // 크기 조절 가능 여부
|
|
splitter_size: number; // 분할선 두께
|
|
};
|
|
|
|
// 탭 레이아웃 설정
|
|
tabs?: {
|
|
position: "top" | "bottom" | "left" | "right";
|
|
variant: "default" | "pills" | "underline";
|
|
size: "sm" | "md" | "lg";
|
|
default_tab: string; // 기본 선택 탭
|
|
closable: boolean; // 탭 닫기 가능 여부
|
|
};
|
|
|
|
// 아코디언 설정
|
|
accordion?: {
|
|
multiple: boolean; // 다중 확장 허용
|
|
default_expanded: string[]; // 기본 확장 항목
|
|
collapsible: boolean; // 모두 닫기 허용
|
|
};
|
|
|
|
// 사이드바 설정
|
|
sidebar?: {
|
|
position: "left" | "right";
|
|
width: number | string;
|
|
collapsible: boolean;
|
|
collapsed: boolean;
|
|
overlay: boolean; // 오버레이 모드
|
|
};
|
|
|
|
// 헤더-푸터 설정
|
|
header_footer?: {
|
|
header_height: number | string;
|
|
footer_height: number | string;
|
|
sticky_header: boolean;
|
|
sticky_footer: boolean;
|
|
};
|
|
|
|
// 대시보드 설정
|
|
dashboard?: {
|
|
columns: number;
|
|
row_height: number;
|
|
margin: [number, number];
|
|
padding: [number, number];
|
|
is_draggable: boolean;
|
|
is_resizable: boolean;
|
|
};
|
|
|
|
// 커스텀 설정
|
|
custom?: {
|
|
css_properties: Record<string, string>;
|
|
class_name: string;
|
|
template: string; // HTML 템플릿
|
|
};
|
|
|
|
// 카드 레이아웃 설정
|
|
card?: {
|
|
// 테이블 컬럼 매핑 설정
|
|
column_mapping?: {
|
|
title_column?: string; // 카드 타이틀로 사용할 컬럼
|
|
subtitle_column?: string; // 카드 서브타이틀로 사용할 컬럼
|
|
image_column?: string; // 카드 이미지로 사용할 컬럼
|
|
description_column?: string; // 카드 설명으로 사용할 컬럼
|
|
display_columns?: string[]; // 카드에 표시할 추가 컬럼들
|
|
action_columns?: string[]; // 액션 버튼으로 표시할 컬럼들
|
|
};
|
|
// 카드 스타일 설정
|
|
card_style?: {
|
|
show_image?: boolean;
|
|
show_title?: boolean;
|
|
show_subtitle?: boolean;
|
|
show_description?: boolean;
|
|
max_description_length?: number;
|
|
image_position?: "top" | "left" | "right";
|
|
image_size?: "small" | "medium" | "large";
|
|
};
|
|
// 그리드 설정
|
|
cards_per_row?: number; // 한 행에 표시할 카드 수
|
|
card_spacing?: number; // 카드 간격
|
|
auto_height?: boolean; // 자동 높이 조정
|
|
};
|
|
}
|
|
|
|
// 드롭존 설정
|
|
export interface DropZoneConfig {
|
|
show_drop_zones: boolean;
|
|
drop_zone_style?: ComponentStyle;
|
|
highlight_on_drag_over: boolean;
|
|
allowed_types?: ComponentType[];
|
|
}
|
|
|
|
// 레이아웃 컴포넌트 인터페이스 (기존 screen.ts에 추가될 예정)
|
|
export interface LayoutComponent {
|
|
id: string;
|
|
type: "layout";
|
|
layout_type: LayoutType;
|
|
layout_config: LayoutConfig;
|
|
children: ComponentData[];
|
|
zones: LayoutZone[]; // 레이아웃 영역 정의
|
|
allowed_component_types?: ComponentType[]; // 허용된 자식 컴포넌트 타입
|
|
drop_zone_config?: DropZoneConfig; // 드롭존 설정
|
|
position: { x: number; y: number };
|
|
size: { width: number; height: number };
|
|
parent_id?: string;
|
|
style?: ComponentStyle;
|
|
table_name?: string;
|
|
label?: string;
|
|
}
|
|
|
|
// 레이아웃 표준 정의 (데이터베이스에서 조회)
|
|
export interface LayoutStandard {
|
|
layout_code: string;
|
|
layout_name: string;
|
|
layout_name_eng?: string;
|
|
description?: string;
|
|
layout_type: LayoutType;
|
|
category: LayoutCategory;
|
|
icon_name?: string;
|
|
default_size?: { width: number; height: number };
|
|
layout_config: LayoutConfig;
|
|
zones_config: LayoutZone[];
|
|
preview_image?: string;
|
|
sort_order?: number;
|
|
is_active?: string;
|
|
is_public?: string;
|
|
company_code: string;
|
|
created_date?: Date;
|
|
created_by?: string;
|
|
updated_date?: Date;
|
|
updated_by?: string;
|
|
}
|
|
|
|
// 레이아웃 생성 요청
|
|
export interface CreateLayoutRequest {
|
|
layout_name: string;
|
|
layout_name_eng?: string;
|
|
description?: string;
|
|
layout_type: LayoutType;
|
|
category: LayoutCategory;
|
|
icon_name?: string;
|
|
default_size?: { width: number; height: number };
|
|
layout_config: LayoutConfig;
|
|
zones_config: LayoutZone[];
|
|
is_public?: boolean;
|
|
}
|
|
|
|
// 레이아웃 수정 요청
|
|
export interface UpdateLayoutRequest extends Partial<CreateLayoutRequest> {
|
|
layout_code: string;
|
|
}
|
|
|
|
// 레이아웃 정의 (레지스트리에서 사용)
|
|
export interface LayoutDefinition {
|
|
id: string;
|
|
name: string;
|
|
name_eng?: string;
|
|
description?: string;
|
|
category: LayoutCategory;
|
|
icon?: string;
|
|
component: React.ComponentType<any>;
|
|
default_config: LayoutConfig;
|
|
default_zones: LayoutZone[];
|
|
tags?: string[];
|
|
is_active?: boolean;
|
|
metadata?: {
|
|
version?: string;
|
|
author?: string;
|
|
documentation?: string;
|
|
created_at?: string;
|
|
last_updated?: string;
|
|
[key: string]: any;
|
|
};
|
|
}
|
|
|
|
// 사전 정의 레이아웃 템플릿
|
|
export const PREDEFINED_LAYOUTS: Omit<
|
|
LayoutStandard,
|
|
"layout_code" | "company_code" | "created_date" | "created_by" | "updated_date" | "updated_by"
|
|
>[] = [
|
|
// 기본 레이아웃
|
|
{
|
|
layout_name: "2x2 그리드",
|
|
layout_name_eng: "2x2 Grid",
|
|
layout_type: "grid",
|
|
category: "basic",
|
|
icon_name: "grid",
|
|
default_size: { width: 800, height: 600 },
|
|
layout_config: {
|
|
grid: { rows: 2, columns: 2, gap: 16 },
|
|
},
|
|
zones_config: [
|
|
{
|
|
id: "zone1",
|
|
name: "상단 좌측",
|
|
position: { row: 0, column: 0 },
|
|
size: { width: "50%", height: "50%" },
|
|
},
|
|
{
|
|
id: "zone2",
|
|
name: "상단 우측",
|
|
position: { row: 0, column: 1 },
|
|
size: { width: "50%", height: "50%" },
|
|
},
|
|
{
|
|
id: "zone3",
|
|
name: "하단 좌측",
|
|
position: { row: 1, column: 0 },
|
|
size: { width: "50%", height: "50%" },
|
|
},
|
|
{
|
|
id: "zone4",
|
|
name: "하단 우측",
|
|
position: { row: 1, column: 1 },
|
|
size: { width: "50%", height: "50%" },
|
|
},
|
|
],
|
|
sort_order: 1,
|
|
},
|
|
|
|
// 폼 레이아웃
|
|
{
|
|
layout_name: "2단 폼 레이아웃",
|
|
layout_name_eng: "Two Column Form",
|
|
layout_type: "grid",
|
|
category: "form",
|
|
icon_name: "columns",
|
|
default_size: { width: 800, height: 400 },
|
|
layout_config: {
|
|
grid: { rows: 1, columns: 2, gap: 24 },
|
|
},
|
|
zones_config: [
|
|
{
|
|
id: "left",
|
|
name: "좌측 입력 영역",
|
|
position: { row: 0, column: 0 },
|
|
size: { width: "50%", height: "100%" },
|
|
},
|
|
{
|
|
id: "right",
|
|
name: "우측 입력 영역",
|
|
position: { row: 0, column: 1 },
|
|
size: { width: "50%", height: "100%" },
|
|
},
|
|
],
|
|
sort_order: 2,
|
|
},
|
|
|
|
// 대시보드 레이아웃
|
|
{
|
|
layout_name: "메인 대시보드",
|
|
layout_name_eng: "Main Dashboard",
|
|
layout_type: "grid",
|
|
category: "dashboard",
|
|
icon_name: "layout-dashboard",
|
|
default_size: { width: 1200, height: 800 },
|
|
layout_config: {
|
|
grid: { rows: 2, columns: 4, gap: 16 },
|
|
},
|
|
zones_config: [
|
|
{
|
|
id: "header",
|
|
name: "헤더",
|
|
position: { row: 0, column: 0 },
|
|
size: { width: "100%", height: "80px" },
|
|
},
|
|
{
|
|
id: "sidebar",
|
|
name: "사이드바",
|
|
position: { row: 1, column: 0 },
|
|
size: { width: "250px", height: "100%" },
|
|
},
|
|
{
|
|
id: "main",
|
|
name: "메인 컨텐츠",
|
|
position: { row: 1, column: 1 },
|
|
size: { width: "calc(100% - 250px)", height: "100%" },
|
|
},
|
|
],
|
|
sort_order: 3,
|
|
},
|
|
|
|
// 테이블 레이아웃
|
|
{
|
|
layout_name: "필터가 있는 테이블",
|
|
layout_name_eng: "Table with Filters",
|
|
layout_type: "flexbox",
|
|
category: "table",
|
|
icon_name: "table",
|
|
default_size: { width: 1000, height: 600 },
|
|
layout_config: {
|
|
flexbox: { direction: "column", justify: "flex-start", align: "stretch", wrap: "nowrap", gap: 16 },
|
|
},
|
|
zones_config: [
|
|
{
|
|
id: "filters",
|
|
name: "검색 필터",
|
|
position: {},
|
|
size: { width: "100%", height: "auto" },
|
|
},
|
|
{
|
|
id: "table",
|
|
name: "데이터 테이블",
|
|
position: {},
|
|
size: { width: "100%", height: "1fr" },
|
|
},
|
|
],
|
|
sort_order: 4,
|
|
},
|
|
|
|
// 분할 레이아웃
|
|
{
|
|
layout_name: "수평 분할",
|
|
layout_name_eng: "Horizontal Split",
|
|
layout_type: "split",
|
|
category: "basic",
|
|
icon_name: "separator-horizontal",
|
|
default_size: { width: 800, height: 400 },
|
|
layout_config: {
|
|
split: { direction: "horizontal", ratio: [50, 50], min_size: [200, 200], resizable: true, splitter_size: 4 },
|
|
},
|
|
zones_config: [
|
|
{
|
|
id: "left",
|
|
name: "좌측 영역",
|
|
position: {},
|
|
size: { width: "50%", height: "100%" },
|
|
is_resizable: true,
|
|
},
|
|
{
|
|
id: "right",
|
|
name: "우측 영역",
|
|
position: {},
|
|
size: { width: "50%", height: "100%" },
|
|
is_resizable: true,
|
|
},
|
|
],
|
|
sort_order: 5,
|
|
},
|
|
|
|
// 탭 레이아웃
|
|
{
|
|
layout_name: "수평 탭",
|
|
layout_name_eng: "Horizontal Tabs",
|
|
layout_type: "tabs",
|
|
category: "navigation",
|
|
icon_name: "tabs",
|
|
default_size: { width: 800, height: 500 },
|
|
layout_config: {
|
|
tabs: { position: "top", variant: "default", size: "md", default_tab: "tab1", closable: false },
|
|
},
|
|
zones_config: [
|
|
{
|
|
id: "tab1",
|
|
name: "첫 번째 탭",
|
|
position: {},
|
|
size: { width: "100%", height: "100%" },
|
|
},
|
|
{
|
|
id: "tab2",
|
|
name: "두 번째 탭",
|
|
position: {},
|
|
size: { width: "100%", height: "100%" },
|
|
},
|
|
{
|
|
id: "tab3",
|
|
name: "세 번째 탭",
|
|
position: {},
|
|
size: { width: "100%", height: "100%" },
|
|
},
|
|
],
|
|
sort_order: 6,
|
|
},
|
|
];
|