// 그룹화 관련 유틸리티 함수들 import { ComponentData, GroupComponent, Position, Size } from "@/types/screen"; // 컴포넌트 ID 생성 export function generateComponentId(): string { return `comp_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`; } // 그룹 컴포넌트 생성 export function createGroupComponent( componentIds: string[], title: string = "새 그룹", position: Position = { x: 0, y: 0 }, boundingBox?: { width: number; height: number }, style?: any, ): GroupComponent { // 픽셀 기반 크기 계산 (격자 제거) const groupWidth = Math.max(200, (boundingBox?.width || 200) + 40); // 최소 200px, 여백 40px const groupHeight = Math.max(100, (boundingBox?.height || 200) + 40); // 최소 100px, 여백 40px return { id: generateComponentId(), type: "group", position: { ...position, z: position.z || 1 }, // z 값 포함 size: { width: groupWidth, height: groupHeight }, title: title, // GroupComponent 타입에 맞게 title 사용 backgroundColor: "#f8f9fa", border: "1px solid #dee2e6", borderRadius: 8, shadow: "0 2px 4px rgba(0,0,0,0.1)", collapsible: true, collapsed: false, children: componentIds, style: { padding: "16px", ...style, }, }; } // 선택된 컴포넌트들의 경계 박스 계산 (픽셀 기반) export function calculateBoundingBox(components: ComponentData[]): { minX: number; minY: number; maxX: number; maxY: number; width: number; height: number; } { if (components.length === 0) { return { minX: 0, minY: 0, maxX: 0, maxY: 0, width: 0, height: 0 }; } const minX = Math.min(...components.map((c) => c.position.x)); const minY = Math.min(...components.map((c) => c.position.y)); const maxX = Math.max(...components.map((c) => c.position.x + c.size.width)); // 격자 계산 제거 const maxY = Math.max(...components.map((c) => c.position.y + c.size.height)); return { minX, minY, maxX, maxY, width: maxX - minX, height: maxY - minY, }; } // 그룹 내 컴포넌트들의 상대 위치 계산 export function calculateRelativePositions( components: ComponentData[], groupPosition: Position, groupId: string, ): ComponentData[] { return components.map((component) => ({ ...component, position: { x: component.position.x - groupPosition.x, y: component.position.y - groupPosition.y, z: component.position.z || 1, // z 값 유지 }, parentId: groupId, // 그룹 ID를 부모로 설정 })); } // 그룹 해제 시 컴포넌트들의 절대 위치 복원 export function restoreAbsolutePositions(components: ComponentData[], groupPosition: Position): ComponentData[] { return components.map((component) => ({ ...component, position: { x: component.position.x + groupPosition.x, y: component.position.y + groupPosition.y, z: component.position.z || 1, // z 값 유지 }, parentId: undefined, })); } // 그룹 내 컴포넌트 필터링 export function getGroupChildren(components: ComponentData[], groupId: string): ComponentData[] { return components.filter((component) => component.parentId === groupId); } // 그룹이 아닌 컴포넌트들 필터링 export function getNonGroupComponents(components: ComponentData[]): ComponentData[] { return components.filter((component) => component.type !== "group"); } // 선택 가능한 컴포넌트들 필터링 (그룹 제외) export function getSelectableComponents(components: ComponentData[]): ComponentData[] { return components.filter((component) => component.type === "widget" || component.type === "container"); } // 그룹 스타일 생성 export function createGroupStyle( backgroundColor: string = "#f8f9fa", border: string = "1px solid #dee2e6", borderRadius: number = 8, ): any { return { backgroundColor, border, borderRadius, padding: "16px", boxShadow: "0 2px 4px rgba(0,0,0,0.1)", }; } // 그룹 제목 유효성 검사 export function validateGroupTitle(title: string): boolean { return title.trim().length > 0 && title.trim().length <= 50; } // 그룹 크기 자동 조정 export function autoResizeGroup(group: GroupComponent, children: ComponentData[]): GroupComponent { if (children.length === 0) { return group; } const boundingBox = calculateBoundingBox(children); return { ...group, size: { width: Math.max(6, Math.ceil(boundingBox.width / 80) + 2), // 최소 6 그리드, 여백 2 height: Math.max(100, boundingBox.height + 40), // 최소 100px, 여백 40px }, }; }