From 4d07c09a6ce86e89812b3eda6973f6e2cad78855 Mon Sep 17 00:00:00 2001 From: "David P. Novakovic" Date: Fri, 6 Dec 2024 10:56:14 +1000 Subject: [PATCH 01/11] feat: show remote cursors when in collaboration. --- .changeset/odd-radios-smoke.md | 5 + apps/www/content/docs/collaboration.mdx | 6 +- packages/yjs/package.json | 2 + packages/yjs/src/react/Overlay.tsx | 123 +++++++++++++ packages/yjs/src/react/YjsAboveEditable.tsx | 3 +- yarn.lock | 192 +++++++++++--------- 6 files changed, 241 insertions(+), 90 deletions(-) create mode 100644 .changeset/odd-radios-smoke.md create mode 100644 packages/yjs/src/react/Overlay.tsx diff --git a/.changeset/odd-radios-smoke.md b/.changeset/odd-radios-smoke.md new file mode 100644 index 0000000000..cd58f7aef0 --- /dev/null +++ b/.changeset/odd-radios-smoke.md @@ -0,0 +1,5 @@ +--- +'@udecode/plate-yjs': patch +--- + +Add a small component to show remote cursors. diff --git a/apps/www/content/docs/collaboration.mdx b/apps/www/content/docs/collaboration.mdx index 04793a286f..c596338522 100644 --- a/apps/www/content/docs/collaboration.mdx +++ b/apps/www/content/docs/collaboration.mdx @@ -13,6 +13,8 @@ title: Collaboration ## Features - The yjs plugin enables support for collaboration using [slate-yjs](https://docs.slate-yjs.dev/) and [Hocuspocus](https://docs.slate-yjs.dev/walkthroughs/collaboration-hocuspocus). +- By default remote cursors are rendered slightly faded and become solid on hover. +- To customize, copy `YjsAboveEditable.tsx` and `Overlay.tsx` into your project and override the `options.render.aboveEditable` option to this plugin. @@ -35,8 +37,8 @@ const editor = createPlateEditor({ YjsPlugin.configure({ options: { hocuspocusProviderOptions: { - url: "https://hocuspocus.test/hocuspocus", - name: "test", + url: 'https://hocuspocus.test/hocuspocus', + name: 'test', }, }, }), diff --git a/packages/yjs/package.json b/packages/yjs/package.json index a2a8240fa0..9cc35e64b2 100644 --- a/packages/yjs/package.json +++ b/packages/yjs/package.json @@ -53,6 +53,8 @@ "dependencies": { "@hocuspocus/provider": "^2.13.5", "@slate-yjs/core": "^1.0.2", + "@slate-yjs/react": "1.1.0", + "@udecode/cn": "workspace:^", "yjs": "^13.6.19" }, "devDependencies": { diff --git a/packages/yjs/src/react/Overlay.tsx b/packages/yjs/src/react/Overlay.tsx new file mode 100644 index 0000000000..d56f96573f --- /dev/null +++ b/packages/yjs/src/react/Overlay.tsx @@ -0,0 +1,123 @@ +// Lifted from slate-yjs https://github.com/BitPhinix/slate-yjs/blob/main/examples/frontend/src/pages/RemoteCursorOverlay/Overlay.tsx + +import React, { + type CSSProperties, + type PropsWithChildren, + useRef, + useState, +} from 'react'; + +import { + type CursorOverlayData, + useRemoteCursorOverlayPositions, +} from '@slate-yjs/react'; + +import { cn } from '@udecode/cn'; + +export function addAlpha(hexColor: string, opacity: number): string { + const normalized = Math.round(Math.min(Math.max(opacity, 0), 1) * 255); + + return hexColor + normalized.toString(16).toUpperCase(); +} + +export type CursorData = { + color: string; + name: string; +}; + +type CaretProps = Pick, 'caretPosition' | 'data'>; +const cursorOpacity = 0.7; +const hoverOpacity = 1; + +function Caret({ caretPosition, data }: CaretProps) { + const [isHover, setIsHover] = useState(false); + + const handleMouseEnter = () => { + setIsHover(true); + }; + const handleMouseLeave = () => { + setIsHover(false); + }; + const caretStyle: CSSProperties = { + ...caretPosition, + background: data?.color, + opacity: cursorOpacity, + transition: 'opacity 0.2s', + }; + const caretStyleHover = { ...caretStyle, opacity: hoverOpacity }; + + const labelStyle: CSSProperties = { + background: data?.color, + opacity: cursorOpacity, + transform: 'translateY(-100%)', + transition: 'opacity 0.2s', + }; + const labelStyleHover = { ...labelStyle, opacity: hoverOpacity }; + + return ( +
+
+ {data?.name} +
+
+ ); +} + +function RemoteSelection({ + caretPosition, + data, + selectionRects, +}: CursorOverlayData) { + if (!data) { + return null; + } + + const selectionStyle: CSSProperties = { + // Add a opacity to the background color + backgroundColor: addAlpha(data.color, 0.5), + }; + + return ( + + {selectionRects.map((position, i) => ( +
+ ))} + {caretPosition && } +
+ ); +} + +type RemoteCursorsProps = PropsWithChildren<{ + className?: string; +}>; + +export function RemoteCursorOverlay({ + children, + className, +}: RemoteCursorsProps) { + const containerRef = useRef(null); + const [cursors] = useRemoteCursorOverlayPositions({ + containerRef, + }); + + return ( +
+ {children} + {cursors.map((cursor) => ( + + ))} +
+ ); +} diff --git a/packages/yjs/src/react/YjsAboveEditable.tsx b/packages/yjs/src/react/YjsAboveEditable.tsx index 140aadc7dc..537f3b09b0 100644 --- a/packages/yjs/src/react/YjsAboveEditable.tsx +++ b/packages/yjs/src/react/YjsAboveEditable.tsx @@ -4,6 +4,7 @@ import { YjsEditor } from '@slate-yjs/core'; import { useEditorPlugin } from '@udecode/plate-common/react'; import { type YjsConfig, BaseYjsPlugin } from '../lib/BaseYjsPlugin'; +import { RemoteCursorOverlay } from './Overlay'; export const YjsAboveEditable: React.FC<{ children: React.ReactNode; @@ -28,5 +29,5 @@ export const YjsAboveEditable: React.FC<{ if (!isSynced) return null; - return <>{children}; + return {children}; }; diff --git a/yarn.lock b/yarn.lock index e47c9970e7..dc89d31c0d 100644 --- a/yarn.lock +++ b/yarn.lock @@ -5162,6 +5162,22 @@ __metadata: languageName: node linkType: hard +"@slate-yjs/react@npm:1.1.0": + version: 1.1.0 + resolution: "@slate-yjs/react@npm:1.1.0" + dependencies: + use-sync-external-store: "npm:^1.2.0" + y-protocols: "npm:^1.0.5" + peerDependencies: + "@slate-yjs/core": ^1.0.0 + react: ">=16.8.0" + slate: ">=0.70.0" + slate-react: ">=0.70.0" + yjs: ^13.5.29 + checksum: 10c0/5fc3b49ec209f175bb9d11c206f76c792c0fcb234bf223082497e267658edaa97a530295fb396af63aae9a930bad8e6e761ac7bea63ee1c91d632a981b287506 + languageName: node + linkType: hard + "@socket.io/component-emitter@npm:~3.1.0": version: 3.1.2 resolution: "@socket.io/component-emitter@npm:3.1.2" @@ -6370,7 +6386,7 @@ __metadata: version: 0.0.0-use.local resolution: "@udecode/cn@workspace:packages/cn" dependencies: - "@udecode/react-utils": "npm:39.0.0" + "@udecode/react-utils": "npm:40.2.8" peerDependencies: class-variance-authority: ">=0.7.0" react: ">=16.8.0" @@ -6385,7 +6401,7 @@ __metadata: dependencies: "@udecode/plate-combobox": "npm:40.0.0" "@udecode/plate-markdown": "npm:40.2.2" - "@udecode/plate-selection": "npm:40.1.0" + "@udecode/plate-selection": "npm:40.2.9" ai: "npm:^3.4.10" lodash: "npm:^4.17.21" peerDependencies: @@ -6406,7 +6422,7 @@ __metadata: dependencies: "@udecode/plate-common": "workspace:^" peerDependencies: - "@udecode/plate-common": ">=40.0.3" + "@udecode/plate-common": ">=40.2.8" react: ">=16.8.0" react-dom: ">=16.8.0" slate: ">=0.103.0" @@ -6424,7 +6440,7 @@ __metadata: "@udecode/plate-common": "workspace:^" lodash: "npm:^4.17.21" peerDependencies: - "@udecode/plate-common": ">=40.0.3" + "@udecode/plate-common": ">=40.2.8" react: ">=16.8.0" react-dom: ">=16.8.0" slate: ">=0.103.0" @@ -6444,7 +6460,7 @@ __metadata: "@udecode/plate-common": "workspace:^" "@udecode/plate-heading": "npm:40.2.6" peerDependencies: - "@udecode/plate-common": ">=40.0.3" + "@udecode/plate-common": ">=40.2.8" react: ">=16.8.0" react-dom: ">=16.8.0" slate: ">=0.103.0" @@ -6461,7 +6477,7 @@ __metadata: dependencies: "@udecode/plate-common": "workspace:^" peerDependencies: - "@udecode/plate-common": ">=40.0.3" + "@udecode/plate-common": ">=40.2.8" react: ">=16.8.0" react-dom: ">=16.8.0" slate: ">=0.103.0" @@ -6478,7 +6494,7 @@ __metadata: dependencies: "@udecode/plate-common": "workspace:^" peerDependencies: - "@udecode/plate-common": ">=40.0.3" + "@udecode/plate-common": ">=40.2.8" react: ">=16.8.0" react-dom: ">=16.8.0" slate: ">=0.103.0" @@ -6495,7 +6511,7 @@ __metadata: dependencies: "@udecode/plate-common": "workspace:^" peerDependencies: - "@udecode/plate-common": ">=40.0.3" + "@udecode/plate-common": ">=40.2.8" react: ">=16.8.0" react-dom: ">=16.8.0" slate: ">=0.103.0" @@ -6512,7 +6528,7 @@ __metadata: dependencies: "@udecode/plate-common": "workspace:^" peerDependencies: - "@udecode/plate-common": ">=40.0.3" + "@udecode/plate-common": ">=40.2.8" react: ">=16.8.0" react-dom: ">=16.8.0" slate: ">=0.103.0" @@ -6530,7 +6546,7 @@ __metadata: "@udecode/plate-common": "workspace:^" react-textarea-autosize: "npm:^8.5.3" peerDependencies: - "@udecode/plate-common": ">=40.0.3" + "@udecode/plate-common": ">=40.2.8" react: ">=16.8.0" react-dom: ">=16.8.0" slate: ">=0.103.0" @@ -6550,7 +6566,7 @@ __metadata: delay: "npm:5.0.0" p-defer: "npm:^4.0.1" peerDependencies: - "@udecode/plate-common": ">=40.0.3" + "@udecode/plate-common": ">=40.2.8" react: ">=16.8.0" react-dom: ">=16.8.0" slate: ">=0.103.0" @@ -6568,7 +6584,7 @@ __metadata: "@udecode/plate-common": "workspace:^" prismjs: "npm:^1.29.0" peerDependencies: - "@udecode/plate-common": ">=40.0.3" + "@udecode/plate-common": ">=40.2.8" react: ">=16.8.0" react-dom: ">=16.8.0" slate: ">=0.103.0" @@ -6585,7 +6601,7 @@ __metadata: dependencies: "@udecode/plate-common": "workspace:^" peerDependencies: - "@udecode/plate-common": ">=40.0.3" + "@udecode/plate-common": ">=40.2.8" react: ">=16.8.0" react-dom: ">=16.8.0" slate: ">=0.103.0" @@ -6603,7 +6619,7 @@ __metadata: "@udecode/plate-common": "workspace:^" lodash: "npm:^4.17.21" peerDependencies: - "@udecode/plate-common": ">=40.0.3" + "@udecode/plate-common": ">=40.2.8" react: ">=16.8.0" react-dom: ">=16.8.0" slate: ">=0.103.0" @@ -6614,17 +6630,17 @@ __metadata: languageName: unknown linkType: soft -"@udecode/plate-common@npm:40.0.3, @udecode/plate-common@workspace:^, @udecode/plate-common@workspace:packages/common": +"@udecode/plate-common@npm:40.2.8, @udecode/plate-common@workspace:^, @udecode/plate-common@workspace:packages/common": version: 0.0.0-use.local resolution: "@udecode/plate-common@workspace:packages/common" dependencies: - "@udecode/plate-core": "npm:40.0.3" - "@udecode/plate-utils": "npm:40.0.3" + "@udecode/plate-core": "npm:40.2.8" + "@udecode/plate-utils": "npm:40.2.8" "@udecode/react-hotkeys": "npm:37.0.0" - "@udecode/react-utils": "npm:39.0.0" + "@udecode/react-utils": "npm:40.2.8" "@udecode/slate": "npm:39.2.1" - "@udecode/slate-react": "npm:40.0.0" - "@udecode/slate-utils": "npm:39.2.20" + "@udecode/slate-react": "npm:40.2.8" + "@udecode/slate-utils": "npm:40.2.7" "@udecode/utils": "npm:37.0.0" peerDependencies: react: ">=16.8.0" @@ -6637,15 +6653,15 @@ __metadata: languageName: unknown linkType: soft -"@udecode/plate-core@npm:40.0.3, @udecode/plate-core@workspace:^, @udecode/plate-core@workspace:packages/core": +"@udecode/plate-core@npm:40.2.8, @udecode/plate-core@workspace:^, @udecode/plate-core@workspace:packages/core": version: 0.0.0-use.local resolution: "@udecode/plate-core@workspace:packages/core" dependencies: "@udecode/react-hotkeys": "npm:37.0.0" - "@udecode/react-utils": "npm:39.0.0" + "@udecode/react-utils": "npm:40.2.8" "@udecode/slate": "npm:39.2.1" - "@udecode/slate-react": "npm:40.0.0" - "@udecode/slate-utils": "npm:39.2.20" + "@udecode/slate-react": "npm:40.2.8" + "@udecode/slate-utils": "npm:40.2.7" "@udecode/utils": "npm:37.0.0" clsx: "npm:^2.1.1" is-hotkey: "npm:^0.2.0" @@ -6678,7 +6694,7 @@ __metadata: "@udecode/plate-table": "npm:40.0.0" papaparse: "npm:^5.4.1" peerDependencies: - "@udecode/plate-common": ">=40.0.3" + "@udecode/plate-common": ">=40.2.8" react: ">=16.8.0" react-dom: ">=16.8.0" slate: ">=0.103.0" @@ -6695,7 +6711,7 @@ __metadata: dependencies: "@udecode/plate-common": "workspace:^" peerDependencies: - "@udecode/plate-common": ">=40.0.3" + "@udecode/plate-common": ">=40.2.8" react: ">=16.8.0" react-dom: ">=16.8.0" slate: ">=0.103.0" @@ -6712,7 +6728,7 @@ __metadata: dependencies: "@udecode/plate-common": "workspace:^" peerDependencies: - "@udecode/plate-common": ">=40.0.3" + "@udecode/plate-common": ">=40.2.8" react: ">=16.8.0" react-dom: ">=16.8.0" slate: ">=0.94.0" @@ -6730,7 +6746,7 @@ __metadata: diff-match-patch-ts: "npm:^0.6.0" lodash: "npm:^4.17.21" peerDependencies: - "@udecode/plate-common": ">=40.0.3" + "@udecode/plate-common": ">=40.2.8" react: ">=16.8.0" react-dom: ">=16.8.0" slate: ">=0.103.0" @@ -6749,7 +6765,7 @@ __metadata: lodash: "npm:^4.17.21" raf: "npm:^3.4.1" peerDependencies: - "@udecode/plate-common": ">=40.0.3" + "@udecode/plate-common": ">=40.2.8" react: ">=16.8.0" react-dnd: ">=14.0.0" react-dnd-html5-backend: ">=14.0.0" @@ -6762,7 +6778,7 @@ __metadata: languageName: unknown linkType: soft -"@udecode/plate-docx@npm:40.2.6, @udecode/plate-docx@workspace:^, @udecode/plate-docx@workspace:packages/docx": +"@udecode/plate-docx@npm:40.2.7, @udecode/plate-docx@workspace:^, @udecode/plate-docx@workspace:packages/docx": version: 0.0.0-use.local resolution: "@udecode/plate-docx@workspace:packages/docx" dependencies: @@ -6770,11 +6786,11 @@ __metadata: "@udecode/plate-heading": "npm:40.2.6" "@udecode/plate-indent": "npm:40.0.0" "@udecode/plate-indent-list": "npm:40.0.0" - "@udecode/plate-media": "npm:40.2.4" + "@udecode/plate-media": "npm:40.2.7" "@udecode/plate-table": "npm:40.0.0" validator: "npm:^13.12.0" peerDependencies: - "@udecode/plate-common": ">=40.0.3" + "@udecode/plate-common": ">=40.2.8" react: ">=16.8.0" react-dom: ">=16.8.0" slate: ">=0.103.0" @@ -6793,7 +6809,7 @@ __metadata: "@udecode/plate-combobox": "npm:40.0.0" "@udecode/plate-common": "workspace:^" peerDependencies: - "@udecode/plate-common": ">=40.0.3" + "@udecode/plate-common": ">=40.2.8" react: ">=16.8.0" react-dom: ">=16.8.0" slate: ">=0.103.0" @@ -6811,7 +6827,7 @@ __metadata: "@excalidraw/excalidraw": "npm:0.16.4" "@udecode/plate-common": "workspace:^" peerDependencies: - "@udecode/plate-common": ">=40.0.3" + "@udecode/plate-common": ">=40.2.8" react: ">=16.8.0" react-dom: ">=16.8.0" slate: ">=0.103.0" @@ -6822,13 +6838,13 @@ __metadata: languageName: unknown linkType: soft -"@udecode/plate-find-replace@npm:40.0.0, @udecode/plate-find-replace@workspace:^, @udecode/plate-find-replace@workspace:packages/find-replace": +"@udecode/plate-find-replace@npm:40.2.8, @udecode/plate-find-replace@workspace:^, @udecode/plate-find-replace@workspace:packages/find-replace": version: 0.0.0-use.local resolution: "@udecode/plate-find-replace@workspace:packages/find-replace" dependencies: "@udecode/plate-common": "workspace:^" peerDependencies: - "@udecode/plate-common": ">=40.0.3" + "@udecode/plate-common": ">=40.2.8" react: ">=16.8.0" react-dom: ">=16.8.0" slate: ">=0.103.0" @@ -6847,7 +6863,7 @@ __metadata: "@floating-ui/react": "npm:^0.26.23" "@udecode/plate-common": "workspace:^" peerDependencies: - "@udecode/plate-common": ">=40.0.3" + "@udecode/plate-common": ">=40.2.8" react: ">=16.8.0" react-dom: ">=16.8.0" slate: ">=0.103.0" @@ -6865,7 +6881,7 @@ __metadata: "@udecode/plate-common": "workspace:^" lodash: "npm:^4.17.21" peerDependencies: - "@udecode/plate-common": ">=40.0.3" + "@udecode/plate-common": ">=40.2.8" react: ">=16.8.0" react-dom: ">=16.8.0" slate: ">=0.103.0" @@ -6882,7 +6898,7 @@ __metadata: dependencies: "@udecode/plate-common": "workspace:^" peerDependencies: - "@udecode/plate-common": ">=40.0.3" + "@udecode/plate-common": ">=40.2.8" react: ">=16.8.0" react-dom: ">=16.8.0" slate: ">=0.103.0" @@ -6899,7 +6915,7 @@ __metadata: dependencies: "@udecode/plate-common": "workspace:^" peerDependencies: - "@udecode/plate-common": ">=40.0.3" + "@udecode/plate-common": ">=40.2.8" react: ">=16.8.0" react-dom: ">=16.8.0" slate: ">=0.103.0" @@ -6916,7 +6932,7 @@ __metadata: dependencies: "@udecode/plate-common": "workspace:^" peerDependencies: - "@udecode/plate-common": ">=40.0.3" + "@udecode/plate-common": ">=40.2.8" react: ">=16.8.0" react-dom: ">=16.8.0" slate: ">=0.103.0" @@ -6935,7 +6951,7 @@ __metadata: "@udecode/plate-common": "workspace:^" html-entities: "npm:^2.5.2" peerDependencies: - "@udecode/plate-common": ">=40.0.3" + "@udecode/plate-common": ">=40.2.8" react: ">=16.8.0" react-dom: ">=16.8.0" slate: ">=0.103.0" @@ -6955,7 +6971,7 @@ __metadata: "@udecode/plate-list": "npm:40.0.0" clsx: "npm:^2.1.1" peerDependencies: - "@udecode/plate-common": ">=40.0.3" + "@udecode/plate-common": ">=40.2.8" react: ">=16.8.0" react-dom: ">=16.8.0" slate: ">=0.103.0" @@ -6972,7 +6988,7 @@ __metadata: dependencies: "@udecode/plate-common": "workspace:^" peerDependencies: - "@udecode/plate-common": ">=40.0.3" + "@udecode/plate-common": ">=40.2.8" react: ">=16.8.0" react-dom: ">=16.8.0" slate: ">=0.103.0" @@ -6990,7 +7006,7 @@ __metadata: "@udecode/plate-common": "workspace:^" juice: "npm:^8.1.0" peerDependencies: - "@udecode/plate-common": ">=40.0.3" + "@udecode/plate-common": ">=40.2.8" react: ">=16.8.0" react-dom: ">=16.8.0" slate: ">=0.103.0" @@ -7007,7 +7023,7 @@ __metadata: dependencies: "@udecode/plate-common": "workspace:^" peerDependencies: - "@udecode/plate-common": ">=40.0.3" + "@udecode/plate-common": ">=40.2.8" react: ">=16.8.0" react-dom: ">=16.8.0" slate: ">=0.103.0" @@ -7024,7 +7040,7 @@ __metadata: dependencies: "@udecode/plate-common": "workspace:^" peerDependencies: - "@udecode/plate-common": ">=40.0.3" + "@udecode/plate-common": ">=40.2.8" react: ">=16.8.0" react-dom: ">=16.8.0" slate: ">=0.103.0" @@ -7041,7 +7057,7 @@ __metadata: dependencies: "@udecode/plate-common": "workspace:^" peerDependencies: - "@udecode/plate-common": ">=40.0.3" + "@udecode/plate-common": ">=40.2.8" react: ">=16.8.0" react-dom: ">=16.8.0" slate: ">=0.103.0" @@ -7060,7 +7076,7 @@ __metadata: "@udecode/plate-floating": "npm:40.0.0" "@udecode/plate-normalizers": "npm:40.0.0" peerDependencies: - "@udecode/plate-common": ">=40.0.3" + "@udecode/plate-common": ">=40.2.8" react: ">=16.8.0" react-dom: ">=16.8.0" slate: ">=0.103.0" @@ -7079,7 +7095,7 @@ __metadata: "@udecode/plate-reset-node": "npm:40.0.0" lodash: "npm:^4.17.21" peerDependencies: - "@udecode/plate-common": ">=40.0.3" + "@udecode/plate-common": ">=40.2.8" react: ">=16.8.0" react-dom: ">=16.8.0" slate: ">=0.103.0" @@ -7100,7 +7116,7 @@ __metadata: remark-parse: "npm:^11.0.0" unified: "npm:^11.0.5" peerDependencies: - "@udecode/plate-common": ">=40.0.3" + "@udecode/plate-common": ">=40.2.8" react: ">=16.8.0" react-dom: ">=16.8.0" slate: ">=0.103.0" @@ -7119,7 +7135,7 @@ __metadata: "@udecode/plate-common": "workspace:^" katex: "npm:0.16.11" peerDependencies: - "@udecode/plate-common": ">=40.0.3" + "@udecode/plate-common": ">=40.2.8" react: ">=16.8.0" react-dom: ">=16.8.0" slate: ">=0.103.0" @@ -7130,14 +7146,14 @@ __metadata: languageName: unknown linkType: soft -"@udecode/plate-media@npm:40.2.4, @udecode/plate-media@workspace:^, @udecode/plate-media@workspace:packages/media": +"@udecode/plate-media@npm:40.2.7, @udecode/plate-media@workspace:^, @udecode/plate-media@workspace:packages/media": version: 0.0.0-use.local resolution: "@udecode/plate-media@workspace:packages/media" dependencies: "@udecode/plate-common": "workspace:^" js-video-url-parser: "npm:^0.5.1" peerDependencies: - "@udecode/plate-common": ">=40.0.3" + "@udecode/plate-common": ">=40.2.8" react: ">=16.8.0" react-dom: ">=16.8.0" slate: ">=0.103.0" @@ -7155,7 +7171,7 @@ __metadata: "@udecode/plate-combobox": "npm:40.0.0" "@udecode/plate-common": "workspace:^" peerDependencies: - "@udecode/plate-common": ">=40.0.3" + "@udecode/plate-common": ">=40.2.8" react: ">=16.8.0" react-dom: ">=16.8.0" slate: ">=0.103.0" @@ -7173,7 +7189,7 @@ __metadata: "@udecode/plate-common": "workspace:^" lodash: "npm:^4.17.21" peerDependencies: - "@udecode/plate-common": ">=40.0.3" + "@udecode/plate-common": ">=40.2.8" react: ">=16.8.0" react-dom: ">=16.8.0" slate: ">=0.103.0" @@ -7191,7 +7207,7 @@ __metadata: "@udecode/plate-common": "workspace:^" lodash: "npm:^4.17.21" peerDependencies: - "@udecode/plate-common": ">=40.0.3" + "@udecode/plate-common": ">=40.2.8" react: ">=16.8.0" react-dom: ">=16.8.0" slate: ">=0.103.0" @@ -7209,7 +7225,7 @@ __metadata: "@udecode/plate-common": "workspace:^" peerDependencies: "@playwright/test": ">=1.42.1" - "@udecode/plate-common": ">=40.0.3" + "@udecode/plate-common": ">=40.2.8" react: ">=16.8.0" react-dom: ">=16.8.0" slate: ">=0.103.0" @@ -7226,7 +7242,7 @@ __metadata: dependencies: "@udecode/plate-common": "workspace:^" peerDependencies: - "@udecode/plate-common": ">=40.0.3" + "@udecode/plate-common": ">=40.2.8" react: ">=16.8.0" react-dom: ">=16.8.0" slate: ">=0.103.0" @@ -7243,7 +7259,7 @@ __metadata: dependencies: "@udecode/plate-common": "workspace:^" peerDependencies: - "@udecode/plate-common": ">=40.0.3" + "@udecode/plate-common": ">=40.2.8" react: ">=16.8.0" react-dom: ">=16.8.0" slate: ">=0.103.0" @@ -7260,7 +7276,7 @@ __metadata: dependencies: "@udecode/plate-common": "workspace:^" peerDependencies: - "@udecode/plate-common": ">=40.0.3" + "@udecode/plate-common": ">=40.2.8" react: ">=16.8.0" react-dom: ">=16.8.0" slate: ">=0.103.0" @@ -7271,14 +7287,14 @@ __metadata: languageName: unknown linkType: soft -"@udecode/plate-selection@npm:40.1.0, @udecode/plate-selection@workspace:^, @udecode/plate-selection@workspace:packages/selection": +"@udecode/plate-selection@npm:40.2.9, @udecode/plate-selection@workspace:^, @udecode/plate-selection@workspace:packages/selection": version: 0.0.0-use.local resolution: "@udecode/plate-selection@workspace:packages/selection" dependencies: "@udecode/plate-common": "workspace:^" copy-to-clipboard: "npm:^3.3.3" peerDependencies: - "@udecode/plate-common": ">=40.0.3" + "@udecode/plate-common": ">=40.2.8" react: ">=16.8.0" react-dom: ">=16.8.0" slate: ">=0.103.0" @@ -7296,7 +7312,7 @@ __metadata: "@udecode/plate-combobox": "npm:40.0.0" "@udecode/plate-common": "workspace:^" peerDependencies: - "@udecode/plate-common": ">=40.0.3" + "@udecode/plate-common": ">=40.2.8" react: ">=16.8.0" react-dom: ">=16.8.0" slate: ">=0.103.0" @@ -7307,7 +7323,7 @@ __metadata: languageName: unknown linkType: soft -"@udecode/plate-suggestion@npm:40.0.0, @udecode/plate-suggestion@workspace:^, @udecode/plate-suggestion@workspace:packages/suggestion": +"@udecode/plate-suggestion@npm:40.2.8, @udecode/plate-suggestion@workspace:^, @udecode/plate-suggestion@workspace:packages/suggestion": version: 0.0.0-use.local resolution: "@udecode/plate-suggestion@workspace:packages/suggestion" dependencies: @@ -7315,7 +7331,7 @@ __metadata: "@udecode/plate-diff": "npm:40.0.0" lodash: "npm:^4.17.21" peerDependencies: - "@udecode/plate-common": ">=40.0.3" + "@udecode/plate-common": ">=40.2.8" react: ">=16.8.0" react-dom: ">=16.8.0" slate: ">=0.103.0" @@ -7333,7 +7349,7 @@ __metadata: "@udecode/plate-common": "workspace:^" tabbable: "npm:^6.2.0" peerDependencies: - "@udecode/plate-common": ">=40.0.3" + "@udecode/plate-common": ">=40.2.8" react: ">=16.8.0" react-dom: ">=16.8.0" slate: ">=0.103.0" @@ -7352,7 +7368,7 @@ __metadata: "@udecode/plate-resizable": "npm:40.0.0" lodash: "npm:^4.17.21" peerDependencies: - "@udecode/plate-common": ">=40.0.3" + "@udecode/plate-common": ">=40.2.8" react: ">=16.8.0" react-dom: ">=16.8.0" slate: ">=0.103.0" @@ -7368,7 +7384,7 @@ __metadata: dependencies: "@udecode/plate-common": "workspace:^" peerDependencies: - "@udecode/plate-common": ">=40.0.3" + "@udecode/plate-common": ">=40.2.8" react: ">=16.8.0" react-dom: ">=16.8.0" slate: ">=0.103.0" @@ -7397,7 +7413,7 @@ __metadata: "@udecode/plate-node-id": "npm:40.0.0" lodash: "npm:^4.17.21" peerDependencies: - "@udecode/plate-common": ">=40.0.3" + "@udecode/plate-common": ">=40.2.8" react: ">=16.8.0" react-dom: ">=16.8.0" slate: ">=0.103.0" @@ -7414,7 +7430,7 @@ __metadata: dependencies: "@udecode/plate-common": "workspace:^" peerDependencies: - "@udecode/plate-common": ">=40.0.3" + "@udecode/plate-common": ">=40.2.8" react: ">=16.8.0" react-dom: ">=16.8.0" slate: ">=0.103.0" @@ -7425,15 +7441,15 @@ __metadata: languageName: unknown linkType: soft -"@udecode/plate-utils@npm:40.0.3, @udecode/plate-utils@workspace:^, @udecode/plate-utils@workspace:packages/plate-utils": +"@udecode/plate-utils@npm:40.2.8, @udecode/plate-utils@workspace:^, @udecode/plate-utils@workspace:packages/plate-utils": version: 0.0.0-use.local resolution: "@udecode/plate-utils@workspace:packages/plate-utils" dependencies: - "@udecode/plate-core": "npm:40.0.3" - "@udecode/react-utils": "npm:39.0.0" + "@udecode/plate-core": "npm:40.2.8" + "@udecode/react-utils": "npm:40.2.8" "@udecode/slate": "npm:39.2.1" - "@udecode/slate-react": "npm:40.0.0" - "@udecode/slate-utils": "npm:39.2.20" + "@udecode/slate-react": "npm:40.2.8" + "@udecode/slate-utils": "npm:40.2.7" "@udecode/utils": "npm:37.0.0" clsx: "npm:^2.1.1" lodash: "npm:^4.17.21" @@ -7453,10 +7469,12 @@ __metadata: dependencies: "@hocuspocus/provider": "npm:^2.13.5" "@slate-yjs/core": "npm:^1.0.2" + "@slate-yjs/react": "npm:1.1.0" + "@udecode/cn": "workspace:^" "@udecode/plate-common": "workspace:^" yjs: "npm:^13.6.19" peerDependencies: - "@udecode/plate-common": ">=40.0.3" + "@udecode/plate-common": ">=40.2.8" react: ">=16.8.0" react-dom: ">=16.8.0" slate: ">=0.103.0" @@ -7480,11 +7498,11 @@ __metadata: "@udecode/plate-code-block": "npm:40.0.0" "@udecode/plate-combobox": "npm:40.0.0" "@udecode/plate-comments": "npm:40.0.0" - "@udecode/plate-common": "npm:40.0.3" + "@udecode/plate-common": "npm:40.2.8" "@udecode/plate-csv": "npm:40.0.0" "@udecode/plate-diff": "npm:40.0.0" - "@udecode/plate-docx": "npm:40.2.6" - "@udecode/plate-find-replace": "npm:40.0.0" + "@udecode/plate-docx": "npm:40.2.7" + "@udecode/plate-find-replace": "npm:40.2.8" "@udecode/plate-floating": "npm:40.0.0" "@udecode/plate-font": "npm:40.0.0" "@udecode/plate-heading": "npm:40.2.6" @@ -7499,16 +7517,16 @@ __metadata: "@udecode/plate-link": "npm:40.0.0" "@udecode/plate-list": "npm:40.0.0" "@udecode/plate-markdown": "npm:40.2.2" - "@udecode/plate-media": "npm:40.2.4" + "@udecode/plate-media": "npm:40.2.7" "@udecode/plate-mention": "npm:40.0.0" "@udecode/plate-node-id": "npm:40.0.0" "@udecode/plate-normalizers": "npm:40.0.0" "@udecode/plate-reset-node": "npm:40.0.0" "@udecode/plate-resizable": "npm:40.0.0" "@udecode/plate-select": "npm:40.0.0" - "@udecode/plate-selection": "npm:40.1.0" + "@udecode/plate-selection": "npm:40.2.9" "@udecode/plate-slash-command": "npm:40.0.0" - "@udecode/plate-suggestion": "npm:40.0.0" + "@udecode/plate-suggestion": "npm:40.2.8" "@udecode/plate-tabbable": "npm:40.0.0" "@udecode/plate-table": "npm:40.0.0" "@udecode/plate-toggle": "npm:40.0.0" @@ -7533,7 +7551,7 @@ __metadata: languageName: unknown linkType: soft -"@udecode/react-utils@npm:39.0.0, @udecode/react-utils@workspace:^, @udecode/react-utils@workspace:packages/react-utils": +"@udecode/react-utils@npm:40.2.8, @udecode/react-utils@workspace:^, @udecode/react-utils@workspace:packages/react-utils": version: 0.0.0-use.local resolution: "@udecode/react-utils@workspace:packages/react-utils" dependencies: @@ -7546,11 +7564,11 @@ __metadata: languageName: unknown linkType: soft -"@udecode/slate-react@npm:40.0.0, @udecode/slate-react@workspace:^, @udecode/slate-react@workspace:packages/slate-react": +"@udecode/slate-react@npm:40.2.8, @udecode/slate-react@workspace:^, @udecode/slate-react@workspace:packages/slate-react": version: 0.0.0-use.local resolution: "@udecode/slate-react@workspace:packages/slate-react" dependencies: - "@udecode/react-utils": "npm:39.0.0" + "@udecode/react-utils": "npm:40.2.8" "@udecode/slate": "npm:39.2.1" "@udecode/utils": "npm:37.0.0" peerDependencies: @@ -7562,7 +7580,7 @@ __metadata: languageName: unknown linkType: soft -"@udecode/slate-utils@npm:39.2.20, @udecode/slate-utils@workspace:^, @udecode/slate-utils@workspace:packages/slate-utils": +"@udecode/slate-utils@npm:40.2.7, @udecode/slate-utils@workspace:^, @udecode/slate-utils@workspace:packages/slate-utils": version: 0.0.0-use.local resolution: "@udecode/slate-utils@workspace:packages/slate-utils" dependencies: From bd5b7ac41d06322bd919ef40fccae03ab13a305e Mon Sep 17 00:00:00 2001 From: "David P. Novakovic" Date: Fri, 6 Dec 2024 11:12:47 +1000 Subject: [PATCH 02/11] doc: how to change name and color for cursors --- apps/www/content/docs/collaboration.mdx | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/apps/www/content/docs/collaboration.mdx b/apps/www/content/docs/collaboration.mdx index c596338522..1b2689a9ed 100644 --- a/apps/www/content/docs/collaboration.mdx +++ b/apps/www/content/docs/collaboration.mdx @@ -13,7 +13,7 @@ title: Collaboration ## Features - The yjs plugin enables support for collaboration using [slate-yjs](https://docs.slate-yjs.dev/) and [Hocuspocus](https://docs.slate-yjs.dev/walkthroughs/collaboration-hocuspocus). -- By default remote cursors are rendered slightly faded and become solid on hover. +- By default remote cursors are rendered slightly faded and become solid on hover. Use the `data` field in `cursorOptions` to customise the display name and color. - To customize, copy `YjsAboveEditable.tsx` and `Overlay.tsx` into your project and override the `options.render.aboveEditable` option to this plugin. @@ -36,6 +36,11 @@ const editor = createPlateEditor({ // ...otherPlugins, YjsPlugin.configure({ options: { + cursorOptions: { + autoSend: true, + data: { name: 'A plate user', color: '#5AC990' }, + }, + disableCursors: false, hocuspocusProviderOptions: { url: 'https://hocuspocus.test/hocuspocus', name: 'test', From a06771e1096dfc615b4cf56559e8f13f0234434a Mon Sep 17 00:00:00 2001 From: "David P. Novakovic" Date: Fri, 6 Dec 2024 19:02:21 +1000 Subject: [PATCH 03/11] chore: move overlay to plate-ui registry --- .../www/src/registry/default/plate-ui/remote-cursor-overlay.tsx | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename packages/yjs/src/react/Overlay.tsx => apps/www/src/registry/default/plate-ui/remote-cursor-overlay.tsx (100%) diff --git a/packages/yjs/src/react/Overlay.tsx b/apps/www/src/registry/default/plate-ui/remote-cursor-overlay.tsx similarity index 100% rename from packages/yjs/src/react/Overlay.tsx rename to apps/www/src/registry/default/plate-ui/remote-cursor-overlay.tsx From 74abc4c6e8017693bc4b60b67ffc9660ffec1bab Mon Sep 17 00:00:00 2001 From: "David P. Novakovic" Date: Fri, 6 Dec 2024 19:39:10 +1000 Subject: [PATCH 04/11] feat: split out UI to component registry and tweak docs. --- apps/www/content/docs/collaboration.mdx | 25 ++++++++++++++++-- apps/www/public/r/index.json | 24 +++++++++++++++++ .../styles/default/remote-cursor-overlay.json | 26 +++++++++++++++++++ apps/www/src/__registry__/index.tsx | 16 ++++++++++++ .../plate-ui/remote-cursor-overlay.tsx | 15 ++++------- apps/www/src/registry/registry-ui.ts | 15 +++++++++++ packages/yjs/src/react/YjsAboveEditable.tsx | 3 +-- packages/yjs/src/react/YjsPlugin.tsx | 4 ++- 8 files changed, 113 insertions(+), 15 deletions(-) create mode 100644 apps/www/public/r/styles/default/remote-cursor-overlay.json diff --git a/apps/www/content/docs/collaboration.mdx b/apps/www/content/docs/collaboration.mdx index 1b2689a9ed..c6a959e7d2 100644 --- a/apps/www/content/docs/collaboration.mdx +++ b/apps/www/content/docs/collaboration.mdx @@ -13,8 +13,8 @@ title: Collaboration ## Features - The yjs plugin enables support for collaboration using [slate-yjs](https://docs.slate-yjs.dev/) and [Hocuspocus](https://docs.slate-yjs.dev/walkthroughs/collaboration-hocuspocus). -- By default remote cursors are rendered slightly faded and become solid on hover. Use the `data` field in `cursorOptions` to customise the display name and color. -- To customize, copy `YjsAboveEditable.tsx` and `Overlay.tsx` into your project and override the `options.render.aboveEditable` option to this plugin. +- By default remote cursors are not rendered unless you install the remote cursor overlay and include it in the config. +- Cursors are rendered slightly faded and become solid on hover. Use the `data` field in `cursorOptions` to customise the display name and color. @@ -30,12 +30,16 @@ npm install @udecode/plate-yjs ```tsx import { YjsPlugin } from '@udecode/plate-yjs/react'; +import { RemoteCursorOverlay } from '@/components/plate-ui/remote-cursor-overlay'; const editor = createPlateEditor({ plugins: [ // ...otherPlugins, YjsPlugin.configure({ options: { + render: { + afterEditable: RemoteCursorOverlay, + }, cursorOptions: { autoSend: true, data: { name: 'A plate user', color: '#5AC990' }, @@ -51,6 +55,23 @@ const editor = createPlateEditor({ }); ``` +### Editor Container + +The editor requires a container component above `PlateContent` to ensure correct cursor overlay positioning: + +```tsx +export const EditorContainer = ( + props: React.HTMLAttributes +) => { + const editor = useEditorRef(); + const containerRef = useEditorContainerRef(); + + return
; +}; +``` + +This component is available in [Editor](/docs/components/editor). + ## Backend Follow the backend instructions in [Hocuspocus docs](https://tiptap.dev/hocuspocus/getting-started). diff --git a/apps/www/public/r/index.json b/apps/www/public/r/index.json index c02c508c18..87fdbd1b60 100644 --- a/apps/www/public/r/index.json +++ b/apps/www/public/r/index.json @@ -1476,6 +1476,30 @@ "registryDependencies": [], "type": "registry:ui" }, + { + "dependencies": [ + "@slate-yjs/react", + "@udecode/plate-core" + ], + "doc": { + "description": "A cursor overlay to display multiplayer cursors in the yjs plugin.", + "docs": [ + { + "route": "/docs/collaboration" + } + ], + "examples": [] + }, + "files": [ + { + "path": "plate-ui/remote-cursor-overlay.tsx", + "type": "registry:ui" + } + ], + "name": "remote-cursor-overlay", + "registryDependencies": [], + "type": "registry:ui" + }, { "dependencies": [ "@udecode/plate-ai", diff --git a/apps/www/public/r/styles/default/remote-cursor-overlay.json b/apps/www/public/r/styles/default/remote-cursor-overlay.json new file mode 100644 index 0000000000..7f8dfd9c6c --- /dev/null +++ b/apps/www/public/r/styles/default/remote-cursor-overlay.json @@ -0,0 +1,26 @@ +{ + "dependencies": [ + "@slate-yjs/react", + "@udecode/plate-core" + ], + "doc": { + "description": "A cursor overlay to display multiplayer cursors in the yjs plugin.", + "docs": [ + { + "route": "/docs/collaboration" + } + ], + "examples": [] + }, + "files": [ + { + "content": "import React, {\n type CSSProperties,\n type PropsWithChildren,\n useState,\n} from 'react';\n\nimport {\n type CursorOverlayData,\n useRemoteCursorOverlayPositions,\n} from '@slate-yjs/react';\nimport { useEditorContainerRef } from '@udecode/plate-core/react';\n\nexport function addAlpha(hexColor: string, opacity: number): string {\n const normalized = Math.round(Math.min(Math.max(opacity, 0), 1) * 255);\n\n return hexColor + normalized.toString(16).toUpperCase();\n}\n\nexport type CursorData = {\n color: string;\n name: string;\n};\n\ntype CaretProps = Pick, 'caretPosition' | 'data'>;\nconst cursorOpacity = 0.7;\nconst hoverOpacity = 1;\n\nfunction Caret({ caretPosition, data }: CaretProps) {\n const [isHover, setIsHover] = useState(false);\n\n const handleMouseEnter = () => {\n setIsHover(true);\n };\n const handleMouseLeave = () => {\n setIsHover(false);\n };\n const caretStyle: CSSProperties = {\n ...caretPosition,\n background: data?.color,\n opacity: cursorOpacity,\n transition: 'opacity 0.2s',\n };\n const caretStyleHover = { ...caretStyle, opacity: hoverOpacity };\n\n const labelStyle: CSSProperties = {\n background: data?.color,\n opacity: cursorOpacity,\n transform: 'translateY(-100%)',\n transition: 'opacity 0.2s',\n };\n const labelStyleHover = { ...labelStyle, opacity: hoverOpacity };\n\n return (\n \n \n {data?.name}\n
\n \n );\n}\n\nfunction RemoteSelection({\n caretPosition,\n data,\n selectionRects,\n}: CursorOverlayData) {\n if (!data) {\n return null;\n }\n\n const selectionStyle: CSSProperties = {\n // Add a opacity to the background color\n backgroundColor: addAlpha(data.color, 0.5),\n };\n\n return (\n \n {selectionRects.map((position, i) => (\n \n ))}\n {caretPosition && }\n \n );\n}\n\ntype RemoteCursorsProps = PropsWithChildren<{\n className?: string;\n}>;\n\nexport function RemoteCursorOverlay({ children }: RemoteCursorsProps) {\n const containerRef = useEditorContainerRef();\n const [cursors] = useRemoteCursorOverlayPositions({\n containerRef,\n });\n\n return (\n <>\n {children}\n {cursors.map((cursor) => (\n \n ))}\n \n );\n}\n", + "path": "plate-ui/remote-cursor-overlay.tsx", + "target": "components/plate-ui/remote-cursor-overlay.tsx", + "type": "registry:ui" + } + ], + "name": "remote-cursor-overlay", + "registryDependencies": [], + "type": "registry:ui" +} \ No newline at end of file diff --git a/apps/www/src/__registry__/index.tsx b/apps/www/src/__registry__/index.tsx index 911bd6352e..363f673908 100644 --- a/apps/www/src/__registry__/index.tsx +++ b/apps/www/src/__registry__/index.tsx @@ -877,6 +877,22 @@ export const Index: Record = { subcategory: "", chunks: [] }, + "remote-cursor-overlay": { + name: "remote-cursor-overlay", + description: "", + type: "registry:ui", + registryDependencies: [], + files: [{ + path: "src/registry/default/plate-ui/remote-cursor-overlay.tsx", + type: "registry:ui", + target: "" + }], + component: React.lazy(() => import("@/registry/default/plate-ui/remote-cursor-overlay.tsx")), + source: "", + category: "", + subcategory: "", + chunks: [] + }, "ai-menu": { name: "ai-menu", description: "", diff --git a/apps/www/src/registry/default/plate-ui/remote-cursor-overlay.tsx b/apps/www/src/registry/default/plate-ui/remote-cursor-overlay.tsx index d56f96573f..e5babeb8cd 100644 --- a/apps/www/src/registry/default/plate-ui/remote-cursor-overlay.tsx +++ b/apps/www/src/registry/default/plate-ui/remote-cursor-overlay.tsx @@ -3,7 +3,6 @@ import React, { type CSSProperties, type PropsWithChildren, - useRef, useState, } from 'react'; @@ -11,8 +10,7 @@ import { type CursorOverlayData, useRemoteCursorOverlayPositions, } from '@slate-yjs/react'; - -import { cn } from '@udecode/cn'; +import { useEditorContainerRef } from '@udecode/plate-core/react'; export function addAlpha(hexColor: string, opacity: number): string { const normalized = Math.round(Math.min(Math.max(opacity, 0), 1) * 255); @@ -103,21 +101,18 @@ type RemoteCursorsProps = PropsWithChildren<{ className?: string; }>; -export function RemoteCursorOverlay({ - children, - className, -}: RemoteCursorsProps) { - const containerRef = useRef(null); +export function RemoteCursorOverlay({ children }: RemoteCursorsProps) { + const containerRef = useEditorContainerRef(); const [cursors] = useRemoteCursorOverlayPositions({ containerRef, }); return ( -
+ <> {children} {cursors.map((cursor) => ( ))} -
+ ); } diff --git a/apps/www/src/registry/registry-ui.ts b/apps/www/src/registry/registry-ui.ts index 0493161d8e..da247fa870 100644 --- a/apps/www/src/registry/registry-ui.ts +++ b/apps/www/src/registry/registry-ui.ts @@ -1723,6 +1723,21 @@ export const uiPrimitives: Registry = [ registryDependencies: [], type: 'registry:ui', }, + { + dependencies: ['@slate-yjs/react', '@udecode/plate-core'], + doc: { + description: + 'A cursor overlay to display multiplayer cursors in the yjs plugin.', + docs: [{ route: '/docs/collaboration' }], + examples: [], + }, + files: [ + { path: 'plate-ui/remote-cursor-overlay.tsx', type: 'registry:ui' }, + ], + name: 'remote-cursor-overlay', + registryDependencies: [], + type: 'registry:ui', + }, ]; export const ui: Registry = [...uiNodes, ...uiPrimitives, ...uiComponents]; diff --git a/packages/yjs/src/react/YjsAboveEditable.tsx b/packages/yjs/src/react/YjsAboveEditable.tsx index 537f3b09b0..140aadc7dc 100644 --- a/packages/yjs/src/react/YjsAboveEditable.tsx +++ b/packages/yjs/src/react/YjsAboveEditable.tsx @@ -4,7 +4,6 @@ import { YjsEditor } from '@slate-yjs/core'; import { useEditorPlugin } from '@udecode/plate-common/react'; import { type YjsConfig, BaseYjsPlugin } from '../lib/BaseYjsPlugin'; -import { RemoteCursorOverlay } from './Overlay'; export const YjsAboveEditable: React.FC<{ children: React.ReactNode; @@ -29,5 +28,5 @@ export const YjsAboveEditable: React.FC<{ if (!isSynced) return null; - return {children}; + return <>{children}; }; diff --git a/packages/yjs/src/react/YjsPlugin.tsx b/packages/yjs/src/react/YjsPlugin.tsx index 53b44a76b6..3525139bed 100644 --- a/packages/yjs/src/react/YjsPlugin.tsx +++ b/packages/yjs/src/react/YjsPlugin.tsx @@ -5,5 +5,7 @@ import { YjsAboveEditable } from './YjsAboveEditable'; /** Enables support for real-time collaboration using Yjs. */ export const YjsPlugin = toPlatePlugin(BaseYjsPlugin, { - render: { aboveEditable: YjsAboveEditable }, + render: { + aboveEditable: YjsAboveEditable, + }, }); From f623147a24c97ef7ef7836a0191163e5e707dd10 Mon Sep 17 00:00:00 2001 From: "David P. Novakovic" Date: Fri, 6 Dec 2024 19:42:13 +1000 Subject: [PATCH 05/11] fix: remove deps that are listed in the registry now. --- packages/yjs/package.json | 2 -- yarn.lock | 18 ------------------ 2 files changed, 20 deletions(-) diff --git a/packages/yjs/package.json b/packages/yjs/package.json index 9cc35e64b2..a2a8240fa0 100644 --- a/packages/yjs/package.json +++ b/packages/yjs/package.json @@ -53,8 +53,6 @@ "dependencies": { "@hocuspocus/provider": "^2.13.5", "@slate-yjs/core": "^1.0.2", - "@slate-yjs/react": "1.1.0", - "@udecode/cn": "workspace:^", "yjs": "^13.6.19" }, "devDependencies": { diff --git a/yarn.lock b/yarn.lock index dc89d31c0d..b513c71a5f 100644 --- a/yarn.lock +++ b/yarn.lock @@ -5162,22 +5162,6 @@ __metadata: languageName: node linkType: hard -"@slate-yjs/react@npm:1.1.0": - version: 1.1.0 - resolution: "@slate-yjs/react@npm:1.1.0" - dependencies: - use-sync-external-store: "npm:^1.2.0" - y-protocols: "npm:^1.0.5" - peerDependencies: - "@slate-yjs/core": ^1.0.0 - react: ">=16.8.0" - slate: ">=0.70.0" - slate-react: ">=0.70.0" - yjs: ^13.5.29 - checksum: 10c0/5fc3b49ec209f175bb9d11c206f76c792c0fcb234bf223082497e267658edaa97a530295fb396af63aae9a930bad8e6e761ac7bea63ee1c91d632a981b287506 - languageName: node - linkType: hard - "@socket.io/component-emitter@npm:~3.1.0": version: 3.1.2 resolution: "@socket.io/component-emitter@npm:3.1.2" @@ -7469,8 +7453,6 @@ __metadata: dependencies: "@hocuspocus/provider": "npm:^2.13.5" "@slate-yjs/core": "npm:^1.0.2" - "@slate-yjs/react": "npm:1.1.0" - "@udecode/cn": "workspace:^" "@udecode/plate-common": "workspace:^" yjs: "npm:^13.6.19" peerDependencies: From ddca8d187965b82e6b369faa034cf0d995137a22 Mon Sep 17 00:00:00 2001 From: David Novakovic Date: Fri, 6 Dec 2024 19:54:37 +1000 Subject: [PATCH 06/11] Update apps/www/src/registry/registry-ui.ts Co-authored-by: Ziad Beyens --- apps/www/src/registry/registry-ui.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/www/src/registry/registry-ui.ts b/apps/www/src/registry/registry-ui.ts index da247fa870..20158398fc 100644 --- a/apps/www/src/registry/registry-ui.ts +++ b/apps/www/src/registry/registry-ui.ts @@ -1724,7 +1724,7 @@ export const uiPrimitives: Registry = [ type: 'registry:ui', }, { - dependencies: ['@slate-yjs/react', '@udecode/plate-core'], + dependencies: ['@slate-yjs/react'], doc: { description: 'A cursor overlay to display multiplayer cursors in the yjs plugin.', From 1eeb103f07447785c3f4df6fa569386a9badf8e8 Mon Sep 17 00:00:00 2001 From: "David P. Novakovic" Date: Fri, 6 Dec 2024 19:58:24 +1000 Subject: [PATCH 07/11] review: tidy based on review --- .changeset/odd-radios-smoke.md | 5 ----- .../registry/default/plate-ui/remote-cursor-overlay.tsx | 7 +------ 2 files changed, 1 insertion(+), 11 deletions(-) delete mode 100644 .changeset/odd-radios-smoke.md diff --git a/.changeset/odd-radios-smoke.md b/.changeset/odd-radios-smoke.md deleted file mode 100644 index cd58f7aef0..0000000000 --- a/.changeset/odd-radios-smoke.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@udecode/plate-yjs': patch ---- - -Add a small component to show remote cursors. diff --git a/apps/www/src/registry/default/plate-ui/remote-cursor-overlay.tsx b/apps/www/src/registry/default/plate-ui/remote-cursor-overlay.tsx index e5babeb8cd..fff8b58140 100644 --- a/apps/www/src/registry/default/plate-ui/remote-cursor-overlay.tsx +++ b/apps/www/src/registry/default/plate-ui/remote-cursor-overlay.tsx @@ -97,11 +97,7 @@ function RemoteSelection({ ); } -type RemoteCursorsProps = PropsWithChildren<{ - className?: string; -}>; - -export function RemoteCursorOverlay({ children }: RemoteCursorsProps) { +export function RemoteCursorOverlay() { const containerRef = useEditorContainerRef(); const [cursors] = useRemoteCursorOverlayPositions({ containerRef, @@ -109,7 +105,6 @@ export function RemoteCursorOverlay({ children }: RemoteCursorsProps) { return ( <> - {children} {cursors.map((cursor) => ( ))} From 8aec824b2bf3d64babdba65ef8e9e62ef74de05a Mon Sep 17 00:00:00 2001 From: "David P. Novakovic" Date: Fri, 6 Dec 2024 20:00:25 +1000 Subject: [PATCH 08/11] fix: remove redundant import --- .../src/registry/default/plate-ui/remote-cursor-overlay.tsx | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/apps/www/src/registry/default/plate-ui/remote-cursor-overlay.tsx b/apps/www/src/registry/default/plate-ui/remote-cursor-overlay.tsx index fff8b58140..acfe6eedf1 100644 --- a/apps/www/src/registry/default/plate-ui/remote-cursor-overlay.tsx +++ b/apps/www/src/registry/default/plate-ui/remote-cursor-overlay.tsx @@ -1,10 +1,6 @@ // Lifted from slate-yjs https://github.com/BitPhinix/slate-yjs/blob/main/examples/frontend/src/pages/RemoteCursorOverlay/Overlay.tsx -import React, { - type CSSProperties, - type PropsWithChildren, - useState, -} from 'react'; +import React, { type CSSProperties, useState } from 'react'; import { type CursorOverlayData, From 0d945e8f4521f9f37aa40446368ae5cc34f5f926 Mon Sep 17 00:00:00 2001 From: "David P. Novakovic" Date: Fri, 6 Dec 2024 20:05:05 +1000 Subject: [PATCH 09/11] chore: bring brl into sync --- packages/markdown/src/lib/remark-slate/index.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/packages/markdown/src/lib/remark-slate/index.ts b/packages/markdown/src/lib/remark-slate/index.ts index 6271256236..9350f682b1 100644 --- a/packages/markdown/src/lib/remark-slate/index.ts +++ b/packages/markdown/src/lib/remark-slate/index.ts @@ -2,10 +2,11 @@ * @file Automatically generated by barrelsby. */ +export * from './remarkDefaultCompiler'; export * from './remarkDefaultElementRules'; export * from './remarkDefaultTextRules'; -export * from './remarkSplitLineBreaksCompiler'; export * from './remarkPlugin'; +export * from './remarkSplitLineBreaksCompiler'; export * from './remarkTextTypes'; export * from './remarkTransformElement'; export * from './remarkTransformElementChildren'; From d8283388bf2cf8f944b98c455be9b4c71d624d5c Mon Sep 17 00:00:00 2001 From: David Novakovic Date: Fri, 6 Dec 2024 20:07:10 +1000 Subject: [PATCH 10/11] Update apps/www/src/registry/default/plate-ui/remote-cursor-overlay.tsx Co-authored-by: Ziad Beyens --- .../www/src/registry/default/plate-ui/remote-cursor-overlay.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/www/src/registry/default/plate-ui/remote-cursor-overlay.tsx b/apps/www/src/registry/default/plate-ui/remote-cursor-overlay.tsx index acfe6eedf1..6055cc6dbb 100644 --- a/apps/www/src/registry/default/plate-ui/remote-cursor-overlay.tsx +++ b/apps/www/src/registry/default/plate-ui/remote-cursor-overlay.tsx @@ -6,7 +6,7 @@ import { type CursorOverlayData, useRemoteCursorOverlayPositions, } from '@slate-yjs/react'; -import { useEditorContainerRef } from '@udecode/plate-core/react'; +import { useEditorContainerRef } from '@udecode/plate-common/react'; export function addAlpha(hexColor: string, opacity: number): string { const normalized = Math.round(Math.min(Math.max(opacity, 0), 1) * 255); From 88236a242440a79a66383e46e8bf626785ad6a15 Mon Sep 17 00:00:00 2001 From: "David P. Novakovic" Date: Fri, 6 Dec 2024 20:37:51 +1000 Subject: [PATCH 11/11] fix: cursor overlay is strictly client side --- apps/www/package.json | 1 + .../default/plate-ui/remote-cursor-overlay.tsx | 1 + yarn.lock | 17 +++++++++++++++++ 3 files changed, 19 insertions(+) diff --git a/apps/www/package.json b/apps/www/package.json index 4f73ba3fc1..a2a303c607 100644 --- a/apps/www/package.json +++ b/apps/www/package.json @@ -71,6 +71,7 @@ "@radix-ui/react-toggle-group": "^1.1.0", "@radix-ui/react-toolbar": "^1.1.0", "@radix-ui/react-tooltip": "^1.1.2", + "@slate-yjs/react": "1.1.0", "@udecode/cmdk": "workspace:^", "@udecode/cn": "workspace:^", "@udecode/plate": "workspace:^", diff --git a/apps/www/src/registry/default/plate-ui/remote-cursor-overlay.tsx b/apps/www/src/registry/default/plate-ui/remote-cursor-overlay.tsx index acfe6eedf1..ce1ddb9b59 100644 --- a/apps/www/src/registry/default/plate-ui/remote-cursor-overlay.tsx +++ b/apps/www/src/registry/default/plate-ui/remote-cursor-overlay.tsx @@ -1,3 +1,4 @@ +'use client'; // Lifted from slate-yjs https://github.com/BitPhinix/slate-yjs/blob/main/examples/frontend/src/pages/RemoteCursorOverlay/Overlay.tsx import React, { type CSSProperties, useState } from 'react'; diff --git a/yarn.lock b/yarn.lock index b513c71a5f..b115033ec6 100644 --- a/yarn.lock +++ b/yarn.lock @@ -5162,6 +5162,22 @@ __metadata: languageName: node linkType: hard +"@slate-yjs/react@npm:1.1.0": + version: 1.1.0 + resolution: "@slate-yjs/react@npm:1.1.0" + dependencies: + use-sync-external-store: "npm:^1.2.0" + y-protocols: "npm:^1.0.5" + peerDependencies: + "@slate-yjs/core": ^1.0.0 + react: ">=16.8.0" + slate: ">=0.70.0" + slate-react: ">=0.70.0" + yjs: ^13.5.29 + checksum: 10c0/5fc3b49ec209f175bb9d11c206f76c792c0fcb234bf223082497e267658edaa97a530295fb396af63aae9a930bad8e6e761ac7bea63ee1c91d632a981b287506 + languageName: node + linkType: hard + "@socket.io/component-emitter@npm:~3.1.0": version: 3.1.2 resolution: "@socket.io/component-emitter@npm:3.1.2" @@ -22473,6 +22489,7 @@ __metadata: "@radix-ui/react-toolbar": "npm:^1.1.0" "@radix-ui/react-tooltip": "npm:^1.1.2" "@shikijs/compat": "npm:^1.17.5" + "@slate-yjs/react": "npm:1.1.0" "@types/lodash.template": "npm:^4.5.3" "@types/react-color": "npm:^3.0.12" "@types/react-syntax-highlighter": "npm:^15.5.13"