From edd2e4af2c506d671518678cd5b36b34cf699a33 Mon Sep 17 00:00:00 2001 From: Oleksandr Danylchenko Date: Thu, 1 Aug 2024 17:45:52 +0300 Subject: [PATCH 01/15] Added `annotatingEnabled` flag updating support --- .../text-annotator/src/SelectionHandler.ts | 20 +++++++----- packages/text-annotator/src/TextAnnotator.ts | 31 +++++++++++++++---- 2 files changed, 38 insertions(+), 13 deletions(-) diff --git a/packages/text-annotator/src/SelectionHandler.ts b/packages/text-annotator/src/SelectionHandler.ts index eedef19e..429e0365 100644 --- a/packages/text-annotator/src/SelectionHandler.ts +++ b/packages/text-annotator/src/SelectionHandler.ts @@ -10,10 +10,9 @@ import { NOT_ANNOTATABLE_SELECTOR } from './utils'; -export const SelectionHandler = ( +export const createSelectionHandler = ( container: HTMLElement, state: TextAnnotatorState, - annotatingEnabled: boolean, offsetReferenceSelector?: string ) => { @@ -25,6 +24,10 @@ export const SelectionHandler = ( const setFilter = (filter?: Filter) => currentFilter = filter; + let currentAnnotatingEnabled = true; + + const setAnnotatingEnabled = (enabled: boolean) => currentAnnotatingEnabled = enabled; + const { store, selection } = state; let currentTarget: TextAnnotationTarget | undefined; @@ -34,6 +37,8 @@ export const SelectionHandler = ( let lastPointerDown: PointerEvent | undefined; const onSelectStart = (evt: PointerEvent) => { + if (!currentAnnotatingEnabled) return; + if (!isLeftClick) return; // Make sure we don't listen to selection changes that were @@ -53,10 +58,11 @@ export const SelectionHandler = ( } } - if (annotatingEnabled) - container.addEventListener('selectstart', onSelectStart); + container.addEventListener('selectstart', onSelectStart); const onSelectionChange = debounce((evt: PointerEvent) => { + if (!currentAnnotatingEnabled) return; + const sel = document.getSelection(); // This is to handle cases where the selection is "hijacked" by another element @@ -110,8 +116,7 @@ export const SelectionHandler = ( } }) - if (annotatingEnabled) - document.addEventListener('selectionchange', onSelectionChange); + document.addEventListener('selectionchange', onSelectionChange); // Select events don't carry information about the mouse button // Therefore, to prevent right-click selection, we need to listen @@ -170,7 +175,8 @@ export const SelectionHandler = ( return { destroy, setFilter, - setUser + setUser, + setAnnotatingEnabled } } diff --git a/packages/text-annotator/src/TextAnnotator.ts b/packages/text-annotator/src/TextAnnotator.ts index 4aeaf358..774d2eb9 100644 --- a/packages/text-annotator/src/TextAnnotator.ts +++ b/packages/text-annotator/src/TextAnnotator.ts @@ -1,13 +1,24 @@ -import { createAnonymousGuest, createLifecycleObserver, createBaseAnnotator, Filter, createUndoStack } from '@annotorious/core'; +import { + createAnonymousGuest, + createLifecycleObserver, + createBaseAnnotator, + Filter, + createUndoStack +} from '@annotorious/core'; import type { Annotator, User, PresenceProvider } from '@annotorious/core'; -import { createCanvasRenderer, createHighlightsRenderer, createSpansRenderer, type HighlightStyleExpression } from './highlight'; +import { + createCanvasRenderer, + createHighlightsRenderer, + createSpansRenderer, + type HighlightStyleExpression +} from './highlight'; import { createPresencePainter } from './presence'; import { scrollIntoView } from './api'; import { TextAnnotationStore, TextAnnotatorState, createTextAnnotatorState } from './state'; import type { TextAnnotation } from './model'; import { cancelSingleClickEvents } from './utils'; import { fillDefaults, type RendererType, type TextAnnotatorOptions } from './TextAnnotatorOptions'; -import { SelectionHandler } from './SelectionHandler'; +import { createSelectionHandler } from './SelectionHandler'; import './TextAnnotator.css'; @@ -22,6 +33,8 @@ export interface TextAnnotator extends Annot // Returns true if successful (or false if the annotation is not currently rendered) scrollIntoView(annotation: TextAnnotation): boolean; + setAnnotatingEnabled: (enabled: boolean) => void; + state: TextAnnotatorState; } @@ -59,8 +72,8 @@ export const createTextAnnotator = ( const highlightRenderer = useRenderer === 'SPANS' ? createSpansRenderer(container, state, viewport) : - useRenderer === 'CSS_HIGHLIGHTS' ? createHighlightsRenderer(container, state, viewport) : - useRenderer === 'CANVAS' ? createCanvasRenderer(container, state, viewport) : undefined; + useRenderer === 'CSS_HIGHLIGHTS' ? createHighlightsRenderer(container, state, viewport) : + useRenderer === 'CANVAS' ? createCanvasRenderer(container, state, viewport) : undefined; if (!highlightRenderer) throw `Unknown renderer implementation: ${useRenderer}`; @@ -70,8 +83,9 @@ export const createTextAnnotator = ( if (opts.style) highlightRenderer.setStyle(opts.style); - const selectionHandler = SelectionHandler(container, state, opts.annotatingEnabled, opts.offsetReferenceSelector); + const selectionHandler = createSelectionHandler(container, state, opts.offsetReferenceSelector); selectionHandler.setUser(currentUser); + selectionHandler.setAnnotatingEnabled(opts.annotatingEnabled); /*************************/ /* External API */ @@ -82,6 +96,10 @@ export const createTextAnnotator = ( const getUser = () => currentUser; + const setAnnotatingEnabled = (enabled: boolean) => { + selectionHandler.setAnnotatingEnabled(enabled); + }; + const setFilter = (filter?: Filter) => { highlightRenderer.setFilter(filter); selectionHandler.setFilter(filter); @@ -126,6 +144,7 @@ export const createTextAnnotator = ( destroy, element: container, getUser, + setAnnotatingEnabled, setFilter, setStyle, setUser, From 2c5b4da7b42fe2a8acaf5cbde291f077ae91fbcc Mon Sep 17 00:00:00 2001 From: Oleksandr Danylchenko Date: Thu, 1 Aug 2024 17:49:23 +0300 Subject: [PATCH 02/15] Added `annotatingEnabled` update from the react wrapper --- .../text-annotator-react/src/TextAnnotator.tsx | 14 ++++---------- 1 file changed, 4 insertions(+), 10 deletions(-) diff --git a/packages/text-annotator-react/src/TextAnnotator.tsx b/packages/text-annotator-react/src/TextAnnotator.tsx index 5024fea9..0c67fe90 100644 --- a/packages/text-annotator-react/src/TextAnnotator.tsx +++ b/packages/text-annotator-react/src/TextAnnotator.tsx @@ -40,22 +40,16 @@ export const TextAnnotator = (props: TextAnnotatorProps) = return () => anno.destroy(); }, [setAnno]); - useEffect(() => { - if (!anno) return; - - anno.setStyle(props.style); - }, [anno, props.style]); + useEffect(() => anno?.setStyle(props.style), [anno, props.style]); - useEffect(() => { - if (!anno) return; + useEffect(() => anno?.setFilter(props.filter), [anno, props.filter]); - anno.setFilter(props.filter); - }, [anno, props.filter]); + useEffect(() => anno?.setAnnotatingEnabled(props.annotatingEnabled), [anno, props.annotatingEnabled]); return (
{children}
- ) + ); } From 2c9fb93585e75d72a4fa30899817d88bfa33a65f Mon Sep 17 00:00:00 2001 From: Oleksandr Danylchenko Date: Wed, 28 Aug 2024 14:53:16 +0300 Subject: [PATCH 03/15] Added undefined `annotatingEnabled` prop handling --- packages/text-annotator/src/TextAnnotator.ts | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/packages/text-annotator/src/TextAnnotator.ts b/packages/text-annotator/src/TextAnnotator.ts index 774d2eb9..e0728042 100644 --- a/packages/text-annotator/src/TextAnnotator.ts +++ b/packages/text-annotator/src/TextAnnotator.ts @@ -96,8 +96,10 @@ export const createTextAnnotator = ( const getUser = () => currentUser; - const setAnnotatingEnabled = (enabled: boolean) => { - selectionHandler.setAnnotatingEnabled(enabled); + const setAnnotatingEnabled = (enabled?: boolean) => { + selectionHandler.setAnnotatingEnabled( + enabled === undefined ? true : enabled + ); }; const setFilter = (filter?: Filter) => { From e6ef046e4150aeb8a49f3a1784bb81348d130bac Mon Sep 17 00:00:00 2001 From: Oleksandr Danylchenko Date: Wed, 28 Aug 2024 19:05:47 +0300 Subject: [PATCH 04/15] Added cleanable `debounce` function --- package-lock.json | 13 +++++++++++++ packages/text-annotator/package.json | 3 ++- packages/text-annotator/src/SelectionHandler.ts | 16 +++++++++++----- .../text-annotator/src/highlight/baseRenderer.ts | 4 +++- .../src/highlight/canvas/canvasRenderer.ts | 8 ++++---- packages/text-annotator/src/utils/debounce.ts | 8 -------- packages/text-annotator/src/utils/index.ts | 1 - 7 files changed, 33 insertions(+), 20 deletions(-) delete mode 100644 packages/text-annotator/src/utils/debounce.ts diff --git a/package-lock.json b/package-lock.json index 1530d74c..c07a4675 100644 --- a/package-lock.json +++ b/package-lock.json @@ -2367,6 +2367,18 @@ "integrity": "sha512-e/1zu3xH5MQryN2zdVaF0OrdNLUbvWxzMbi+iNA6Bky7l1RoP8a2fIbRocyHclXt/arDrrR6lL3TqFD9pMQTsg==", "dev": true }, + "node_modules/debounce": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/debounce/-/debounce-2.1.0.tgz", + "integrity": "sha512-OkL3+0pPWCqoBc/nhO9u6TIQNTK44fnBnzuVtJAbp13Naxw9R6u21x+8tVTka87AhDZ3htqZ2pSSsZl9fqL2Wg==", + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/debug": { "version": "4.3.6", "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.6.tgz", @@ -4370,6 +4382,7 @@ "dependencies": { "@annotorious/core": "^3.0.0-rc.31", "colord": "^2.9.3", + "debounce": "^2.1.0", "dequal": "^2.0.3", "rbush": "^4.0.0", "uuid": "^10.0.0" diff --git a/packages/text-annotator/package.json b/packages/text-annotator/package.json index 81cb5be9..bcb99787 100644 --- a/packages/text-annotator/package.json +++ b/packages/text-annotator/package.json @@ -39,8 +39,9 @@ "dependencies": { "@annotorious/core": "^3.0.0-rc.31", "colord": "^2.9.3", + "debounce": "^2.1.0", "dequal": "^2.0.3", "rbush": "^4.0.0", "uuid": "^10.0.0" } -} \ No newline at end of file +} diff --git a/packages/text-annotator/src/SelectionHandler.ts b/packages/text-annotator/src/SelectionHandler.ts index 429e0365..2027fc4d 100644 --- a/packages/text-annotator/src/SelectionHandler.ts +++ b/packages/text-annotator/src/SelectionHandler.ts @@ -1,9 +1,10 @@ import { Filter, Origin, type User } from '@annotorious/core'; import { v4 as uuidv4 } from 'uuid'; +import debounce from 'debounce'; + import type { TextAnnotatorState } from './state'; import type { TextAnnotationTarget } from './model'; import { - debounce, splitAnnotatableRanges, rangeToSelector, isWhitespaceOrEmpty, @@ -82,13 +83,13 @@ export const createSelectionHandler = ( const selectionRange = sel.getRangeAt(0); if (isWhitespaceOrEmpty(selectionRange)) return; - + const annotatableRanges = splitAnnotatableRanges(selectionRange.cloneRange()); const hasChanged = annotatableRanges.length !== currentTarget.selector.length || annotatableRanges.some((r, i) => r.toString() !== currentTarget.selector[i]?.quote); - + if (!hasChanged) return; currentTarget = { @@ -102,7 +103,7 @@ export const createSelectionHandler = ( } else { // Proper lifecycle management: clear selection first... selection.clear(); - + // ...then add annotation to store... store.addAnnotation({ id: currentTarget.annotation, @@ -165,9 +166,14 @@ export const createSelectionHandler = ( document.addEventListener('pointerup', onPointerUp); const destroy = () => { + currentTarget = undefined; + lastPointerDown = undefined; + + onSelectionChange.clear(); + container.removeEventListener('selectstart', onSelectStart); document.removeEventListener('selectionchange', onSelectionChange); - + container.removeEventListener('pointerdown', onPointerDown); document.removeEventListener('pointerup', onPointerUp); } diff --git a/packages/text-annotator/src/highlight/baseRenderer.ts b/packages/text-annotator/src/highlight/baseRenderer.ts index 3698c4dd..d4fde630 100644 --- a/packages/text-annotator/src/highlight/baseRenderer.ts +++ b/packages/text-annotator/src/highlight/baseRenderer.ts @@ -1,6 +1,7 @@ +import debounce from 'debounce'; import type { Filter, ViewportState } from '@annotorious/core'; + import type { TextAnnotatorState } from '../state'; -import { debounce } from '../utils'; import { ViewportBounds, getViewportBounds, trackViewport } from './viewport'; import type { HighlightPainter } from './HighlightPainter'; import type { Highlight } from './Highlight'; @@ -169,6 +170,7 @@ export const createBaseRenderer = ( document.removeEventListener('scroll', onScroll); + onResize.clear(); window.removeEventListener('resize', onResize); resizeObserver.disconnect(); diff --git a/packages/text-annotator/src/highlight/canvas/canvasRenderer.ts b/packages/text-annotator/src/highlight/canvas/canvasRenderer.ts index 76237351..3927200b 100644 --- a/packages/text-annotator/src/highlight/canvas/canvasRenderer.ts +++ b/packages/text-annotator/src/highlight/canvas/canvasRenderer.ts @@ -1,6 +1,7 @@ +import debounce from 'debounce'; import type { ViewportState } from '@annotorious/core'; + import type { TextAnnotatorState } from '../../state'; -import { debounce } from '../../utils'; import type { ViewportBounds } from '../viewport'; import type { HighlightStyle } from '../HighlightStyle'; import { DEFAULT_SELECTED_STYLE, DEFAULT_STYLE, HighlightStyleExpression } from '../HighlightStyle'; @@ -116,9 +117,7 @@ const createRenderer = (container: HTMLElement): RendererImplementation => { }); }); - const onResize = debounce(() => { - resetCanvas(canvas); - }); + const onResize = debounce(() => resetCanvas(canvas)); window.addEventListener('resize', onResize); @@ -129,6 +128,7 @@ const createRenderer = (container: HTMLElement): RendererImplementation => { const destroy = () => { canvas.remove(); + onResize.clear(); window.removeEventListener('resize', onResize); } diff --git a/packages/text-annotator/src/utils/debounce.ts b/packages/text-annotator/src/utils/debounce.ts deleted file mode 100644 index c20a566c..00000000 --- a/packages/text-annotator/src/utils/debounce.ts +++ /dev/null @@ -1,8 +0,0 @@ -export const debounce = void>(func: T, delay = 10): T => { - let timeoutId: ReturnType; - - return ((...args: any[]) => { - clearTimeout(timeoutId); - timeoutId = setTimeout(() => func.apply(this, args), delay); - }) as T; -} diff --git a/packages/text-annotator/src/utils/index.ts b/packages/text-annotator/src/utils/index.ts index e8b379d8..77d41b8d 100644 --- a/packages/text-annotator/src/utils/index.ts +++ b/packages/text-annotator/src/utils/index.ts @@ -1,5 +1,4 @@ export * from './cancelSingleClickEvents'; -export * from './debounce'; export * from './getAnnotatableFragment'; export * from './getClientRectsPonyfill'; export * from './getQuoteContext'; From da40d2c230131a3fc0e42847f7075d2c843ed837 Mon Sep 17 00:00:00 2001 From: Oleksandr Danylchenko Date: Wed, 28 Aug 2024 19:08:24 +0300 Subject: [PATCH 05/15] Added selection state cleanup on annotating disabling --- packages/text-annotator/src/SelectionHandler.ts | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/packages/text-annotator/src/SelectionHandler.ts b/packages/text-annotator/src/SelectionHandler.ts index 2027fc4d..efe299eb 100644 --- a/packages/text-annotator/src/SelectionHandler.ts +++ b/packages/text-annotator/src/SelectionHandler.ts @@ -27,7 +27,15 @@ export const createSelectionHandler = ( let currentAnnotatingEnabled = true; - const setAnnotatingEnabled = (enabled: boolean) => currentAnnotatingEnabled = enabled; + const setAnnotatingEnabled = (enabled: boolean) => { + currentAnnotatingEnabled = enabled; + onSelectionChange.clear(); + + if (!enabled) { + currentTarget = undefined; + lastPointerDown = undefined; + } + }; const { store, selection } = state; From 5baebe73e4da6cecab1bd94fd04249be87ee4a3d Mon Sep 17 00:00:00 2001 From: Oleksandr Danylchenko Date: Wed, 28 Aug 2024 20:06:43 +0300 Subject: [PATCH 06/15] Decreased debounce timeout to 10ms --- packages/text-annotator/src/SelectionHandler.ts | 2 +- packages/text-annotator/src/highlight/baseRenderer.ts | 2 +- packages/text-annotator/src/highlight/canvas/canvasRenderer.ts | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/text-annotator/src/SelectionHandler.ts b/packages/text-annotator/src/SelectionHandler.ts index efe299eb..dc26e65d 100644 --- a/packages/text-annotator/src/SelectionHandler.ts +++ b/packages/text-annotator/src/SelectionHandler.ts @@ -123,7 +123,7 @@ export const createSelectionHandler = ( // select events don't have offsetX/offsetY - reuse last up/down) selection.userSelect(currentTarget.annotation, lastPointerDown); } - }) + }, 10) document.addEventListener('selectionchange', onSelectionChange); diff --git a/packages/text-annotator/src/highlight/baseRenderer.ts b/packages/text-annotator/src/highlight/baseRenderer.ts index d4fde630..272c854b 100644 --- a/packages/text-annotator/src/highlight/baseRenderer.ts +++ b/packages/text-annotator/src/highlight/baseRenderer.ts @@ -140,7 +140,7 @@ export const createBaseRenderer = ( currentPainter.reset(); redraw(); - }); + }, 10); window.addEventListener('resize', onResize); diff --git a/packages/text-annotator/src/highlight/canvas/canvasRenderer.ts b/packages/text-annotator/src/highlight/canvas/canvasRenderer.ts index 3927200b..12c0f038 100644 --- a/packages/text-annotator/src/highlight/canvas/canvasRenderer.ts +++ b/packages/text-annotator/src/highlight/canvas/canvasRenderer.ts @@ -117,7 +117,7 @@ const createRenderer = (container: HTMLElement): RendererImplementation => { }); }); - const onResize = debounce(() => resetCanvas(canvas)); + const onResize = debounce(() => resetCanvas(canvas), 10); window.addEventListener('resize', onResize); From 3d7e09c1ff7c9b63f03c01eaf6cfb2358cc2db26 Mon Sep 17 00:00:00 2001 From: Oleksandr Danylchenko Date: Fri, 30 Aug 2024 14:37:48 +0300 Subject: [PATCH 07/15] Added strict context binding to the debounced function # See - https://github.com/sindresorhus/debounce/issues/8. The debounced function has a strict context comparison that breaks when applied in the resize listener --- packages/text-annotator/src/SelectionHandler.ts | 2 +- packages/text-annotator/src/highlight/baseRenderer.ts | 2 +- packages/text-annotator/src/highlight/canvas/canvasRenderer.ts | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/text-annotator/src/SelectionHandler.ts b/packages/text-annotator/src/SelectionHandler.ts index 131f9d78..be2e72b9 100644 --- a/packages/text-annotator/src/SelectionHandler.ts +++ b/packages/text-annotator/src/SelectionHandler.ts @@ -128,7 +128,7 @@ export const createSelectionHandler = ( // select events don't have offsetX/offsetY - reuse last up/down) selection.userSelect(currentTarget.annotation, lastPointerDown); } - }, 10) + }, 10).bind(undefined); document.addEventListener('selectionchange', onSelectionChange); diff --git a/packages/text-annotator/src/highlight/baseRenderer.ts b/packages/text-annotator/src/highlight/baseRenderer.ts index 272c854b..9adeb400 100644 --- a/packages/text-annotator/src/highlight/baseRenderer.ts +++ b/packages/text-annotator/src/highlight/baseRenderer.ts @@ -140,7 +140,7 @@ export const createBaseRenderer = ( currentPainter.reset(); redraw(); - }, 10); + }, 10).bind(undefined); window.addEventListener('resize', onResize); diff --git a/packages/text-annotator/src/highlight/canvas/canvasRenderer.ts b/packages/text-annotator/src/highlight/canvas/canvasRenderer.ts index 12c0f038..25aea98c 100644 --- a/packages/text-annotator/src/highlight/canvas/canvasRenderer.ts +++ b/packages/text-annotator/src/highlight/canvas/canvasRenderer.ts @@ -117,7 +117,7 @@ const createRenderer = (container: HTMLElement): RendererImplementation => { }); }); - const onResize = debounce(() => resetCanvas(canvas), 10); + const onResize = debounce(() => resetCanvas(canvas), 10).bind(undefined); window.addEventListener('resize', onResize); From c0dc15689cd4543e10817eff7e0fb17329fa4d3a Mon Sep 17 00:00:00 2001 From: Oleksandr Danylchenko Date: Fri, 30 Aug 2024 17:04:10 +0300 Subject: [PATCH 08/15] Added context-unaware `debounce` wrapper --- .../text-annotator/src/SelectionHandler.ts | 4 ++-- .../src/highlight/baseRenderer.ts | 7 +++--- .../src/highlight/canvas/canvasRenderer.ts | 4 ++-- packages/text-annotator/src/utils/debounce.ts | 22 +++++++++++++++++++ packages/text-annotator/src/utils/index.ts | 1 + 5 files changed, 30 insertions(+), 8 deletions(-) create mode 100644 packages/text-annotator/src/utils/debounce.ts diff --git a/packages/text-annotator/src/SelectionHandler.ts b/packages/text-annotator/src/SelectionHandler.ts index be2e72b9..d1e3714c 100644 --- a/packages/text-annotator/src/SelectionHandler.ts +++ b/packages/text-annotator/src/SelectionHandler.ts @@ -1,10 +1,10 @@ import { Filter, Origin, type User } from '@annotorious/core'; import { v4 as uuidv4 } from 'uuid'; -import debounce from 'debounce'; import type { TextAnnotatorState } from './state'; import type { TextAnnotationTarget } from './model'; import { + debounce, splitAnnotatableRanges, rangeToSelector, isWhitespaceOrEmpty, @@ -128,7 +128,7 @@ export const createSelectionHandler = ( // select events don't have offsetX/offsetY - reuse last up/down) selection.userSelect(currentTarget.annotation, lastPointerDown); } - }, 10).bind(undefined); + }); document.addEventListener('selectionchange', onSelectionChange); diff --git a/packages/text-annotator/src/highlight/baseRenderer.ts b/packages/text-annotator/src/highlight/baseRenderer.ts index 9adeb400..976460ec 100644 --- a/packages/text-annotator/src/highlight/baseRenderer.ts +++ b/packages/text-annotator/src/highlight/baseRenderer.ts @@ -1,4 +1,3 @@ -import debounce from 'debounce'; import type { Filter, ViewportState } from '@annotorious/core'; import type { TextAnnotatorState } from '../state'; @@ -6,6 +5,7 @@ import { ViewportBounds, getViewportBounds, trackViewport } from './viewport'; import type { HighlightPainter } from './HighlightPainter'; import type { Highlight } from './Highlight'; import type { HighlightStyleExpression } from './HighlightStyle'; +import { debounce } from '../utils'; export interface RendererImplementation { @@ -136,11 +136,10 @@ export const createBaseRenderer = ( const onResize = debounce(() => { store.recalculatePositions(); - if (currentPainter) - currentPainter.reset(); + currentPainter?.reset(); redraw(); - }, 10).bind(undefined); + }); window.addEventListener('resize', onResize); diff --git a/packages/text-annotator/src/highlight/canvas/canvasRenderer.ts b/packages/text-annotator/src/highlight/canvas/canvasRenderer.ts index 25aea98c..d0a9656f 100644 --- a/packages/text-annotator/src/highlight/canvas/canvasRenderer.ts +++ b/packages/text-annotator/src/highlight/canvas/canvasRenderer.ts @@ -1,4 +1,3 @@ -import debounce from 'debounce'; import type { ViewportState } from '@annotorious/core'; import type { TextAnnotatorState } from '../../state'; @@ -8,6 +7,7 @@ import { DEFAULT_SELECTED_STYLE, DEFAULT_STYLE, HighlightStyleExpression } from import type { HighlightPainter } from '../HighlightPainter'; import { createBaseRenderer, type RendererImplementation } from '../baseRenderer'; import type { Highlight } from '../Highlight'; +import { debounce } from '../../utils'; import './canvasRenderer.css'; @@ -117,7 +117,7 @@ const createRenderer = (container: HTMLElement): RendererImplementation => { }); }); - const onResize = debounce(() => resetCanvas(canvas), 10).bind(undefined); + const onResize = debounce(() => resetCanvas(canvas)); window.addEventListener('resize', onResize); diff --git a/packages/text-annotator/src/utils/debounce.ts b/packages/text-annotator/src/utils/debounce.ts new file mode 100644 index 00000000..147267e7 --- /dev/null +++ b/packages/text-annotator/src/utils/debounce.ts @@ -0,0 +1,22 @@ +import libDebounce from 'debounce'; + +/** + * Wraps the `debounce` function from the `debounce` package + * to make it context-agnostic. + * Otherwise, we won't be able to use it in multiple event listeners simultaneously, + * like `window.onresize` and `ResizeObserver`. + * @see https://github.com/sindresorhus/debounce/issues/8#issuecomment-2321341074 + */ +export const debounce: typeof libDebounce = (function_, wait = 10, options) => { + const fn = libDebounce(function_, wait, options); + + const boundFn = fn.bind(undefined); + + Object.getOwnPropertyNames(fn).forEach( + prop => Object.defineProperty(boundFn, prop, Object.getOwnPropertyDescriptor(fn, prop)) + ); + const proto = Object.getPrototypeOf(fn); + Object.setPrototypeOf(boundFn, proto); + + return boundFn; +} diff --git a/packages/text-annotator/src/utils/index.ts b/packages/text-annotator/src/utils/index.ts index 4b947537..0af8376f 100644 --- a/packages/text-annotator/src/utils/index.ts +++ b/packages/text-annotator/src/utils/index.ts @@ -11,4 +11,5 @@ export * from './reviveSelector'; export * from './reviveTarget'; export * from './splitAnnotatableRanges'; export * from './trimRangeToContainer'; +export * from './debounce'; From 21249d7481b866ea3927ddf401c6fe0ecd83fc22 Mon Sep 17 00:00:00 2001 From: Oleksandr Danylchenko Date: Tue, 10 Sep 2024 18:13:27 +0300 Subject: [PATCH 09/15] Replaced custom `debounce` method with the library one --- package-lock.json | 8 +++---- packages/text-annotator/package.json | 2 +- .../text-annotator/src/SelectionHandler.ts | 5 +++-- .../src/highlight/baseRenderer.ts | 3 ++- .../src/highlight/canvas/canvasRenderer.ts | 3 ++- packages/text-annotator/src/utils/debounce.ts | 22 ------------------- packages/text-annotator/src/utils/index.ts | 1 - 7 files changed, 12 insertions(+), 32 deletions(-) delete mode 100644 packages/text-annotator/src/utils/debounce.ts diff --git a/package-lock.json b/package-lock.json index 138aa8b9..3e708333 100644 --- a/package-lock.json +++ b/package-lock.json @@ -2615,9 +2615,9 @@ "license": "MIT" }, "node_modules/debounce": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/debounce/-/debounce-2.1.0.tgz", - "integrity": "sha512-OkL3+0pPWCqoBc/nhO9u6TIQNTK44fnBnzuVtJAbp13Naxw9R6u21x+8tVTka87AhDZ3htqZ2pSSsZl9fqL2Wg==", + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/debounce/-/debounce-2.1.1.tgz", + "integrity": "sha512-+xRWxgel9LgTC4PwKlm7TJUK6B6qsEK77NaiNvXmeQ7Y3e6OVVsBC4a9BSptS/mAYceyAz37Oa8JTTuPRft7uQ==", "license": "MIT", "engines": { "node": ">=18" @@ -4809,7 +4809,7 @@ "dependencies": { "@annotorious/core": "^3.0.3", "colord": "^2.9.3", - "debounce": "^2.1.0", + "debounce": "^2.1.1", "dequal": "^2.0.3", "rbush": "^4.0.1", "uuid": "^10.0.0" diff --git a/packages/text-annotator/package.json b/packages/text-annotator/package.json index d89fbd2d..55de812c 100644 --- a/packages/text-annotator/package.json +++ b/packages/text-annotator/package.json @@ -39,7 +39,7 @@ "dependencies": { "@annotorious/core": "^3.0.3", "colord": "^2.9.3", - "debounce": "^2.1.0", + "debounce": "^2.1.1", "dequal": "^2.0.3", "rbush": "^4.0.1", "uuid": "^10.0.0" diff --git a/packages/text-annotator/src/SelectionHandler.ts b/packages/text-annotator/src/SelectionHandler.ts index 22d6ff56..f9498051 100644 --- a/packages/text-annotator/src/SelectionHandler.ts +++ b/packages/text-annotator/src/SelectionHandler.ts @@ -1,10 +1,11 @@ -import { Filter, Origin, type User } from '@annotorious/core'; +import debounce from 'debounce'; import { v4 as uuidv4 } from 'uuid'; +import { Filter, Origin, type User } from '@annotorious/core'; + import type { TextAnnotatorState } from './state'; import type { TextAnnotationTarget } from './model'; import { - debounce, splitAnnotatableRanges, rangeToSelector, isWhitespaceOrEmpty, diff --git a/packages/text-annotator/src/highlight/baseRenderer.ts b/packages/text-annotator/src/highlight/baseRenderer.ts index 0afa5e50..75eed243 100644 --- a/packages/text-annotator/src/highlight/baseRenderer.ts +++ b/packages/text-annotator/src/highlight/baseRenderer.ts @@ -1,3 +1,5 @@ +import debounce from 'debounce'; + import type { Filter, ViewportState } from '@annotorious/core'; import type { TextAnnotatorState } from '../state'; @@ -5,7 +7,6 @@ import { ViewportBounds, getViewportBounds, trackViewport } from './viewport'; import type { HighlightPainter } from './HighlightPainter'; import type { Highlight } from './Highlight'; import type { HighlightStyleExpression } from './HighlightStyle'; -import { debounce } from '../utils'; export interface RendererImplementation { diff --git a/packages/text-annotator/src/highlight/canvas/canvasRenderer.ts b/packages/text-annotator/src/highlight/canvas/canvasRenderer.ts index d0a9656f..c18be549 100644 --- a/packages/text-annotator/src/highlight/canvas/canvasRenderer.ts +++ b/packages/text-annotator/src/highlight/canvas/canvasRenderer.ts @@ -1,3 +1,5 @@ +import debounce from 'debounce'; + import type { ViewportState } from '@annotorious/core'; import type { TextAnnotatorState } from '../../state'; @@ -7,7 +9,6 @@ import { DEFAULT_SELECTED_STYLE, DEFAULT_STYLE, HighlightStyleExpression } from import type { HighlightPainter } from '../HighlightPainter'; import { createBaseRenderer, type RendererImplementation } from '../baseRenderer'; import type { Highlight } from '../Highlight'; -import { debounce } from '../../utils'; import './canvasRenderer.css'; diff --git a/packages/text-annotator/src/utils/debounce.ts b/packages/text-annotator/src/utils/debounce.ts deleted file mode 100644 index 147267e7..00000000 --- a/packages/text-annotator/src/utils/debounce.ts +++ /dev/null @@ -1,22 +0,0 @@ -import libDebounce from 'debounce'; - -/** - * Wraps the `debounce` function from the `debounce` package - * to make it context-agnostic. - * Otherwise, we won't be able to use it in multiple event listeners simultaneously, - * like `window.onresize` and `ResizeObserver`. - * @see https://github.com/sindresorhus/debounce/issues/8#issuecomment-2321341074 - */ -export const debounce: typeof libDebounce = (function_, wait = 10, options) => { - const fn = libDebounce(function_, wait, options); - - const boundFn = fn.bind(undefined); - - Object.getOwnPropertyNames(fn).forEach( - prop => Object.defineProperty(boundFn, prop, Object.getOwnPropertyDescriptor(fn, prop)) - ); - const proto = Object.getPrototypeOf(fn); - Object.setPrototypeOf(boundFn, proto); - - return boundFn; -} diff --git a/packages/text-annotator/src/utils/index.ts b/packages/text-annotator/src/utils/index.ts index dce7a5c8..7df0ca3c 100644 --- a/packages/text-annotator/src/utils/index.ts +++ b/packages/text-annotator/src/utils/index.ts @@ -10,5 +10,4 @@ export * from './reviveSelector'; export * from './reviveTarget'; export * from './splitAnnotatableRanges'; export * from './trimRangeToContainer'; -export * from './debounce'; From a4079b0f494da822521f350de36c6e962c319f03 Mon Sep 17 00:00:00 2001 From: Oleksandr Danylchenko Date: Tue, 10 Sep 2024 20:36:36 +0300 Subject: [PATCH 10/15] Decreased debounce timeout to 10ms --- packages/text-annotator/src/SelectionHandler.ts | 4 ++-- packages/text-annotator/src/highlight/baseRenderer.ts | 2 +- .../text-annotator/src/highlight/canvas/canvasRenderer.ts | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/packages/text-annotator/src/SelectionHandler.ts b/packages/text-annotator/src/SelectionHandler.ts index f9498051..6fe04944 100644 --- a/packages/text-annotator/src/SelectionHandler.ts +++ b/packages/text-annotator/src/SelectionHandler.ts @@ -1,7 +1,7 @@ import debounce from 'debounce'; import { v4 as uuidv4 } from 'uuid'; -import { Filter, Origin, type User } from '@annotorious/core'; +import { Origin, type Filter, type User } from '@annotorious/core'; import type { TextAnnotatorState } from './state'; import type { TextAnnotationTarget } from './model'; @@ -127,7 +127,7 @@ export const createSelectionHandler = ( // select events don't have offsetX/offsetY - reuse last up/down) selection.userSelect(currentTarget.annotation, lastPointerDown); } - }); + }, 10); // Select events don't carry information about the mouse button // Therefore, to prevent right-click selection, we need to listen diff --git a/packages/text-annotator/src/highlight/baseRenderer.ts b/packages/text-annotator/src/highlight/baseRenderer.ts index 75eed243..21539d8c 100644 --- a/packages/text-annotator/src/highlight/baseRenderer.ts +++ b/packages/text-annotator/src/highlight/baseRenderer.ts @@ -140,7 +140,7 @@ export const createBaseRenderer = { }); }); - const onResize = debounce(() => resetCanvas(canvas)); + const onResize = debounce(() => resetCanvas(canvas), 10); window.addEventListener('resize', onResize); From ac26a81ebed79f5afdf770d7008df61c93e9bd18 Mon Sep 17 00:00:00 2001 From: Oleksandr Danylchenko Date: Mon, 30 Sep 2024 12:21:53 +0300 Subject: [PATCH 11/15] Merged `main` into `#127-annotating-enabled-reactive` --- packages/text-annotator/src/SelectionHandler.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/text-annotator/src/SelectionHandler.ts b/packages/text-annotator/src/SelectionHandler.ts index 951648f4..c453a832 100644 --- a/packages/text-annotator/src/SelectionHandler.ts +++ b/packages/text-annotator/src/SelectionHandler.ts @@ -2,7 +2,7 @@ import debounce from 'debounce'; import { v4 as uuidv4 } from 'uuid'; import hotkeys from 'hotkeys-js'; -import { Origin, type Filter, type User } from '@annotorious/core'; +import { Origin, type Filter, type Selection, type User } from '@annotorious/core'; import type { TextAnnotatorState } from './state'; import type { TextAnnotation, TextAnnotationTarget } from './model'; @@ -273,7 +273,7 @@ export const createSelectionHandler = ( const destroy = () => { currentTarget = undefined; - lastPointerDown = undefined; + lastDownEvent = undefined; onSelectionChange.clear(); From 2ee7384853cc084ececaadcd419366169999da4e Mon Sep 17 00:00:00 2001 From: Oleksandr Danylchenko Date: Mon, 4 Nov 2024 14:21:49 +0200 Subject: [PATCH 12/15] Removed `isContextMenuOpen` usage --- packages/text-annotator/src/SelectionHandler.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/text-annotator/src/SelectionHandler.ts b/packages/text-annotator/src/SelectionHandler.ts index 4e272c3c..7289bedd 100644 --- a/packages/text-annotator/src/SelectionHandler.ts +++ b/packages/text-annotator/src/SelectionHandler.ts @@ -344,8 +344,8 @@ export const createSelectionHandler = ( const destroy = () => { currentTarget = undefined; + isLeftClick = undefined; lastDownEvent = undefined; - isContextMenuOpen = false; onSelectionChange.clear(); From 7a44635bc18ce0b43074df23f7d7bf78079ec8af Mon Sep 17 00:00:00 2001 From: Oleksandr Danylchenko Date: Mon, 4 Nov 2024 18:26:40 +0200 Subject: [PATCH 13/15] Added `isLeftClick` reset on annotating disabling --- packages/text-annotator/src/SelectionHandler.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/text-annotator/src/SelectionHandler.ts b/packages/text-annotator/src/SelectionHandler.ts index 7289bedd..1c68a405 100644 --- a/packages/text-annotator/src/SelectionHandler.ts +++ b/packages/text-annotator/src/SelectionHandler.ts @@ -58,6 +58,7 @@ export const createSelectionHandler = ( if (!enabled) { currentTarget = undefined; + isLeftClick = undefined; lastDownEvent = undefined; } }; From 4249cc1e44931943f25c1b026966f8bf4f9bf1d0 Mon Sep 17 00:00:00 2001 From: Oleksandr Danylchenko Date: Mon, 11 Nov 2024 13:43:07 +0200 Subject: [PATCH 14/15] Added `annotatingEnabled` in opts handling --- packages/text-annotator/src/SelectionHandler.ts | 2 +- .../text-annotator/src/TextAnnotatorOptions.ts | 14 +++++--------- 2 files changed, 6 insertions(+), 10 deletions(-) diff --git a/packages/text-annotator/src/SelectionHandler.ts b/packages/text-annotator/src/SelectionHandler.ts index 42d9cb17..4d68443a 100644 --- a/packages/text-annotator/src/SelectionHandler.ts +++ b/packages/text-annotator/src/SelectionHandler.ts @@ -53,7 +53,7 @@ export const createSelectionHandler = ( let lastDownEvent: Selection['event'] | undefined; - let currentAnnotatingEnabled = true; + let currentAnnotatingEnabled = annotatingEnabled; const setAnnotatingEnabled = (enabled: boolean) => { currentAnnotatingEnabled = enabled; diff --git a/packages/text-annotator/src/TextAnnotatorOptions.ts b/packages/text-annotator/src/TextAnnotatorOptions.ts index bd9ced93..aa4f6a9e 100644 --- a/packages/text-annotator/src/TextAnnotatorOptions.ts +++ b/packages/text-annotator/src/TextAnnotatorOptions.ts @@ -30,12 +30,8 @@ export type RendererType = 'SPANS' | 'CANVAS' | 'CSS_HIGHLIGHTS'; export const fillDefaults = ( opts: TextAnnotatorOptions, defaults: TextAnnotatorOptions -): TextAnnotatorOptions => { - - return { - ...opts, - annotatingEnabled: opts.annotatingEnabled ?? defaults.annotatingEnabled, - user: opts.user || defaults.user - }; - -}; +): TextAnnotatorOptions => ({ + ...opts, + annotatingEnabled: opts.annotatingEnabled ?? defaults.annotatingEnabled, + user: opts.user || defaults.user +}); From b2bbebc52da522da76295dfa48538353501b173b Mon Sep 17 00:00:00 2001 From: Oleksandr Danylchenko Date: Mon, 16 Dec 2024 15:37:46 +0200 Subject: [PATCH 15/15] Comment formatting --- packages/text-annotator/src/SelectionHandler.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/text-annotator/src/SelectionHandler.ts b/packages/text-annotator/src/SelectionHandler.ts index 438c11ca..6b6f99d7 100644 --- a/packages/text-annotator/src/SelectionHandler.ts +++ b/packages/text-annotator/src/SelectionHandler.ts @@ -156,7 +156,7 @@ export const createSelectionHandler = ( const selectionRange = sel.getRangeAt(0); -// The selection should be captured only within the annotatable container + // The selection should be captured only within the annotatable container const containedRange = trimRangeToContainer(selectionRange, container); if (isWhitespaceOrEmpty(containedRange)) return;