리스트/에디터 스타일 정리 및 버그 수정
This commit is contained in:
+61
-14
@@ -3,6 +3,7 @@
|
||||
import type { CSSProperties, ReactNode } from "react";
|
||||
import { Fragment, useEffect, useState } from "react";
|
||||
import Link from "next/link";
|
||||
import { FilePlus2, Trash2, Eye, Pencil, ListChecks, Undo2, Redo2 } from "lucide-react";
|
||||
import {
|
||||
DndContext,
|
||||
PointerSensor,
|
||||
@@ -132,6 +133,11 @@ export default function EditorPage() {
|
||||
// 멀티 선택(MVP)을 위한 로컬 선택 상태: 단일 선택(selectedBlockId)과 병행 사용한다.
|
||||
const [selectedBlockIds, setSelectedBlockIds] = useState<string[]>([]);
|
||||
|
||||
const historyLength = useEditorStore((state) => (state as any).history?.length ?? 0);
|
||||
const futureLength = useEditorStore((state) => (state as any).future?.length ?? 0);
|
||||
const canUndo = historyLength > 0;
|
||||
const canRedo = futureLength > 0;
|
||||
|
||||
const [menuOpen, setMenuOpen] = useState(false);
|
||||
const [activeModal, setActiveModal] = useState<"project" | "json" | null>(null);
|
||||
|
||||
@@ -738,31 +744,57 @@ export default function EditorPage() {
|
||||
|
||||
return (
|
||||
<main className="h-screen flex flex-col overflow-hidden">
|
||||
<header className="border-b border-slate-800 px-6 py-4 flex items-center justify-between relative z-20">
|
||||
<div className="flex items-baseline gap-3">
|
||||
<h1 className="text-xl font-semibold">Page Editor</h1>
|
||||
<p className="text-xs text-slate-400">빌더 MVP용 에디터 페이지 골격</p>
|
||||
<header className="border-b border-slate-800 px-6 py-4 flex items-center justify-between relative z-20 bg-slate-950/80 backdrop-blur">
|
||||
<div className="flex items-center gap-3">
|
||||
<span className="inline-flex items-center justify-center w-8 h-8 rounded-full bg-slate-900 border border-sky-700 shadow-sm">
|
||||
<Pencil className="w-4 h-4 text-sky-400" aria-hidden="true" />
|
||||
</span>
|
||||
<div className="flex flex-col">
|
||||
<h1 className="text-xl font-semibold">Page Editor</h1>
|
||||
<p className="text-xs text-slate-400">빌더 MVP용 에디터 페이지 골격</p>
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex items-center gap-2 text-xs">
|
||||
<Link
|
||||
href="/projects"
|
||||
className="inline-flex items-center rounded border border-slate-700 bg-slate-900 px-3 py-1 text-slate-100 hover:bg-slate-800"
|
||||
className="inline-flex items-center gap-1 rounded border border-slate-700 bg-slate-900 px-3 py-1 text-slate-100 hover:bg-slate-800"
|
||||
>
|
||||
프로젝트 목록
|
||||
<ListChecks className="w-3 h-3" aria-hidden="true" />
|
||||
<span>프로젝트 목록</span>
|
||||
</Link>
|
||||
<Link
|
||||
href="/preview"
|
||||
className="inline-flex items-center rounded border border-slate-700 bg-slate-900 px-3 py-1 text-slate-100 hover:bg-slate-800"
|
||||
className="inline-flex items-center gap-1 rounded border border-slate-700 bg-slate-900 px-3 py-1 text-slate-100 hover:bg-slate-800"
|
||||
>
|
||||
프리뷰 열기
|
||||
<Eye className="w-3 h-3" aria-hidden="true" />
|
||||
<span>프리뷰 열기</span>
|
||||
</Link>
|
||||
<button
|
||||
type="button"
|
||||
className="inline-flex items-center gap-1 rounded border border-slate-700 bg-slate-900 px-3 py-1 text-slate-100 hover:bg-slate-800 disabled:opacity-40 disabled:cursor-not-allowed"
|
||||
onClick={undo}
|
||||
disabled={!canUndo}
|
||||
>
|
||||
<Undo2 className="w-3 h-3" aria-hidden="true" />
|
||||
<span>실행 취소</span>
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
className="inline-flex items-center gap-1 rounded border border-slate-700 bg-slate-900 px-3 py-1 text-slate-100 hover:bg-slate-800 disabled:opacity-40 disabled:cursor-not-allowed"
|
||||
onClick={redo}
|
||||
disabled={!canRedo}
|
||||
>
|
||||
<Redo2 className="w-3 h-3" aria-hidden="true" />
|
||||
<span>다시 실행</span>
|
||||
</button>
|
||||
<div className="relative">
|
||||
<button
|
||||
type="button"
|
||||
className="inline-flex items-center gap-1 rounded border border-slate-700 bg-slate-900 px-3 py-1 text-slate-100 hover:bg-slate-800"
|
||||
onClick={() => setMenuOpen((prev) => !prev)}
|
||||
>
|
||||
메뉴
|
||||
<FilePlus2 className="w-3 h-3" aria-hidden="true" />
|
||||
<span>메뉴</span>
|
||||
<span className="text-[9px]">▼</span>
|
||||
</button>
|
||||
{menuOpen && (
|
||||
@@ -775,7 +807,10 @@ export default function EditorPage() {
|
||||
setActiveModal("project");
|
||||
}}
|
||||
>
|
||||
프로젝트 저장/불러오기
|
||||
<span className="inline-flex items-center gap-2">
|
||||
<FilePlus2 className="w-3 h-3" aria-hidden="true" />
|
||||
<span>프로젝트 저장/불러오기</span>
|
||||
</span>
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
@@ -785,7 +820,10 @@ export default function EditorPage() {
|
||||
setActiveModal("json");
|
||||
}}
|
||||
>
|
||||
JSON 내보내기/불러오기
|
||||
<span className="inline-flex items-center gap-2">
|
||||
<FilePlus2 className="w-3 h-3" aria-hidden="true" />
|
||||
<span>JSON 내보내기/불러오기</span>
|
||||
</span>
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
@@ -795,7 +833,10 @@ export default function EditorPage() {
|
||||
void handleDeleteProject();
|
||||
}}
|
||||
>
|
||||
프로젝트 삭제
|
||||
<span className="inline-flex items-center gap-2">
|
||||
<Trash2 className="w-3 h-3" aria-hidden="true" />
|
||||
<span>프로젝트 삭제</span>
|
||||
</span>
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
@@ -805,7 +846,10 @@ export default function EditorPage() {
|
||||
void handleExportZip();
|
||||
}}
|
||||
>
|
||||
페이지 파일로 내보내기 (ZIP)
|
||||
<span className="inline-flex items-center gap-2">
|
||||
<FilePlus2 className="w-3 h-3" aria-hidden="true" />
|
||||
<span>페이지 파일로 내보내기 (ZIP)</span>
|
||||
</span>
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
@@ -817,7 +861,10 @@ export default function EditorPage() {
|
||||
}
|
||||
}}
|
||||
>
|
||||
캔버스 초기화
|
||||
<span className="inline-flex items-center gap-2">
|
||||
<Trash2 className="w-3 h-3" aria-hidden="true" />
|
||||
<span>캔버스 초기화</span>
|
||||
</span>
|
||||
</button>
|
||||
</div>
|
||||
)}
|
||||
|
||||
Reference in New Issue
Block a user