@prose-reader/enhancer-annotations 1.124.0 → 1.126.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/Commands.d.ts +6 -8
- package/dist/annotationsEnhancer.d.ts +2 -2
- package/dist/highlights/Highlight.d.ts +21 -0
- package/dist/highlights/ReaderHighlights.d.ts +3 -2
- package/dist/highlights/SpineItemHighlight.d.ts +4 -4
- package/dist/highlights/SpineItemHighlights.d.ts +3 -3
- package/dist/highlights/consolidate.d.ts +3 -0
- package/dist/highlights/utils.d.ts +1 -8
- package/dist/index.d.ts +1 -0
- package/dist/index.js +150 -61
- package/dist/index.js.map +1 -1
- package/dist/index.umd.cjs +152 -62
- package/dist/index.umd.cjs.map +1 -1
- package/dist/types.d.ts +5 -3
- package/package.json +2 -2
package/dist/Commands.d.ts
CHANGED
|
@@ -1,9 +1,7 @@
|
|
|
1
|
-
import { DestroyableClass } from '@prose-reader/core';
|
|
2
|
-
import {
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
selection: Selection;
|
|
6
|
-
itemId: string;
|
|
1
|
+
import { DestroyableClass, Reader } from '@prose-reader/core';
|
|
2
|
+
import { ObservedValueOf } from 'rxjs';
|
|
3
|
+
import { SerializableHighlight } from './types';
|
|
4
|
+
type HighlightParams = ObservedValueOf<Reader["selection"]["selection$"]> & {
|
|
7
5
|
color?: string;
|
|
8
6
|
contents?: string[];
|
|
9
7
|
};
|
|
@@ -15,7 +13,7 @@ export declare class Commands extends DestroyableClass {
|
|
|
15
13
|
}>;
|
|
16
14
|
readonly add$: import('rxjs').Observable<{
|
|
17
15
|
type: "add";
|
|
18
|
-
data:
|
|
16
|
+
data: SerializableHighlight | SerializableHighlight[];
|
|
19
17
|
}>;
|
|
20
18
|
readonly delete$: import('rxjs').Observable<{
|
|
21
19
|
type: "delete";
|
|
@@ -31,7 +29,7 @@ export declare class Commands extends DestroyableClass {
|
|
|
31
29
|
id: string | undefined;
|
|
32
30
|
}>;
|
|
33
31
|
highlight: (params: HighlightParams) => void;
|
|
34
|
-
add: (data:
|
|
32
|
+
add: (data: SerializableHighlight | SerializableHighlight[]) => void;
|
|
35
33
|
delete: (id: string) => void;
|
|
36
34
|
update: (id: string, data: Pick<HighlightParams, "color" | "contents">) => void;
|
|
37
35
|
select: (id: string | undefined) => void;
|
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
import { Reader } from '@prose-reader/core';
|
|
2
2
|
import { Observable } from 'rxjs';
|
|
3
|
-
import { RuntimeHighlight } from './types';
|
|
4
3
|
import { ReaderHighlights } from './highlights/ReaderHighlights';
|
|
5
4
|
import { Commands } from './Commands';
|
|
5
|
+
import { Highlight } from './highlights/Highlight';
|
|
6
6
|
export declare const annotationsEnhancer: <InheritOptions, InheritOutput extends Reader>(next: (options: InheritOptions) => InheritOutput) => (options: InheritOptions) => InheritOutput & {
|
|
7
7
|
annotations: {
|
|
8
|
-
|
|
8
|
+
highlights$: Observable<Highlight[]>;
|
|
9
9
|
highlightTap$: ReaderHighlights["tap$"];
|
|
10
10
|
highlight: Commands["highlight"];
|
|
11
11
|
add: Commands["add"];
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { SerializableHighlight } from '../types';
|
|
2
|
+
export declare class Highlight {
|
|
3
|
+
anchorCfi: string | undefined;
|
|
4
|
+
focusCfi: string | undefined;
|
|
5
|
+
itemIndex?: number;
|
|
6
|
+
color?: string;
|
|
7
|
+
contents?: string[];
|
|
8
|
+
spineItemPageIndex?: number;
|
|
9
|
+
absolutePageIndex?: number;
|
|
10
|
+
range?: Range;
|
|
11
|
+
lastSelectionText?: string;
|
|
12
|
+
/**
|
|
13
|
+
* Unique local ID. This is to ensure unicity
|
|
14
|
+
* for duplicate selections
|
|
15
|
+
*/
|
|
16
|
+
id: string;
|
|
17
|
+
constructor(params: SerializableHighlight & {
|
|
18
|
+
itemIndex?: number;
|
|
19
|
+
});
|
|
20
|
+
toJSON(): SerializableHighlight;
|
|
21
|
+
}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { DestroyableClass, Reader } from '@prose-reader/core';
|
|
2
2
|
import { BehaviorSubject } from 'rxjs';
|
|
3
|
-
import { Highlight } from '../types';
|
|
4
3
|
import { SpineItemHighlights } from './SpineItemHighlights';
|
|
4
|
+
import { Highlight } from './Highlight';
|
|
5
5
|
export declare class ReaderHighlights extends DestroyableClass {
|
|
6
6
|
private reader;
|
|
7
7
|
private highlights;
|
|
@@ -9,6 +9,7 @@ export declare class ReaderHighlights extends DestroyableClass {
|
|
|
9
9
|
private spineItemHighlights;
|
|
10
10
|
tap$: SpineItemHighlights["tap$"];
|
|
11
11
|
constructor(reader: Reader, highlights: BehaviorSubject<Highlight[]>, selectedHighlight: BehaviorSubject<string | undefined>);
|
|
12
|
-
getHighlightsForTarget: (target: EventTarget) =>
|
|
12
|
+
getHighlightsForTarget: (target: EventTarget) => Highlight[];
|
|
13
13
|
isTargetWithinHighlight: (target: EventTarget) => boolean;
|
|
14
|
+
layout(): void;
|
|
14
15
|
}
|
|
@@ -1,18 +1,18 @@
|
|
|
1
1
|
import { DestroyableClass, Reader, SpineItem } from '@prose-reader/core';
|
|
2
|
-
import { RuntimeHighlight } from '../types';
|
|
3
2
|
import { Observable } from 'rxjs';
|
|
3
|
+
import { Highlight } from './Highlight';
|
|
4
4
|
export declare class SpineItemHighlight extends DestroyableClass {
|
|
5
5
|
private spineItem;
|
|
6
6
|
private containerElement;
|
|
7
7
|
private reader;
|
|
8
|
-
readonly highlight:
|
|
8
|
+
readonly highlight: Highlight;
|
|
9
9
|
private isSelected;
|
|
10
10
|
private container;
|
|
11
11
|
readonly tap$: Observable<{
|
|
12
12
|
event: Event;
|
|
13
|
-
highlight:
|
|
13
|
+
highlight: Highlight;
|
|
14
14
|
}>;
|
|
15
|
-
constructor(spineItem: SpineItem, containerElement: HTMLElement, reader: Reader, highlight:
|
|
15
|
+
constructor(spineItem: SpineItem, containerElement: HTMLElement, reader: Reader, highlight: Highlight, isSelected: Observable<boolean>);
|
|
16
16
|
render(): void;
|
|
17
17
|
isWithinTarget(target: Node): boolean;
|
|
18
18
|
destroy(): void;
|
|
@@ -1,16 +1,16 @@
|
|
|
1
1
|
import { Observable } from 'rxjs';
|
|
2
|
-
import { RuntimeHighlight } from '../types';
|
|
3
2
|
import { DestroyableClass, Reader, SpineItem } from '@prose-reader/core';
|
|
4
3
|
import { SpineItemHighlight } from './SpineItemHighlight';
|
|
4
|
+
import { Highlight } from './Highlight';
|
|
5
5
|
export declare class SpineItemHighlights extends DestroyableClass {
|
|
6
|
-
private
|
|
6
|
+
private highlights$;
|
|
7
7
|
private spineItem;
|
|
8
8
|
private reader;
|
|
9
9
|
private selectedHighlight;
|
|
10
10
|
private layer;
|
|
11
11
|
private highlights;
|
|
12
12
|
readonly tap$: SpineItemHighlight["tap$"];
|
|
13
|
-
constructor(
|
|
13
|
+
constructor(highlights$: Observable<Highlight[]>, spineItem: SpineItem, reader: Reader, selectedHighlight: Observable<string | undefined>);
|
|
14
14
|
layout(): void;
|
|
15
15
|
getHighlightsForTarget(target: EventTarget): SpineItemHighlight[];
|
|
16
16
|
destroy(): void;
|
|
@@ -1,11 +1,4 @@
|
|
|
1
|
-
export declare const
|
|
2
|
-
export declare const getRangeFromSelection: (overlayElement: HTMLElement, anchor: {
|
|
3
|
-
node: Node;
|
|
4
|
-
offset?: number;
|
|
5
|
-
}, focus: {
|
|
6
|
-
node: Node;
|
|
7
|
-
offset?: number;
|
|
8
|
-
}) => Range;
|
|
1
|
+
export declare const createElementForRange: (range: Range, container: HTMLElement, color: string) => HTMLDivElement[];
|
|
9
2
|
export declare const copyPositionStyle: (source: HTMLElement, target: HTMLElement) => void;
|
|
10
3
|
export declare const createAnnotationLayer: (container: HTMLElement, layer: HTMLElement) => HTMLDivElement;
|
|
11
4
|
export declare const layoutAnnotationLayer: (layer: HTMLElement, annotationLayer: HTMLElement) => void;
|
package/dist/index.d.ts
CHANGED
package/dist/index.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
1
|
+
import { DestroyableClass, waitForSwitch } from '@prose-reader/core';
|
|
2
|
+
import { fromEvent, map, share, skip, tap, takeUntil, switchMap, distinctUntilChanged, of, shareReplay, merge, BehaviorSubject, Subject, filter, debounceTime, withLatestFrom } from 'rxjs';
|
|
3
3
|
|
|
4
4
|
const c = "@prose-reader", i = (e) => `[${e}]`, p = (e, n) => ({
|
|
5
5
|
debug: !n || (window == null ? void 0 : window.__PROSE_READER_DEBUG) !== !0 ? () => {
|
|
@@ -16,7 +16,7 @@ const c = "@prose-reader", i = (e) => `[${e}]`, p = (e, n) => ({
|
|
|
16
16
|
const IS_DEBUG_ENABLED = true;
|
|
17
17
|
const report = $.namespace(`enhancer-annotations`, IS_DEBUG_ENABLED);
|
|
18
18
|
|
|
19
|
-
const
|
|
19
|
+
const createElementForRange = (range, container, color) => {
|
|
20
20
|
const rects = Array.from(range.getClientRects());
|
|
21
21
|
const lineGroups = /* @__PURE__ */ new Map();
|
|
22
22
|
rects.forEach((rect) => {
|
|
@@ -31,39 +31,27 @@ const getElementsForRange = (range, container) => {
|
|
|
31
31
|
const right = Math.max(...lineRects.map((r) => r.right));
|
|
32
32
|
const top = lineRects[0]?.top;
|
|
33
33
|
const height = lineRects[0]?.height;
|
|
34
|
+
const rectEltContainer = container.ownerDocument.createElement("div");
|
|
34
35
|
const rectElt = container.ownerDocument.createElement("div");
|
|
35
|
-
|
|
36
|
+
rectEltContainer.style.cssText = `
|
|
36
37
|
position: absolute;
|
|
37
38
|
width: ${right - left}px;
|
|
38
39
|
height: ${height}px;
|
|
39
40
|
top: ${top}px;
|
|
40
41
|
left: ${left}px;
|
|
41
|
-
|
|
42
|
+
box-sizing: border-box;
|
|
43
|
+
border: 3px dashed transparent;
|
|
44
|
+
`;
|
|
45
|
+
rectElt.style.cssText = `
|
|
46
|
+
height: 100%;
|
|
47
|
+
width: 100%;
|
|
48
|
+
opacity: 25%;
|
|
49
|
+
background-color: ${color}
|
|
42
50
|
`;
|
|
43
|
-
|
|
51
|
+
rectEltContainer.appendChild(rectElt);
|
|
52
|
+
return rectEltContainer;
|
|
44
53
|
});
|
|
45
54
|
};
|
|
46
|
-
const getRangeFromSelection = (overlayElement, anchor, focus) => {
|
|
47
|
-
const range = overlayElement.ownerDocument.createRange();
|
|
48
|
-
const comparison = anchor.node.compareDocumentPosition(focus.node);
|
|
49
|
-
try {
|
|
50
|
-
if (comparison & Node.DOCUMENT_POSITION_PRECEDING) {
|
|
51
|
-
range.setStart(focus.node, focus.offset || 0);
|
|
52
|
-
range.setEnd(anchor.node, anchor.offset || 0);
|
|
53
|
-
} else if (comparison & Node.DOCUMENT_POSITION_FOLLOWING) {
|
|
54
|
-
range.setStart(anchor.node, anchor.offset || 0);
|
|
55
|
-
range.setEnd(focus.node, focus.offset || 0);
|
|
56
|
-
} else {
|
|
57
|
-
const startOffset = Math.min(anchor.offset || 0, focus.offset || 0);
|
|
58
|
-
const endOffset = Math.max(anchor.offset || 0, focus.offset || 0);
|
|
59
|
-
range.setStart(anchor.node, startOffset);
|
|
60
|
-
range.setEnd(anchor.node, endOffset);
|
|
61
|
-
}
|
|
62
|
-
} catch (e) {
|
|
63
|
-
report.error(e);
|
|
64
|
-
}
|
|
65
|
-
return range;
|
|
66
|
-
};
|
|
67
55
|
const copyPositionStyle = (source, target) => {
|
|
68
56
|
target.style.cssText = source.style.cssText;
|
|
69
57
|
};
|
|
@@ -92,6 +80,7 @@ class SpineItemHighlight extends DestroyableClass {
|
|
|
92
80
|
this.highlight = highlight;
|
|
93
81
|
this.isSelected = isSelected;
|
|
94
82
|
void this.spineItem;
|
|
83
|
+
void this.reader;
|
|
95
84
|
this.container = this.containerElement.ownerDocument.createElement("div");
|
|
96
85
|
this.container.dataset["highlightContainer"] = this.highlight.id;
|
|
97
86
|
this.containerElement.appendChild(this.container);
|
|
@@ -105,7 +94,7 @@ class SpineItemHighlight extends DestroyableClass {
|
|
|
105
94
|
tap((isSelected2) => {
|
|
106
95
|
Array.from(this.container.children).forEach((child) => {
|
|
107
96
|
if (child instanceof HTMLElement) {
|
|
108
|
-
child.style.border = isSelected2 ? "3px dashed red" : "
|
|
97
|
+
child.style.border = isSelected2 ? "3px dashed red" : "3px dashed transparent";
|
|
109
98
|
}
|
|
110
99
|
});
|
|
111
100
|
}),
|
|
@@ -116,25 +105,29 @@ class SpineItemHighlight extends DestroyableClass {
|
|
|
116
105
|
tap$;
|
|
117
106
|
render() {
|
|
118
107
|
this.container.innerHTML = "";
|
|
119
|
-
const
|
|
120
|
-
|
|
121
|
-
if (!anchorNode || !focusNode) {
|
|
122
|
-
report.error(`Unable to resolve anchor cfi: ${this.highlight.anchorCfi}`);
|
|
108
|
+
const range = this.highlight.range;
|
|
109
|
+
if (!range) {
|
|
123
110
|
return;
|
|
124
111
|
}
|
|
125
|
-
const
|
|
126
|
-
this.containerElement,
|
|
127
|
-
{ node: anchorNode, offset: anchorOffset },
|
|
128
|
-
{ node: focusNode, offset: focusOffset }
|
|
129
|
-
);
|
|
130
|
-
const rectElements = getElementsForRange(range, this.container);
|
|
112
|
+
const rectElements = createElementForRange(range, this.container, this.highlight.color ?? "yellow");
|
|
131
113
|
rectElements.forEach((elt) => {
|
|
132
114
|
elt.style.pointerEvents = "initial";
|
|
133
115
|
elt.style.cursor = "pointer";
|
|
134
|
-
elt.style.backgroundColor = this.highlight.color ?? "yellow";
|
|
135
116
|
elt.dataset["highlightRect"] = this.highlight.id;
|
|
136
117
|
this.container.appendChild(elt);
|
|
137
118
|
});
|
|
119
|
+
const firstElement = rectElements[0];
|
|
120
|
+
if (firstElement && this.highlight.contents?.length) {
|
|
121
|
+
const noteIcon = document.createElement("span");
|
|
122
|
+
noteIcon.textContent = "📝";
|
|
123
|
+
noteIcon.style.position = "absolute";
|
|
124
|
+
noteIcon.style.top = "0";
|
|
125
|
+
noteIcon.style.left = "0";
|
|
126
|
+
noteIcon.style.transform = "translate(-0%, -80%)";
|
|
127
|
+
noteIcon.style.fontSize = "18px";
|
|
128
|
+
noteIcon.style.opacity = "50%";
|
|
129
|
+
firstElement.appendChild(noteIcon);
|
|
130
|
+
}
|
|
138
131
|
}
|
|
139
132
|
isWithinTarget(target) {
|
|
140
133
|
return this.container.contains(target);
|
|
@@ -146,20 +139,20 @@ class SpineItemHighlight extends DestroyableClass {
|
|
|
146
139
|
}
|
|
147
140
|
|
|
148
141
|
class SpineItemHighlights extends DestroyableClass {
|
|
149
|
-
constructor(
|
|
142
|
+
constructor(highlights$, spineItem, reader, selectedHighlight) {
|
|
150
143
|
super();
|
|
151
|
-
this.
|
|
144
|
+
this.highlights$ = highlights$;
|
|
152
145
|
this.spineItem = spineItem;
|
|
153
146
|
this.reader = reader;
|
|
154
147
|
this.selectedHighlight = selectedHighlight;
|
|
155
148
|
const firstLayerElement = spineItem.renderer.layers[0]?.element ?? document.createElement("div");
|
|
156
149
|
this.layer = createAnnotationLayer(this.spineItem.containerElement, firstLayerElement);
|
|
157
|
-
const
|
|
150
|
+
const itemHighlights$ = this.highlights$.pipe(
|
|
158
151
|
switchMap((annotations) => {
|
|
159
152
|
this.highlights.forEach((highlight) => highlight.destroy());
|
|
160
153
|
this.highlights = [];
|
|
161
154
|
annotations.forEach((annotation) => {
|
|
162
|
-
if (annotation.
|
|
155
|
+
if (annotation.itemIndex !== this.spineItem.item.index) return;
|
|
163
156
|
const isSelected$ = this.selectedHighlight.pipe(
|
|
164
157
|
map((id) => id === annotation.id),
|
|
165
158
|
distinctUntilChanged()
|
|
@@ -172,7 +165,7 @@ class SpineItemHighlights extends DestroyableClass {
|
|
|
172
165
|
shareReplay(1),
|
|
173
166
|
takeUntil(this.destroy$)
|
|
174
167
|
);
|
|
175
|
-
this.tap$ =
|
|
168
|
+
this.tap$ = itemHighlights$.pipe(switchMap((highlights) => merge(...highlights.map((highlight) => highlight.tap$))));
|
|
176
169
|
highlights$.subscribe();
|
|
177
170
|
}
|
|
178
171
|
layer;
|
|
@@ -202,18 +195,13 @@ class ReaderHighlights extends DestroyableClass {
|
|
|
202
195
|
const spineItem = reader.spineItemsManager.get(itemId);
|
|
203
196
|
if (!spineItem) return;
|
|
204
197
|
const spineItemHighlights$ = this.highlights.pipe(
|
|
205
|
-
map((highlights2) => highlights2.filter((highlight) => highlight.
|
|
198
|
+
map((highlights2) => highlights2.filter((highlight) => highlight.itemIndex === spineItem.item.index))
|
|
206
199
|
);
|
|
207
200
|
const spineItemHighlights = new SpineItemHighlights(spineItemHighlights$, spineItem, reader, this.selectedHighlight);
|
|
208
201
|
this.spineItemHighlights.next([...this.spineItemHighlights.getValue(), spineItemHighlights]);
|
|
209
|
-
const deregister = reader.hookManager.register("item.onAfterLayout", ({ item }) => {
|
|
210
|
-
if (item.id !== itemId) return;
|
|
211
|
-
spineItemHighlights.layout();
|
|
212
|
-
});
|
|
213
202
|
destroy(() => {
|
|
214
203
|
this.spineItemHighlights.next(this.spineItemHighlights.getValue().filter((layer) => layer !== spineItemHighlights));
|
|
215
204
|
spineItemHighlights.destroy();
|
|
216
|
-
deregister();
|
|
217
205
|
});
|
|
218
206
|
});
|
|
219
207
|
this.tap$ = this.spineItemHighlights.pipe(switchMap((layers) => merge(...layers.map((layer) => layer.tap$))));
|
|
@@ -226,6 +214,9 @@ class ReaderHighlights extends DestroyableClass {
|
|
|
226
214
|
isTargetWithinHighlight = (target) => {
|
|
227
215
|
return !!this.getHighlightsForTarget(target).length;
|
|
228
216
|
};
|
|
217
|
+
layout() {
|
|
218
|
+
this.spineItemHighlights.getValue().forEach((item) => item.layout());
|
|
219
|
+
}
|
|
229
220
|
}
|
|
230
221
|
|
|
231
222
|
class Commands extends DestroyableClass {
|
|
@@ -256,6 +247,82 @@ class Commands extends DestroyableClass {
|
|
|
256
247
|
}
|
|
257
248
|
}
|
|
258
249
|
|
|
250
|
+
class Highlight {
|
|
251
|
+
anchorCfi;
|
|
252
|
+
focusCfi;
|
|
253
|
+
itemIndex;
|
|
254
|
+
color;
|
|
255
|
+
contents;
|
|
256
|
+
spineItemPageIndex;
|
|
257
|
+
absolutePageIndex;
|
|
258
|
+
range;
|
|
259
|
+
lastSelectionText;
|
|
260
|
+
/**
|
|
261
|
+
* Unique local ID. This is to ensure unicity
|
|
262
|
+
* for duplicate selections
|
|
263
|
+
*/
|
|
264
|
+
id;
|
|
265
|
+
constructor(params) {
|
|
266
|
+
this.anchorCfi = params.anchorCfi;
|
|
267
|
+
this.focusCfi = params.focusCfi;
|
|
268
|
+
this.itemIndex = params.itemIndex;
|
|
269
|
+
this.color = params.color;
|
|
270
|
+
this.contents = params.contents;
|
|
271
|
+
this.id = params.id;
|
|
272
|
+
this.lastSelectionText = params.lastSelectionText;
|
|
273
|
+
}
|
|
274
|
+
toJSON() {
|
|
275
|
+
return {
|
|
276
|
+
anchorCfi: this.anchorCfi,
|
|
277
|
+
focusCfi: this.focusCfi,
|
|
278
|
+
color: this.color,
|
|
279
|
+
contents: this.contents,
|
|
280
|
+
id: this.id,
|
|
281
|
+
lastSelectionText: this.lastSelectionText
|
|
282
|
+
};
|
|
283
|
+
}
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
const consolidate = (highlight, reader) => {
|
|
287
|
+
const { itemIndex } = reader.cfi.parseCfi(highlight.anchorCfi ?? "");
|
|
288
|
+
const spineItem = reader.spineItemsManager.get(itemIndex);
|
|
289
|
+
const resolvedAnchorCfi = reader.cfi.resolveCfi({ cfi: highlight.anchorCfi ?? "" });
|
|
290
|
+
const resolvedFocusCfi = reader.cfi.resolveCfi({ cfi: highlight.focusCfi ?? "" });
|
|
291
|
+
if (!spineItem) return;
|
|
292
|
+
if (spineItem.item.renditionLayout === `pre-paginated`) {
|
|
293
|
+
highlight.spineItemPageIndex = 0;
|
|
294
|
+
} else {
|
|
295
|
+
if (resolvedAnchorCfi?.node) {
|
|
296
|
+
highlight.spineItemPageIndex = reader.spine.locator.spineItemLocator.getSpineItemPageIndexFromNode(
|
|
297
|
+
resolvedAnchorCfi.node,
|
|
298
|
+
resolvedAnchorCfi.offset ?? 0,
|
|
299
|
+
spineItem
|
|
300
|
+
);
|
|
301
|
+
}
|
|
302
|
+
}
|
|
303
|
+
if (resolvedAnchorCfi?.node && resolvedFocusCfi?.node && spineItem.isReady) {
|
|
304
|
+
const range = reader.selection.createRangeFromSelection({
|
|
305
|
+
selection: {
|
|
306
|
+
anchorNode: resolvedAnchorCfi.node,
|
|
307
|
+
anchorOffset: resolvedAnchorCfi.offset ?? 0,
|
|
308
|
+
focusNode: resolvedFocusCfi.node,
|
|
309
|
+
focusOffset: resolvedFocusCfi.offset ?? 0
|
|
310
|
+
},
|
|
311
|
+
spineItem
|
|
312
|
+
});
|
|
313
|
+
highlight.range = range;
|
|
314
|
+
highlight.lastSelectionText = range?.toString();
|
|
315
|
+
} else {
|
|
316
|
+
highlight.range = void 0;
|
|
317
|
+
}
|
|
318
|
+
if (highlight.spineItemPageIndex !== void 0) {
|
|
319
|
+
highlight.absolutePageIndex = reader.spine.locator.getAbsolutePageIndexFromPageIndex({
|
|
320
|
+
pageIndex: highlight.spineItemPageIndex,
|
|
321
|
+
spineItemOrId: spineItem
|
|
322
|
+
});
|
|
323
|
+
}
|
|
324
|
+
};
|
|
325
|
+
|
|
259
326
|
const annotationsEnhancer = (next) => (options) => {
|
|
260
327
|
const reader = next(options);
|
|
261
328
|
const commands = new Commands();
|
|
@@ -263,16 +330,27 @@ const annotationsEnhancer = (next) => (options) => {
|
|
|
263
330
|
const selectedHighlightSubject = new BehaviorSubject(void 0);
|
|
264
331
|
const readerHighlights = new ReaderHighlights(reader, highlightsSubject, selectedHighlightSubject);
|
|
265
332
|
const highlight$ = commands.highlight$.pipe(
|
|
266
|
-
tap(({ data: {
|
|
267
|
-
const
|
|
268
|
-
|
|
269
|
-
|
|
333
|
+
tap(({ data: { itemIndex, selection, ...rest } }) => {
|
|
334
|
+
const item = reader.spineItemsManager.get(itemIndex)?.item;
|
|
335
|
+
if (!item) return;
|
|
336
|
+
const { anchorCfi, focusCfi } = reader.cfi.generateCfiFromSelection({ item, selection });
|
|
337
|
+
const highlight = new Highlight({ anchorCfi, focusCfi, itemIndex, id: window.crypto.randomUUID(), ...rest });
|
|
338
|
+
consolidate(highlight, reader);
|
|
339
|
+
highlightsSubject.next([...highlightsSubject.getValue(), highlight]);
|
|
270
340
|
})
|
|
271
341
|
);
|
|
272
342
|
const add$ = commands.add$.pipe(
|
|
273
343
|
tap(({ data }) => {
|
|
274
344
|
const annotations = Array.isArray(data) ? data : [data];
|
|
275
|
-
highlightsSubject.next([
|
|
345
|
+
highlightsSubject.next([
|
|
346
|
+
...highlightsSubject.getValue(),
|
|
347
|
+
...annotations.map((annotation) => {
|
|
348
|
+
const { itemIndex } = reader.cfi.parseCfi(annotation.anchorCfi ?? "");
|
|
349
|
+
const highlight = new Highlight({ ...annotation, itemIndex });
|
|
350
|
+
consolidate(highlight, reader);
|
|
351
|
+
return highlight;
|
|
352
|
+
})
|
|
353
|
+
]);
|
|
276
354
|
})
|
|
277
355
|
);
|
|
278
356
|
const delete$ = commands.delete$.pipe(
|
|
@@ -283,7 +361,7 @@ const annotationsEnhancer = (next) => (options) => {
|
|
|
283
361
|
const update$ = commands.update$.pipe(
|
|
284
362
|
tap(({ id, data }) => {
|
|
285
363
|
highlightsSubject.next(
|
|
286
|
-
highlightsSubject.getValue().map((highlight) => highlight.id === id ? { ...highlight, ...data } : highlight)
|
|
364
|
+
highlightsSubject.getValue().map((highlight) => highlight.id === id ? new Highlight({ ...highlight, ...data }) : highlight)
|
|
287
365
|
);
|
|
288
366
|
})
|
|
289
367
|
);
|
|
@@ -292,16 +370,27 @@ const annotationsEnhancer = (next) => (options) => {
|
|
|
292
370
|
selectedHighlightSubject.next(id);
|
|
293
371
|
})
|
|
294
372
|
);
|
|
295
|
-
const
|
|
373
|
+
const highlights$ = highlightsSubject.asObservable();
|
|
374
|
+
const highlightsConsolidation$ = reader.layout$.pipe(
|
|
375
|
+
debounceTime(50),
|
|
376
|
+
waitForSwitch(reader.viewportFree$),
|
|
377
|
+
withLatestFrom(highlights$),
|
|
378
|
+
tap(([, highlights]) => {
|
|
379
|
+
highlights.forEach((highlight) => consolidate(highlight, reader));
|
|
380
|
+
highlightsSubject.next(highlights);
|
|
381
|
+
readerHighlights.layout();
|
|
382
|
+
})
|
|
383
|
+
);
|
|
296
384
|
merge(
|
|
297
385
|
highlight$,
|
|
298
386
|
add$,
|
|
299
387
|
delete$,
|
|
300
388
|
update$,
|
|
301
389
|
select$,
|
|
302
|
-
|
|
390
|
+
highlightsConsolidation$,
|
|
391
|
+
highlights$.pipe(
|
|
303
392
|
tap((annotations) => {
|
|
304
|
-
report.debug("
|
|
393
|
+
report.debug("highlights", annotations);
|
|
305
394
|
})
|
|
306
395
|
)
|
|
307
396
|
).pipe(takeUntil(reader.$.destroy$)).subscribe();
|
|
@@ -314,7 +403,7 @@ const annotationsEnhancer = (next) => (options) => {
|
|
|
314
403
|
reader.destroy();
|
|
315
404
|
},
|
|
316
405
|
annotations: {
|
|
317
|
-
|
|
406
|
+
highlights$,
|
|
318
407
|
highlightTap$: readerHighlights.tap$,
|
|
319
408
|
isTargetWithinHighlight: readerHighlights.isTargetWithinHighlight,
|
|
320
409
|
highlight: commands.highlight,
|
|
@@ -326,5 +415,5 @@ const annotationsEnhancer = (next) => (options) => {
|
|
|
326
415
|
};
|
|
327
416
|
};
|
|
328
417
|
|
|
329
|
-
export { annotationsEnhancer };
|
|
418
|
+
export { Highlight, annotationsEnhancer };
|
|
330
419
|
//# sourceMappingURL=index.js.map
|