mirror of
https://github.com/ueberdosis/tiptap.git
synced 2024-11-23 19:19:03 +08:00
fix(vue-3): on editor destruction, transition smoothly (#5772)
This commit is contained in:
parent
8a2e548c5b
commit
94a8d258f8
5
.changeset/five-flowers-eat.md
Normal file
5
.changeset/five-flowers-eat.md
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
---
|
||||||
|
"@tiptap/vue-3": patch
|
||||||
|
---
|
||||||
|
|
||||||
|
Fix editor destruction before transition end if editor is nested
|
@ -36,7 +36,7 @@ Before submitting a pull request:
|
|||||||
- Check the codebase to ensure that your feature doesn't already exist.
|
- Check the codebase to ensure that your feature doesn't already exist.
|
||||||
- Check the pull requests to ensure that another person hasn't already submitted the feature or fix.
|
- Check the pull requests to ensure that another person hasn't already submitted the feature or fix.
|
||||||
|
|
||||||
Before commiting:
|
Before committing:
|
||||||
|
|
||||||
- Make sure to run the tests and linter before committing your changes.
|
- Make sure to run the tests and linter before committing your changes.
|
||||||
- If you are making changes to one of the packages, make sure to **always** include a [changeset](https://github.com/changesets/changesets) in your PR describing **what changed** with a **description** of the change. Those are responsible for changelog creation
|
- If you are making changes to one of the packages, make sure to **always** include a [changeset](https://github.com/changesets/changesets) in your PR describing **what changed** with a **description** of the change. Those are responsible for changelog creation
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
import { mergeAttributes, Node } from '@tiptap/core'
|
import { mergeAttributes, Node } from '@tiptap/core'
|
||||||
import { VueNodeViewRenderer } from '@tiptap/vue-3'
|
import { VueNodeViewRenderer } from '@tiptap/vue-3'
|
||||||
|
|
||||||
import Component from './Component.vue'
|
import Component from './VueComponent.vue'
|
||||||
|
|
||||||
export default Node.create({
|
export default Node.create({
|
||||||
name: 'vueComponent',
|
name: 'vueComponent',
|
||||||
|
36
demos/src/Examples/Transition/Vue/ParentComponent.vue
Normal file
36
demos/src/Examples/Transition/Vue/ParentComponent.vue
Normal file
@ -0,0 +1,36 @@
|
|||||||
|
<template>
|
||||||
|
<div>
|
||||||
|
<EditorContent :editor="editor" />
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<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,
|
||||||
|
],
|
||||||
|
})
|
||||||
|
|
||||||
|
</script>
|
@ -3,26 +3,30 @@ context('/src/Examples/Transition/Vue/', () => {
|
|||||||
cy.visit('/src/Examples/Transition/Vue/')
|
cy.visit('/src/Examples/Transition/Vue/')
|
||||||
})
|
})
|
||||||
|
|
||||||
it('should not have an active tiptap instance but a button', () => {
|
it('should have two buttons and no active tiptap instance', () => {
|
||||||
cy.get('.tiptap').should('not.exist')
|
cy.get('.tiptap').should('not.exist')
|
||||||
|
|
||||||
cy.get('#toggle-editor').should('exist')
|
cy.get('#toggle-direct-editor').should('exist')
|
||||||
|
cy.get('#toggle-nested-editor').should('exist')
|
||||||
})
|
})
|
||||||
|
|
||||||
it('clicking the button should show the editor', () => {
|
it('clicking the buttons should show two editors', () => {
|
||||||
cy.get('#toggle-editor').click()
|
cy.get('#toggle-direct-editor').click()
|
||||||
|
cy.get('#toggle-nested-editor').click()
|
||||||
|
|
||||||
cy.get('.tiptap').should('exist')
|
cy.get('.tiptap').should('exist')
|
||||||
cy.get('.tiptap').should('be.visible')
|
cy.get('.tiptap').should('be.visible')
|
||||||
})
|
})
|
||||||
|
|
||||||
it('clicking the button again should hide the editor', () => {
|
it('clicking the buttons again should hide the editors', () => {
|
||||||
cy.get('#toggle-editor').click()
|
cy.get('#toggle-direct-editor').click()
|
||||||
|
cy.get('#toggle-nested-editor').click()
|
||||||
|
|
||||||
cy.get('.tiptap').should('exist')
|
cy.get('.tiptap').should('exist')
|
||||||
cy.get('.tiptap').should('be.visible')
|
cy.get('.tiptap').should('be.visible')
|
||||||
|
|
||||||
cy.get('#toggle-editor').click()
|
cy.get('#toggle-direct-editor').click()
|
||||||
|
cy.get('#toggle-nested-editor').click()
|
||||||
|
|
||||||
cy.get('.tiptap').should('not.exist')
|
cy.get('.tiptap').should('not.exist')
|
||||||
})
|
})
|
||||||
|
@ -1,12 +1,18 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
|
|
||||||
import StarterKit from '@tiptap/starter-kit'
|
import StarterKit from '@tiptap/starter-kit'
|
||||||
import { EditorContent, useEditor } from '@tiptap/vue-3'
|
import { EditorContent, useEditor } from '@tiptap/vue-3'
|
||||||
import { ref } from 'vue'
|
import { ref } from 'vue'
|
||||||
|
|
||||||
import VueComponent from './Extension.js'
|
import VueComponent from './Extension.js'
|
||||||
|
import ParentComponent from './ParentComponent.vue'
|
||||||
import type { TNote } from './types.js'
|
import type { TNote } from './types.js'
|
||||||
|
|
||||||
|
/** Display editor in the same component */
|
||||||
|
const showDirectEditor = ref(false)
|
||||||
|
|
||||||
|
/** Display editor in a child component */
|
||||||
|
const showNestedEditor = ref(false)
|
||||||
|
|
||||||
const note = ref<TNote>({
|
const note = ref<TNote>({
|
||||||
id: 'note-1',
|
id: 'note-1',
|
||||||
content: `
|
content: `
|
||||||
@ -28,24 +34,43 @@ const editor = useEditor({
|
|||||||
],
|
],
|
||||||
})
|
})
|
||||||
|
|
||||||
const showEditor = ref(false)
|
|
||||||
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
|
<!-- Transition with editor in the same component -->
|
||||||
<div>
|
<div>
|
||||||
<button
|
<button
|
||||||
|
id="toggle-direct-editor"
|
||||||
type="button"
|
type="button"
|
||||||
@click="showEditor = !showEditor"
|
|
||||||
style="margin-bottom: 1rem;"
|
style="margin-bottom: 1rem;"
|
||||||
id="toggle-editor"
|
@click="showDirectEditor = !showDirectEditor"
|
||||||
>
|
>
|
||||||
{{ showEditor ? 'Hide editor' : 'Show editor' }}
|
{{ showDirectEditor ? 'Hide direct editor' : 'Show direct editor' }}
|
||||||
</button>
|
</button>
|
||||||
|
|
||||||
<transition name="fade">
|
<transition name="fade">
|
||||||
<div v-if="showEditor" class="tiptap-wrapper">
|
<div v-if="showDirectEditor" class="tiptap-wrapper">
|
||||||
<editor-content :editor="editor" />
|
<EditorContent :editor="editor" />
|
||||||
|
</div>
|
||||||
|
</transition>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<hr>
|
||||||
|
|
||||||
|
<!-- Transition with editor in a child component -->
|
||||||
|
<div>
|
||||||
|
<button
|
||||||
|
id="toggle-nested-editor"
|
||||||
|
type="button"
|
||||||
|
style="margin-bottom: 1rem;"
|
||||||
|
@click="showNestedEditor = !showNestedEditor"
|
||||||
|
>
|
||||||
|
{{ showNestedEditor ? 'Hide nested editor' : 'Show nested editor' }}
|
||||||
|
</button>
|
||||||
|
|
||||||
|
<transition name="fade">
|
||||||
|
<div v-if="showNestedEditor" class="tiptap-wrapper">
|
||||||
|
<ParentComponent />
|
||||||
</div>
|
</div>
|
||||||
</transition>
|
</transition>
|
||||||
</div>
|
</div>
|
||||||
@ -62,6 +87,11 @@ const showEditor = ref(false)
|
|||||||
opacity: 0;
|
opacity: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
hr {
|
||||||
|
margin-top: 1rem;
|
||||||
|
margin-bottom: 1rem;
|
||||||
|
}
|
||||||
|
|
||||||
.tiptap-wrapper {
|
.tiptap-wrapper {
|
||||||
background-color: var(--purple-light);
|
background-color: var(--purple-light);
|
||||||
border: 2px solid var(--purple);
|
border: 2px solid var(--purple);
|
||||||
|
@ -59,7 +59,6 @@ export const EditorContent = defineComponent({
|
|||||||
|
|
||||||
editor.createNodeViews()
|
editor.createNodeViews()
|
||||||
})
|
})
|
||||||
|
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
|
@ -11,6 +11,12 @@ export const useEditor = (options: Partial<EditorOptions> = {}) => {
|
|||||||
})
|
})
|
||||||
|
|
||||||
onBeforeUnmount(() => {
|
onBeforeUnmount(() => {
|
||||||
|
// Cloning root node (and its children) to avoid content being lost by destroy
|
||||||
|
const nodes = editor.value?.options.element
|
||||||
|
const newEl = nodes?.cloneNode(true) as HTMLElement
|
||||||
|
|
||||||
|
nodes?.parentNode?.replaceChild(newEl, nodes)
|
||||||
|
|
||||||
editor.value?.destroy()
|
editor.value?.destroy()
|
||||||
})
|
})
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user