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> |