A deployable markdown editor that connects with your self hosted files and lets you edit in a beautiful interface
at main 143 lines 4.3 kB view raw
1import { Editor } from '@tiptap/react'; 2 3interface MenuBarProps { 4 editor: Editor; 5} 6 7export function MenuBar({ editor }: MenuBarProps) { 8 const buttonClass = (isActive: boolean) => 9 `px-3 py-1.5 text-sm font-semibold transition-colors border-2 border-transparent ${ 10 isActive 11 ? 'bg-gray-900 text-white' 12 : 'bg-gray-100 text-gray-900 hover:bg-gray-200' 13 }`; 14 15 return ( 16 <div className="border-b-2 border-gray-900 p-3 bg-amber-50 flex flex-wrap gap-1"> 17 <button 18 onClick={() => editor.chain().focus().toggleBold().run()} 19 disabled={!editor.can().chain().focus().toggleBold().run()} 20 className={buttonClass(editor.isActive('bold'))} 21 title="Bold (Cmd+B)" 22 > 23 <strong>B</strong> 24 </button> 25 26 <button 27 onClick={() => editor.chain().focus().toggleItalic().run()} 28 disabled={!editor.can().chain().focus().toggleItalic().run()} 29 className={buttonClass(editor.isActive('italic'))} 30 title="Italic (Cmd+I)" 31 > 32 <em>I</em> 33 </button> 34 35 <button 36 onClick={() => editor.chain().focus().toggleCode().run()} 37 disabled={!editor.can().chain().focus().toggleCode().run()} 38 className={buttonClass(editor.isActive('code'))} 39 title="Inline Code" 40 > 41 {'</>'} 42 </button> 43 44 <div className="w-px bg-gray-900 mx-1"></div> 45 46 <button 47 onClick={() => editor.chain().focus().toggleHeading({ level: 1 }).run()} 48 className={buttonClass(editor.isActive('heading', { level: 1 }))} 49 title="Heading 1" 50 > 51 H1 52 </button> 53 54 <button 55 onClick={() => editor.chain().focus().toggleHeading({ level: 2 }).run()} 56 className={buttonClass(editor.isActive('heading', { level: 2 }))} 57 title="Heading 2" 58 > 59 H2 60 </button> 61 62 <button 63 onClick={() => editor.chain().focus().toggleHeading({ level: 3 }).run()} 64 className={buttonClass(editor.isActive('heading', { level: 3 }))} 65 title="Heading 3" 66 > 67 H3 68 </button> 69 70 <div className="w-px bg-gray-900 mx-1"></div> 71 72 <button 73 onClick={() => editor.chain().focus().toggleBulletList().run()} 74 className={buttonClass(editor.isActive('bulletList'))} 75 title="Bullet List" 76 > 77 List 78 </button> 79 80 <button 81 onClick={() => editor.chain().focus().toggleOrderedList().run()} 82 className={buttonClass(editor.isActive('orderedList'))} 83 title="Numbered List" 84 > 85 1. List 86 </button> 87 88 <button 89 onClick={() => editor.chain().focus().toggleBlockquote().run()} 90 className={buttonClass(editor.isActive('blockquote'))} 91 title="Quote" 92 > 93 " Quote 94 </button> 95 96 <button 97 onClick={() => editor.chain().focus().toggleCodeBlock().run()} 98 className={buttonClass(editor.isActive('codeBlock'))} 99 title="Code Block" 100 > 101 {'{ } Code'} 102 </button> 103 104 <div className="w-px bg-gray-900 mx-1"></div> 105 106 <button 107 onClick={() => editor.chain().focus().setHorizontalRule().run()} 108 className={buttonClass(false)} 109 title="Horizontal Rule" 110 > 111 ─── 112 </button> 113 114 <button 115 onClick={() => editor.chain().focus().setHardBreak().run()} 116 className={buttonClass(false)} 117 title="Line Break" 118 > 119120 </button> 121 122 <div className="w-px bg-gray-900 mx-1"></div> 123 124 <button 125 onClick={() => editor.chain().focus().undo().run()} 126 disabled={!editor.can().chain().focus().undo().run()} 127 className="px-3 py-1.5 text-sm font-semibold bg-gray-100 text-gray-900 hover:bg-gray-200 disabled:opacity-50 disabled:cursor-not-allowed border-2 border-transparent transition-colors" 128 title="Undo (Cmd+Z)" 129 > 130 ↶ Undo 131 </button> 132 133 <button 134 onClick={() => editor.chain().focus().redo().run()} 135 disabled={!editor.can().chain().focus().redo().run()} 136 className="px-3 py-1.5 text-sm font-semibold bg-gray-100 text-gray-900 hover:bg-gray-200 disabled:opacity-50 disabled:cursor-not-allowed border-2 border-transparent transition-colors" 137 title="Redo (Cmd+Shift+Z)" 138 > 139 Redo 140 </button> 141 </div> 142 ); 143}