diff --git a/package-lock.json b/package-lock.json index ae2cdf55..24d0780e 100644 --- a/package-lock.json +++ b/package-lock.json @@ -4577,6 +4577,7 @@ "colord": "^2.9.3", "dequal": "^2.0.3", "hotkeys-js": "^3.13.7", + "nanoevents": "^9.0.0", "rbush": "^4.0.1", "uuid": "^10.0.0" }, diff --git a/packages/text-annotator/package.json b/packages/text-annotator/package.json index fed72f8c..36ffb05a 100644 --- a/packages/text-annotator/package.json +++ b/packages/text-annotator/package.json @@ -41,6 +41,7 @@ "colord": "^2.9.3", "dequal": "^2.0.3", "hotkeys-js": "^3.13.7", + "nanoevents": "^9.0.0", "rbush": "^4.0.1", "uuid": "^10.0.0" } diff --git a/packages/text-annotator/src/state/TextAnnotationStore.ts b/packages/text-annotator/src/state/TextAnnotationStore.ts index b03da8ad..25e420f4 100644 --- a/packages/text-annotator/src/state/TextAnnotationStore.ts +++ b/packages/text-annotator/src/state/TextAnnotationStore.ts @@ -1,5 +1,9 @@ +import type { Unsubscribe } from 'nanoevents'; import type { Filter, Origin, Store } from '@annotorious/core'; + import type { TextAnnotation } from '../model'; +import type { SpatialTreeEvents } from './spatialTree'; + export interface TextAnnotationStore extends Omit, 'addAnnotation' | 'bulkAddAnnotation'> { @@ -23,6 +27,8 @@ export interface TextAnnotationStore recalculatePositions(): void; + onRecalculatePositions(callback: SpatialTreeEvents['recalculate']): Unsubscribe; + } export interface AnnotationRects { diff --git a/packages/text-annotator/src/state/TextAnnotatorState.ts b/packages/text-annotator/src/state/TextAnnotatorState.ts index 5cb5a4ab..916cbe34 100644 --- a/packages/text-annotator/src/state/TextAnnotatorState.ts +++ b/packages/text-annotator/src/state/TextAnnotatorState.ts @@ -11,7 +11,7 @@ import type { SelectionState, HoverState, } from '@annotorious/core'; -import { createSpatialTree } from './spatialTree'; +import { createSpatialTree, type SpatialTreeEvents } from './spatialTree'; import type { TextAnnotation, TextAnnotationTarget } from '../model'; import type { AnnotationRects, TextAnnotationStore } from './TextAnnotationStore'; import { isRevived, reviveAnnotation, reviveTarget } from '../utils'; @@ -142,6 +142,7 @@ export const createTextAnnotatorState = tree.getAnnotationRects(id); const recalculatePositions = () => tree.recalculate(); + const onRecalculatePositions = (callback: SpatialTreeEvents['recalculate']) => tree.on('recalculate', callback); store.observe(({ changes }) => { const deleted = (changes.deleted || []).filter(a => isRevived(a.target.selector)); @@ -170,6 +171,7 @@ export const createTextAnnotatorState = (store: Store, container: HTMLElement) => { const tree = new RBush(); const index = new Map(); + const emitter = createNanoEvents(); + // Helper: converts a single text annotation target to a list of hightlight rects const toItems = (target: TextAnnotationTarget, offset: DOMRect): IndexedHighlightRect[] => { const rects = target.selector.flatMap(s => { @@ -180,8 +190,12 @@ export const createSpatialTree = (store: Store, con const size = () => tree.all().length; - const recalculate = () => + const recalculate = () => { set(store.all().map(a => a.target), true); + emitter.emit('recalculate'); + }; + + const on = (event: E, callback: SpatialTreeEvents[E]): Unsubscribe => emitter.on(event, callback); return { all, @@ -195,7 +209,8 @@ export const createSpatialTree = (store: Store, con remove, set, size, - update + update, + on } }