diff --git a/client/src/State.ts b/client/src/State.ts index 8d64399..ae3a804 100644 --- a/client/src/State.ts +++ b/client/src/State.ts @@ -773,7 +773,6 @@ const stateAtom = atom( // we can't do that inside the 'create' function (which is just about // making changes *within* the state, so we do it at the top of the function - case "setSelectionStart": { if (state.ux.mode !== ApplicationMode.SelectingNewCitation) { return; @@ -791,27 +790,45 @@ const stateAtom = atom( state.ux.excerpt = summary.excerpt; state.ux.bounds = summaryToBounds(summary, true); state.ux.isSelecting = true; + state.ux.hoverBounds = undefined; break; } case "setSelectionEnd": { // Can only set cursor range in SelectingNewCitation mode - if (state.ux.mode !== ApplicationMode.SelectingNewCitation || !state.ux.isSelecting ) { + if (state.ux.mode !== ApplicationMode.SelectingNewCitation) { return; } + const start = state.ux.isSelecting ? state.ux.start : action.end; const page = state.ux.pageNumber; + const summary = rangeToSummary( - { start: { page, point: state.ux.start }, end: { page, point: action.end } }, + { start: { page, point: start }, end: { page, point: action.end } }, docFromId[state.ux.documentId].di ); const bounds = summaryToBounds(summary, true); - if (!boundsAreEqual(bounds, state.ux.bounds)) { - state.ux.bounds = bounds; - state.ux.excerpt = summary.excerpt; + if (state.ux.isSelecting) { + if (!boundsAreEqual(bounds, state.ux.bounds)) { + state.ux.bounds = bounds; + state.ux.excerpt = summary.excerpt; + } + } else { + if (state.ux.hoverBounds === undefined || !boundsAreEqual(bounds, state.ux.hoverBounds)) { + state.ux.hoverBounds = bounds; + } + } + break; + } + + case "endSelectionHover": { + if (state.ux.mode !== ApplicationMode.SelectingNewCitation) { + return; } + + state.ux.hoverBounds = undefined; break; } diff --git a/client/src/Types.ts b/client/src/Types.ts index eecc80b..8e0e39f 100644 --- a/client/src/Types.ts +++ b/client/src/Types.ts @@ -139,6 +139,9 @@ export type Action = type: "setSelectionEnd"; end: Point; } + | { + type: "endSelectionHover"; + } | { type: "endSelection"; } @@ -316,6 +319,7 @@ export interface SelectingNewCitationModeState extends BaseDocumentModeState { start: Point; excerpt?: string; bounds?: Bounds[]; + hoverBounds?: Bounds[]; } // Resizing an existing citation diff --git a/client/src/Viewer.tsx b/client/src/Viewer.tsx index 92b9a68..6958141 100644 --- a/client/src/Viewer.tsx +++ b/client/src/Viewer.tsx @@ -37,8 +37,7 @@ export function Viewer() { const viewerRef = useRef(null); const mode = ux.mode; - const isSelecting = mode === ApplicationMode.SelectingNewCitation && ux.isSelecting; - + useEffect(() => { const viewerElem = viewerRef.current; @@ -57,13 +56,10 @@ export function Viewer() { } const handleMouseUp = () => { - console.assert(isSelecting, "Mouse up without mouse down"); dispatch({ type: "endSelection" }); } const handleMouseMove = (e: MouseEvent) => { - if (!isSelecting) return; - const rect = viewerElem.getBoundingClientRect(); dispatch({ @@ -74,16 +70,22 @@ export function Viewer() { }); } + const handleMouseLeave = () => { + dispatch({ type: "endSelectionHover" }); + } + viewerElem.addEventListener("mousedown", handleMouseDown); viewerElem.addEventListener("mouseup", handleMouseUp); viewerElem.addEventListener("mousemove", handleMouseMove); + viewerElem.addEventListener("mouseleave", handleMouseLeave); return () => { viewerElem.removeEventListener("mousedown", handleMouseDown); viewerElem.removeEventListener("mouseup", handleMouseUp); viewerElem.removeEventListener("mousemove", handleMouseMove); + viewerElem.removeEventListener("mouseleave", handleMouseLeave); }; - }, [isSelecting, mode, dispatch]); + }, [mode, dispatch]); const onDocumentLoadSuccess = useCallback(() => { }, []); @@ -222,15 +224,30 @@ const AddSelection = () => { if (ux.mode !== ApplicationMode.SelectingNewCitation) return null; - const { pageNumber, bounds } = ux; + const { pageNumber, bounds, hoverBounds } = ux; + + let highlightSvg: JSX.Element; + let hoverSvg: JSX.Element; + + if (bounds === undefined || bounds.length === 0) { + highlightSvg = (<>); + } else { + const polygons = bounds + .filter((bounds) => bounds.pageNumber === pageNumber) + .map(({ polygon }) => polygon); - if (bounds === undefined || bounds.length === 0) return null; + highlightSvg = ; + } - const polygons = bounds - .filter((bounds) => bounds.pageNumber === pageNumber) - .map(({ polygon }) => polygon); + if (hoverBounds === undefined || hoverBounds.length === 0) { + hoverSvg = (<>); + } else { + const polygons = hoverBounds + .filter((bounds) => bounds.pageNumber === pageNumber) + .map(({ polygon }) => polygon); - const highlightSvg = ; + hoverSvg = ; + } return (
{ }} > {highlightSvg} + {hoverSvg}
) }