import { createStore } from "zustand"; import { create } from "zustand"; // 블록 타입 정의: 텍스트/버튼/이미지/섹션 블록 export type BlockType = "text" | "button" | "image" | "section"; // 텍스트 블록 속성 export interface TextBlockProps { text: string; // 텍스트 정렬: 기본은 left align: "left" | "center" | "right"; // 텍스트 크기: 기본은 base size: "sm" | "base" | "lg"; } // 버튼 블록 속성 export interface ButtonBlockProps { label: string; href: string; // TODO: variant, size 등은 추후 확장 } // 이미지 블록 속성 export interface ImageBlockProps { src: string; alt: string; } // 섹션 블록 속성 export interface SectionBlockProps { background: "default" | "muted" | "primary"; paddingY: "sm" | "md" | "lg"; } // 공통 블록 모델 export interface Block { id: string; type: BlockType; props: TextBlockProps | ButtonBlockProps | ImageBlockProps | SectionBlockProps; } // 에디터 상태 인터페이스 export interface EditorState { blocks: Block[]; selectedBlockId: string | null; addTextBlock: () => void; addButtonBlock: () => void; addImageBlock: () => void; addSectionBlock: () => void; updateBlock: (id: string, partial: Partial) => void; selectBlock: (id: string | null) => void; replaceBlocks: (blocks: Block[]) => void; reorderBlocks: (activeId: string, overId: string) => void; } // 간단한 ID 생성기 (추후 uuid 라이브러리로 교체 가능) let idCounter = 0; const createId = () => `blk_${Date.now()}_${idCounter++}`; // 에디터 스토어 생성 함수 (테스트 및 앱에서 공유 사용) // set/get 은 zustand 내부 구현에 의해 주입되므로, 여기서는 any 로 완화해 사용한다. const createEditorState = (set: any, get: any): EditorState => ({ blocks: [], selectedBlockId: null, // 텍스트 블록 추가: 기본 텍스트와 함께 생성 후 선택 상태로 만든다 addTextBlock: () => { const id = createId(); const newBlock: Block = { id, type: "text", props: { text: "새 텍스트", align: "left", size: "base", }, }; set((state: EditorState) => ({ blocks: [...state.blocks, newBlock], selectedBlockId: id, })); }, // 이미지 블록 추가: 기본 플레이스홀더와 함께 생성 후 선택 상태로 만든다 addImageBlock: () => { const id = createId(); const newBlock: Block = { id, type: "image", props: { src: "", alt: "이미지 설명", }, }; set((state: EditorState) => ({ blocks: [...state.blocks, newBlock], selectedBlockId: id, })); }, // 섹션 블록 추가: 배경/패딩 기본값과 함께 생성 후 선택 상태로 만든다 addSectionBlock: () => { const id = createId(); const newBlock: Block = { id, type: "section", props: { background: "default", paddingY: "md", }, }; set((state: EditorState) => ({ blocks: [...state.blocks, newBlock], selectedBlockId: id, })); }, // 버튼 블록 추가: 기본 라벨/링크와 함께 생성 후 선택 상태로 만든다 addButtonBlock: () => { const id = createId(); const newBlock: Block = { id, type: "button", props: { label: "버튼", href: "#", }, }; set((state: EditorState) => ({ blocks: [...state.blocks, newBlock], selectedBlockId: id, })); }, // 특정 블록의 속성을 부분 업데이트 (텍스트/버튼 공통 사용) updateBlock: (id, partial) => { set((state: EditorState) => ({ blocks: state.blocks.map((block: Block) => block.id === id ? { ...block, props: { ...block.props, ...partial }, } : block, ), })); }, // 선택된 블록 ID를 변경 selectBlock: (id) => { const { blocks } = get(); // 존재하지 않는 ID를 선택하려고 할 경우, 그냥 null 로 초기화 if (id && !blocks.some((b: Block) => b.id === id)) { set({ selectedBlockId: null }); return; } set({ selectedBlockId: id }); }, replaceBlocks: (blocks) => { set({ blocks, selectedBlockId: blocks.length > 0 ? blocks[0].id : null, }); }, reorderBlocks: (activeId, overId) => { set((state: EditorState) => { const current = state.blocks; const oldIndex = current.findIndex((b: Block) => b.id === activeId); const newIndex = current.findIndex((b: Block) => b.id === overId); if (oldIndex === -1 || newIndex === -1 || oldIndex === newIndex) { return { blocks: current }; } const updated = [...current]; const [moved] = updated.splice(oldIndex, 1); updated.splice(newIndex, 0, moved); return { blocks: updated }; }); }, }); // React 컴포넌트에서 사용하는 전역 훅 스토어 export const useEditorStore = create()(createEditorState); // 테스트 등에서 독립적인 스토어 인스턴스를 만들 때 사용하는 팩토리 export const createEditorStore = () => createStore(createEditorState);