diff --git a/packages/text-annotator/src/SelectionHandler.ts b/packages/text-annotator/src/SelectionHandler.ts index 29a55f4f..986383b0 100644 --- a/packages/text-annotator/src/SelectionHandler.ts +++ b/packages/text-annotator/src/SelectionHandler.ts @@ -84,7 +84,7 @@ export const SelectionHandler = ( const hasChanged = annotatableRanges.length !== currentTarget.selector.length || annotatableRanges.some((r, i) => r.toString() !== currentTarget.selector[i]?.quote); - + if (!hasChanged) return; currentTarget = { @@ -98,7 +98,7 @@ export const SelectionHandler = ( } else { // Proper lifecycle management: clear selection first... selection.clear(); - + // ...then add annotation to store... store.addAnnotation({ id: currentTarget.annotation, @@ -149,13 +149,26 @@ export const SelectionHandler = ( const timeDifference = evt.timeStamp - lastPointerDown.timeStamp; - // Just a click, not a selection - if (document.getSelection().isCollapsed && timeDifference < 300) { - currentTarget = undefined; - clickSelect(); - } else if (currentTarget) { - selection.userSelect(currentTarget.annotation, evt); - } + /** + * We must check the `isCollapsed` within the 0-timeout + * to handle the annotation dismissal after a click properly. + * + * Otherwise, the `isCollapsed` will return an obsolete `false` value, + * click won't be processed, and the annotation will get falsely re-selected. + * + * @see https://github.com/recogito/text-annotator-js/issues/136 + */ + setTimeout(() => { + const sel = document.getSelection() + + // Just a click, not a selection + if (sel?.isCollapsed && timeDifference < 300) { + currentTarget = undefined; + clickSelect(); + } else if (currentTarget) { + selection.userSelect(currentTarget.annotation, evt); + } + }); } container.addEventListener('pointerdown', onPointerDown); @@ -172,6 +185,7 @@ export const SelectionHandler = ( container.removeEventListener('selectstart', onSelectStart); document.removeEventListener('selectionchange', onSelectionChange); + } return {