488 lines
13 KiB
Vue
488 lines
13 KiB
Vue
<template>
|
|
<div class="editor-toolbar">
|
|
<div class="editor-toolbar__segment">
|
|
<BaseButton
|
|
class="editor-toolbar__button"
|
|
@click="editor.chain().focus().toggleHeading({ level: 1 }).run()"
|
|
:class="{ 'is-active': editor.isActive('heading', { level: 1 }) }"
|
|
title="h1"
|
|
>
|
|
<span class="icon"> <icon :icon="['fa', 'fa-header']"/> </span>1
|
|
</BaseButton>
|
|
<BaseButton
|
|
class="editor-toolbar__button"
|
|
@click="editor.chain().focus().toggleHeading({ level: 2 }).run()"
|
|
:class="{ 'is-active': editor.isActive('heading', { level: 2 }) }"
|
|
title="h2"
|
|
>
|
|
<span class="icon"> <icon :icon="['fa', 'fa-header']"/> </span>2
|
|
</BaseButton>
|
|
<BaseButton
|
|
class="editor-toolbar__button"
|
|
@click="editor.chain().focus().toggleHeading({ level: 3 }).run()"
|
|
:class="{ 'is-active': editor.isActive('heading', { level: 3 }) }"
|
|
title="h3"
|
|
>
|
|
<span class="icon"> <icon :icon="['fa', 'fa-header']"/> </span>3
|
|
</BaseButton>
|
|
<BaseButton
|
|
class="editor-toolbar__button"
|
|
@click="editor.chain().focus().toggleHeading({ level: 4 }).run()"
|
|
:class="{ 'is-active': editor.isActive('heading', { level: 4 }) }"
|
|
title="h4"
|
|
>
|
|
<span class="icon"> <icon :icon="['fa', 'fa-header']"/> </span>4
|
|
</BaseButton>
|
|
<BaseButton
|
|
class="editor-toolbar__button"
|
|
@click="editor.chain().focus().toggleHeading({ level: 5 }).run()"
|
|
:class="{ 'is-active': editor.isActive('heading', { level: 5 }) }"
|
|
title="h5"
|
|
>
|
|
<span class="icon"> <icon :icon="['fa', 'fa-header']"/> </span>5
|
|
</BaseButton>
|
|
<BaseButton
|
|
class="editor-toolbar__button"
|
|
@click="editor.chain().focus().toggleHeading({ level: 6 }).run()"
|
|
:class="{ 'is-active': editor.isActive('heading', { level: 6 }) }"
|
|
title="h6"
|
|
>
|
|
<span class="icon"> <icon :icon="['fa', 'fa-header']"/> </span>6
|
|
</BaseButton>
|
|
</div>
|
|
|
|
<div class="editor-toolbar__segment">
|
|
<BaseButton
|
|
class="editor-toolbar__button"
|
|
@click="editor.chain().focus().toggleBold().run()"
|
|
:class="{ 'is-active': editor.isActive('bold') }"
|
|
title="bold"
|
|
>
|
|
<span class="icon">
|
|
<icon :icon="['fa', 'fa-bold']"/>
|
|
</span>
|
|
</BaseButton>
|
|
<BaseButton
|
|
class="editor-toolbar__button"
|
|
@click="editor.chain().focus().toggleItalic().run()"
|
|
:class="{ 'is-active': editor.isActive('italic') }"
|
|
title="italic"
|
|
>
|
|
<span class="icon">
|
|
<icon :icon="['fa', 'fa-italic']"/>
|
|
</span>
|
|
</BaseButton>
|
|
<BaseButton
|
|
class="editor-toolbar__button"
|
|
@click="editor.chain().focus().toggleStrike().run()"
|
|
:class="{ 'is-active': editor.isActive('strike') }"
|
|
title="strike"
|
|
>
|
|
<span class="icon">
|
|
<icon :icon="['fa', 'fa-strikethrough']"/>
|
|
</span>
|
|
</BaseButton>
|
|
</div>
|
|
|
|
<div class="editor-toolbar__segment">
|
|
<BaseButton
|
|
class="editor-toolbar__button"
|
|
@click="editor.chain().focus().toggleCode().run()"
|
|
:class="{ 'is-active': editor.isActive('code') }"
|
|
title="code"
|
|
>
|
|
<span class="icon">
|
|
<icon :icon="['fa', 'fa-code']"/>
|
|
</span>
|
|
</BaseButton>
|
|
<BaseButton
|
|
class="editor-toolbar__button"
|
|
@click="editor.chain().focus().toggleCodeBlock().run()"
|
|
:class="{ 'is-active': editor.isActive('codeBlock') }"
|
|
title="code block"
|
|
>
|
|
<span class="icon">
|
|
<icon :icon="['fa', 'fa-code']"/>
|
|
</span>
|
|
</BaseButton>
|
|
<BaseButton
|
|
class="editor-toolbar__button"
|
|
@click="editor.chain().focus().toggleBlockquote().run()"
|
|
:class="{ 'is-active': editor.isActive('blockquote') }"
|
|
title="quote"
|
|
>
|
|
<span class="icon">
|
|
<icon :icon="['fa', 'fa-quote-right']"/>
|
|
</span>
|
|
</BaseButton>
|
|
</div>
|
|
|
|
<div class="editor-toolbar__segment">
|
|
<BaseButton
|
|
class="editor-toolbar__button"
|
|
@click="editor.chain().focus().toggleBulletList().run()"
|
|
:class="{ 'is-active': editor.isActive('bulletList') }"
|
|
title="bullet list"
|
|
>
|
|
<span class="icon">
|
|
<icon :icon="['fa', 'fa-list-ol']"/>
|
|
</span>
|
|
</BaseButton>
|
|
<BaseButton
|
|
class="editor-toolbar__button"
|
|
@click="editor.chain().focus().toggleOrderedList().run()"
|
|
:class="{ 'is-active': editor.isActive('orderedList') }"
|
|
title="ordered list"
|
|
>
|
|
<span class="icon">
|
|
<icon :icon="['fa', 'fa-list-ul']"/>
|
|
</span>
|
|
</BaseButton>
|
|
<BaseButton
|
|
class="editor-toolbar__button"
|
|
@click="editor.chain().focus().toggleTaskList().run()"
|
|
:class="{ 'is-active': editor.isActive('taskList') }"
|
|
title="task list"
|
|
>
|
|
<span class="icon">
|
|
<icon icon="fa-list-check"/>
|
|
</span>
|
|
</BaseButton>
|
|
</div>
|
|
|
|
<div class="editor-toolbar__segment">
|
|
<BaseButton class="editor-toolbar__button" @click="openImagePicker" title="Add image">
|
|
<span class="icon">
|
|
<icon icon="fa-image"/>
|
|
</span>
|
|
</BaseButton>
|
|
</div>
|
|
|
|
<div class="editor-toolbar__segment">
|
|
<BaseButton
|
|
class="editor-toolbar__button"
|
|
@click="editor.chain().focus().unsetAllMarks().run()"
|
|
>
|
|
<icon icon="xmark"/>
|
|
</BaseButton>
|
|
<BaseButton
|
|
class="editor-toolbar__button"
|
|
@click="editor.chain().focus().clearNodes().run()"
|
|
>
|
|
<icon icon="xmarks-lines"/>
|
|
</BaseButton>
|
|
</div>
|
|
|
|
<div class="editor-toolbar__segment">
|
|
<BaseButton
|
|
class="editor-toolbar__button"
|
|
@click="setLink"
|
|
:class="{ 'is-active': editor.isActive('link') }"
|
|
title="set link"
|
|
>
|
|
<span class="icon">
|
|
<icon :icon="['fa', 'fa-link']"/>
|
|
</span>
|
|
</BaseButton>
|
|
<BaseButton
|
|
class="editor-toolbar__button"
|
|
@click="editor.chain().focus().unsetLink().run()"
|
|
:disabled="!editor.isActive('link')"
|
|
title="unset link"
|
|
>
|
|
<span class="icon">
|
|
<icon :icon="['fa', 'fa-unlink']"/>
|
|
</span>
|
|
</BaseButton>
|
|
<BaseButton
|
|
class="editor-toolbar__button"
|
|
@click="editor.chain().focus().setParagraph().run()"
|
|
:class="{ 'is-active': editor.isActive('paragraph') }"
|
|
title="paragraph"
|
|
>
|
|
<span class="icon">
|
|
<icon :icon="['fa', 'fa-paragraph']"/>
|
|
</span>
|
|
</BaseButton>
|
|
|
|
<BaseButton
|
|
class="editor-toolbar__button"
|
|
@click="editor.chain().focus().setHorizontalRule().run()"
|
|
>
|
|
<span class="editor-toolbar__pseudo-icon">
|
|
-
|
|
</span>
|
|
</BaseButton>
|
|
<BaseButton
|
|
class="editor-toolbar__button"
|
|
@click="editor.chain().focus().setHardBreak().run()"
|
|
>
|
|
<span class="icon">
|
|
<icon :icon="['fa', 'fa-arrow-turn-down']"/>
|
|
</span>
|
|
</BaseButton>
|
|
</div>
|
|
|
|
<div class="editor-toolbar__segment">
|
|
<BaseButton
|
|
class="editor-toolbar__button"
|
|
@click="editor.chain().focus().undo().run()"
|
|
title="undo"
|
|
>
|
|
<span class="icon">
|
|
<icon :icon="['fa', 'fa-undo']"/>
|
|
</span>
|
|
</BaseButton>
|
|
<BaseButton
|
|
class="editor-toolbar__button"
|
|
@click="editor.chain().focus().redo().run()"
|
|
title="redo"
|
|
>
|
|
<span class="icon">
|
|
<icon :icon="['fa', 'fa-redo']"/>
|
|
</span>
|
|
</BaseButton>
|
|
</div>
|
|
|
|
<div class="editor-toolbar__segment">
|
|
<!-- table -->
|
|
<BaseButton
|
|
class="editor-toolbar__button"
|
|
@click="toggleTableMode"
|
|
:class="{ 'is-active': editor.isActive('table') }"
|
|
title="table"
|
|
>
|
|
<span class="icon">
|
|
<icon :icon="['fa', 'fa-table']"/>
|
|
</span>
|
|
</BaseButton>
|
|
<div v-if="tableMode" class="editor-toolbar__table-buttons">
|
|
<BaseButton
|
|
class="editor-toolbar__button"
|
|
@click="
|
|
editor
|
|
.chain()
|
|
.focus()
|
|
.insertTable({ rows: 3, cols: 3, withHeaderRow: true })
|
|
.run()
|
|
"
|
|
>
|
|
{{ $t('input.editor.table.insert') }}
|
|
</BaseButton>
|
|
<BaseButton
|
|
class="editor-toolbar__button"
|
|
@click="editor.chain().focus().addColumnBefore().run()"
|
|
:disabled="!editor.can().addColumnBefore"
|
|
>
|
|
{{ $t('input.editor.table.addColumnBefore') }}
|
|
</BaseButton>
|
|
<BaseButton
|
|
class="editor-toolbar__button"
|
|
@click="editor.chain().focus().addColumnAfter().run()"
|
|
:disabled="!editor.can().addColumnAfter"
|
|
>
|
|
{{ $t('input.editor.table.addColumnAfter') }}
|
|
</BaseButton>
|
|
<BaseButton
|
|
class="editor-toolbar__button"
|
|
@click="editor.chain().focus().deleteColumn().run()"
|
|
:disabled="!editor.can().deleteColumn"
|
|
>
|
|
{{ $t('input.editor.table.deleteColumn') }}
|
|
</BaseButton>
|
|
<BaseButton
|
|
class="editor-toolbar__button"
|
|
@click="editor.chain().focus().addRowBefore().run()"
|
|
:disabled="!editor.can().addRowBefore"
|
|
>
|
|
{{ $t('input.editor.table.addRowBefore') }}
|
|
</BaseButton>
|
|
<BaseButton
|
|
class="editor-toolbar__button"
|
|
@click="editor.chain().focus().addRowAfter().run()"
|
|
:disabled="!editor.can().addRowAfter"
|
|
>
|
|
{{ $t('input.editor.table.addRowAfter') }}
|
|
</BaseButton>
|
|
<BaseButton
|
|
class="editor-toolbar__button"
|
|
@click="editor.chain().focus().deleteRow().run()"
|
|
:disabled="!editor.can().deleteRow"
|
|
>
|
|
{{ $t('input.editor.table.deleteRow') }}
|
|
</BaseButton>
|
|
<BaseButton
|
|
class="editor-toolbar__button"
|
|
@click="editor.chain().focus().deleteTable().run()"
|
|
:disabled="!editor.can().deleteTable"
|
|
>
|
|
{{ $t('input.editor.table.deleteTable') }}
|
|
</BaseButton>
|
|
<BaseButton
|
|
class="editor-toolbar__button"
|
|
@click="editor.chain().focus().mergeCells().run()"
|
|
:disabled="!editor.can().mergeCells"
|
|
>
|
|
{{ $t('input.editor.table.mergeCells') }}
|
|
</BaseButton>
|
|
<BaseButton
|
|
class="editor-toolbar__button"
|
|
@click="editor.chain().focus().splitCell().run()"
|
|
:disabled="!editor.can().splitCell"
|
|
>
|
|
{{ $t('input.editor.table.splitCell') }}
|
|
</BaseButton>
|
|
<BaseButton
|
|
class="editor-toolbar__button"
|
|
@click="editor.chain().focus().toggleHeaderColumn().run()"
|
|
:disabled="!editor.can().toggleHeaderColumn"
|
|
>
|
|
{{ $t('input.editor.table.toggleHeaderColumn') }}
|
|
</BaseButton>
|
|
<BaseButton
|
|
class="editor-toolbar__button"
|
|
@click="editor.chain().focus().toggleHeaderRow().run()"
|
|
:disabled="!editor.can().toggleHeaderRow"
|
|
>
|
|
{{ $t('input.editor.table.toggleHeaderRow') }}
|
|
</BaseButton>
|
|
<BaseButton
|
|
class="editor-toolbar__button"
|
|
@click="editor.chain().focus().toggleHeaderCell().run()"
|
|
:disabled="!editor.can().toggleHeaderCell"
|
|
>
|
|
{{ $t('input.editor.table.toggleHeaderCell') }}
|
|
</BaseButton>
|
|
<BaseButton
|
|
class="editor-toolbar__button"
|
|
@click="editor.chain().focus().mergeOrSplit().run()"
|
|
:disabled="!editor.can().mergeOrSplit"
|
|
>
|
|
{{ $t('input.editor.table.mergeOrSplit') }}
|
|
</BaseButton>
|
|
<BaseButton
|
|
class="editor-toolbar__button"
|
|
@click="editor.chain().focus().fixTables().run()"
|
|
:disabled="!editor.can().fixTables"
|
|
>
|
|
{{ $t('input.editor.table.fixTables') }}
|
|
</BaseButton>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</template>
|
|
|
|
<script setup lang="ts">
|
|
import {ref} from 'vue'
|
|
import {Editor} from '@tiptap/vue-3'
|
|
import type {UploadCallback} from './types'
|
|
|
|
import BaseButton from '@/components/base/BaseButton.vue'
|
|
|
|
const {
|
|
editor = null,
|
|
uploadCallback,
|
|
} = defineProps<{
|
|
editor: Editor,
|
|
uploadCallback?: UploadCallback,
|
|
}>()
|
|
|
|
const tableMode = ref(false)
|
|
|
|
function toggleTableMode() {
|
|
tableMode.value = !tableMode.value
|
|
}
|
|
|
|
function openImagePicker() {
|
|
document.getElementById('tiptap__image-upload').click()
|
|
}
|
|
|
|
function setLink() {
|
|
const previousUrl = editor.getAttributes('link').href
|
|
const url = window.prompt('URL', previousUrl)
|
|
|
|
// cancelled
|
|
if (url === null) {
|
|
return
|
|
}
|
|
|
|
// empty
|
|
if (url === '') {
|
|
editor.chain().focus().extendMarkRange('link').unsetLink().run()
|
|
|
|
return
|
|
}
|
|
|
|
// update link
|
|
editor
|
|
.chain()
|
|
.focus()
|
|
.extendMarkRange('link')
|
|
.setLink({href: url, target: '_blank'})
|
|
.run()
|
|
}
|
|
</script>
|
|
|
|
<style lang="scss" scoped>
|
|
.editor-toolbar {
|
|
background: var(--grey-50);
|
|
border: 1px solid var(--grey-200);
|
|
border-bottom: none;
|
|
// position: relative;
|
|
user-select: none;
|
|
padding: 9px 10px;
|
|
border-top-left-radius: 4px;
|
|
border-top-right-radius: 4px;
|
|
display: flex;
|
|
flex-wrap: wrap;
|
|
|
|
> * + * {
|
|
// .editor-toolbar i.separator {
|
|
border--left-color: var(--grey-200) !important;
|
|
// }
|
|
// .editor-toolbar i.separator {
|
|
// display: inline-block;
|
|
// width: 0;
|
|
border-left: 1px solid var(--grey-200);
|
|
// border-right: 1px solid #fff;
|
|
// color: transparent;
|
|
// text-indent: -10px;
|
|
margin-left: 6px;
|
|
padding-left: 6px;
|
|
// }
|
|
}
|
|
}
|
|
|
|
.editor-toolbar__button {
|
|
color: var(--grey-700);
|
|
min-width: 30px;
|
|
height: 30px;
|
|
border-radius: 3px;
|
|
border: 1px solid transparent;
|
|
|
|
&:hover {
|
|
background: var(--grey-200);
|
|
border-color: var(--grey-300);
|
|
}
|
|
}
|
|
|
|
.editor-toolbar__pseudo-icon {
|
|
padding: 0 .5rem;
|
|
font-weight: bold;
|
|
vertical-align: top;
|
|
}
|
|
|
|
.editor-toolbar__table-buttons {
|
|
margin-top: .5rem;
|
|
|
|
> .editor-toolbar__button {
|
|
margin-right: .5rem;
|
|
margin-bottom: .5rem;
|
|
padding: 0 .25rem;
|
|
border: 1px solid var(--grey-400);
|
|
font-size: .75rem;
|
|
height: 1.5rem;
|
|
}
|
|
}
|
|
</style>
|