diff --git a/src/editor/FigureView.tsx b/src/editor/FigureView.tsx new file mode 100644 index 0000000..b99ac58 --- /dev/null +++ b/src/editor/FigureView.tsx @@ -0,0 +1,35 @@ +import { NodeViewWrapper, type NodeViewProps } from "@tiptap/react"; + +const PLACEMENTS = [ + { key: "inline", label: "Inline" }, + { key: "full-width", label: "Full width" }, + { key: "full-page", label: "Full page" }, + { key: "float-top", label: "Float" }, +]; + +export function FigureView({ node, updateAttributes, selected }: NodeViewProps) { + const { src, alt, caption, placement } = node.attrs; + + return ( + +
+ {src ? {alt} :
No image
} +
+ updateAttributes({ caption: e.target.value })} + /> + {selected && ( +
+ {PLACEMENTS.map((p) => ( + + ))} +
+ )} +
+ ); +} diff --git a/src/editor/extensions.ts b/src/editor/extensions.ts index e395b92..a2201ad 100644 --- a/src/editor/extensions.ts +++ b/src/editor/extensions.ts @@ -1,11 +1,15 @@ import type { Extensions } from "@tiptap/core"; import StarterKit from "@tiptap/starter-kit"; import Placeholder from "@tiptap/extension-placeholder"; +import { Figure } from "./figure"; export const editorExtensions: Extensions = [ StarterKit.configure({ heading: { levels: [2, 3] }, codeBlock: false, }), - Placeholder.configure({ placeholder: "Begin your chapter…" }), + Placeholder.configure({ + placeholder: ({ node }) => (node.type.name === "heading" ? "" : "Begin your chapter…"), + }), + Figure, ]; diff --git a/src/editor/figure.ts b/src/editor/figure.ts new file mode 100644 index 0000000..cceca77 --- /dev/null +++ b/src/editor/figure.ts @@ -0,0 +1,38 @@ +import { Node, mergeAttributes } from "@tiptap/core"; +import { ReactNodeViewRenderer } from "@tiptap/react"; +import { FigureView } from "./FigureView"; + +export const Figure = Node.create({ + name: "figure", + group: "block", + atom: true, + draggable: true, + selectable: true, + + addAttributes() { + return { + src: { default: "" }, + alt: { default: "" }, + caption: { default: "" }, + placement: { default: "full-width" }, + }; + }, + + parseHTML() { + return [{ tag: "figure[data-figure]" }]; + }, + + renderHTML({ HTMLAttributes }) { + const { src, alt, caption, placement } = HTMLAttributes; + return [ + "figure", + mergeAttributes({ "data-figure": "", "data-placement": placement }), + ["img", { src, alt }], + ["figcaption", {}, caption ?? ""], + ]; + }, + + addNodeView() { + return ReactNodeViewRenderer(FigureView); + }, +});