{"version":3,"file":"floating-threads.cjs","sources":["../../src/comments/floating-threads.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, ThreadData } from \"@liveblocks/client\";\nimport type { DCM, DTM } 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 {\n  $getSelection,\n  COMMAND_PRIORITY_HIGH,\n  KEY_ESCAPE_COMMAND,\n} from \"lexical\";\nimport {\n  type ComponentType,\n  type HTMLAttributes,\n  type KeyboardEvent,\n  type ReactNode,\n  useCallback,\n  useContext,\n  useEffect,\n  useState,\n  useSyncExternalStore,\n} from \"react\";\n\nimport { compareNodes } from \"./anchored-threads\";\nimport {\n  ActiveThreadsContext,\n  type ThreadToNodesMap,\n} from \"./comment-plugin-provider\";\nimport { ThreadToNodesContext } from \"./comment-plugin-provider\";\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\nexport function FloatingThreads({\n  threads,\n  components,\n  ...props\n}: FloatingThreadsProps) {\n  const activeThreads = useActiveThreads();\n\n  const Thread = useStableComponent(components?.Thread, DefaultThread);\n\n  const [editor] = useLexicalComposerContext();\n  const nodes = useThreadToNodes(); // A map of thread ids to a set of thread mark nodes associated with the thread\n\n  const [range, setRange] = useState<{\n    range: Range;\n    threads: ThreadData[];\n  } | null>(null);\n\n  const handleUpdateRange = useCallback(() => {\n    function getActiveRange(): Range | null {\n      function getActiveElements() {\n        const activeElements = new Set<HTMLElement>();\n\n        for (const thread of activeThreads) {\n          const keys = nodes.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      const sortedElements = Array.from(activeElements).sort(compareNodes);\n      if (sortedElements.length === 0) return null;\n\n      const range = document.createRange();\n      range.setStartBefore(sortedElements[0]);\n      range.setEndAfter(sortedElements[sortedElements.length - 1]);\n\n      return range;\n    }\n\n    const active = (threads ?? []).filter((thread) =>\n      activeThreads.includes(thread.id)\n    );\n\n    const range = getActiveRange();\n    if (range === null) {\n      setRange(null);\n      return;\n    }\n\n    setRange({ range, threads: active });\n  }, [activeThreads, nodes, editor, threads]);\n\n  useEffect(() => {\n    handleUpdateRange();\n  }, [handleUpdateRange]);\n\n  useEffect(() => {\n    return editor.registerUpdateListener(handleUpdateRange);\n  }, [editor, handleUpdateRange]);\n\n  const handleEscapeKeydown = useCallback((): boolean => {\n    if (range === null) return false;\n    setRange(null);\n    return true;\n  }, [range]);\n\n  useEffect(() => {\n    return editor.registerCommand(\n      KEY_ESCAPE_COMMAND,\n      handleEscapeKeydown,\n      COMMAND_PRIORITY_HIGH\n    );\n  }, [editor, handleEscapeKeydown]);\n\n  const isCollapsed = useIsSelectionCollapsed();\n\n  if (range === null || isCollapsed === null || !isCollapsed) return null;\n\n  return (\n    <FloatingThreadPortal range={range.range} {...props}>\n      {range.threads.map((thread) => (\n        <ThreadWrapper\n          key={thread.id}\n          thread={thread}\n          Thread={Thread}\n          onEscapeKeydown={handleEscapeKeydown}\n          className=\"lb-lexical-floating-threads-thread\"\n        />\n      ))}\n    </FloatingThreadPortal>\n  );\n}\n\ninterface FloatingThreadPortalProps extends Omit<\n  HTMLAttributes<HTMLDivElement>,\n  \"children\"\n> {\n  range: Range;\n  children: ReactNode;\n}\n\nexport const FLOATING_THREAD_COLLISION_PADDING = 10;\n\nfunction FloatingThreadPortal({\n  range,\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-lexical-floating-threads-available-width\",\n            `${availableWidth}px`\n          );\n          elements.floating.style.setProperty(\n            \"--lb-lexical-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  useLayoutEffect(() => {\n    setReference({\n      getBoundingClientRect: () => range.getBoundingClientRect(),\n    });\n  }, [setReference, range]);\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-lexical-floating lb-lexical-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\nfunction useThreadToNodes(): ThreadToNodesMap {\n  const threadToNodes = useContext(ThreadToNodesContext);\n  if (threadToNodes === null) {\n    throw new Error(\n      \"FloatingThreads component must be used within a LiveblocksPlugin component.\"\n    );\n  }\n  return threadToNodes;\n}\n\nfunction useIsSelectionCollapsed(): boolean | null {\n  const [editor] = useLexicalComposerContext();\n\n  const subscribe = useCallback(\n    (onStoreChange: () => void) => {\n      return editor.registerUpdateListener(onStoreChange);\n    },\n    [editor]\n  );\n\n  const getSnapshot = useCallback(() => {\n    return editor.getEditorState().read(() => {\n      const selection = $getSelection();\n      if (selection === null) return null;\n      return selection.isCollapsed();\n    });\n  }, [editor]);\n\n  return useSyncExternalStore(subscribe, getSnapshot, getSnapshot);\n}\n\nfunction useActiveThreads() {\n  const activeThreads = useContext(ActiveThreadsContext);\n  if (activeThreads === null) {\n    throw new Error(\n      \"FloatingThreads component must be used within LiveblocksPlugin.\"\n    );\n  }\n\n  return activeThreads;\n}\n"],"names":["useStableComponent","DefaultThread","useLexicalComposerContext","useState","useCallback","activeElements","compareNodes","range","useEffect","KEY_ESCAPE_COMMAND","COMMAND_PRIORITY_HIGH","jsx","useFloating","flip","offset","hide","shift","limitShift","size","autoUpdate","useLayoutEffect","Portal","cn","useContext","ThreadToNodesContext","$getSelection","useSyncExternalStore","ActiveThreadsContext"],"mappings":"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"}