@liveblocks/react-lexical 1.12.0-lexical4 → 2.0.0-alpha2

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.
Files changed (85) hide show
  1. package/dist/classnames.js +8 -0
  2. package/dist/classnames.js.map +1 -0
  3. package/dist/classnames.mjs +6 -0
  4. package/dist/classnames.mjs.map +1 -0
  5. package/dist/comments/ThreadPanel.js +27 -7
  6. package/dist/comments/ThreadPanel.js.map +1 -1
  7. package/dist/comments/ThreadPanel.mjs +28 -8
  8. package/dist/comments/ThreadPanel.mjs.map +1 -1
  9. package/dist/comments/comment-plugin-provider.js +18 -106
  10. package/dist/comments/comment-plugin-provider.js.map +1 -1
  11. package/dist/comments/comment-plugin-provider.mjs +17 -104
  12. package/dist/comments/comment-plugin-provider.mjs.map +1 -1
  13. package/dist/comments/floating-composer.js +226 -15
  14. package/dist/comments/floating-composer.js.map +1 -1
  15. package/dist/comments/floating-composer.mjs +224 -15
  16. package/dist/comments/floating-composer.mjs.map +1 -1
  17. package/dist/comments/thread-mark-node.js +1 -16
  18. package/dist/comments/thread-mark-node.js.map +1 -1
  19. package/dist/comments/thread-mark-node.mjs +1 -16
  20. package/dist/comments/thread-mark-node.mjs.map +1 -1
  21. package/dist/create-dom-range.js +63 -0
  22. package/dist/create-dom-range.js.map +1 -0
  23. package/dist/create-dom-range.mjs +61 -0
  24. package/dist/create-dom-range.mjs.map +1 -0
  25. package/dist/create-rects-from-dom-range.js +36 -0
  26. package/dist/create-rects-from-dom-range.js.map +1 -0
  27. package/dist/create-rects-from-dom-range.mjs +34 -0
  28. package/dist/create-rects-from-dom-range.mjs.map +1 -0
  29. package/dist/index.d.mts +113 -0
  30. package/dist/index.d.ts +96 -44
  31. package/dist/index.js +9 -6
  32. package/dist/index.js.map +1 -1
  33. package/dist/index.mjs +5 -4
  34. package/dist/index.mjs.map +1 -1
  35. package/dist/liveblocks-config.js +3 -75
  36. package/dist/liveblocks-config.js.map +1 -1
  37. package/dist/liveblocks-config.mjs +4 -56
  38. package/dist/liveblocks-config.mjs.map +1 -1
  39. package/dist/liveblocks-plugin-provider.js +12 -29
  40. package/dist/liveblocks-plugin-provider.js.map +1 -1
  41. package/dist/liveblocks-plugin-provider.mjs +13 -29
  42. package/dist/liveblocks-plugin-provider.mjs.map +1 -1
  43. package/dist/mentions/avatar.js +11 -7
  44. package/dist/mentions/avatar.js.map +1 -1
  45. package/dist/mentions/avatar.mjs +11 -7
  46. package/dist/mentions/avatar.mjs.map +1 -1
  47. package/dist/mentions/mention-component.js +5 -18
  48. package/dist/mentions/mention-component.js.map +1 -1
  49. package/dist/mentions/mention-component.mjs +7 -19
  50. package/dist/mentions/mention-component.mjs.map +1 -1
  51. package/dist/mentions/mention-node.js +77 -74
  52. package/dist/mentions/mention-node.js.map +1 -1
  53. package/dist/mentions/mention-node.mjs +76 -75
  54. package/dist/mentions/mention-node.mjs.map +1 -1
  55. package/dist/mentions/mention-plugin.js +130 -91
  56. package/dist/mentions/mention-plugin.js.map +1 -1
  57. package/dist/mentions/mention-plugin.mjs +119 -76
  58. package/dist/mentions/mention-plugin.mjs.map +1 -1
  59. package/dist/mentions/suggestions.js +34 -6
  60. package/dist/mentions/suggestions.js.map +1 -1
  61. package/dist/mentions/suggestions.mjs +28 -3
  62. package/dist/mentions/suggestions.mjs.map +1 -1
  63. package/dist/mentions/user.js +9 -6
  64. package/dist/mentions/user.js.map +1 -1
  65. package/dist/mentions/user.mjs +9 -6
  66. package/dist/mentions/user.mjs.map +1 -1
  67. package/dist/version.js +1 -1
  68. package/dist/version.js.map +1 -1
  69. package/dist/version.mjs +1 -1
  70. package/dist/version.mjs.map +1 -1
  71. package/package.json +23 -15
  72. package/src/styles/constants.css +1 -0
  73. package/src/styles/index.css +154 -0
  74. package/src/styles/utils.css +6 -0
  75. package/styles.css +1 -0
  76. package/styles.css.d.ts +1 -0
  77. package/styles.css.map +1 -0
  78. package/dist/active-selection.js +0 -143
  79. package/dist/active-selection.js.map +0 -1
  80. package/dist/active-selection.mjs +0 -123
  81. package/dist/active-selection.mjs.map +0 -1
  82. package/dist/floating-selection-container.js +0 -157
  83. package/dist/floating-selection-container.js.map +0 -1
  84. package/dist/floating-selection-container.mjs +0 -155
  85. package/dist/floating-selection-container.mjs.map +0 -1
@@ -1 +1 @@
1
- {"version":3,"file":"comment-plugin-provider.mjs","sources":["../../src/comments/comment-plugin-provider.tsx"],"sourcesContent":["import { useLexicalComposerContext } from \"@lexical/react/LexicalComposerContext\";\nimport {\n addClassNamesToElement,\n registerNestedElementResolver,\n removeClassNamesFromElement,\n} from \"@lexical/utils\";\nimport { kInternal } from \"@liveblocks/core\";\nimport {\n CreateThreadError,\n useClient,\n useRoomContextBundle,\n} from \"@liveblocks/react\";\nimport type { BaseSelection, NodeKey, NodeMutation } from \"lexical\";\nimport {\n $getNodeByKey,\n $getSelection,\n $isRangeSelection,\n $isTextNode,\n $setSelection,\n} from \"lexical\";\nimport type { PropsWithChildren } from \"react\";\nimport * as React from \"react\";\nimport { useCallback, useEffect, useState } from \"react\";\nimport { useSyncExternalStoreWithSelector } from \"use-sync-external-store/shim/with-selector.js\";\n\nimport { ActiveSelection } from \"../active-selection\";\nimport $getThreadMarkIds from \"./get-thread-mark-ids\";\nimport {\n $createThreadMarkNode,\n $isThreadMarkNode,\n ThreadMarkNode,\n} from \"./thread-mark-node\";\nimport $unwrapThreadMarkNode from \"./unwrap-thread-mark-node\";\nimport $wrapSelectionInThreadMarkNode from \"./wrap-selection-in-thread-mark-node\";\n\ntype ThreadToNodesMap = Map<string, Set<NodeKey>>;\n\ntype ShowFloatingComposerContextType = {\n showFloatingComposer: boolean;\n setShowFloatingComposer: (show: boolean) => void;\n} | null;\nconst ShowFloatingComposerContext =\n React.createContext<ShowFloatingComposerContextType>(null);\n\nconst ShowFloatingComposerProvider = ({ children }: PropsWithChildren) => {\n const [showFloatingComposer, setShowFloatingComposer] = useState(false);\n return (\n <ShowFloatingComposerContext.Provider\n value={{\n setShowFloatingComposer,\n showFloatingComposer,\n }}\n >\n {children}\n </ShowFloatingComposerContext.Provider>\n );\n};\n\nexport function useCreateThread() {\n const context = React.useContext(ShowFloatingComposerContext);\n if (context === null) {\n throw new Error(\n \"useCreateThread must be used within a LiveblocksPluginProvider with comments enabled\"\n );\n }\n\n return () => {\n context.setShowFloatingComposer(true);\n };\n}\n\nexport function useShowFloatingComposer() {\n const context = React.useContext(ShowFloatingComposerContext);\n if (context === null) {\n throw new Error(\n \"useShowFloatingComposer must be used within a LiveblocksPluginProvider with comments enabled\"\n );\n }\n\n return context.showFloatingComposer;\n}\n\nexport function useHideFloatingComposer() {\n const context = React.useContext(ShowFloatingComposerContext);\n if (context === null) {\n throw new Error(\n \"useHideFloatingComposer must be used within a LiveblocksPluginProvider with comments enabled\"\n );\n }\n\n return () => {\n context.setShowFloatingComposer(false);\n };\n}\n\nexport function CommentPluginProvider({ children }: PropsWithChildren) {\n const [editor, context] = useLexicalComposerContext();\n\n const [threadToNodes, setThreadToNodes] = useState<ThreadToNodesMap>(\n new Map()\n ); // A map from thread id to a set of (thread mark) node keys that are associated with the thread\n\n const [activeThreads, setActiveThreads] = useState<string[]>([]); // The threads that are currently active (or selected) in the editor\n\n const [showActiveSelection, setShowActiveSelection] = useState(false);\n\n const client = useClient();\n const {\n useRoom,\n [kInternal]: {\n useCommentsErrorListener,\n ThreadCreateCallbackProvider,\n ThreadDeleteCallbackProvider,\n ComposerFocusCallbackProvider,\n IsThreadActiveCallbackProvider,\n },\n } = useRoomContextBundle();\n\n const room = useRoom();\n\n useEffect(() => {\n if (!editor.hasNodes([ThreadMarkNode])) {\n throw new Error(\n \"CommentPluginProvider: ThreadMarkNode not registered on editor\"\n );\n }\n }, [editor]);\n\n const isThreadActive = useCallback(\n (threadId: string) => {\n return activeThreads.includes(threadId);\n },\n [activeThreads]\n );\n\n /**\n * Create a new ThreadMarkNode and wrap the selected content in it.\n * @param threadId The id of the thread to associate with the selected content\n */\n const handleThreadCreate = useCallback(\n (threadId: string) => {\n editor.update(() => {\n const selection = $getSelection();\n if (!$isRangeSelection(selection)) return;\n\n // If the selection is collapsed, we do not create a new thread node in the editor.\n if (selection.isCollapsed()) return;\n\n const isBackward = selection.isBackward();\n // Wrap content in a ThreadMarkNode\n $wrapSelectionInThreadMarkNode(selection, isBackward, threadId);\n\n // Clear the selection after wrapping\n $setSelection(null);\n });\n },\n [editor]\n );\n\n /**\n * Remove the thread id from the associated nodes and unwrap the nodes if they are no longer associated with any threads.\n * @param threadId The id of the thread to remove\n */\n const handleThreadDelete = useCallback(\n (threadId: string) => {\n editor.update(() => {\n // Retrieve node keys associated with the thread\n const keys = threadToNodes.get(threadId);\n if (keys === undefined) return;\n\n // Iterate over each thread node and disassociate the thread if from the node and unwrap the node if it is no longer associated with any threads\n for (const key of keys) {\n const node = $getNodeByKey(key);\n if (!$isThreadMarkNode(node)) continue;\n node.deleteID(threadId);\n\n if (node.getIDs().length === 0) {\n $unwrapThreadMarkNode(node);\n }\n }\n });\n },\n [editor, threadToNodes]\n );\n\n useCommentsErrorListener((error) => {\n // If thread creation fails, we remove the thread id from the associated nodes and unwrap the nodes if they are no longer associated with any threads\n if (error instanceof CreateThreadError) {\n handleThreadDelete(error.context.threadId);\n }\n });\n\n const store = client[kInternal].cacheStore;\n\n const threads = useSyncExternalStoreWithSelector(\n store.subscribe,\n store.get,\n store.get,\n useCallback(\n () =>\n client[kInternal].comments.selectedThreads(room.id, store.get(), {}),\n [client, room.id, store]\n )\n );\n\n /**\n * Only add styles to the thread mark elements that currently have threads associated with them.\n */\n useEffect(() => {\n function getThreadMarkElements() {\n const activeElements = new Set<HTMLElement>();\n\n for (const thread of threads) {\n const keys = threadToNodes.get(thread.id);\n if (keys === undefined) continue;\n\n for (const key of keys) {\n const element = editor.getElementByKey(key);\n if (element === null) continue;\n activeElements.add(element);\n }\n }\n return activeElements;\n }\n\n const elements = getThreadMarkElements();\n\n const theme = context.getTheme();\n if (theme === null || theme === undefined) return;\n\n elements.forEach((element) => {\n addClassNamesToElement(element, theme.threadMark as string);\n });\n\n return () => {\n elements.forEach((element) => {\n removeClassNamesFromElement(element, theme.threadMark as string);\n });\n };\n }, [context, editor, threadToNodes, threads]);\n\n /**\n * Register a mutation listener that listens for mutations on 'ThreadMarkNode's and updates the map of thread to node keys accordingly.\n */\n useEffect(() => {\n function onMutation(mutations: Map<string, NodeMutation>) {\n const state = editor.getEditorState();\n setThreadToNodes((prev) => {\n const updatedMap = new Map(prev);\n state.read(() => {\n for (const [key, mutation] of mutations) {\n // If the node is destroyed, we remove its key from the map of thread to node keys\n if (mutation === \"destroyed\") {\n for (const [, nodes] of updatedMap) {\n nodes.delete(key);\n }\n }\n // Otherwise, if a new node is created or an existing node is updated, we update the map of thread to node keys to include the new/updated node\n else if (mutation === \"created\" || mutation === \"updated\") {\n const node = $getNodeByKey(key);\n if (!$isThreadMarkNode(node)) continue;\n\n const threadIds = node.getIDs();\n\n for (const id of threadIds) {\n const keys = updatedMap.get(id) ?? new Set();\n keys.add(key);\n updatedMap.set(id, keys);\n }\n }\n }\n });\n return updatedMap;\n });\n }\n\n return editor.registerMutationListener(ThreadMarkNode, onMutation);\n }, [editor]);\n\n /**\n * Register an update listener that listens for changes in the selection and updates the active threads accordingly.\n */\n useEffect(() => {\n const selectedThreads = client[kInternal].comments.selectedThreads;\n\n function $getThreadIds(selection: BaseSelection | null): string[] {\n if (selection === null) return [];\n\n if (!$isRangeSelection(selection)) return [];\n\n const anchor = selection.anchor.getNode();\n if (!$isTextNode(anchor)) return [];\n\n return $getThreadMarkIds(anchor, selection.anchor.offset) ?? [];\n }\n\n function $onStateRead() {\n const selection = $getSelection();\n\n const threadIds = $getThreadIds(selection).filter((id) => {\n return selectedThreads(room.id, store.get(), {}).some(\n (thread) => thread.id === id\n );\n });\n setActiveThreads(threadIds);\n }\n\n const unsubscribeCache = store.subscribe(() => {\n editor.getEditorState().read($onStateRead);\n });\n\n const unregisterUpdateListener = editor.registerUpdateListener(\n ({ editorState: state }) => {\n state.read($onStateRead);\n }\n );\n\n return () => {\n unregisterUpdateListener();\n unsubscribeCache();\n };\n }, [editor, client, room.id, store]);\n\n /**\n * When active threads change, we add a data-state attribute and set it to \"active\" for all HTML elements that are associated with the active threads.\n */\n useEffect(() => {\n function getActiveElements() {\n const activeElements = new Set<HTMLElement>();\n\n for (const thread of activeThreads) {\n const keys = threadToNodes.get(thread);\n if (keys === undefined) continue;\n\n for (const key of keys) {\n const element = editor.getElementByKey(key);\n if (element === null) continue;\n activeElements.add(element);\n }\n }\n return activeElements;\n }\n\n const activeElements = getActiveElements();\n\n activeElements.forEach((element) => {\n element.setAttribute(\"data-state\", \"active\");\n });\n\n return () => {\n activeElements.forEach((element) => {\n element.removeAttribute(\"data-state\");\n });\n };\n }, [activeThreads, editor, threadToNodes]);\n\n useEffect(() => {\n return registerNestedElementResolver<ThreadMarkNode>(\n editor,\n ThreadMarkNode,\n (from: ThreadMarkNode) => {\n return $createThreadMarkNode(from.getIDs());\n },\n (from: ThreadMarkNode, to: ThreadMarkNode) => {\n const ids = from.getIDs();\n ids.forEach((id) => {\n to.addID(id);\n });\n }\n );\n }, [editor]);\n\n const handleComposerFocus = useCallback(\n (commentId: string | undefined, threadId: string | undefined) => {\n if (commentId === undefined && threadId === undefined) {\n setShowActiveSelection(true);\n } else {\n setShowActiveSelection(false);\n }\n },\n []\n );\n\n useEffect(() => {\n return editor.registerUpdateListener(({ editorState: state, tags }) => {\n // Ignore selection updates related to collaboration\n if (tags.has(\"collaboration\")) return;\n state.read(() => setShowActiveSelection(false));\n });\n }, [editor]);\n\n return (\n <ShowFloatingComposerProvider>\n <ThreadCreateCallbackProvider value={handleThreadCreate}>\n <ThreadDeleteCallbackProvider value={handleThreadDelete}>\n <ComposerFocusCallbackProvider value={handleComposerFocus}>\n <IsThreadActiveCallbackProvider value={isThreadActive}>\n {showActiveSelection && <ActiveSelection />}\n {children}\n </IsThreadActiveCallbackProvider>\n </ComposerFocusCallbackProvider>\n </ThreadDeleteCallbackProvider>\n </ThreadCreateCallbackProvider>\n </ShowFloatingComposerProvider>\n );\n}\n"],"names":["activeElements"],"mappings":"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"}
1
+ {"version":3,"file":"comment-plugin-provider.mjs","sources":["../../src/comments/comment-plugin-provider.tsx"],"sourcesContent":["import { useLexicalComposerContext } from \"@lexical/react/LexicalComposerContext\";\nimport {\n addClassNamesToElement,\n registerNestedElementResolver,\n removeClassNamesFromElement,\n} from \"@lexical/utils\";\nimport { kInternal } from \"@liveblocks/core\";\nimport {\n CreateThreadError,\n selectedThreads,\n useClient,\n useCommentsErrorListener,\n useRoom,\n} from \"@liveblocks/react\";\nimport type { BaseSelection, NodeKey, NodeMutation } from \"lexical\";\nimport {\n $getNodeByKey,\n $getSelection,\n $isRangeSelection,\n $isTextNode,\n} from \"lexical\";\nimport type { PropsWithChildren } from \"react\";\nimport * as React from \"react\";\nimport { createContext, useCallback, useEffect, useState } from \"react\";\nimport { useSyncExternalStoreWithSelector } from \"use-sync-external-store/shim/with-selector.js\";\n\nimport $getThreadMarkIds from \"./get-thread-mark-ids\";\nimport {\n $createThreadMarkNode,\n $isThreadMarkNode,\n ThreadMarkNode,\n} from \"./thread-mark-node\";\nimport $unwrapThreadMarkNode from \"./unwrap-thread-mark-node\";\n\nexport const OnDeleteThreadCallback = createContext<\n ((threadId: string) => void) | null\n>(null);\n\nexport const IsActiveThreadContext = createContext<\n (threadId: string) => boolean\n>((_: string) => false);\n\ntype ThreadToNodesMap = Map<string, Set<NodeKey>>;\n\nexport function CommentPluginProvider({ children }: PropsWithChildren) {\n const [editor, context] = useLexicalComposerContext();\n\n const [threadToNodes, setThreadToNodes] = useState<ThreadToNodesMap>(\n new Map()\n ); // A map from thread id to a set of (thread mark) node keys that are associated with the thread\n\n const [activeThreads, setActiveThreads] = useState<string[]>([]); // The threads that are currently active (or selected) in the editor\n\n const client = useClient();\n\n const room = useRoom();\n\n useEffect(() => {\n if (!editor.hasNodes([ThreadMarkNode])) {\n throw new Error(\n \"CommentPluginProvider: ThreadMarkNode not registered on editor\"\n );\n }\n }, [editor]);\n\n const isThreadActive = useCallback(\n (threadId: string) => {\n return activeThreads.includes(threadId);\n },\n [activeThreads]\n );\n\n /**\n * Remove the thread id from the associated nodes and unwrap the nodes if they are no longer associated with any threads.\n * @param threadId The id of the thread to remove\n */\n const handleThreadDelete = useCallback(\n (threadId: string) => {\n editor.update(() => {\n // Retrieve node keys associated with the thread\n const keys = threadToNodes.get(threadId);\n if (keys === undefined) return;\n\n // Iterate over each thread node and disassociate the thread if from the node and unwrap the node if it is no longer associated with any threads\n for (const key of keys) {\n const node = $getNodeByKey(key);\n if (!$isThreadMarkNode(node)) continue;\n node.deleteID(threadId);\n\n if (node.getIDs().length === 0) {\n $unwrapThreadMarkNode(node);\n }\n }\n });\n },\n [editor, threadToNodes]\n );\n\n useCommentsErrorListener((error) => {\n // If thread creation fails, we remove the thread id from the associated nodes and unwrap the nodes if they are no longer associated with any threads\n if (error instanceof CreateThreadError) {\n handleThreadDelete(error.context.threadId);\n }\n });\n\n const store = client[kInternal].cacheStore;\n\n const threads = useSyncExternalStoreWithSelector(\n store.subscribe,\n store.get,\n store.get,\n useCallback(\n () => selectedThreads(room.id, store.get(), {}),\n [room.id, store]\n )\n );\n\n /**\n * Only add styles to the thread mark elements that currently have threads associated with them.\n */\n useEffect(() => {\n function getThreadMarkElements() {\n const activeElements = new Set<HTMLElement>();\n\n for (const thread of threads) {\n const keys = threadToNodes.get(thread.id);\n if (keys === undefined) continue;\n\n for (const key of keys) {\n const element = editor.getElementByKey(key);\n if (element === null) continue;\n activeElements.add(element);\n }\n }\n return activeElements;\n }\n\n const elements = getThreadMarkElements();\n\n const theme = context.getTheme();\n\n const classNames = [\"lb-thread-mark\"];\n if (theme && theme.liveblocks && \"threadMark\" in theme.liveblocks) {\n classNames.push((theme.liveblocks as { threadMark: string }).threadMark);\n }\n\n elements.forEach((element) => {\n addClassNamesToElement(element, ...classNames);\n });\n\n return () => {\n elements.forEach((element) => {\n removeClassNamesFromElement(element, ...classNames);\n });\n };\n }, [context, editor, threadToNodes, threads]);\n\n /**\n * Register a mutation listener that listens for mutations on 'ThreadMarkNode's and updates the map of thread to node keys accordingly.\n */\n useEffect(() => {\n function onMutation(mutations: Map<string, NodeMutation>) {\n const state = editor.getEditorState();\n setThreadToNodes((prev) => {\n const updatedMap = new Map(prev);\n state.read(() => {\n for (const [key, mutation] of mutations) {\n // If the node is destroyed, we remove its key from the map of thread to node keys\n if (mutation === \"destroyed\") {\n for (const [, nodes] of updatedMap) {\n nodes.delete(key);\n }\n }\n // Otherwise, if a new node is created or an existing node is updated, we update the map of thread to node keys to include the new/updated node\n else if (mutation === \"created\" || mutation === \"updated\") {\n const node = $getNodeByKey(key);\n if (!$isThreadMarkNode(node)) continue;\n\n const threadIds = node.getIDs();\n\n for (const id of threadIds) {\n const keys = updatedMap.get(id) ?? new Set();\n keys.add(key);\n updatedMap.set(id, keys);\n }\n }\n }\n });\n return updatedMap;\n });\n }\n\n return editor.registerMutationListener(ThreadMarkNode, onMutation);\n }, [editor]);\n\n /**\n * Register an update listener that listens for changes in the selection and updates the active threads accordingly.\n */\n useEffect(() => {\n function $getThreadIds(selection: BaseSelection | null): string[] {\n if (selection === null) return [];\n\n if (!$isRangeSelection(selection)) return [];\n\n const anchor = selection.anchor.getNode();\n if (!$isTextNode(anchor)) return [];\n\n return $getThreadMarkIds(anchor, selection.anchor.offset) ?? [];\n }\n\n function $onStateRead() {\n const selection = $getSelection();\n\n const threadIds = $getThreadIds(selection).filter((id) => {\n return selectedThreads(room.id, store.get(), {}).some(\n (thread) => thread.id === id\n );\n });\n setActiveThreads(threadIds);\n }\n\n const unsubscribeCache = store.subscribe(() => {\n editor.getEditorState().read($onStateRead);\n });\n\n const unregisterUpdateListener = editor.registerUpdateListener(\n ({ editorState: state }) => {\n state.read($onStateRead);\n }\n );\n\n return () => {\n unregisterUpdateListener();\n unsubscribeCache();\n };\n }, [editor, client, room.id, store]);\n\n /**\n * When active threads change, we add a data-state attribute and set it to \"active\" for all HTML elements that are associated with the active threads.\n */\n useEffect(() => {\n function getActiveElements() {\n const activeElements = new Set<HTMLElement>();\n\n for (const thread of activeThreads) {\n const keys = threadToNodes.get(thread);\n if (keys === undefined) continue;\n\n for (const key of keys) {\n const element = editor.getElementByKey(key);\n if (element === null) continue;\n activeElements.add(element);\n }\n }\n return activeElements;\n }\n\n const activeElements = getActiveElements();\n\n activeElements.forEach((element) => {\n element.setAttribute(\"data-state\", \"active\");\n });\n\n return () => {\n activeElements.forEach((element) => {\n element.removeAttribute(\"data-state\");\n });\n };\n }, [activeThreads, editor, threadToNodes]);\n\n useEffect(() => {\n return registerNestedElementResolver<ThreadMarkNode>(\n editor,\n ThreadMarkNode,\n (from: ThreadMarkNode) => {\n return $createThreadMarkNode(from.getIDs());\n },\n (from: ThreadMarkNode, to: ThreadMarkNode) => {\n const ids = from.getIDs();\n ids.forEach((id) => {\n to.addID(id);\n });\n }\n );\n }, [editor]);\n\n return (\n <OnDeleteThreadCallback.Provider value={handleThreadDelete}>\n <IsActiveThreadContext.Provider value={isThreadActive}>\n {children}\n </IsActiveThreadContext.Provider>\n </OnDeleteThreadCallback.Provider>\n );\n}\n"],"names":["activeElements"],"mappings":"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"}
@@ -1,34 +1,245 @@
1
1
  'use strict';
2
2
 
3
- var reactComments = require('@liveblocks/react-comments');
4
- var React = require('react');
5
- var floatingSelectionContainer = require('../floating-selection-container.js');
6
- var commentPluginProvider = require('./comment-plugin-provider.js');
3
+ var reactDom = require('@floating-ui/react-dom');
7
4
  var LexicalComposerContext = require('@lexical/react/LexicalComposerContext');
5
+ var react = require('@liveblocks/react');
6
+ var reactUi = require('@liveblocks/react-ui');
7
+ var lexical = require('lexical');
8
+ var React = require('react');
9
+ var reactDom$1 = require('react-dom');
10
+ var createDomRange = require('../create-dom-range.js');
11
+ var createRectsFromDomRange = require('../create-rects-from-dom-range.js');
12
+ var wrapSelectionInThreadMarkNode = require('./wrap-selection-in-thread-mark-node.js');
8
13
 
14
+ const OPEN_FLOATING_COMPOSER_COMMAND = lexical.createCommand("OPEN_FLOATING_COMPOSER_COMMAND");
9
15
  const FloatingComposer = React.forwardRef(function FloatingComposer2(props, forwardedRef) {
10
- const shouldShowFloatingComposer = commentPluginProvider.useShowFloatingComposer();
11
- const hideFloatingComposer = commentPluginProvider.useHideFloatingComposer();
16
+ const [range, setRange] = React.useState(null);
12
17
  const [editor] = LexicalComposerContext.useLexicalComposerContext();
13
- if (!shouldShowFloatingComposer)
18
+ React.useEffect(() => {
19
+ return editor.registerCommand(
20
+ OPEN_FLOATING_COMPOSER_COMMAND,
21
+ () => {
22
+ const selection = lexical.$getSelection();
23
+ if (!lexical.$isRangeSelection(selection))
24
+ return false;
25
+ if (selection.isCollapsed())
26
+ return false;
27
+ const { anchor, focus } = selection;
28
+ const range2 = createDomRange.createDOMRange(
29
+ editor,
30
+ anchor.getNode(),
31
+ anchor.offset,
32
+ focus.getNode(),
33
+ focus.offset
34
+ );
35
+ setRange(range2);
36
+ return true;
37
+ },
38
+ lexical.COMMAND_PRIORITY_EDITOR
39
+ );
40
+ }, [editor]);
41
+ if (range === null)
14
42
  return null;
43
+ return /* @__PURE__ */ React.createElement(FloatingComposerImpl, {
44
+ ref: forwardedRef,
45
+ ...props,
46
+ range,
47
+ onRangeChange: setRange
48
+ });
49
+ });
50
+ const FloatingComposerImpl = React.forwardRef(function FloatingComposer3(props, forwardedRef) {
51
+ const {
52
+ range,
53
+ onRangeChange,
54
+ onKeyDown,
55
+ onComposerSubmit,
56
+ ...composerProps
57
+ } = props;
58
+ const [editor] = LexicalComposerContext.useLexicalComposerContext();
59
+ const createThread = react.useCreateThread();
60
+ const $onStateRead = React.useCallback(() => {
61
+ const selection = lexical.$getSelection();
62
+ if (!lexical.$isRangeSelection(selection) || selection.isCollapsed()) {
63
+ return null;
64
+ }
65
+ const { anchor, focus } = selection;
66
+ const range2 = createDomRange.createDOMRange(
67
+ editor,
68
+ anchor.getNode(),
69
+ anchor.offset,
70
+ focus.getNode(),
71
+ focus.offset
72
+ );
73
+ return range2;
74
+ }, [editor]);
75
+ React.useEffect(() => {
76
+ return editor.registerUpdateListener(({ editorState: state, tags }) => {
77
+ if (!tags.has("collaboration")) {
78
+ onRangeChange(null);
79
+ return;
80
+ }
81
+ const range2 = state.read(() => $onStateRead());
82
+ onRangeChange(range2);
83
+ });
84
+ }, [editor, range, onRangeChange, $onStateRead]);
85
+ const onThreadCreate = React.useCallback(
86
+ (threadId) => {
87
+ editor.update(() => {
88
+ const selection = lexical.$getSelection();
89
+ if (!lexical.$isRangeSelection(selection))
90
+ return;
91
+ if (selection.isCollapsed())
92
+ return;
93
+ const isBackward = selection.isBackward();
94
+ wrapSelectionInThreadMarkNode(selection, isBackward, threadId);
95
+ lexical.$setSelection(null);
96
+ });
97
+ },
98
+ [editor]
99
+ );
100
+ const handleComposerSubmit = React.useCallback(
101
+ (comment, event) => {
102
+ onComposerSubmit?.(comment, event);
103
+ if (event.defaultPrevented)
104
+ return;
105
+ event.preventDefault();
106
+ const thread = createThread({
107
+ body: comment.body,
108
+ metadata: props.metadata ?? {}
109
+ });
110
+ onThreadCreate(thread.id);
111
+ },
112
+ [onThreadCreate, onComposerSubmit, props.metadata, createThread]
113
+ );
15
114
  function handleKeyDown(event) {
16
115
  if (event.key === "Escape") {
17
- hideFloatingComposer();
116
+ onRangeChange(null);
18
117
  editor.focus();
19
118
  }
119
+ onKeyDown?.(event);
20
120
  }
21
- return /* @__PURE__ */ React.createElement(floatingSelectionContainer.FloatingSelectionContainer, {
22
- sideOffset: 5,
23
- alignOffset: 0,
24
- collisionPadding: 5
25
- }, /* @__PURE__ */ React.createElement(reactComments.Composer, {
121
+ return /* @__PURE__ */ React.createElement(React.Fragment, null, /* @__PURE__ */ React.createElement(ActiveSelectionPortal, {
122
+ range,
123
+ container: document.body
124
+ }), /* @__PURE__ */ React.createElement(FloatingComposerPortal, {
125
+ range,
126
+ container: document.body
127
+ }, /* @__PURE__ */ React.createElement(reactUi.Composer, {
26
128
  autoFocus: true,
129
+ ...composerProps,
27
130
  onKeyDown: handleKeyDown,
28
- ...props,
131
+ onComposerSubmit: handleComposerSubmit,
29
132
  ref: forwardedRef
30
- }));
133
+ })));
31
134
  });
135
+ function ActiveSelectionPortal({
136
+ range,
137
+ container
138
+ }) {
139
+ const {
140
+ refs: { setReference, setFloating },
141
+ strategy,
142
+ x,
143
+ y
144
+ } = reactDom.useFloating({
145
+ strategy: "fixed",
146
+ placement: "bottom",
147
+ middleware: [reactDom.offset(-range.getBoundingClientRect().height)],
148
+ whileElementsMounted: (...args) => {
149
+ return reactDom.autoUpdate(...args, {
150
+ animationFrame: true
151
+ });
152
+ }
153
+ });
154
+ React.useLayoutEffect(() => {
155
+ setReference({
156
+ getBoundingClientRect: () => range.getBoundingClientRect()
157
+ });
158
+ }, [setReference, range]);
159
+ const [editor] = LexicalComposerContext.useLexicalComposerContext();
160
+ const rects = createRectsFromDomRange.createRectsFromDOMRange(editor, range);
161
+ return reactDom$1.createPortal(
162
+ /* @__PURE__ */ React.createElement(React.Fragment, null, /* @__PURE__ */ React.createElement("span", {
163
+ ref: setFloating,
164
+ style: {
165
+ position: strategy,
166
+ top: 0,
167
+ left: 0,
168
+ transform: `translate3d(${Math.round(x)}px, ${Math.round(y)}px, 0)`,
169
+ minWidth: "max-content",
170
+ width: range.getBoundingClientRect().width,
171
+ height: range.getBoundingClientRect().height,
172
+ pointerEvents: "none"
173
+ },
174
+ className: "lb-root lb-portal"
175
+ }, rects.map((rect) => /* @__PURE__ */ React.createElement("span", {
176
+ key: JSON.stringify(rect),
177
+ style: {
178
+ position: "absolute",
179
+ top: rect.top - range.getBoundingClientRect().top,
180
+ left: rect.left - range.getBoundingClientRect().left,
181
+ width: rect.width,
182
+ height: rect.height,
183
+ backgroundColor: "rgba(0, 0, 255, 0.2)",
184
+ pointerEvents: "none"
185
+ },
186
+ className: "lb-lexical-active-selection"
187
+ })))),
188
+ container
189
+ );
190
+ }
191
+ const FLOATING_COMPOSER_COLLISION_PADDING = 10;
192
+ function FloatingComposerPortal({
193
+ container,
194
+ range,
195
+ children
196
+ }) {
197
+ const {
198
+ refs: { setReference, setFloating },
199
+ strategy,
200
+ x,
201
+ y
202
+ } = reactDom.useFloating({
203
+ strategy: "fixed",
204
+ placement: "bottom",
205
+ middleware: [
206
+ reactDom.flip({ padding: FLOATING_COMPOSER_COLLISION_PADDING, crossAxis: false }),
207
+ reactDom.offset(10),
208
+ reactDom.hide({ padding: FLOATING_COMPOSER_COLLISION_PADDING }),
209
+ reactDom.shift({
210
+ padding: FLOATING_COMPOSER_COLLISION_PADDING,
211
+ limiter: reactDom.limitShift()
212
+ }),
213
+ reactDom.size({ padding: FLOATING_COMPOSER_COLLISION_PADDING })
214
+ ],
215
+ whileElementsMounted: (...args) => {
216
+ return reactDom.autoUpdate(...args, {
217
+ animationFrame: true
218
+ });
219
+ }
220
+ });
221
+ React.useLayoutEffect(() => {
222
+ setReference({
223
+ getBoundingClientRect: () => range.getBoundingClientRect()
224
+ });
225
+ }, [range, setReference]);
226
+ return reactDom$1.createPortal(
227
+ /* @__PURE__ */ React.createElement("div", {
228
+ ref: setFloating,
229
+ style: {
230
+ position: strategy,
231
+ top: 0,
232
+ left: 0,
233
+ transform: `translate3d(${Math.round(x)}px, ${Math.round(y)}px, 0)`,
234
+ minWidth: "max-content"
235
+ },
236
+ className: "lb-root lb-portal lb-elevation lb-lexical-floating-composer"
237
+ }, children),
238
+ container
239
+ );
240
+ }
32
241
 
242
+ exports.FLOATING_COMPOSER_COLLISION_PADDING = FLOATING_COMPOSER_COLLISION_PADDING;
33
243
  exports.FloatingComposer = FloatingComposer;
244
+ exports.OPEN_FLOATING_COMPOSER_COMMAND = OPEN_FLOATING_COMPOSER_COMMAND;
34
245
  //# sourceMappingURL=floating-composer.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"floating-composer.js","sources":["../../src/comments/floating-composer.tsx"],"sourcesContent":["import { Composer, ComposerProps } from \"@liveblocks/react-comments\";\nimport React, { ComponentRef, forwardRef, KeyboardEvent } from \"react\";\nimport { FloatingSelectionContainer } from \"../floating-selection-container\";\nimport type { BaseMetadata } from \"@liveblocks/core\";\nimport {\n useHideFloatingComposer,\n useShowFloatingComposer,\n} from \"./comment-plugin-provider\";\nimport { useLexicalComposerContext } from \"@lexical/react/LexicalComposerContext\";\n\ntype ComposerElement = ComponentRef<typeof Composer>;\n\ntype ThreadMetadata = {\n resolved?: boolean;\n};\n\ntype FloatingComposerProps<M extends BaseMetadata = ThreadMetadata> = Omit<\n ComposerProps<M>,\n \"threadId\" | \"commentId\"\n>;\n\nexport const FloatingComposer = forwardRef<\n ComposerElement,\n FloatingComposerProps\n>(function FloatingComposer(props, forwardedRef) {\n const shouldShowFloatingComposer = useShowFloatingComposer();\n const hideFloatingComposer = useHideFloatingComposer();\n const [editor] = useLexicalComposerContext();\n\n if (!shouldShowFloatingComposer) return null;\n\n function handleKeyDown(event: KeyboardEvent<HTMLFormElement>) {\n if (event.key === \"Escape\") {\n hideFloatingComposer();\n editor.focus();\n }\n }\n\n return (\n <FloatingSelectionContainer\n sideOffset={5}\n alignOffset={0}\n collisionPadding={5}\n >\n <Composer\n autoFocus\n onKeyDown={handleKeyDown}\n {...props}\n ref={forwardedRef}\n />\n </FloatingSelectionContainer>\n );\n});\n"],"names":["forwardRef","FloatingComposer","useShowFloatingComposer","useHideFloatingComposer","useLexicalComposerContext","FloatingSelectionContainer","Composer"],"mappings":";;;;;;;;AAqBO,MAAM,gBAAmB,GAAAA,gBAAA,CAG9B,SAASC,iBAAAA,CAAiB,OAAO,YAAc,EAAA;AAC/C,EAAA,MAAM,6BAA6BC,6CAAwB,EAAA,CAAA;AAC3D,EAAA,MAAM,uBAAuBC,6CAAwB,EAAA,CAAA;AACrD,EAAM,MAAA,CAAC,MAAM,CAAA,GAAIC,gDAA0B,EAAA,CAAA;AAE3C,EAAA,IAAI,CAAC,0BAAA;AAA4B,IAAO,OAAA,IAAA,CAAA;AAExC,EAAA,SAAS,cAAc,KAAuC,EAAA;AAC5D,IAAI,IAAA,KAAA,CAAM,QAAQ,QAAU,EAAA;AAC1B,MAAqB,oBAAA,EAAA,CAAA;AACrB,MAAA,MAAA,CAAO,KAAM,EAAA,CAAA;AAAA,KACf;AAAA,GACF;AAEA,EAAA,uBACG,KAAA,CAAA,aAAA,CAAAC,qDAAA,EAAA;AAAA,IACC,UAAY,EAAA,CAAA;AAAA,IACZ,WAAa,EAAA,CAAA;AAAA,IACb,gBAAkB,EAAA,CAAA;AAAA,GAAA,kBAEjB,KAAA,CAAA,aAAA,CAAAC,sBAAA,EAAA;AAAA,IACC,SAAS,EAAA,IAAA;AAAA,IACT,SAAW,EAAA,aAAA;AAAA,IACV,GAAG,KAAA;AAAA,IACJ,GAAK,EAAA,YAAA;AAAA,GACP,CACF,CAAA,CAAA;AAEJ,CAAC;;;;"}
1
+ {"version":3,"file":"floating-composer.js","sources":["../../src/comments/floating-composer.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 { useLexicalComposerContext } from \"@lexical/react/LexicalComposerContext\";\nimport type { BaseMetadata } from \"@liveblocks/core\";\nimport { useCreateThread } from \"@liveblocks/react\";\nimport type {\n ComposerProps,\n ComposerSubmitComment,\n} from \"@liveblocks/react-ui\";\nimport { Composer } from \"@liveblocks/react-ui\";\nimport type { LexicalCommand } from \"lexical\";\nimport {\n $getSelection,\n $isRangeSelection,\n $setSelection,\n COMMAND_PRIORITY_EDITOR,\n createCommand,\n} from \"lexical\";\nimport type { ComponentRef, FormEvent, KeyboardEvent, ReactNode } from \"react\";\nimport React, {\n forwardRef,\n useCallback,\n useEffect,\n useLayoutEffect,\n useState,\n} from \"react\";\nimport { createPortal } from \"react-dom\";\n\nimport { createDOMRange } from \"../create-dom-range\";\nimport { createRectsFromDOMRange } from \"../create-rects-from-dom-range\";\nimport $wrapSelectionInThreadMarkNode from \"./wrap-selection-in-thread-mark-node\";\n\nexport const OPEN_FLOATING_COMPOSER_COMMAND: LexicalCommand<void> =\n createCommand(\"OPEN_FLOATING_COMPOSER_COMMAND\");\n\ntype ComposerElement = ComponentRef<typeof Composer>;\n\ntype ThreadMetadata = {\n resolved?: boolean;\n};\n\ntype FloatingComposerProps<M extends BaseMetadata = ThreadMetadata> = Omit<\n ComposerProps<M>,\n \"threadId\" | \"commentId\"\n>;\n\nexport const FloatingComposer = forwardRef<\n ComposerElement,\n FloatingComposerProps\n>(function FloatingComposer(props, forwardedRef) {\n const [range, setRange] = useState<Range | null>(null);\n const [editor] = useLexicalComposerContext();\n\n useEffect(() => {\n return editor.registerCommand(\n OPEN_FLOATING_COMPOSER_COMMAND,\n () => {\n const selection = $getSelection();\n if (!$isRangeSelection(selection)) return false;\n\n if (selection.isCollapsed()) return false;\n\n const { anchor, focus } = selection;\n\n const range = createDOMRange(\n editor,\n anchor.getNode(),\n anchor.offset,\n focus.getNode(),\n focus.offset\n );\n\n setRange(range);\n\n return true;\n },\n COMMAND_PRIORITY_EDITOR\n );\n }, [editor]);\n\n if (range === null) return null;\n\n return (\n <FloatingComposerImpl\n ref={forwardedRef}\n {...props}\n range={range}\n onRangeChange={setRange}\n />\n );\n});\n\ninterface FloatingComposerImplProps extends FloatingComposerProps {\n range: Range;\n onRangeChange: (range: Range | null) => void;\n}\n\nconst FloatingComposerImpl = forwardRef<\n ComposerElement,\n FloatingComposerImplProps\n>(function FloatingComposer(props, forwardedRef) {\n const {\n range,\n onRangeChange,\n onKeyDown,\n onComposerSubmit,\n ...composerProps\n } = props;\n\n const [editor] = useLexicalComposerContext();\n const createThread = useCreateThread();\n\n const $onStateRead = useCallback((): Range | null => {\n const selection = $getSelection();\n\n // If the selection is not a range selection or is collapsed, clear the range so the composer is no longer displayed.\n if (!$isRangeSelection(selection) || selection.isCollapsed()) {\n return null;\n }\n\n const { anchor, focus } = selection;\n const range = createDOMRange(\n editor,\n anchor.getNode(),\n anchor.offset,\n focus.getNode(),\n focus.offset\n );\n\n return range;\n }, [editor]);\n\n useEffect(() => {\n return editor.registerUpdateListener(({ editorState: state, tags }) => {\n // If the update is not related to collaboration, clear the range so the composer is no longer displayed.\n if (!tags.has(\"collaboration\")) {\n onRangeChange(null);\n return;\n }\n\n const range = state.read(() => $onStateRead());\n onRangeChange(range);\n });\n }, [editor, range, onRangeChange, $onStateRead]);\n\n /**\n * Create a new ThreadMarkNode and wrap the selected content in it.\n * @param threadId The id of the thread to associate with the selected content\n */\n const onThreadCreate = useCallback(\n (threadId: string) => {\n editor.update(() => {\n const selection = $getSelection();\n if (!$isRangeSelection(selection)) return;\n\n // If the selection is collapsed, we do not create a new thread node in the editor.\n if (selection.isCollapsed()) return;\n\n const isBackward = selection.isBackward();\n // Wrap content in a ThreadMarkNode\n $wrapSelectionInThreadMarkNode(selection, isBackward, threadId);\n\n // Clear the selection after wrapping\n $setSelection(null);\n });\n },\n [editor]\n );\n\n const handleComposerSubmit = useCallback(\n (comment: ComposerSubmitComment, event: FormEvent<HTMLFormElement>) => {\n onComposerSubmit?.(comment, event);\n if (event.defaultPrevented) return;\n\n event.preventDefault();\n\n const thread = createThread({\n body: comment.body,\n metadata: props.metadata ?? {},\n });\n\n onThreadCreate(thread.id);\n },\n [onThreadCreate, onComposerSubmit, props.metadata, createThread]\n );\n\n function handleKeyDown(event: KeyboardEvent<HTMLFormElement>) {\n if (event.key === \"Escape\") {\n onRangeChange(null);\n editor.focus();\n }\n onKeyDown?.(event);\n }\n\n return (\n <>\n <ActiveSelectionPortal range={range} container={document.body} />\n\n <FloatingComposerPortal range={range} container={document.body}>\n <Composer\n autoFocus\n {...composerProps}\n onKeyDown={handleKeyDown}\n onComposerSubmit={handleComposerSubmit}\n ref={forwardedRef}\n />\n </FloatingComposerPortal>\n </>\n );\n});\n\nfunction ActiveSelectionPortal({\n range,\n container,\n}: {\n range: Range;\n container: HTMLElement;\n}) {\n const {\n refs: { setReference, setFloating },\n strategy,\n x,\n y,\n } = useFloating({\n strategy: \"fixed\",\n placement: \"bottom\",\n middleware: [offset(-range.getBoundingClientRect().height)],\n whileElementsMounted: (...args) => {\n return autoUpdate(...args, {\n animationFrame: true,\n });\n },\n });\n\n useLayoutEffect(() => {\n setReference({\n getBoundingClientRect: () => range.getBoundingClientRect(),\n });\n }, [setReference, range]);\n\n const [editor] = useLexicalComposerContext();\n const rects = createRectsFromDOMRange(editor, range);\n\n return createPortal(\n <>\n <span\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 width: range.getBoundingClientRect().width,\n height: range.getBoundingClientRect().height,\n pointerEvents: \"none\",\n }}\n className=\"lb-root lb-portal\"\n >\n {rects.map((rect) => (\n <span\n key={JSON.stringify(rect)}\n style={{\n position: \"absolute\",\n top: rect.top - range.getBoundingClientRect().top,\n left: rect.left - range.getBoundingClientRect().left,\n width: rect.width,\n height: rect.height,\n backgroundColor: \"rgba(0, 0, 255, 0.2)\",\n pointerEvents: \"none\",\n }}\n className=\"lb-lexical-active-selection\"\n />\n ))}\n </span>\n </>,\n container\n );\n}\n\nexport const FLOATING_COMPOSER_COLLISION_PADDING = 10;\n\nfunction FloatingComposerPortal({\n container,\n range,\n children,\n}: {\n container: HTMLElement;\n range: Range;\n children: ReactNode;\n}) {\n const {\n refs: { setReference, setFloating },\n strategy,\n x,\n y,\n } = useFloating({\n strategy: \"fixed\",\n placement: \"bottom\",\n middleware: [\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 setReference({\n getBoundingClientRect: () => range.getBoundingClientRect(),\n });\n }, [range, setReference]);\n\n return createPortal(\n <div\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 className=\"lb-root lb-portal lb-elevation lb-lexical-floating-composer\"\n >\n {children}\n </div>,\n container\n );\n}\n"],"names":["createCommand","forwardRef","FloatingComposer","useState","useLexicalComposerContext","useEffect","$getSelection","$isRangeSelection","range","createDOMRange","COMMAND_PRIORITY_EDITOR","useCreateThread","useCallback","$wrapSelectionInThreadMarkNode","$setSelection","Composer","useFloating","offset","autoUpdate","useLayoutEffect","createRectsFromDOMRange","createPortal","flip","hide","shift","limitShift","size"],"mappings":"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"}
@@ -1,32 +1,241 @@
1
- import { Composer } from '@liveblocks/react-comments';
2
- import React__default, { forwardRef } from 'react';
3
- import { FloatingSelectionContainer } from '../floating-selection-container.mjs';
4
- import { useShowFloatingComposer, useHideFloatingComposer } from './comment-plugin-provider.mjs';
1
+ import { useFloating, offset, autoUpdate, flip, hide, shift, limitShift, size } from '@floating-ui/react-dom';
5
2
  import { useLexicalComposerContext } from '@lexical/react/LexicalComposerContext';
3
+ import { useCreateThread } from '@liveblocks/react';
4
+ import { Composer } from '@liveblocks/react-ui';
5
+ import { createCommand, $getSelection, $isRangeSelection, COMMAND_PRIORITY_EDITOR, $setSelection } from 'lexical';
6
+ import React__default, { forwardRef, useState, useEffect, useCallback, useLayoutEffect } from 'react';
7
+ import { createPortal } from 'react-dom';
8
+ import { createDOMRange } from '../create-dom-range.mjs';
9
+ import { createRectsFromDOMRange } from '../create-rects-from-dom-range.mjs';
10
+ import $wrapSelectionInThreadMarkNode from './wrap-selection-in-thread-mark-node.mjs';
6
11
 
12
+ const OPEN_FLOATING_COMPOSER_COMMAND = createCommand("OPEN_FLOATING_COMPOSER_COMMAND");
7
13
  const FloatingComposer = forwardRef(function FloatingComposer2(props, forwardedRef) {
8
- const shouldShowFloatingComposer = useShowFloatingComposer();
9
- const hideFloatingComposer = useHideFloatingComposer();
14
+ const [range, setRange] = useState(null);
10
15
  const [editor] = useLexicalComposerContext();
11
- if (!shouldShowFloatingComposer)
16
+ useEffect(() => {
17
+ return editor.registerCommand(
18
+ OPEN_FLOATING_COMPOSER_COMMAND,
19
+ () => {
20
+ const selection = $getSelection();
21
+ if (!$isRangeSelection(selection))
22
+ return false;
23
+ if (selection.isCollapsed())
24
+ return false;
25
+ const { anchor, focus } = selection;
26
+ const range2 = createDOMRange(
27
+ editor,
28
+ anchor.getNode(),
29
+ anchor.offset,
30
+ focus.getNode(),
31
+ focus.offset
32
+ );
33
+ setRange(range2);
34
+ return true;
35
+ },
36
+ COMMAND_PRIORITY_EDITOR
37
+ );
38
+ }, [editor]);
39
+ if (range === null)
12
40
  return null;
41
+ return /* @__PURE__ */ React__default.createElement(FloatingComposerImpl, {
42
+ ref: forwardedRef,
43
+ ...props,
44
+ range,
45
+ onRangeChange: setRange
46
+ });
47
+ });
48
+ const FloatingComposerImpl = forwardRef(function FloatingComposer3(props, forwardedRef) {
49
+ const {
50
+ range,
51
+ onRangeChange,
52
+ onKeyDown,
53
+ onComposerSubmit,
54
+ ...composerProps
55
+ } = props;
56
+ const [editor] = useLexicalComposerContext();
57
+ const createThread = useCreateThread();
58
+ const $onStateRead = useCallback(() => {
59
+ const selection = $getSelection();
60
+ if (!$isRangeSelection(selection) || selection.isCollapsed()) {
61
+ return null;
62
+ }
63
+ const { anchor, focus } = selection;
64
+ const range2 = createDOMRange(
65
+ editor,
66
+ anchor.getNode(),
67
+ anchor.offset,
68
+ focus.getNode(),
69
+ focus.offset
70
+ );
71
+ return range2;
72
+ }, [editor]);
73
+ useEffect(() => {
74
+ return editor.registerUpdateListener(({ editorState: state, tags }) => {
75
+ if (!tags.has("collaboration")) {
76
+ onRangeChange(null);
77
+ return;
78
+ }
79
+ const range2 = state.read(() => $onStateRead());
80
+ onRangeChange(range2);
81
+ });
82
+ }, [editor, range, onRangeChange, $onStateRead]);
83
+ const onThreadCreate = useCallback(
84
+ (threadId) => {
85
+ editor.update(() => {
86
+ const selection = $getSelection();
87
+ if (!$isRangeSelection(selection))
88
+ return;
89
+ if (selection.isCollapsed())
90
+ return;
91
+ const isBackward = selection.isBackward();
92
+ $wrapSelectionInThreadMarkNode(selection, isBackward, threadId);
93
+ $setSelection(null);
94
+ });
95
+ },
96
+ [editor]
97
+ );
98
+ const handleComposerSubmit = useCallback(
99
+ (comment, event) => {
100
+ onComposerSubmit?.(comment, event);
101
+ if (event.defaultPrevented)
102
+ return;
103
+ event.preventDefault();
104
+ const thread = createThread({
105
+ body: comment.body,
106
+ metadata: props.metadata ?? {}
107
+ });
108
+ onThreadCreate(thread.id);
109
+ },
110
+ [onThreadCreate, onComposerSubmit, props.metadata, createThread]
111
+ );
13
112
  function handleKeyDown(event) {
14
113
  if (event.key === "Escape") {
15
- hideFloatingComposer();
114
+ onRangeChange(null);
16
115
  editor.focus();
17
116
  }
117
+ onKeyDown?.(event);
18
118
  }
19
- return /* @__PURE__ */ React__default.createElement(FloatingSelectionContainer, {
20
- sideOffset: 5,
21
- alignOffset: 0,
22
- collisionPadding: 5
119
+ return /* @__PURE__ */ React__default.createElement(React__default.Fragment, null, /* @__PURE__ */ React__default.createElement(ActiveSelectionPortal, {
120
+ range,
121
+ container: document.body
122
+ }), /* @__PURE__ */ React__default.createElement(FloatingComposerPortal, {
123
+ range,
124
+ container: document.body
23
125
  }, /* @__PURE__ */ React__default.createElement(Composer, {
24
126
  autoFocus: true,
127
+ ...composerProps,
25
128
  onKeyDown: handleKeyDown,
26
- ...props,
129
+ onComposerSubmit: handleComposerSubmit,
27
130
  ref: forwardedRef
28
- }));
131
+ })));
29
132
  });
133
+ function ActiveSelectionPortal({
134
+ range,
135
+ container
136
+ }) {
137
+ const {
138
+ refs: { setReference, setFloating },
139
+ strategy,
140
+ x,
141
+ y
142
+ } = useFloating({
143
+ strategy: "fixed",
144
+ placement: "bottom",
145
+ middleware: [offset(-range.getBoundingClientRect().height)],
146
+ whileElementsMounted: (...args) => {
147
+ return autoUpdate(...args, {
148
+ animationFrame: true
149
+ });
150
+ }
151
+ });
152
+ useLayoutEffect(() => {
153
+ setReference({
154
+ getBoundingClientRect: () => range.getBoundingClientRect()
155
+ });
156
+ }, [setReference, range]);
157
+ const [editor] = useLexicalComposerContext();
158
+ const rects = createRectsFromDOMRange(editor, range);
159
+ return createPortal(
160
+ /* @__PURE__ */ React__default.createElement(React__default.Fragment, null, /* @__PURE__ */ React__default.createElement("span", {
161
+ ref: setFloating,
162
+ style: {
163
+ position: strategy,
164
+ top: 0,
165
+ left: 0,
166
+ transform: `translate3d(${Math.round(x)}px, ${Math.round(y)}px, 0)`,
167
+ minWidth: "max-content",
168
+ width: range.getBoundingClientRect().width,
169
+ height: range.getBoundingClientRect().height,
170
+ pointerEvents: "none"
171
+ },
172
+ className: "lb-root lb-portal"
173
+ }, rects.map((rect) => /* @__PURE__ */ React__default.createElement("span", {
174
+ key: JSON.stringify(rect),
175
+ style: {
176
+ position: "absolute",
177
+ top: rect.top - range.getBoundingClientRect().top,
178
+ left: rect.left - range.getBoundingClientRect().left,
179
+ width: rect.width,
180
+ height: rect.height,
181
+ backgroundColor: "rgba(0, 0, 255, 0.2)",
182
+ pointerEvents: "none"
183
+ },
184
+ className: "lb-lexical-active-selection"
185
+ })))),
186
+ container
187
+ );
188
+ }
189
+ const FLOATING_COMPOSER_COLLISION_PADDING = 10;
190
+ function FloatingComposerPortal({
191
+ container,
192
+ range,
193
+ children
194
+ }) {
195
+ const {
196
+ refs: { setReference, setFloating },
197
+ strategy,
198
+ x,
199
+ y
200
+ } = useFloating({
201
+ strategy: "fixed",
202
+ placement: "bottom",
203
+ middleware: [
204
+ flip({ padding: FLOATING_COMPOSER_COLLISION_PADDING, crossAxis: false }),
205
+ offset(10),
206
+ hide({ padding: FLOATING_COMPOSER_COLLISION_PADDING }),
207
+ shift({
208
+ padding: FLOATING_COMPOSER_COLLISION_PADDING,
209
+ limiter: limitShift()
210
+ }),
211
+ size({ padding: FLOATING_COMPOSER_COLLISION_PADDING })
212
+ ],
213
+ whileElementsMounted: (...args) => {
214
+ return autoUpdate(...args, {
215
+ animationFrame: true
216
+ });
217
+ }
218
+ });
219
+ useLayoutEffect(() => {
220
+ setReference({
221
+ getBoundingClientRect: () => range.getBoundingClientRect()
222
+ });
223
+ }, [range, setReference]);
224
+ return createPortal(
225
+ /* @__PURE__ */ React__default.createElement("div", {
226
+ ref: setFloating,
227
+ style: {
228
+ position: strategy,
229
+ top: 0,
230
+ left: 0,
231
+ transform: `translate3d(${Math.round(x)}px, ${Math.round(y)}px, 0)`,
232
+ minWidth: "max-content"
233
+ },
234
+ className: "lb-root lb-portal lb-elevation lb-lexical-floating-composer"
235
+ }, children),
236
+ container
237
+ );
238
+ }
30
239
 
31
- export { FloatingComposer };
240
+ export { FLOATING_COMPOSER_COLLISION_PADDING, FloatingComposer, OPEN_FLOATING_COMPOSER_COMMAND };
32
241
  //# sourceMappingURL=floating-composer.mjs.map