/** * Comment controls painted over the document, never into it. * * The collaborative tree is prose alone. Thread chrome belongs to one reader, * so this adapter resolves its anchors into DOM rectangles and portals ordinary * React controls into the document page. */ import { useEffect, useLayoutEffect, useRef, useState } from "react"; import { createPortal } from "react-dom"; import { useLexicalComposerContext } from "@lexical/react/LexicalComposerContext"; import { DraftCard, ThreadCard } from "./comments"; import { gutterPoint, popoverPoint } from "./comment-geometry"; import { $rangeOf } from "./marks"; import { blockElement } from "./scroll"; import { useThreads } from "./threads"; import type { CSSProperties } from "react"; import type { Point, Rect } from "./comment-geometry"; import type { ThreadStore, ThreadView } from "./threads"; type PlacedThread = { view: ThreadView; button: Point }; function rect(value: DOMRect): Rect { return { top: value.top, right: value.right, bottom: value.bottom, left: value.left, width: value.width, height: value.height, }; } /** A small thread summary while a reader is deciding whether to open it. */ function Preview({ onEnter, onLeave, style, view, }: { onEnter: () => void; onLeave: () => void; style: CSSProperties; view: ThreadView; }) { let replies = Math.max(0, view.thread.notes.length - 1); return (
{view.quote}
{replies > 0 && {replies} {replies === 1 ? "reply" : "replies"}}