From f9089932ff58a5819e677de9eb5df78ce76fbd70 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philipp=20Ku=CC=88hn?= Date: Fri, 30 Oct 2020 11:08:23 +0100 Subject: [PATCH] add basic implementation for node views --- packages/core/src/Editor.ts | 11 ++-- packages/core/src/ExtensionManager.ts | 52 +++--------------- packages/core/src/NodeExtension.ts | 4 +- packages/core/src/NodeView.ts | 3 -- packages/core/src/types.ts | 12 +++++ packages/extension-image/index.ts | 7 ++- packages/vue/index.ts | 11 +--- packages/vue/src/Renderer.ts | 32 ----------- packages/vue/src/VueRenderer.ts | 78 +++++---------------------- 9 files changed, 47 insertions(+), 163 deletions(-) delete mode 100644 packages/core/src/NodeView.ts delete mode 100644 packages/vue/src/Renderer.ts diff --git a/packages/core/src/Editor.ts b/packages/core/src/Editor.ts index e925f422..238140df 100644 --- a/packages/core/src/Editor.ts +++ b/packages/core/src/Editor.ts @@ -75,8 +75,6 @@ declare module './Editor' { @magicMethods export class Editor extends EventEmitter { - public renderer!: any - private proxy!: Editor private commandManager!: CommandManager @@ -256,7 +254,6 @@ export class Editor extends EventEmitter { * Creates a ProseMirror view. */ private createView() { - console.log('CREATE VIEW') this.view = new EditorView(this.options.element, { state: EditorState.create({ doc: this.createDocument(this.options.content), @@ -266,10 +263,16 @@ export class Editor extends EventEmitter { ], }), dispatchTransaction: this.dispatchTransaction.bind(this), + }) + + // `editor.view` is not yet available at this time. + // Therefore we will add all node views directly afterwards. + this.view.setProps({ nodeViews: this.extensionManager.nodeViews, }) - // store editor in dom element for better testing + // Let’s store the editor instance in the DOM element. + // So we’ll have access to it for tests. const dom = this.view.dom as HTMLElement dom.editor = this.proxy } diff --git a/packages/core/src/ExtensionManager.ts b/packages/core/src/ExtensionManager.ts index ab26ef84..2d27f087 100644 --- a/packages/core/src/ExtensionManager.ts +++ b/packages/core/src/ExtensionManager.ts @@ -4,8 +4,7 @@ import { Schema, Node as ProsemirrorNode } from 'prosemirror-model' import { inputRules } from 'prosemirror-inputrules' import { EditorView, Decoration } from 'prosemirror-view' import { Editor } from './Editor' -// import capitalize from './utils/capitalize' -import { Extensions } from './types' +import { Extensions, NodeViewRenderer } from './types' import getSchema from './utils/getSchema' import getSchemaTypeByName from './utils/getSchemaTypeByName' import splitExtensions from './utils/splitExtensions' @@ -109,57 +108,22 @@ export default class ExtensionManager { type: getSchemaTypeByName(extension.name, this.schema), } - const renderer = extension.addNodeView?.bind(context)?.() + const renderer = extension.addNodeView?.bind(context)?.() as NodeViewRenderer const nodeview = ( node: ProsemirrorNode, view: EditorView, getPos: (() => number) | boolean, decorations: Decoration[], - ) => { - // @ts-ignore - return new renderer({ - editor: this.editor, - view, - node, - getPos, - decorations, - }) - } + ) => renderer({ + editor: this.editor, + node, + getPos, + decorations, + }) return [extension.name, nodeview] })) - - // const { renderer: Renderer } = this.editor - - // if (!Renderer || !Renderer.type) { - // return {} - // } - - // const prop = `to${capitalize(Renderer.type)}` - - // return collect(this.extensions) - // .where('extensionType', 'node') - // .filter((extension: any) => extension.schema()[prop]) - // .map((extension: any) => { - // return ( - // node: ProsemirrorNode, - // view: EditorView, - // getPos: (() => number) | boolean, - // decorations: Decoration[], - // ) => { - // return new Renderer(extension.schema()[prop], { - // extension, - // editor: this.editor, - // node, - // getPos, - // decorations, - // }) - // } - // }) - // .all() - - return {} } } diff --git a/packages/core/src/NodeExtension.ts b/packages/core/src/NodeExtension.ts index 8de0c678..17d6e91d 100644 --- a/packages/core/src/NodeExtension.ts +++ b/packages/core/src/NodeExtension.ts @@ -3,7 +3,7 @@ import { } from 'prosemirror-model' import { Plugin } from 'prosemirror-state' import { ExtensionSpec, defaultExtension } from './Extension' -import { Attributes, Overwrite } from './types' +import { Attributes, NodeViewRenderer, Overwrite } from './types' import { Editor } from './Editor' export interface NodeExtensionSpec extends Overwrite, { @@ -143,7 +143,7 @@ export interface NodeExtensionSpec extends Overwrit /** * Node View */ - addNodeView?: (() => any) | null, + addNodeView?: (() => NodeViewRenderer) | null, }> {} export type NodeExtension = Required & { diff --git a/packages/core/src/NodeView.ts b/packages/core/src/NodeView.ts deleted file mode 100644 index 30a8e894..00000000 --- a/packages/core/src/NodeView.ts +++ /dev/null @@ -1,3 +0,0 @@ -export class NodeView { - -} diff --git a/packages/core/src/types.ts b/packages/core/src/types.ts index 5533a4fd..d1385106 100644 --- a/packages/core/src/types.ts +++ b/packages/core/src/types.ts @@ -1,6 +1,9 @@ +import { Node } from 'prosemirror-model' +import { Decoration, NodeView } from 'prosemirror-view' import { Extension } from './Extension' import { NodeExtension } from './NodeExtension' import { MarkExtension } from './MarkExtension' +import Editor from '..' export type Extensions = (Extension | NodeExtension | MarkExtension)[] @@ -40,3 +43,12 @@ export type Overwrite = Pick> & U; export type AnyObject = { [key: string]: any } + +export type NodeViewRendererProps = { + editor: Editor, + node: Node, + getPos: (() => number) | boolean, + decorations: Decoration[] +} + +export type NodeViewRenderer = (props: NodeViewRendererProps) => NodeView diff --git a/packages/extension-image/index.ts b/packages/extension-image/index.ts index bf975076..fccc8ff8 100644 --- a/packages/extension-image/index.ts +++ b/packages/extension-image/index.ts @@ -1,5 +1,6 @@ import { Command, createNode, nodeInputRule } from '@tiptap/core' import { VueRenderer } from '@tiptap/vue' +import Vue from 'vue' export const inputRegex = /!\[(.+|:?)]\((\S+)(?:(?:\s+)["'](\S+)["'])?\)/ @@ -62,9 +63,11 @@ const Image = createNode({ }, addNodeView() { - return VueRenderer({ - template: '
vue component
', + const Component = Vue.extend({ + template: '
this is a vue component
', }) + + return VueRenderer(Component) }, }) diff --git a/packages/vue/index.ts b/packages/vue/index.ts index f1e855cc..80cb8873 100644 --- a/packages/vue/index.ts +++ b/packages/vue/index.ts @@ -1,12 +1,3 @@ -import { Editor as CoreEditor } from '@tiptap/core' -import Renderer from './src/Renderer' -import VueRenderer from './src/VueRenderer' - export * from '@tiptap/core' -export { Renderer } -export { VueRenderer } +export { default as VueRenderer } from './src/VueRenderer' export { default as EditorContent } from './src/components/EditorContent' - -export class Editor extends CoreEditor { - renderer = Renderer -} diff --git a/packages/vue/src/Renderer.ts b/packages/vue/src/Renderer.ts deleted file mode 100644 index 67d3e6ec..00000000 --- a/packages/vue/src/Renderer.ts +++ /dev/null @@ -1,32 +0,0 @@ -import Vue from 'vue' -import { ComponentRenderer } from '@tiptap/core' - -export default class Renderer extends ComponentRenderer { - - static type = 'vue' - - vm!: Vue - - constructor(component: Vue) { - super() - this.mount(component) - } - - mount(component: Vue) { - const Component = Vue.extend(component) - - this.vm = new Component({ - // parent: this.parent, - // propsData: props, - }).$mount() - } - - get dom() { - return this.vm.$el - } - - get contentDOM() { - return this.vm.$refs.content - } - -} diff --git a/packages/vue/src/VueRenderer.ts b/packages/vue/src/VueRenderer.ts index e58ab726..9bfd525c 100644 --- a/packages/vue/src/VueRenderer.ts +++ b/packages/vue/src/VueRenderer.ts @@ -1,57 +1,20 @@ -// @ts-nocheck - -// export default (component: any) => { -// console.log('vue renderer', component) - -// // return (node, view, getPos) => { -// return what => { - -// console.log(what) -// // return new class ImageView { -// // constructor(node, view, getPos) { -// // this.dom = document.createElement('img') -// // this.dom.src = node.attrs.src -// // this.dom.alt = node.attrs.alt -// // this.dom.addEventListener('click', e => { -// // e.preventDefault() -// // const alt = prompt('New alt text:', '') -// // if (alt) { -// // view.dispatch(view.state.tr.setNodeMarkup(getPos(), null, { -// // src: node.attrs.src, -// // alt, -// // })) -// // } -// // }) -// // } - -// // stopEvent() { return true } -// // }(node, view, getPos) - -// } -// } - -import { Editor } from '@tiptap/core' -import { Node as ProsemirrorNode } from 'prosemirror-model' -import { EditorView } from 'prosemirror-view' +import { NodeViewRendererProps } from '@tiptap/core' +import { NodeView } from 'prosemirror-view' import Vue from 'vue' +import { VueConstructor } from 'vue/types/umd' -export default (component: any) => class ImageView { +class VueNodeView implements NodeView { vm!: Vue - constructor(props: { editor: Editor, node: ProsemirrorNode, view: EditorView, getPos: any }) { - const { - node, editor, getPos, view, - } = props + constructor(component: Vue | VueConstructor, props: NodeViewRendererProps) { + // const { node, editor, getPos } = props // const { view } = editor - // this.dom = document.createElement('div') - // this.dom.innerHTML = 'hello node view' - this.mount(component) } - mount(component: Vue) { + mount(component: Vue | VueConstructor) { const Component = Vue.extend(component) this.vm = new Component({ @@ -65,32 +28,15 @@ export default (component: any) => class ImageView { } get contentDOM() { - return this.vm.$refs.content + return this.vm.$refs.content as Element } stopEvent() { return true } - // console.log(what) - // return new class ImageView { - // constructor(node, view, getPos) { - // this.dom = document.createElement('img') - // this.dom.src = node.attrs.src - // this.dom.alt = node.attrs.alt - // this.dom.addEventListener('click', e => { - // e.preventDefault() - // const alt = prompt('New alt text:', '') - // if (alt) { - // view.dispatch(view.state.tr.setNodeMarkup(getPos(), null, { - // src: node.attrs.src, - // alt, - // })) - // } - // }) - // } - - // stopEvent() { return true } - // }(node, view, getPos) - +} + +export default function VueRenderer(component: Vue | VueConstructor) { + return (props: NodeViewRendererProps) => new VueNodeView(component, props) as NodeView }