/** * POP 컴포넌트 설정 스키마 및 유틸리티 * * POP(모바일/태블릿) 컴포넌트의 overrides 스키마 및 기본값을 관리 * - 공통 요소는 componentConfig.ts에서 import하여 재사용 * - POP 전용 컴포넌트의 overrides 스키마만 새로 정의 */ import { z } from "zod"; // ============================================ // 공통 요소 재사용 (componentConfig.ts에서 import) // ============================================ export { // 공통 스키마 customConfigSchema, componentV2Schema, layoutV2Schema, // 공통 유틸리티 함수 deepMerge, mergeComponentConfig, extractCustomConfig, isDeepEqual, getComponentTypeFromUrl, } from "./componentConfig"; // ============================================ // POP 전용 URL 생성 함수 // ============================================ export function getPopComponentUrl(componentType: string): string { return `@/lib/registry/pop-components/${componentType}`; } // ============================================ // POP 전용 컴포넌트 기본값 // ============================================ // POP 카드 리스트 기본값 export const popCardListDefaults = { displayMode: "card" as const, cardStyle: "compact" as const, showHeader: true, showFooter: false, pageSize: 10, enablePullToRefresh: true, enableInfiniteScroll: false, cardColumns: 1, gap: 8, padding: 16, // 터치 최적화 touchFeedback: true, swipeActions: false, }; // POP 터치 버튼 기본값 export const popTouchButtonDefaults = { variant: "primary" as const, size: "lg" as const, text: "확인", icon: null, iconPosition: "left" as const, fullWidth: true, // 터치 최적화 minHeight: 48, // 최소 터치 영역 48px hapticFeedback: true, pressDelay: 0, }; // POP 스캐너 입력 기본값 export const popScannerInputDefaults = { placeholder: "바코드를 스캔하세요", showKeyboard: false, autoFocus: true, autoSubmit: true, submitDelay: 300, // 스캐너 설정 scannerMode: "auto" as const, beepOnScan: true, vibrationOnScan: true, clearOnSubmit: true, }; // POP 상태 배지 기본값 export const popStatusBadgeDefaults = { variant: "default" as const, size: "md" as const, text: "", icon: null, // 스타일 rounded: true, pulse: false, }; // ============================================ // POP 전용 overrides 스키마 // ============================================ // POP 카드 리스트 overrides 스키마 export const popCardListOverridesSchema = z .object({ displayMode: z.enum(["card", "list", "grid"]).default("card"), cardStyle: z.enum(["compact", "default", "expanded"]).default("compact"), showHeader: z.boolean().default(true), showFooter: z.boolean().default(false), pageSize: z.number().default(10), enablePullToRefresh: z.boolean().default(true), enableInfiniteScroll: z.boolean().default(false), cardColumns: z.number().default(1), gap: z.number().default(8), padding: z.number().default(16), touchFeedback: z.boolean().default(true), swipeActions: z.boolean().default(false), // 데이터 바인딩 tableName: z.string().optional(), columns: z.array(z.string()).optional(), titleField: z.string().optional(), subtitleField: z.string().optional(), statusField: z.string().optional(), }) .passthrough(); // POP 터치 버튼 overrides 스키마 export const popTouchButtonOverridesSchema = z .object({ variant: z.enum(["primary", "secondary", "success", "warning", "danger", "ghost"]).default("primary"), size: z.enum(["sm", "md", "lg", "xl"]).default("lg"), text: z.string().default("확인"), icon: z.string().nullable().default(null), iconPosition: z.enum(["left", "right", "top", "bottom"]).default("left"), fullWidth: z.boolean().default(true), minHeight: z.number().default(48), hapticFeedback: z.boolean().default(true), pressDelay: z.number().default(0), // 액션 actionType: z.string().optional(), actionParams: z.record(z.string(), z.any()).optional(), }) .passthrough(); // POP 스캐너 입력 overrides 스키마 export const popScannerInputOverridesSchema = z .object({ placeholder: z.string().default("바코드를 스캔하세요"), showKeyboard: z.boolean().default(false), autoFocus: z.boolean().default(true), autoSubmit: z.boolean().default(true), submitDelay: z.number().default(300), scannerMode: z.enum(["auto", "camera", "external"]).default("auto"), beepOnScan: z.boolean().default(true), vibrationOnScan: z.boolean().default(true), clearOnSubmit: z.boolean().default(true), // 데이터 바인딩 tableName: z.string().optional(), columnName: z.string().optional(), }) .passthrough(); // POP 상태 배지 overrides 스키마 export const popStatusBadgeOverridesSchema = z .object({ variant: z.enum(["default", "success", "warning", "danger", "info"]).default("default"), size: z.enum(["sm", "md", "lg"]).default("md"), text: z.string().default(""), icon: z.string().nullable().default(null), rounded: z.boolean().default(true), pulse: z.boolean().default(false), // 조건부 스타일 conditionField: z.string().optional(), conditionMapping: z.record(z.string(), z.string()).optional(), }) .passthrough(); // ============================================ // POP 컴포넌트 overrides 스키마 레지스트리 // ============================================ export const popComponentOverridesSchemaRegistry: Record = { "pop-card-list": popCardListOverridesSchema, "pop-touch-button": popTouchButtonOverridesSchema, "pop-scanner-input": popScannerInputOverridesSchema, "pop-status-badge": popStatusBadgeOverridesSchema, }; // ============================================ // POP 컴포넌트 기본값 레지스트리 // ============================================ export const popComponentDefaultsRegistry: Record> = { "pop-card-list": popCardListDefaults, "pop-touch-button": popTouchButtonDefaults, "pop-scanner-input": popScannerInputDefaults, "pop-status-badge": popStatusBadgeDefaults, }; // ============================================ // POP 기본값 조회 함수 // ============================================ export function getPopComponentDefaults(componentType: string): Record { return popComponentDefaultsRegistry[componentType] || {}; } // ============================================ // POP URL로 기본값 조회 // ============================================ export function getPopDefaultsByUrl(componentUrl: string): Record { // "@/lib/registry/pop-components/pop-card-list" → "pop-card-list" const parts = componentUrl.split("/"); const componentType = parts[parts.length - 1]; return getPopComponentDefaults(componentType); } // ============================================ // POP overrides 파싱 및 검증 // ============================================ export function parsePopOverridesByUrl( componentUrl: string, overrides: Record, ): Record { const parts = componentUrl.split("/"); const componentType = parts[parts.length - 1]; const schema = popComponentOverridesSchemaRegistry[componentType]; if (!schema) { // 스키마 없으면 그대로 반환 return overrides || {}; } try { return schema.parse(overrides || {}); } catch { // 파싱 실패 시 기본값 반환 return getPopComponentDefaults(componentType); } }