2023-02-06 18:34:50 +09:00
|
|
|
import type { Editor } from '@tiptap/vue-3'
|
2024-09-30 17:11:56 +09:00
|
|
|
import type { Ref } from 'vue'
|
2022-11-26 11:01:50 +09:00
|
|
|
import Bold from '@tiptap/extension-bold'
|
|
|
|
import Code from '@tiptap/extension-code'
|
2024-09-30 17:11:56 +09:00
|
|
|
import Document from '@tiptap/extension-document'
|
|
|
|
import HardBreak from '@tiptap/extension-hard-break'
|
2023-01-11 06:16:56 +09:00
|
|
|
import History from '@tiptap/extension-history'
|
2024-09-30 17:11:56 +09:00
|
|
|
import Italic from '@tiptap/extension-italic'
|
|
|
|
import Mention from '@tiptap/extension-mention'
|
|
|
|
import Paragraph from '@tiptap/extension-paragraph'
|
|
|
|
import Placeholder from '@tiptap/extension-placeholder'
|
|
|
|
import Text from '@tiptap/extension-text'
|
|
|
|
import { Extension, useEditor } from '@tiptap/vue-3'
|
2022-11-25 22:21:02 +09:00
|
|
|
|
2024-09-30 17:11:56 +09:00
|
|
|
import { Plugin } from 'prosemirror-state'
|
2023-01-16 20:40:47 +09:00
|
|
|
import { TiptapPluginCustomEmoji } from './tiptap/custom-emoji'
|
|
|
|
import { TiptapPluginEmoji } from './tiptap/emoji'
|
2024-09-30 17:11:56 +09:00
|
|
|
import { TiptapPluginCodeBlockShiki } from './tiptap/shiki'
|
|
|
|
import { TiptapEmojiSuggestion, TiptapHashtagSuggestion, TiptapMentionSuggestion } from './tiptap/suggestion'
|
2022-11-25 22:21:02 +09:00
|
|
|
|
|
|
|
export interface UseTiptapOptions {
|
2023-01-04 19:21:18 +09:00
|
|
|
content: Ref<string>
|
2022-11-30 07:47:24 +09:00
|
|
|
placeholder: Ref<string | undefined>
|
2022-11-25 23:07:31 +09:00
|
|
|
onSubmit: () => void
|
2022-11-25 22:21:02 +09:00
|
|
|
onFocus: () => void
|
|
|
|
onPaste: (event: ClipboardEvent) => void
|
|
|
|
autofocus: boolean
|
|
|
|
}
|
|
|
|
|
|
|
|
export function useTiptap(options: UseTiptapOptions) {
|
2024-02-25 01:46:14 +09:00
|
|
|
if (import.meta.server)
|
2023-02-06 18:34:50 +09:00
|
|
|
return { editor: ref<Editor | undefined>() }
|
|
|
|
|
2022-11-25 22:21:02 +09:00
|
|
|
const {
|
|
|
|
autofocus,
|
|
|
|
content,
|
|
|
|
placeholder,
|
|
|
|
} = options
|
|
|
|
|
|
|
|
const editor = useEditor({
|
|
|
|
content: content.value,
|
|
|
|
extensions: [
|
|
|
|
Document,
|
|
|
|
Paragraph,
|
2022-11-26 11:01:50 +09:00
|
|
|
HardBreak,
|
|
|
|
Bold,
|
|
|
|
Italic,
|
|
|
|
Code,
|
2022-11-25 22:21:02 +09:00
|
|
|
Text,
|
2023-01-16 20:40:47 +09:00
|
|
|
TiptapPluginEmoji,
|
|
|
|
TiptapPluginCustomEmoji.configure({
|
2022-12-28 03:38:57 +09:00
|
|
|
inline: true,
|
|
|
|
HTMLAttributes: {
|
|
|
|
class: 'custom-emoji',
|
|
|
|
},
|
|
|
|
}),
|
2022-11-25 22:21:02 +09:00
|
|
|
Mention.configure({
|
2024-03-07 00:42:41 +09:00
|
|
|
renderHTML({ options, node }) {
|
|
|
|
return ['span', { 'data-type': 'mention', 'data-id': node.attrs.id }, `${options.suggestion.char}${node.attrs.label ?? node.attrs.id}`]
|
|
|
|
},
|
2023-01-16 20:40:47 +09:00
|
|
|
suggestion: TiptapMentionSuggestion,
|
2022-11-25 22:21:02 +09:00
|
|
|
}),
|
2022-11-26 15:55:54 +09:00
|
|
|
Mention
|
2023-01-05 05:47:29 +09:00
|
|
|
.extend({ name: 'hashtag' })
|
2022-11-26 15:55:54 +09:00
|
|
|
.configure({
|
2024-03-13 05:47:05 +09:00
|
|
|
renderHTML({ options, node }) {
|
|
|
|
return ['span', { 'data-type': 'hashtag', 'data-id': node.attrs.id }, `${options.suggestion.char}${node.attrs.label ?? node.attrs.id}`]
|
|
|
|
},
|
2023-01-16 20:40:47 +09:00
|
|
|
suggestion: TiptapHashtagSuggestion,
|
2022-11-26 15:55:54 +09:00
|
|
|
}),
|
2023-01-16 19:22:26 +09:00
|
|
|
Mention
|
|
|
|
.extend({ name: 'emoji' })
|
|
|
|
.configure({
|
2023-01-16 20:40:47 +09:00
|
|
|
suggestion: TiptapEmojiSuggestion,
|
2023-01-16 19:22:26 +09:00
|
|
|
}),
|
2022-11-25 22:21:02 +09:00
|
|
|
Placeholder.configure({
|
2023-01-09 18:34:26 +09:00
|
|
|
placeholder: () => placeholder.value!,
|
2022-11-25 22:21:02 +09:00
|
|
|
}),
|
2024-02-15 16:43:09 +09:00
|
|
|
TiptapPluginCodeBlockShiki,
|
2023-01-11 06:16:56 +09:00
|
|
|
History.configure({
|
|
|
|
depth: 10,
|
|
|
|
}),
|
2022-11-25 22:21:02 +09:00
|
|
|
Extension.create({
|
|
|
|
name: 'api',
|
|
|
|
addKeyboardShortcuts() {
|
|
|
|
return {
|
|
|
|
'Mod-Enter': () => {
|
2022-11-25 23:07:31 +09:00
|
|
|
options.onSubmit()
|
2022-11-25 22:21:02 +09:00
|
|
|
return true
|
|
|
|
},
|
|
|
|
}
|
|
|
|
},
|
|
|
|
onFocus() {
|
|
|
|
options.onFocus()
|
|
|
|
},
|
|
|
|
addProseMirrorPlugins() {
|
|
|
|
return [
|
|
|
|
new Plugin({
|
|
|
|
props: {
|
|
|
|
handleDOMEvents: {
|
|
|
|
paste(view, event) {
|
|
|
|
options.onPaste(event)
|
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
}),
|
|
|
|
]
|
|
|
|
},
|
|
|
|
}),
|
|
|
|
],
|
|
|
|
onUpdate({ editor }) {
|
|
|
|
content.value = editor.getHTML()
|
|
|
|
},
|
|
|
|
editorProps: {
|
|
|
|
attributes: {
|
|
|
|
class: 'content-editor content-rich',
|
|
|
|
},
|
|
|
|
},
|
2024-01-27 03:44:07 +09:00
|
|
|
parseOptions: {
|
|
|
|
preserveWhitespace: 'full',
|
|
|
|
},
|
2022-11-25 22:21:02 +09:00
|
|
|
autofocus,
|
|
|
|
editable: true,
|
|
|
|
})
|
|
|
|
|
2022-11-26 03:10:17 +09:00
|
|
|
watch(content, (value) => {
|
|
|
|
if (editor.value?.getHTML() === value)
|
|
|
|
return
|
|
|
|
editor.value?.commands.setContent(value || '', false)
|
|
|
|
})
|
2023-01-09 18:34:26 +09:00
|
|
|
watch(placeholder, () => {
|
|
|
|
editor.value?.view.dispatch(editor.value?.state.tr)
|
|
|
|
})
|
2022-11-26 03:10:17 +09:00
|
|
|
|
2022-11-25 22:21:02 +09:00
|
|
|
return {
|
|
|
|
editor,
|
|
|
|
}
|
|
|
|
}
|