diff --git a/packages/text-annotator-react/src/TextAnnotationPopup/TextAnnotationPopup.tsx b/packages/text-annotator-react/src/TextAnnotationPopup/TextAnnotationPopup.tsx index f0493149..f452359a 100644 --- a/packages/text-annotator-react/src/TextAnnotationPopup/TextAnnotationPopup.tsx +++ b/packages/text-annotator-react/src/TextAnnotationPopup/TextAnnotationPopup.tsx @@ -2,6 +2,7 @@ import { ReactNode, useEffect, useMemo, useRef, useState } from 'react'; import { useAnnotator, useSelection } from '@annotorious/react'; import { NOT_ANNOTATABLE_CLASS, + toViewportBounds, toDomRectList, type TextAnnotation, type TextAnnotator, @@ -49,12 +50,6 @@ export interface TextAnnotationPopupContentProps { } -const toViewportBounds = (annotationBounds: DOMRect, container: HTMLElement): DOMRect => { - const { left, top, right, bottom } = annotationBounds; - const containerBounds = container.getBoundingClientRect(); - return new DOMRect(left + containerBounds.left, top + containerBounds.top, right - left, bottom - top); -} - export const TextAnnotationPopup = (props: TextAnnotationPopupProps) => { const r = useAnnotator(); @@ -107,16 +102,17 @@ export const TextAnnotationPopup = (props: TextAnnotationPopupProps) => { if (isOpen && annotation?.id) { refs.setPositionReference({ getBoundingClientRect: () => { - // Annotation bounds are relative to the document element const bounds = r.state.store.getAnnotationBounds(annotation.id); return bounds - ? toViewportBounds(bounds, r.element) + ? toViewportBounds(bounds, r.element.getBoundingClientRect()) : new DOMRect(); }, getClientRects: () => { const rects = r.state.store.getAnnotationRects(annotation.id); - const viewportRects = rects.map(rect => toViewportBounds(rect, r.element)); - return toDomRectList(viewportRects); + const denormalizedRects = rects.map((rect) => + toViewportBounds(rect, r.element.getBoundingClientRect()) + ); + return toDomRectList(denormalizedRects); } }); } else { diff --git a/packages/text-annotator/src/state/TextAnnotatorState.ts b/packages/text-annotator/src/state/TextAnnotatorState.ts index 90301c21..b2168c3d 100644 --- a/packages/text-annotator/src/state/TextAnnotatorState.ts +++ b/packages/text-annotator/src/state/TextAnnotatorState.ts @@ -116,8 +116,7 @@ export const createTextAnnotatorState = { const rects = tree.getAnnotationRects(id); - if (rects.length === 0) return; - return tree.getAnnotationBounds(id); + return rects.length > 0 ? tree.getAnnotationBounds(id) : undefined; } const getIntersecting = ( diff --git a/packages/text-annotator/src/state/spatialTree.ts b/packages/text-annotator/src/state/spatialTree.ts index 72552c7b..d742fb5f 100644 --- a/packages/text-annotator/src/state/spatialTree.ts +++ b/packages/text-annotator/src/state/spatialTree.ts @@ -1,7 +1,12 @@ import RBush from 'rbush'; import type { Store } from '@annotorious/core'; import type { TextAnnotation, TextAnnotationTarget } from '../model'; -import { isRevived, reviveSelector, mergeClientRects } from '../utils'; +import { + isRevived, + reviveSelector, + mergeClientRects, + toParentBounds +} from '../utils'; import type { AnnotationRects } from './TextAnnotationStore'; interface IndexedHighlightRect { @@ -37,11 +42,11 @@ export const createSpatialTree = (store: Store, con return Array.from(revivedRange.getClientRects()); }); - const merged = mergeClientRects(rects) - // Offset the merged client rects so that coords - // are relative to the parent container - .map(({ left, top, right, bottom }) => - new DOMRect(left - offset.left, top - offset.top, right - left, bottom - top)); + /** + * Offset the merged client rects so that coords + * are relative to the parent container + */ + const merged = mergeClientRects(rects).map(rect => toParentBounds(rect, offset)); return merged.map(rect => { const { x, y, width, height } = rect; diff --git a/packages/text-annotator/src/utils/index.ts b/packages/text-annotator/src/utils/index.ts index 5d536dd2..599fc737 100644 --- a/packages/text-annotator/src/utils/index.ts +++ b/packages/text-annotator/src/utils/index.ts @@ -14,4 +14,4 @@ export * from './reviveSelector'; export * from './reviveTarget'; export * from './splitAnnotatableRanges'; export * from './trimRangeToContainer'; - +export * from './rectsToBounds'; diff --git a/packages/text-annotator/src/utils/rectsToBounds.ts b/packages/text-annotator/src/utils/rectsToBounds.ts new file mode 100644 index 00000000..ba9fd14a --- /dev/null +++ b/packages/text-annotator/src/utils/rectsToBounds.ts @@ -0,0 +1,9 @@ +export const toParentBounds = (rect: DOMRect, offset: DOMRect): DOMRect => { + const { left, top, right, bottom } = rect; + return new DOMRect(left - offset.left, top - offset.top, right - left, bottom - top); +}; + +export const toViewportBounds = (rect: DOMRect, offset: DOMRect): DOMRect => { + const { left, top, right, bottom } = rect; + return new DOMRect(left + offset.left, top + offset.top, right - left, bottom - top); +}