diff --git a/demos/src/Examples/InteractivityComponent/React/Component.jsx b/demos/src/Examples/InteractivityComponent/React/Component.jsx new file mode 100644 index 00000000..efe0d9b1 --- /dev/null +++ b/demos/src/Examples/InteractivityComponent/React/Component.jsx @@ -0,0 +1,22 @@ +import React from 'react' +import { NodeViewWrapper } from '@tiptap/react' + +export default props => { + const increase = () => { + props.updateAttributes({ + count: props.node.attrs.count + 1, + }) + } + + return ( + + React Component + +
+ +
+
+ ) +} diff --git a/demos/src/Examples/InteractivityComponent/React/Extension.js b/demos/src/Examples/InteractivityComponent/React/Extension.js new file mode 100644 index 00000000..36d61318 --- /dev/null +++ b/demos/src/Examples/InteractivityComponent/React/Extension.js @@ -0,0 +1,35 @@ +import { Node, mergeAttributes } from '@tiptap/core' +import { ReactNodeViewRenderer } from '@tiptap/react' +import Component from './Component.jsx' + +export default Node.create({ + name: 'reactComponent', + + group: 'block', + + atom: true, + + addAttributes() { + return { + count: { + default: 0, + }, + } + }, + + parseHTML() { + return [ + { + tag: 'react-component', + }, + ] + }, + + renderHTML({ HTMLAttributes }) { + return ['react-component', mergeAttributes(HTMLAttributes)] + }, + + addNodeView() { + return ReactNodeViewRenderer(Component) + }, +}) diff --git a/demos/src/Examples/InteractivityComponent/React/index.html b/demos/src/Examples/InteractivityComponent/React/index.html new file mode 100644 index 00000000..e545403c --- /dev/null +++ b/demos/src/Examples/InteractivityComponent/React/index.html @@ -0,0 +1,15 @@ + + + + + + + +
+ + + diff --git a/demos/src/Examples/InteractivityComponent/React/index.jsx b/demos/src/Examples/InteractivityComponent/React/index.jsx new file mode 100644 index 00000000..394c78fe --- /dev/null +++ b/demos/src/Examples/InteractivityComponent/React/index.jsx @@ -0,0 +1,27 @@ +import React from 'react' +import { useEditor, EditorContent } from '@tiptap/react' +import StarterKit from '@tiptap/starter-kit' +import ReactComponent from './Extension.js' +import './styles.scss' + +export default () => { + const editor = useEditor({ + extensions: [ + StarterKit, + ReactComponent, + ], + content: ` +

+ This is still the text editor you’re used to, but enriched with node views. +

+ +

+ Did you see that? That’s a React component. We are really living in the future. +

+ `, + }) + + return ( + + ) +} diff --git a/demos/src/Examples/InteractivityComponent/React/styles.scss b/demos/src/Examples/InteractivityComponent/React/styles.scss new file mode 100644 index 00000000..93c543c4 --- /dev/null +++ b/demos/src/Examples/InteractivityComponent/React/styles.scss @@ -0,0 +1,33 @@ +/* Basic editor styles */ +.ProseMirror { + > * + * { + margin-top: 0.75em; + } +} + +.react-component { + background: #FAF594; + border: 3px solid #0D0D0D; + border-radius: 0.5rem; + margin: 1rem 0; + position: relative; + + .label { + margin-left: 1rem; + background-color: #0D0D0D; + font-size: 0.6rem; + letter-spacing: 1px; + font-weight: bold; + text-transform: uppercase; + color: #fff; + position: absolute; + top: 0; + padding: 0.25rem 0.75rem; + border-radius: 0 0 0.5rem 0.5rem; + } + + .content { + margin-top: 1.5rem; + padding: 1rem; + } +} diff --git a/demos/src/Examples/InteractivityComponent/Vue/Component.vue b/demos/src/Examples/InteractivityComponent/Vue/Component.vue new file mode 100644 index 00000000..1fe5d072 --- /dev/null +++ b/demos/src/Examples/InteractivityComponent/Vue/Component.vue @@ -0,0 +1,60 @@ + + + + + diff --git a/demos/src/Examples/InteractivityComponent/Vue/Extension.js b/demos/src/Examples/InteractivityComponent/Vue/Extension.js new file mode 100644 index 00000000..a4f786d3 --- /dev/null +++ b/demos/src/Examples/InteractivityComponent/Vue/Extension.js @@ -0,0 +1,35 @@ +import { Node, mergeAttributes } from '@tiptap/core' +import { VueNodeViewRenderer } from '@tiptap/vue-3' +import Component from './Component.vue' + +export default Node.create({ + name: 'vueComponent', + + group: 'block', + + atom: true, + + addAttributes() { + return { + count: { + default: 0, + }, + } + }, + + parseHTML() { + return [ + { + tag: 'vue-component', + }, + ] + }, + + renderHTML({ HTMLAttributes }) { + return ['vue-component', mergeAttributes(HTMLAttributes)] + }, + + addNodeView() { + return VueNodeViewRenderer(Component) + }, +}) diff --git a/demos/src/Examples/InteractivityComponent/Vue/index.html b/demos/src/Examples/InteractivityComponent/Vue/index.html new file mode 100644 index 00000000..00f83f97 --- /dev/null +++ b/demos/src/Examples/InteractivityComponent/Vue/index.html @@ -0,0 +1,15 @@ + + + + + + + +
+ + + diff --git a/demos/src/Examples/InteractivityComponent/Vue/index.vue b/demos/src/Examples/InteractivityComponent/Vue/index.vue new file mode 100644 index 00000000..3e050e2a --- /dev/null +++ b/demos/src/Examples/InteractivityComponent/Vue/index.vue @@ -0,0 +1,52 @@ + + + + + diff --git a/demos/src/Examples/InteractivityComponentContent/React/Component.jsx b/demos/src/Examples/InteractivityComponentContent/React/Component.jsx new file mode 100644 index 00000000..efcd93a1 --- /dev/null +++ b/demos/src/Examples/InteractivityComponentContent/React/Component.jsx @@ -0,0 +1,12 @@ +import React from 'react' +import { NodeViewWrapper, NodeViewContent } from '@tiptap/react' + +export default () => { + return ( + + React Component + + + + ) +} diff --git a/demos/src/Examples/InteractivityComponentContent/React/Extension.js b/demos/src/Examples/InteractivityComponentContent/React/Extension.js new file mode 100644 index 00000000..90a92887 --- /dev/null +++ b/demos/src/Examples/InteractivityComponentContent/React/Extension.js @@ -0,0 +1,27 @@ +import { Node, mergeAttributes } from '@tiptap/core' +import { ReactNodeViewRenderer } from '@tiptap/react' +import Component from './Component.jsx' + +export default Node.create({ + name: 'reactComponent', + + group: 'block', + + content: 'inline*', + + parseHTML() { + return [ + { + tag: 'react-component', + }, + ] + }, + + renderHTML({ HTMLAttributes }) { + return ['react-component', mergeAttributes(HTMLAttributes), 0] + }, + + addNodeView() { + return ReactNodeViewRenderer(Component) + }, +}) diff --git a/demos/src/Examples/InteractivityComponentContent/React/index.html b/demos/src/Examples/InteractivityComponentContent/React/index.html new file mode 100644 index 00000000..ec657ce8 --- /dev/null +++ b/demos/src/Examples/InteractivityComponentContent/React/index.html @@ -0,0 +1,15 @@ + + + + + + + +
+ + + diff --git a/demos/src/Examples/InteractivityComponentContent/React/index.jsx b/demos/src/Examples/InteractivityComponentContent/React/index.jsx new file mode 100644 index 00000000..ac2e67a2 --- /dev/null +++ b/demos/src/Examples/InteractivityComponentContent/React/index.jsx @@ -0,0 +1,29 @@ +import React from 'react' +import { useEditor, EditorContent } from '@tiptap/react' +import StarterKit from '@tiptap/starter-kit' +import ReactComponent from './Extension.js' +import './styles.scss' + +export default () => { + const editor = useEditor({ + extensions: [ + StarterKit, + ReactComponent, + ], + content: ` +

+ This is still the text editor you’re used to, but enriched with node views. +

+ +

This is editable.

+
+

+ Did you see that? That’s a React component. We are really living in the future. +

+ `, + }) + + return ( + + ) +} diff --git a/demos/src/Examples/InteractivityComponentContent/React/styles.scss b/demos/src/Examples/InteractivityComponentContent/React/styles.scss new file mode 100644 index 00000000..c61ccbe7 --- /dev/null +++ b/demos/src/Examples/InteractivityComponentContent/React/styles.scss @@ -0,0 +1,36 @@ +/* Basic editor styles */ +.ProseMirror { + > * + * { + margin-top: 0.75em; + } +} + +.react-component-with-content { + background: #FAF594; + border: 3px solid #0D0D0D; + border-radius: 0.5rem; + margin: 1rem 0; + position: relative; + + .label { + margin-left: 1rem; + background-color: #0D0D0D; + font-size: 0.6rem; + letter-spacing: 1px; + font-weight: bold; + text-transform: uppercase; + color: #fff; + position: absolute; + top: 0; + padding: 0.25rem 0.75rem; + border-radius: 0 0 0.5rem 0.5rem; + } + + .content { + margin: 2.5rem 1rem 1rem; + padding: 0.5rem; + border: 2px dashed #0D0D0D20; + border-radius: 0.5rem; + } +} + diff --git a/demos/src/Examples/InteractivityComponentContent/Vue/Component.vue b/demos/src/Examples/InteractivityComponentContent/Vue/Component.vue new file mode 100644 index 00000000..89c606ef --- /dev/null +++ b/demos/src/Examples/InteractivityComponentContent/Vue/Component.vue @@ -0,0 +1,50 @@ + + + + + diff --git a/demos/src/Examples/InteractivityComponentContent/Vue/Extension.js b/demos/src/Examples/InteractivityComponentContent/Vue/Extension.js new file mode 100644 index 00000000..89a991ec --- /dev/null +++ b/demos/src/Examples/InteractivityComponentContent/Vue/Extension.js @@ -0,0 +1,27 @@ +import { Node, mergeAttributes } from '@tiptap/core' +import { VueNodeViewRenderer } from '@tiptap/vue-3' +import Component from './Component.vue' + +export default Node.create({ + name: 'vueComponent', + + group: 'block', + + content: 'inline*', + + parseHTML() { + return [ + { + tag: 'vue-component', + }, + ] + }, + + renderHTML({ HTMLAttributes }) { + return ['vue-component', mergeAttributes(HTMLAttributes), 0] + }, + + addNodeView() { + return VueNodeViewRenderer(Component) + }, +}) diff --git a/demos/src/Examples/InteractivityComponentContent/Vue/index.html b/demos/src/Examples/InteractivityComponentContent/Vue/index.html new file mode 100644 index 00000000..bcfbeb3f --- /dev/null +++ b/demos/src/Examples/InteractivityComponentContent/Vue/index.html @@ -0,0 +1,15 @@ + + + + + + + +
+ + + diff --git a/demos/src/Examples/InteractivityComponentContent/Vue/index.vue b/demos/src/Examples/InteractivityComponentContent/Vue/index.vue new file mode 100644 index 00000000..6cc6bcb8 --- /dev/null +++ b/demos/src/Examples/InteractivityComponentContent/Vue/index.vue @@ -0,0 +1,54 @@ + + + + + diff --git a/docs/examples/interactivity.md b/docs/examples/interactivity.md index 9e7d71e2..219a8d95 100644 --- a/docs/examples/interactivity.md +++ b/docs/examples/interactivity.md @@ -2,6 +2,6 @@ Thanks to [node views](/guide/node-views) you can add interactivity to your nodes. If you can write it in JavaScript, you can add it to the editor. -https://embed.tiptap.dev/preview/GuideNodeViews/VueComponent +https://embed.tiptap.dev/preview/Examples/InteractivityComponent -https://embed.tiptap.dev/preview/GuideNodeViews/VueComponentContent +https://embed.tiptap.dev/preview/Examples/InteractivityComponentContent