diff --git a/docs/pages/docs/collaboration/comments.mdx b/docs/pages/docs/collaboration/comments.mdx index 38de97cf9d..803611e3df 100644 --- a/docs/pages/docs/collaboration/comments.mdx +++ b/docs/pages/docs/collaboration/comments.mdx @@ -4,7 +4,7 @@ description: Learn how to enable comments in your BlockNote editor imageTitle: Comments --- -import { Example } from "@/components/example"; +import {Example} from "@/components/example"; # Comments @@ -33,7 +33,7 @@ const editor = useCreateBlockNote({ **Demo** - + ## ThreadStores @@ -63,7 +63,10 @@ The `RESTYjsThreadStore` combines Yjs storage with a REST API backend, providing In this implementation, data is written to the Yjs document via a REST API which can handle access control. Data is still retrieved from the Yjs document directly (after it's been updated by the REST API), this way all comment information automatically syncs between clients using the existing collaboration provider. ```tsx -import { RESTYjsThreadStore, DefaultThreadStoreAuth } from "@blocknote/core/comments"; +import { + RESTYjsThreadStore, + DefaultThreadStoreAuth, +} from "@blocknote/core/comments"; const threadStore = new RESTYjsThreadStore( "https://api.example.com/comments", // Base URL for the REST API @@ -84,7 +87,10 @@ _Note: Because writes are executed via a REST API, the `RESTYjsThreadStore` is n The `TiptapThreadStore` integrates with Tiptap's collaboration provider for comment management. This implementation is designed specifically for use with Tiptap's collaborative editing features. ```tsx -import { TiptapThreadStore, DefaultThreadStoreAuth } from "@blocknote/core/comments"; +import { + TiptapThreadStore, + DefaultThreadStoreAuth, +} from "@blocknote/core/comments"; import { TiptapCollabProvider } from "@hocuspocus/provider"; // Create a TiptapCollabProvider (you probably have this already) @@ -130,9 +136,27 @@ async function myResolveUsers(userIds: string[]): Promise { // fetch user information from your database / backend // and return an array of User objects - return await callYourBackend(userIds); // + return await callYourBackend(userIds); // Return a list of users return users; } ``` + +## Sidebar View + +BlockNote also offers a different way of viewing and interacting with comments, via a sidebar instead of floating in the editor, using the `ThreadsSidebar` component: + + + +The only requirement for `ThreadsSidebar` is that it should be placed somewhere within your `BlockNoteView`, other than that you can position and style it however you want. + +`ThreadsSidebar` also takes 2 props: + +**`filter`**: Filter the comments in the sidebar. Can pass `"open"`, `"resolved"`, or `"all"`, to only show open, resolved, or all comments. Defaults to `"all"`. + +**`sort`**: Sort the comments in the sidebar. Can pass `"position"`, `"recent-activity"`, or `"oldest"`. Sorting by `"recent-activity"` uses the most recently added comment to sort threads, while `"oldest"` uses the thread creation date. Sorting by `"position"` puts comments in the same order as their reference text in the editor. Defaults to `"position"`. + +**`maxCommentsBeforeCollapse`**: The maximum number of comments that can be in a thread before the replies get collapsed. Defaults to 5. + +See [here](https://playground.blocknotejs.org/collaboration/comments-with-sidebar?hideMenu=true) for a standalone example of the `ThreadsSidebar` component. diff --git a/examples/03-ui-components/02-formatting-toolbar-buttons/App.tsx b/examples/03-ui-components/02-formatting-toolbar-buttons/App.tsx index 92c4b48073..aa8e5b9f92 100644 --- a/examples/03-ui-components/02-formatting-toolbar-buttons/App.tsx +++ b/examples/03-ui-components/02-formatting-toolbar-buttons/App.tsx @@ -16,7 +16,7 @@ import { useCreateBlockNote, } from "@blocknote/react"; -import { BlueButton } from "./BlueButton"; +import { BlueButton } from "./BlueButton.js"; export default function App() { // Creates a new editor instance. diff --git a/examples/03-ui-components/04-side-menu-buttons/App.tsx b/examples/03-ui-components/04-side-menu-buttons/App.tsx index 8661c08baa..445b2daba9 100644 --- a/examples/03-ui-components/04-side-menu-buttons/App.tsx +++ b/examples/03-ui-components/04-side-menu-buttons/App.tsx @@ -8,7 +8,7 @@ import { useCreateBlockNote, } from "@blocknote/react"; -import { RemoveBlockButton } from "./RemoveBlockButton"; +import { RemoveBlockButton } from "./RemoveBlockButton.js"; export default function App() { // Creates a new editor instance. diff --git a/examples/03-ui-components/05-side-menu-drag-handle-items/App.tsx b/examples/03-ui-components/05-side-menu-drag-handle-items/App.tsx index 80733a2c9b..a923e77c64 100644 --- a/examples/03-ui-components/05-side-menu-drag-handle-items/App.tsx +++ b/examples/03-ui-components/05-side-menu-drag-handle-items/App.tsx @@ -10,7 +10,7 @@ import { useCreateBlockNote, } from "@blocknote/react"; -import { ResetBlockTypeItem } from "./ResetBlockTypeItem"; +import { ResetBlockTypeItem } from "./ResetBlockTypeItem.js"; export default function App() { // Creates a new editor instance. diff --git a/examples/03-ui-components/10-suggestion-menus-grid-mentions/App.tsx b/examples/03-ui-components/10-suggestion-menus-grid-mentions/App.tsx index c0d9f8d05f..49749a8c49 100644 --- a/examples/03-ui-components/10-suggestion-menus-grid-mentions/App.tsx +++ b/examples/03-ui-components/10-suggestion-menus-grid-mentions/App.tsx @@ -12,7 +12,7 @@ import { useCreateBlockNote, } from "@blocknote/react"; -import { Mention } from "./Mention"; +import { Mention } from "./Mention.js"; // Our schema with inline content specs, which contain the configs and // implementations for inline content that we want our editor to use. diff --git a/examples/03-ui-components/11-uppy-file-panel/App.tsx b/examples/03-ui-components/11-uppy-file-panel/App.tsx index d94aaa2def..00962f1b1f 100644 --- a/examples/03-ui-components/11-uppy-file-panel/App.tsx +++ b/examples/03-ui-components/11-uppy-file-panel/App.tsx @@ -9,8 +9,8 @@ import { useCreateBlockNote, } from "@blocknote/react"; -import { FileReplaceButton } from "./FileReplaceButton"; -import { uploadFile, UppyFilePanel } from "./UppyFilePanel"; +import { FileReplaceButton } from "./FileReplaceButton.js"; +import { uploadFile, UppyFilePanel } from "./UppyFilePanel.js"; export default function App() { // Creates a new editor instance. diff --git a/examples/03-ui-components/11-uppy-file-panel/FileReplaceButton.tsx b/examples/03-ui-components/11-uppy-file-panel/FileReplaceButton.tsx index d1f1444469..a3f48d96b6 100644 --- a/examples/03-ui-components/11-uppy-file-panel/FileReplaceButton.tsx +++ b/examples/03-ui-components/11-uppy-file-panel/FileReplaceButton.tsx @@ -13,7 +13,8 @@ import { import { useEffect, useState } from "react"; import { RiImageEditFill } from "react-icons/ri"; -import { UppyFilePanel } from "./UppyFilePanel"; + +import { UppyFilePanel } from "./UppyFilePanel.js"; // Copied with minor changes from: // https://github.com/TypeCellOS/BlockNote/blob/main/packages/react/src/components/FormattingToolbar/DefaultButtons/FileReplaceButton.tsx diff --git a/examples/03-ui-components/13-custom-ui/App.tsx b/examples/03-ui-components/13-custom-ui/App.tsx index f2892ae0d4..29e5e2ded8 100644 --- a/examples/03-ui-components/13-custom-ui/App.tsx +++ b/examples/03-ui-components/13-custom-ui/App.tsx @@ -11,10 +11,10 @@ import "@blocknote/react/style.css"; import { createTheme, ThemeProvider, useMediaQuery } from "@mui/material"; import { useMemo } from "react"; -import { schema } from "./schema"; -import { CustomMUIFormattingToolbar } from "./MUIFormattingToolbar"; -import { CustomMUISideMenu } from "./MUISideMenu"; -import { MUISuggestionMenu } from "./MUISuggestionMenu"; +import { schema } from "./schema.js"; +import { CustomMUIFormattingToolbar } from "./MUIFormattingToolbar.js"; +import { CustomMUISideMenu } from "./MUISideMenu.js"; +import { MUISuggestionMenu } from "./MUISuggestionMenu.js"; import "./style.css"; diff --git a/examples/03-ui-components/13-custom-ui/MUIFormattingToolbar.tsx b/examples/03-ui-components/13-custom-ui/MUIFormattingToolbar.tsx index 4c464ebd56..7c4cabc3d3 100644 --- a/examples/03-ui-components/13-custom-ui/MUIFormattingToolbar.tsx +++ b/examples/03-ui-components/13-custom-ui/MUIFormattingToolbar.tsx @@ -42,7 +42,7 @@ import { ReactNode, } from "react"; -import { TextBlockSchema } from "./schema"; +import { TextBlockSchema } from "./schema.js"; // This replaces the generic Mantine `ToolbarSelect` component with a simplified // MUI version: diff --git a/examples/03-ui-components/13-custom-ui/MUISideMenu.tsx b/examples/03-ui-components/13-custom-ui/MUISideMenu.tsx index 60b53e48ba..251b409d58 100644 --- a/examples/03-ui-components/13-custom-ui/MUISideMenu.tsx +++ b/examples/03-ui-components/13-custom-ui/MUISideMenu.tsx @@ -11,7 +11,7 @@ import { } from "@mui/material"; import { MouseEvent, ReactNode, useCallback, useMemo, useState } from "react"; -import { TextBlockSchema } from "./schema"; +import { TextBlockSchema } from "./schema.js"; // This replaces the default `RemoveBlockItem` component with a simplified // MUI version: diff --git a/examples/03-ui-components/13-custom-ui/MUISuggestionMenu.tsx b/examples/03-ui-components/13-custom-ui/MUISuggestionMenu.tsx index d677bfe155..9b8aed593c 100644 --- a/examples/03-ui-components/13-custom-ui/MUISuggestionMenu.tsx +++ b/examples/03-ui-components/13-custom-ui/MUISuggestionMenu.tsx @@ -16,7 +16,7 @@ import { } from "@mui/material"; import { useEffect, useMemo, useRef } from "react"; -import { TextBlockSchema } from "./schema"; +import { TextBlockSchema } from "./schema.js"; // If you want to change the items in a Suggestion Menu, like the Slash Menu, // you don't need to modify any of the components in this file. Instead, you diff --git a/examples/03-ui-components/link-toolbar-buttons/App.tsx b/examples/03-ui-components/link-toolbar-buttons/App.tsx index 23e9902f85..27a3fdb1e8 100644 --- a/examples/03-ui-components/link-toolbar-buttons/App.tsx +++ b/examples/03-ui-components/link-toolbar-buttons/App.tsx @@ -7,7 +7,7 @@ import { useCreateBlockNote, } from "@blocknote/react"; -import { AlertButton } from "./AlertButton"; +import { AlertButton } from "./AlertButton.js"; export default function App() { // Creates a new editor instance. diff --git a/examples/06-custom-schema/01-alert-block/App.tsx b/examples/06-custom-schema/01-alert-block/App.tsx index 6d58d740ed..4582849798 100644 --- a/examples/06-custom-schema/01-alert-block/App.tsx +++ b/examples/06-custom-schema/01-alert-block/App.tsx @@ -14,7 +14,7 @@ import { } from "@blocknote/react"; import { RiAlertFill } from "react-icons/ri"; -import { Alert } from "./Alert"; +import { Alert } from "./Alert.js"; // Our schema with block specs, which contain the configs and implementations for blocks // that we want our editor to use. diff --git a/examples/06-custom-schema/02-suggestion-menus-mentions/App.tsx b/examples/06-custom-schema/02-suggestion-menus-mentions/App.tsx index c1fec94a4f..16d93cdeb9 100644 --- a/examples/06-custom-schema/02-suggestion-menus-mentions/App.tsx +++ b/examples/06-custom-schema/02-suggestion-menus-mentions/App.tsx @@ -12,7 +12,7 @@ import { useCreateBlockNote, } from "@blocknote/react"; -import { Mention } from "./Mention"; +import { Mention } from "./Mention.js"; // Our schema with inline content specs, which contain the configs and // implementations for inline content that we want our editor to use. diff --git a/examples/06-custom-schema/03-font-style/App.tsx b/examples/06-custom-schema/03-font-style/App.tsx index 4cae9935b1..ecd08a6846 100644 --- a/examples/06-custom-schema/03-font-style/App.tsx +++ b/examples/06-custom-schema/03-font-style/App.tsx @@ -21,7 +21,7 @@ import { import { RiText } from "react-icons/ri"; -import { Font } from "./Font"; +import { Font } from "./Font.js"; // Our schema with style specs, which contain the configs and implementations for styles // that we want our editor to use. diff --git a/examples/06-custom-schema/04-pdf-file-block/App.tsx b/examples/06-custom-schema/04-pdf-file-block/App.tsx index 1c3129e3bc..327ff9fc8b 100644 --- a/examples/06-custom-schema/04-pdf-file-block/App.tsx +++ b/examples/06-custom-schema/04-pdf-file-block/App.tsx @@ -15,7 +15,7 @@ import { import { RiFilePdfFill } from "react-icons/ri"; -import { PDF } from "./PDF"; +import { PDF } from "./PDF.js"; // Our schema with block specs, which contain the configs and implementations for blocks // that we want our editor to use. diff --git a/examples/07-collaboration/04-comments/App.tsx b/examples/07-collaboration/04-comments/App.tsx index 6f8b921ea5..665d243a87 100644 --- a/examples/07-collaboration/04-comments/App.tsx +++ b/examples/07-collaboration/04-comments/App.tsx @@ -7,11 +7,14 @@ import { import { BlockNoteView } from "@blocknote/mantine"; import "@blocknote/mantine/style.css"; import { useCreateBlockNote } from "@blocknote/react"; -import { MantineProvider, Select } from "@mantine/core"; import { YDocProvider, useYDoc, useYjsProvider } from "@y-sweet/react"; import { useMemo, useState } from "react"; + +import { SettingsSelect } from "./SettingsSelect.js"; import { HARDCODED_USERS, MyUserType, getRandomColor } from "./userdata.js"; +import "./style.css"; + // The resolveUsers function fetches information about your users // (e.g. their name, avatar, etc.). Usually, you'd fetch this from your // own database or user management system. @@ -27,21 +30,20 @@ async function resolveUsers(userIds: string[]) { // (but of course, you also use other collaboration providers // see the docs for more information) export default function App() { - const docId = "my-blocknote-document-with-comments"; + const docId = "my-blocknote-document-with-comments-1"; return ( - - - - - + + + ); } function Document() { - const [user, setUser] = useState(HARDCODED_USERS[0]); + const [activeUser, setActiveUser] = useState(HARDCODED_USERS[0]); + const provider = useYjsProvider(); // take the Y.Doc collaborative document from Y-Sweet @@ -57,16 +59,16 @@ function Document() { // document: doc, // }); // return new TiptapThreadStore( - // user.id, + // activeUser.id, // provider, - // new DefaultThreadStoreAuth(user.id, user.role) + // new DefaultThreadStoreAuth(activeUser.id, activeUser.role) // ); return new YjsThreadStore( - user.id, + activeUser.id, doc.getMap("threads"), - new DefaultThreadStoreAuth(user.id, user.role) + new DefaultThreadStoreAuth(activeUser.id, activeUser.role) ); - }, [doc, user]); + }, [doc, activeUser]); // setup the editor with comments and collaboration const editor = useCreateBlockNote( @@ -78,34 +80,32 @@ function Document() { collaboration: { provider, fragment: doc.getXmlFragment("blocknote"), - user: { color: getRandomColor(), name: user.username }, + user: { color: getRandomColor(), name: activeUser.username }, }, }, - [user, threadStore] + [activeUser, threadStore] ); return ( -
- {/* This is a simple user selector to switch between users, for demo purposes */} -