175 lines
4.5 KiB
TypeScript
175 lines
4.5 KiB
TypeScript
|
|
"use client";
|
||
|
|
|
||
|
|
import { LayoutDefinition, LayoutType, LayoutCategory } from "@/types/layout";
|
||
|
|
import React from "react";
|
||
|
|
|
||
|
|
/**
|
||
|
|
* 레이아웃 정의를 생성하는 헬퍼 함수
|
||
|
|
* 타입 안전성과 기본값을 보장합니다.
|
||
|
|
*/
|
||
|
|
export interface CreateLayoutDefinitionOptions {
|
||
|
|
id: string;
|
||
|
|
name: string;
|
||
|
|
nameEng?: string;
|
||
|
|
description: string;
|
||
|
|
category: LayoutCategory;
|
||
|
|
icon?: string;
|
||
|
|
component: React.ComponentType<any>;
|
||
|
|
defaultConfig?: Record<string, any>;
|
||
|
|
defaultZones?: Array<{
|
||
|
|
id: string;
|
||
|
|
name: string;
|
||
|
|
position?: Record<string, any>;
|
||
|
|
size?: {
|
||
|
|
width: number | string;
|
||
|
|
height: number | string;
|
||
|
|
minWidth?: number;
|
||
|
|
minHeight?: number;
|
||
|
|
maxWidth?: number;
|
||
|
|
maxHeight?: number;
|
||
|
|
};
|
||
|
|
[key: string]: any;
|
||
|
|
}>;
|
||
|
|
tags?: string[];
|
||
|
|
isActive?: boolean;
|
||
|
|
version?: string;
|
||
|
|
author?: string;
|
||
|
|
documentation?: string;
|
||
|
|
}
|
||
|
|
|
||
|
|
/**
|
||
|
|
* 레이아웃 정의를 생성합니다.
|
||
|
|
*/
|
||
|
|
export function createLayoutDefinition(options: CreateLayoutDefinitionOptions): LayoutDefinition {
|
||
|
|
const {
|
||
|
|
id,
|
||
|
|
name,
|
||
|
|
nameEng = name,
|
||
|
|
description,
|
||
|
|
category,
|
||
|
|
icon = "layout",
|
||
|
|
component,
|
||
|
|
defaultConfig = {},
|
||
|
|
defaultZones = [],
|
||
|
|
tags = [],
|
||
|
|
isActive = true,
|
||
|
|
version = "1.0.0",
|
||
|
|
author = "Developer",
|
||
|
|
documentation = "",
|
||
|
|
} = options;
|
||
|
|
|
||
|
|
// ID 유효성 검사
|
||
|
|
if (!id || typeof id !== "string" || !/^[a-z][a-z0-9-]*$/.test(id)) {
|
||
|
|
throw new Error(
|
||
|
|
`Invalid layout ID: "${id}". ID must start with a letter and contain only lowercase letters, numbers, and hyphens.`,
|
||
|
|
);
|
||
|
|
}
|
||
|
|
|
||
|
|
// 필수 필드 검사
|
||
|
|
if (!name.trim()) {
|
||
|
|
throw new Error("Layout name is required");
|
||
|
|
}
|
||
|
|
|
||
|
|
if (!description.trim()) {
|
||
|
|
throw new Error("Layout description is required");
|
||
|
|
}
|
||
|
|
|
||
|
|
if (!component) {
|
||
|
|
throw new Error("Layout component is required");
|
||
|
|
}
|
||
|
|
|
||
|
|
// 기본 태그 자동 추가
|
||
|
|
const autoTags = [id, category, "layout"];
|
||
|
|
const allTags = [...new Set([...autoTags, ...tags])];
|
||
|
|
|
||
|
|
const definition: LayoutDefinition = {
|
||
|
|
id: id as LayoutType,
|
||
|
|
name,
|
||
|
|
nameEng,
|
||
|
|
description,
|
||
|
|
category,
|
||
|
|
icon,
|
||
|
|
component,
|
||
|
|
defaultConfig,
|
||
|
|
defaultZones,
|
||
|
|
tags: allTags,
|
||
|
|
isActive,
|
||
|
|
metadata: {
|
||
|
|
version,
|
||
|
|
author,
|
||
|
|
documentation,
|
||
|
|
createdAt: new Date().toISOString(),
|
||
|
|
lastUpdated: new Date().toISOString(),
|
||
|
|
},
|
||
|
|
};
|
||
|
|
|
||
|
|
console.log(`📦 레이아웃 정의 생성: ${id} (${name})`);
|
||
|
|
return definition;
|
||
|
|
}
|
||
|
|
|
||
|
|
/**
|
||
|
|
* 레이아웃 정의의 유효성을 검사합니다.
|
||
|
|
*/
|
||
|
|
export function validateLayoutDefinition(definition: LayoutDefinition): {
|
||
|
|
isValid: boolean;
|
||
|
|
errors: string[];
|
||
|
|
warnings: string[];
|
||
|
|
} {
|
||
|
|
const errors: string[] = [];
|
||
|
|
const warnings: string[] = [];
|
||
|
|
|
||
|
|
// 필수 필드 검사
|
||
|
|
if (!definition.id) errors.push("ID is required");
|
||
|
|
if (!definition.name) errors.push("Name is required");
|
||
|
|
if (!definition.description) errors.push("Description is required");
|
||
|
|
if (!definition.category) errors.push("Category is required");
|
||
|
|
if (!definition.component) errors.push("Component is required");
|
||
|
|
|
||
|
|
// ID 형식 검사
|
||
|
|
if (definition.id && !/^[a-z][a-z0-9-]*$/.test(definition.id)) {
|
||
|
|
errors.push("ID must start with a letter and contain only lowercase letters, numbers, and hyphens");
|
||
|
|
}
|
||
|
|
|
||
|
|
// defaultZones 검사
|
||
|
|
if (definition.defaultZones) {
|
||
|
|
definition.defaultZones.forEach((zone, index) => {
|
||
|
|
if (!zone.id) errors.push(`Zone ${index}: ID is required`);
|
||
|
|
if (!zone.name) errors.push(`Zone ${index}: Name is required`);
|
||
|
|
});
|
||
|
|
}
|
||
|
|
|
||
|
|
// 경고사항 검사
|
||
|
|
if (!definition.nameEng) warnings.push("English name is recommended");
|
||
|
|
if (!definition.icon || definition.icon === "layout") warnings.push("Custom icon is recommended");
|
||
|
|
if (!definition.defaultZones || definition.defaultZones.length === 0) {
|
||
|
|
warnings.push("Default zones are recommended for better UX");
|
||
|
|
}
|
||
|
|
|
||
|
|
return {
|
||
|
|
isValid: errors.length === 0,
|
||
|
|
errors,
|
||
|
|
warnings,
|
||
|
|
};
|
||
|
|
}
|
||
|
|
|
||
|
|
/**
|
||
|
|
* 레이아웃 정의를 디버그 정보와 함께 출력합니다.
|
||
|
|
*/
|
||
|
|
export function debugLayoutDefinition(definition: LayoutDefinition): void {
|
||
|
|
console.group(`🔍 Layout Debug: ${definition.id}`);
|
||
|
|
console.log("📋 Definition:", definition);
|
||
|
|
|
||
|
|
const validation = validateLayoutDefinition(definition);
|
||
|
|
if (validation.errors.length > 0) {
|
||
|
|
console.error("❌ Errors:", validation.errors);
|
||
|
|
}
|
||
|
|
if (validation.warnings.length > 0) {
|
||
|
|
console.warn("⚠️ Warnings:", validation.warnings);
|
||
|
|
}
|
||
|
|
if (validation.isValid) {
|
||
|
|
console.log("✅ Definition is valid");
|
||
|
|
}
|
||
|
|
|
||
|
|
console.groupEnd();
|
||
|
|
}
|