feat(editor): edit mode
This commit is contained in:
parent
632e3c5a0b
commit
d7503dc4a2
@ -1,12 +1,12 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="tiptap">
|
<div class="tiptap">
|
||||||
<EditorToolbar
|
<EditorToolbar
|
||||||
v-if="editor && isEditEnabled"
|
v-if="editor && isEditing"
|
||||||
:editor="editor"
|
:editor="editor"
|
||||||
:upload-callback="uploadCallback"
|
:upload-callback="uploadCallback"
|
||||||
/>
|
/>
|
||||||
<BubbleMenu
|
<BubbleMenu
|
||||||
v-if="editor && isEditEnabled"
|
v-if="editor && isEditing"
|
||||||
:editor="editor"
|
:editor="editor"
|
||||||
class="editor-bubble__wrapper"
|
class="editor-bubble__wrapper"
|
||||||
>
|
>
|
||||||
@ -62,12 +62,12 @@
|
|||||||
|
|
||||||
<editor-content
|
<editor-content
|
||||||
class="tiptap__editor"
|
class="tiptap__editor"
|
||||||
:class="{'tiptap__editor-is-empty': isEmpty, 'tiptap__editor-is-edit-enabled': isEditEnabled}"
|
:class="{'tiptap__editor-is-empty': isEmpty, 'tiptap__editor-is-edit-enabled': isEditing}"
|
||||||
:editor="editor"
|
:editor="editor"
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<input
|
<input
|
||||||
v-if="isEditEnabled"
|
v-if="isEditing"
|
||||||
type="file"
|
type="file"
|
||||||
id="tiptap__image-upload"
|
id="tiptap__image-upload"
|
||||||
class="is-hidden"
|
class="is-hidden"
|
||||||
@ -75,20 +75,36 @@
|
|||||||
@change="addImage"
|
@change="addImage"
|
||||||
/>
|
/>
|
||||||
|
|
||||||
|
<ul class="tiptap__editor-actions d-print-none" v-if="bottomActions.length === 0 && !isEditing">
|
||||||
|
<li>
|
||||||
|
<BaseButton
|
||||||
|
@click="setEdit"
|
||||||
|
class="done-edit">
|
||||||
|
{{ $t('input.editor.edit') }}
|
||||||
|
</BaseButton>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
<ul class="tiptap__editor-actions d-print-none" v-if="bottomActions.length > 0">
|
<ul class="tiptap__editor-actions d-print-none" v-if="bottomActions.length > 0">
|
||||||
<li v-if="isEditEnabled && showSave">
|
<li v-if="isEditing && showSave">
|
||||||
<BaseButton
|
<BaseButton
|
||||||
@click="bubbleSave"
|
@click="bubbleSave"
|
||||||
class="done-edit">
|
class="done-edit">
|
||||||
{{ $t('misc.save') }}
|
{{ $t('misc.save') }}
|
||||||
</BaseButton>
|
</BaseButton>
|
||||||
</li>
|
</li>
|
||||||
|
<li v-if="!isEditing">
|
||||||
|
<BaseButton
|
||||||
|
@click="setEdit"
|
||||||
|
class="done-edit">
|
||||||
|
{{ $t('input.editor.edit') }}
|
||||||
|
</BaseButton>
|
||||||
|
</li>
|
||||||
<li v-for="(action, k) in bottomActions" :key="k">
|
<li v-for="(action, k) in bottomActions" :key="k">
|
||||||
<BaseButton @click="action.action">{{ action.title }}</BaseButton>
|
<BaseButton @click="action.action">{{ action.title }}</BaseButton>
|
||||||
</li>
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
<x-button
|
<x-button
|
||||||
v-else-if="isEditEnabled && showSave"
|
v-else-if="isEditing && showSave"
|
||||||
class="mt-4"
|
class="mt-4"
|
||||||
@click="bubbleSave"
|
@click="bubbleSave"
|
||||||
variant="secondary"
|
variant="secondary"
|
||||||
@ -130,7 +146,6 @@ import {Blockquote} from '@tiptap/extension-blockquote'
|
|||||||
import {Bold} from '@tiptap/extension-bold'
|
import {Bold} from '@tiptap/extension-bold'
|
||||||
import {BulletList} from '@tiptap/extension-bullet-list'
|
import {BulletList} from '@tiptap/extension-bullet-list'
|
||||||
import {Code} from '@tiptap/extension-code'
|
import {Code} from '@tiptap/extension-code'
|
||||||
import {CodeBlock} from '@tiptap/extension-code-block'
|
|
||||||
import {Document} from '@tiptap/extension-document'
|
import {Document} from '@tiptap/extension-document'
|
||||||
import {Dropcursor} from '@tiptap/extension-dropcursor'
|
import {Dropcursor} from '@tiptap/extension-dropcursor'
|
||||||
import {Gapcursor} from '@tiptap/extension-gapcursor'
|
import {Gapcursor} from '@tiptap/extension-gapcursor'
|
||||||
@ -187,6 +202,7 @@ const CustomTableCell = TableCell.extend({
|
|||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
|
||||||
|
type Mode = 'edit' | 'preview'
|
||||||
|
|
||||||
const {
|
const {
|
||||||
modelValue,
|
modelValue,
|
||||||
@ -196,6 +212,7 @@ const {
|
|||||||
showSave = false,
|
showSave = false,
|
||||||
placeholder = '',
|
placeholder = '',
|
||||||
editShortcut = '',
|
editShortcut = '',
|
||||||
|
initialMode = 'edit',
|
||||||
} = defineProps<{
|
} = defineProps<{
|
||||||
modelValue: string,
|
modelValue: string,
|
||||||
uploadCallback?: UploadCallback,
|
uploadCallback?: UploadCallback,
|
||||||
@ -204,6 +221,7 @@ const {
|
|||||||
showSave?: boolean,
|
showSave?: boolean,
|
||||||
placeholder?: string,
|
placeholder?: string,
|
||||||
editShortcut?: string,
|
editShortcut?: string,
|
||||||
|
initialMode?: Mode,
|
||||||
}>()
|
}>()
|
||||||
|
|
||||||
const baseStore = useBaseStore()
|
const baseStore = useBaseStore()
|
||||||
@ -233,6 +251,13 @@ watch(
|
|||||||
)
|
)
|
||||||
|
|
||||||
const isEmpty = computed(() => inputHTML.value === '')
|
const isEmpty = computed(() => inputHTML.value === '')
|
||||||
|
const internalMode = ref<Mode>(initialMode)
|
||||||
|
const isEditing = computed(() => internalMode.value === 'edit' && isEditEnabled)
|
||||||
|
|
||||||
|
function setEdit() {
|
||||||
|
internalMode.value = 'edit'
|
||||||
|
editor.value?.commands.focus()
|
||||||
|
}
|
||||||
|
|
||||||
function onImageAdded() {
|
function onImageAdded() {
|
||||||
bubbleSave()
|
bubbleSave()
|
||||||
@ -282,11 +307,14 @@ function bubbleNow() {
|
|||||||
function bubbleSave() {
|
function bubbleSave() {
|
||||||
bubbleNow()
|
bubbleNow()
|
||||||
emit('save', TIPTAP_TEXT_VALUE_PREFIX + inputHTML.value)
|
emit('save', TIPTAP_TEXT_VALUE_PREFIX + inputHTML.value)
|
||||||
|
if (initialMode === 'preview' && isEditing.value) {
|
||||||
|
internalMode.value = 'preview'
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const editor = useEditor({
|
const editor = useEditor({
|
||||||
content: inputHTML.value,
|
content: inputHTML.value,
|
||||||
editable: isEditEnabled,
|
editable: isEditing.value,
|
||||||
extensions: [
|
extensions: [
|
||||||
// Starterkit:
|
// Starterkit:
|
||||||
Blockquote,
|
Blockquote,
|
||||||
@ -320,7 +348,7 @@ const editor = useEditor({
|
|||||||
|
|
||||||
Placeholder.configure({
|
Placeholder.configure({
|
||||||
placeholder: ({editor}) => {
|
placeholder: ({editor}) => {
|
||||||
if (!isEditEnabled) {
|
if (!isEditing) {
|
||||||
return ''
|
return ''
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -374,6 +402,13 @@ const editor = useEditor({
|
|||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
|
||||||
|
watch(
|
||||||
|
() => isEditing.value,
|
||||||
|
() => {
|
||||||
|
editor.value?.setEditable(isEditing.value)
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
watch(inputHTML, (value) => {
|
watch(inputHTML, (value) => {
|
||||||
if (!editor.value) return
|
if (!editor.value) return
|
||||||
// HTML
|
// HTML
|
||||||
@ -459,6 +494,7 @@ function setLink() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
|
internalMode.value = initialMode
|
||||||
document.addEventListener('paste', handleImagePaste)
|
document.addEventListener('paste', handleImagePaste)
|
||||||
if (editShortcut !== '') {
|
if (editShortcut !== '') {
|
||||||
document.addEventListener('keydown', setFocusToEditor)
|
document.addEventListener('keydown', setFocusToEditor)
|
||||||
@ -488,13 +524,20 @@ function setFocusToEditor(event) {
|
|||||||
if (hotkeyString !== editShortcut || baseStore.editorFocused) return
|
if (hotkeyString !== editShortcut || baseStore.editorFocused) return
|
||||||
event.preventDefault()
|
event.preventDefault()
|
||||||
|
|
||||||
|
if (initialMode === 'preview' && isEditEnabled && !isEditing.value) {
|
||||||
|
internalMode.value = 'edit'
|
||||||
|
}
|
||||||
|
|
||||||
editor.value?.commands.focus()
|
editor.value?.commands.focus()
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="scss">
|
<style lang="scss">
|
||||||
.tiptap__editor {
|
.tiptap__editor {
|
||||||
min-height: 10rem;
|
&.tiptap__editor-is-edit-enabled {
|
||||||
|
min-height: 10rem;
|
||||||
|
}
|
||||||
|
|
||||||
transition: box-shadow $transition;
|
transition: box-shadow $transition;
|
||||||
border-radius: $radius;
|
border-radius: $radius;
|
||||||
|
|
||||||
|
@ -82,6 +82,7 @@
|
|||||||
}"
|
}"
|
||||||
:bottom-actions="actions[c.id]"
|
:bottom-actions="actions[c.id]"
|
||||||
:show-save="true"
|
:show-save="true"
|
||||||
|
initial-mode="preview"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -25,6 +25,7 @@
|
|||||||
v-model="task.description"
|
v-model="task.description"
|
||||||
@update:model-value="saveWithDelay"
|
@update:model-value="saveWithDelay"
|
||||||
@save="save"
|
@save="save"
|
||||||
|
:initial-mode="task.description === '' ? 'edit' : 'preview'"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
Loading…
x
Reference in New Issue
Block a user