fix(design): a right-click on selected words opens the browser's own menu (E28 S3e4b)

The row menu took every right-click, so selected text could only be copied
with Ctrl+C. On the selection (its laid-out line boxes) the event is left to
the browser; beside it the object's menu opens as before.
This commit is contained in:
2026-09-26 19:03:34 +02:00
parent 079cee4e9c
commit bd0914b7ed
3 changed files with 72 additions and 3 deletions
+21
View File
@@ -29,3 +29,24 @@ export function movedPastSlop(
if (!from || e.detail === 0) return false;
return Math.abs(e.clientX - from.x) > CLICK_SLOP || Math.abs(e.clientY - from.y) > CLICK_SLOP;
}
// The copy half: a right-click on selected words is a request for the browser's own menu (Copy,
// Search, Translate), not the object's. An app menu there would leave Ctrl+C as the only way to copy
// what the reader just selected. Only ON the selection: a right-click beside it (the row's picture,
// its padding) still means the object.
/** True when the page's selection is non-empty and the point (x, y) over `target` lies on it. Where
* the engine lays out ranges (a browser), "on it" is inside one of the selection's line boxes; where
* it does not (jsdom), it is `target` touching the selection. */
export function pointOnSelection(target: EventTarget | null, x: number, y: number): boolean {
const sel = typeof window === "undefined" ? null : window.getSelection();
if (!sel || sel.isCollapsed || !sel.toString().trim() || !(target instanceof Node)) return false;
for (let i = 0; i < sel.rangeCount; i++) {
const range = sel.getRangeAt(i);
if (!range.intersectsNode(target)) continue;
if (typeof range.getClientRects !== "function") return true;
for (const r of range.getClientRects())
if (x >= r.left && x <= r.right && y >= r.top && y <= r.bottom) return true;
}
return false;
}