add basic implementation for node views
This commit is contained in:
@@ -75,8 +75,6 @@ declare module './Editor' {
|
|||||||
@magicMethods
|
@magicMethods
|
||||||
export class Editor extends EventEmitter {
|
export class Editor extends EventEmitter {
|
||||||
|
|
||||||
public renderer!: any
|
|
||||||
|
|
||||||
private proxy!: Editor
|
private proxy!: Editor
|
||||||
|
|
||||||
private commandManager!: CommandManager
|
private commandManager!: CommandManager
|
||||||
@@ -256,7 +254,6 @@ export class Editor extends EventEmitter {
|
|||||||
* Creates a ProseMirror view.
|
* Creates a ProseMirror view.
|
||||||
*/
|
*/
|
||||||
private createView() {
|
private createView() {
|
||||||
console.log('CREATE VIEW')
|
|
||||||
this.view = new EditorView(this.options.element, {
|
this.view = new EditorView(this.options.element, {
|
||||||
state: EditorState.create({
|
state: EditorState.create({
|
||||||
doc: this.createDocument(this.options.content),
|
doc: this.createDocument(this.options.content),
|
||||||
@@ -266,10 +263,16 @@ export class Editor extends EventEmitter {
|
|||||||
],
|
],
|
||||||
}),
|
}),
|
||||||
dispatchTransaction: this.dispatchTransaction.bind(this),
|
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,
|
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
|
const dom = this.view.dom as HTMLElement
|
||||||
dom.editor = this.proxy
|
dom.editor = this.proxy
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,8 +4,7 @@ import { Schema, Node as ProsemirrorNode } from 'prosemirror-model'
|
|||||||
import { inputRules } from 'prosemirror-inputrules'
|
import { inputRules } from 'prosemirror-inputrules'
|
||||||
import { EditorView, Decoration } from 'prosemirror-view'
|
import { EditorView, Decoration } from 'prosemirror-view'
|
||||||
import { Editor } from './Editor'
|
import { Editor } from './Editor'
|
||||||
// import capitalize from './utils/capitalize'
|
import { Extensions, NodeViewRenderer } from './types'
|
||||||
import { Extensions } from './types'
|
|
||||||
import getSchema from './utils/getSchema'
|
import getSchema from './utils/getSchema'
|
||||||
import getSchemaTypeByName from './utils/getSchemaTypeByName'
|
import getSchemaTypeByName from './utils/getSchemaTypeByName'
|
||||||
import splitExtensions from './utils/splitExtensions'
|
import splitExtensions from './utils/splitExtensions'
|
||||||
@@ -109,57 +108,22 @@ export default class ExtensionManager {
|
|||||||
type: getSchemaTypeByName(extension.name, this.schema),
|
type: getSchemaTypeByName(extension.name, this.schema),
|
||||||
}
|
}
|
||||||
|
|
||||||
const renderer = extension.addNodeView?.bind(context)?.()
|
const renderer = extension.addNodeView?.bind(context)?.() as NodeViewRenderer
|
||||||
|
|
||||||
const nodeview = (
|
const nodeview = (
|
||||||
node: ProsemirrorNode,
|
node: ProsemirrorNode,
|
||||||
view: EditorView,
|
view: EditorView,
|
||||||
getPos: (() => number) | boolean,
|
getPos: (() => number) | boolean,
|
||||||
decorations: Decoration[],
|
decorations: Decoration[],
|
||||||
) => {
|
) => renderer({
|
||||||
// @ts-ignore
|
editor: this.editor,
|
||||||
return new renderer({
|
node,
|
||||||
editor: this.editor,
|
getPos,
|
||||||
view,
|
decorations,
|
||||||
node,
|
})
|
||||||
getPos,
|
|
||||||
decorations,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
return [extension.name, nodeview]
|
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 {}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ import {
|
|||||||
} from 'prosemirror-model'
|
} from 'prosemirror-model'
|
||||||
import { Plugin } from 'prosemirror-state'
|
import { Plugin } from 'prosemirror-state'
|
||||||
import { ExtensionSpec, defaultExtension } from './Extension'
|
import { ExtensionSpec, defaultExtension } from './Extension'
|
||||||
import { Attributes, Overwrite } from './types'
|
import { Attributes, NodeViewRenderer, Overwrite } from './types'
|
||||||
import { Editor } from './Editor'
|
import { Editor } from './Editor'
|
||||||
|
|
||||||
export interface NodeExtensionSpec<Options = {}, Commands = {}> extends Overwrite<ExtensionSpec<Options, Commands>, {
|
export interface NodeExtensionSpec<Options = {}, Commands = {}> extends Overwrite<ExtensionSpec<Options, Commands>, {
|
||||||
@@ -143,7 +143,7 @@ export interface NodeExtensionSpec<Options = {}, Commands = {}> extends Overwrit
|
|||||||
/**
|
/**
|
||||||
* Node View
|
* Node View
|
||||||
*/
|
*/
|
||||||
addNodeView?: (() => any) | null,
|
addNodeView?: (() => NodeViewRenderer) | null,
|
||||||
}> {}
|
}> {}
|
||||||
|
|
||||||
export type NodeExtension = Required<Omit<NodeExtensionSpec, 'defaultOptions'> & {
|
export type NodeExtension = Required<Omit<NodeExtensionSpec, 'defaultOptions'> & {
|
||||||
|
|||||||
@@ -1,3 +0,0 @@
|
|||||||
export class NodeView {
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -1,6 +1,9 @@
|
|||||||
|
import { Node } from 'prosemirror-model'
|
||||||
|
import { Decoration, NodeView } from 'prosemirror-view'
|
||||||
import { Extension } from './Extension'
|
import { Extension } from './Extension'
|
||||||
import { NodeExtension } from './NodeExtension'
|
import { NodeExtension } from './NodeExtension'
|
||||||
import { MarkExtension } from './MarkExtension'
|
import { MarkExtension } from './MarkExtension'
|
||||||
|
import Editor from '..'
|
||||||
|
|
||||||
export type Extensions = (Extension | NodeExtension | MarkExtension)[]
|
export type Extensions = (Extension | NodeExtension | MarkExtension)[]
|
||||||
|
|
||||||
@@ -40,3 +43,12 @@ export type Overwrite<T, U> = Pick<T, Diff<keyof T, keyof U>> & U;
|
|||||||
export type AnyObject = {
|
export type AnyObject = {
|
||||||
[key: string]: any
|
[key: string]: any
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export type NodeViewRendererProps = {
|
||||||
|
editor: Editor,
|
||||||
|
node: Node,
|
||||||
|
getPos: (() => number) | boolean,
|
||||||
|
decorations: Decoration[]
|
||||||
|
}
|
||||||
|
|
||||||
|
export type NodeViewRenderer = (props: NodeViewRendererProps) => NodeView
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
import { Command, createNode, nodeInputRule } from '@tiptap/core'
|
import { Command, createNode, nodeInputRule } from '@tiptap/core'
|
||||||
import { VueRenderer } from '@tiptap/vue'
|
import { VueRenderer } from '@tiptap/vue'
|
||||||
|
import Vue from 'vue'
|
||||||
|
|
||||||
export const inputRegex = /!\[(.+|:?)]\((\S+)(?:(?:\s+)["'](\S+)["'])?\)/
|
export const inputRegex = /!\[(.+|:?)]\((\S+)(?:(?:\s+)["'](\S+)["'])?\)/
|
||||||
|
|
||||||
@@ -62,9 +63,11 @@ const Image = createNode({
|
|||||||
},
|
},
|
||||||
|
|
||||||
addNodeView() {
|
addNodeView() {
|
||||||
return VueRenderer({
|
const Component = Vue.extend({
|
||||||
template: '<div>vue component</div>',
|
template: '<div>this is a vue component</div>',
|
||||||
})
|
})
|
||||||
|
|
||||||
|
return VueRenderer(Component)
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|||||||
@@ -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 * from '@tiptap/core'
|
||||||
export { Renderer }
|
export { default as VueRenderer } from './src/VueRenderer'
|
||||||
export { VueRenderer }
|
|
||||||
export { default as EditorContent } from './src/components/EditorContent'
|
export { default as EditorContent } from './src/components/EditorContent'
|
||||||
|
|
||||||
export class Editor extends CoreEditor {
|
|
||||||
renderer = Renderer
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -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
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -1,57 +1,20 @@
|
|||||||
// @ts-nocheck
|
import { NodeViewRendererProps } from '@tiptap/core'
|
||||||
|
import { NodeView } from 'prosemirror-view'
|
||||||
// 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 Vue from 'vue'
|
import Vue from 'vue'
|
||||||
|
import { VueConstructor } from 'vue/types/umd'
|
||||||
|
|
||||||
export default (component: any) => class ImageView {
|
class VueNodeView implements NodeView {
|
||||||
|
|
||||||
vm!: Vue
|
vm!: Vue
|
||||||
|
|
||||||
constructor(props: { editor: Editor, node: ProsemirrorNode, view: EditorView, getPos: any }) {
|
constructor(component: Vue | VueConstructor, props: NodeViewRendererProps) {
|
||||||
const {
|
// const { node, editor, getPos } = props
|
||||||
node, editor, getPos, view,
|
|
||||||
} = props
|
|
||||||
// const { view } = editor
|
// const { view } = editor
|
||||||
|
|
||||||
// this.dom = document.createElement('div')
|
|
||||||
// this.dom.innerHTML = 'hello node view'
|
|
||||||
|
|
||||||
this.mount(component)
|
this.mount(component)
|
||||||
}
|
}
|
||||||
|
|
||||||
mount(component: Vue) {
|
mount(component: Vue | VueConstructor) {
|
||||||
const Component = Vue.extend(component)
|
const Component = Vue.extend(component)
|
||||||
|
|
||||||
this.vm = new Component({
|
this.vm = new Component({
|
||||||
@@ -65,32 +28,15 @@ export default (component: any) => class ImageView {
|
|||||||
}
|
}
|
||||||
|
|
||||||
get contentDOM() {
|
get contentDOM() {
|
||||||
return this.vm.$refs.content
|
return this.vm.$refs.content as Element
|
||||||
}
|
}
|
||||||
|
|
||||||
stopEvent() {
|
stopEvent() {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
// console.log(what)
|
}
|
||||||
// return new class ImageView {
|
|
||||||
// constructor(node, view, getPos) {
|
export default function VueRenderer(component: Vue | VueConstructor) {
|
||||||
// this.dom = document.createElement('img')
|
return (props: NodeViewRendererProps) => new VueNodeView(component, props) as NodeView
|
||||||
// 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)
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user