import { useEffect, useRef, useState } from "react"; /** * The notes for a page, written in the margin beside it. * * This is where a reader has always put remarks: level with the passage, in the * white space, not over the words. Writing them onto the page would cover the * sentence the note is about, which is the one thing a note about a sentence * must not do. * * Each card sits at the height of its anchor. Cards keep one height so that two * notes close together can be laid out without measuring: they are simply pushed * down until they no longer overlap, and the marker's hairline still says which * spot each belongs to. The card being written in grows and rises above the * others, because that one is being read closely and the rest are not. */ export const MARGIN_W = 208; const CARD_H = 74; const CARD_GAP = 6; /** Cards at their anchors, pushed down where they would sit on top of each other. */ function layout(notes, pageHeight, cssHeight) { let previousBottom = -Infinity; return notes .slice() .sort((a, b) => a.y - b.y) .map((note) => { const anchor = (note.y / pageHeight) * cssHeight - 12; const top = Math.max(anchor, previousBottom + CARD_GAP); previousBottom = top + CARD_H; return { note, top }; }); } function Card({ T, note, top, number, open, onOpen, onChange, onRemove }) { const [draft, setDraft] = useState(note.text); const areaRef = useRef(null); // Adopt text that arrived from elsewhere — another device, or an undo — but // never while this card is the one being typed into. useEffect(() => { if (!open) setDraft(note.text); }, [note.text, open]); useEffect(() => { if (open) areaRef.current?.focus(); }, [open]); // Committed on the way out, not per keystroke: every save rewrites the whole // overlay, and one per character would be absurd. const commit = () => { if (draft !== note.text) onChange(draft); }; return (