@liveblocks/react-tiptap 3.15.0-feeds1 → 3.15.0-rc1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,9 +1,9 @@
1
1
  'use strict';
2
2
 
3
3
  var jsxRuntime = require('react/jsx-runtime');
4
- var _private = require('@liveblocks/react/_private');
4
+ var _private$1 = require('@liveblocks/react/_private');
5
5
  var reactUi = require('@liveblocks/react-ui');
6
- var _private$1 = require('@liveblocks/react-ui/_private');
6
+ var _private = require('@liveblocks/react-ui/_private');
7
7
  var react$1 = require('@tiptap/react');
8
8
  var react = require('react');
9
9
  var types = require('../types.cjs');
@@ -21,7 +21,7 @@ function AnchoredThreads({
21
21
  editor,
22
22
  ...props
23
23
  }) {
24
- const Thread = components?.Thread ?? reactUi.Thread;
24
+ const Thread = _private.useStableComponent(components?.Thread, reactUi.Thread);
25
25
  const containerRef = react.useRef(null);
26
26
  const [orderedThreads, setOrderedThreads] = react.useState([]);
27
27
  const [elements, setElements] = react.useState(/* @__PURE__ */ new Map());
@@ -44,8 +44,9 @@ function AnchoredThreads({
44
44
  }) ?? { pluginState: void 0 };
45
45
  const handlePositionThreads = react.useCallback(() => {
46
46
  const container = containerRef.current;
47
- if (container === null || !editor || !editor.view)
47
+ if (container === null || !editor || !editor.view || editor.view.isDestroyed) {
48
48
  return;
49
+ }
49
50
  const activeIndex = orderedThreads.findIndex(
50
51
  ({ thread }) => thread.id === pluginState?.selectedThreadId
51
52
  );
@@ -106,7 +107,7 @@ function AnchoredThreads({
106
107
  );
107
108
  handlePositionThreads();
108
109
  }, [pluginState, threads]);
109
- _private.useLayoutEffect(handlePositionThreads, [handlePositionThreads]);
110
+ _private$1.useLayoutEffect(handlePositionThreads, [handlePositionThreads]);
110
111
  react.useEffect(() => {
111
112
  const observer = new ResizeObserver(handlePositionThreads);
112
113
  const container = editor?.view?.dom;
@@ -142,24 +143,21 @@ function AnchoredThreads({
142
143
  "div",
143
144
  {
144
145
  ...props,
145
- className: _private$1.cn(className, "lb-root lb-tiptap-anchored-threads"),
146
+ className: _private.cn(className, "lb-root lb-tiptap-anchored-threads"),
146
147
  ref: containerRef,
147
148
  style: {
148
149
  position: "relative",
149
150
  ...style
150
151
  },
151
152
  children: orderedThreads.map(({ thread, position }) => {
152
- if (!editor.view) {
153
+ if (!editor.view || editor.view.isDestroyed) {
153
154
  return null;
154
155
  }
155
156
  const coords = editor.view.coordsAtPos(
156
157
  Math.min(position.from, editor.state.doc.content.size - 1)
157
158
  );
158
159
  const rect = utils.getRectFromCoords(coords);
159
- let offset = 0;
160
- if (editor.options.element instanceof HTMLElement) {
161
- offset = editor.options.element.getBoundingClientRect().top;
162
- }
160
+ const offset = editor.view.dom.getBoundingClientRect().top ?? 0;
163
161
  let top = rect.top - offset;
164
162
  if (positions.has(thread.id)) {
165
163
  top = positions.get(thread.id);
@@ -199,7 +197,7 @@ function ThreadWrapper({
199
197
  ...props
200
198
  }) {
201
199
  const divRef = react.useRef(null);
202
- _private.useLayoutEffect(() => {
200
+ _private$1.useLayoutEffect(() => {
203
201
  const el = divRef.current;
204
202
  if (el === null)
205
203
  return;
@@ -215,7 +213,7 @@ function ThreadWrapper({
215
213
  "div",
216
214
  {
217
215
  ref: divRef,
218
- className: _private$1.cn("lb-tiptap-anchored-threads-thread-container", className),
216
+ className: _private.cn("lb-tiptap-anchored-threads-thread-container", className),
219
217
  ...props,
220
218
  children: /* @__PURE__ */ jsxRuntime.jsx(
221
219
  Thread,
@@ -1 +1 @@
1
- {"version":3,"file":"AnchoredThreads.cjs","sources":["../../src/comments/AnchoredThreads.tsx"],"sourcesContent":["import type { BaseMetadata, DCM, DTM, ThreadData } from \"@liveblocks/core\";\nimport { useLayoutEffect } from \"@liveblocks/react/_private\";\nimport {\n Thread as DefaultThread,\n type ThreadProps,\n} from \"@liveblocks/react-ui\";\nimport { cn } from \"@liveblocks/react-ui/_private\";\nimport { type Editor, useEditorState } from \"@tiptap/react\";\nimport type { ComponentPropsWithoutRef, ComponentType } from \"react\";\nimport { useCallback, useEffect, useRef, useState } from \"react\";\n\nimport { THREADS_PLUGIN_KEY } from \"../types\";\nimport { getRectFromCoords } from \"../utils\";\n\nconst DEFAULT_GAP = 20;\nconst DEFAULT_ACTIVE_THREAD_OFFSET = -12;\n\n// TODO: move that back to a variable\nconst GAP = `var(--lb-tiptap-anchored-threads-gap, ${DEFAULT_GAP}px)`;\nconst ACTIVE_THREAD_OFFSET = `var(--lb-tiptap-anchored-threads-active-thread-offset, ${DEFAULT_ACTIVE_THREAD_OFFSET}px)`;\n\ntype AnchoredThreadsComponents = {\n Thread: ComponentType<ThreadProps>;\n};\n\nexport interface AnchoredThreadsProps<\n TM extends BaseMetadata = DTM,\n CM extends BaseMetadata = DCM,\n> extends Omit<ComponentPropsWithoutRef<\"div\">, \"children\"> {\n /**\n * The threads to display.\n */\n threads: ThreadData<TM, CM>[];\n\n /**\n * Override the component's components.\n */\n components?: Partial<AnchoredThreadsComponents>;\n\n /**\n * The Tiptap editor.\n */\n editor: Editor | null;\n}\n\nexport function AnchoredThreads({\n threads,\n components,\n className,\n style,\n editor,\n ...props\n}: AnchoredThreadsProps) {\n const Thread = components?.Thread ?? DefaultThread;\n const containerRef = useRef<HTMLDivElement>(null);\n const [orderedThreads, setOrderedThreads] = useState<\n { position: { from: number; to: number }; thread: ThreadData }[]\n >([]);\n const [elements, setElements] = useState<Map<string, HTMLElement>>(new Map());\n const [positions, setPositions] = useState<Map<string, number>>(new Map()); // A map of thread ids to their 'top' position in the document\n\n const { pluginState } = useEditorState({\n editor,\n selector: (ctx) => {\n if (!ctx?.editor?.state) return { pluginState: undefined };\n const state = THREADS_PLUGIN_KEY.getState(ctx.editor.state);\n return {\n pluginState: state,\n };\n },\n equalityFn: (prev, next) => {\n if (!prev || !next) return false;\n return (\n prev.pluginState?.selectedThreadId ===\n next.pluginState?.selectedThreadId &&\n prev.pluginState?.threadPositions === next.pluginState?.threadPositions\n ); // new map is made each time threadPos updates so shallow equality is fine\n },\n }) ?? { pluginState: undefined };\n\n // TODO: lexical supoprts multiple threads being active, should probably do that here as well\n const handlePositionThreads = useCallback(() => {\n const container = containerRef.current;\n if (container === null || !editor || !editor.view) return;\n\n const activeIndex = orderedThreads.findIndex(\n ({ thread }) => thread.id === pluginState?.selectedThreadId\n );\n const ascending =\n activeIndex !== -1 ? orderedThreads.slice(activeIndex) : orderedThreads;\n const descending =\n activeIndex !== -1 ? orderedThreads.slice(0, activeIndex) : [];\n\n const newPositions = new Map<string, number>();\n\n // Iterate over each thread and calculate its new position by taking into account the position of the previously positioned threads\n for (const { thread, position } of ascending) {\n const coords = editor.view.coordsAtPos(\n Math.min(position.from, editor.view.state.doc.content.size - 1)\n );\n const rect = getRectFromCoords(coords);\n let top = rect.top - container.getBoundingClientRect().top;\n\n for (const [id, position] of newPositions) {\n // Retrieve the element associated with the thread\n const el = elements.get(id);\n if (el === undefined) continue;\n\n if (\n top >= position &&\n top <= position + el.getBoundingClientRect().height\n ) {\n top = position + el.getBoundingClientRect().height;\n }\n }\n\n newPositions.set(thread.id, top);\n }\n\n for (const { thread, position } of descending.reverse()) {\n const coords = editor.view.coordsAtPos(position.from);\n const rect = getRectFromCoords(coords);\n // Retrieve the element associated with the current thread\n const el = elements.get(thread.id);\n if (el === undefined) continue;\n\n let top = rect.top - container.getBoundingClientRect().top;\n for (const [, position] of newPositions) {\n if (top >= position - el.getBoundingClientRect().height) {\n top = position - el.getBoundingClientRect().height;\n }\n }\n\n newPositions.set(thread.id, top);\n }\n\n setPositions(newPositions);\n }, [editor, orderedThreads, pluginState?.selectedThreadId, elements]);\n\n useEffect(() => {\n if (!pluginState) return;\n setOrderedThreads(\n Array.from(pluginState.threadPositions, ([threadId, position]) => ({\n threadId,\n position,\n })).reduce(\n (acc, { threadId, position }) => {\n const thread = threads.find(\n (thread) => thread.id === threadId && !thread.resolved\n );\n if (!thread) return acc;\n acc.push({ thread, position });\n return acc;\n },\n [] as { thread: ThreadData; position: { from: number; to: number } }[]\n )\n );\n handlePositionThreads();\n // disable exhaustive deps because we don't want an infinite loop\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [pluginState, threads]);\n\n useLayoutEffect(handlePositionThreads, [handlePositionThreads]);\n\n useEffect(() => {\n const observer = new ResizeObserver(handlePositionThreads);\n const container = editor?.view?.dom;\n if (container) {\n observer.observe(container);\n }\n for (const element of elements.values()) {\n observer.observe(element);\n }\n\n return () => observer.disconnect();\n }, [elements, editor, handlePositionThreads]);\n\n const onItemAdd = useCallback((id: string, el: HTMLElement) => {\n setElements((prev) => new Map(prev).set(id, el));\n }, []);\n\n const onItemRemove = useCallback((id: string) => {\n setElements((prev) => {\n const items = new Map(prev);\n items.delete(id);\n return items;\n });\n }, []);\n\n const onThreadSelect = useCallback(\n (id: string) => {\n if (!editor) return;\n editor.commands.selectThread(id);\n },\n [editor]\n );\n\n if (!editor) return null;\n\n return (\n <div\n {...props}\n className={cn(className, \"lb-root lb-tiptap-anchored-threads\")}\n ref={containerRef}\n style={{\n position: \"relative\",\n ...style,\n }}\n >\n {orderedThreads.map(({ thread, position }) => {\n // In blocknote, it's possible for this to be undefined\n if (!editor.view) {\n return null;\n }\n const coords = editor.view.coordsAtPos(\n Math.min(position.from, editor.state.doc.content.size - 1)\n );\n const rect = getRectFromCoords(coords);\n let offset = 0;\n if (editor.options.element instanceof HTMLElement) {\n offset = editor.options.element.getBoundingClientRect().top;\n }\n\n let top = rect.top - offset;\n\n if (positions.has(thread.id)) {\n top = positions.get(thread.id)!;\n }\n\n const isActive = thread.id === pluginState?.selectedThreadId;\n\n return (\n <ThreadWrapper\n key={thread.id}\n onThreadClick={onThreadSelect}\n onItemAdd={onItemAdd}\n onItemRemove={onItemRemove}\n Thread={Thread}\n thread={thread}\n isActive={isActive}\n style={{\n position: \"absolute\",\n transform: `translate3d(${isActive ? ACTIVE_THREAD_OFFSET : 0}, ${top}px, 0)`,\n insetInlineStart: 0,\n inlineSize: \"100%\",\n paddingBlockEnd: GAP,\n }}\n />\n );\n })}\n </div>\n );\n}\n\ninterface ThreadWrapperProps extends ThreadProps {\n Thread: ComponentType<ThreadProps>;\n onThreadClick: (id: string) => void;\n onItemAdd: (id: string, el: HTMLElement) => void;\n onItemRemove: (id: string) => void;\n isActive: boolean;\n}\n\nfunction ThreadWrapper({\n onThreadClick,\n onItemAdd,\n onItemRemove,\n thread,\n Thread,\n className,\n isActive,\n ...props\n}: ThreadWrapperProps) {\n const divRef = useRef<HTMLDivElement>(null);\n\n useLayoutEffect(() => {\n const el = divRef.current;\n if (el === null) return;\n\n onItemAdd(thread.id, el);\n return () => {\n onItemRemove(thread.id);\n };\n }, [onItemAdd, onItemRemove, thread.id]);\n\n function handleThreadClick() {\n onThreadClick(thread.id);\n }\n\n return (\n <div\n ref={divRef}\n className={cn(\"lb-tiptap-anchored-threads-thread-container\", className)}\n {...props}\n >\n <Thread\n thread={thread}\n data-state={isActive ? \"active\" : \"inactive\"}\n onClick={handleThreadClick}\n className=\"lb-tiptap-anchored-threads-thread\"\n showComposer={isActive ? true : false}\n />\n </div>\n );\n}\n"],"names":["DefaultThread","useRef","useState","useEditorState","THREADS_PLUGIN_KEY","useCallback","getRectFromCoords","position","useEffect","thread","useLayoutEffect","jsx","cn"],"mappings":"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"}
1
+ {"version":3,"file":"AnchoredThreads.cjs","sources":["../../src/comments/AnchoredThreads.tsx"],"sourcesContent":["import type { BaseMetadata, DCM, DTM, ThreadData } from \"@liveblocks/core\";\nimport { useLayoutEffect } from \"@liveblocks/react/_private\";\nimport {\n Thread as DefaultThread,\n type ThreadProps,\n} from \"@liveblocks/react-ui\";\nimport { cn, useStableComponent } from \"@liveblocks/react-ui/_private\";\nimport { type Editor, useEditorState } from \"@tiptap/react\";\nimport type { ComponentPropsWithoutRef, ComponentType } from \"react\";\nimport { useCallback, useEffect, useRef, useState } from \"react\";\n\nimport { THREADS_PLUGIN_KEY } from \"../types\";\nimport { getRectFromCoords } from \"../utils\";\n\nconst DEFAULT_GAP = 20;\nconst DEFAULT_ACTIVE_THREAD_OFFSET = -12;\n\n// TODO: move that back to a variable\nconst GAP = `var(--lb-tiptap-anchored-threads-gap, ${DEFAULT_GAP}px)`;\nconst ACTIVE_THREAD_OFFSET = `var(--lb-tiptap-anchored-threads-active-thread-offset, ${DEFAULT_ACTIVE_THREAD_OFFSET}px)`;\n\ntype AnchoredThreadsComponents = {\n Thread: ComponentType<ThreadProps>;\n};\n\nexport interface AnchoredThreadsProps<\n TM extends BaseMetadata = DTM,\n CM extends BaseMetadata = DCM,\n> extends Omit<ComponentPropsWithoutRef<\"div\">, \"children\"> {\n /**\n * The threads to display.\n */\n threads: ThreadData<TM, CM>[];\n\n /**\n * Override the component's components.\n */\n components?: Partial<AnchoredThreadsComponents>;\n\n /**\n * The Tiptap editor.\n */\n editor: Editor | null;\n}\n\nexport function AnchoredThreads({\n threads,\n components,\n className,\n style,\n editor,\n ...props\n}: AnchoredThreadsProps) {\n const Thread = useStableComponent(components?.Thread, DefaultThread);\n const containerRef = useRef<HTMLDivElement>(null);\n const [orderedThreads, setOrderedThreads] = useState<\n { position: { from: number; to: number }; thread: ThreadData }[]\n >([]);\n const [elements, setElements] = useState<Map<string, HTMLElement>>(new Map());\n const [positions, setPositions] = useState<Map<string, number>>(new Map()); // A map of thread ids to their 'top' position in the document\n\n const { pluginState } = useEditorState({\n editor,\n selector: (ctx) => {\n if (!ctx?.editor?.state) return { pluginState: undefined };\n const state = THREADS_PLUGIN_KEY.getState(ctx.editor.state);\n return {\n pluginState: state,\n };\n },\n equalityFn: (prev, next) => {\n if (!prev || !next) return false;\n return (\n prev.pluginState?.selectedThreadId ===\n next.pluginState?.selectedThreadId &&\n prev.pluginState?.threadPositions === next.pluginState?.threadPositions\n ); // new map is made each time threadPos updates so shallow equality is fine\n },\n }) ?? { pluginState: undefined };\n\n // TODO: lexical supoprts multiple threads being active, should probably do that here as well\n const handlePositionThreads = useCallback(() => {\n const container = containerRef.current;\n if (\n container === null ||\n !editor ||\n !editor.view ||\n editor.view.isDestroyed\n ) {\n return;\n }\n\n const activeIndex = orderedThreads.findIndex(\n ({ thread }) => thread.id === pluginState?.selectedThreadId\n );\n const ascending =\n activeIndex !== -1 ? orderedThreads.slice(activeIndex) : orderedThreads;\n const descending =\n activeIndex !== -1 ? orderedThreads.slice(0, activeIndex) : [];\n\n const newPositions = new Map<string, number>();\n\n // Iterate over each thread and calculate its new position by taking into account the position of the previously positioned threads\n for (const { thread, position } of ascending) {\n const coords = editor.view.coordsAtPos(\n Math.min(position.from, editor.view.state.doc.content.size - 1)\n );\n const rect = getRectFromCoords(coords);\n let top = rect.top - container.getBoundingClientRect().top;\n\n for (const [id, position] of newPositions) {\n // Retrieve the element associated with the thread\n const el = elements.get(id);\n if (el === undefined) continue;\n\n if (\n top >= position &&\n top <= position + el.getBoundingClientRect().height\n ) {\n top = position + el.getBoundingClientRect().height;\n }\n }\n\n newPositions.set(thread.id, top);\n }\n\n for (const { thread, position } of descending.reverse()) {\n const coords = editor.view.coordsAtPos(position.from);\n const rect = getRectFromCoords(coords);\n // Retrieve the element associated with the current thread\n const el = elements.get(thread.id);\n if (el === undefined) continue;\n\n let top = rect.top - container.getBoundingClientRect().top;\n for (const [, position] of newPositions) {\n if (top >= position - el.getBoundingClientRect().height) {\n top = position - el.getBoundingClientRect().height;\n }\n }\n\n newPositions.set(thread.id, top);\n }\n\n setPositions(newPositions);\n }, [editor, orderedThreads, pluginState?.selectedThreadId, elements]);\n\n useEffect(() => {\n if (!pluginState) return;\n setOrderedThreads(\n Array.from(pluginState.threadPositions, ([threadId, position]) => ({\n threadId,\n position,\n })).reduce(\n (acc, { threadId, position }) => {\n const thread = threads.find(\n (thread) => thread.id === threadId && !thread.resolved\n );\n if (!thread) return acc;\n acc.push({ thread, position });\n return acc;\n },\n [] as { thread: ThreadData; position: { from: number; to: number } }[]\n )\n );\n handlePositionThreads();\n // disable exhaustive deps because we don't want an infinite loop\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [pluginState, threads]);\n\n useLayoutEffect(handlePositionThreads, [handlePositionThreads]);\n\n useEffect(() => {\n const observer = new ResizeObserver(handlePositionThreads);\n const container = editor?.view?.dom;\n if (container) {\n observer.observe(container);\n }\n for (const element of elements.values()) {\n observer.observe(element);\n }\n\n return () => observer.disconnect();\n }, [elements, editor, handlePositionThreads]);\n\n const onItemAdd = useCallback((id: string, el: HTMLElement) => {\n setElements((prev) => new Map(prev).set(id, el));\n }, []);\n\n const onItemRemove = useCallback((id: string) => {\n setElements((prev) => {\n const items = new Map(prev);\n items.delete(id);\n return items;\n });\n }, []);\n\n const onThreadSelect = useCallback(\n (id: string) => {\n if (!editor) return;\n editor.commands.selectThread(id);\n },\n [editor]\n );\n\n if (!editor) return null;\n\n return (\n <div\n {...props}\n className={cn(className, \"lb-root lb-tiptap-anchored-threads\")}\n ref={containerRef}\n style={{\n position: \"relative\",\n ...style,\n }}\n >\n {orderedThreads.map(({ thread, position }) => {\n // In blocknote, it's possible for this to be undefined\n if (!editor.view || editor.view.isDestroyed) {\n return null;\n }\n const coords = editor.view.coordsAtPos(\n Math.min(position.from, editor.state.doc.content.size - 1)\n );\n const rect = getRectFromCoords(coords);\n\n const offset = editor.view.dom.getBoundingClientRect().top ?? 0;\n\n let top = rect.top - offset;\n\n if (positions.has(thread.id)) {\n top = positions.get(thread.id)!;\n }\n\n const isActive = thread.id === pluginState?.selectedThreadId;\n\n return (\n <ThreadWrapper\n key={thread.id}\n onThreadClick={onThreadSelect}\n onItemAdd={onItemAdd}\n onItemRemove={onItemRemove}\n Thread={Thread}\n thread={thread}\n isActive={isActive}\n style={{\n position: \"absolute\",\n transform: `translate3d(${isActive ? ACTIVE_THREAD_OFFSET : 0}, ${top}px, 0)`,\n insetInlineStart: 0,\n inlineSize: \"100%\",\n paddingBlockEnd: GAP,\n }}\n />\n );\n })}\n </div>\n );\n}\n\ninterface ThreadWrapperProps extends ThreadProps {\n Thread: ComponentType<ThreadProps>;\n onThreadClick: (id: string) => void;\n onItemAdd: (id: string, el: HTMLElement) => void;\n onItemRemove: (id: string) => void;\n isActive: boolean;\n}\n\nfunction ThreadWrapper({\n onThreadClick,\n onItemAdd,\n onItemRemove,\n thread,\n Thread,\n className,\n isActive,\n ...props\n}: ThreadWrapperProps) {\n const divRef = useRef<HTMLDivElement>(null);\n\n useLayoutEffect(() => {\n const el = divRef.current;\n if (el === null) return;\n\n onItemAdd(thread.id, el);\n return () => {\n onItemRemove(thread.id);\n };\n }, [onItemAdd, onItemRemove, thread.id]);\n\n function handleThreadClick() {\n onThreadClick(thread.id);\n }\n\n return (\n <div\n ref={divRef}\n className={cn(\"lb-tiptap-anchored-threads-thread-container\", className)}\n {...props}\n >\n <Thread\n thread={thread}\n data-state={isActive ? \"active\" : \"inactive\"}\n onClick={handleThreadClick}\n className=\"lb-tiptap-anchored-threads-thread\"\n showComposer={isActive ? true : false}\n />\n </div>\n );\n}\n"],"names":["useStableComponent","DefaultThread","useRef","useState","useEditorState","THREADS_PLUGIN_KEY","useCallback","getRectFromCoords","position","useEffect","thread","useLayoutEffect","jsx","cn"],"mappings":"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"}
@@ -1,7 +1,7 @@
1
1
  import { jsx } from 'react/jsx-runtime';
2
2
  import { useLayoutEffect } from '@liveblocks/react/_private';
3
3
  import { Thread } from '@liveblocks/react-ui';
4
- import { cn } from '@liveblocks/react-ui/_private';
4
+ import { useStableComponent, cn } from '@liveblocks/react-ui/_private';
5
5
  import { useEditorState } from '@tiptap/react';
6
6
  import { useRef, useState, useCallback, useEffect } from 'react';
7
7
  import { THREADS_PLUGIN_KEY } from '../types.js';
@@ -19,7 +19,7 @@ function AnchoredThreads({
19
19
  editor,
20
20
  ...props
21
21
  }) {
22
- const Thread$1 = components?.Thread ?? Thread;
22
+ const Thread$1 = useStableComponent(components?.Thread, Thread);
23
23
  const containerRef = useRef(null);
24
24
  const [orderedThreads, setOrderedThreads] = useState([]);
25
25
  const [elements, setElements] = useState(/* @__PURE__ */ new Map());
@@ -42,8 +42,9 @@ function AnchoredThreads({
42
42
  }) ?? { pluginState: void 0 };
43
43
  const handlePositionThreads = useCallback(() => {
44
44
  const container = containerRef.current;
45
- if (container === null || !editor || !editor.view)
45
+ if (container === null || !editor || !editor.view || editor.view.isDestroyed) {
46
46
  return;
47
+ }
47
48
  const activeIndex = orderedThreads.findIndex(
48
49
  ({ thread }) => thread.id === pluginState?.selectedThreadId
49
50
  );
@@ -147,17 +148,14 @@ function AnchoredThreads({
147
148
  ...style
148
149
  },
149
150
  children: orderedThreads.map(({ thread, position }) => {
150
- if (!editor.view) {
151
+ if (!editor.view || editor.view.isDestroyed) {
151
152
  return null;
152
153
  }
153
154
  const coords = editor.view.coordsAtPos(
154
155
  Math.min(position.from, editor.state.doc.content.size - 1)
155
156
  );
156
157
  const rect = getRectFromCoords(coords);
157
- let offset = 0;
158
- if (editor.options.element instanceof HTMLElement) {
159
- offset = editor.options.element.getBoundingClientRect().top;
160
- }
158
+ const offset = editor.view.dom.getBoundingClientRect().top ?? 0;
161
159
  let top = rect.top - offset;
162
160
  if (positions.has(thread.id)) {
163
161
  top = positions.get(thread.id);
@@ -1 +1 @@
1
- {"version":3,"file":"AnchoredThreads.js","sources":["../../src/comments/AnchoredThreads.tsx"],"sourcesContent":["import type { BaseMetadata, DCM, DTM, ThreadData } from \"@liveblocks/core\";\nimport { useLayoutEffect } from \"@liveblocks/react/_private\";\nimport {\n Thread as DefaultThread,\n type ThreadProps,\n} from \"@liveblocks/react-ui\";\nimport { cn } from \"@liveblocks/react-ui/_private\";\nimport { type Editor, useEditorState } from \"@tiptap/react\";\nimport type { ComponentPropsWithoutRef, ComponentType } from \"react\";\nimport { useCallback, useEffect, useRef, useState } from \"react\";\n\nimport { THREADS_PLUGIN_KEY } from \"../types\";\nimport { getRectFromCoords } from \"../utils\";\n\nconst DEFAULT_GAP = 20;\nconst DEFAULT_ACTIVE_THREAD_OFFSET = -12;\n\n// TODO: move that back to a variable\nconst GAP = `var(--lb-tiptap-anchored-threads-gap, ${DEFAULT_GAP}px)`;\nconst ACTIVE_THREAD_OFFSET = `var(--lb-tiptap-anchored-threads-active-thread-offset, ${DEFAULT_ACTIVE_THREAD_OFFSET}px)`;\n\ntype AnchoredThreadsComponents = {\n Thread: ComponentType<ThreadProps>;\n};\n\nexport interface AnchoredThreadsProps<\n TM extends BaseMetadata = DTM,\n CM extends BaseMetadata = DCM,\n> extends Omit<ComponentPropsWithoutRef<\"div\">, \"children\"> {\n /**\n * The threads to display.\n */\n threads: ThreadData<TM, CM>[];\n\n /**\n * Override the component's components.\n */\n components?: Partial<AnchoredThreadsComponents>;\n\n /**\n * The Tiptap editor.\n */\n editor: Editor | null;\n}\n\nexport function AnchoredThreads({\n threads,\n components,\n className,\n style,\n editor,\n ...props\n}: AnchoredThreadsProps) {\n const Thread = components?.Thread ?? DefaultThread;\n const containerRef = useRef<HTMLDivElement>(null);\n const [orderedThreads, setOrderedThreads] = useState<\n { position: { from: number; to: number }; thread: ThreadData }[]\n >([]);\n const [elements, setElements] = useState<Map<string, HTMLElement>>(new Map());\n const [positions, setPositions] = useState<Map<string, number>>(new Map()); // A map of thread ids to their 'top' position in the document\n\n const { pluginState } = useEditorState({\n editor,\n selector: (ctx) => {\n if (!ctx?.editor?.state) return { pluginState: undefined };\n const state = THREADS_PLUGIN_KEY.getState(ctx.editor.state);\n return {\n pluginState: state,\n };\n },\n equalityFn: (prev, next) => {\n if (!prev || !next) return false;\n return (\n prev.pluginState?.selectedThreadId ===\n next.pluginState?.selectedThreadId &&\n prev.pluginState?.threadPositions === next.pluginState?.threadPositions\n ); // new map is made each time threadPos updates so shallow equality is fine\n },\n }) ?? { pluginState: undefined };\n\n // TODO: lexical supoprts multiple threads being active, should probably do that here as well\n const handlePositionThreads = useCallback(() => {\n const container = containerRef.current;\n if (container === null || !editor || !editor.view) return;\n\n const activeIndex = orderedThreads.findIndex(\n ({ thread }) => thread.id === pluginState?.selectedThreadId\n );\n const ascending =\n activeIndex !== -1 ? orderedThreads.slice(activeIndex) : orderedThreads;\n const descending =\n activeIndex !== -1 ? orderedThreads.slice(0, activeIndex) : [];\n\n const newPositions = new Map<string, number>();\n\n // Iterate over each thread and calculate its new position by taking into account the position of the previously positioned threads\n for (const { thread, position } of ascending) {\n const coords = editor.view.coordsAtPos(\n Math.min(position.from, editor.view.state.doc.content.size - 1)\n );\n const rect = getRectFromCoords(coords);\n let top = rect.top - container.getBoundingClientRect().top;\n\n for (const [id, position] of newPositions) {\n // Retrieve the element associated with the thread\n const el = elements.get(id);\n if (el === undefined) continue;\n\n if (\n top >= position &&\n top <= position + el.getBoundingClientRect().height\n ) {\n top = position + el.getBoundingClientRect().height;\n }\n }\n\n newPositions.set(thread.id, top);\n }\n\n for (const { thread, position } of descending.reverse()) {\n const coords = editor.view.coordsAtPos(position.from);\n const rect = getRectFromCoords(coords);\n // Retrieve the element associated with the current thread\n const el = elements.get(thread.id);\n if (el === undefined) continue;\n\n let top = rect.top - container.getBoundingClientRect().top;\n for (const [, position] of newPositions) {\n if (top >= position - el.getBoundingClientRect().height) {\n top = position - el.getBoundingClientRect().height;\n }\n }\n\n newPositions.set(thread.id, top);\n }\n\n setPositions(newPositions);\n }, [editor, orderedThreads, pluginState?.selectedThreadId, elements]);\n\n useEffect(() => {\n if (!pluginState) return;\n setOrderedThreads(\n Array.from(pluginState.threadPositions, ([threadId, position]) => ({\n threadId,\n position,\n })).reduce(\n (acc, { threadId, position }) => {\n const thread = threads.find(\n (thread) => thread.id === threadId && !thread.resolved\n );\n if (!thread) return acc;\n acc.push({ thread, position });\n return acc;\n },\n [] as { thread: ThreadData; position: { from: number; to: number } }[]\n )\n );\n handlePositionThreads();\n // disable exhaustive deps because we don't want an infinite loop\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [pluginState, threads]);\n\n useLayoutEffect(handlePositionThreads, [handlePositionThreads]);\n\n useEffect(() => {\n const observer = new ResizeObserver(handlePositionThreads);\n const container = editor?.view?.dom;\n if (container) {\n observer.observe(container);\n }\n for (const element of elements.values()) {\n observer.observe(element);\n }\n\n return () => observer.disconnect();\n }, [elements, editor, handlePositionThreads]);\n\n const onItemAdd = useCallback((id: string, el: HTMLElement) => {\n setElements((prev) => new Map(prev).set(id, el));\n }, []);\n\n const onItemRemove = useCallback((id: string) => {\n setElements((prev) => {\n const items = new Map(prev);\n items.delete(id);\n return items;\n });\n }, []);\n\n const onThreadSelect = useCallback(\n (id: string) => {\n if (!editor) return;\n editor.commands.selectThread(id);\n },\n [editor]\n );\n\n if (!editor) return null;\n\n return (\n <div\n {...props}\n className={cn(className, \"lb-root lb-tiptap-anchored-threads\")}\n ref={containerRef}\n style={{\n position: \"relative\",\n ...style,\n }}\n >\n {orderedThreads.map(({ thread, position }) => {\n // In blocknote, it's possible for this to be undefined\n if (!editor.view) {\n return null;\n }\n const coords = editor.view.coordsAtPos(\n Math.min(position.from, editor.state.doc.content.size - 1)\n );\n const rect = getRectFromCoords(coords);\n let offset = 0;\n if (editor.options.element instanceof HTMLElement) {\n offset = editor.options.element.getBoundingClientRect().top;\n }\n\n let top = rect.top - offset;\n\n if (positions.has(thread.id)) {\n top = positions.get(thread.id)!;\n }\n\n const isActive = thread.id === pluginState?.selectedThreadId;\n\n return (\n <ThreadWrapper\n key={thread.id}\n onThreadClick={onThreadSelect}\n onItemAdd={onItemAdd}\n onItemRemove={onItemRemove}\n Thread={Thread}\n thread={thread}\n isActive={isActive}\n style={{\n position: \"absolute\",\n transform: `translate3d(${isActive ? ACTIVE_THREAD_OFFSET : 0}, ${top}px, 0)`,\n insetInlineStart: 0,\n inlineSize: \"100%\",\n paddingBlockEnd: GAP,\n }}\n />\n );\n })}\n </div>\n );\n}\n\ninterface ThreadWrapperProps extends ThreadProps {\n Thread: ComponentType<ThreadProps>;\n onThreadClick: (id: string) => void;\n onItemAdd: (id: string, el: HTMLElement) => void;\n onItemRemove: (id: string) => void;\n isActive: boolean;\n}\n\nfunction ThreadWrapper({\n onThreadClick,\n onItemAdd,\n onItemRemove,\n thread,\n Thread,\n className,\n isActive,\n ...props\n}: ThreadWrapperProps) {\n const divRef = useRef<HTMLDivElement>(null);\n\n useLayoutEffect(() => {\n const el = divRef.current;\n if (el === null) return;\n\n onItemAdd(thread.id, el);\n return () => {\n onItemRemove(thread.id);\n };\n }, [onItemAdd, onItemRemove, thread.id]);\n\n function handleThreadClick() {\n onThreadClick(thread.id);\n }\n\n return (\n <div\n ref={divRef}\n className={cn(\"lb-tiptap-anchored-threads-thread-container\", className)}\n {...props}\n >\n <Thread\n thread={thread}\n data-state={isActive ? \"active\" : \"inactive\"}\n onClick={handleThreadClick}\n className=\"lb-tiptap-anchored-threads-thread\"\n showComposer={isActive ? true : false}\n />\n </div>\n );\n}\n"],"names":["Thread","DefaultThread","position","thread"],"mappings":"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"}
1
+ {"version":3,"file":"AnchoredThreads.js","sources":["../../src/comments/AnchoredThreads.tsx"],"sourcesContent":["import type { BaseMetadata, DCM, DTM, ThreadData } from \"@liveblocks/core\";\nimport { useLayoutEffect } from \"@liveblocks/react/_private\";\nimport {\n Thread as DefaultThread,\n type ThreadProps,\n} from \"@liveblocks/react-ui\";\nimport { cn, useStableComponent } from \"@liveblocks/react-ui/_private\";\nimport { type Editor, useEditorState } from \"@tiptap/react\";\nimport type { ComponentPropsWithoutRef, ComponentType } from \"react\";\nimport { useCallback, useEffect, useRef, useState } from \"react\";\n\nimport { THREADS_PLUGIN_KEY } from \"../types\";\nimport { getRectFromCoords } from \"../utils\";\n\nconst DEFAULT_GAP = 20;\nconst DEFAULT_ACTIVE_THREAD_OFFSET = -12;\n\n// TODO: move that back to a variable\nconst GAP = `var(--lb-tiptap-anchored-threads-gap, ${DEFAULT_GAP}px)`;\nconst ACTIVE_THREAD_OFFSET = `var(--lb-tiptap-anchored-threads-active-thread-offset, ${DEFAULT_ACTIVE_THREAD_OFFSET}px)`;\n\ntype AnchoredThreadsComponents = {\n Thread: ComponentType<ThreadProps>;\n};\n\nexport interface AnchoredThreadsProps<\n TM extends BaseMetadata = DTM,\n CM extends BaseMetadata = DCM,\n> extends Omit<ComponentPropsWithoutRef<\"div\">, \"children\"> {\n /**\n * The threads to display.\n */\n threads: ThreadData<TM, CM>[];\n\n /**\n * Override the component's components.\n */\n components?: Partial<AnchoredThreadsComponents>;\n\n /**\n * The Tiptap editor.\n */\n editor: Editor | null;\n}\n\nexport function AnchoredThreads({\n threads,\n components,\n className,\n style,\n editor,\n ...props\n}: AnchoredThreadsProps) {\n const Thread = useStableComponent(components?.Thread, DefaultThread);\n const containerRef = useRef<HTMLDivElement>(null);\n const [orderedThreads, setOrderedThreads] = useState<\n { position: { from: number; to: number }; thread: ThreadData }[]\n >([]);\n const [elements, setElements] = useState<Map<string, HTMLElement>>(new Map());\n const [positions, setPositions] = useState<Map<string, number>>(new Map()); // A map of thread ids to their 'top' position in the document\n\n const { pluginState } = useEditorState({\n editor,\n selector: (ctx) => {\n if (!ctx?.editor?.state) return { pluginState: undefined };\n const state = THREADS_PLUGIN_KEY.getState(ctx.editor.state);\n return {\n pluginState: state,\n };\n },\n equalityFn: (prev, next) => {\n if (!prev || !next) return false;\n return (\n prev.pluginState?.selectedThreadId ===\n next.pluginState?.selectedThreadId &&\n prev.pluginState?.threadPositions === next.pluginState?.threadPositions\n ); // new map is made each time threadPos updates so shallow equality is fine\n },\n }) ?? { pluginState: undefined };\n\n // TODO: lexical supoprts multiple threads being active, should probably do that here as well\n const handlePositionThreads = useCallback(() => {\n const container = containerRef.current;\n if (\n container === null ||\n !editor ||\n !editor.view ||\n editor.view.isDestroyed\n ) {\n return;\n }\n\n const activeIndex = orderedThreads.findIndex(\n ({ thread }) => thread.id === pluginState?.selectedThreadId\n );\n const ascending =\n activeIndex !== -1 ? orderedThreads.slice(activeIndex) : orderedThreads;\n const descending =\n activeIndex !== -1 ? orderedThreads.slice(0, activeIndex) : [];\n\n const newPositions = new Map<string, number>();\n\n // Iterate over each thread and calculate its new position by taking into account the position of the previously positioned threads\n for (const { thread, position } of ascending) {\n const coords = editor.view.coordsAtPos(\n Math.min(position.from, editor.view.state.doc.content.size - 1)\n );\n const rect = getRectFromCoords(coords);\n let top = rect.top - container.getBoundingClientRect().top;\n\n for (const [id, position] of newPositions) {\n // Retrieve the element associated with the thread\n const el = elements.get(id);\n if (el === undefined) continue;\n\n if (\n top >= position &&\n top <= position + el.getBoundingClientRect().height\n ) {\n top = position + el.getBoundingClientRect().height;\n }\n }\n\n newPositions.set(thread.id, top);\n }\n\n for (const { thread, position } of descending.reverse()) {\n const coords = editor.view.coordsAtPos(position.from);\n const rect = getRectFromCoords(coords);\n // Retrieve the element associated with the current thread\n const el = elements.get(thread.id);\n if (el === undefined) continue;\n\n let top = rect.top - container.getBoundingClientRect().top;\n for (const [, position] of newPositions) {\n if (top >= position - el.getBoundingClientRect().height) {\n top = position - el.getBoundingClientRect().height;\n }\n }\n\n newPositions.set(thread.id, top);\n }\n\n setPositions(newPositions);\n }, [editor, orderedThreads, pluginState?.selectedThreadId, elements]);\n\n useEffect(() => {\n if (!pluginState) return;\n setOrderedThreads(\n Array.from(pluginState.threadPositions, ([threadId, position]) => ({\n threadId,\n position,\n })).reduce(\n (acc, { threadId, position }) => {\n const thread = threads.find(\n (thread) => thread.id === threadId && !thread.resolved\n );\n if (!thread) return acc;\n acc.push({ thread, position });\n return acc;\n },\n [] as { thread: ThreadData; position: { from: number; to: number } }[]\n )\n );\n handlePositionThreads();\n // disable exhaustive deps because we don't want an infinite loop\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [pluginState, threads]);\n\n useLayoutEffect(handlePositionThreads, [handlePositionThreads]);\n\n useEffect(() => {\n const observer = new ResizeObserver(handlePositionThreads);\n const container = editor?.view?.dom;\n if (container) {\n observer.observe(container);\n }\n for (const element of elements.values()) {\n observer.observe(element);\n }\n\n return () => observer.disconnect();\n }, [elements, editor, handlePositionThreads]);\n\n const onItemAdd = useCallback((id: string, el: HTMLElement) => {\n setElements((prev) => new Map(prev).set(id, el));\n }, []);\n\n const onItemRemove = useCallback((id: string) => {\n setElements((prev) => {\n const items = new Map(prev);\n items.delete(id);\n return items;\n });\n }, []);\n\n const onThreadSelect = useCallback(\n (id: string) => {\n if (!editor) return;\n editor.commands.selectThread(id);\n },\n [editor]\n );\n\n if (!editor) return null;\n\n return (\n <div\n {...props}\n className={cn(className, \"lb-root lb-tiptap-anchored-threads\")}\n ref={containerRef}\n style={{\n position: \"relative\",\n ...style,\n }}\n >\n {orderedThreads.map(({ thread, position }) => {\n // In blocknote, it's possible for this to be undefined\n if (!editor.view || editor.view.isDestroyed) {\n return null;\n }\n const coords = editor.view.coordsAtPos(\n Math.min(position.from, editor.state.doc.content.size - 1)\n );\n const rect = getRectFromCoords(coords);\n\n const offset = editor.view.dom.getBoundingClientRect().top ?? 0;\n\n let top = rect.top - offset;\n\n if (positions.has(thread.id)) {\n top = positions.get(thread.id)!;\n }\n\n const isActive = thread.id === pluginState?.selectedThreadId;\n\n return (\n <ThreadWrapper\n key={thread.id}\n onThreadClick={onThreadSelect}\n onItemAdd={onItemAdd}\n onItemRemove={onItemRemove}\n Thread={Thread}\n thread={thread}\n isActive={isActive}\n style={{\n position: \"absolute\",\n transform: `translate3d(${isActive ? ACTIVE_THREAD_OFFSET : 0}, ${top}px, 0)`,\n insetInlineStart: 0,\n inlineSize: \"100%\",\n paddingBlockEnd: GAP,\n }}\n />\n );\n })}\n </div>\n );\n}\n\ninterface ThreadWrapperProps extends ThreadProps {\n Thread: ComponentType<ThreadProps>;\n onThreadClick: (id: string) => void;\n onItemAdd: (id: string, el: HTMLElement) => void;\n onItemRemove: (id: string) => void;\n isActive: boolean;\n}\n\nfunction ThreadWrapper({\n onThreadClick,\n onItemAdd,\n onItemRemove,\n thread,\n Thread,\n className,\n isActive,\n ...props\n}: ThreadWrapperProps) {\n const divRef = useRef<HTMLDivElement>(null);\n\n useLayoutEffect(() => {\n const el = divRef.current;\n if (el === null) return;\n\n onItemAdd(thread.id, el);\n return () => {\n onItemRemove(thread.id);\n };\n }, [onItemAdd, onItemRemove, thread.id]);\n\n function handleThreadClick() {\n onThreadClick(thread.id);\n }\n\n return (\n <div\n ref={divRef}\n className={cn(\"lb-tiptap-anchored-threads-thread-container\", className)}\n {...props}\n >\n <Thread\n thread={thread}\n data-state={isActive ? \"active\" : \"inactive\"}\n onClick={handleThreadClick}\n className=\"lb-tiptap-anchored-threads-thread\"\n showComposer={isActive ? true : false}\n />\n </div>\n );\n}\n"],"names":["Thread","DefaultThread","position","thread"],"mappings":"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"}
@@ -3,16 +3,16 @@
3
3
  var jsxRuntime = require('react/jsx-runtime');
4
4
  var reactDom = require('@floating-ui/react-dom');
5
5
  var react$1 = require('@liveblocks/react');
6
- var _private = require('@liveblocks/react/_private');
6
+ var _private$1 = require('@liveblocks/react/_private');
7
7
  var reactUi = require('@liveblocks/react-ui');
8
- var _private$1 = require('@liveblocks/react-ui/_private');
8
+ var _private = require('@liveblocks/react-ui/_private');
9
9
  var react$2 = require('@tiptap/react');
10
10
  var react = require('react');
11
11
  var utils = require('../utils.cjs');
12
12
 
13
13
  const FLOATING_COMPOSER_COLLISION_PADDING = 10;
14
14
  const FloatingComposer = react.forwardRef(function FloatingComposer2({ editor, onComposerSubmit, onKeyDown, onClick, components, ...props }, forwardedRef) {
15
- const Composer = components?.Composer ?? reactUi.Composer;
15
+ const Composer = _private.useStableComponent(components?.Composer, reactUi.Composer);
16
16
  const createThread = react$1.useCreateThread();
17
17
  const pendingCommentSelection = react$2.useEditorState({
18
18
  editor,
@@ -52,7 +52,7 @@ const FloatingComposer = react.forwardRef(function FloatingComposer2({ editor, o
52
52
  });
53
53
  }
54
54
  });
55
- _private.useLayoutEffect(() => {
55
+ _private$1.useLayoutEffect(() => {
56
56
  if (!editor || !isOpen) {
57
57
  return;
58
58
  }
@@ -109,7 +109,7 @@ const FloatingComposer = react.forwardRef(function FloatingComposer2({ editor, o
109
109
  if (!isOpen || !editor) {
110
110
  return null;
111
111
  }
112
- return /* @__PURE__ */ jsxRuntime.jsx(_private$1.Portal, { asChild: true, children: /* @__PURE__ */ jsxRuntime.jsx(
112
+ return /* @__PURE__ */ jsxRuntime.jsx(_private.Portal, { asChild: true, children: /* @__PURE__ */ jsxRuntime.jsx(
113
113
  "div",
114
114
  {
115
115
  className: "lb-root lb-portal lb-elevation lb-tiptap-floating lb-tiptap-floating-composer",
@@ -1 +1 @@
1
- {"version":3,"file":"FloatingComposer.cjs","sources":["../../src/comments/FloatingComposer.tsx"],"sourcesContent":["import {\n autoUpdate,\n flip,\n hide,\n inline,\n limitShift,\n offset,\n shift,\n size,\n useFloating,\n} from \"@floating-ui/react-dom\";\nimport type { BaseMetadata } from \"@liveblocks/client\";\nimport type { DCM, DTM } from \"@liveblocks/core\";\nimport { useCreateThread } from \"@liveblocks/react\";\nimport { useLayoutEffect } from \"@liveblocks/react/_private\";\nimport type {\n ComposerProps,\n ComposerSubmitComment,\n} from \"@liveblocks/react-ui\";\nimport { Composer as DefaultComposer } from \"@liveblocks/react-ui\";\nimport { Portal } from \"@liveblocks/react-ui/_private\";\nimport { type Editor, useEditorState } from \"@tiptap/react\";\nimport type {\n ComponentType,\n FormEvent,\n KeyboardEvent,\n MouseEvent,\n} from \"react\";\nimport { forwardRef, useCallback } from \"react\";\n\nimport type { ExtendedChainedCommands } from \"../types\";\nimport { compareSelections, getDomRangeFromSelection } from \"../utils\";\n\ntype ExcludeProps<T, K extends Record<string, unknown>> = Omit<\n Exclude<T, T & K>,\n keyof K\n>;\n\ntype ComposerPropsCreateThread<\n TM extends BaseMetadata,\n CM extends BaseMetadata,\n> = ExcludeProps<\n ComposerProps<TM, CM>,\n { threadId: string; commentId: string }\n>;\n\ntype FloatingComposerComponents = {\n Composer: ComponentType<ComposerPropsCreateThread<DTM, DCM>>;\n};\n\nexport type FloatingComposerProps<\n TM extends BaseMetadata = DTM,\n CM extends BaseMetadata = DCM,\n> = ComposerPropsCreateThread<TM, CM> & {\n /**\n * Override the component's components.\n */\n components?: Partial<FloatingComposerComponents>;\n\n /**\n * The Tiptap editor.\n */\n editor: Editor | null;\n};\n\nexport const FLOATING_COMPOSER_COLLISION_PADDING = 10;\n\nexport const FloatingComposer = forwardRef<\n HTMLFormElement,\n FloatingComposerProps\n>(function FloatingComposer(\n { editor, onComposerSubmit, onKeyDown, onClick, components, ...props },\n forwardedRef\n) {\n const Composer = components?.Composer ?? DefaultComposer;\n const createThread = useCreateThread();\n const pendingCommentSelection =\n useEditorState({\n editor,\n selector: (ctx) => {\n if (!ctx.editor) {\n return undefined;\n }\n\n const hasPendingComment =\n ctx.editor.storage.liveblocksComments.pendingComment;\n const isEmpty = ctx.editor.state.selection.empty;\n\n return hasPendingComment && !isEmpty\n ? ctx.editor.state.selection\n : undefined;\n },\n equalityFn: compareSelections,\n }) ?? undefined;\n const isOpen = pendingCommentSelection !== undefined;\n const {\n refs: { setReference, setFloating },\n strategy,\n x,\n y,\n } = useFloating({\n strategy: \"fixed\",\n placement: \"bottom\",\n middleware: [\n inline({ padding: FLOATING_COMPOSER_COLLISION_PADDING }),\n flip({ padding: FLOATING_COMPOSER_COLLISION_PADDING, crossAxis: false }),\n offset(10),\n hide({ padding: FLOATING_COMPOSER_COLLISION_PADDING }),\n shift({\n padding: FLOATING_COMPOSER_COLLISION_PADDING,\n limiter: limitShift(),\n }),\n size({ padding: FLOATING_COMPOSER_COLLISION_PADDING }),\n ],\n whileElementsMounted: (...args) => {\n return autoUpdate(...args, {\n animationFrame: true,\n });\n },\n });\n\n useLayoutEffect(() => {\n if (!editor || !isOpen) {\n return;\n }\n\n if (!pendingCommentSelection) {\n setReference(null);\n } else {\n const domRange = getDomRangeFromSelection(\n editor,\n pendingCommentSelection\n );\n\n setReference(domRange);\n }\n }, [pendingCommentSelection, editor, isOpen, setReference]);\n\n // Submit a new thread and update the comment highlight to show a completed highlight\n const handleComposerSubmit = useCallback(\n (comment: ComposerSubmitComment, event: FormEvent<HTMLFormElement>) => {\n onComposerSubmit?.(comment, event);\n if (event.defaultPrevented) return;\n\n if (!editor) {\n return;\n }\n\n event.preventDefault();\n\n const thread = createThread({\n body: comment.body,\n attachments: comment.attachments,\n metadata: props.metadata ?? {},\n });\n editor.commands.addComment(thread.id);\n },\n [onComposerSubmit, editor, createThread, props.metadata]\n );\n\n const handleKeyDown = useCallback(\n (event: KeyboardEvent<HTMLFormElement>) => {\n onKeyDown?.(event);\n\n if (event.isDefaultPrevented() || !editor) {\n return;\n }\n\n if (event.key === \"Escape\") {\n (editor.chain() as ExtendedChainedCommands<\"closePendingComment\">)\n .closePendingComment()\n .run();\n }\n },\n [editor, onKeyDown]\n );\n\n const handleClick = useCallback(\n (event: MouseEvent<HTMLFormElement>) => {\n onClick?.(event);\n\n if (event.defaultPrevented) {\n return;\n }\n\n // Don't send up a click event from emoji picker and close the composer.\n event.stopPropagation();\n },\n [onClick]\n );\n\n if (!isOpen || !editor) {\n return null;\n }\n\n return (\n <Portal asChild>\n <div\n className=\"lb-root lb-portal lb-elevation lb-tiptap-floating lb-tiptap-floating-composer\"\n ref={setFloating}\n style={{\n position: strategy,\n top: 0,\n left: 0,\n transform: `translate3d(${Math.round(x)}px, ${Math.round(y)}px, 0)`,\n minWidth: \"max-content\",\n }}\n >\n <Composer\n ref={forwardedRef}\n autoFocus\n {...props}\n onKeyDown={handleKeyDown}\n onComposerSubmit={handleComposerSubmit}\n onClick={handleClick}\n />\n </div>\n </Portal>\n );\n});\n"],"names":["forwardRef","FloatingComposer","DefaultComposer","useCreateThread","useEditorState","compareSelections","useFloating","inline","flip","offset","hide","shift","limitShift","size","autoUpdate","useLayoutEffect","getDomRangeFromSelection","useCallback","jsx","Portal"],"mappings":"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"}
1
+ {"version":3,"file":"FloatingComposer.cjs","sources":["../../src/comments/FloatingComposer.tsx"],"sourcesContent":["import {\n autoUpdate,\n flip,\n hide,\n inline,\n limitShift,\n offset,\n shift,\n size,\n useFloating,\n} from \"@floating-ui/react-dom\";\nimport type { BaseMetadata } from \"@liveblocks/client\";\nimport type { DCM, DTM } from \"@liveblocks/core\";\nimport { useCreateThread } from \"@liveblocks/react\";\nimport { useLayoutEffect } from \"@liveblocks/react/_private\";\nimport type {\n ComposerProps,\n ComposerSubmitComment,\n} from \"@liveblocks/react-ui\";\nimport { Composer as DefaultComposer } from \"@liveblocks/react-ui\";\nimport { Portal, useStableComponent } from \"@liveblocks/react-ui/_private\";\nimport { type Editor, useEditorState } from \"@tiptap/react\";\nimport type {\n ComponentType,\n FormEvent,\n KeyboardEvent,\n MouseEvent,\n} from \"react\";\nimport { forwardRef, useCallback } from \"react\";\n\nimport type { ExtendedChainedCommands } from \"../types\";\nimport { compareSelections, getDomRangeFromSelection } from \"../utils\";\n\ntype ExcludeProps<T, K extends Record<string, unknown>> = Omit<\n Exclude<T, T & K>,\n keyof K\n>;\n\ntype ComposerPropsCreateThread<\n TM extends BaseMetadata,\n CM extends BaseMetadata,\n> = ExcludeProps<\n ComposerProps<TM, CM>,\n { threadId: string; commentId: string }\n>;\n\ntype FloatingComposerComponents = {\n Composer: ComponentType<ComposerPropsCreateThread<DTM, DCM>>;\n};\n\nexport type FloatingComposerProps<\n TM extends BaseMetadata = DTM,\n CM extends BaseMetadata = DCM,\n> = ComposerPropsCreateThread<TM, CM> & {\n /**\n * Override the component's components.\n */\n components?: Partial<FloatingComposerComponents>;\n\n /**\n * The Tiptap editor.\n */\n editor: Editor | null;\n};\n\nexport const FLOATING_COMPOSER_COLLISION_PADDING = 10;\n\nexport const FloatingComposer = forwardRef<\n HTMLFormElement,\n FloatingComposerProps\n>(function FloatingComposer(\n { editor, onComposerSubmit, onKeyDown, onClick, components, ...props },\n forwardedRef\n) {\n const Composer = useStableComponent(components?.Composer, DefaultComposer);\n const createThread = useCreateThread();\n const pendingCommentSelection =\n useEditorState({\n editor,\n selector: (ctx) => {\n if (!ctx.editor) {\n return undefined;\n }\n\n const hasPendingComment =\n ctx.editor.storage.liveblocksComments.pendingComment;\n const isEmpty = ctx.editor.state.selection.empty;\n\n return hasPendingComment && !isEmpty\n ? ctx.editor.state.selection\n : undefined;\n },\n equalityFn: compareSelections,\n }) ?? undefined;\n const isOpen = pendingCommentSelection !== undefined;\n const {\n refs: { setReference, setFloating },\n strategy,\n x,\n y,\n } = useFloating({\n strategy: \"fixed\",\n placement: \"bottom\",\n middleware: [\n inline({ padding: FLOATING_COMPOSER_COLLISION_PADDING }),\n flip({ padding: FLOATING_COMPOSER_COLLISION_PADDING, crossAxis: false }),\n offset(10),\n hide({ padding: FLOATING_COMPOSER_COLLISION_PADDING }),\n shift({\n padding: FLOATING_COMPOSER_COLLISION_PADDING,\n limiter: limitShift(),\n }),\n size({ padding: FLOATING_COMPOSER_COLLISION_PADDING }),\n ],\n whileElementsMounted: (...args) => {\n return autoUpdate(...args, {\n animationFrame: true,\n });\n },\n });\n\n useLayoutEffect(() => {\n if (!editor || !isOpen) {\n return;\n }\n\n if (!pendingCommentSelection) {\n setReference(null);\n } else {\n const domRange = getDomRangeFromSelection(\n editor,\n pendingCommentSelection\n );\n\n setReference(domRange);\n }\n }, [pendingCommentSelection, editor, isOpen, setReference]);\n\n // Submit a new thread and update the comment highlight to show a completed highlight\n const handleComposerSubmit = useCallback(\n (comment: ComposerSubmitComment, event: FormEvent<HTMLFormElement>) => {\n onComposerSubmit?.(comment, event);\n if (event.defaultPrevented) return;\n\n if (!editor) {\n return;\n }\n\n event.preventDefault();\n\n const thread = createThread({\n body: comment.body,\n attachments: comment.attachments,\n metadata: props.metadata ?? {},\n });\n editor.commands.addComment(thread.id);\n },\n [onComposerSubmit, editor, createThread, props.metadata]\n );\n\n const handleKeyDown = useCallback(\n (event: KeyboardEvent<HTMLFormElement>) => {\n onKeyDown?.(event);\n\n if (event.isDefaultPrevented() || !editor) {\n return;\n }\n\n if (event.key === \"Escape\") {\n (editor.chain() as ExtendedChainedCommands<\"closePendingComment\">)\n .closePendingComment()\n .run();\n }\n },\n [editor, onKeyDown]\n );\n\n const handleClick = useCallback(\n (event: MouseEvent<HTMLFormElement>) => {\n onClick?.(event);\n\n if (event.defaultPrevented) {\n return;\n }\n\n // Don't send up a click event from emoji picker and close the composer.\n event.stopPropagation();\n },\n [onClick]\n );\n\n if (!isOpen || !editor) {\n return null;\n }\n\n return (\n <Portal asChild>\n <div\n className=\"lb-root lb-portal lb-elevation lb-tiptap-floating lb-tiptap-floating-composer\"\n ref={setFloating}\n style={{\n position: strategy,\n top: 0,\n left: 0,\n transform: `translate3d(${Math.round(x)}px, ${Math.round(y)}px, 0)`,\n minWidth: \"max-content\",\n }}\n >\n <Composer\n ref={forwardedRef}\n autoFocus\n {...props}\n onKeyDown={handleKeyDown}\n onComposerSubmit={handleComposerSubmit}\n onClick={handleClick}\n />\n </div>\n </Portal>\n );\n});\n"],"names":["forwardRef","FloatingComposer","useStableComponent","DefaultComposer","useCreateThread","useEditorState","compareSelections","useFloating","inline","flip","offset","hide","shift","limitShift","size","autoUpdate","useLayoutEffect","getDomRangeFromSelection","useCallback","jsx","Portal"],"mappings":"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"}
@@ -3,14 +3,14 @@ import { useFloating, inline, flip, offset, hide, shift, limitShift, size, autoU
3
3
  import { useCreateThread } from '@liveblocks/react';
4
4
  import { useLayoutEffect } from '@liveblocks/react/_private';
5
5
  import { Composer } from '@liveblocks/react-ui';
6
- import { Portal } from '@liveblocks/react-ui/_private';
6
+ import { useStableComponent, Portal } from '@liveblocks/react-ui/_private';
7
7
  import { useEditorState } from '@tiptap/react';
8
8
  import { forwardRef, useCallback } from 'react';
9
9
  import { compareSelections, getDomRangeFromSelection } from '../utils.js';
10
10
 
11
11
  const FLOATING_COMPOSER_COLLISION_PADDING = 10;
12
12
  const FloatingComposer = forwardRef(function FloatingComposer2({ editor, onComposerSubmit, onKeyDown, onClick, components, ...props }, forwardedRef) {
13
- const Composer$1 = components?.Composer ?? Composer;
13
+ const Composer$1 = useStableComponent(components?.Composer, Composer);
14
14
  const createThread = useCreateThread();
15
15
  const pendingCommentSelection = useEditorState({
16
16
  editor,
@@ -1 +1 @@
1
- {"version":3,"file":"FloatingComposer.js","sources":["../../src/comments/FloatingComposer.tsx"],"sourcesContent":["import {\n autoUpdate,\n flip,\n hide,\n inline,\n limitShift,\n offset,\n shift,\n size,\n useFloating,\n} from \"@floating-ui/react-dom\";\nimport type { BaseMetadata } from \"@liveblocks/client\";\nimport type { DCM, DTM } from \"@liveblocks/core\";\nimport { useCreateThread } from \"@liveblocks/react\";\nimport { useLayoutEffect } from \"@liveblocks/react/_private\";\nimport type {\n ComposerProps,\n ComposerSubmitComment,\n} from \"@liveblocks/react-ui\";\nimport { Composer as DefaultComposer } from \"@liveblocks/react-ui\";\nimport { Portal } from \"@liveblocks/react-ui/_private\";\nimport { type Editor, useEditorState } from \"@tiptap/react\";\nimport type {\n ComponentType,\n FormEvent,\n KeyboardEvent,\n MouseEvent,\n} from \"react\";\nimport { forwardRef, useCallback } from \"react\";\n\nimport type { ExtendedChainedCommands } from \"../types\";\nimport { compareSelections, getDomRangeFromSelection } from \"../utils\";\n\ntype ExcludeProps<T, K extends Record<string, unknown>> = Omit<\n Exclude<T, T & K>,\n keyof K\n>;\n\ntype ComposerPropsCreateThread<\n TM extends BaseMetadata,\n CM extends BaseMetadata,\n> = ExcludeProps<\n ComposerProps<TM, CM>,\n { threadId: string; commentId: string }\n>;\n\ntype FloatingComposerComponents = {\n Composer: ComponentType<ComposerPropsCreateThread<DTM, DCM>>;\n};\n\nexport type FloatingComposerProps<\n TM extends BaseMetadata = DTM,\n CM extends BaseMetadata = DCM,\n> = ComposerPropsCreateThread<TM, CM> & {\n /**\n * Override the component's components.\n */\n components?: Partial<FloatingComposerComponents>;\n\n /**\n * The Tiptap editor.\n */\n editor: Editor | null;\n};\n\nexport const FLOATING_COMPOSER_COLLISION_PADDING = 10;\n\nexport const FloatingComposer = forwardRef<\n HTMLFormElement,\n FloatingComposerProps\n>(function FloatingComposer(\n { editor, onComposerSubmit, onKeyDown, onClick, components, ...props },\n forwardedRef\n) {\n const Composer = components?.Composer ?? DefaultComposer;\n const createThread = useCreateThread();\n const pendingCommentSelection =\n useEditorState({\n editor,\n selector: (ctx) => {\n if (!ctx.editor) {\n return undefined;\n }\n\n const hasPendingComment =\n ctx.editor.storage.liveblocksComments.pendingComment;\n const isEmpty = ctx.editor.state.selection.empty;\n\n return hasPendingComment && !isEmpty\n ? ctx.editor.state.selection\n : undefined;\n },\n equalityFn: compareSelections,\n }) ?? undefined;\n const isOpen = pendingCommentSelection !== undefined;\n const {\n refs: { setReference, setFloating },\n strategy,\n x,\n y,\n } = useFloating({\n strategy: \"fixed\",\n placement: \"bottom\",\n middleware: [\n inline({ padding: FLOATING_COMPOSER_COLLISION_PADDING }),\n flip({ padding: FLOATING_COMPOSER_COLLISION_PADDING, crossAxis: false }),\n offset(10),\n hide({ padding: FLOATING_COMPOSER_COLLISION_PADDING }),\n shift({\n padding: FLOATING_COMPOSER_COLLISION_PADDING,\n limiter: limitShift(),\n }),\n size({ padding: FLOATING_COMPOSER_COLLISION_PADDING }),\n ],\n whileElementsMounted: (...args) => {\n return autoUpdate(...args, {\n animationFrame: true,\n });\n },\n });\n\n useLayoutEffect(() => {\n if (!editor || !isOpen) {\n return;\n }\n\n if (!pendingCommentSelection) {\n setReference(null);\n } else {\n const domRange = getDomRangeFromSelection(\n editor,\n pendingCommentSelection\n );\n\n setReference(domRange);\n }\n }, [pendingCommentSelection, editor, isOpen, setReference]);\n\n // Submit a new thread and update the comment highlight to show a completed highlight\n const handleComposerSubmit = useCallback(\n (comment: ComposerSubmitComment, event: FormEvent<HTMLFormElement>) => {\n onComposerSubmit?.(comment, event);\n if (event.defaultPrevented) return;\n\n if (!editor) {\n return;\n }\n\n event.preventDefault();\n\n const thread = createThread({\n body: comment.body,\n attachments: comment.attachments,\n metadata: props.metadata ?? {},\n });\n editor.commands.addComment(thread.id);\n },\n [onComposerSubmit, editor, createThread, props.metadata]\n );\n\n const handleKeyDown = useCallback(\n (event: KeyboardEvent<HTMLFormElement>) => {\n onKeyDown?.(event);\n\n if (event.isDefaultPrevented() || !editor) {\n return;\n }\n\n if (event.key === \"Escape\") {\n (editor.chain() as ExtendedChainedCommands<\"closePendingComment\">)\n .closePendingComment()\n .run();\n }\n },\n [editor, onKeyDown]\n );\n\n const handleClick = useCallback(\n (event: MouseEvent<HTMLFormElement>) => {\n onClick?.(event);\n\n if (event.defaultPrevented) {\n return;\n }\n\n // Don't send up a click event from emoji picker and close the composer.\n event.stopPropagation();\n },\n [onClick]\n );\n\n if (!isOpen || !editor) {\n return null;\n }\n\n return (\n <Portal asChild>\n <div\n className=\"lb-root lb-portal lb-elevation lb-tiptap-floating lb-tiptap-floating-composer\"\n ref={setFloating}\n style={{\n position: strategy,\n top: 0,\n left: 0,\n transform: `translate3d(${Math.round(x)}px, ${Math.round(y)}px, 0)`,\n minWidth: \"max-content\",\n }}\n >\n <Composer\n ref={forwardedRef}\n autoFocus\n {...props}\n onKeyDown={handleKeyDown}\n onComposerSubmit={handleComposerSubmit}\n onClick={handleClick}\n />\n </div>\n </Portal>\n );\n});\n"],"names":["FloatingComposer","Composer","DefaultComposer"],"mappings":"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"}
1
+ {"version":3,"file":"FloatingComposer.js","sources":["../../src/comments/FloatingComposer.tsx"],"sourcesContent":["import {\n autoUpdate,\n flip,\n hide,\n inline,\n limitShift,\n offset,\n shift,\n size,\n useFloating,\n} from \"@floating-ui/react-dom\";\nimport type { BaseMetadata } from \"@liveblocks/client\";\nimport type { DCM, DTM } from \"@liveblocks/core\";\nimport { useCreateThread } from \"@liveblocks/react\";\nimport { useLayoutEffect } from \"@liveblocks/react/_private\";\nimport type {\n ComposerProps,\n ComposerSubmitComment,\n} from \"@liveblocks/react-ui\";\nimport { Composer as DefaultComposer } from \"@liveblocks/react-ui\";\nimport { Portal, useStableComponent } from \"@liveblocks/react-ui/_private\";\nimport { type Editor, useEditorState } from \"@tiptap/react\";\nimport type {\n ComponentType,\n FormEvent,\n KeyboardEvent,\n MouseEvent,\n} from \"react\";\nimport { forwardRef, useCallback } from \"react\";\n\nimport type { ExtendedChainedCommands } from \"../types\";\nimport { compareSelections, getDomRangeFromSelection } from \"../utils\";\n\ntype ExcludeProps<T, K extends Record<string, unknown>> = Omit<\n Exclude<T, T & K>,\n keyof K\n>;\n\ntype ComposerPropsCreateThread<\n TM extends BaseMetadata,\n CM extends BaseMetadata,\n> = ExcludeProps<\n ComposerProps<TM, CM>,\n { threadId: string; commentId: string }\n>;\n\ntype FloatingComposerComponents = {\n Composer: ComponentType<ComposerPropsCreateThread<DTM, DCM>>;\n};\n\nexport type FloatingComposerProps<\n TM extends BaseMetadata = DTM,\n CM extends BaseMetadata = DCM,\n> = ComposerPropsCreateThread<TM, CM> & {\n /**\n * Override the component's components.\n */\n components?: Partial<FloatingComposerComponents>;\n\n /**\n * The Tiptap editor.\n */\n editor: Editor | null;\n};\n\nexport const FLOATING_COMPOSER_COLLISION_PADDING = 10;\n\nexport const FloatingComposer = forwardRef<\n HTMLFormElement,\n FloatingComposerProps\n>(function FloatingComposer(\n { editor, onComposerSubmit, onKeyDown, onClick, components, ...props },\n forwardedRef\n) {\n const Composer = useStableComponent(components?.Composer, DefaultComposer);\n const createThread = useCreateThread();\n const pendingCommentSelection =\n useEditorState({\n editor,\n selector: (ctx) => {\n if (!ctx.editor) {\n return undefined;\n }\n\n const hasPendingComment =\n ctx.editor.storage.liveblocksComments.pendingComment;\n const isEmpty = ctx.editor.state.selection.empty;\n\n return hasPendingComment && !isEmpty\n ? ctx.editor.state.selection\n : undefined;\n },\n equalityFn: compareSelections,\n }) ?? undefined;\n const isOpen = pendingCommentSelection !== undefined;\n const {\n refs: { setReference, setFloating },\n strategy,\n x,\n y,\n } = useFloating({\n strategy: \"fixed\",\n placement: \"bottom\",\n middleware: [\n inline({ padding: FLOATING_COMPOSER_COLLISION_PADDING }),\n flip({ padding: FLOATING_COMPOSER_COLLISION_PADDING, crossAxis: false }),\n offset(10),\n hide({ padding: FLOATING_COMPOSER_COLLISION_PADDING }),\n shift({\n padding: FLOATING_COMPOSER_COLLISION_PADDING,\n limiter: limitShift(),\n }),\n size({ padding: FLOATING_COMPOSER_COLLISION_PADDING }),\n ],\n whileElementsMounted: (...args) => {\n return autoUpdate(...args, {\n animationFrame: true,\n });\n },\n });\n\n useLayoutEffect(() => {\n if (!editor || !isOpen) {\n return;\n }\n\n if (!pendingCommentSelection) {\n setReference(null);\n } else {\n const domRange = getDomRangeFromSelection(\n editor,\n pendingCommentSelection\n );\n\n setReference(domRange);\n }\n }, [pendingCommentSelection, editor, isOpen, setReference]);\n\n // Submit a new thread and update the comment highlight to show a completed highlight\n const handleComposerSubmit = useCallback(\n (comment: ComposerSubmitComment, event: FormEvent<HTMLFormElement>) => {\n onComposerSubmit?.(comment, event);\n if (event.defaultPrevented) return;\n\n if (!editor) {\n return;\n }\n\n event.preventDefault();\n\n const thread = createThread({\n body: comment.body,\n attachments: comment.attachments,\n metadata: props.metadata ?? {},\n });\n editor.commands.addComment(thread.id);\n },\n [onComposerSubmit, editor, createThread, props.metadata]\n );\n\n const handleKeyDown = useCallback(\n (event: KeyboardEvent<HTMLFormElement>) => {\n onKeyDown?.(event);\n\n if (event.isDefaultPrevented() || !editor) {\n return;\n }\n\n if (event.key === \"Escape\") {\n (editor.chain() as ExtendedChainedCommands<\"closePendingComment\">)\n .closePendingComment()\n .run();\n }\n },\n [editor, onKeyDown]\n );\n\n const handleClick = useCallback(\n (event: MouseEvent<HTMLFormElement>) => {\n onClick?.(event);\n\n if (event.defaultPrevented) {\n return;\n }\n\n // Don't send up a click event from emoji picker and close the composer.\n event.stopPropagation();\n },\n [onClick]\n );\n\n if (!isOpen || !editor) {\n return null;\n }\n\n return (\n <Portal asChild>\n <div\n className=\"lb-root lb-portal lb-elevation lb-tiptap-floating lb-tiptap-floating-composer\"\n ref={setFloating}\n style={{\n position: strategy,\n top: 0,\n left: 0,\n transform: `translate3d(${Math.round(x)}px, ${Math.round(y)}px, 0)`,\n minWidth: \"max-content\",\n }}\n >\n <Composer\n ref={forwardedRef}\n autoFocus\n {...props}\n onKeyDown={handleKeyDown}\n onComposerSubmit={handleComposerSubmit}\n onClick={handleClick}\n />\n </div>\n </Portal>\n );\n});\n"],"names":["FloatingComposer","Composer","DefaultComposer"],"mappings":"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"}
@@ -2,9 +2,9 @@
2
2
 
3
3
  var jsxRuntime = require('react/jsx-runtime');
4
4
  var reactDom = require('@floating-ui/react-dom');
5
- var _private = require('@liveblocks/react/_private');
5
+ var _private$1 = require('@liveblocks/react/_private');
6
6
  var reactUi = require('@liveblocks/react-ui');
7
- var _private$1 = require('@liveblocks/react-ui/_private');
7
+ var _private = require('@liveblocks/react-ui/_private');
8
8
  var react = require('@tiptap/react');
9
9
  var react$1 = require('react');
10
10
  var types = require('../types.cjs');
@@ -15,7 +15,7 @@ function FloatingThreads({
15
15
  editor,
16
16
  ...props
17
17
  }) {
18
- const Thread = components?.Thread ?? reactUi.Thread;
18
+ const Thread = _private.useStableComponent(components?.Thread, reactUi.Thread);
19
19
  const { pluginState } = react.useEditorState({
20
20
  editor,
21
21
  selector: (ctx) => {
@@ -126,8 +126,8 @@ function FloatingThreadPortal({
126
126
  editor.off("transaction", updateRef);
127
127
  };
128
128
  }, [editor, updateRef]);
129
- _private.useLayoutEffect(updateRef, [updateRef]);
130
- return /* @__PURE__ */ jsxRuntime.jsx(_private$1.Portal, { asChild: true, children: /* @__PURE__ */ jsxRuntime.jsx(
129
+ _private$1.useLayoutEffect(updateRef, [updateRef]);
130
+ return /* @__PURE__ */ jsxRuntime.jsx(_private.Portal, { asChild: true, children: /* @__PURE__ */ jsxRuntime.jsx(
131
131
  "div",
132
132
  {
133
133
  ref: setFloating,
@@ -140,7 +140,7 @@ function FloatingThreadPortal({
140
140
  transform: `translate3d(${Math.round(x)}px, ${Math.round(y)}px, 0)`,
141
141
  minWidth: "max-content"
142
142
  },
143
- className: _private$1.cn(
143
+ className: _private.cn(
144
144
  "lb-root lb-portal lb-elevation lb-tiptap-floating lb-tiptap-floating-threads",
145
145
  className
146
146
  ),
@@ -1 +1 @@
1
- {"version":3,"file":"FloatingThreads.cjs","sources":["../../src/comments/FloatingThreads.tsx"],"sourcesContent":["import {\n autoUpdate,\n flip,\n hide,\n limitShift,\n offset,\n shift,\n size,\n useFloating,\n} from \"@floating-ui/react-dom\";\nimport type { BaseMetadata, DCM, DTM, ThreadData } from \"@liveblocks/core\";\nimport { useLayoutEffect } from \"@liveblocks/react/_private\";\nimport {\n Thread as DefaultThread,\n type ThreadProps,\n} from \"@liveblocks/react-ui\";\nimport { cn, Portal } from \"@liveblocks/react-ui/_private\";\nimport { type Editor, useEditorState } from \"@tiptap/react\";\nimport {\n type ComponentType,\n type HTMLAttributes,\n type KeyboardEvent,\n type ReactNode,\n useCallback,\n useEffect,\n useState,\n} from \"react\";\n\nimport { THREADS_PLUGIN_KEY } from \"../types\";\n\ntype FloatingThreadsComponents = {\n Thread: ComponentType<ThreadProps>;\n};\n\nexport interface FloatingThreadsProps<\n TM extends BaseMetadata = DTM,\n CM extends BaseMetadata = DCM,\n> extends Omit<HTMLAttributes<HTMLDivElement>, \"children\"> {\n /**\n * The threads to display.\n */\n threads: ThreadData<TM, CM>[];\n\n /**\n * Override the component's components.\n */\n components?: Partial<FloatingThreadsComponents>;\n\n /**\n * The Tiptap editor.\n */\n editor: Editor | null;\n}\n\nexport function FloatingThreads({\n threads,\n components,\n editor,\n ...props\n}: FloatingThreadsProps) {\n const Thread = components?.Thread ?? DefaultThread;\n\n const { pluginState } = useEditorState({\n editor,\n selector: (ctx) => {\n if (!ctx?.editor?.state) return { pluginState: undefined };\n const state = THREADS_PLUGIN_KEY.getState(ctx.editor.state);\n return {\n pluginState: state,\n };\n },\n equalityFn: (prev, next) => {\n if (!prev || !next) return false;\n return (\n prev.pluginState?.selectedThreadPos ===\n next.pluginState?.selectedThreadPos &&\n prev.pluginState?.selectedThreadId ===\n next.pluginState?.selectedThreadId\n );\n },\n }) ?? { pluginState: undefined };\n\n const [activeThread, setActiveThread] = useState<ThreadData | null>(null);\n\n useEffect(() => {\n if (!editor || !pluginState) {\n setActiveThread(null);\n return;\n }\n const { selectedThreadId, selectedThreadPos } = pluginState;\n if (selectedThreadId === null || selectedThreadPos === null) {\n setActiveThread(null);\n return;\n }\n const active = (threads ?? []).find(\n (thread) => selectedThreadId === thread.id\n );\n setActiveThread(active ?? null);\n }, [editor, pluginState, threads]);\n\n const handleEscapeKeydown = useCallback((): boolean => {\n if (!editor || activeThread === null) return false;\n editor.commands.selectThread(null);\n return true;\n }, [activeThread, editor]);\n\n if (!activeThread || !editor || activeThread.resolved) return null;\n\n return (\n <FloatingThreadPortal thread={activeThread} editor={editor} {...props}>\n {activeThread && (\n <ThreadWrapper\n key={activeThread.id}\n thread={activeThread}\n Thread={Thread}\n onEscapeKeydown={handleEscapeKeydown}\n className=\"lb-tiptap-floating-threads-thread\"\n />\n )}\n </FloatingThreadPortal>\n );\n}\n\ninterface FloatingThreadPortalProps\n extends Omit<HTMLAttributes<HTMLDivElement>, \"children\"> {\n thread: ThreadData;\n editor: Editor;\n children: ReactNode;\n}\n\nexport const FLOATING_THREAD_COLLISION_PADDING = 10;\n\nfunction FloatingThreadPortal({\n editor,\n thread,\n children,\n className,\n style,\n ...props\n}: FloatingThreadPortalProps) {\n const {\n refs: { setReference, setFloating },\n strategy,\n x,\n y,\n } = useFloating({\n strategy: \"absolute\",\n placement: \"bottom\",\n middleware: [\n flip({ padding: FLOATING_THREAD_COLLISION_PADDING, crossAxis: false }),\n offset(10),\n hide({ padding: FLOATING_THREAD_COLLISION_PADDING }),\n shift({\n padding: FLOATING_THREAD_COLLISION_PADDING,\n limiter: limitShift(),\n }),\n size({\n padding: FLOATING_THREAD_COLLISION_PADDING,\n apply({ availableWidth, availableHeight, elements }) {\n elements.floating.style.setProperty(\n \"--lb-tiptap-floating-threads-available-width\",\n `${availableWidth}px`\n );\n elements.floating.style.setProperty(\n \"--lb-tiptap-floating-threads-available-height\",\n `${availableHeight}px`\n );\n },\n }),\n ],\n whileElementsMounted: (...args) => {\n return autoUpdate(...args, {\n animationFrame: true,\n });\n },\n });\n\n const updateRef = useCallback(() => {\n const el = editor.view.dom.querySelector(\n `[data-lb-thread-id=\"${thread.id}\"]`\n );\n if (el) {\n setReference(el);\n }\n }, [setReference, editor, thread.id]);\n\n // Remote cursor updates and other edits can cause the ref to break\n useEffect(() => {\n editor.on(\"transaction\", updateRef);\n return () => {\n editor.off(\"transaction\", updateRef);\n };\n }, [editor, updateRef]);\n\n useLayoutEffect(updateRef, [updateRef]);\n\n return (\n <Portal asChild>\n <div\n ref={setFloating}\n {...props}\n style={{\n ...style,\n position: strategy,\n top: 0,\n left: 0,\n transform: `translate3d(${Math.round(x)}px, ${Math.round(y)}px, 0)`,\n minWidth: \"max-content\",\n }}\n className={cn(\n \"lb-root lb-portal lb-elevation lb-tiptap-floating lb-tiptap-floating-threads\",\n className\n )}\n >\n {children}\n </div>\n </Portal>\n );\n}\n\ninterface ThreadWrapperProps extends ThreadProps {\n thread: ThreadData;\n Thread: ComponentType<ThreadProps>;\n onEscapeKeydown: () => void;\n}\n\nfunction ThreadWrapper({\n thread,\n Thread,\n onEscapeKeydown,\n onKeyDown,\n ...threadProps\n}: ThreadWrapperProps) {\n const handleKeyDown = useCallback(\n (event: KeyboardEvent<HTMLDivElement>) => {\n onKeyDown?.(event);\n\n // TODO: Add ability to preventDefault on keydown to override the default behavior, e.g. to show an alert dialog\n if (event.key === \"Escape\") {\n onEscapeKeydown();\n }\n },\n [onEscapeKeydown, onKeyDown]\n );\n\n return <Thread thread={thread} onKeyDown={handleKeyDown} {...threadProps} />;\n}\n"],"names":["DefaultThread","useEditorState","THREADS_PLUGIN_KEY","useState","useEffect","useCallback","jsx","useFloating","flip","offset","hide","shift","limitShift","size","autoUpdate","useLayoutEffect","Portal","cn"],"mappings":"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"}
1
+ {"version":3,"file":"FloatingThreads.cjs","sources":["../../src/comments/FloatingThreads.tsx"],"sourcesContent":["import {\n autoUpdate,\n flip,\n hide,\n limitShift,\n offset,\n shift,\n size,\n useFloating,\n} from \"@floating-ui/react-dom\";\nimport type { BaseMetadata, DCM, DTM, ThreadData } from \"@liveblocks/core\";\nimport { useLayoutEffect } from \"@liveblocks/react/_private\";\nimport {\n Thread as DefaultThread,\n type ThreadProps,\n} from \"@liveblocks/react-ui\";\nimport { cn, Portal, useStableComponent } from \"@liveblocks/react-ui/_private\";\nimport { type Editor, useEditorState } from \"@tiptap/react\";\nimport {\n type ComponentType,\n type HTMLAttributes,\n type KeyboardEvent,\n type ReactNode,\n useCallback,\n useEffect,\n useState,\n} from \"react\";\n\nimport { THREADS_PLUGIN_KEY } from \"../types\";\n\ntype FloatingThreadsComponents = {\n Thread: ComponentType<ThreadProps>;\n};\n\nexport interface FloatingThreadsProps<\n TM extends BaseMetadata = DTM,\n CM extends BaseMetadata = DCM,\n> extends Omit<HTMLAttributes<HTMLDivElement>, \"children\"> {\n /**\n * The threads to display.\n */\n threads: ThreadData<TM, CM>[];\n\n /**\n * Override the component's components.\n */\n components?: Partial<FloatingThreadsComponents>;\n\n /**\n * The Tiptap editor.\n */\n editor: Editor | null;\n}\n\nexport function FloatingThreads({\n threads,\n components,\n editor,\n ...props\n}: FloatingThreadsProps) {\n const Thread = useStableComponent(components?.Thread, DefaultThread);\n\n const { pluginState } = useEditorState({\n editor,\n selector: (ctx) => {\n if (!ctx?.editor?.state) return { pluginState: undefined };\n const state = THREADS_PLUGIN_KEY.getState(ctx.editor.state);\n return {\n pluginState: state,\n };\n },\n equalityFn: (prev, next) => {\n if (!prev || !next) return false;\n return (\n prev.pluginState?.selectedThreadPos ===\n next.pluginState?.selectedThreadPos &&\n prev.pluginState?.selectedThreadId ===\n next.pluginState?.selectedThreadId\n );\n },\n }) ?? { pluginState: undefined };\n\n const [activeThread, setActiveThread] = useState<ThreadData | null>(null);\n\n useEffect(() => {\n if (!editor || !pluginState) {\n setActiveThread(null);\n return;\n }\n const { selectedThreadId, selectedThreadPos } = pluginState;\n if (selectedThreadId === null || selectedThreadPos === null) {\n setActiveThread(null);\n return;\n }\n const active = (threads ?? []).find(\n (thread) => selectedThreadId === thread.id\n );\n setActiveThread(active ?? null);\n }, [editor, pluginState, threads]);\n\n const handleEscapeKeydown = useCallback((): boolean => {\n if (!editor || activeThread === null) return false;\n editor.commands.selectThread(null);\n return true;\n }, [activeThread, editor]);\n\n if (!activeThread || !editor || activeThread.resolved) return null;\n\n return (\n <FloatingThreadPortal thread={activeThread} editor={editor} {...props}>\n {activeThread && (\n <ThreadWrapper\n key={activeThread.id}\n thread={activeThread}\n Thread={Thread}\n onEscapeKeydown={handleEscapeKeydown}\n className=\"lb-tiptap-floating-threads-thread\"\n />\n )}\n </FloatingThreadPortal>\n );\n}\n\ninterface FloatingThreadPortalProps\n extends Omit<HTMLAttributes<HTMLDivElement>, \"children\"> {\n thread: ThreadData;\n editor: Editor;\n children: ReactNode;\n}\n\nexport const FLOATING_THREAD_COLLISION_PADDING = 10;\n\nfunction FloatingThreadPortal({\n editor,\n thread,\n children,\n className,\n style,\n ...props\n}: FloatingThreadPortalProps) {\n const {\n refs: { setReference, setFloating },\n strategy,\n x,\n y,\n } = useFloating({\n strategy: \"absolute\",\n placement: \"bottom\",\n middleware: [\n flip({ padding: FLOATING_THREAD_COLLISION_PADDING, crossAxis: false }),\n offset(10),\n hide({ padding: FLOATING_THREAD_COLLISION_PADDING }),\n shift({\n padding: FLOATING_THREAD_COLLISION_PADDING,\n limiter: limitShift(),\n }),\n size({\n padding: FLOATING_THREAD_COLLISION_PADDING,\n apply({ availableWidth, availableHeight, elements }) {\n elements.floating.style.setProperty(\n \"--lb-tiptap-floating-threads-available-width\",\n `${availableWidth}px`\n );\n elements.floating.style.setProperty(\n \"--lb-tiptap-floating-threads-available-height\",\n `${availableHeight}px`\n );\n },\n }),\n ],\n whileElementsMounted: (...args) => {\n return autoUpdate(...args, {\n animationFrame: true,\n });\n },\n });\n\n const updateRef = useCallback(() => {\n const el = editor.view.dom.querySelector(\n `[data-lb-thread-id=\"${thread.id}\"]`\n );\n if (el) {\n setReference(el);\n }\n }, [setReference, editor, thread.id]);\n\n // Remote cursor updates and other edits can cause the ref to break\n useEffect(() => {\n editor.on(\"transaction\", updateRef);\n return () => {\n editor.off(\"transaction\", updateRef);\n };\n }, [editor, updateRef]);\n\n useLayoutEffect(updateRef, [updateRef]);\n\n return (\n <Portal asChild>\n <div\n ref={setFloating}\n {...props}\n style={{\n ...style,\n position: strategy,\n top: 0,\n left: 0,\n transform: `translate3d(${Math.round(x)}px, ${Math.round(y)}px, 0)`,\n minWidth: \"max-content\",\n }}\n className={cn(\n \"lb-root lb-portal lb-elevation lb-tiptap-floating lb-tiptap-floating-threads\",\n className\n )}\n >\n {children}\n </div>\n </Portal>\n );\n}\n\ninterface ThreadWrapperProps extends ThreadProps {\n thread: ThreadData;\n Thread: ComponentType<ThreadProps>;\n onEscapeKeydown: () => void;\n}\n\nfunction ThreadWrapper({\n thread,\n Thread,\n onEscapeKeydown,\n onKeyDown,\n ...threadProps\n}: ThreadWrapperProps) {\n const handleKeyDown = useCallback(\n (event: KeyboardEvent<HTMLDivElement>) => {\n onKeyDown?.(event);\n\n // TODO: Add ability to preventDefault on keydown to override the default behavior, e.g. to show an alert dialog\n if (event.key === \"Escape\") {\n onEscapeKeydown();\n }\n },\n [onEscapeKeydown, onKeyDown]\n );\n\n return <Thread thread={thread} onKeyDown={handleKeyDown} {...threadProps} />;\n}\n"],"names":["useStableComponent","DefaultThread","useEditorState","THREADS_PLUGIN_KEY","useState","useEffect","useCallback","jsx","useFloating","flip","offset","hide","shift","limitShift","size","autoUpdate","useLayoutEffect","Portal","cn"],"mappings":"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"}
@@ -2,7 +2,7 @@ import { jsx } from 'react/jsx-runtime';
2
2
  import { useFloating, flip, offset, hide, shift, limitShift, size, autoUpdate } from '@floating-ui/react-dom';
3
3
  import { useLayoutEffect } from '@liveblocks/react/_private';
4
4
  import { Thread } from '@liveblocks/react-ui';
5
- import { Portal, cn } from '@liveblocks/react-ui/_private';
5
+ import { useStableComponent, Portal, cn } from '@liveblocks/react-ui/_private';
6
6
  import { useEditorState } from '@tiptap/react';
7
7
  import { useState, useEffect, useCallback } from 'react';
8
8
  import { THREADS_PLUGIN_KEY } from '../types.js';
@@ -13,7 +13,7 @@ function FloatingThreads({
13
13
  editor,
14
14
  ...props
15
15
  }) {
16
- const Thread$1 = components?.Thread ?? Thread;
16
+ const Thread$1 = useStableComponent(components?.Thread, Thread);
17
17
  const { pluginState } = useEditorState({
18
18
  editor,
19
19
  selector: (ctx) => {