mirror of
https://github.com/ueberdosis/tiptap.git
synced 2024-11-27 14:59:27 +08:00
Fix editor destruction at the end of Vue transition (#5648)
* wip: destruction éditeur * fix: replacing DOM nodes at unmount * fix: event for useEditor destroy * chore: Generating changeset * chore: generating changeset * chore: delete duplicate changeset * revert: note tutorial * feat: add Vue transition example * fix: test for Vue transition * fix: components within editor * chore: remove useless ref
This commit is contained in:
parent
4efd2278a1
commit
364231a1bd
5
.changeset/five-melons-compete.md
Normal file
5
.changeset/five-melons-compete.md
Normal file
@ -0,0 +1,5 @@
|
||||
---
|
||||
"@tiptap/vue-3": patch
|
||||
---
|
||||
|
||||
Fix editor content being destroyed before transition end
|
21
demos/src/Examples/Transition/Vue/Component.vue
Normal file
21
demos/src/Examples/Transition/Vue/Component.vue
Normal file
@ -0,0 +1,21 @@
|
||||
<template>
|
||||
<NodeViewWrapper>
|
||||
<label>Vue Component</label>
|
||||
|
||||
<div class="content">
|
||||
<button @click="increase">This button has been clicked {{ node.attrs.count }} times.</button>
|
||||
</div>
|
||||
</NodeViewWrapper>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { nodeViewProps, NodeViewWrapper } from '@tiptap/vue-3'
|
||||
|
||||
const props = defineProps(nodeViewProps)
|
||||
|
||||
function increase() {
|
||||
props.updateAttributes({
|
||||
count: props.node.attrs.count + 1,
|
||||
})
|
||||
}
|
||||
</script>
|
36
demos/src/Examples/Transition/Vue/Extension.js
Normal file
36
demos/src/Examples/Transition/Vue/Extension.js
Normal file
@ -0,0 +1,36 @@
|
||||
import { mergeAttributes, Node } from '@tiptap/core'
|
||||
import { VueNodeViewRenderer } from '@tiptap/vue-3'
|
||||
|
||||
import Component from './Component.vue'
|
||||
|
||||
export default Node.create({
|
||||
name: 'vueComponent',
|
||||
|
||||
group: 'block',
|
||||
|
||||
atom: true,
|
||||
|
||||
addAttributes() {
|
||||
return {
|
||||
count: {
|
||||
default: 0,
|
||||
},
|
||||
}
|
||||
},
|
||||
|
||||
parseHTML() {
|
||||
return [
|
||||
{
|
||||
tag: 'vue-component',
|
||||
},
|
||||
]
|
||||
},
|
||||
|
||||
renderHTML({ HTMLAttributes }) {
|
||||
return ['vue-component', mergeAttributes(HTMLAttributes)]
|
||||
},
|
||||
|
||||
addNodeView() {
|
||||
return VueNodeViewRenderer(Component)
|
||||
},
|
||||
})
|
0
demos/src/Examples/Transition/Vue/index.html
Normal file
0
demos/src/Examples/Transition/Vue/index.html
Normal file
29
demos/src/Examples/Transition/Vue/index.spec.js
Normal file
29
demos/src/Examples/Transition/Vue/index.spec.js
Normal file
@ -0,0 +1,29 @@
|
||||
context('/src/Examples/Transition/Vue/', () => {
|
||||
beforeEach(() => {
|
||||
cy.visit('/src/Examples/Transition/Vue/')
|
||||
})
|
||||
|
||||
it('should not have an active tiptap instance but a button', () => {
|
||||
cy.get('.tiptap').should('not.exist')
|
||||
|
||||
cy.get('button').should('exist')
|
||||
})
|
||||
|
||||
it('clicking the button should show the editor', () => {
|
||||
cy.get('button').click()
|
||||
|
||||
cy.get('.tiptap').should('exist')
|
||||
cy.get('.tiptap').should('be.visible')
|
||||
})
|
||||
|
||||
it('clicking the button again should hide the editor', () => {
|
||||
cy.get('button').click()
|
||||
|
||||
cy.get('.tiptap').should('exist')
|
||||
cy.get('.tiptap').should('be.visible')
|
||||
|
||||
cy.get('button').click()
|
||||
|
||||
cy.get('.tiptap').should('not.exist')
|
||||
})
|
||||
})
|
66
demos/src/Examples/Transition/Vue/index.vue
Normal file
66
demos/src/Examples/Transition/Vue/index.vue
Normal file
@ -0,0 +1,66 @@
|
||||
<script setup lang="ts">
|
||||
|
||||
import StarterKit from '@tiptap/starter-kit'
|
||||
import { EditorContent, useEditor } from '@tiptap/vue-3'
|
||||
import { ref } from 'vue'
|
||||
|
||||
import VueComponent from './Extension.js'
|
||||
import type { TNote } from './types.js'
|
||||
|
||||
const note = ref<TNote>({
|
||||
id: 'note-1',
|
||||
content: `
|
||||
<p>Some random note text</p>
|
||||
<vue-component count="0"></vue-component>
|
||||
`,
|
||||
})
|
||||
|
||||
const editor = useEditor({
|
||||
content: note.value.content,
|
||||
editorProps: {
|
||||
attributes: {
|
||||
class: 'textarea',
|
||||
},
|
||||
},
|
||||
extensions: [
|
||||
StarterKit,
|
||||
VueComponent,
|
||||
],
|
||||
})
|
||||
|
||||
const showEditor = ref(false)
|
||||
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div>
|
||||
<button type="button" @click="showEditor = !showEditor" style="margin-bottom: 1rem;">
|
||||
{{ showEditor ? 'Hide editor' : 'Show editor' }}
|
||||
</button>
|
||||
|
||||
<transition name="fade">
|
||||
<div v-if="showEditor" class="tiptap-wrapper">
|
||||
<editor-content :editor="editor" />
|
||||
</div>
|
||||
</transition>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
.fade-enter-active,
|
||||
.fade-leave-active {
|
||||
transition: opacity 1s ease;
|
||||
}
|
||||
|
||||
.fade-enter-from,
|
||||
.fade-leave-to {
|
||||
opacity: 0;
|
||||
}
|
||||
|
||||
.tiptap-wrapper {
|
||||
background-color: var(--purple-light);
|
||||
border: 2px solid var(--purple);
|
||||
border-radius: 0.5rem;
|
||||
margin: 1rem 0;
|
||||
}
|
||||
</style>
|
@ -59,6 +59,7 @@ export const EditorContent = defineComponent({
|
||||
|
||||
editor.createNodeViews()
|
||||
})
|
||||
|
||||
}
|
||||
})
|
||||
|
||||
@ -69,27 +70,8 @@ export const EditorContent = defineComponent({
|
||||
return
|
||||
}
|
||||
|
||||
// destroy nodeviews before vue removes dom element
|
||||
if (!editor.isDestroyed) {
|
||||
editor.view.setProps({
|
||||
nodeViews: {},
|
||||
})
|
||||
}
|
||||
|
||||
editor.contentComponent = null
|
||||
editor.appContext = null
|
||||
|
||||
if (!editor.options.element.firstChild) {
|
||||
return
|
||||
}
|
||||
|
||||
const newElement = document.createElement('div')
|
||||
|
||||
newElement.append(...editor.options.element.childNodes)
|
||||
|
||||
editor.setOptions({
|
||||
element: newElement,
|
||||
})
|
||||
})
|
||||
|
||||
return { rootEl }
|
||||
|
Loading…
Reference in New Issue
Block a user