diff --git a/packages/vue-3/src/Editor.ts b/packages/vue-3/src/Editor.ts index 691eec2ff..0933a7992 100644 --- a/packages/vue-3/src/Editor.ts +++ b/packages/vue-3/src/Editor.ts @@ -1,14 +1,16 @@ import { Editor as CoreEditor, EditorOptions } from '@tiptap/core' import { EditorState, Plugin, PluginKey } from '@tiptap/pm/state' import { - AppContext, ComponentInternalInstance, ComponentPublicInstance, customRef, markRaw, + reactive, Ref, } from 'vue' +import { VueRenderer } from './VueRenderer.js' + function useDebouncedRef(value: T) { return customRef((track, trigger) => { return { @@ -40,9 +42,9 @@ export class Editor extends CoreEditor { private reactiveExtensionStorage: Ref> - public contentComponent: ContentComponent | null = null + public vueRenderers = reactive>(new Map()) - public appContext: AppContext | null = null + public contentComponent: ContentComponent | null = null constructor(options: Partial = {}) { super(options) diff --git a/packages/vue-3/src/EditorContent.ts b/packages/vue-3/src/EditorContent.ts index 7c215c3c9..09f1b51c4 100644 --- a/packages/vue-3/src/EditorContent.ts +++ b/packages/vue-3/src/EditorContent.ts @@ -1,4 +1,5 @@ import { + DefineComponent, defineComponent, getCurrentInstance, h, @@ -7,6 +8,7 @@ import { PropType, Ref, ref, + Teleport, unref, watchEffect, } from 'vue' @@ -43,17 +45,6 @@ export const EditorContent = defineComponent({ // @ts-ignore editor.contentComponent = instance.ctx._ - if (instance) { - editor.appContext = { - ...instance.appContext, - provides: { - // @ts-ignore - ...instance.provides, - ...instance.appContext.provides, - }, - } - } - editor.setOptions({ element, }) @@ -78,7 +69,6 @@ export const EditorContent = defineComponent({ } editor.contentComponent = null - editor.appContext = null if (!editor.options.element.firstChild) { return @@ -97,11 +87,35 @@ export const EditorContent = defineComponent({ }, render() { + const vueRenderers: any[] = [] + + if (this.editor) { + this.editor.vueRenderers.forEach(vueRenderer => { + const node = h( + Teleport, + { + to: vueRenderer.teleportElement, + key: vueRenderer.id, + }, + h( + vueRenderer.component as DefineComponent, + { + ref: vueRenderer.id, + ...vueRenderer.props, + }, + ), + ) + + vueRenderers.push(node) + }) + } + return h( 'div', { ref: (el: any) => { this.rootEl = el }, }, + ...vueRenderers, ) }, }) diff --git a/packages/vue-3/src/VueNodeViewRenderer.ts b/packages/vue-3/src/VueNodeViewRenderer.ts index 201e051e0..a9f9c448d 100644 --- a/packages/vue-3/src/VueNodeViewRenderer.ts +++ b/packages/vue-3/src/VueNodeViewRenderer.ts @@ -124,7 +124,7 @@ class VueNodeView extends NodeView, } -type ExtendedVNode = ReturnType | null - -interface RenderedComponent { - vNode: ExtendedVNode - destroy: () => void - el: Element | null -} - /** * This class is used to render Vue components inside the editor. */ export class VueRenderer { id: string - renderedComponent!: RenderedComponent - editor: ExtendedEditor component: Component - el: Element | null + teleportElement: Element + + element: Element props: Record @@ -38,40 +28,35 @@ export class VueRenderer { this.id = Math.floor(Math.random() * 0xFFFFFFFF).toString() this.editor = editor as ExtendedEditor this.component = markRaw(component) - this.el = document.createElement('div') + this.teleportElement = document.createElement('div') + this.element = this.teleportElement this.props = reactive(props) - this.renderedComponent = this.renderComponent() - } + this.editor.vueRenderers.set(this.id, this) - get element(): Element | null { - return this.renderedComponent.el - } + if (this.editor.contentComponent) { + this.editor.contentComponent.update() - renderComponent() { - let vNode: ExtendedVNode = h(this.component as DefineComponent, this.props) + if (this.teleportElement.children.length !== 1) { + throw Error('VueRenderer doesn’t support multiple child elements.') + } - if (typeof document !== 'undefined' && this.el) { render(vNode, this.el) } - - const destroy = () => { - if (this.el) { render(null, this.el) } - this.el = null - vNode = null + this.element = this.teleportElement.firstElementChild as Element } + } - return { vNode, destroy, el: this.el ? this.el.firstElementChild : null } + get ref(): any { + return this.editor.contentComponent?.refs[this.id] } updateProps(props: Record = {}): void { - Object .entries(props) .forEach(([key, value]) => { this.props[key] = value }) - this.renderComponent() } destroy(): void { - this.renderedComponent.destroy() + this.editor.vueRenderers.delete(this.id) } }