From 130e29f966e17b8842450ad2afee2fc867226203 Mon Sep 17 00:00:00 2001 From: Rainer Simon Date: Mon, 13 Jul 2020 13:15:17 +0200 Subject: [PATCH] Bugfix: avoids stratification when making a selection that perfectly overlaps an annotation --- src/selection/SelectionHandler.js | 20 +++++++++++++++----- src/selection/SelectionUtils.js | 26 ++++++++++++++++++++++++++ 2 files changed, 41 insertions(+), 5 deletions(-) diff --git a/src/selection/SelectionHandler.js b/src/selection/SelectionHandler.js index 1c3e63b..24d4b66 100644 --- a/src/selection/SelectionHandler.js +++ b/src/selection/SelectionHandler.js @@ -1,4 +1,4 @@ -import { trimRange, rangeToSelection, enableTouch } from './SelectionUtils'; +import { trimRange, rangeToSelection, enableTouch, getExactOverlaps } from './SelectionUtils'; import EventEmitter from 'tiny-emitter'; const IS_TOUCH = 'ontouchstart' in window || navigator.maxTouchPoints > 0; @@ -62,10 +62,20 @@ export default class SelectionHandler extends EventEmitter { this._hideNativeSelection(); - this.emit('select', { - selection: stub, - element: selectedRange - }); + const exactOverlaps = getExactOverlaps(stub, spans); + if (exactOverlaps.length > 0) { + // User selected existing - reuse top-most original to avoid stratification + this.clearSelection(); + this.emit('select', { + selection: exactOverlaps[0], + element: evt.target.closest('.r6o-annotation') + }); + } else { + this.emit('select', { + selection: stub, + element: selectedRange + }); + } } } } diff --git a/src/selection/SelectionUtils.js b/src/selection/SelectionUtils.js index 50bd897..2f30ec0 100644 --- a/src/selection/SelectionUtils.js +++ b/src/selection/SelectionUtils.js @@ -50,6 +50,32 @@ export const rangeToSelection = (range, containerEl) => { }; +/** + * Util function that checks if the given selection is an exact overlap to any + * existing annotations, and returns them, if so + */ +export const getExactOverlaps = (newAnnotation, selectedSpans) => { + // All existing annotations at this point + const existingAnnotations = []; + + selectedSpans.forEach(span => { + const enclosingAnnotationSpan = span.closest('.r6o-annotation'); + const enclosingAnnotation = enclosingAnnotationSpan?.annotation; + + if (enclosingAnnotation && !existingAnnotations.includes(enclosingAnnotation)) + existingAnnotations.push(enclosingAnnotation); + }); + + if (existingAnnotations.length > 0) + return existingAnnotations.filter(anno => { + const isSameAnchor = anno.anchor == newAnnotation.anchor; + const isSameQuote = anno.quote == newAnnotation.quote; + return isSameAnchor && isSameQuote; + }); + else + return []; +}; + export const enableTouch = (element, selectHandler) => { let touchTimeout; let lastTouchEvent;