import { ComponentProps, useCallback } from 'react' import { useRouter } from 'next/router' import { useEditor, EditorContent } from '@tiptap/react' import StarterKit from '@tiptap/starter-kit' import Link from '@tiptap/extension-link' import Youtube from '@tiptap/extension-youtube' import CustomMention from '~extensions/CustomMention' import classNames from 'classnames' import { mentionSuggestionOptions } from '~utils/mentionSuggestions' import type { JSONContent } from '@tiptap/core' import styles from './index.module.scss' interface Props extends ComponentProps<'div'> { bound: boolean editable?: boolean content?: string onUpdate?: (content: JSONContent) => void } const Editor = ({ bound, className, content, editable, onUpdate, ...props }: Props) => { const router = useRouter() const locale = router.locale || 'en' function isJSON(content?: string) { if (!content) return false try { JSON.parse(content) } catch (e) { return false } return true } function formatContent(content?: string) { if (!content) return '' if (isJSON(content)) return JSON.parse(content) else { // Otherwise, create a new

tag after each double newline. // Add < br /> tags for single newlines. // Add a < br /> after each paragraph. const paragraphs = content.split('\n\n') const formatted = paragraphs .map((p) => { const lines = p.split('\n') return lines.join('
') }) .join('


') return formatted } } const editor = useEditor({ content: formatContent(content), editable: editable, editorProps: { attributes: { class: classNames( { [styles.editor]: true, [styles.bound]: bound, }, className?.split(' ').map((c) => styles[c]) ), }, }, extensions: [ StarterKit, Link, CustomMention.configure({ renderLabel({ options, node }) { return `${node.attrs.id.name[locale] ?? node.attrs.id.granblue_en}` }, suggestion: mentionSuggestionOptions, HTMLAttributes: { class: classNames({ [styles.mention]: true, }), }, }), Youtube.configure({ inline: false, modestBranding: true, interfaceLanguage: locale, }), ], onUpdate: ({ editor }) => { const json = editor.getJSON() if (onUpdate) onUpdate(json) }, }) const setLink = useCallback(() => { const previousUrl = editor?.getAttributes('link').href const url = window.prompt('URL', previousUrl) // cancelled if (url === null) { return } // empty if (url === '') { editor?.chain().focus().extendMarkRange('link').unsetLink().run() return } // update link editor?.chain().focus().extendMarkRange('link').setLink({ href: url }).run() }, [editor]) const addYoutubeVideo = () => { const url = prompt('Enter YouTube URL') if (editor && url) { editor.commands.setYoutubeVideo({ src: url, width: 320, height: 180, }) } } if (!editor) { return null } return (

{editor && editable === true && ( )}
) } Editor.defaultProps = { bound: false, editable: false, } export default Editor