Skip to content

Commit 4c7160b

Browse files
Merge branch 'main' into #71-fix-losing-styleClass
2 parents 118776e + 69be7ea commit 4c7160b

File tree

9 files changed

+46
-54
lines changed

9 files changed

+46
-54
lines changed

packages/text-annotator-react/src/TextAnnotatorPopup/TextAnnotatorPopup.tsx

Lines changed: 6 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import { PointerEvent, ReactNode, useCallback, useEffect, useMemo, useState } from 'react';
22
import { useAnnotator, useSelection } from '@annotorious/react';
3-
import type { TextAnnotation, TextAnnotator } from '@recogito/text-annotator';
3+
import { isRevived, TextAnnotation, TextAnnotator } from '@recogito/text-annotator';
44
import { isMobile } from './isMobile';
55
import {
66
autoUpdate,
@@ -71,15 +71,10 @@ export const TextAnnotatorPopup = (props: TextAnnotationPopupProps) => {
7171
const { getFloatingProps } = useInteractions([dismiss, role]);
7272

7373
useEffect(() => {
74-
setOpen(
75-
// Selected annotation exists and has a selector?
76-
annotation?.target.selector &&
77-
// Selector not empty? (Annotations from plugins, general defensive programming)
78-
annotation.target.selector.length > 0 &&
79-
// Range not collapsed? (E.g. lazy loading PDFs. Note that this will have to
80-
// change if we switch from ranges to pre-computed bounds!)
81-
!annotation.target.selector[0].range.collapsed
82-
);
74+
const annotationSelector = annotation?.target.selector;
75+
if (!annotationSelector) return;
76+
77+
setOpen(isRevived(annotationSelector));
8378
}, [annotation]);
8479

8580
useEffect(() => {
@@ -154,4 +149,4 @@ export const TextAnnotatorPopup = (props: TextAnnotationPopupProps) => {
154149
</FloatingPortal>
155150
) : null;
156151

157-
}
152+
}

packages/text-annotator/src/SelectionHandler.ts

Lines changed: 16 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ import {
1313
isMac,
1414
isWhitespaceOrEmpty,
1515
trimRangeToContainer,
16-
NOT_ANNOTATABLE_SELECTOR
16+
isNotAnnotatable
1717
} from './utils';
1818

1919
const CLICK_TIMEOUT = 300;
@@ -64,13 +64,14 @@ export const SelectionHandler = (
6464
* be annotatable (like a component popup).
6565
* Note that Chrome/iOS will sometimes return the root doc as target!
6666
*/
67-
const annotatable = !(evt.target as Node).parentElement?.closest(NOT_ANNOTATABLE_SELECTOR);
68-
currentTarget = annotatable ? {
69-
annotation: uuidv4(),
70-
selector: [],
71-
creator: currentUser,
72-
created: new Date()
73-
} : undefined;
67+
currentTarget = isNotAnnotatable(evt.target as Node)
68+
? undefined
69+
: {
70+
annotation: uuidv4(),
71+
selector: [],
72+
creator: currentUser,
73+
created: new Date()
74+
};
7475
};
7576

7677
const onSelectionChange = debounce((evt: Event) => {
@@ -79,8 +80,7 @@ export const SelectionHandler = (
7980
// This is to handle cases where the selection is "hijacked" by another element
8081
// in a not-annotatable area. A rare case in theory. But rich text editors
8182
// will like Quill do it...
82-
const annotatable = !sel.anchorNode?.parentElement?.closest(NOT_ANNOTATABLE_SELECTOR);
83-
if (!annotatable) {
83+
if (isNotAnnotatable(sel.anchorNode)) {
8484
currentTarget = undefined;
8585
return;
8686
}
@@ -149,6 +149,9 @@ export const SelectionHandler = (
149149
*/
150150
if (store.getAnnotation(currentTarget.annotation)) {
151151
store.updateTarget(currentTarget, Origin.LOCAL);
152+
} else {
153+
// Proper lifecycle management: clear the previous selection first...
154+
selection.clear();
152155
}
153156
});
154157

@@ -160,8 +163,7 @@ export const SelectionHandler = (
160163
const onPointerDown = (evt: PointerEvent) => {
161164
if (isContextMenuOpen) return;
162165

163-
const annotatable = !(evt.target as Node).parentElement?.closest(NOT_ANNOTATABLE_SELECTOR);
164-
if (!annotatable) return;
166+
if (isNotAnnotatable(evt.target as Node)) return;
165167

166168
/**
167169
* Cloning the event to prevent it from accidentally being `undefined`
@@ -188,8 +190,7 @@ export const SelectionHandler = (
188190
const onPointerUp = (evt: PointerEvent) => {
189191
if (isContextMenuOpen) return;
190192

191-
const annotatable = !(evt.target as Node).parentElement?.closest(NOT_ANNOTATABLE_SELECTOR);
192-
if (!annotatable || !isLeftClick) return;
193+
if (isNotAnnotatable(evt.target as Node) || !isLeftClick) return;
193194

194195
// Logic for selecting an existing annotation
195196
const clickSelect = () => {
@@ -206,7 +207,7 @@ export const SelectionHandler = (
206207
if (selected.length !== 1 || selected[0].id !== hovered.id) {
207208
selection.userSelect(hovered.id, evt);
208209
}
209-
} else if (!selection.isEmpty()) {
210+
} else {
210211
selection.clear();
211212
}
212213
};

packages/text-annotator/src/utils/cancelSingleClickEvents.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { NOT_ANNOTATABLE_SELECTOR } from './splitAnnotatableRanges';
1+
import { NOT_ANNOTATABLE_SELECTOR } from './isNotAnnotatable';
22

33
/**
44
* Calls .preventDefault() on click events in annotable areas, in order

packages/text-annotator/src/utils/getAnnotatableFragment.ts

Lines changed: 0 additions & 11 deletions
This file was deleted.
Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,18 @@
11
export * from './cancelSingleClickEvents';
2+
export * from './cloneEvents';
23
export * from './device';
34
export * from './programmaticallyFocusable';
45
export * from './debounce';
5-
export * from './getAnnotatableFragment';
66
export * from './getQuoteContext';
7-
export * from './isWhitespaceOrEmpty';
7+
export * from './isNotAnnotatable';
88
export * from './isRevived';
9+
export * from './isWhitespaceOrEmpty';
910
export * from './mergeClientRects';
1011
export * from './rangeToSelector';
1112
export * from './reviveAnnotation';
1213
export * from './reviveSelector';
1314
export * from './reviveTarget';
1415
export * from './splitAnnotatableRanges';
1516
export * from './trimRangeToContainer';
16-
export * from './cloneEvents';
17+
1718

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
export const NOT_ANNOTATABLE_CLASS = 'not-annotatable';
2+
3+
export const NOT_ANNOTATABLE_SELECTOR = `.${NOT_ANNOTATABLE_CLASS}`;
4+
5+
export const isNotAnnotatable = (node: Node): boolean => {
6+
const closestNotAnnotatable = node instanceof HTMLElement
7+
? node.closest(NOT_ANNOTATABLE_SELECTOR)
8+
: node.parentElement?.closest(NOT_ANNOTATABLE_SELECTOR);
9+
return Boolean(closestNotAnnotatable);
10+
}
11+
12+
export const isRangeAnnotatable = (range: Range): boolean => {
13+
const ancestor = range.commonAncestorContainer;
14+
return !isNotAnnotatable(ancestor);
15+
}

packages/text-annotator/src/utils/reviveSelector.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import type { TextSelector } from '../model';
2-
import { NOT_ANNOTATABLE_SELECTOR } from './splitAnnotatableRanges';
2+
import { NOT_ANNOTATABLE_SELECTOR } from './isNotAnnotatable';
33

44
/**
55
* Creates a new selector object with the revived DOM range from the given text annotation position

packages/text-annotator/src/utils/splitAnnotatableRanges.ts

Lines changed: 1 addition & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,4 @@
1-
export const NOT_ANNOTATABLE_CLASS = 'not-annotatable';
2-
3-
export const NOT_ANNOTATABLE_SELECTOR = `.${NOT_ANNOTATABLE_CLASS}`;
4-
5-
const isRangeAnnotatable = (range: Range): boolean => {
6-
const ancestor = range.commonAncestorContainer;
7-
return ancestor instanceof HTMLElement
8-
? !ancestor.closest(NOT_ANNOTATABLE_SELECTOR)
9-
: !ancestor.parentElement?.closest(NOT_ANNOTATABLE_SELECTOR);
10-
}
1+
import { isRangeAnnotatable, NOT_ANNOTATABLE_CLASS, NOT_ANNOTATABLE_SELECTOR } from './isNotAnnotatable';
112

123
const iterateNotAnnotatableElements = function*(range: Range): Generator<HTMLElement> {
134
const notAnnotatableIterator = document.createNodeIterator(

packages/text-annotator/test/model/w3c/fixtures.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -28,8 +28,8 @@ export const textAnnotation: W3CTextAnnotation = {
2828
},
2929
{
3030
type: 'TextPositionSelector',
31-
start: 986,
32-
end: 998
31+
start: 958,
32+
end: 970
3333
},
3434
]
3535
}

0 commit comments

Comments
 (0)