From a397d68dcd3fcc5a90487b48d14b4360a0baa8ba Mon Sep 17 00:00:00 2001 From: Oleksandr Danylchenko Date: Tue, 13 Aug 2024 18:53:37 +0300 Subject: [PATCH] Added selection trimming down to the annotatable container --- .../text-annotator/src/SelectionHandler.ts | 15 ++++++++++----- packages/text-annotator/src/utils/index.ts | 1 + .../src/utils/trimRangeToContainer.ts | 18 ++++++++++++++++++ 3 files changed, 29 insertions(+), 5 deletions(-) create mode 100644 packages/text-annotator/src/utils/trimRangeToContainer.ts diff --git a/packages/text-annotator/src/SelectionHandler.ts b/packages/text-annotator/src/SelectionHandler.ts index eedef19e..da32534b 100644 --- a/packages/text-annotator/src/SelectionHandler.ts +++ b/packages/text-annotator/src/SelectionHandler.ts @@ -7,6 +7,7 @@ import { splitAnnotatableRanges, rangeToSelector, isWhitespaceOrEmpty, + trimRangeToContainer, NOT_ANNOTATABLE_SELECTOR } from './utils'; @@ -69,15 +70,19 @@ export const SelectionHandler = ( } // Chrome/iOS does not reliably fire the 'selectstart' event! - if (evt.timeStamp - (lastPointerDown?.timeStamp || evt.timeStamp) < 1000 && !currentTarget) + const timeDifference = evt.timeStamp - (lastPointerDown?.timeStamp || evt.timeStamp); + if (timeDifference < 1000 && !currentTarget) onSelectStart(lastPointerDown); if (sel.isCollapsed || !isLeftClick || !currentTarget) return; const selectionRange = sel.getRangeAt(0); - if (isWhitespaceOrEmpty(selectionRange)) return; - - const annotatableRanges = splitAnnotatableRanges(selectionRange.cloneRange()); + + // The selection should be captured only within the annotatable container + const containedRange = trimRangeToContainer(selectionRange, container); + if (isWhitespaceOrEmpty(containedRange)) return; + + const annotatableRanges = splitAnnotatableRanges(containedRange.cloneRange()); const hasChanged = annotatableRanges.length !== currentTarget.selector.length || @@ -162,7 +167,7 @@ export const SelectionHandler = ( const destroy = () => { container.removeEventListener('selectstart', onSelectStart); document.removeEventListener('selectionchange', onSelectionChange); - + container.removeEventListener('pointerdown', onPointerDown); document.removeEventListener('pointerup', onPointerUp); } diff --git a/packages/text-annotator/src/utils/index.ts b/packages/text-annotator/src/utils/index.ts index e8b379d8..3b7b0005 100644 --- a/packages/text-annotator/src/utils/index.ts +++ b/packages/text-annotator/src/utils/index.ts @@ -11,4 +11,5 @@ export * from './reviveAnnotation'; export * from './reviveSelector'; export * from './reviveTarget'; export * from './splitAnnotatableRanges'; +export * from './trimRangeToContainer'; diff --git a/packages/text-annotator/src/utils/trimRangeToContainer.ts b/packages/text-annotator/src/utils/trimRangeToContainer.ts new file mode 100644 index 00000000..8a419a4b --- /dev/null +++ b/packages/text-annotator/src/utils/trimRangeToContainer.ts @@ -0,0 +1,18 @@ +export const trimRangeToContainer = ( + range: Range, + container: HTMLElement +): Range => { + const trimmedRange = range.cloneRange(); + + // If the start is outside the container - set it to the start of the container + if (!container.contains(trimmedRange.startContainer)) { + trimmedRange.setStart(container, 0); + } + + // If the end is outside the container - set it to the end of the container + if (!container.contains(trimmedRange.endContainer)) { + trimmedRange.setEnd(container, container.childNodes.length); + } + + return trimmedRange; +};