mirror of
https://github.com/priyanshujain/margin.git
synced 2026-10-03 11:37:03 +00:00
feat(ui): connect shell to store with live preview dock
This commit is contained in:
1 parent
3cf07d4ef6
commit
29c412849e
5 files changed
+197
-71
No files matched your search
@@ -0,0 +1,40 @@
|
||||
import { useMemo } from "react";
|
||||
import { generateHTML } from "@tiptap/core";
|
||||
import { useBook } from "../store/useBook";
|
||||
import { editorExtensions } from "../editor/extensions";
|
||||
|
||||
const TRIM_LABEL: Record<string, string> = {
|
||||
"6x9": "6 × 9 in",
|
||||
"5.5x8.5": "5.5 × 8.5 in",
|
||||
"5x8": "5 × 8 in",
|
||||
a5: "A5",
|
||||
};
|
||||
|
||||
export function Dock() {
|
||||
const chapters = useBook((s) => s.book.chapters);
|
||||
const trim = useBook((s) => s.book.settings.trim);
|
||||
const activeChapterId = useBook((s) => s.activeChapterId);
|
||||
|
||||
const idx = chapters.findIndex((c) => c.id === activeChapterId);
|
||||
const chapter = chapters[idx];
|
||||
const html = useMemo(() => generateHTML(chapter.content, editorExtensions), [chapter.content]);
|
||||
|
||||
return (
|
||||
<section className="dock">
|
||||
<div className="dock-head">
|
||||
<span className="label">Preview</span>
|
||||
<span className="meta">
|
||||
{TRIM_LABEL[trim]} · {idx + 1} of {chapters.length}
|
||||
</span>
|
||||
</div>
|
||||
<div className="page">
|
||||
<div className="p-opener">
|
||||
<div className="p-num">Chapter {idx + 1}</div>
|
||||
<div className="p-title">{chapter.title || "Untitled"}</div>
|
||||
</div>
|
||||
<div className="page-body" dangerouslySetInnerHTML={{ __html: html }} />
|
||||
<div className="folio">{idx * 8 + 7}</div>
|
||||
</div>
|
||||
</section>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
interface IconProps {
|
||||
d: string;
|
||||
size?: number;
|
||||
}
|
||||
|
||||
export function Icon({ d, size = 16 }: IconProps) {
|
||||
return (
|
||||
<svg
|
||||
width={size}
|
||||
height={size}
|
||||
viewBox="0 0 24 24"
|
||||
fill="none"
|
||||
stroke="currentColor"
|
||||
strokeWidth="1.6"
|
||||
strokeLinecap="round"
|
||||
strokeLinejoin="round"
|
||||
>
|
||||
<path d={d} />
|
||||
</svg>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
import { useBook } from "../store/useBook";
|
||||
import { Icon } from "./Icon";
|
||||
|
||||
export function Sidebar() {
|
||||
const chapters = useBook((s) => s.book.chapters);
|
||||
const activeChapterId = useBook((s) => s.activeChapterId);
|
||||
const setActiveChapter = useBook((s) => s.setActiveChapter);
|
||||
const addChapter = useBook((s) => s.addChapter);
|
||||
|
||||
return (
|
||||
<aside className="sidebar">
|
||||
<div className="brand">
|
||||
<span className="mark">margin</span>
|
||||
<span className="dot" />
|
||||
</div>
|
||||
<div className="nav-label">Chapters</div>
|
||||
<ul className="chapters">
|
||||
{chapters.map((chapter, i) => (
|
||||
<li
|
||||
key={chapter.id}
|
||||
className="chapter"
|
||||
data-active={chapter.id === activeChapterId}
|
||||
onClick={() => setActiveChapter(chapter.id)}
|
||||
>
|
||||
<span className="num">{i + 1}</span>
|
||||
<span className="title">{chapter.title || "Untitled"}</span>
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
<button className="add-chapter" onClick={addChapter}>
|
||||
<Icon d="M12 5v14M5 12h14" />
|
||||
New chapter
|
||||
</button>
|
||||
</aside>
|
||||
);
|
||||
}
|
||||
Reference in new issue
Block a user