From ae05150583b0ca5e1df8894acdc293eeae7a00f6 Mon Sep 17 00:00:00 2001 From: Nick the Sick Date: Tue, 12 May 2026 15:28:23 +0200 Subject: [PATCH] fix: add `bn-thread-orphaned` CSS class to distinguish orphaned threads in DOM When a thread's referenced text is deleted from the document, the thread becomes orphaned. This adds a `bn-thread-orphaned` CSS class to the Thread component's card element so consumers can target these threads with custom styling or behavior. Closes #2735 --- .../react/src/components/Comments/Thread.tsx | 9 ++++++- .../components/Comments/ThreadsSidebar.tsx | 25 ++++++++++++------- 2 files changed, 24 insertions(+), 10 deletions(-) diff --git a/packages/react/src/components/Comments/Thread.tsx b/packages/react/src/components/Comments/Thread.tsx index a1da1484e6..c666cd33e2 100644 --- a/packages/react/src/components/Comments/Thread.tsx +++ b/packages/react/src/components/Comments/Thread.tsx @@ -22,6 +22,12 @@ export type ThreadProps = { * editor for replies, and add a `selected` CSS class to the thread. */ selected?: boolean; + /** + * A boolean flag for whether the thread is orphaned (i.e. the referenced text + * has been deleted from the document). Adds a `bn-thread-orphaned` CSS class + * to the thread. + */ + orphaned?: boolean; /** * The text in the editor that the thread refers to. See the * [`ThreadsSidebar`](https://github.com/TypeCellOS/BlockNote/tree/main/packages/react/src/components/Comments/ThreadsSidebar.tsx#L137) @@ -55,6 +61,7 @@ export type ThreadProps = { export const Thread = ({ thread, selected, + orphaned, referenceText, maxCommentsBeforeCollapse, onFocus, @@ -94,7 +101,7 @@ export const Thread = ({ return ( ; maxCommentsBeforeCollapse?: number; referenceText: string; + orphaned?: boolean; }; /** @@ -25,6 +26,7 @@ const ThreadItem = React.memo( selectedThreadId, maxCommentsBeforeCollapse, referenceText, + orphaned, }: ThreadItemProps) => { const comments = useExtension(CommentsExtension); @@ -76,6 +78,7 @@ const ThreadItem = React.memo( = []; + const ret: Array<{ + thread: ThreadData; + referenceText: string; + orphaned: boolean; + }> = []; for (const thread of sortedThreads) { + const threadPosition = threadPositions.get(thread.id); + const orphaned = threadPosition === undefined; + if (!thread.resolved) { if (props.filter === "open" || props.filter === "all") { ret.push({ thread, - referenceText: getReferenceText( - editor, - threadPositions.get(thread.id), - ), + referenceText: getReferenceText(editor, threadPosition), + orphaned, }); } } else { if (props.filter === "resolved" || props.filter === "all") { ret.push({ thread, - referenceText: getReferenceText( - editor, - threadPositions.get(thread.id), - ), + referenceText: getReferenceText(editor, threadPosition), + orphaned, }); } } @@ -244,6 +250,7 @@ export function ThreadsSidebar(props: { selectedThreadId={selectedThreadId} editor={editor} referenceText={thread.referenceText} + orphaned={thread.orphaned} maxCommentsBeforeCollapse={props.maxCommentsBeforeCollapse} /> ))}