feat(editor): add bubble menu
This commit is contained in:
parent
17c23d9463
commit
beefc1d5ef
@ -69,6 +69,7 @@
|
||||
"@tiptap/extension-task-item": "2.0.3",
|
||||
"@tiptap/extension-task-list": "2.0.3",
|
||||
"@tiptap/extension-typography": "2.0.3",
|
||||
"@tiptap/extension-underline": "^2.1.12",
|
||||
"@tiptap/starter-kit": "2.0.3",
|
||||
"@tiptap/suggestion": "^2.1.12",
|
||||
"@tiptap/vue-3": "2.0.3",
|
||||
|
11
pnpm-lock.yaml
generated
11
pnpm-lock.yaml
generated
@ -82,6 +82,9 @@ dependencies:
|
||||
'@tiptap/extension-typography':
|
||||
specifier: 2.0.3
|
||||
version: 2.0.3(@tiptap/core@2.1.12)
|
||||
'@tiptap/extension-underline':
|
||||
specifier: ^2.1.12
|
||||
version: 2.1.12(@tiptap/core@2.1.12)
|
||||
'@tiptap/starter-kit':
|
||||
specifier: 2.0.3
|
||||
version: 2.0.3(@tiptap/pm@2.1.12)
|
||||
@ -4498,6 +4501,14 @@ packages:
|
||||
'@tiptap/core': 2.1.12(@tiptap/pm@2.1.12)
|
||||
dev: false
|
||||
|
||||
/@tiptap/extension-underline@2.1.12(@tiptap/core@2.1.12):
|
||||
resolution: {integrity: sha512-NwwdhFT8gDD0VUNLQx85yFBhP9a8qg8GPuxlGzAP/lPTV8Ubh3vSeQ5N9k2ZF/vHlEvnugzeVCbmYn7wf8vn1g==}
|
||||
peerDependencies:
|
||||
'@tiptap/core': ^2.0.0
|
||||
dependencies:
|
||||
'@tiptap/core': 2.1.12(@tiptap/pm@2.1.12)
|
||||
dev: false
|
||||
|
||||
/@tiptap/pm@2.1.12:
|
||||
resolution: {integrity: sha512-Q3MXXQABG4CZBesSp82yV84uhJh/W0Gag6KPm2HRWPimSFELM09Z9/5WK9RItAYE0aLhe4Krnyiczn9AAa1tQQ==}
|
||||
dependencies:
|
||||
|
@ -5,6 +5,60 @@
|
||||
:editor="editor"
|
||||
:upload-callback="uploadCallback"
|
||||
/>
|
||||
<BubbleMenu
|
||||
v-if="editor"
|
||||
:editor="editor"
|
||||
class="editor-bubble__wrapper"
|
||||
>
|
||||
<BaseButton
|
||||
class="editor-bubble__button"
|
||||
@click="editor.chain().focus().toggleBold().run()"
|
||||
:class="{ 'is-active': editor.isActive('bold') }"
|
||||
title="bold"
|
||||
>
|
||||
<icon :icon="['fa', 'fa-bold']"/>
|
||||
</BaseButton>
|
||||
<BaseButton
|
||||
class="editor-bubble__button"
|
||||
@click="editor.chain().focus().toggleItalic().run()"
|
||||
:class="{ 'is-active': editor.isActive('italic') }"
|
||||
title="italic"
|
||||
>
|
||||
<icon :icon="['fa', 'fa-italic']"/>
|
||||
</BaseButton>
|
||||
<BaseButton
|
||||
class="editor-bubble__button"
|
||||
@click="editor.chain().focus().toggleUnderline().run()"
|
||||
:class="{ 'is-active': editor.isActive('underline') }"
|
||||
title="italic"
|
||||
>
|
||||
<icon :icon="['fa', 'fa-underline']"/>
|
||||
</BaseButton>
|
||||
<BaseButton
|
||||
class="editor-bubble__button"
|
||||
@click="editor.chain().focus().toggleStrike().run()"
|
||||
:class="{ 'is-active': editor.isActive('strike') }"
|
||||
title="strike"
|
||||
>
|
||||
<icon :icon="['fa', 'fa-strikethrough']"/>
|
||||
</BaseButton>
|
||||
<BaseButton
|
||||
class="editor-bubble__button"
|
||||
@click="editor.chain().focus().toggleCode().run()"
|
||||
:class="{ 'is-active': editor.isActive('code') }"
|
||||
title="code"
|
||||
>
|
||||
<icon :icon="['fa', 'fa-code']"/>
|
||||
</BaseButton>
|
||||
<BaseButton
|
||||
class="editor-bubble__button"
|
||||
@click="setLink"
|
||||
:class="{ 'is-active': editor.isActive('link') }"
|
||||
title="set link"
|
||||
>
|
||||
<icon :icon="['fa', 'fa-link']"/>
|
||||
</BaseButton>
|
||||
</BubbleMenu>
|
||||
<editor-content
|
||||
class="tiptap__editor"
|
||||
:editor="editor"
|
||||
@ -42,6 +96,7 @@ import Highlight from '@tiptap/extension-highlight'
|
||||
import Typography from '@tiptap/extension-typography'
|
||||
import Document from '@tiptap/extension-document'
|
||||
import Image from '@tiptap/extension-image'
|
||||
import Underline from '@tiptap/extension-underline'
|
||||
// import Text from '@tiptap/extension-text'
|
||||
|
||||
import TaskItem from '@tiptap/extension-task-item'
|
||||
@ -50,7 +105,7 @@ import TaskList from '@tiptap/extension-task-list'
|
||||
import CharacterCount from '@tiptap/extension-character-count'
|
||||
|
||||
import StarterKit from '@tiptap/starter-kit'
|
||||
import {EditorContent, useEditor, VueNodeViewRenderer} from '@tiptap/vue-3'
|
||||
import {BubbleMenu, EditorContent, useEditor} from '@tiptap/vue-3'
|
||||
|
||||
import Commands from './commands'
|
||||
import suggestionSetup from './suggestion'
|
||||
@ -65,6 +120,7 @@ import type {IAttachment} from '@/modelTypes/IAttachment'
|
||||
import AttachmentModel from '@/models/attachment'
|
||||
import AttachmentService from '@/services/attachment'
|
||||
import {useI18n} from 'vue-i18n'
|
||||
import BaseButton from '@/components/base/BaseButton.vue'
|
||||
|
||||
const {t} = useI18n()
|
||||
|
||||
@ -181,6 +237,7 @@ const editor = useEditor({
|
||||
StarterKit,
|
||||
Highlight,
|
||||
Typography,
|
||||
Underline,
|
||||
Link.configure({
|
||||
openOnClick: false,
|
||||
validate: (href: string) => /^https?:\/\//.test(href),
|
||||
@ -215,6 +272,7 @@ const editor = useEditor({
|
||||
Commands.configure({
|
||||
suggestion: suggestionSetup(t),
|
||||
}),
|
||||
BubbleMenu,
|
||||
],
|
||||
onUpdate: () => {
|
||||
// HTML
|
||||
@ -256,7 +314,7 @@ function addImage() {
|
||||
uploadCallback(files).then(urls => {
|
||||
urls.forEach(url => {
|
||||
editor.value
|
||||
.chain()
|
||||
?.chain()
|
||||
.focus()
|
||||
.setImage({src: url})
|
||||
.run()
|
||||
@ -270,10 +328,40 @@ function addImage() {
|
||||
const url = window.prompt('URL')
|
||||
|
||||
if (url) {
|
||||
editor.value.chain().focus().setImage({src: url}).run()
|
||||
editor.value?.chain().focus().setImage({src: url}).run()
|
||||
onImageAdded()
|
||||
}
|
||||
}
|
||||
|
||||
function setLink() {
|
||||
const previousUrl = editor.value?.getAttributes('link').href
|
||||
const url = window.prompt('URL', previousUrl)
|
||||
|
||||
// cancelled
|
||||
if (url === null) {
|
||||
return
|
||||
}
|
||||
|
||||
// empty
|
||||
if (url === '') {
|
||||
editor.value
|
||||
?.chain()
|
||||
.focus()
|
||||
.extendMarkRange('link')
|
||||
.unsetLink()
|
||||
.run()
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// update link
|
||||
editor.value
|
||||
?.chain()
|
||||
.focus()
|
||||
.extendMarkRange('link')
|
||||
.setLink({href: url, target: '_blank'})
|
||||
.run()
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss">
|
||||
@ -551,4 +639,32 @@ ul[data-type='taskList'] {
|
||||
color: #868e96;
|
||||
}
|
||||
}
|
||||
|
||||
.editor-bubble__wrapper {
|
||||
background: var(--white);
|
||||
border-radius: $radius;
|
||||
border: 1px solid var(--grey-200);
|
||||
box-shadow: var(--shadow-md);
|
||||
display: flex;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.editor-bubble__button {
|
||||
color: var(--grey-700);
|
||||
transition: all $transition;
|
||||
background: transparent;
|
||||
|
||||
svg {
|
||||
box-sizing: border-box;
|
||||
display: block;
|
||||
width: 1rem;
|
||||
height: 1rem;
|
||||
padding: .5rem;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
&:hover {
|
||||
background: var(--grey-200);
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
@ -73,7 +73,7 @@ import {
|
||||
faUnlink,
|
||||
faParagraph,
|
||||
faTable,
|
||||
faX, faArrowTurnDown, faListCheck, faXmark, faXmarksLines, faFont, faRulerHorizontal,
|
||||
faX, faArrowTurnDown, faListCheck, faXmark, faXmarksLines, faFont, faRulerHorizontal, faUnderline,
|
||||
} from '@fortawesome/free-solid-svg-icons'
|
||||
import {
|
||||
faBellSlash,
|
||||
@ -185,6 +185,7 @@ library.add(faXmark)
|
||||
library.add(faXmarksLines)
|
||||
library.add(faFont)
|
||||
library.add(faRulerHorizontal)
|
||||
library.add(faUnderline)
|
||||
|
||||
// overwriting the wrong types
|
||||
export default FontAwesomeIcon as unknown as FontAwesomeIconFixedTypes
|
Loading…
x
Reference in New Issue
Block a user