From fa0e1222dd3ab8046ed37e59428dc2c4ca6bef64 Mon Sep 17 00:00:00 2001 From: Hans Pagel Date: Tue, 23 Feb 2021 21:05:07 +0100 Subject: [PATCH 1/8] whitespace --- .github/workflows/build.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index a28e89f1..97d11803 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -96,7 +96,7 @@ jobs: wait-on: 'http://localhost:3000' project: ./tests browser: chrome - quiet: true + quiet: true - name: Export screenshots (on failure only) uses: actions/upload-artifact@v2.2.2 From 432ff517536456169d3aee995faa06636770a208 Mon Sep 17 00:00:00 2001 From: Hans Pagel Date: Tue, 23 Feb 2021 21:05:30 +0100 Subject: [PATCH 2/8] docs: update content, fix #171 --- docs/src/docPages/examples/tables.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/src/docPages/examples/tables.md b/docs/src/docPages/examples/tables.md index 491020cf..897339d1 100644 --- a/docs/src/docPages/examples/tables.md +++ b/docs/src/docPages/examples/tables.md @@ -1,7 +1,7 @@ # Tables :::pro Commercial use -Using the collaborative editing commercially? [Become a sponsor](/sponsor) to fund its development! +Using this extension in a commercial project? [Become a sponsor](/sponsor) to fund its development! ::: From bc88c601b39c806fdfe847279e7b19f09e225e72 Mon Sep 17 00:00:00 2001 From: Hans Pagel Date: Wed, 24 Feb 2021 12:02:52 +0100 Subject: [PATCH 3/8] docs: update the collaborative editing guide --- .../examples/collaborative-editing.md | 15 +- .../docPages/guide/collaborative-editing.md | 171 +++++++++--------- 2 files changed, 93 insertions(+), 93 deletions(-) diff --git a/docs/src/docPages/examples/collaborative-editing.md b/docs/src/docPages/examples/collaborative-editing.md index 2a7d23b0..da070d6c 100644 --- a/docs/src/docPages/examples/collaborative-editing.md +++ b/docs/src/docPages/examples/collaborative-editing.md @@ -21,16 +21,21 @@ Be nice! The content of this editor is shared with other users from the Internet ## Backend In case you’re wondering what kind of sorcery you need on the server to achieve this, here is the whole backend code for the demo: +:::warning Request early access +Our plug & play collaboration backend hocuspocus is still work in progress. If you want to give it a try, [request early access](https://hocuspocus.dev/). +::: + ```js import { Server } from '@hocuspocus/server' -import { LevelDB } from '@hocuspocus/leveldb' +import { RocksDB } from '@hocuspocus/rocksdb' const server = Server.configure({ port: 80, - - persistence: new LevelDB({ - path: './database', - }), + extensions: [ + new RocksDB({ + path: './database', + }) + ], }) server.listen() diff --git a/docs/src/docPages/guide/collaborative-editing.md b/docs/src/docPages/guide/collaborative-editing.md index 1c3c7f05..6d647cd3 100644 --- a/docs/src/docPages/guide/collaborative-editing.md +++ b/docs/src/docPages/guide/collaborative-editing.md @@ -1,9 +1,5 @@ # Collaborative editing -:::pro Commercial use -Using the collaborative editing commercially? [Become a sponsor](/sponsor) to fund its development! -::: - ## toc ## Introduction @@ -12,12 +8,14 @@ Real-time collaboration, syncing between different devices and working offline u ## Configure the editor The underyling schema tiptap uses is an excellent foundation to sync documents. With the [`Collaboration`](/api/extensions/collaboration) you can tell tiptap to track changes to the document with [Y.js](https://github.com/yjs/yjs). -Y.js is a conflict-free replicated data types implementation, or in other words: It’s reaaally good in merging changes. And to achieve that, changes don’t have to come in order. It’s totally fine to change a document while being offline and merge the it with other changes when the device is online again. +Y.js is a conflict-free replicated data types implementation, or in other words: It’s reaaally good in merging changes. And to achieve that, changes don’t have to come in order. It’s totally fine to change a document while being offline and merge it with other changes when the device is online again. -But somehow, the clients need to interchange document modifications. The most technologies used to do that are WebRTC and WebSocket, so let’s have a look those: +But somehow, all clients need to interchange document modifications at some point. The most popular technologies to do that are [WebRTC](https://developer.mozilla.org/en-US/docs/Web/API/WebRTC_API) and [WebSockets](https://developer.mozilla.org/de/docs/Web/API/WebSocket), so let’s have a closer look at those: ### WebRTC -Anyway, let’s take the first steps. Install the dependencies: +WebRTC uses a server only to connect clients with each other. The actual data is then flowing between the clients, without the server knowing anything about it and that’s great to take the first steps with collaborative editing. + +First, install the dependencies: ```bash # with npm @@ -53,16 +51,15 @@ const editor = new Editor({ This should be enough to create a collaborative instance of tiptap. Crazy, isn’t it? Try it out, and open the editor in two different browsers. Changes should be synced between different windows. -So how does this magic work? All clients need to connect with eachother, that’s the job of providers. The [WebRTC](https://developer.mozilla.org/en-US/docs/Web/API/WebRTC_API) provider is the easiest way to get started with, as it requires a public server to connect clients directly with-each other, but not to sync the actual changes. This has two downsides, though. +So how does this magic work? All clients need to connect with eachother, that’s the job of a *provider*. The [WebRTC provider](https://github.com/yjs/y-webrtc) is the easiest way to get started with, as it requires a public server to connect clients directly with each other, but not to sync the actual changes. This has two downsides, though. -On the one hand, browsers refuse to connect with too many clients. With Y.js it’s enough if all clients are connected indirectly, but even that isn’t possible at some point. Or in other words, it doesn’t scale well for more than 100+ clients in the same document. - -On the other hand, it’s likely you want to involve a server to persist changes anyway. But the WebRTC signaling server (which connects all clients with eachother) doesn’t receive the changes and therefore doesn’t know what’s in the document. +1. Browsers refuse to connect with too many clients. With Y.js it’s enough if all clients are connected indirectly, but even that isn’t possible at some point. Or in other words, it doesn’t scale well for more than 100+ clients in the same document. +2. It’s likely you want to involve a server to persist changes anyway. But the WebRTC signaling server (which connects all clients with eachother) doesn’t receive the changes and therefore doesn’t know what’s in the document. Anyway, if you want to dive deeper, head over to [the Y WebRTC repository](https://github.com/yjs/y-webrtc) on GitHub. ### WebSocket (Recommended) -For most uses cases, the WebSocket provider is the recommended choice. It’s very flexible and can scale very well. For the client, the example is nearly the same, only the provider is different. Install the dependencies first: +For most uses cases, the WebSocket provider is the recommended choice. It’s very flexible and can scale very well. For the client, the example is nearly the same, only the provider is different. First, let’s install the dependencies: ```bash # with npm @@ -96,9 +93,12 @@ const editor = new Editor({ }) ``` -That example doesn’t work out of the box. As you can see, it’s configured to talk to a WebSocket server which is available under `ws://127.0.0.1:1234` (WebSocket protocol, your local IP and port 1234). You need to set this up, too. +That example doesn’t work out of the box. As you can see, it’s configured to talk to a WebSocket server which is available under `ws://127.0.0.1:1234` (WebSocket protocol `ws://`, your local IP `127.0.0.1` and the port `1234`). You need to set this up, too. -To make the server part as easy as possible, we provide you with an opinionated server package, called hocuspocus (NOT PUBLISHED YET). Create a new project, and install the hocuspocus server as a dependency: +#### The WebSocket backend +To make the server part as easy as possible, we provide [an opinionated server package, called hocuspocus](http://hocuspocus.dev/) (not published yet). Let’s go through, how this will work once its released. + +Create a new project, and install the hocuspocus server as a dependency: ```bash # with npm @@ -126,10 +126,13 @@ That’s all. Start the script with: node ./index.js ``` -This should output something like “Listening on ws://127.0.0.1:1234”. If you go back to your tiptap editor and hit reload, it should connect to the WebSocket server and changes should sync with all other clients. Amazing, isn’t it? + +Try opening http://127.0.0.1:1234 in your browser. You should see a plain text `OK` if everything works fine. + +Go back to your tiptap editor and hit reload, it should now connect to the WebSocket server and changes should sync with all other clients. Amazing, isn’t it? ### Multiple network providers -You can even combine multiple providers. That’s not needed, but could keep clients connected, even if one connection - for example the websocket server - goes down for a while. Here is an example: +You can even combine multiple providers. That’s not needed, but could keep clients connected, even if one connection - for example the WebSocket server - goes down for a while. Here is an example: ```js new WebrtcProvider('example-document', ydoc) @@ -138,10 +141,10 @@ new WebsocketProvider('ws://127.0.0.1:1234', 'example-document', ydoc) Yes, that’s all. -Keep in mind that WebRTC needs a signaling server to connect clients. This signaling server doesn’t receive the synced data, but helps to let clients find each other. You can [run your own signaling server](https://github.com/yjs/y-webrtc#signaling), if you like. +Keep in mind that WebRTC needs a signaling server to connect clients. This signaling server doesn’t receive the synced data, but helps to let clients find each other. You can [run your own signaling server](https://github.com/yjs/y-webrtc#signaling), if you like. Otherwise it’s using a default URL baked into the package. ### Show other cursors -If you want to enable users to see the cursor and text selections of each other, add the [`CollaborationCursor`](/api/extensions/collaboration-cursor) extension. +To enable users to see the cursor and text selections of each other, add the [`CollaborationCursor`](/api/extensions/collaboration-cursor) extension. ```js import { Editor } from '@tiptap/core' @@ -209,12 +212,11 @@ All changes will be stored in the browser then, even if you close the tab, go of Yes, it’s magic. As already mentioned, that is all based on the fantastic Y.js framework. And if you’re using it, or our integration, you should definitely [sponsor Kevin Jahns on GitHub](https://github.com/dmonad), he is the brain behind Y.js. -## A plug & play backend -Our collaborative editing backend is ready to handle advanced use cases, like authorization, persistence and scaling. Let’s go through a few common use cases here! +## Our plug & play collaboration backend +Our collaborative editing backend handles the syncing, authorization, persistence and scaling. Let’s go through a few common use cases here! -### Where is it? -:::warning Work in progress -Our plug & play collaboration backend hocuspocus is still work in progress. If you want to give it a try, send us an email to [humans@tiptap.dev](mailto:humans@tiptap.dev) to receive early access. +:::warning Request early access +Our plug & play collaboration backend hocuspocus is still work in progress. If you want to give it a try, [request early access](https://hocuspocus.dev/). ::: ### The document name @@ -259,20 +261,15 @@ import { Server } from '@hocuspocus/server' const server = Server.configure({ onConnect(data, resolve, reject) { - const { requestHeaders, requestParameters } = data - // Your code here, for example a request to an API - - // If the user is not authenticated … - if (requestParameters.access_token !== 'super-secret-token') { - return reject() - } - - // Set contextual data + // You can set contextual data… const context = { - user_id: 1234, + user: { + id: 1234, + name: 'John', + }, } - // If the user is authenticated … + // …and pass it along to use it in other hooks resolve(context) }, }) @@ -281,31 +278,21 @@ server.listen() ``` ### Authorization -With the `onJoinDocument` hook you can check if a user is authorized to edit the current document. This works in the same way the [Authentication](#authentication) works. +With the `onConnect` hook you can check if a user is authorized to edit the current document. This works in the same way the [Authentication](#authentication) works. ```js import { Server } from '@hocuspocus/server' const server = Server.configure({ - onJoinDocument(data, resolve, reject) { - const { - clientsCount, - context, - document, - documentName, - requestHeaders, - requestParameters, - } = data - // Your code here, for example a request to an API + onConnect(data, resolve, reject) { + const { requestParameters } = data - // Access the contextual data from the onConnect hook, in this example this will print { user_id: 1234 } - console.log(context) + // Example: Check if a user is authenticated using a request parameter + if (requestParameters.access_token !== 'super-secret-token') { + return reject() + } - // If the user is authorized … resolve() - - // if the user isn’t authorized … - reject() }, }) @@ -313,53 +300,57 @@ server.listen() ``` ### Persist the document -By default, documents are only stored in the memory. Hence they are deleted when the WebSocket server is stopped. To prevent this, store changes on the hard disk with the LevelDB adapter. When you restart the server, it’ll restore documents from the hard disk, in that case from the `./database` folder: +By default, documents are only stored in the memory. Hence they are deleted when the WebSocket server is stopped. To prevent this, store changes on the hard disk with the RocksDB adapter. When you restart the server, it’ll restore documents from the hard disk, in that case from the `./database` folder: ```js import { Server } from '@hocuspocus/server' -import { LevelDB } from '@hocuspocus/leveldb' +import { RocksDB } from '@hocuspocus/rocksdb' const server = Server.configure({ - persistence: new LevelDB({ - path: './database', - }), + + extensions: [ + new RocksDB({ + // Store the actual data in that folder: + path: './database', + }) + ], + }) server.listen() ``` -### Send it to an API -To pass the updated documents to an API, or to a database, you can use the `onChange` hook, which is executed when a document changes. With the `debounce` setting you can slow down the execution, with the `debounceMaxWait` setting you can make sure the content is sent at least every few seconds: +### Store the documents as JSON +To pass the updated documents to an API, to a database, or store on it on the hard disk as JSON, you can use the `onChange` hook, which is executed when a document changes. ```js +import { writeFile } from 'fs' import { Server } from '@hocuspocus/server' +import { yDocToProsemirrorJSON } from 'y-prosemirror' -const server = Server.configure({ - // time to wait before sending changes (in milliseconds) - debounce: 2000, - - // maximum time to wait (in milliseconds) - debounceMaxWait: 10000, - - // executed when the document is changed +const hocuspocus = Server.configure({ onChange(data) { - const { - clientsCount, - document, - documentName, - requestHeaders, - requestParameters, - } = data + const save = () => { + // Get the underlying Y Document + const ydoc = data.document - // Your code here, for example a request to an API + // Convert the Y Document to the format your editor uses, in this + // example Prosemirror JSON for the tiptap editor + const prosemirrorDocument = yDocToProsemirrorJSON(ydoc, 'default') + + // Save your document. In a real-world app this could be a database query + // a webhook or something else + writeFile( + `/path/to/your/documents/${data.documentName}.json`, + prosemirrorDocument + ) + } }, }) -server.listen() +hocuspocus.listen() ``` -There is no method to restore documents from an external source, so you’ll need a [persistence driver](#persist-the-document) though. Those persistence drivers store every change to the document. That’s probably not needed in your external source, but is needed to make the merging of changes conflict-free in the collaborative editing backend. - ### Scale with Redis (Advanced) :::warning Keep in mind @@ -373,10 +364,12 @@ import { Server } from '@hocuspocus/server' import { Redis } from '@hocuspocus/redis' const server = Server.configure({ - persistence: new Redis({ - host: '127.0.0.1', - port: 6379, - }), + extensions: [ + new Redis({ + host: '127.0.0.1', + port: 6379, + }) + ], }) server.listen() @@ -389,13 +382,15 @@ import { Server } from '@hocuspocus/server' import { RedisCluster } from '@hocuspocus/redis' const server = Server.configure({ - persistence: new RedisCluster({ - scaleReads: 'all', - redisOptions: { - host: '127.0.0.1', - port: 6379, - } - }), + extensions: [ + new RedisCluster({ + scaleReads: 'all', + redisOptions: { + host: '127.0.0.1', + port: 6379, + }, + }) + ], }) server.listen() From 38952fb8c94adf4b70fc1b04b3f2ed73703643e9 Mon Sep 17 00:00:00 2001 From: Hans Pagel Date: Wed, 24 Feb 2021 21:08:54 +0100 Subject: [PATCH 4/8] docs: fix broken link --- docs/src/docPages/api/schema.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/src/docPages/api/schema.md b/docs/src/docPages/api/schema.md index cc91da3f..5980c28e 100644 --- a/docs/src/docPages/api/schema.md +++ b/docs/src/docPages/api/schema.md @@ -231,7 +231,7 @@ Node.create({ ``` #### Table roles -The [`Table`](/api/extensions/table) extension registers a new schema attribute to configure which role an Node has. Allowed values are `table`, `row`, `cell`, and `header_cell`. +The [`Table`](/api/nodes/table) extension registers a new schema attribute to configure which role an Node has. Allowed values are `table`, `row`, `cell`, and `header_cell`. ```js Node.create({ From bf5fb2ad3c2b4458e5fcfde03a5b9828cd45391f Mon Sep 17 00:00:00 2001 From: Hans Pagel Date: Fri, 26 Feb 2021 02:00:35 +0100 Subject: [PATCH 5/8] move React components to a package, translate default editor example to React --- .../demos/Examples/Default/React/index.jsx | 167 ++++++++++++++++++ .../demos/Examples/Default/React/styles.scss | 55 ++++++ .../Examples/Default/{ => Vue}/index.spec.js | 4 +- .../Examples/Default/{ => Vue}/index.vue | 0 docs/src/demos/React/index.jsx | 2 +- docs/src/docPages/examples/default.md | 6 +- packages/react/README.md | 14 ++ packages/react/package.json | 32 ++++ packages/react/src/ReactNodeViewRenderer.ts | 1 + packages/react/src/ReactRenderer.ts | 1 + packages/react/src/components/Editor.jsx | 34 ++++ packages/react/src/index.ts | 7 + 12 files changed, 319 insertions(+), 4 deletions(-) create mode 100644 docs/src/demos/Examples/Default/React/index.jsx create mode 100644 docs/src/demos/Examples/Default/React/styles.scss rename docs/src/demos/Examples/Default/{ => Vue}/index.spec.js (86%) rename docs/src/demos/Examples/Default/{ => Vue}/index.vue (100%) create mode 100644 packages/react/README.md create mode 100644 packages/react/package.json create mode 100644 packages/react/src/ReactNodeViewRenderer.ts create mode 100644 packages/react/src/ReactRenderer.ts create mode 100644 packages/react/src/components/Editor.jsx create mode 100644 packages/react/src/index.ts diff --git a/docs/src/demos/Examples/Default/React/index.jsx b/docs/src/demos/Examples/Default/React/index.jsx new file mode 100644 index 00000000..8fb12656 --- /dev/null +++ b/docs/src/demos/Examples/Default/React/index.jsx @@ -0,0 +1,167 @@ +import React, { useState } from 'react' +import { defaultExtensions } from '@tiptap/starter-kit' +import { useEditor, Editor } from '@tiptap/react' +import './styles.scss' + +// useEditor only works for child components of +const MenuBar = () => { + const editor = useEditor() + + return ( + <> + + + + + + + + + + + + + + + + + + + + + + + ) +} + +export default () => { + const [value, setValue] = useState(` +

+ Hi there, +

+

+ this is a basic basic example of tiptap. Sure, there are all kind of basic text styles you’d probably expect from a text editor. But wait until you see the lists: +

+
    +
  • + That’s a bullet list with one … +
  • +
  • + … or two list items. +
  • +
+

+ Isn’t that great? And all of that is editable. But wait, there’s more. Let’s try a code block: +

+
body {
+  display: none;
+}
+

+ I know, I know, this is impressive. It’s only the tip of the iceberg though. Give it a try and click a little bit around. Don’t forget to check the other examples too. +

+
+ Wow, that’s amazing. Good work, boy! 👏 +
+ — Mom +
+`) + + return ( + <> + + + + + ) +} diff --git a/docs/src/demos/Examples/Default/React/styles.scss b/docs/src/demos/Examples/Default/React/styles.scss new file mode 100644 index 00000000..dc996245 --- /dev/null +++ b/docs/src/demos/Examples/Default/React/styles.scss @@ -0,0 +1,55 @@ +/* Basic editor styles */ +.ProseMirror { + > * + * { + margin-top: 0.75em; + } + + ul, + ol { + padding: 0 1rem; + } + + h1, + h2, + h3, + h4, + h5, + h6 { + line-height: 1.1; + } + + code { + background-color: rgba(#616161, 0.1); + color: #616161; + } + + pre { + background: #0D0D0D; + color: #FFF; + font-family: 'JetBrainsMono', monospace; + padding: 0.75rem 1rem; + border-radius: 0.5rem; + + code { + color: inherit; + background: none; + font-size: 0.8rem; + } + } + + img { + max-width: 100%; + height: auto; + } + + blockquote { + padding-left: 1rem; + border-left: 2px solid rgba(#0D0D0D, 0.1); + } + + hr { + border: none; + border-top: 2px solid rgba(#0D0D0D, 0.1); + margin: 2rem 0; + } +} diff --git a/docs/src/demos/Examples/Default/index.spec.js b/docs/src/demos/Examples/Default/Vue/index.spec.js similarity index 86% rename from docs/src/demos/Examples/Default/index.spec.js rename to docs/src/demos/Examples/Default/Vue/index.spec.js index 33a3d128..8580f40a 100644 --- a/docs/src/demos/Examples/Default/index.spec.js +++ b/docs/src/demos/Examples/Default/Vue/index.spec.js @@ -1,6 +1,6 @@ -context('/demos/Examples/Default', () => { +context('/demos/Examples/Default/Vue', () => { before(() => { - cy.visit('/demos/Examples/Default') + cy.visit('/demos/Examples/Default/Vue') }) beforeEach(() => { diff --git a/docs/src/demos/Examples/Default/index.vue b/docs/src/demos/Examples/Default/Vue/index.vue similarity index 100% rename from docs/src/demos/Examples/Default/index.vue rename to docs/src/demos/Examples/Default/Vue/index.vue diff --git a/docs/src/demos/React/index.jsx b/docs/src/demos/React/index.jsx index 021ac371..3e0bd73a 100644 --- a/docs/src/demos/React/index.jsx +++ b/docs/src/demos/React/index.jsx @@ -1,6 +1,6 @@ import React, { useState } from 'react' import { defaultExtensions } from '@tiptap/starter-kit' -import { useEditor, Editor } from './components/Editor' +import { useEditor, Editor } from '@tiptap/react' // Menu bar example component // useEditor only works for child components of diff --git a/docs/src/docPages/examples/default.md b/docs/src/docPages/examples/default.md index 855996c8..47ff89cd 100644 --- a/docs/src/docPages/examples/default.md +++ b/docs/src/docPages/examples/default.md @@ -1,4 +1,8 @@ # Default text editor Did we mention that you have full control over the rendering of the editor? Here is barebones example without any styling, but packed with a whole set of common extensions. - +## Vue + + +## React + diff --git a/packages/react/README.md b/packages/react/README.md new file mode 100644 index 00000000..a2dfdd18 --- /dev/null +++ b/packages/react/README.md @@ -0,0 +1,14 @@ +# @tiptap/react +[![Version](https://img.shields.io/npm/v/@tiptap/react.svg?label=version)](https://www.npmjs.com/package/@tiptap/react) +[![Downloads](https://img.shields.io/npm/dm/@tiptap/react.svg)](https://npmcharts.com/compare/tiptap?minimal=true) +[![License](https://img.shields.io/npm/l/@tiptap/react.svg)](https://www.npmjs.com/package/@tiptap/react) +[![Sponsor](https://img.shields.io/static/v1?label=Sponsor&message=%E2%9D%A4&logo=GitHub)](https://github.com/sponsors/ueberdosis) + +## Introduction +tiptap is a headless wrapper around [ProseMirror](https://ProseMirror.net) – a toolkit for building rich text WYSIWYG editors, which is already in use at many well-known companies such as *New York Times*, *The Guardian* or *Atlassian*. + +## Offical Documentation +Documentation can be found on the [tiptap website](https://tiptap.dev). + +## License +tiptap is open-sourced software licensed under the [MIT license](https://github.com/ueberdosis/tiptap-next/blob/main/LICENSE.md). diff --git a/packages/react/package.json b/packages/react/package.json new file mode 100644 index 00000000..95f31fb4 --- /dev/null +++ b/packages/react/package.json @@ -0,0 +1,32 @@ +{ + "name": "@tiptap/react", + "description": "React components for tiptap", + "version": "2.0.0-alpha.1", + "private": true, + "homepage": "https://tiptap.dev", + "keywords": [ + "tiptap", + "tiptap react components" + ], + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/ueberdosis" + }, + "main": "dist/tiptap-react.cjs.js", + "umd": "dist/tiptap-react.umd.js", + "module": "dist/tiptap-react.esm.js", + "unpkg": "dist/tiptap-react.bundle.umd.min.js", + "types": "dist/packages/react/src/index.d.ts", + "files": [ + "src", + "dist" + ], + "peerDependencies": { + "@tiptap/core": "^2.0.0-alpha.6", + "react": "^17.0.1" + }, + "dependencies": { + "prosemirror-view": "^1.17.6" + } +} diff --git a/packages/react/src/ReactNodeViewRenderer.ts b/packages/react/src/ReactNodeViewRenderer.ts new file mode 100644 index 00000000..7fb1a1a8 --- /dev/null +++ b/packages/react/src/ReactNodeViewRenderer.ts @@ -0,0 +1 @@ +export default class ReactNodeViewRenderer {} diff --git a/packages/react/src/ReactRenderer.ts b/packages/react/src/ReactRenderer.ts new file mode 100644 index 00000000..d2672c60 --- /dev/null +++ b/packages/react/src/ReactRenderer.ts @@ -0,0 +1 @@ +export default class ReactRenderer {} diff --git a/packages/react/src/components/Editor.jsx b/packages/react/src/components/Editor.jsx new file mode 100644 index 00000000..8eec95cd --- /dev/null +++ b/packages/react/src/components/Editor.jsx @@ -0,0 +1,34 @@ +import React, { + useState, useRef, useEffect, createContext, useContext, +} from 'react' +import { Editor as Tiptap } from '@tiptap/core' + +export const EditorContext = createContext({}) + +export const useEditor = () => useContext(EditorContext) + +export const Editor = ({ + value, onChange, children, ...props +}) => { + const [editor, setEditor] = useState(null) + const editorRef = useRef(null) + + useEffect(() => { + const e = new Tiptap({ + element: editorRef.current, + content: value, + ...props, + }).on('transaction', () => { + onChange(e.getJSON()) + }) + + setEditor(e) + }, []) + + return ( + + {editorRef.current && children} +
+ + ) +} diff --git a/packages/react/src/index.ts b/packages/react/src/index.ts new file mode 100644 index 00000000..2fdac48e --- /dev/null +++ b/packages/react/src/index.ts @@ -0,0 +1,7 @@ +// @ts-nocheck +export * from '@tiptap/core' +export { default as ReactRenderer } from './ReactRenderer' +export { default as ReactNodeViewRenderer } from './ReactNodeViewRenderer' +export { + Editor, EditorContext, useEditor, +} from './components/Editor' From ca19923ad2617967ffd09b122170646b7ebe2ca8 Mon Sep 17 00:00:00 2001 From: Hans Pagel Date: Fri, 26 Feb 2021 02:02:10 +0100 Subject: [PATCH 6/8] copy tests to the React default example --- .../Examples/Default/React/index.spec.js | 22 +++++++++++++++++++ 1 file changed, 22 insertions(+) create mode 100644 docs/src/demos/Examples/Default/React/index.spec.js diff --git a/docs/src/demos/Examples/Default/React/index.spec.js b/docs/src/demos/Examples/Default/React/index.spec.js new file mode 100644 index 00000000..350c2d41 --- /dev/null +++ b/docs/src/demos/Examples/Default/React/index.spec.js @@ -0,0 +1,22 @@ +context('/demos/Examples/Default/React', () => { + before(() => { + cy.visit('/demos/Examples/Default/React') + }) + + beforeEach(() => { + cy.get('.ProseMirror').then(([{ editor }]) => { + editor.commands.setContent('

Example Text

') + cy.get('.ProseMirror').type('{selectall}') + }) + }) + + it('should apply the paragraph style when the keyboard shortcut is pressed', () => { + cy.get('.ProseMirror h1').should('exist') + cy.get('.ProseMirror p').should('not.exist') + + cy.get('.ProseMirror') + .trigger('keydown', { modKey: true, altKey: true, key: '0' }) + .find('p') + .should('contain', 'Example Text') + }) +}) From c84176c0644f865821e13dcb07c38b420972348b Mon Sep 17 00:00:00 2001 From: Hans Pagel Date: Fri, 26 Feb 2021 02:25:33 +0100 Subject: [PATCH 7/8] fix depency error --- packages/react/package.json | 1 - 1 file changed, 1 deletion(-) diff --git a/packages/react/package.json b/packages/react/package.json index 95f31fb4..4e36de26 100644 --- a/packages/react/package.json +++ b/packages/react/package.json @@ -2,7 +2,6 @@ "name": "@tiptap/react", "description": "React components for tiptap", "version": "2.0.0-alpha.1", - "private": true, "homepage": "https://tiptap.dev", "keywords": [ "tiptap", From 6305e865a704a050bfc994b2d390e74c136a78d3 Mon Sep 17 00:00:00 2001 From: Hans Pagel Date: Fri, 26 Feb 2021 02:40:22 +0100 Subject: [PATCH 8/8] add @babel/preset-react --- babel.config.js | 1 + package.json | 1 + 2 files changed, 2 insertions(+) diff --git a/babel.config.js b/babel.config.js index 016cb186..b519a4df 100644 --- a/babel.config.js +++ b/babel.config.js @@ -1,6 +1,7 @@ module.exports = { presets: [ '@babel/preset-env', + '@babel/preset-react', ], plugins: [ '@babel/plugin-proposal-nullish-coalescing-operator', diff --git a/package.json b/package.json index 8fdf51b2..eee56270 100644 --- a/package.json +++ b/package.json @@ -29,6 +29,7 @@ "@babel/plugin-proposal-nullish-coalescing-operator": "^7.12.13", "@babel/plugin-proposal-optional-chaining": "^7.12.17", "@babel/preset-env": "^7.12.17", + "@babel/preset-react": "^7.12.13", "@lerna/batch-packages": "^3.16.0", "@lerna/filter-packages": "^3.18.0", "@lerna/project": "^3.21.0",