import React, {useState} from 'react'; import {EditorContent, useEditor, useEditorState} from '@tiptap/react'; import StarterKit from '@tiptap/starter-kit'; import {Markdown} from '@tiptap/markdown'; const BLOCKS = [ {value: 'paragraph', label: 'Paragraph'}, {value: 'h2', label: 'Heading'}, {value: 'h3', label: 'Subheading'}, ]; function ToolButton({label, active, onClick, children, ...rest}) { return ( ); } function Toolbar({editor}) { const state = useEditorState({ editor, selector: ({editor: e}) => ({ block: e.isActive('heading', {level: 2}) ? 'h2' : e.isActive('heading', {level: 3}) ? 'h3' : 'paragraph', bold: e.isActive('bold'), italic: e.isActive('italic'), link: e.isActive('link'), bulletList: e.isActive('bulletList'), blockquote: e.isActive('blockquote'), }), }); const setBlock = (value) => { const chain = editor.chain().focus(); if (value === 'paragraph') chain.setParagraph().run(); else chain.toggleHeading({level: Number(value[1])}).run(); }; const setLink = () => { const previous = editor.getAttributes('link').href || ''; const url = window.prompt('Link address', previous); if (url === null) return; if (url === '') editor.chain().focus().extendMarkRange('link').unsetLink().run(); else editor.chain().focus().extendMarkRange('link').setLink({href: url}).run(); }; return (
editor.chain().focus().toggleBold().run()}> B editor.chain().focus().toggleItalic().run()}> I Link editor.chain().focus().toggleBulletList().run()} > • List editor.chain().focus().toggleBlockquote().run()} > ❝ Quote
); } // A rich text editor for a post body that stores markdown, so people can // format without knowing it. The Markdown tab shows and edits the raw text. // The markdown goes in a hidden field called `name`, so the surrounding form // submits it as it would a textarea. export default function PostEditor({name, value = '', id = 'post_body'}) { const [markdown, setMarkdown] = useState(value); const [tab, setTab] = useState('write'); const editor = useEditor({ extensions: [StarterKit, Markdown], content: value, contentType: 'markdown', onUpdate: ({editor: e}) => setMarkdown(e.getMarkdown().trim()), editorProps: {attributes: {class: 'post-editor-content', 'aria-label': 'Post body', role: 'textbox', 'aria-multiline': 'true'}}, }); const showTab = (next) => { if (next === 'write' && editor) { editor.commands.setContent(markdown, {contentType: 'markdown'}); } setTab(next); }; return (
{[['write', 'Write'], ['markdown', 'Markdown']].map(([key, label]) => ( ))}
{tab === 'write' && editor && ( <> )} {tab === 'markdown' && (