From b6875183a36aca81310ae605fe66ab66341c6d32 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philipp=20Ku=CC=88hn?= Date: Wed, 2 Dec 2020 14:28:51 +0100 Subject: [PATCH 1/3] add some extensions to collab demo --- .../Examples/CollaborativeEditing/MenuBar.vue | 40 +++++++------------ .../Examples/CollaborativeEditing/index.vue | 6 +++ 2 files changed, 20 insertions(+), 26 deletions(-) diff --git a/docs/src/demos/Examples/CollaborativeEditing/MenuBar.vue b/docs/src/demos/Examples/CollaborativeEditing/MenuBar.vue index b1ccacfb..aa59e984 100644 --- a/docs/src/demos/Examples/CollaborativeEditing/MenuBar.vue +++ b/docs/src/demos/Examples/CollaborativeEditing/MenuBar.vue @@ -47,10 +47,10 @@ export default { isActive: () => this.editor.isActive('code'), }, { - icon: 'paragraph', - title: 'Paragraph', - action: () => this.editor.chain().focus().setParagraph().run(), - isActive: () => this.editor.isActive('paragraph'), + icon: 'mark-pen-line', + title: 'Highlight', + action: () => this.editor.chain().focus().toggleHighlight().run(), + isActive: () => this.editor.isActive('highlight'), }, { icon: 'h-1', @@ -65,28 +65,10 @@ export default { isActive: () => this.editor.isActive('heading', { level: 2 }), }, { - icon: 'h-3', - title: 'Heading 3', - action: () => this.editor.chain().focus().toggleHeading({ level: 3 }).run(), - isActive: () => this.editor.isActive('heading', { level: 3 }), - }, - { - icon: 'h-4', - title: 'Heading 4', - action: () => this.editor.chain().focus().toggleHeading({ level: 4 }).run(), - isActive: () => this.editor.isActive('heading', { level: 4 }), - }, - { - icon: 'h-5', - title: 'Heading 5', - action: () => this.editor.chain().focus().toggleHeading({ level: 5 }).run(), - isActive: () => this.editor.isActive('heading', { level: 5 }), - }, - { - icon: 'h-6', - title: 'Heading 6', - action: () => this.editor.chain().focus().toggleHeading({ level: 6 }).run(), - isActive: () => this.editor.isActive('heading', { level: 6 }), + icon: 'paragraph', + title: 'Paragraph', + action: () => this.editor.chain().focus().setParagraph().run(), + isActive: () => this.editor.isActive('paragraph'), }, { icon: 'list-unordered', @@ -100,6 +82,12 @@ export default { action: () => this.editor.chain().focus().toggleOrderedList().run(), isActive: () => this.editor.isActive('orderedList'), }, + { + icon: 'list-check-2', + title: 'Task List', + action: () => this.editor.chain().focus().toggleTaskList().run(), + isActive: () => this.editor.isActive('taskList'), + }, { icon: 'code-box-line', title: 'Code Block', diff --git a/docs/src/demos/Examples/CollaborativeEditing/index.vue b/docs/src/demos/Examples/CollaborativeEditing/index.vue index 10d46630..c46df0af 100644 --- a/docs/src/demos/Examples/CollaborativeEditing/index.vue +++ b/docs/src/demos/Examples/CollaborativeEditing/index.vue @@ -41,6 +41,9 @@ import { Editor, EditorContent, defaultExtensions } from '@tiptap/vue-starter-kit' import Collaboration from '@tiptap/extension-collaboration' import CollaborationCursor from '@tiptap/extension-collaboration-cursor' +import TaskList from '@tiptap/extension-task-list' +import TaskItem from '@tiptap/extension-task-item' +import Highlight from '@tiptap/extension-highlight' import * as Y from 'yjs' import { WebsocketProvider } from 'y-websocket' import { IndexeddbPersistence } from 'y-indexeddb' @@ -81,6 +84,9 @@ export default { this.editor = new Editor({ extensions: [ ...defaultExtensions().filter(extension => extension.config.name !== 'history'), + Highlight, + TaskList, + TaskItem, Collaboration.configure({ provider, }), From ae3e36e2ce252ade4e48f731e2158a2039d66b62 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philipp=20Ku=CC=88hn?= Date: Wed, 2 Dec 2020 14:39:25 +0100 Subject: [PATCH 2/3] improve isMarkActive --- packages/core/src/helpers/isMarkActive.ts | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/packages/core/src/helpers/isMarkActive.ts b/packages/core/src/helpers/isMarkActive.ts index f276670f..b41f1c2f 100644 --- a/packages/core/src/helpers/isMarkActive.ts +++ b/packages/core/src/helpers/isMarkActive.ts @@ -36,7 +36,7 @@ export default function isMarkActive( let markRanges: MarkRange[] = [] state.doc.nodesBetween(from, to, (node, pos) => { - if (node.isInline) { + if (node.isText) { const relativeFrom = Math.max(from, pos) const relativeTo = Math.min(to, pos + node.nodeSize) const range = relativeTo - relativeFrom @@ -51,6 +51,10 @@ export default function isMarkActive( } }) + if (selectionRange === 0) { + return false + } + const range = markRanges .filter(markRange => { if (!type) { From 685132bbf8ae8ba34930be7e145ed180acfc7290 Mon Sep 17 00:00:00 2001 From: kriskbx Date: Wed, 2 Dec 2020 15:35:41 +0100 Subject: [PATCH 3/3] update collaborative editing guide --- .../docPages/guide/collaborative-editing.md | 50 +++++++++++++++++-- 1 file changed, 47 insertions(+), 3 deletions(-) diff --git a/docs/src/docPages/guide/collaborative-editing.md b/docs/src/docPages/guide/collaborative-editing.md index 90e99c00..bbeb5482 100644 --- a/docs/src/docPages/guide/collaborative-editing.md +++ b/docs/src/docPages/guide/collaborative-editing.md @@ -205,17 +205,55 @@ Yes, it’s magic. As already mentioned, that is all based on the fantastic Y.js ## Store the content 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! +### Authentication +With the `onConnect` hook you can write a custom Promise to check if a client is authenticated. That can be a request to an API, to a microservice, a database query, or whatever is needed, as long as it’s executing `resolve()` at some point. You can also pass contextual data to the `resolve()` method which will be accessible in other hooks. + +```js +import { Server } from '@hocuspocus/server' + +const server = Server.configure({ + onConnect(data, resolve, reject) { + const { requestHeaders } = data + // Your code here, for example a request to an API + + // If the user is not authorized … + if (requestHeaders.access_token !== 'super-secret-token') { + return reject() + } + + // Set contextual data + const context = { + user_id: 1234, + } + + // If the user is authorized … + resolve(context) + }, +}) + +server.listen() +``` + ### Authorization -With the `onJoinDocument` hook you can write a custom Promise to check if a client is authorized. That can be a request to an API, to a microservice, a database query, or whatever is needed, as long as it’s executing `resolve()` at some point. +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. ```js import { Server } from '@hocuspocus/server' const server = Server.configure({ onJoinDocument(data, resolve, reject) { - const { documentName, clientID, requestHeaders, clientsCount, document } = data + const { + clientsCount, + context, + document, + documentName, + requestHeaders, + } = data // Your code here, for example a request to an API + // Access the contextual data from the onConnect hook, in this example this will print { user_id: 1234 } + console.log(context) + // If the user is authorized … resolve() @@ -258,8 +296,14 @@ const server = Server.configure({ // executed when the document is changed onChange(data) { - const { documentName, clientID, requestHeaders, clientsCount, document } = data + const { + clientsCount, + document, + documentName, + requestHeaders, + } = data + // Your code here, for example a request to an API }, })