212 lines
4.8 KiB
Vue
212 lines
4.8 KiB
Vue
|
|
<template>
|
|||
|
|
<div>
|
|||
|
|
<bubble-menu
|
|||
|
|
class="bubble-menu"
|
|||
|
|
:tippy-options="{ duration: 100 }"
|
|||
|
|
:editor="editor"
|
|||
|
|
v-if="editor"
|
|||
|
|
>
|
|||
|
|
<button
|
|||
|
|
@click="editor.chain().focus().toggleBold().run()"
|
|||
|
|
:class="{ 'is-active': editor.isActive('bold') }"
|
|||
|
|
>Bold</button>
|
|||
|
|
<button
|
|||
|
|
@click="editor.chain().focus().toggleItalic().run()"
|
|||
|
|
:class="{ 'is-active': editor.isActive('italic') }"
|
|||
|
|
>Italic</button>
|
|||
|
|
<button
|
|||
|
|
@click="editor.chain().focus().toggleStrike().run()"
|
|||
|
|
:class="{ 'is-active': editor.isActive('strike') }"
|
|||
|
|
>Strike</button>
|
|||
|
|
</bubble-menu>
|
|||
|
|
|
|||
|
|
<floating-menu
|
|||
|
|
class="floating-menu"
|
|||
|
|
:tippy-options="{ duration: 100 }"
|
|||
|
|
:editor="editor"
|
|||
|
|
v-if="editor"
|
|||
|
|
>
|
|||
|
|
<button
|
|||
|
|
@click="editor.chain().focus().toggleHeading({ level: 1 }).run()"
|
|||
|
|
:class="{ 'is-active': editor.isActive('heading', { level: 1 }) }"
|
|||
|
|
>H1</button>
|
|||
|
|
<button
|
|||
|
|
@click="editor.chain().focus().toggleHeading({ level: 2 }).run()"
|
|||
|
|
:class="{ 'is-active': editor.isActive('heading', { level: 2 }) }"
|
|||
|
|
>H2</button>
|
|||
|
|
<button
|
|||
|
|
@click="editor.chain().focus().toggleBulletList().run()"
|
|||
|
|
:class="{ 'is-active': editor.isActive('bulletList') }"
|
|||
|
|
>Bullet List</button>
|
|||
|
|
</floating-menu>
|
|||
|
|
<editor-content :editor="editor" />
|
|||
|
|
</div>
|
|||
|
|
</template>
|
|||
|
|
<script setup lang="ts">
|
|||
|
|
import {
|
|||
|
|
Editor, EditorContent,
|
|||
|
|
FloatingMenu,
|
|||
|
|
BubbleMenu
|
|||
|
|
} from '@tiptap/vue-3'
|
|||
|
|
import { PropType } from 'vue'
|
|||
|
|
import Link from '@tiptap/extension-link'
|
|||
|
|
import StarterKit from '@tiptap/starter-kit'
|
|||
|
|
import TextStyle from '@tiptap/extension-text-style'
|
|||
|
|
import { HtmlAttrsType } from '~/typs/cv'
|
|||
|
|
// import { auth_data } from '~/hooks/utils'
|
|||
|
|
import useState from '~/hooks/useState'
|
|||
|
|
const props = defineProps({
|
|||
|
|
data: {
|
|||
|
|
type: String,
|
|||
|
|
default: '',
|
|||
|
|
required: true,
|
|||
|
|
},
|
|||
|
|
src: {
|
|||
|
|
type: String,
|
|||
|
|
default: '',
|
|||
|
|
required: true,
|
|||
|
|
},
|
|||
|
|
field: {
|
|||
|
|
type: [String, Number],
|
|||
|
|
default: '',
|
|||
|
|
required: true,
|
|||
|
|
},
|
|||
|
|
idx: {
|
|||
|
|
type: Number,
|
|||
|
|
default: -1,
|
|||
|
|
required: true,
|
|||
|
|
},
|
|||
|
|
editable: {
|
|||
|
|
type: Boolean,
|
|||
|
|
default: false,
|
|||
|
|
required: true,
|
|||
|
|
},
|
|||
|
|
htmlattrs: {
|
|||
|
|
type: Object as PropType<HtmlAttrsType>,
|
|||
|
|
default: useState().htmlAttrs,
|
|||
|
|
required: false,
|
|||
|
|
},
|
|||
|
|
})
|
|||
|
|
const emit = defineEmits(['onEditorBlur'])
|
|||
|
|
const editor = new Editor({
|
|||
|
|
extensions: [
|
|||
|
|
StarterKit.configure({
|
|||
|
|
// history: false,
|
|||
|
|
bold: {
|
|||
|
|
HTMLAttributes: {
|
|||
|
|
class: props.htmlattrs.bold ? props.htmlattrs.bold : useState().htmlAttrs.bold,
|
|||
|
|
},
|
|||
|
|
},
|
|||
|
|
listItem: {
|
|||
|
|
HTMLAttributes: {
|
|||
|
|
class: props.htmlattrs.list ? props.htmlattrs.list : useState().htmlAttrs.list,
|
|||
|
|
},
|
|||
|
|
},
|
|||
|
|
}),
|
|||
|
|
TextStyle.configure({
|
|||
|
|
HTMLAttributes: {
|
|||
|
|
class: props.htmlattrs.text ? props.htmlattrs.text : useState().htmlAttrs.text,
|
|||
|
|
},
|
|||
|
|
}),
|
|||
|
|
Link.configure({
|
|||
|
|
openOnClick: false,
|
|||
|
|
HTMLAttributes: {
|
|||
|
|
class: props.htmlattrs.link ? props.htmlattrs.link : useState().htmlAttrs.link,
|
|||
|
|
},
|
|||
|
|
}),
|
|||
|
|
],
|
|||
|
|
content: props.data,
|
|||
|
|
editorProps: {
|
|||
|
|
attributes: {
|
|||
|
|
class: 'focus:p-2 focus:dark:bg-gray-700 focus:bg-gray-100 focus:border-gray-500 focus:border-1 prose prose-sm sm:prose lg:prose-lg xl:prose-2xl mx-auto focus:outline-none',
|
|||
|
|
},
|
|||
|
|
transformPastedText(text) {
|
|||
|
|
return text.toUpperCase()
|
|||
|
|
}
|
|||
|
|
},
|
|||
|
|
// '<p>I’m running Tiptap with Vue.js. 🎉</p>',
|
|||
|
|
onBeforeCreate({ editor }) {
|
|||
|
|
// Before the view is created.
|
|||
|
|
},
|
|||
|
|
onCreate({ editor }) {
|
|||
|
|
// The editor is ready.
|
|||
|
|
//editor.commands.setContent(data)
|
|||
|
|
// console.log('debugger create '+editor.state)
|
|||
|
|
},
|
|||
|
|
onUpdate({ editor }) {
|
|||
|
|
// The content has changed.
|
|||
|
|
},
|
|||
|
|
onBlur({ editor, event }) {
|
|||
|
|
const data = editor.getHTML()
|
|||
|
|
emit('onEditorBlur', { src: props.src, field: props.field, idx: props.idx, data, ev: event })
|
|||
|
|
},
|
|||
|
|
injectCSS: true,
|
|||
|
|
editable: props.editable,
|
|||
|
|
})
|
|||
|
|
onBeforeMount(async () => {
|
|||
|
|
// const d = useState()
|
|||
|
|
// console.log('Editor: ' + props.data)
|
|||
|
|
})
|
|||
|
|
onBeforeUnmount(async () => {
|
|||
|
|
editor.destroy()
|
|||
|
|
// console.log('Editor: destroy')
|
|||
|
|
})
|
|||
|
|
</script>
|
|||
|
|
<style scoped>
|
|||
|
|
/* Basic editor styles */
|
|||
|
|
/* .ProseMirror {
|
|||
|
|
> * + * {
|
|||
|
|
margin-top: 0.75em;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
ul,
|
|||
|
|
ol {
|
|||
|
|
padding: 0 1rem;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
blockquote {
|
|||
|
|
padding-left: 1rem;
|
|||
|
|
border-left: 2px solid rgba(#0D0D0D, 0.1);
|
|||
|
|
}
|
|||
|
|
} */
|
|||
|
|
|
|||
|
|
.bubble-menu {
|
|||
|
|
display: flex;
|
|||
|
|
background-color: #0D0D0D;
|
|||
|
|
padding: 0.2rem;
|
|||
|
|
border-radius: 0.5rem;
|
|||
|
|
}
|
|||
|
|
.bubble-menu button {
|
|||
|
|
border: none;
|
|||
|
|
background: none;
|
|||
|
|
color: #FFF;
|
|||
|
|
font-size: 0.85rem;
|
|||
|
|
font-weight: 500;
|
|||
|
|
padding: 0 0.2rem;
|
|||
|
|
opacity: 0.6;
|
|||
|
|
}
|
|||
|
|
.bubble-menu button:hover,
|
|||
|
|
.bubble-menu button.is-active {
|
|||
|
|
opacity: 1;
|
|||
|
|
}
|
|||
|
|
.floating-menu {
|
|||
|
|
display: flex;
|
|||
|
|
background-color: #0D0D0D10;
|
|||
|
|
padding: 0.2rem;
|
|||
|
|
border-radius: 0.5rem;
|
|||
|
|
}
|
|||
|
|
.dark .floating-menu { background-color: #0D0D0D;}
|
|||
|
|
.floating-menu button {
|
|||
|
|
border: none;
|
|||
|
|
background: none;
|
|||
|
|
font-size: 0.85rem;
|
|||
|
|
font-weight: 500;
|
|||
|
|
padding: 0 0.2rem;
|
|||
|
|
border-left: #7c7c7c 1px solid;
|
|||
|
|
opacity: 0.6;
|
|||
|
|
}
|
|||
|
|
.floating-menu button:hover,
|
|||
|
|
.floating-menu button.is-active {
|
|||
|
|
opacity: 1;
|
|||
|
|
}
|
|||
|
|
</style>
|