diff --git a/package-lock.json b/package-lock.json index 6780d919..3f8e5950 100644 --- a/package-lock.json +++ b/package-lock.json @@ -5483,6 +5483,7 @@ "colord": "^2.9.3", "dequal": "^2.0.3", "hotkeys-js": "^3.13.9", + "nanoevents": "^9.0.0", "rbush": "^4.0.1", "uuid": "^11.0.3" }, diff --git a/packages/text-annotator/package.json b/packages/text-annotator/package.json index 4e684700..f371ce23 100644 --- a/packages/text-annotator/package.json +++ b/packages/text-annotator/package.json @@ -39,7 +39,8 @@ "colord": "^2.9.3", "dequal": "^2.0.3", "hotkeys-js": "^3.13.9", + "nanoevents": "^9.0.0", "rbush": "^4.0.1", "uuid": "^11.0.3" } -} \ No newline at end of file +} diff --git a/packages/text-annotator/src/state/TextAnnotationStore.ts b/packages/text-annotator/src/state/TextAnnotationStore.ts index 185f0154..99879c57 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'> { @@ -25,6 +29,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 90301c21..bc0f9a24 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'; @@ -130,6 +130,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)); @@ -158,6 +159,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 } }