From d720edbe2421aa18a2e568bba63622165207c983 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philipp=20K=C3=BChn?= Date: Tue, 27 Jul 2021 12:26:24 +0200 Subject: [PATCH] feat!: provide more context to update function of node views, fix #1611 * add more powerful update option to node views * add object params for all node view option props --- packages/core/src/NodeView.ts | 26 ++++++++------ packages/core/src/types.ts | 9 +++-- packages/react/src/ReactNodeViewRenderer.tsx | 38 +++++++++++++++----- packages/vue-2/src/VueNodeViewRenderer.ts | 38 +++++++++++++++----- packages/vue-3/src/VueNodeViewRenderer.ts | 38 +++++++++++++++----- 5 files changed, 111 insertions(+), 38 deletions(-) diff --git a/packages/core/src/NodeView.ts b/packages/core/src/NodeView.ts index c0e6d4b1..020582f5 100644 --- a/packages/core/src/NodeView.ts +++ b/packages/core/src/NodeView.ts @@ -6,12 +6,18 @@ import { Node } from './Node' import isiOS from './utilities/isiOS' import { NodeViewRendererProps, NodeViewRendererOptions } from './types' -export class NodeView implements ProseMirrorNodeView { +export class NodeView< + Component, + Editor extends CoreEditor = CoreEditor, + Options extends NodeViewRendererOptions = NodeViewRendererOptions, +> implements ProseMirrorNodeView { component: Component editor: Editor + options: Options + extension: Node node: ProseMirrorNode @@ -22,16 +28,14 @@ export class NodeView impleme isDragging = false - options: NodeViewRendererOptions = { - stopEvent: null, - update: null, - ignoreMutation: null, - } - - constructor(component: Component, props: NodeViewRendererProps, options?: Partial) { + constructor(component: Component, props: NodeViewRendererProps, options?: Partial) { this.component = component - this.options = { ...this.options, ...options } this.editor = props.editor as Editor + this.options = { + stopEvent: null, + ignoreMutation: null, + ...options, + } as Options this.extension = props.extension this.node = props.node this.decorations = props.decorations @@ -98,7 +102,7 @@ export class NodeView impleme } if (typeof this.options.stopEvent === 'function') { - return this.options.stopEvent(event) + return this.options.stopEvent({ event }) } const target = (event.target as HTMLElement) @@ -178,7 +182,7 @@ export class NodeView impleme } if (typeof this.options.ignoreMutation === 'function') { - return this.options.ignoreMutation(mutation) + return this.options.ignoreMutation({ mutation }) } // a leaf/atom node is like a black box for ProseMirror diff --git a/packages/core/src/types.ts b/packages/core/src/types.ts index 0bdde225..fb9692dd 100644 --- a/packages/core/src/types.ts +++ b/packages/core/src/types.ts @@ -146,9 +146,12 @@ export type NodeViewProps = { } export interface NodeViewRendererOptions { - stopEvent: ((event: Event) => boolean) | null, - update: ((node: ProseMirrorNode, decorations: Decoration[]) => boolean) | null, - ignoreMutation: ((mutation: MutationRecord | { type: 'selection', target: Element }) => boolean) | null, + stopEvent: ((props: { + event: Event + }) => boolean) | null, + ignoreMutation: ((props: { + mutation: MutationRecord | { type: 'selection', target: Element } + }) => boolean) | null, } export type NodeViewRendererProps = { diff --git a/packages/react/src/ReactNodeViewRenderer.tsx b/packages/react/src/ReactNodeViewRenderer.tsx index 1b11af87..f93181ad 100644 --- a/packages/react/src/ReactNodeViewRenderer.tsx +++ b/packages/react/src/ReactNodeViewRenderer.tsx @@ -4,6 +4,7 @@ import { NodeViewProps, NodeViewRenderer, NodeViewRendererProps, + NodeViewRendererOptions, } from '@tiptap/core' import { Decoration, NodeView as ProseMirrorNodeView } from 'prosemirror-view' import { Node as ProseMirrorNode } from 'prosemirror-model' @@ -11,13 +12,17 @@ import { Editor } from './Editor' import { ReactRenderer } from './ReactRenderer' import { ReactNodeViewContext } from './useReactNodeView' -export interface ReactNodeViewRendererOptions { - stopEvent: ((event: Event) => boolean) | null, - update: ((node: ProseMirrorNode, decorations: Decoration[]) => boolean) | null, - ignoreMutation: ((mutation: MutationRecord | { type: 'selection', target: Element }) => boolean) | null, +export interface ReactNodeViewRendererOptions extends NodeViewRendererOptions { + update: ((props: { + oldNode: ProseMirrorNode, + oldDecorations: Decoration[], + newNode: ProseMirrorNode, + newDecorations: Decoration[], + updateProps: () => void, + }) => boolean) | null, } -class ReactNodeView extends NodeView { +class ReactNodeView extends NodeView { renderer!: ReactRenderer @@ -110,8 +115,25 @@ class ReactNodeView extends NodeView { } update(node: ProseMirrorNode, decorations: Decoration[]) { + const updateProps = (props?: Record) => { + this.renderer.updateProps(props) + this.maybeMoveContentDOM() + } + if (typeof this.options.update === 'function') { - return this.options.update(node, decorations) + const oldNode = this.node + const oldDecorations = this.decorations + + this.node = node + this.decorations = decorations + + return this.options.update({ + oldNode, + oldDecorations, + newNode: node, + newDecorations: decorations, + updateProps, + }) } if (node.type !== this.node.type) { @@ -124,8 +146,8 @@ class ReactNodeView extends NodeView { this.node = node this.decorations = decorations - this.renderer.updateProps({ node, decorations }) - this.maybeMoveContentDOM() + + updateProps() return true } diff --git a/packages/vue-2/src/VueNodeViewRenderer.ts b/packages/vue-2/src/VueNodeViewRenderer.ts index 8a0bcf8c..6f0c79ff 100644 --- a/packages/vue-2/src/VueNodeViewRenderer.ts +++ b/packages/vue-2/src/VueNodeViewRenderer.ts @@ -3,6 +3,7 @@ import { NodeViewProps, NodeViewRenderer, NodeViewRendererProps, + NodeViewRendererOptions, } from '@tiptap/core' import { Decoration, NodeView as ProseMirrorNodeView } from 'prosemirror-view' import { Node as ProseMirrorNode } from 'prosemirror-model' @@ -46,13 +47,17 @@ export const nodeViewProps = { }, } -export interface VueNodeViewRendererOptions { - stopEvent: ((event: Event) => boolean) | null, - update: ((node: ProseMirrorNode, decorations: Decoration[]) => boolean) | null, - ignoreMutation: ((mutation: MutationRecord | { type: 'selection', target: Element }) => boolean) | null, +export interface VueNodeViewRendererOptions extends NodeViewRendererOptions { + update: ((props: { + oldNode: ProseMirrorNode, + oldDecorations: Decoration[], + newNode: ProseMirrorNode, + newDecorations: Decoration[], + updateProps: () => void, + }) => boolean) | null, } -class VueNodeView extends NodeView<(Vue | VueConstructor), Editor> { +class VueNodeView extends NodeView<(Vue | VueConstructor), Editor, VueNodeViewRendererOptions> { renderer!: VueRenderer @@ -115,8 +120,25 @@ class VueNodeView extends NodeView<(Vue | VueConstructor), Editor> { } update(node: ProseMirrorNode, decorations: Decoration[]) { + const updateProps = (props?: Record) => { + this.decorationClasses.value = this.getDecorationClasses() + this.renderer.updateProps(props) + } + if (typeof this.options.update === 'function') { - return this.options.update(node, decorations) + const oldNode = this.node + const oldDecorations = this.decorations + + this.node = node + this.decorations = decorations + + return this.options.update({ + oldNode, + oldDecorations, + newNode: node, + newDecorations: decorations, + updateProps, + }) } if (node.type !== this.node.type) { @@ -129,8 +151,8 @@ class VueNodeView extends NodeView<(Vue | VueConstructor), Editor> { this.node = node this.decorations = decorations - this.decorationClasses.value = this.getDecorationClasses() - this.renderer.updateProps({ node, decorations }) + + updateProps() return true } diff --git a/packages/vue-3/src/VueNodeViewRenderer.ts b/packages/vue-3/src/VueNodeViewRenderer.ts index 856896ea..ed034b0f 100644 --- a/packages/vue-3/src/VueNodeViewRenderer.ts +++ b/packages/vue-3/src/VueNodeViewRenderer.ts @@ -3,6 +3,7 @@ import { NodeViewProps, NodeViewRenderer, NodeViewRendererProps, + NodeViewRendererOptions, } from '@tiptap/core' import { ref, @@ -52,13 +53,17 @@ export const nodeViewProps = { }, } -export interface VueNodeViewRendererOptions { - stopEvent: ((event: Event) => boolean) | null, - update: ((node: ProseMirrorNode, decorations: Decoration[]) => boolean) | null, - ignoreMutation: ((mutation: MutationRecord | { type: 'selection', target: Element }) => boolean) | null, +export interface VueNodeViewRendererOptions extends NodeViewRendererOptions { + update: ((props: { + oldNode: ProseMirrorNode, + oldDecorations: Decoration[], + newNode: ProseMirrorNode, + newDecorations: Decoration[], + updateProps: () => void, + }) => boolean) | null, } -class VueNodeView extends NodeView { +class VueNodeView extends NodeView { renderer!: VueRenderer @@ -116,8 +121,25 @@ class VueNodeView extends NodeView { } update(node: ProseMirrorNode, decorations: Decoration[]) { + const updateProps = (props?: Record) => { + this.decorationClasses.value = this.getDecorationClasses() + this.renderer.updateProps(props) + } + if (typeof this.options.update === 'function') { - return this.options.update(node, decorations) + const oldNode = this.node + const oldDecorations = this.decorations + + this.node = node + this.decorations = decorations + + return this.options.update({ + oldNode, + oldDecorations, + newNode: node, + newDecorations: decorations, + updateProps, + }) } if (node.type !== this.node.type) { @@ -130,8 +152,8 @@ class VueNodeView extends NodeView { this.node = node this.decorations = decorations - this.decorationClasses.value = this.getDecorationClasses() - this.renderer.updateProps({ node, decorations }) + + updateProps() return true }