diff --git a/components/common/Editor/index.module.scss b/components/common/Editor/index.module.scss index faebbf51..6be160ed 100644 --- a/components/common/Editor/index.module.scss +++ b/components/common/Editor/index.module.scss @@ -61,6 +61,44 @@ 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 { background: var(--input-bound-bg); border-radius: $card-corner; @@ -180,26 +218,6 @@ padding: $unit; 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 { background: var(--toolbar-divider-bg); border-radius: $full-corner; diff --git a/components/common/Editor/index.tsx b/components/common/Editor/index.tsx index 69620dbe..06bb5dd2 100644 --- a/components/common/Editor/index.tsx +++ b/components/common/Editor/index.tsx @@ -1,15 +1,29 @@ -import { ComponentProps, useCallback } from 'react' +import { ComponentProps, useCallback, useEffect } 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 Highlight from '@tiptap/extension-highlight' +import Typography from '@tiptap/extension-typography' 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 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' interface Props extends ComponentProps<'div'> { @@ -27,9 +41,62 @@ const Editor = ({ onUpdate, ...props }: Props) => { + // Hooks: Router const router = useRouter() const locale = router.locale || 'en' + useEffect(() => { + editor?.commands.setContent(formatContent(content)) + }, [content]) + + // Setup: Editor + 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.configure({ + heading: { + levels: [1, 2, 3], + }, + }), + Link, + Highlight, + Typography, + 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) + }, + }) + + // Methods: Convenience function isJSON(content?: string) { if (!content) return false @@ -59,46 +126,7 @@ const Editor = ({ } } - 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) - }, - }) - + // Methods: Actions const setLink = useCallback(() => { const previousUrl = editor?.getAttributes('link').href const url = window.prompt('URL', previousUrl) @@ -131,6 +159,7 @@ const Editor = ({ } } + // Methods: Rendering if (!editor) { return null } @@ -139,32 +168,84 @@ const Editor = ({
{editor && editable === true && ( )} diff --git a/components/common/ToolbarButton/index.module.scss b/components/common/ToolbarButton/index.module.scss new file mode 100644 index 00000000..bbfdbef7 --- /dev/null +++ b/components/common/ToolbarButton/index.module.scss @@ -0,0 +1,36 @@ +.button { + background: var(--toolbar-item-bg); + border-radius: $bubble-menu-item-corner; + color: var(--toolbar-item-text); + display: flex; + align-items: center; + justify-content: center; + font-weight: $medium; + font-size: $font-small; + padding: $unit-half; + + &:hover { + background: var(--toolbar-item-bg-hover); + color: var(--toolbar-item-text-hover); + cursor: pointer; + + svg { + fill: var(--text-primary); + } + } + + &.active { + background: var(--toolbar-item-bg-active); + color: var(--toolbar-item-text-active); + + svg { + fill: white; + } + } + + svg { + fill: var(--text-tertiary); + height: $unit-2x; + width: $unit-2x; + } +} diff --git a/components/common/ToolbarButton/index.tsx b/components/common/ToolbarButton/index.tsx new file mode 100644 index 00000000..363d4187 --- /dev/null +++ b/components/common/ToolbarButton/index.tsx @@ -0,0 +1,35 @@ +import { useTranslation } from 'next-i18next' +import classNames from 'classnames' + +import { Editor } from '@tiptap/react' +import Tooltip from '~components/common/Tooltip' + +import styles from './index.module.scss' + +interface Props { + editor: Editor + action: string + level?: number + icon: React.ReactNode + onClick: () => void +} + +const ToolbarIcon = ({ editor, action, level, icon, onClick }: Props) => { + const { t } = useTranslation('common') + const classes = classNames({ + [styles.button]: true, + [styles.active]: level + ? editor.isActive(action, { level: level }) + : editor.isActive(action), + }) + + return ( + + + + ) +} + +export default ToolbarIcon diff --git a/components/party/EditPartyModal/index.tsx b/components/party/EditPartyModal/index.tsx index 8f346544..e794fdca 100644 --- a/components/party/EditPartyModal/index.tsx +++ b/components/party/EditPartyModal/index.tsx @@ -466,22 +466,12 @@ const EditPartyModal = ({ } } - const descriptionField = ( -