Skip to content

Commit

Permalink
feat: init living editor
Browse files Browse the repository at this point in the history
  • Loading branch information
phodal committed Nov 20, 2023
1 parent 5e102f2 commit 6102249
Show file tree
Hide file tree
Showing 8 changed files with 1,541 additions and 118 deletions.
299 changes: 299 additions & 0 deletions components/editor/live-editor.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,299 @@
import {Color} from '@tiptap/extension-color'
import ListItem from '@tiptap/extension-list-item'
import TextStyle from '@tiptap/extension-text-style'
import { FloatingMenu, Extension, useCurrentEditor, useEditor, EditorProvider} from '@tiptap/react'
import StarterKit from '@tiptap/starter-kit'
import React, {useEffect} from 'react'

const MenuBar = () => {
const {editor} = useCurrentEditor()

if (!editor) {
return null
}

return (
<>
<button
onClick={() => editor.chain().focus().toggleBold().run()}
disabled={
!editor.can()
.chain()
.focus()
.toggleBold()
.run()
}
className={editor.isActive('bold') ? 'is-active' : ''}
>
bold
</button>
<button
onClick={() => editor.chain().focus().toggleItalic().run()}
disabled={
!editor.can()
.chain()
.focus()
.toggleItalic()
.run()
}
className={editor.isActive('italic') ? 'is-active' : ''}
>
italic
</button>
<button
onClick={() => editor.chain().focus().toggleStrike().run()}
disabled={
!editor.can()
.chain()
.focus()
.toggleStrike()
.run()
}
className={editor.isActive('strike') ? 'is-active' : ''}
>
strike
</button>
<button
onClick={() => editor.chain().focus().toggleCode().run()}
disabled={
!editor.can()
.chain()
.focus()
.toggleCode()
.run()
}
className={editor.isActive('code') ? 'is-active' : ''}
>
code
</button>
<button onClick={() => editor.chain().focus().unsetAllMarks().run()}>
clear marks
</button>
<button onClick={() => editor.chain().focus().clearNodes().run()}>
clear nodes
</button>
<button
onClick={() => editor.chain().focus().setParagraph().run()}
className={editor.isActive('paragraph') ? 'is-active' : ''}
>
paragraph
</button>
<button
onClick={() => editor.chain().focus().toggleHeading({level: 1}).run()}
className={editor.isActive('heading', {level: 1}) ? 'is-active' : ''}
>
h1
</button>
<button
onClick={() => editor.chain().focus().toggleHeading({level: 2}).run()}
className={editor.isActive('heading', {level: 2}) ? 'is-active' : ''}
>
h2
</button>
<button
onClick={() => editor.chain().focus().toggleHeading({level: 3}).run()}
className={editor.isActive('heading', {level: 3}) ? 'is-active' : ''}
>
h3
</button>
<button
onClick={() => editor.chain().focus().toggleHeading({level: 4}).run()}
className={editor.isActive('heading', {level: 4}) ? 'is-active' : ''}
>
h4
</button>
<button
onClick={() => editor.chain().focus().toggleHeading({level: 5}).run()}
className={editor.isActive('heading', {level: 5}) ? 'is-active' : ''}
>
h5
</button>
<button
onClick={() => editor.chain().focus().toggleHeading({level: 6}).run()}
className={editor.isActive('heading', {level: 6}) ? 'is-active' : ''}
>
h6
</button>
<button
onClick={() => editor.chain().focus().toggleBulletList().run()}
className={editor.isActive('bulletList') ? 'is-active' : ''}
>
bullet list
</button>
<button
onClick={() => editor.chain().focus().toggleOrderedList().run()}
className={editor.isActive('orderedList') ? 'is-active' : ''}
>
ordered list
</button>
<button
onClick={() => editor.chain().focus().toggleCodeBlock().run()}
className={editor.isActive('codeBlock') ? 'is-active' : ''}
>
code block
</button>
<button
onClick={() => editor.chain().focus().toggleBlockquote().run()}
className={editor.isActive('blockquote') ? 'is-active' : ''}
>
blockquote
</button>
<button onClick={() => editor.chain().focus().setHorizontalRule().run()}>
horizontal rule
</button>
<button onClick={() => editor.chain().focus().setHardBreak().run()}>
hard break
</button>
<button
onClick={() => editor.chain().focus().undo().run()}
disabled={
!editor.can()
.chain()
.focus()
.undo()
.run()
}
>
undo
</button>
<button
onClick={() => editor.chain().focus().redo().run()}
disabled={
!editor.can()
.chain()
.focus()
.redo()
.run()
}
>
redo
</button>
<button
onClick={() => editor.chain().focus().setColor('#958DF1').run()}
className={editor.isActive('textStyle', {color: '#958DF1'}) ? 'is-active' : ''}
>
purple
</button>
</>
)
}

const AiPlugin = Extension.create({
// @ts-ignore
addKeyboardShortcuts() {
return {
'/': (props) => {
const editor = props.editor
//
if (editor.isActive('paragraph')) {
console.log("paragraph")
}
// editor.isActive('heading', {level: 3})
if (editor.isActive('heading')) {
console.log("heading")
}
}
}
}
})

const extensions = [
AiPlugin,
Color.configure({types: [TextStyle.name, ListItem.name]}),
// @ts-ignore
TextStyle.configure({types: [ListItem.name]}),
StarterKit.configure({
bulletList: {
keepMarks: true,
keepAttributes: false, // TODO : Making this as `false` becase marks are not preserved when I try to preserve attrs, awaiting a bit of help
},
orderedList: {
keepMarks: true,
keepAttributes: false, // TODO : Making this as `false` becase marks are not preserved when I try to preserve attrs, awaiting a bit of help
},
}),
]

const content = `
<h2>
Hi there,
</h2>
<p>
this is a <em>basic</em> example of <strong>tiptap</strong>. Sure, there are all kind of basic text styles you’d probably expect from a text editor. But wait until you see the lists:
</p>
<ul>
<li>
That’s a bullet list with one …
</li>
<li>
… or two list items.
</li>
</ul>
<p>
Isn’t that great? And all of that is editable. But wait, there’s more. Let’s try a code block:
</p>
<pre><code class="language-css">body {
display: none;
}</code></pre>
<p>
I know, I know, this is impressive. It’s only the tip of the iceberg though. Give it a try and click a little bit around. Don’t forget to check the other examples too.
</p>
<blockquote>
Wow, that’s amazing. Good work, boy! 👏
<br />
— Mom
</blockquote>
`

const LiveEditor = () => {
const editor = useEditor({
extensions: extensions,
content: content,
})

const [isEditable, setIsEditable] = React.useState(true)

useEffect(() => {
if (editor) {
editor.setEditable(isEditable)
}
}, [isEditable, editor])

return (
<>
<div>
<input type="checkbox" checked={isEditable} onChange={() => setIsEditable(!isEditable)}/>
Editable
</div>
{editor && <FloatingMenu editor={editor} tippyOptions={{duration: 100}}>
<button
onClick={() => editor.chain().focus().toggleHeading({level: 1}).run()}
className={editor.isActive('heading', {level: 1}) ? 'is-active' : ''}
>
h1
</button>
<button
onClick={() => editor.chain().focus().toggleHeading({level: 2}).run()}
className={editor.isActive('heading', {level: 2}) ? 'is-active' : ''}
>
h2
</button>
<button
onClick={() => editor.chain().focus().toggleBulletList().run()}
className={editor.isActive('bulletList') ? 'is-active' : ''}
>
bullet list
</button>
</FloatingMenu>}
<EditorProvider
extensions={extensions}
content={content}
slotBefore={<MenuBar/>}
>

</EditorProvider>
</>
)
}

export default LiveEditor
2 changes: 1 addition & 1 deletion next.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ const nextConfig = {
*
* @see https://nextjs.org/docs/app/api-reference/next-config-js/basePath
*/
basePath: "/2b",
// basePath: "/2b",

/**
* Disable server-based image optimization. Next.js does not support
Expand Down
Loading

0 comments on commit 6102249

Please sign in to comment.