integrate tiptap text editor

This commit is contained in:
bkfox
2024-04-30 18:29:34 +02:00
parent 55123c386d
commit 93fba2d46d
21 changed files with 327 additions and 149 deletions

View File

@ -20,6 +20,11 @@
"vue": "^3.4.21"
},
"devDependencies": {
"@tiptap/extension-link": "^2.3.0",
"@tiptap/extension-underline": "^2.3.0",
"@tiptap/pm": "^2.3.0",
"@tiptap/starter-kit": "^2.3.0",
"@tiptap/vue-3": "^2.3.0",
"@vitejs/plugin-vue": "^5.0.4",
"bulma": "^0.9.4",
"eslint": "^7.32.0",

View File

@ -0,0 +1,132 @@
<template>
<input ref="input" type="hidden" :name="name" :value="value"/>
<div class="">
<template v-for="group, index in menu" :key="index">
<div class="button-group d-inline-block mr-3">
<template v-for="info, index in group" :key="index">
<button type="button" class="button square smaller" :title="info.label" @click="edit(info.action, ...(info.args || []))">
<span class="icon"><i :class="info.icon"/></span>
</button>
</template>
</div>
</template>
<div class="button-group d-inline-block">
<div class="dropdown is-hoverable">
<div class="dropdown-trigger">
<button type="button" class="button square smaller">
<span class="icon"><i class="fa fa-link"/></span>
</button>
</div>
<div class="dropdown-menu" style="min-width: 20rem; margin-top: -0.2rem;">
<div class="dropdown-content p-3">
<div class="field">
<label class="label">Lien</label>
<div class="control">
<input ref="link-url" type="text" class="input" placeholder="lien"/>
</div>
</div>
<div class="has-text-right">
<button type="button" class="button secondary"
@click="edit('setLink', {href:$refs['link-url'].value})">
Ajouter le lien
</button>
</div>
</div>
</div>
</div>
<button type="button" class="button square smaller" title="Remove link" @click="edit('unsetLink')">
<span class="icon"><i class="fa fa-link-slash"/></span>
</button>
</div>
</div>
<editor-content class="editor" v-if="editor" :editor="editor" />
</template>
<style>
.editor .tiptap {
border: 1px black solid;
padding: 0.3em;
}
.editor .tiptap ul, .editor .tiptap ol {
margin-left: 1.3em;
}
.editor .tiptap ul { list-style: disc }
</style>
<script>
import { Editor, EditorContent } from '@tiptap/vue-3'
import StarterKit from '@tiptap/starter-kit'
import Underline from '@tiptap/extension-underline'
import Link from '@tiptap/extension-link'
export default {
components: {EditorContent},
props: {
config: {type: Object, default: (() => {})},
//! Input field name.
name: String,
//! Initial input value
initial: String,
},
data() {
return {
editor: null,
menu: [
[
{label: "Bold", icon: "fa fa-bold", action: "toggleBold" },
{label: "Italic", icon: "fa fa-italic", action: "toggleItalic" },
{label: "Underline", icon: "fa fa-underline", action: "toggleUnderline" },
{label: "Strike", icon: "fa fa-strikethrough", action: "toggleStrike" },
],[
{label: "List", icon: "fa fa-list", action: "toggleBulletList" },
{label: "Ordered List", icon: "fa fa-list-ol", action: "toggleOrderedList" },
],[
{label: "Heading 1", icon: "fa fa-h", action: "setHeading", args: [{level:3}] },
{label: "Heading 2", icon: "fa fa-h smaller", action: "toggleHeading", args: [{level:4}] },
// {label: "Heading 3", icon: "fa fa-h small", action: "toggleHeading", args: [{level:5}] },
],
]
}
},
computed: {
value() { return this.editor && this.editor.getHTML() },
},
methods: {
chain(action, ...args) {
let chain = this.editor.chain().focus()
return chain[action](...args)
},
edit(action, ...args) {
this.chain(action, ...args).run()
},
setLink() {
this.edit("setLink", {href: this.$refs['link-url']})
},
},
mounted() {
this.editor = new Editor({
content: this.initial || "",
injectCss: false,
extensions: [
StarterKit.configure({
heading: {
levels: [3, 4, 5]
}
}),
Underline,
Link.configure({autolink: true}),
],
})
},
beforeUnmount() {
this.editor.destroy()
},
}
</script>

View File

@ -6,6 +6,7 @@ import AStreamer from './AStreamer.vue'
import AFormSet from './AFormSet.vue'
import ATrackListEditor from './ATrackListEditor.vue'
import ASoundListEditor from './ASoundListEditor.vue'
import AEditor from './AEditor.vue'
import AManyToManyEdit from "./AManyToManyEdit.vue"
@ -15,7 +16,7 @@ import base from "./index.js"
export const admin = {
...base,
AManyToManyEdit,
AFileUpload, ASelectFile,
AFileUpload, ASelectFile, AEditor,
AFormSet, ATrackListEditor, ASoundListEditor,
AStatistics, AStreamer,
}

View File

@ -3,7 +3,8 @@
:root {
--title-1-sz: 1.6rem;
--title-2-sz: 1.4rem;
--title-3-sz: 1.2rem;
--title-3-sz: 1.3rem;
--title-4-sz: 1.2rem;
--subtitle-1-sz: 1.6rem;
--subtitle-2-sz: 1.4rem;
--subtitle-3-sz: 1.2rem;
@ -96,6 +97,14 @@
}
// ---- headings
.no-reset h1 { font-size: var(--title-1-sz); }
.no-reset h2 { font-size: var(--title-2-sz); }
.no-reset h3 { font-size: var(--title-3-sz); }
.no-reset h3 { font-size: var(--title-3-sz); }
.no-reset h4 { font-size: var(--title-4-sz); }
.no-reset h5 { font-size: var(--title-5-sz); }
.title, .header.preview .title {
&.is-1 { font-size: var(--title-1-sz); }
&.is-2 { font-size: var(--title-2-sz); }
@ -215,6 +224,10 @@
&:last-child { border-right: 0px; }
}
}
.button-group + .button-group {
border-left: 1px solid var(--text-color-light);
}
}

View File

@ -138,6 +138,9 @@
.bg-secondary-light { background-color: var(--secondary-color-light); }
.bg-transparent { background-color: transparent; }
.border { border: 1px solid var(--text-color); }
.border-main { border: 1px solid var(--main-color); }
.border-secondary { border: 1px solid var(--secondary-color); }
.border-bottom-main { border-bottom: 1px solid var(--main-color); }
.border-bottom-secondary { border-bottom: 1px solid var(--secondary-color); }