Add extensions and implement ToolbarButton

This commit is contained in:
Justin Edmund 2023-07-06 01:53:42 -07:00
parent 516b34752f
commit fc0a4b1165
2 changed files with 177 additions and 86 deletions

View file

@ -61,6 +61,44 @@
font-style: italic; font-style: italic;
} }
ul {
padding: 0 $unit-2x;
list-style-type: disc;
}
ol {
padding: 0 $unit-2x;
list-style-type: decimal;
}
h1 {
font-size: $font-xlarge;
font-weight: $medium;
margin: $unit 0;
text-align: left;
}
h2 {
font-size: $font-large;
font-weight: $medium;
margin: $unit 0;
text-align: left;
}
h3 {
font-size: $font-regular;
font-weight: $medium;
margin: $unit 0;
text-align: left;
}
mark {
border-radius: $item-corner-small;
background: var(--highlight-bg);
color: var(--highlight-text);
padding: 1px $unit-fourth;
}
iframe { iframe {
background: var(--input-bound-bg); background: var(--input-bound-bg);
border-radius: $card-corner; border-radius: $card-corner;
@ -180,26 +218,6 @@
padding: $unit; padding: $unit;
z-index: 10; z-index: 10;
button {
background: var(--toolbar-item-bg);
border-radius: $bubble-menu-item-corner;
color: var(--toolbar-item-text);
font-weight: $medium;
font-size: $font-small;
padding: $unit-half $unit;
&:hover {
background: var(--toolbar-item-bg-hover);
color: var(--toolbar-item-text-hover);
cursor: pointer;
}
&.active {
background: var(--toolbar-item-bg-active);
color: var(--toolbar-item-text-active);
}
}
.divider { .divider {
background: var(--toolbar-divider-bg); background: var(--toolbar-divider-bg);
border-radius: $full-corner; border-radius: $full-corner;

View file

@ -1,15 +1,29 @@
import { ComponentProps, useCallback, useEffect, useState } from 'react' import { ComponentProps, useCallback, useEffect } from 'react'
import { useRouter } from 'next/router' import { useRouter } from 'next/router'
import { useEditor, Editor as TiptapEditor, EditorContent } from '@tiptap/react' import { useEditor, EditorContent } from '@tiptap/react'
import StarterKit from '@tiptap/starter-kit' import StarterKit from '@tiptap/starter-kit'
import Link from '@tiptap/extension-link' import Link from '@tiptap/extension-link'
import Highlight from '@tiptap/extension-highlight'
import Typography from '@tiptap/extension-typography'
import Youtube from '@tiptap/extension-youtube' import Youtube from '@tiptap/extension-youtube'
import CustomMention from '~extensions/CustomMention' import CustomMention from '~extensions/CustomMention'
import classNames from 'classnames' import classNames from 'classnames'
import { mentionSuggestionOptions } from '~utils/mentionSuggestions' import { mentionSuggestionOptions } from '~utils/mentionSuggestions'
import type { JSONContent } from '@tiptap/core' import type { JSONContent } from '@tiptap/core'
import ToolbarButton from '~components/common/ToolbarButton'
import BoldIcon from 'remixicon-react/BoldIcon'
import ItalicIcon from 'remixicon-react/ItalicIcon'
import StrikethroughIcon from 'remixicon-react/StrikethroughIcon'
import UnorderedListIcon from 'remixicon-react/ListUnorderedIcon'
import OrderedListIcon from '~public/icons/remix/list-ordered-2.svg'
import PaintbrushIcon from 'remixicon-react/PaintbrushLineIcon'
import H1Icon from 'remixicon-react/H1Icon'
import H2Icon from 'remixicon-react/H2Icon'
import H3Icon from 'remixicon-react/H3Icon'
import LinkIcon from 'remixicon-react/LinkIcon'
import YoutubeIcon from 'remixicon-react/YoutubeLineIcon'
import styles from './index.module.scss' import styles from './index.module.scss'
interface Props extends ComponentProps<'div'> { interface Props extends ComponentProps<'div'> {
@ -30,7 +44,7 @@ const Editor = ({
const router = useRouter() const router = useRouter()
const locale = router.locale || 'en' const locale = router.locale || 'en'
const [editor, setEditor] = useState<TiptapEditor | undefined>(undefined) // const [editor, setEditor] = useState<TiptapEditor | undefined>(undefined)
function isJSON(content?: string) { function isJSON(content?: string) {
if (!content) return false if (!content) return false
@ -44,8 +58,12 @@ const Editor = ({
} }
useEffect(() => { useEffect(() => {
editor?.destroy() // console.log('Recreating editor...')
const newEditor: TiptapEditor = new TiptapEditor({ // editor?.destroy()
// setEditor(newEditor)
}, [content])
const editor = useEditor({
content: formatContent(content), content: formatContent(content),
editable: editable, editable: editable,
editorProps: { editorProps: {
@ -60,8 +78,14 @@ const Editor = ({
}, },
}, },
extensions: [ extensions: [
StarterKit, StarterKit.configure({
heading: {
levels: [1, 2, 3],
},
}),
Link, Link,
Highlight,
Typography,
CustomMention.configure({ CustomMention.configure({
renderLabel({ options, node }) { renderLabel({ options, node }) {
return `${node.attrs.id.name[locale] ?? node.attrs.id.granblue_en}` return `${node.attrs.id.name[locale] ?? node.attrs.id.granblue_en}`
@ -85,9 +109,6 @@ const Editor = ({
}, },
}) })
setEditor(newEditor)
}, [content])
function formatContent(content?: string) { function formatContent(content?: string) {
if (!content) return '' if (!content) return ''
if (isJSON(content)) return JSON.parse(content) if (isJSON(content)) return JSON.parse(content)
@ -146,32 +167,84 @@ const Editor = ({
<section className={styles.wrapper}> <section className={styles.wrapper}>
{editor && editable === true && ( {editor && editable === true && (
<nav className={styles.toolbar}> <nav className={styles.toolbar}>
<button <ToolbarButton
editor={editor}
action="bold"
icon={<BoldIcon />}
onClick={() => editor.chain().focus().toggleBold().run()} onClick={() => editor.chain().focus().toggleBold().run()}
className={editor.isActive('bold') ? styles.active : ''} />
> <ToolbarButton
bold editor={editor}
</button> action="italic"
<button icon={<ItalicIcon />}
onClick={() => editor.chain().focus().toggleItalic().run()} onClick={() => editor.chain().focus().toggleItalic().run()}
className={editor.isActive('italic') ? styles.active : ''} />
> <ToolbarButton
italic editor={editor}
</button> action="strike"
<button icon={<StrikethroughIcon />}
onClick={() => editor.chain().focus().toggleStrike().run()} onClick={() => editor.chain().focus().toggleStrike().run()}
className={editor.isActive('strike') ? styles.active : ''} />
> <ToolbarButton
strike editor={editor}
</button> action="highlight"
icon={<PaintbrushIcon />}
onClick={() => editor.chain().focus().toggleHighlight().run()}
/>
<div className={styles.divider} /> <div className={styles.divider} />
<button <ToolbarButton
editor={editor}
action="heading"
level={1}
icon={<H1Icon />}
onClick={() =>
editor.chain().focus().toggleHeading({ level: 1 }).run()
}
/>
<ToolbarButton
editor={editor}
action="heading"
level={2}
icon={<H2Icon />}
onClick={() =>
editor.chain().focus().toggleHeading({ level: 2 }).run()
}
/>
<ToolbarButton
editor={editor}
action="heading"
level={3}
icon={<H3Icon />}
onClick={() =>
editor.chain().focus().toggleHeading({ level: 3 }).run()
}
/>
<div className={styles.divider} />
<ToolbarButton
editor={editor}
action="bulletList"
icon={<UnorderedListIcon />}
onClick={() => editor.chain().focus().toggleBulletList().run()}
/>
<ToolbarButton
editor={editor}
action="orderedList"
icon={<OrderedListIcon />}
onClick={() => editor.chain().focus().toggleOrderedList().run()}
/>
<div className={styles.divider} />
<ToolbarButton
editor={editor}
action="link"
icon={<LinkIcon />}
onClick={setLink} onClick={setLink}
className={editor.isActive('link') ? styles.active : ''} />
> <ToolbarButton
+ link editor={editor}
</button> action="youtube"
<button onClick={addYoutubeVideo}>+ youtube</button> icon={<YoutubeIcon />}
onClick={addYoutubeVideo}
/>
</nav> </nav>
)} )}
<EditorContent editor={editor} /> <EditorContent editor={editor} />