diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 912ca6ca..62301594 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -27,7 +27,7 @@ jobs: - uses: actions/checkout@v2.3.4 - name: Use Node.js ${{ matrix.node-version }} - uses: actions/setup-node@v2.1.2 + uses: actions/setup-node@v2.1.3 with: node-version: ${{ matrix.node-version }} @@ -83,7 +83,7 @@ jobs: - uses: actions/checkout@v2.3.4 - name: Use Node.js ${{ matrix.node-version }} - uses: actions/setup-node@v2.1.2 + uses: actions/setup-node@v2.1.3 with: node-version: ${{ matrix.node-version }} @@ -138,7 +138,7 @@ jobs: - uses: actions/checkout@v2.3.4 - name: Use Node.js ${{ matrix.node-version }} - uses: actions/setup-node@v2.1.2 + uses: actions/setup-node@v2.1.3 with: node-version: ${{ matrix.node-version }} diff --git a/docs/src/demos/Examples/Drawing/Component.vue b/docs/src/demos/Examples/Drawing/Component.vue index 9fed1988..3d426471 100644 --- a/docs/src/demos/Examples/Drawing/Component.vue +++ b/docs/src/demos/Examples/Drawing/Component.vue @@ -31,6 +31,8 @@ import * as d3 from 'd3' import simplify from 'simplify-js' export default { + name: 'Paper', + props: { updateAttributes: { type: Function, diff --git a/docs/src/docPages/guide/node-views.md b/docs/src/docPages/guide/node-views.md index f4d864a6..130cd64d 100644 --- a/docs/src/docPages/guide/node-views.md +++ b/docs/src/docPages/guide/node-views.md @@ -6,6 +6,26 @@ TODO +```js +import { Node } from '@tiptap/core' +import { VueRenderer } from '@tiptap/vue' +import Component from './Component.vue' + +export default Node.create({ + addNodeView() { + return ({ editor, node, getPos, HTMLAttributes, decorations, extension }) => { + const dom = document.createElement('div') + + dom.innerHTML = 'I’m a node view' + + return { + dom, + } + }) + }, +}) +``` + ## Different types of node views ### Simple @@ -53,3 +73,60 @@ TODO ## Render Vue components +### Node + +```js +import { Node } from '@tiptap/core' +import { VueRenderer } from '@tiptap/vue' +import Component from './Component.vue' + +export default Node.create({ + addNodeView() { + return VueRenderer(Component) + }, +}) +``` + +### Component + +```html + + + +``` diff --git a/package.json b/package.json index f958b674..447f1dce 100644 --- a/package.json +++ b/package.json @@ -33,8 +33,8 @@ "@lerna/filter-packages": "^3.18.0", "@lerna/project": "^3.21.0", "@rollup/plugin-babel": "^5.2.1", - "@rollup/plugin-commonjs": "^16.0.0", - "@rollup/plugin-node-resolve": "^10.0.0", + "@rollup/plugin-commonjs": "^17.0.0", + "@rollup/plugin-node-resolve": "^11.0.0", "@typescript-eslint/eslint-plugin": "^4.9.1", "@typescript-eslint/parser": "^4.9.1", "cypress": "^6.1.0", diff --git a/packages/core/src/Editor.ts b/packages/core/src/Editor.ts index 7a1fe8f7..afda2259 100644 --- a/packages/core/src/Editor.ts +++ b/packages/core/src/Editor.ts @@ -252,9 +252,7 @@ export class Editor extends EventEmitter { this.view.updateState(newState) - this.view.setProps({ - nodeViews: this.extensionManager.nodeViews, - }) + this.createNodeViews() // Let’s store the editor instance in the DOM element. // So we’ll have access to it for tests. @@ -262,6 +260,15 @@ export class Editor extends EventEmitter { dom.editor = this.proxy } + /** + * Creates all node views. + */ + public createNodeViews() { + this.view.setProps({ + nodeViews: this.extensionManager.nodeViews, + }) + } + /** * Creates a ProseMirror document. */ diff --git a/packages/core/src/types.ts b/packages/core/src/types.ts index ff36dcdf..534735a7 100644 --- a/packages/core/src/types.ts +++ b/packages/core/src/types.ts @@ -92,8 +92,8 @@ export type NodeViewRendererProps = { editor: Editor, node: ProseMirrorNode, getPos: (() => number) | boolean, - decorations: Decoration[], HTMLAttributes: { [key: string]: any }, + decorations: Decoration[], extension: Node, } diff --git a/packages/vue/src/VueRenderer.ts b/packages/vue/src/VueRenderer.ts index 3212f62c..f5cd34ba 100644 --- a/packages/vue/src/VueRenderer.ts +++ b/packages/vue/src/VueRenderer.ts @@ -5,6 +5,12 @@ import { Node as ProseMirrorNode } from 'prosemirror-model' import Vue from 'vue' import { VueConstructor } from 'vue/types/umd' +function getComponentFromElement(element: HTMLElement): Vue { + // @ts-ignore + // eslint-disable-next-line + return element.__vue__ +} + interface VueRendererOptions { stopEvent: ((event: Event) => boolean) | null, update: ((node: ProseMirrorNode, decorations: Decoration[]) => boolean) | null, @@ -132,7 +138,7 @@ class VueNodeView implements NodeView { }, }) - const props = { + const propsData = { NodeViewWrapper, NodeViewContent, editor: this.editor, @@ -144,10 +150,13 @@ class VueNodeView implements NodeView { updateAttributes: (attributes = {}) => this.updateAttributes(attributes), } + const parent = this.editor.view.dom.parentElement + ? getComponentFromElement(this.editor.view.dom.parentElement) + : undefined + this.vm = new Component({ - // TODO: get parent component - // parent: this.parent, - propsData: props, + parent, + propsData, }).$mount() } @@ -258,11 +267,17 @@ class VueNodeView implements NodeView { return } + // prevents `Avoid mutating a prop directly` error message + const originalSilent = Vue.config.silent + Vue.config.silent = true + Object .entries(data) .forEach(([key, value]) => { this.vm.$props[key] = value }) + + Vue.config.silent = originalSilent } updateAttributes(attributes: {}) { @@ -295,5 +310,18 @@ class VueNodeView implements NodeView { } export default function VueRenderer(component: Vue | VueConstructor, options?: Partial) { - return (props: NodeViewRendererProps) => new VueNodeView(component, props, options) as NodeView + return (props: NodeViewRendererProps) => { + // try to get the parent component + // this is important for vue devtools to show the component hierarchy correctly + // maybe it’s `undefined` because isn’t rendered yet + const parent = props.editor.view.dom.parentElement + ? getComponentFromElement(props.editor.view.dom.parentElement) + : undefined + + if (!parent) { + return undefined + } + + return new VueNodeView(component, props, options) as NodeView + } } diff --git a/packages/vue/src/components/EditorContent.ts b/packages/vue/src/components/EditorContent.ts index 2f74d5ff..1bfa76ab 100644 --- a/packages/vue/src/components/EditorContent.ts +++ b/packages/vue/src/components/EditorContent.ts @@ -17,7 +17,7 @@ export default Vue.extend({ if (editor && editor.options.element) { this.$nextTick(() => { this.$el.appendChild(editor.options.element.firstChild) - // editor.setParentComponent(this) + editor.createNodeViews() }) } }, diff --git a/yarn.lock b/yarn.lock index 97e4563f..25b1693c 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2213,10 +2213,10 @@ "@babel/helper-module-imports" "^7.10.4" "@rollup/pluginutils" "^3.1.0" -"@rollup/plugin-commonjs@^16.0.0": - version "16.0.0" - resolved "https://registry.yarnpkg.com/@rollup/plugin-commonjs/-/plugin-commonjs-16.0.0.tgz#169004d56cd0f0a1d0f35915d31a036b0efe281f" - integrity sha512-LuNyypCP3msCGVQJ7ki8PqYdpjfEkE/xtFa5DqlF+7IBD0JsfMZ87C58heSwIMint58sAUZbt3ITqOmdQv/dXw== +"@rollup/plugin-commonjs@^17.0.0": + version "17.0.0" + resolved "https://registry.yarnpkg.com/@rollup/plugin-commonjs/-/plugin-commonjs-17.0.0.tgz#2ae2228354cf0fbba6cf9f06f30b2c66a974324c" + integrity sha512-/omBIJG1nHQc+bgkYDuLpb/V08QyutP9amOrJRUSlYJZP+b/68gM//D8sxJe3Yry2QnYIr3QjR3x4AlxJEN3GA== dependencies: "@rollup/pluginutils" "^3.1.0" commondir "^1.0.1" @@ -2226,17 +2226,17 @@ magic-string "^0.25.7" resolve "^1.17.0" -"@rollup/plugin-node-resolve@^10.0.0": - version "10.0.0" - resolved "https://registry.yarnpkg.com/@rollup/plugin-node-resolve/-/plugin-node-resolve-10.0.0.tgz#44064a2b98df7530e66acf8941ff262fc9b4ead8" - integrity sha512-sNijGta8fqzwA1VwUEtTvWCx2E7qC70NMsDh4ZG13byAXYigBNZMxALhKUSycBks5gupJdq0lFrKumFrRZ8H3A== +"@rollup/plugin-node-resolve@^11.0.0": + version "11.0.0" + resolved "https://registry.yarnpkg.com/@rollup/plugin-node-resolve/-/plugin-node-resolve-11.0.0.tgz#770458fb26691a686c5f29f37dded94832ffce59" + integrity sha512-8Hrmwjn1pLYjUxcv7U7IPP0qfnzEJWHyHE6CaZ8jbLM+8axaarJRB1jB6JgKTDp5gNga+TpsgX6F8iuvgOerKQ== dependencies: "@rollup/pluginutils" "^3.1.0" "@types/resolve" "1.17.1" builtin-modules "^3.1.0" deepmerge "^4.2.2" is-module "^1.0.0" - resolve "^1.17.0" + resolve "^1.19.0" "@rollup/pluginutils@^3.0.9", "@rollup/pluginutils@^3.1.0": version "3.1.0" @@ -11741,9 +11741,9 @@ prosemirror-keymap@^1.0.0, prosemirror-keymap@^1.1.2, prosemirror-keymap@^1.1.3: w3c-keyname "^2.2.0" prosemirror-model@^1.0.0, prosemirror-model@^1.1.0, prosemirror-model@^1.12.0, prosemirror-model@^1.8.1: - version "1.12.0" - resolved "https://registry.yarnpkg.com/prosemirror-model/-/prosemirror-model-1.12.0.tgz#deb6acbce5c62ea35ef3d59c7d4c54f65d6d9fba" - integrity sha512-B5syrXluQwEPfih8PqZcVg2VWRUf8Rj97K/VNSkQtjUPL1BCoTUgdLERIlxdWHkwqvujFsT3Pw5ubc4/ofF1jQ== + version "1.13.0" + resolved "https://registry.yarnpkg.com/prosemirror-model/-/prosemirror-model-1.13.0.tgz#ce5574669489fb4acfd875f7bbfa11f3a88319a2" + integrity sha512-j5F0Wt5Me8a1qKI6xNRNET6l07tWTpXwxfcs7xTl5PWAxJGgAC+vHIuwenGZMWSE6kU2k4qr55pw5aFXlUfgVA== dependencies: orderedmap "^1.1.0" @@ -11787,9 +11787,9 @@ prosemirror-utils@^1.0.0-0: integrity sha512-11hTMG4Qwqlux6Vwp/4m16mLDg6IwWb0/odsWXGtWvvRJo61SfG0RGYlA8H72vExmbnWpiXa7PNenZ6t12Rkqw== prosemirror-view@^1.0.0, prosemirror-view@^1.1.0, prosemirror-view@^1.13.3, prosemirror-view@^1.16.3: - version "1.16.4" - resolved "https://registry.yarnpkg.com/prosemirror-view/-/prosemirror-view-1.16.4.tgz#f7034c52566338f6872874a56b02fcb234adb8a5" - integrity sha512-2loIeZhHDWSNn4fxriiq4oizp5D2GzvVZwkuEKNSEPV5OHh4Pl3JS6R5OV4UwSvh8UbpPsdNfsrcvSheMJPAQg== + version "1.16.5" + resolved "https://registry.yarnpkg.com/prosemirror-view/-/prosemirror-view-1.16.5.tgz#1a4646832e16c1cf116b54b9becf4b0663821125" + integrity sha512-cFEjzhqQZIRDALEgQt8CNn+Qb+BUOvNxxaljaWoCbAYlsWGMiNNQG06I1MwbRNDcwnZKeFmOGpLEB4eorYYGig== dependencies: prosemirror-model "^1.1.0" prosemirror-state "^1.0.0" @@ -12620,7 +12620,7 @@ resolve@1.17.0: dependencies: path-parse "^1.0.6" -resolve@^1.1.6, resolve@^1.10.0, resolve@^1.13.1, resolve@^1.17.0, resolve@^1.2.0, resolve@^1.3.2, resolve@^1.8.1: +resolve@^1.1.6, resolve@^1.10.0, resolve@^1.13.1, resolve@^1.17.0, resolve@^1.19.0, resolve@^1.2.0, resolve@^1.3.2, resolve@^1.8.1: version "1.19.0" resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.19.0.tgz#1af5bf630409734a067cae29318aac7fa29a267c" integrity sha512-rArEXAgsBG4UgRGcynxWIWKFvh/XZCcS8UJdHhwy91zwAvCZIbcs+vAbflgBnNjYMs/i/i+/Ux6IZhML1yPvxg== @@ -14227,9 +14227,9 @@ trough@^1.0.0: glob "^7.1.2" ts-loader@^8.0.11: - version "8.0.11" - resolved "https://registry.yarnpkg.com/ts-loader/-/ts-loader-8.0.11.tgz#35d58a65932caacb120426eea59eca841786c899" - integrity sha512-06X+mWA2JXoXJHYAesUUL4mHFYhnmyoCdQVMXofXF552Lzd4wNwSGg7unJpttqUP7ziaruM8d7u8LUB6I1sgzA== + version "8.0.12" + resolved "https://registry.yarnpkg.com/ts-loader/-/ts-loader-8.0.12.tgz#1de9f1de65176318c1e6d187bfc496182f8dc2a0" + integrity sha512-UIivVfGVJDdwwjgSrbtcL9Nf10c1BWnL1mxAQUVcnhNIn/P9W3nP5v60Z0aBMtc7ZrE11lMmU6+5jSgAXmGaYw== dependencies: chalk "^2.3.0" enhanced-resolve "^4.0.0"