Add Url input menu

This commit is contained in:
Seviche CC 2023-03-29 16:50:53 +08:00
parent d2f26c1f64
commit e34e051f41
Signed by: SevicheCC
GPG key ID: C577000000000000
5 changed files with 147 additions and 48 deletions

View file

@ -16,6 +16,7 @@
"@prosemirror-adapter/vue": "^0.2.3", "@prosemirror-adapter/vue": "^0.2.3",
"@tiptap/extension-bubble-menu": "2.0.0-beta.220", "@tiptap/extension-bubble-menu": "2.0.0-beta.220",
"@tiptap/extension-character-count": "2.0.0-beta.220", "@tiptap/extension-character-count": "2.0.0-beta.220",
"@tiptap/extension-link": "2.0.0-beta.220",
"@tiptap/extension-typography": "2.0.0-beta.220", "@tiptap/extension-typography": "2.0.0-beta.220",
"@tiptap/pm": "2.0.0-beta.220", "@tiptap/pm": "2.0.0-beta.220",
"@tiptap/starter-kit": "2.0.0-beta.220", "@tiptap/starter-kit": "2.0.0-beta.220",

View file

@ -16,6 +16,9 @@ dependencies:
'@tiptap/extension-character-count': '@tiptap/extension-character-count':
specifier: 2.0.0-beta.220 specifier: 2.0.0-beta.220
version: 2.0.0-beta.220(@tiptap/core@2.0.0-beta.220)(@tiptap/pm@2.0.0-beta.220) version: 2.0.0-beta.220(@tiptap/core@2.0.0-beta.220)(@tiptap/pm@2.0.0-beta.220)
'@tiptap/extension-link':
specifier: 2.0.0-beta.220
version: 2.0.0-beta.220(@tiptap/core@2.0.0-beta.220)(@tiptap/pm@2.0.0-beta.220)
'@tiptap/extension-typography': '@tiptap/extension-typography':
specifier: 2.0.0-beta.220 specifier: 2.0.0-beta.220
version: 2.0.0-beta.220(@tiptap/core@2.0.0-beta.220) version: 2.0.0-beta.220(@tiptap/core@2.0.0-beta.220)
@ -780,6 +783,17 @@ packages:
'@tiptap/core': 2.0.0-beta.220(@tiptap/pm@2.0.0-beta.220) '@tiptap/core': 2.0.0-beta.220(@tiptap/pm@2.0.0-beta.220)
dev: false dev: false
/@tiptap/extension-link@2.0.0-beta.220(@tiptap/core@2.0.0-beta.220)(@tiptap/pm@2.0.0-beta.220):
resolution: {integrity: sha512-vjEA8cE37ZZVVgPHSpttw3kbJoClb+ya/BVukDtJ1h6C7mIR1rqzNxTgpbnXJuA8xww0JOjpa5dpzEgcs294fA==}
peerDependencies:
'@tiptap/core': ^2.0.0-beta.209
'@tiptap/pm': ^2.0.0-beta.209
dependencies:
'@tiptap/core': 2.0.0-beta.220(@tiptap/pm@2.0.0-beta.220)
'@tiptap/pm': 2.0.0-beta.220(@tiptap/core@2.0.0-beta.220)
linkifyjs: 4.1.1
dev: false
/@tiptap/extension-list-item@2.0.0-beta.220(@tiptap/core@2.0.0-beta.220): /@tiptap/extension-list-item@2.0.0-beta.220(@tiptap/core@2.0.0-beta.220):
resolution: {integrity: sha512-+O0ivwxPP2l/m9PAowb2ytDT/cM5kwu0s1W5MUsHPIqf+M6ahnl4ESjhWZfDHUzvjqPq6MTbqoQLHbB1KS/N7w==} resolution: {integrity: sha512-+O0ivwxPP2l/m9PAowb2ytDT/cM5kwu0s1W5MUsHPIqf+M6ahnl4ESjhWZfDHUzvjqPq6MTbqoQLHbB1KS/N7w==}
peerDependencies: peerDependencies:
@ -2870,6 +2884,10 @@ packages:
uc.micro: 1.0.6 uc.micro: 1.0.6
dev: false dev: false
/linkifyjs@4.1.1:
resolution: {integrity: sha512-zFN/CTVmbcVef+WaDXT63dNzzkfRBKT1j464NJQkV7iSgJU0sLBus9W0HBwnXK13/hf168pbrx/V/bjEHOXNHA==}
dev: false
/local-pkg@0.4.3: /local-pkg@0.4.3:
resolution: {integrity: sha512-SFppqq5p42fe2qcZQqqEOiVRXl+WCP1MdT6k7BDEW1j++sp5fIY+/fdRQitvKgB5BrBcmrs5m/L0v2FrU5MY1g==} resolution: {integrity: sha512-SFppqq5p42fe2qcZQqqEOiVRXl+WCP1MdT6k7BDEW1j++sp5fIY+/fdRQitvKgB5BrBcmrs5m/L0v2FrU5MY1g==}
engines: {node: '>=14'} engines: {node: '>=14'}

View file

@ -1,59 +1,110 @@
<script setup lang="ts"> <script setup lang="ts">
import type { Editor import type { Editor } from '@tiptap/vue-3'
} from '@tiptap/vue-3';
import { BubbleMenu } from '@tiptap/vue-3' import { BubbleMenu } from '@tiptap/vue-3'
import { nextTick, ref } from 'vue'
const { editor } = defineProps<{ editor: Editor }>() const { editor } = defineProps<{ editor: Editor }>()
const url = ref('')
const showUrlInput = ref(false)
const placeholder = ref('Add Link to text')
const inputUrl = ref<HTMLInputElement | null>(null)
const openLinkInput = () => {
showUrlInput.value = true
nextTick(() => inputUrl.value?.focus())
}
const setLink = () => {
const previousUrl = editor.getAttributes('link').href
if (previousUrl)
placeholder.value = previousUrl
// cancelled
if (url.value === null)
return
if (url.value === '') {
editor.chain().focus().extendMarkRange('link').unsetAllMarks().run()
return
}
editor
.chain()
.focus()
.extendMarkRange('link')
.setLink({ href: url.value })
.run()
return (showUrlInput.value = false)
}
</script> </script>
<template> <template>
<BubbleMenu <BubbleMenu
:editor="editor" :editor="editor"
:tippy-options="{ duration: 100 }" :tippy-options="{ duration: 50 }"
class="flex text-gray-700 bg-white grass rounded-md p-[2px] shadow-xl border-slate-100 border" class="flex text-gray-700 bg-white grass rounded-md p-[2px] shadow-xl border-slate-100 border"
> >
<button <div v-show="!showUrlInput">
class="menu-btn" <button
placement="bold" class="menu-btn"
@click="editor.chain().focus().toggleBold().run()" :class="{ 'btn-active': editor.isActive('bold') }"
@click="editor.chain().focus().toggleBold().run()"
>
<span class="i-tabler-bold" />
</button>
<button
class="menu-btn"
:class="{ 'btn-active': editor.isActive('italic') }"
@click="editor.chain().focus().toggleItalic().run()"
>
<span class="i-tabler-italic" />
</button>
<button
class="menu-btn"
:class="{ 'btn-active': editor.isActive('strike') }"
@click="editor.chain().focus().toggleStrike().run()"
>
<span class="i-tabler-strikethrough" />
</button>
<button
class="menu-btn"
:class="{ 'btn-active': editor.isActive('code') }"
@click="editor.chain().focus().toggleCode().run()"
>
<span class="i-tabler-code" />
</button>
<button
class="menu-btn"
:class="{ 'btn-active': editor.isActive('quote') }"
@click="editor.chain().focus().toggleBlockquote().run()"
>
<span class="i-tabler-quote" />
</button>
<button
:class="{ 'btn-active': editor.isActive('link') }"
class="menu-btn"
@click="openLinkInput"
>
<span class="i-tabler-link" />
</button>
</div>
<div
v-show="showUrlInput"
class="input-group input-group-sm border-slate-300 border-1"
> >
<span class="i-tabler-bold" /> <input
</button> ref="inputUrl"
<button v-model.trim="url"
class="menu-btn" class="input input-sm focus:outline-none"
@click="editor.chain().focus().toggleItalic().run()" :placeholder="placeholder"
> @blur="showUrlInput = false"
<span class="i-tabler-italic" /> >
</button> <button class="btn btn-sm btn-square" @click="setLink">
<button <span class="i-tabler-link p-2" />
class="menu-btn" </button>
@click="editor.chain().focus().toggleStrike().run()" </div>
>
<span class="i-tabler-strikethrough" />
</button>
<button
class="menu-btn"
@click="editor.chain().focus().toggleCode().run()"
>
<span class="i-tabler-code" />
</button>
<button
class="menu-btn"
@click="editor.chain().focus().toggleBlockquote().run()"
>
<span class="i-tabler-quote" />
</button>
</BubbleMenu> </BubbleMenu>
</template> </template>
<style lang="pcss">
.menu-btn {
@apply btn btn-ghost btn-sm hover:bg-slate-200 rounded-md p-2;
}
.menu-btn > span {
@apply w-4;
}
</style>

View file

@ -2,19 +2,24 @@
import { EditorContent, useEditor } from '@tiptap/vue-3' import { EditorContent, useEditor } from '@tiptap/vue-3'
import StarterKit from '@tiptap/starter-kit' import StarterKit from '@tiptap/starter-kit'
import Typography from '@tiptap/extension-typography' import Typography from '@tiptap/extension-typography'
import Link from '@tiptap/extension-link'
import BubbleMenu from './BubbleMenu.vue' import BubbleMenu from './BubbleMenu.vue'
const editor = useEditor({ const editor = useEditor({
content: '<p>Im running Tiptap with Vue.js. 🎉</p>', content: '<p>Im running Tiptap with Vue.js. 🎉</p>',
extensions: [StarterKit, Typography], extensions: [
StarterKit,
Typography,
Link.configure({
openOnClick: false,
}),
],
editable: true, editable: true,
autofocus: true, autofocus: true,
}) })
</script> </script>
<template> <template>
<BubbleMenu <BubbleMenu v-if="editor" :editor="editor" />
v-if="editor"
:editor="editor" />
<EditorContent :editor="editor" /> <EditorContent :editor="editor" />
</template> </template>

View file

@ -9,3 +9,27 @@
.editor { .editor {
@apply mx-auto; @apply mx-auto;
} }
.btn-ghost {
@apply hover:bg-slate-200;
}
.btn-active{
@apply bg-slate-200;
}
.menu-btn {
@apply btn btn-ghost btn-sm rounded-md p-2;
}
.menu-btn > span {
@apply w-4;
}
.menu-btn.btn-active {
@apply bg-slate-200;
}
.input-group{
@apply h-4;
}