@embedpdf/plugin-annotation 2.2.0 → 2.3.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/index.cjs +1 -1
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +1196 -92
- package/dist/index.js.map +1 -1
- package/dist/lib/actions.d.ts +31 -2
- package/dist/lib/annotation-plugin.d.ts +177 -2
- package/dist/lib/handlers/types.d.ts +1 -1
- package/dist/lib/helpers.d.ts +6 -1
- package/dist/lib/selectors.d.ts +97 -8
- package/dist/lib/tools/default-tools.d.ts +39 -11
- package/dist/lib/tools/types.d.ts +7 -1
- package/dist/lib/types.d.ts +265 -1
- package/dist/preact/adapter.d.ts +3 -3
- package/dist/preact/index.cjs +1 -1
- package/dist/preact/index.cjs.map +1 -1
- package/dist/preact/index.js +857 -403
- package/dist/preact/index.js.map +1 -1
- package/dist/react/adapter.d.ts +1 -1
- package/dist/react/index.cjs +1 -1
- package/dist/react/index.cjs.map +1 -1
- package/dist/react/index.js +857 -403
- package/dist/react/index.js.map +1 -1
- package/dist/shared/components/annotation-container.d.ts +8 -1
- package/dist/shared/components/annotation-layer.d.ts +4 -2
- package/dist/shared/components/annotations/ink.d.ts +3 -3
- package/dist/shared/components/annotations/link.d.ts +28 -0
- package/dist/shared/components/annotations.d.ts +2 -1
- package/dist/shared/components/group-selection-box.d.ts +32 -0
- package/dist/shared/components/index.d.ts +1 -0
- package/dist/shared/components/text-markup/highlight.d.ts +3 -2
- package/dist/shared/components/text-markup/squiggly.d.ts +3 -2
- package/dist/shared/components/text-markup/strikeout.d.ts +3 -2
- package/dist/shared/components/text-markup/underline.d.ts +3 -2
- package/dist/shared/components/types.d.ts +7 -0
- package/dist/shared-preact/components/annotation-container.d.ts +8 -1
- package/dist/shared-preact/components/annotation-layer.d.ts +4 -2
- package/dist/shared-preact/components/annotations/ink.d.ts +3 -3
- package/dist/shared-preact/components/annotations/link.d.ts +28 -0
- package/dist/shared-preact/components/annotations.d.ts +2 -1
- package/dist/shared-preact/components/group-selection-box.d.ts +32 -0
- package/dist/shared-preact/components/index.d.ts +1 -0
- package/dist/shared-preact/components/text-markup/highlight.d.ts +3 -2
- package/dist/shared-preact/components/text-markup/squiggly.d.ts +3 -2
- package/dist/shared-preact/components/text-markup/strikeout.d.ts +3 -2
- package/dist/shared-preact/components/text-markup/underline.d.ts +3 -2
- package/dist/shared-preact/components/types.d.ts +7 -0
- package/dist/shared-react/components/annotation-container.d.ts +8 -1
- package/dist/shared-react/components/annotation-layer.d.ts +4 -2
- package/dist/shared-react/components/annotations/ink.d.ts +3 -3
- package/dist/shared-react/components/annotations/link.d.ts +28 -0
- package/dist/shared-react/components/annotations.d.ts +2 -1
- package/dist/shared-react/components/group-selection-box.d.ts +32 -0
- package/dist/shared-react/components/index.d.ts +1 -0
- package/dist/shared-react/components/text-markup/highlight.d.ts +3 -2
- package/dist/shared-react/components/text-markup/squiggly.d.ts +3 -2
- package/dist/shared-react/components/text-markup/strikeout.d.ts +3 -2
- package/dist/shared-react/components/text-markup/underline.d.ts +3 -2
- package/dist/shared-react/components/types.d.ts +7 -0
- package/dist/svelte/components/AnnotationLayer.svelte.d.ts +5 -1
- package/dist/svelte/components/Annotations.svelte.d.ts +5 -1
- package/dist/svelte/components/GroupSelectionBox.svelte.d.ts +32 -0
- package/dist/svelte/components/annotations/Ink.svelte.d.ts +2 -1
- package/dist/svelte/components/annotations/Link.svelte.d.ts +24 -0
- package/dist/svelte/components/annotations/index.d.ts +1 -0
- package/dist/svelte/components/index.d.ts +1 -0
- package/dist/svelte/components/text-markup/Highlight.svelte.d.ts +2 -1
- package/dist/svelte/components/text-markup/Squiggly.svelte.d.ts +2 -1
- package/dist/svelte/components/text-markup/Strikeout.svelte.d.ts +2 -1
- package/dist/svelte/components/text-markup/Underline.svelte.d.ts +2 -1
- package/dist/svelte/components/types.d.ts +2 -0
- package/dist/svelte/index.cjs +1 -1
- package/dist/svelte/index.cjs.map +1 -1
- package/dist/svelte/index.js +905 -255
- package/dist/svelte/index.js.map +1 -1
- package/dist/svelte/types.d.ts +7 -0
- package/dist/vue/components/annotation-container.vue.d.ts +2 -0
- package/dist/vue/components/annotation-layer.vue.d.ts +28 -5
- package/dist/vue/components/annotations/index.d.ts +1 -0
- package/dist/vue/components/annotations/ink.vue.d.ts +2 -2
- package/dist/vue/components/annotations/line.vue.d.ts +1 -1
- package/dist/vue/components/annotations/link.vue.d.ts +29 -0
- package/dist/vue/components/annotations/polygon.vue.d.ts +1 -1
- package/dist/vue/components/annotations/polyline.vue.d.ts +1 -1
- package/dist/vue/components/annotations.vue.d.ts +65 -1
- package/dist/vue/components/group-selection-box.vue.d.ts +73 -0
- package/dist/vue/components/index.d.ts +1 -0
- package/dist/vue/components/text-markup/highlight.vue.d.ts +2 -2
- package/dist/vue/components/text-markup/squiggly.vue.d.ts +2 -2
- package/dist/vue/components/text-markup/strikeout.vue.d.ts +2 -2
- package/dist/vue/components/text-markup/underline.vue.d.ts +2 -2
- package/dist/vue/hooks/use-annotation.d.ts +2 -0
- package/dist/vue/index.cjs +1 -1
- package/dist/vue/index.cjs.map +1 -1
- package/dist/vue/index.js +946 -406
- package/dist/vue/index.js.map +1 -1
- package/dist/vue/types.d.ts +7 -0
- package/package.json +10 -10
package/dist/index.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { clamp, BasePlugin, createBehaviorEmitter } from "@embedpdf/core";
|
|
2
|
-
import { PdfAnnotationSubtype, PdfBlendMode, PdfAnnotationBorderStyle, PdfAnnotationLineEnding, PdfVerticalAlignment, PdfTextAlignment, PdfStandardFont, expandRect, rectFromPoints, uuidV4, rotateAndTranslatePoint, PdfAnnotationIcon, PdfPermissionFlag, ignore, PdfTaskHelper, PdfErrorCode, Task } from "@embedpdf/models";
|
|
2
|
+
import { PdfAnnotationSubtype, PdfAnnotationReplyType, PdfBlendMode, PdfAnnotationBorderStyle, PdfAnnotationLineEnding, PdfVerticalAlignment, PdfTextAlignment, PdfStandardFont, expandRect, rectFromPoints, uuidV4, rotateAndTranslatePoint, PdfAnnotationIcon, PdfPermissionFlag, ignore, PdfTaskHelper, PdfErrorCode, Task, TaskStage } from "@embedpdf/models";
|
|
3
3
|
const ANNOTATION_PLUGIN_ID = "annotation";
|
|
4
4
|
const manifest = {
|
|
5
5
|
id: ANNOTATION_PLUGIN_ID,
|
|
@@ -21,6 +21,9 @@ const SET_ACTIVE_DOCUMENT = "ANNOTATION/SET_ACTIVE_DOCUMENT";
|
|
|
21
21
|
const SET_ANNOTATIONS = "ANNOTATION/SET_ANNOTATIONS";
|
|
22
22
|
const SELECT_ANNOTATION = "ANNOTATION/SELECT_ANNOTATION";
|
|
23
23
|
const DESELECT_ANNOTATION = "ANNOTATION/DESELECT_ANNOTATION";
|
|
24
|
+
const ADD_TO_SELECTION = "ANNOTATION/ADD_TO_SELECTION";
|
|
25
|
+
const REMOVE_FROM_SELECTION = "ANNOTATION/REMOVE_FROM_SELECTION";
|
|
26
|
+
const SET_SELECTION = "ANNOTATION/SET_SELECTION";
|
|
24
27
|
const SET_ACTIVE_TOOL_ID = "ANNOTATION/SET_ACTIVE_TOOL_ID";
|
|
25
28
|
const CREATE_ANNOTATION = "ANNOTATION/CREATE_ANNOTATION";
|
|
26
29
|
const PATCH_ANNOTATION = "ANNOTATION/PATCH_ANNOTATION";
|
|
@@ -48,6 +51,18 @@ const deselectAnnotation = (documentId) => ({
|
|
|
48
51
|
type: DESELECT_ANNOTATION,
|
|
49
52
|
payload: { documentId }
|
|
50
53
|
});
|
|
54
|
+
const addToSelection = (documentId, pageIndex, id) => ({
|
|
55
|
+
type: ADD_TO_SELECTION,
|
|
56
|
+
payload: { documentId, pageIndex, id }
|
|
57
|
+
});
|
|
58
|
+
const removeFromSelection = (documentId, id) => ({
|
|
59
|
+
type: REMOVE_FROM_SELECTION,
|
|
60
|
+
payload: { documentId, id }
|
|
61
|
+
});
|
|
62
|
+
const setSelection = (documentId, ids) => ({
|
|
63
|
+
type: SET_SELECTION,
|
|
64
|
+
payload: { documentId, ids }
|
|
65
|
+
});
|
|
51
66
|
const setActiveToolId = (documentId, toolId) => ({
|
|
52
67
|
type: SET_ACTIVE_TOOL_ID,
|
|
53
68
|
payload: { documentId, toolId }
|
|
@@ -64,9 +79,9 @@ const deleteAnnotation = (documentId, pageIndex, id) => ({
|
|
|
64
79
|
type: DELETE_ANNOTATION,
|
|
65
80
|
payload: { documentId, pageIndex, id }
|
|
66
81
|
});
|
|
67
|
-
const commitPendingChanges = (documentId) => ({
|
|
82
|
+
const commitPendingChanges = (documentId, committedUids) => ({
|
|
68
83
|
type: COMMIT_PENDING_CHANGES,
|
|
69
|
-
payload: { documentId }
|
|
84
|
+
payload: { documentId, committedUids }
|
|
70
85
|
});
|
|
71
86
|
const purgeAnnotation = (documentId, uid) => ({
|
|
72
87
|
type: PURGE_ANNOTATION,
|
|
@@ -84,6 +99,9 @@ const addTool = (tool) => ({
|
|
|
84
99
|
type: ADD_TOOL,
|
|
85
100
|
payload: tool
|
|
86
101
|
});
|
|
102
|
+
function rectsIntersect(a, b) {
|
|
103
|
+
return !(a.origin.x + a.size.width < b.origin.x || b.origin.x + b.size.width < a.origin.x || a.origin.y + a.size.height < b.origin.y || b.origin.y + b.size.height < a.origin.y);
|
|
104
|
+
}
|
|
87
105
|
function isInk(a) {
|
|
88
106
|
return a.object.type === PdfAnnotationSubtype.INK;
|
|
89
107
|
}
|
|
@@ -126,6 +144,9 @@ function isStamp(a) {
|
|
|
126
144
|
function isText(a) {
|
|
127
145
|
return a.object.type === PdfAnnotationSubtype.TEXT;
|
|
128
146
|
}
|
|
147
|
+
function isLink(a) {
|
|
148
|
+
return a.object.type === PdfAnnotationSubtype.LINK;
|
|
149
|
+
}
|
|
129
150
|
function isSidebarAnnotation(a) {
|
|
130
151
|
return isTextMarkup(a) || isInk(a) || isSquare(a) || isCircle(a) || isPolygon(a) || isLine(a) || isPolyline(a) || isFreeText(a) || isStamp(a);
|
|
131
152
|
}
|
|
@@ -135,17 +156,25 @@ const getAnnotations = (s) => {
|
|
|
135
156
|
for (const p of Object.keys(s.pages).map(Number)) out[p] = getAnnotationsByPageIndex(s, p);
|
|
136
157
|
return out;
|
|
137
158
|
};
|
|
138
|
-
const getSelectedAnnotation = (s) => s.
|
|
159
|
+
const getSelectedAnnotation = (s) => s.selectedUids.length > 0 ? s.byUid[s.selectedUids[0]] ?? null : null;
|
|
160
|
+
const getSelectedAnnotations = (s) => s.selectedUids.map((uid) => s.byUid[uid]).filter((ta) => ta !== void 0);
|
|
161
|
+
const getSelectedAnnotationIds = (s) => s.selectedUids;
|
|
139
162
|
const getAnnotationByUid = (s, uid) => s.byUid[uid] ?? null;
|
|
140
163
|
const getSelectedAnnotationByPageIndex = (s, pageIndex) => {
|
|
141
|
-
if (!s.selectedUid) return null;
|
|
142
164
|
const pageUids = s.pages[pageIndex] ?? [];
|
|
143
|
-
|
|
144
|
-
|
|
165
|
+
for (const uid of s.selectedUids) {
|
|
166
|
+
if (pageUids.includes(uid)) {
|
|
167
|
+
return s.byUid[uid] ?? null;
|
|
168
|
+
}
|
|
145
169
|
}
|
|
146
170
|
return null;
|
|
147
171
|
};
|
|
148
|
-
const
|
|
172
|
+
const getSelectedAnnotationsByPageIndex = (s, pageIndex) => {
|
|
173
|
+
const pageUids = new Set(s.pages[pageIndex] ?? []);
|
|
174
|
+
return s.selectedUids.filter((uid) => pageUids.has(uid)).map((uid) => s.byUid[uid]).filter((ta) => ta !== void 0);
|
|
175
|
+
};
|
|
176
|
+
const isAnnotationSelected = (s, id) => s.selectedUids.includes(id);
|
|
177
|
+
const hasMultipleSelected = (s) => s.selectedUids.length > 1;
|
|
149
178
|
function getToolDefaultsById(state, toolId) {
|
|
150
179
|
const tool = state.tools.find((t) => t.id === toolId);
|
|
151
180
|
return tool == null ? void 0 : tool.defaults;
|
|
@@ -190,6 +219,101 @@ const getSidebarAnnotationsWithReplies = (s) => {
|
|
|
190
219
|
}
|
|
191
220
|
return out;
|
|
192
221
|
};
|
|
222
|
+
const getIRTChildIds = (s, parentId) => {
|
|
223
|
+
const children = [];
|
|
224
|
+
for (const uidList of Object.values(s.pages)) {
|
|
225
|
+
for (const uid of uidList) {
|
|
226
|
+
const ta = s.byUid[uid];
|
|
227
|
+
if (ta && "inReplyToId" in ta.object && ta.object.inReplyToId === parentId) {
|
|
228
|
+
children.push({ id: ta.object.id, pageIndex: ta.object.pageIndex });
|
|
229
|
+
}
|
|
230
|
+
}
|
|
231
|
+
}
|
|
232
|
+
return children;
|
|
233
|
+
};
|
|
234
|
+
const hasIRTChildren = (s, parentId) => {
|
|
235
|
+
for (const uidList of Object.values(s.pages)) {
|
|
236
|
+
for (const uid of uidList) {
|
|
237
|
+
const ta = s.byUid[uid];
|
|
238
|
+
if (ta && "inReplyToId" in ta.object && ta.object.inReplyToId === parentId) {
|
|
239
|
+
return true;
|
|
240
|
+
}
|
|
241
|
+
}
|
|
242
|
+
}
|
|
243
|
+
return false;
|
|
244
|
+
};
|
|
245
|
+
const getIRTChildrenByType = (s, parentId, types) => {
|
|
246
|
+
const children = [];
|
|
247
|
+
for (const uidList of Object.values(s.pages)) {
|
|
248
|
+
for (const uid of uidList) {
|
|
249
|
+
const ta = s.byUid[uid];
|
|
250
|
+
if (ta && "inReplyToId" in ta.object && ta.object.inReplyToId === parentId && types.includes(ta.object.type)) {
|
|
251
|
+
children.push(ta);
|
|
252
|
+
}
|
|
253
|
+
}
|
|
254
|
+
}
|
|
255
|
+
return children;
|
|
256
|
+
};
|
|
257
|
+
const getAttachedLinks = (s, parentId) => getIRTChildrenByType(s, parentId, [PdfAnnotationSubtype.LINK]);
|
|
258
|
+
const hasAttachedLinks = (s, parentId) => getAttachedLinks(s, parentId).length > 0;
|
|
259
|
+
const getGroupLeaderId = (s, annotationId) => {
|
|
260
|
+
const ta = s.byUid[annotationId];
|
|
261
|
+
if (!ta) return void 0;
|
|
262
|
+
if (ta.object.inReplyToId && ta.object.replyType === PdfAnnotationReplyType.Group) {
|
|
263
|
+
return ta.object.inReplyToId;
|
|
264
|
+
}
|
|
265
|
+
return annotationId;
|
|
266
|
+
};
|
|
267
|
+
const getGroupMembers = (s, annotationId) => {
|
|
268
|
+
const leaderId = getGroupLeaderId(s, annotationId);
|
|
269
|
+
if (!leaderId) return [];
|
|
270
|
+
const members = [];
|
|
271
|
+
const leader = s.byUid[leaderId];
|
|
272
|
+
if (leader && leader.object.type !== PdfAnnotationSubtype.LINK) {
|
|
273
|
+
members.push(leader);
|
|
274
|
+
}
|
|
275
|
+
for (const uidList of Object.values(s.pages)) {
|
|
276
|
+
for (const uid of uidList) {
|
|
277
|
+
const ta = s.byUid[uid];
|
|
278
|
+
if (ta && ta.object.inReplyToId === leaderId && ta.object.replyType === PdfAnnotationReplyType.Group && ta.object.type !== PdfAnnotationSubtype.LINK) {
|
|
279
|
+
members.push(ta);
|
|
280
|
+
}
|
|
281
|
+
}
|
|
282
|
+
}
|
|
283
|
+
return members;
|
|
284
|
+
};
|
|
285
|
+
const isInGroup = (s, annotationId) => {
|
|
286
|
+
const ta = s.byUid[annotationId];
|
|
287
|
+
if (!ta) return false;
|
|
288
|
+
if (ta.object.type === PdfAnnotationSubtype.LINK) {
|
|
289
|
+
return false;
|
|
290
|
+
}
|
|
291
|
+
if (ta.object.inReplyToId && ta.object.replyType === PdfAnnotationReplyType.Group) {
|
|
292
|
+
return true;
|
|
293
|
+
}
|
|
294
|
+
for (const uidList of Object.values(s.pages)) {
|
|
295
|
+
for (const uid of uidList) {
|
|
296
|
+
const other = s.byUid[uid];
|
|
297
|
+
if (other && other.object.inReplyToId === annotationId && other.object.replyType === PdfAnnotationReplyType.Group && other.object.type !== PdfAnnotationSubtype.LINK) {
|
|
298
|
+
return true;
|
|
299
|
+
}
|
|
300
|
+
}
|
|
301
|
+
}
|
|
302
|
+
return false;
|
|
303
|
+
};
|
|
304
|
+
const getSelectionGroupingAction = (s) => {
|
|
305
|
+
const selected = getSelectedAnnotations(s);
|
|
306
|
+
if (selected.length === 0) return "disabled";
|
|
307
|
+
const firstId = selected[0].object.id;
|
|
308
|
+
if (isInGroup(s, firstId)) {
|
|
309
|
+
const members = getGroupMembers(s, firstId);
|
|
310
|
+
const memberIds = new Set(members.map((m) => m.object.id));
|
|
311
|
+
if (selected.every((ta) => memberIds.has(ta.object.id))) {
|
|
312
|
+
return "ungroup";
|
|
313
|
+
}
|
|
314
|
+
}
|
|
315
|
+
return selected.length >= 2 ? "group" : "disabled";
|
|
316
|
+
};
|
|
193
317
|
const defaultTools = [
|
|
194
318
|
// Text Markup Tools
|
|
195
319
|
{
|
|
@@ -200,11 +324,16 @@ const defaultTools = [
|
|
|
200
324
|
exclusive: false,
|
|
201
325
|
textSelection: true,
|
|
202
326
|
isDraggable: false,
|
|
203
|
-
isResizable: false
|
|
327
|
+
isResizable: false,
|
|
328
|
+
// Text markup annotations are anchored to text and should not move/resize in groups
|
|
329
|
+
isGroupDraggable: false,
|
|
330
|
+
isGroupResizable: false
|
|
204
331
|
},
|
|
205
332
|
defaults: {
|
|
206
333
|
type: PdfAnnotationSubtype.HIGHLIGHT,
|
|
334
|
+
strokeColor: "#FFCD45",
|
|
207
335
|
color: "#FFCD45",
|
|
336
|
+
// deprecated alias
|
|
208
337
|
opacity: 1,
|
|
209
338
|
blendMode: PdfBlendMode.Multiply
|
|
210
339
|
}
|
|
@@ -217,11 +346,15 @@ const defaultTools = [
|
|
|
217
346
|
exclusive: false,
|
|
218
347
|
textSelection: true,
|
|
219
348
|
isDraggable: false,
|
|
220
|
-
isResizable: false
|
|
349
|
+
isResizable: false,
|
|
350
|
+
isGroupDraggable: false,
|
|
351
|
+
isGroupResizable: false
|
|
221
352
|
},
|
|
222
353
|
defaults: {
|
|
223
354
|
type: PdfAnnotationSubtype.UNDERLINE,
|
|
355
|
+
strokeColor: "#E44234",
|
|
224
356
|
color: "#E44234",
|
|
357
|
+
// deprecated alias
|
|
225
358
|
opacity: 1
|
|
226
359
|
}
|
|
227
360
|
},
|
|
@@ -231,11 +364,17 @@ const defaultTools = [
|
|
|
231
364
|
matchScore: (a) => a.type === PdfAnnotationSubtype.STRIKEOUT ? 1 : 0,
|
|
232
365
|
interaction: {
|
|
233
366
|
exclusive: false,
|
|
234
|
-
textSelection: true
|
|
367
|
+
textSelection: true,
|
|
368
|
+
isDraggable: false,
|
|
369
|
+
isResizable: false,
|
|
370
|
+
isGroupDraggable: false,
|
|
371
|
+
isGroupResizable: false
|
|
235
372
|
},
|
|
236
373
|
defaults: {
|
|
237
374
|
type: PdfAnnotationSubtype.STRIKEOUT,
|
|
375
|
+
strokeColor: "#E44234",
|
|
238
376
|
color: "#E44234",
|
|
377
|
+
// deprecated alias
|
|
239
378
|
opacity: 1
|
|
240
379
|
}
|
|
241
380
|
},
|
|
@@ -247,11 +386,15 @@ const defaultTools = [
|
|
|
247
386
|
exclusive: false,
|
|
248
387
|
textSelection: true,
|
|
249
388
|
isDraggable: false,
|
|
250
|
-
isResizable: false
|
|
389
|
+
isResizable: false,
|
|
390
|
+
isGroupDraggable: false,
|
|
391
|
+
isGroupResizable: false
|
|
251
392
|
},
|
|
252
393
|
defaults: {
|
|
253
394
|
type: PdfAnnotationSubtype.SQUIGGLY,
|
|
395
|
+
strokeColor: "#E44234",
|
|
254
396
|
color: "#E44234",
|
|
397
|
+
// deprecated alias
|
|
255
398
|
opacity: 1
|
|
256
399
|
}
|
|
257
400
|
},
|
|
@@ -269,7 +412,9 @@ const defaultTools = [
|
|
|
269
412
|
},
|
|
270
413
|
defaults: {
|
|
271
414
|
type: PdfAnnotationSubtype.INK,
|
|
415
|
+
strokeColor: "#E44234",
|
|
272
416
|
color: "#E44234",
|
|
417
|
+
// deprecated alias
|
|
273
418
|
opacity: 1,
|
|
274
419
|
strokeWidth: 6
|
|
275
420
|
}
|
|
@@ -288,7 +433,9 @@ const defaultTools = [
|
|
|
288
433
|
defaults: {
|
|
289
434
|
type: PdfAnnotationSubtype.INK,
|
|
290
435
|
intent: "InkHighlight",
|
|
436
|
+
strokeColor: "#FFCD45",
|
|
291
437
|
color: "#FFCD45",
|
|
438
|
+
// deprecated alias
|
|
292
439
|
opacity: 1,
|
|
293
440
|
strokeWidth: 14,
|
|
294
441
|
blendMode: PdfBlendMode.Multiply
|
|
@@ -352,7 +499,10 @@ const defaultTools = [
|
|
|
352
499
|
cursor: "crosshair",
|
|
353
500
|
isDraggable: true,
|
|
354
501
|
isResizable: false,
|
|
355
|
-
|
|
502
|
+
// Uses vertex editing when selected individually
|
|
503
|
+
lockAspectRatio: false,
|
|
504
|
+
isGroupResizable: true
|
|
505
|
+
// Scales proportionally in a group
|
|
356
506
|
},
|
|
357
507
|
defaults: {
|
|
358
508
|
type: PdfAnnotationSubtype.LINE,
|
|
@@ -376,7 +526,10 @@ const defaultTools = [
|
|
|
376
526
|
cursor: "crosshair",
|
|
377
527
|
isDraggable: true,
|
|
378
528
|
isResizable: false,
|
|
379
|
-
|
|
529
|
+
// Uses vertex editing when selected individually
|
|
530
|
+
lockAspectRatio: false,
|
|
531
|
+
isGroupResizable: true
|
|
532
|
+
// Scales proportionally in a group
|
|
380
533
|
},
|
|
381
534
|
defaults: {
|
|
382
535
|
type: PdfAnnotationSubtype.LINE,
|
|
@@ -405,7 +558,10 @@ const defaultTools = [
|
|
|
405
558
|
cursor: "crosshair",
|
|
406
559
|
isDraggable: true,
|
|
407
560
|
isResizable: false,
|
|
408
|
-
|
|
561
|
+
// Uses vertex editing when selected individually
|
|
562
|
+
lockAspectRatio: false,
|
|
563
|
+
isGroupResizable: true
|
|
564
|
+
// Scales proportionally in a group
|
|
409
565
|
},
|
|
410
566
|
defaults: {
|
|
411
567
|
type: PdfAnnotationSubtype.POLYLINE,
|
|
@@ -424,7 +580,10 @@ const defaultTools = [
|
|
|
424
580
|
cursor: "crosshair",
|
|
425
581
|
isDraggable: true,
|
|
426
582
|
isResizable: false,
|
|
427
|
-
|
|
583
|
+
// Uses vertex editing when selected individually
|
|
584
|
+
lockAspectRatio: false,
|
|
585
|
+
isGroupResizable: true
|
|
586
|
+
// Scales proportionally in a group
|
|
428
587
|
},
|
|
429
588
|
defaults: {
|
|
430
589
|
type: PdfAnnotationSubtype.POLYGON,
|
|
@@ -454,7 +613,10 @@ const defaultTools = [
|
|
|
454
613
|
fontFamily: PdfStandardFont.Helvetica,
|
|
455
614
|
textAlign: PdfTextAlignment.Left,
|
|
456
615
|
verticalAlign: PdfVerticalAlignment.Top,
|
|
616
|
+
color: "transparent",
|
|
617
|
+
// fill color (matches shape convention)
|
|
457
618
|
backgroundColor: "transparent",
|
|
619
|
+
// deprecated alias
|
|
458
620
|
opacity: 1
|
|
459
621
|
},
|
|
460
622
|
clickBehavior: {
|
|
@@ -492,9 +654,11 @@ const DEFAULT_COLORS = [
|
|
|
492
654
|
"#000000",
|
|
493
655
|
"#FFFFFF"
|
|
494
656
|
];
|
|
657
|
+
const computeSelectedUid = (selectedUids) => selectedUids.length === 1 ? selectedUids[0] : null;
|
|
495
658
|
const initialDocumentState = () => ({
|
|
496
659
|
pages: {},
|
|
497
660
|
byUid: {},
|
|
661
|
+
selectedUids: [],
|
|
498
662
|
selectedUid: null,
|
|
499
663
|
activeToolId: null,
|
|
500
664
|
hasPendingChanges: false
|
|
@@ -595,7 +759,7 @@ const reducer = (state, action) => {
|
|
|
595
759
|
...state,
|
|
596
760
|
documents: {
|
|
597
761
|
...state.documents,
|
|
598
|
-
[documentId]: { ...docState, selectedUid: id }
|
|
762
|
+
[documentId]: { ...docState, selectedUids: [id], selectedUid: id }
|
|
599
763
|
}
|
|
600
764
|
};
|
|
601
765
|
}
|
|
@@ -607,7 +771,58 @@ const reducer = (state, action) => {
|
|
|
607
771
|
...state,
|
|
608
772
|
documents: {
|
|
609
773
|
...state.documents,
|
|
610
|
-
[documentId]: { ...docState, selectedUid: null }
|
|
774
|
+
[documentId]: { ...docState, selectedUids: [], selectedUid: null }
|
|
775
|
+
}
|
|
776
|
+
};
|
|
777
|
+
}
|
|
778
|
+
case ADD_TO_SELECTION: {
|
|
779
|
+
const { documentId, id } = action.payload;
|
|
780
|
+
const docState = state.documents[documentId];
|
|
781
|
+
if (!docState) return state;
|
|
782
|
+
if (docState.selectedUids.includes(id)) return state;
|
|
783
|
+
const newSelectedUids = [...docState.selectedUids, id];
|
|
784
|
+
return {
|
|
785
|
+
...state,
|
|
786
|
+
documents: {
|
|
787
|
+
...state.documents,
|
|
788
|
+
[documentId]: {
|
|
789
|
+
...docState,
|
|
790
|
+
selectedUids: newSelectedUids,
|
|
791
|
+
selectedUid: computeSelectedUid(newSelectedUids)
|
|
792
|
+
}
|
|
793
|
+
}
|
|
794
|
+
};
|
|
795
|
+
}
|
|
796
|
+
case REMOVE_FROM_SELECTION: {
|
|
797
|
+
const { documentId, id } = action.payload;
|
|
798
|
+
const docState = state.documents[documentId];
|
|
799
|
+
if (!docState) return state;
|
|
800
|
+
const newSelectedUids = docState.selectedUids.filter((uid) => uid !== id);
|
|
801
|
+
return {
|
|
802
|
+
...state,
|
|
803
|
+
documents: {
|
|
804
|
+
...state.documents,
|
|
805
|
+
[documentId]: {
|
|
806
|
+
...docState,
|
|
807
|
+
selectedUids: newSelectedUids,
|
|
808
|
+
selectedUid: computeSelectedUid(newSelectedUids)
|
|
809
|
+
}
|
|
810
|
+
}
|
|
811
|
+
};
|
|
812
|
+
}
|
|
813
|
+
case SET_SELECTION: {
|
|
814
|
+
const { documentId, ids } = action.payload;
|
|
815
|
+
const docState = state.documents[documentId];
|
|
816
|
+
if (!docState) return state;
|
|
817
|
+
return {
|
|
818
|
+
...state,
|
|
819
|
+
documents: {
|
|
820
|
+
...state.documents,
|
|
821
|
+
[documentId]: {
|
|
822
|
+
...docState,
|
|
823
|
+
selectedUids: ids,
|
|
824
|
+
selectedUid: computeSelectedUid(ids)
|
|
825
|
+
}
|
|
611
826
|
}
|
|
612
827
|
};
|
|
613
828
|
}
|
|
@@ -683,21 +898,30 @@ const reducer = (state, action) => {
|
|
|
683
898
|
};
|
|
684
899
|
}
|
|
685
900
|
case COMMIT_PENDING_CHANGES: {
|
|
686
|
-
const { documentId } = action.payload;
|
|
901
|
+
const { documentId, committedUids } = action.payload;
|
|
687
902
|
const docState = state.documents[documentId];
|
|
688
903
|
if (!docState) return state;
|
|
904
|
+
const committedSet = new Set(committedUids);
|
|
689
905
|
const cleaned = {};
|
|
906
|
+
let stillHasPending = false;
|
|
690
907
|
for (const [uid, ta] of Object.entries(docState.byUid)) {
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
|
|
908
|
+
if (committedSet.has(uid)) {
|
|
909
|
+
cleaned[uid] = {
|
|
910
|
+
...ta,
|
|
911
|
+
commitState: ta.commitState === "dirty" || ta.commitState === "new" ? "synced" : ta.commitState
|
|
912
|
+
};
|
|
913
|
+
} else {
|
|
914
|
+
cleaned[uid] = ta;
|
|
915
|
+
if (ta.commitState === "new" || ta.commitState === "dirty" || ta.commitState === "deleted") {
|
|
916
|
+
stillHasPending = true;
|
|
917
|
+
}
|
|
918
|
+
}
|
|
695
919
|
}
|
|
696
920
|
return {
|
|
697
921
|
...state,
|
|
698
922
|
documents: {
|
|
699
923
|
...state.documents,
|
|
700
|
-
[documentId]: { ...docState, byUid: cleaned, hasPendingChanges:
|
|
924
|
+
[documentId]: { ...docState, byUid: cleaned, hasPendingChanges: stillHasPending }
|
|
701
925
|
}
|
|
702
926
|
};
|
|
703
927
|
}
|
|
@@ -763,7 +987,7 @@ const inkHandlerFactory = {
|
|
|
763
987
|
return {
|
|
764
988
|
...tool.defaults,
|
|
765
989
|
strokeWidth: tool.defaults.strokeWidth ?? 1,
|
|
766
|
-
|
|
990
|
+
strokeColor: tool.defaults.strokeColor ?? tool.defaults.color ?? "#000000",
|
|
767
991
|
opacity: tool.defaults.opacity ?? 1,
|
|
768
992
|
flags: tool.defaults.flags ?? ["print"]
|
|
769
993
|
};
|
|
@@ -899,7 +1123,7 @@ const freeTextHandlerFactory = {
|
|
|
899
1123
|
opacity: tool.defaults.opacity ?? 1,
|
|
900
1124
|
fontSize: tool.defaults.fontSize ?? 12,
|
|
901
1125
|
fontFamily: tool.defaults.fontFamily ?? PdfStandardFont.Helvetica,
|
|
902
|
-
|
|
1126
|
+
color: tool.defaults.color ?? tool.defaults.backgroundColor ?? "transparent",
|
|
903
1127
|
textAlign: tool.defaults.textAlign ?? PdfTextAlignment.Left,
|
|
904
1128
|
verticalAlign: tool.defaults.verticalAlign ?? PdfVerticalAlignment.Top,
|
|
905
1129
|
contents: tool.defaults.contents ?? "Insert text here",
|
|
@@ -1435,7 +1659,10 @@ const polylineHandlerFactory = {
|
|
|
1435
1659
|
};
|
|
1436
1660
|
};
|
|
1437
1661
|
return {
|
|
1438
|
-
onClick: (pos) => {
|
|
1662
|
+
onClick: (pos, evt) => {
|
|
1663
|
+
if (evt.metaKey || evt.ctrlKey) {
|
|
1664
|
+
return;
|
|
1665
|
+
}
|
|
1439
1666
|
const clampedPos = clampToPage(pos);
|
|
1440
1667
|
const vertices = getVertices();
|
|
1441
1668
|
const lastVertex = vertices[vertices.length - 1];
|
|
@@ -1537,7 +1764,10 @@ const polygonHandlerFactory = {
|
|
|
1537
1764
|
};
|
|
1538
1765
|
};
|
|
1539
1766
|
return {
|
|
1540
|
-
onClick: (pos) => {
|
|
1767
|
+
onClick: (pos, evt) => {
|
|
1768
|
+
if (evt.metaKey || evt.ctrlKey) {
|
|
1769
|
+
return;
|
|
1770
|
+
}
|
|
1541
1771
|
const clampedPos = clampToPage(pos);
|
|
1542
1772
|
if (isInsideStartHandle(clampedPos) && getVertices().length >= 3) {
|
|
1543
1773
|
commitPolygon();
|
|
@@ -2234,11 +2464,16 @@ const _AnnotationPlugin = class _AnnotationPlugin extends BasePlugin {
|
|
|
2234
2464
|
this.pendingContexts = /* @__PURE__ */ new Map();
|
|
2235
2465
|
this.isInitialLoadComplete = /* @__PURE__ */ new Map();
|
|
2236
2466
|
this.importQueue = /* @__PURE__ */ new Map();
|
|
2467
|
+
this.commitInProgress = /* @__PURE__ */ new Map();
|
|
2237
2468
|
this.handlerFactories = /* @__PURE__ */ new Map();
|
|
2238
2469
|
this.activeTool$ = createBehaviorEmitter();
|
|
2239
2470
|
this.events$ = createBehaviorEmitter();
|
|
2240
2471
|
this.toolsChange$ = createBehaviorEmitter();
|
|
2241
2472
|
this.patchRegistry = new PatchRegistry();
|
|
2473
|
+
this.unifiedDragStates = /* @__PURE__ */ new Map();
|
|
2474
|
+
this.unifiedDrag$ = createBehaviorEmitter();
|
|
2475
|
+
this.unifiedResizeStates = /* @__PURE__ */ new Map();
|
|
2476
|
+
this.unifiedResize$ = createBehaviorEmitter();
|
|
2242
2477
|
this.config = config;
|
|
2243
2478
|
this.selection = ((_a = registry.getPlugin("selection")) == null ? void 0 : _a.provides()) ?? null;
|
|
2244
2479
|
this.history = ((_b = registry.getPlugin("history")) == null ? void 0 : _b.provides()) ?? null;
|
|
@@ -2268,7 +2503,7 @@ const _AnnotationPlugin = class _AnnotationPlugin extends BasePlugin {
|
|
|
2268
2503
|
if (this.selection) {
|
|
2269
2504
|
for (const tool of this.state.tools) {
|
|
2270
2505
|
if (tool.interaction.textSelection) {
|
|
2271
|
-
this.selection.enableForMode(tool.interaction.mode ?? tool.id);
|
|
2506
|
+
this.selection.enableForMode(tool.interaction.mode ?? tool.id, { showRects: false });
|
|
2272
2507
|
}
|
|
2273
2508
|
}
|
|
2274
2509
|
}
|
|
@@ -2301,7 +2536,7 @@ const _AnnotationPlugin = class _AnnotationPlugin extends BasePlugin {
|
|
|
2301
2536
|
this.patchRegistry.register(PdfAnnotationSubtype.POLYGON, patchPolygon);
|
|
2302
2537
|
}
|
|
2303
2538
|
async initialize() {
|
|
2304
|
-
var _a, _b;
|
|
2539
|
+
var _a, _b, _c;
|
|
2305
2540
|
this.state.tools.forEach((tool) => this.registerInteractionForTool(tool));
|
|
2306
2541
|
if (this.history) {
|
|
2307
2542
|
this.history.onHistoryChange((event) => {
|
|
@@ -2318,8 +2553,28 @@ const _AnnotationPlugin = class _AnnotationPlugin extends BasePlugin {
|
|
|
2318
2553
|
this.dispatch(setActiveToolId(s.documentId, newToolId));
|
|
2319
2554
|
}
|
|
2320
2555
|
});
|
|
2321
|
-
(_b = this.selection) == null ? void 0 : _b.
|
|
2322
|
-
|
|
2556
|
+
(_b = this.selection) == null ? void 0 : _b.onMarqueeEnd(({ documentId, pageIndex, rect }) => {
|
|
2557
|
+
const docState = this.state.documents[documentId];
|
|
2558
|
+
if (!docState) return;
|
|
2559
|
+
const pageAnnotations = (docState.pages[pageIndex] ?? []).map((uid) => docState.byUid[uid]).filter((ta) => ta !== void 0).filter((ta) => !isLink(ta));
|
|
2560
|
+
const selectedIds = pageAnnotations.filter((ta) => rectsIntersect(rect, ta.object.rect)).map((ta) => ta.object.id);
|
|
2561
|
+
if (selectedIds.length > 0) {
|
|
2562
|
+
const expandedIds = /* @__PURE__ */ new Set();
|
|
2563
|
+
for (const id of selectedIds) {
|
|
2564
|
+
if (this.isInGroupMethod(id, documentId)) {
|
|
2565
|
+
const members = this.getGroupMembersMethod(id, documentId);
|
|
2566
|
+
for (const member of members) {
|
|
2567
|
+
expandedIds.add(member.object.id);
|
|
2568
|
+
}
|
|
2569
|
+
} else {
|
|
2570
|
+
expandedIds.add(id);
|
|
2571
|
+
}
|
|
2572
|
+
}
|
|
2573
|
+
this.setSelectionMethod([...expandedIds], documentId);
|
|
2574
|
+
}
|
|
2575
|
+
});
|
|
2576
|
+
(_c = this.selection) == null ? void 0 : _c.onEndSelection(({ documentId }) => {
|
|
2577
|
+
var _a2, _b2, _c2;
|
|
2323
2578
|
if (!this.checkPermission(documentId, PdfPermissionFlag.ModifyAnnotations)) {
|
|
2324
2579
|
return;
|
|
2325
2580
|
}
|
|
@@ -2355,7 +2610,7 @@ const _AnnotationPlugin = class _AnnotationPlugin extends BasePlugin {
|
|
|
2355
2610
|
}
|
|
2356
2611
|
}, ignore);
|
|
2357
2612
|
}
|
|
2358
|
-
(
|
|
2613
|
+
(_c2 = this.selection) == null ? void 0 : _c2.clear();
|
|
2359
2614
|
});
|
|
2360
2615
|
}
|
|
2361
2616
|
registerInteractionForTool(tool) {
|
|
@@ -2375,15 +2630,32 @@ const _AnnotationPlugin = class _AnnotationPlugin extends BasePlugin {
|
|
|
2375
2630
|
getState: () => this.getDocumentState(),
|
|
2376
2631
|
getPageAnnotations: (options) => this.getPageAnnotations(options),
|
|
2377
2632
|
getSelectedAnnotation: () => this.getSelectedAnnotation(),
|
|
2633
|
+
getSelectedAnnotations: () => this.getSelectedAnnotationsMethod(),
|
|
2634
|
+
getSelectedAnnotationIds: () => this.getSelectedAnnotationIdsMethod(),
|
|
2378
2635
|
getAnnotationById: (id) => this.getAnnotationById(id),
|
|
2379
2636
|
selectAnnotation: (pageIndex, id) => this.selectAnnotation(pageIndex, id),
|
|
2637
|
+
toggleSelection: (pageIndex, id) => this.toggleSelectionMethod(pageIndex, id),
|
|
2638
|
+
addToSelection: (pageIndex, id) => this.addToSelectionMethod(pageIndex, id),
|
|
2639
|
+
removeFromSelection: (id) => this.removeFromSelectionMethod(id),
|
|
2640
|
+
setSelection: (ids) => this.setSelectionMethod(ids),
|
|
2380
2641
|
deselectAnnotation: () => this.deselectAnnotation(),
|
|
2381
2642
|
importAnnotations: (items) => this.importAnnotations(items),
|
|
2382
2643
|
createAnnotation: (pageIndex, anno, ctx) => this.createAnnotation(pageIndex, anno, ctx),
|
|
2383
2644
|
updateAnnotation: (pageIndex, id, patch) => this.updateAnnotation(pageIndex, id, patch),
|
|
2645
|
+
updateAnnotations: (patches) => this.updateAnnotationsMethod(patches),
|
|
2384
2646
|
deleteAnnotation: (pageIndex, id) => this.deleteAnnotation(pageIndex, id),
|
|
2647
|
+
deleteAnnotations: (annotations, documentId) => this.deleteAnnotationsMethod(annotations, documentId),
|
|
2385
2648
|
renderAnnotation: (options) => this.renderAnnotation(options),
|
|
2386
2649
|
commit: () => this.commit(),
|
|
2650
|
+
// Attached links (IRT link children)
|
|
2651
|
+
getAttachedLinks: (id, documentId) => this.getAttachedLinksMethod(id, documentId),
|
|
2652
|
+
hasAttachedLinks: (id, documentId) => this.hasAttachedLinksMethod(id, documentId),
|
|
2653
|
+
deleteAttachedLinks: (id, documentId) => this.deleteAttachedLinksMethod(id, documentId),
|
|
2654
|
+
// Annotation grouping (RT = Group)
|
|
2655
|
+
groupAnnotations: (documentId) => this.groupAnnotationsMethod(documentId),
|
|
2656
|
+
ungroupAnnotations: (id, documentId) => this.ungroupAnnotationsMethod(id, documentId),
|
|
2657
|
+
getGroupMembers: (id, documentId) => this.getGroupMembersMethod(id, documentId),
|
|
2658
|
+
isInGroup: (id, documentId) => this.isInGroupMethod(id, documentId),
|
|
2387
2659
|
// Document-scoped operations
|
|
2388
2660
|
forDocument: (documentId) => this.createAnnotationScope(documentId),
|
|
2389
2661
|
// Global operations
|
|
@@ -2414,8 +2686,14 @@ const _AnnotationPlugin = class _AnnotationPlugin extends BasePlugin {
|
|
|
2414
2686
|
getState: () => this.getDocumentState(documentId),
|
|
2415
2687
|
getPageAnnotations: (options) => this.getPageAnnotations(options, documentId),
|
|
2416
2688
|
getSelectedAnnotation: () => this.getSelectedAnnotation(documentId),
|
|
2689
|
+
getSelectedAnnotations: () => this.getSelectedAnnotationsMethod(documentId),
|
|
2690
|
+
getSelectedAnnotationIds: () => this.getSelectedAnnotationIdsMethod(documentId),
|
|
2417
2691
|
getAnnotationById: (id) => this.getAnnotationById(id, documentId),
|
|
2418
2692
|
selectAnnotation: (pageIndex, id) => this.selectAnnotation(pageIndex, id, documentId),
|
|
2693
|
+
toggleSelection: (pageIndex, id) => this.toggleSelectionMethod(pageIndex, id, documentId),
|
|
2694
|
+
addToSelection: (pageIndex, id) => this.addToSelectionMethod(pageIndex, id, documentId),
|
|
2695
|
+
removeFromSelection: (id) => this.removeFromSelectionMethod(id, documentId),
|
|
2696
|
+
setSelection: (ids) => this.setSelectionMethod(ids, documentId),
|
|
2419
2697
|
deselectAnnotation: () => this.deselectAnnotation(documentId),
|
|
2420
2698
|
getActiveTool: () => this.getActiveTool(documentId),
|
|
2421
2699
|
setActiveTool: (toolId) => this.setActiveTool(toolId, documentId),
|
|
@@ -2423,9 +2701,19 @@ const _AnnotationPlugin = class _AnnotationPlugin extends BasePlugin {
|
|
|
2423
2701
|
importAnnotations: (items) => this.importAnnotations(items, documentId),
|
|
2424
2702
|
createAnnotation: (pageIndex, anno, ctx) => this.createAnnotation(pageIndex, anno, ctx, documentId),
|
|
2425
2703
|
updateAnnotation: (pageIndex, id, patch) => this.updateAnnotation(pageIndex, id, patch, documentId),
|
|
2704
|
+
updateAnnotations: (patches) => this.updateAnnotationsMethod(patches, documentId),
|
|
2426
2705
|
deleteAnnotation: (pageIndex, id) => this.deleteAnnotation(pageIndex, id, documentId),
|
|
2706
|
+
deleteAnnotations: (annotations) => this.deleteAnnotationsMethod(annotations, documentId),
|
|
2427
2707
|
renderAnnotation: (options) => this.renderAnnotation(options, documentId),
|
|
2428
2708
|
commit: () => this.commit(documentId),
|
|
2709
|
+
getAttachedLinks: (id) => this.getAttachedLinksMethod(id, documentId),
|
|
2710
|
+
hasAttachedLinks: (id) => this.hasAttachedLinksMethod(id, documentId),
|
|
2711
|
+
deleteAttachedLinks: (id) => this.deleteAttachedLinksMethod(id, documentId),
|
|
2712
|
+
groupAnnotations: () => this.groupAnnotationsMethod(documentId),
|
|
2713
|
+
ungroupAnnotations: (id) => this.ungroupAnnotationsMethod(id, documentId),
|
|
2714
|
+
getGroupMembers: (id) => this.getGroupMembersMethod(id, documentId),
|
|
2715
|
+
isInGroup: (id) => this.isInGroupMethod(id, documentId),
|
|
2716
|
+
getGroupingAction: () => this.getGroupingActionMethod(documentId),
|
|
2429
2717
|
onStateChange: (listener) => this.state$.on((event) => {
|
|
2430
2718
|
if (event.documentId === documentId) listener(event.state);
|
|
2431
2719
|
}),
|
|
@@ -2738,7 +3026,26 @@ const _AnnotationPlugin = class _AnnotationPlugin extends BasePlugin {
|
|
|
2738
3026
|
const docState = this.getDocumentState(docId);
|
|
2739
3027
|
const originalAnnotation = (_a = docState.byUid[id]) == null ? void 0 : _a.object;
|
|
2740
3028
|
if (!originalAnnotation) return;
|
|
3029
|
+
const irtChildren = getIRTChildIds(docState, id);
|
|
3030
|
+
const childAnnotations = irtChildren.map((child) => {
|
|
3031
|
+
var _a2;
|
|
3032
|
+
return (_a2 = docState.byUid[child.id]) == null ? void 0 : _a2.object;
|
|
3033
|
+
}).filter((obj) => obj !== void 0);
|
|
2741
3034
|
const execute = () => {
|
|
3035
|
+
var _a2;
|
|
3036
|
+
for (const child of irtChildren) {
|
|
3037
|
+
const childObj = (_a2 = docState.byUid[child.id]) == null ? void 0 : _a2.object;
|
|
3038
|
+
if (childObj) {
|
|
3039
|
+
this.dispatch(deleteAnnotation(docId, child.pageIndex, child.id));
|
|
3040
|
+
this.events$.emit({
|
|
3041
|
+
type: "delete",
|
|
3042
|
+
documentId: docId,
|
|
3043
|
+
annotation: childObj,
|
|
3044
|
+
pageIndex: child.pageIndex,
|
|
3045
|
+
committed: false
|
|
3046
|
+
});
|
|
3047
|
+
}
|
|
3048
|
+
}
|
|
2742
3049
|
this.dispatch(deselectAnnotation(docId));
|
|
2743
3050
|
this.dispatch(deleteAnnotation(docId, pageIndex, id));
|
|
2744
3051
|
this.events$.emit({
|
|
@@ -2765,19 +3072,703 @@ const _AnnotationPlugin = class _AnnotationPlugin extends BasePlugin {
|
|
|
2765
3072
|
pageIndex,
|
|
2766
3073
|
committed: false
|
|
2767
3074
|
});
|
|
3075
|
+
for (const childObj of childAnnotations) {
|
|
3076
|
+
this.dispatch(createAnnotation(docId, childObj.pageIndex, childObj));
|
|
3077
|
+
this.events$.emit({
|
|
3078
|
+
type: "create",
|
|
3079
|
+
documentId: docId,
|
|
3080
|
+
annotation: childObj,
|
|
3081
|
+
pageIndex: childObj.pageIndex,
|
|
3082
|
+
committed: false
|
|
3083
|
+
});
|
|
3084
|
+
}
|
|
2768
3085
|
}
|
|
2769
3086
|
};
|
|
2770
3087
|
const historyScope = this.history.forDocument(docId);
|
|
2771
3088
|
historyScope.register(command, this.ANNOTATION_HISTORY_TOPIC);
|
|
2772
3089
|
}
|
|
3090
|
+
deleteAnnotationsMethod(annotations, documentId) {
|
|
3091
|
+
for (const { pageIndex, id } of annotations) {
|
|
3092
|
+
this.deleteAnnotation(pageIndex, id, documentId);
|
|
3093
|
+
}
|
|
3094
|
+
}
|
|
2773
3095
|
selectAnnotation(pageIndex, id, documentId) {
|
|
2774
3096
|
const docId = documentId ?? this.getActiveDocumentId();
|
|
2775
|
-
this.
|
|
3097
|
+
if (this.isInGroupMethod(id, docId)) {
|
|
3098
|
+
const members = this.getGroupMembersMethod(id, docId);
|
|
3099
|
+
const memberIds = members.map((m) => m.object.id);
|
|
3100
|
+
this.dispatch(setSelection(docId, memberIds));
|
|
3101
|
+
} else {
|
|
3102
|
+
this.dispatch(selectAnnotation(docId, pageIndex, id));
|
|
3103
|
+
}
|
|
2776
3104
|
}
|
|
2777
3105
|
deselectAnnotation(documentId) {
|
|
2778
3106
|
const docId = documentId ?? this.getActiveDocumentId();
|
|
2779
3107
|
this.dispatch(deselectAnnotation(docId));
|
|
2780
3108
|
}
|
|
3109
|
+
// ─────────────────────────────────────────────────────────
|
|
3110
|
+
// Multi-Select Methods
|
|
3111
|
+
// ─────────────────────────────────────────────────────────
|
|
3112
|
+
getSelectedAnnotationsMethod(documentId) {
|
|
3113
|
+
return getSelectedAnnotations(this.getDocumentState(documentId));
|
|
3114
|
+
}
|
|
3115
|
+
getSelectedAnnotationIdsMethod(documentId) {
|
|
3116
|
+
return getSelectedAnnotationIds(this.getDocumentState(documentId));
|
|
3117
|
+
}
|
|
3118
|
+
toggleSelectionMethod(pageIndex, id, documentId) {
|
|
3119
|
+
const docId = documentId ?? this.getActiveDocumentId();
|
|
3120
|
+
const docState = this.getDocumentState(docId);
|
|
3121
|
+
if (docState.selectedUids.includes(id)) {
|
|
3122
|
+
this.dispatch(removeFromSelection(docId, id));
|
|
3123
|
+
} else {
|
|
3124
|
+
if (this.isInGroupMethod(id, docId)) {
|
|
3125
|
+
const members = this.getGroupMembersMethod(id, docId);
|
|
3126
|
+
for (const member of members) {
|
|
3127
|
+
if (!docState.selectedUids.includes(member.object.id)) {
|
|
3128
|
+
this.dispatch(addToSelection(docId, member.object.pageIndex, member.object.id));
|
|
3129
|
+
}
|
|
3130
|
+
}
|
|
3131
|
+
} else {
|
|
3132
|
+
this.dispatch(addToSelection(docId, pageIndex, id));
|
|
3133
|
+
}
|
|
3134
|
+
}
|
|
3135
|
+
}
|
|
3136
|
+
addToSelectionMethod(pageIndex, id, documentId) {
|
|
3137
|
+
const docId = documentId ?? this.getActiveDocumentId();
|
|
3138
|
+
this.dispatch(addToSelection(docId, pageIndex, id));
|
|
3139
|
+
}
|
|
3140
|
+
removeFromSelectionMethod(id, documentId) {
|
|
3141
|
+
const docId = documentId ?? this.getActiveDocumentId();
|
|
3142
|
+
this.dispatch(removeFromSelection(docId, id));
|
|
3143
|
+
}
|
|
3144
|
+
setSelectionMethod(ids, documentId) {
|
|
3145
|
+
const docId = documentId ?? this.getActiveDocumentId();
|
|
3146
|
+
this.dispatch(setSelection(docId, ids));
|
|
3147
|
+
}
|
|
3148
|
+
// ─────────────────────────────────────────────────────────
|
|
3149
|
+
// Attached Links Methods
|
|
3150
|
+
// ─────────────────────────────────────────────────────────
|
|
3151
|
+
getAttachedLinksMethod(annotationId, documentId) {
|
|
3152
|
+
return getAttachedLinks(this.getDocumentState(documentId), annotationId);
|
|
3153
|
+
}
|
|
3154
|
+
hasAttachedLinksMethod(annotationId, documentId) {
|
|
3155
|
+
return this.getAttachedLinksMethod(annotationId, documentId).length > 0;
|
|
3156
|
+
}
|
|
3157
|
+
deleteAttachedLinksMethod(annotationId, documentId) {
|
|
3158
|
+
const links = this.getAttachedLinksMethod(annotationId, documentId);
|
|
3159
|
+
for (const link of links) {
|
|
3160
|
+
this.deleteAnnotation(link.object.pageIndex, link.object.id, documentId);
|
|
3161
|
+
}
|
|
3162
|
+
}
|
|
3163
|
+
// ─────────────────────────────────────────────────────────
|
|
3164
|
+
// Annotation Grouping Methods
|
|
3165
|
+
// ─────────────────────────────────────────────────────────
|
|
3166
|
+
/**
|
|
3167
|
+
* Group the currently selected annotations.
|
|
3168
|
+
* The first selected annotation becomes the group leader.
|
|
3169
|
+
* All other selected annotations get their IRT set to the leader's ID with RT = Group.
|
|
3170
|
+
*/
|
|
3171
|
+
groupAnnotationsMethod(documentId) {
|
|
3172
|
+
const docId = documentId ?? this.getActiveDocumentId();
|
|
3173
|
+
if (!this.checkPermission(docId, PdfPermissionFlag.ModifyAnnotations)) {
|
|
3174
|
+
this.logger.debug(
|
|
3175
|
+
"AnnotationPlugin",
|
|
3176
|
+
"GroupAnnotations",
|
|
3177
|
+
`Cannot group annotations: document ${docId} lacks ModifyAnnotations permission`
|
|
3178
|
+
);
|
|
3179
|
+
return;
|
|
3180
|
+
}
|
|
3181
|
+
const selected = this.getSelectedAnnotationsMethod(docId);
|
|
3182
|
+
if (selected.length < 2) {
|
|
3183
|
+
this.logger.debug(
|
|
3184
|
+
"AnnotationPlugin",
|
|
3185
|
+
"GroupAnnotations",
|
|
3186
|
+
"Need at least 2 annotations to group"
|
|
3187
|
+
);
|
|
3188
|
+
return;
|
|
3189
|
+
}
|
|
3190
|
+
const leader = selected[0];
|
|
3191
|
+
const members = selected.slice(1);
|
|
3192
|
+
const patches = members.map((ta) => ({
|
|
3193
|
+
pageIndex: ta.object.pageIndex,
|
|
3194
|
+
id: ta.object.id,
|
|
3195
|
+
patch: {
|
|
3196
|
+
inReplyToId: leader.object.id,
|
|
3197
|
+
replyType: PdfAnnotationReplyType.Group
|
|
3198
|
+
}
|
|
3199
|
+
}));
|
|
3200
|
+
this.updateAnnotationsMethod(patches, docId);
|
|
3201
|
+
}
|
|
3202
|
+
/**
|
|
3203
|
+
* Ungroup all annotations in the group containing the specified annotation.
|
|
3204
|
+
* Clears IRT and RT from all group members (the leader doesn't have them).
|
|
3205
|
+
*/
|
|
3206
|
+
ungroupAnnotationsMethod(annotationId, documentId) {
|
|
3207
|
+
const docId = documentId ?? this.getActiveDocumentId();
|
|
3208
|
+
if (!this.checkPermission(docId, PdfPermissionFlag.ModifyAnnotations)) {
|
|
3209
|
+
this.logger.debug(
|
|
3210
|
+
"AnnotationPlugin",
|
|
3211
|
+
"UngroupAnnotations",
|
|
3212
|
+
`Cannot ungroup annotations: document ${docId} lacks ModifyAnnotations permission`
|
|
3213
|
+
);
|
|
3214
|
+
return;
|
|
3215
|
+
}
|
|
3216
|
+
const members = this.getGroupMembersMethod(annotationId, docId);
|
|
3217
|
+
const patches = members.filter((ta) => ta.object.inReplyToId && ta.object.replyType === PdfAnnotationReplyType.Group).map((ta) => ({
|
|
3218
|
+
pageIndex: ta.object.pageIndex,
|
|
3219
|
+
id: ta.object.id,
|
|
3220
|
+
patch: {
|
|
3221
|
+
inReplyToId: void 0,
|
|
3222
|
+
replyType: void 0
|
|
3223
|
+
}
|
|
3224
|
+
}));
|
|
3225
|
+
if (patches.length > 0) {
|
|
3226
|
+
this.updateAnnotationsMethod(patches, docId);
|
|
3227
|
+
}
|
|
3228
|
+
}
|
|
3229
|
+
/**
|
|
3230
|
+
* Get all annotations in the same group as the specified annotation.
|
|
3231
|
+
*/
|
|
3232
|
+
getGroupMembersMethod(annotationId, documentId) {
|
|
3233
|
+
return getGroupMembers(this.getDocumentState(documentId), annotationId);
|
|
3234
|
+
}
|
|
3235
|
+
/**
|
|
3236
|
+
* Check if an annotation is part of a group.
|
|
3237
|
+
*/
|
|
3238
|
+
isInGroupMethod(annotationId, documentId) {
|
|
3239
|
+
return isInGroup(this.getDocumentState(documentId), annotationId);
|
|
3240
|
+
}
|
|
3241
|
+
/**
|
|
3242
|
+
* Get the available grouping action for the current selection.
|
|
3243
|
+
*/
|
|
3244
|
+
getGroupingActionMethod(documentId) {
|
|
3245
|
+
return getSelectionGroupingAction(this.getDocumentState(documentId));
|
|
3246
|
+
}
|
|
3247
|
+
// ─────────────────────────────────────────────────────────
|
|
3248
|
+
// Multi-Drag Coordination (Internal API for framework components)
|
|
3249
|
+
// ─────────────────────────────────────────────────────────
|
|
3250
|
+
/**
|
|
3251
|
+
* Compute combined constraints from all selected annotations.
|
|
3252
|
+
* This finds the "weakest link" in each direction - the annotation with the least
|
|
3253
|
+
* room to move determines the group's limit.
|
|
3254
|
+
*/
|
|
3255
|
+
computeCombinedConstraints(annotations) {
|
|
3256
|
+
let maxUp = Infinity;
|
|
3257
|
+
let maxDown = Infinity;
|
|
3258
|
+
let maxLeft = Infinity;
|
|
3259
|
+
let maxRight = Infinity;
|
|
3260
|
+
for (const anno of annotations) {
|
|
3261
|
+
const upLimit = anno.rect.origin.y;
|
|
3262
|
+
const downLimit = anno.pageSize.height - (anno.rect.origin.y + anno.rect.size.height);
|
|
3263
|
+
const leftLimit = anno.rect.origin.x;
|
|
3264
|
+
const rightLimit = anno.pageSize.width - (anno.rect.origin.x + anno.rect.size.width);
|
|
3265
|
+
maxUp = Math.min(maxUp, upLimit);
|
|
3266
|
+
maxDown = Math.min(maxDown, downLimit);
|
|
3267
|
+
maxLeft = Math.min(maxLeft, leftLimit);
|
|
3268
|
+
maxRight = Math.min(maxRight, rightLimit);
|
|
3269
|
+
}
|
|
3270
|
+
if (!isFinite(maxUp)) maxUp = 0;
|
|
3271
|
+
if (!isFinite(maxDown)) maxDown = 0;
|
|
3272
|
+
if (!isFinite(maxLeft)) maxLeft = 0;
|
|
3273
|
+
if (!isFinite(maxRight)) maxRight = 0;
|
|
3274
|
+
return { maxUp, maxDown, maxLeft, maxRight };
|
|
3275
|
+
}
|
|
3276
|
+
/**
|
|
3277
|
+
* Clamp a delta to the combined constraints.
|
|
3278
|
+
* Negative y = moving up, positive y = moving down
|
|
3279
|
+
* Negative x = moving left, positive x = moving right
|
|
3280
|
+
*/
|
|
3281
|
+
clampDelta(rawDelta, constraints) {
|
|
3282
|
+
return {
|
|
3283
|
+
x: Math.max(-constraints.maxLeft, Math.min(constraints.maxRight, rawDelta.x)),
|
|
3284
|
+
y: Math.max(-constraints.maxUp, Math.min(constraints.maxDown, rawDelta.y))
|
|
3285
|
+
};
|
|
3286
|
+
}
|
|
3287
|
+
// ─────────────────────────────────────────────────────────
|
|
3288
|
+
// Unified Drag API (Plugin owns all logic - framework just calls these)
|
|
3289
|
+
// ─────────────────────────────────────────────────────────
|
|
3290
|
+
/**
|
|
3291
|
+
* Start a unified drag operation.
|
|
3292
|
+
* The plugin automatically expands the selection to include attached links.
|
|
3293
|
+
* Framework components should call this instead of building their own logic.
|
|
3294
|
+
*
|
|
3295
|
+
* @param documentId - The document ID
|
|
3296
|
+
* @param options - Drag options (annotationIds and pageSize)
|
|
3297
|
+
*/
|
|
3298
|
+
startDrag(documentId, options) {
|
|
3299
|
+
const { annotationIds, pageSize } = options;
|
|
3300
|
+
const attachedLinkIds = [];
|
|
3301
|
+
for (const id of annotationIds) {
|
|
3302
|
+
const links = this.getAttachedLinksMethod(id, documentId);
|
|
3303
|
+
for (const link of links) {
|
|
3304
|
+
if (!attachedLinkIds.includes(link.object.id)) {
|
|
3305
|
+
attachedLinkIds.push(link.object.id);
|
|
3306
|
+
}
|
|
3307
|
+
}
|
|
3308
|
+
}
|
|
3309
|
+
const allParticipantIds = [...annotationIds, ...attachedLinkIds];
|
|
3310
|
+
const originalRects = /* @__PURE__ */ new Map();
|
|
3311
|
+
const constraints = [];
|
|
3312
|
+
for (const id of allParticipantIds) {
|
|
3313
|
+
const ta = this.getAnnotationById(id, documentId);
|
|
3314
|
+
if (ta) {
|
|
3315
|
+
originalRects.set(id, { ...ta.object.rect });
|
|
3316
|
+
constraints.push({
|
|
3317
|
+
id,
|
|
3318
|
+
rect: ta.object.rect,
|
|
3319
|
+
pageIndex: ta.object.pageIndex,
|
|
3320
|
+
pageSize
|
|
3321
|
+
});
|
|
3322
|
+
}
|
|
3323
|
+
}
|
|
3324
|
+
const combinedConstraints = this.computeCombinedConstraints(constraints);
|
|
3325
|
+
const state = {
|
|
3326
|
+
documentId,
|
|
3327
|
+
isDragging: true,
|
|
3328
|
+
primaryIds: annotationIds,
|
|
3329
|
+
attachedLinkIds,
|
|
3330
|
+
allParticipantIds,
|
|
3331
|
+
originalRects,
|
|
3332
|
+
delta: { x: 0, y: 0 },
|
|
3333
|
+
combinedConstraints
|
|
3334
|
+
};
|
|
3335
|
+
this.unifiedDragStates.set(documentId, state);
|
|
3336
|
+
this.unifiedDrag$.emit({ documentId, type: "start", state, previewPatches: {} });
|
|
3337
|
+
}
|
|
3338
|
+
/**
|
|
3339
|
+
* Compute preview patches for all drag participants.
|
|
3340
|
+
* Uses transformAnnotation to properly handle vertices, inkList, etc.
|
|
3341
|
+
*/
|
|
3342
|
+
computeDragPreviewPatches(state, documentId) {
|
|
3343
|
+
const previewPatches = {};
|
|
3344
|
+
for (const id of state.allParticipantIds) {
|
|
3345
|
+
const ta = this.getAnnotationById(id, documentId);
|
|
3346
|
+
if (!ta) continue;
|
|
3347
|
+
const originalRect = state.originalRects.get(id);
|
|
3348
|
+
if (!originalRect) continue;
|
|
3349
|
+
const newRect = {
|
|
3350
|
+
...originalRect,
|
|
3351
|
+
origin: {
|
|
3352
|
+
x: originalRect.origin.x + state.delta.x,
|
|
3353
|
+
y: originalRect.origin.y + state.delta.y
|
|
3354
|
+
}
|
|
3355
|
+
};
|
|
3356
|
+
previewPatches[id] = this.transformAnnotation(ta.object, {
|
|
3357
|
+
type: "move",
|
|
3358
|
+
changes: { rect: newRect }
|
|
3359
|
+
});
|
|
3360
|
+
}
|
|
3361
|
+
return previewPatches;
|
|
3362
|
+
}
|
|
3363
|
+
/**
|
|
3364
|
+
* Update the drag delta during a unified drag operation.
|
|
3365
|
+
* Returns the clamped delta synchronously for the caller's preview.
|
|
3366
|
+
*
|
|
3367
|
+
* @param documentId - The document ID
|
|
3368
|
+
* @param rawDelta - The unconstrained delta from the drag gesture
|
|
3369
|
+
* @returns The clamped delta
|
|
3370
|
+
*/
|
|
3371
|
+
updateDrag(documentId, rawDelta) {
|
|
3372
|
+
const state = this.unifiedDragStates.get(documentId);
|
|
3373
|
+
if (!(state == null ? void 0 : state.isDragging)) {
|
|
3374
|
+
return { x: 0, y: 0 };
|
|
3375
|
+
}
|
|
3376
|
+
const clampedDelta = this.clampDelta(rawDelta, state.combinedConstraints);
|
|
3377
|
+
const newState = {
|
|
3378
|
+
...state,
|
|
3379
|
+
delta: clampedDelta
|
|
3380
|
+
};
|
|
3381
|
+
this.unifiedDragStates.set(documentId, newState);
|
|
3382
|
+
const previewPatches = this.computeDragPreviewPatches(newState, documentId);
|
|
3383
|
+
this.unifiedDrag$.emit({ documentId, type: "update", state: newState, previewPatches });
|
|
3384
|
+
return clampedDelta;
|
|
3385
|
+
}
|
|
3386
|
+
/**
|
|
3387
|
+
* Commit the drag - plugin builds and applies ALL patches.
|
|
3388
|
+
* This is the key method that centralizes patch building in the plugin.
|
|
3389
|
+
*
|
|
3390
|
+
* @param documentId - The document ID
|
|
3391
|
+
*/
|
|
3392
|
+
commitDrag(documentId) {
|
|
3393
|
+
const state = this.unifiedDragStates.get(documentId);
|
|
3394
|
+
if (!state) return;
|
|
3395
|
+
const finalDelta = state.delta;
|
|
3396
|
+
if (finalDelta.x !== 0 || finalDelta.y !== 0) {
|
|
3397
|
+
const patches = [];
|
|
3398
|
+
for (const id of state.allParticipantIds) {
|
|
3399
|
+
const ta = this.getAnnotationById(id, documentId);
|
|
3400
|
+
if (!ta) continue;
|
|
3401
|
+
const originalRect = state.originalRects.get(id) ?? ta.object.rect;
|
|
3402
|
+
const newRect = {
|
|
3403
|
+
...originalRect,
|
|
3404
|
+
origin: {
|
|
3405
|
+
x: originalRect.origin.x + finalDelta.x,
|
|
3406
|
+
y: originalRect.origin.y + finalDelta.y
|
|
3407
|
+
}
|
|
3408
|
+
};
|
|
3409
|
+
const patch = this.transformAnnotation(ta.object, {
|
|
3410
|
+
type: "move",
|
|
3411
|
+
changes: { rect: newRect }
|
|
3412
|
+
});
|
|
3413
|
+
patches.push({ pageIndex: ta.object.pageIndex, id, patch });
|
|
3414
|
+
}
|
|
3415
|
+
if (patches.length > 0) {
|
|
3416
|
+
this.updateAnnotationsMethod(patches, documentId);
|
|
3417
|
+
}
|
|
3418
|
+
}
|
|
3419
|
+
const endPatches = this.computeDragPreviewPatches(state, documentId);
|
|
3420
|
+
this.unifiedDrag$.emit({
|
|
3421
|
+
documentId,
|
|
3422
|
+
type: "end",
|
|
3423
|
+
state: { ...state, isDragging: false },
|
|
3424
|
+
previewPatches: endPatches
|
|
3425
|
+
});
|
|
3426
|
+
this.unifiedDragStates.delete(documentId);
|
|
3427
|
+
}
|
|
3428
|
+
/**
|
|
3429
|
+
* Cancel the drag without committing.
|
|
3430
|
+
*
|
|
3431
|
+
* @param documentId - The document ID
|
|
3432
|
+
*/
|
|
3433
|
+
cancelDrag(documentId) {
|
|
3434
|
+
const state = this.unifiedDragStates.get(documentId);
|
|
3435
|
+
if (!state) return;
|
|
3436
|
+
this.unifiedDrag$.emit({
|
|
3437
|
+
documentId,
|
|
3438
|
+
type: "cancel",
|
|
3439
|
+
state: { ...state, isDragging: false, delta: { x: 0, y: 0 } },
|
|
3440
|
+
previewPatches: {}
|
|
3441
|
+
});
|
|
3442
|
+
this.unifiedDragStates.delete(documentId);
|
|
3443
|
+
}
|
|
3444
|
+
/**
|
|
3445
|
+
* Get the current unified drag state for a document.
|
|
3446
|
+
*/
|
|
3447
|
+
getDragState(documentId) {
|
|
3448
|
+
return this.unifiedDragStates.get(documentId) ?? null;
|
|
3449
|
+
}
|
|
3450
|
+
/**
|
|
3451
|
+
* Subscribe to unified drag state changes.
|
|
3452
|
+
* Framework components use this for preview updates.
|
|
3453
|
+
*/
|
|
3454
|
+
get onDragChange() {
|
|
3455
|
+
return this.unifiedDrag$.on;
|
|
3456
|
+
}
|
|
3457
|
+
// ─────────────────────────────────────────────────────────
|
|
3458
|
+
// Unified Resize API (Plugin owns all logic - framework just calls these)
|
|
3459
|
+
// ─────────────────────────────────────────────────────────
|
|
3460
|
+
/**
|
|
3461
|
+
* Compute the union bounding box of multiple rects.
|
|
3462
|
+
*/
|
|
3463
|
+
computeUnifiedGroupBoundingBox(rects) {
|
|
3464
|
+
if (rects.length === 0) {
|
|
3465
|
+
return { origin: { x: 0, y: 0 }, size: { width: 0, height: 0 } };
|
|
3466
|
+
}
|
|
3467
|
+
let minX = Infinity;
|
|
3468
|
+
let minY = Infinity;
|
|
3469
|
+
let maxX = -Infinity;
|
|
3470
|
+
let maxY = -Infinity;
|
|
3471
|
+
for (const rect of rects) {
|
|
3472
|
+
minX = Math.min(minX, rect.origin.x);
|
|
3473
|
+
minY = Math.min(minY, rect.origin.y);
|
|
3474
|
+
maxX = Math.max(maxX, rect.origin.x + rect.size.width);
|
|
3475
|
+
maxY = Math.max(maxY, rect.origin.y + rect.size.height);
|
|
3476
|
+
}
|
|
3477
|
+
return {
|
|
3478
|
+
origin: { x: minX, y: minY },
|
|
3479
|
+
size: { width: maxX - minX, height: maxY - minY }
|
|
3480
|
+
};
|
|
3481
|
+
}
|
|
3482
|
+
/**
|
|
3483
|
+
* Compute relative positions for annotations within a group bounding box.
|
|
3484
|
+
*/
|
|
3485
|
+
computeUnifiedRelativePositions(annotations, groupBox) {
|
|
3486
|
+
return annotations.map((anno) => ({
|
|
3487
|
+
id: anno.id,
|
|
3488
|
+
originalRect: anno.rect,
|
|
3489
|
+
pageIndex: anno.pageIndex,
|
|
3490
|
+
isAttachedLink: anno.isAttachedLink,
|
|
3491
|
+
parentId: anno.parentId,
|
|
3492
|
+
relativeX: groupBox.size.width > 0 ? (anno.rect.origin.x - groupBox.origin.x) / groupBox.size.width : 0,
|
|
3493
|
+
relativeY: groupBox.size.height > 0 ? (anno.rect.origin.y - groupBox.origin.y) / groupBox.size.height : 0,
|
|
3494
|
+
relativeWidth: groupBox.size.width > 0 ? anno.rect.size.width / groupBox.size.width : 1,
|
|
3495
|
+
relativeHeight: groupBox.size.height > 0 ? anno.rect.size.height / groupBox.size.height : 1
|
|
3496
|
+
}));
|
|
3497
|
+
}
|
|
3498
|
+
/**
|
|
3499
|
+
* Compute new rects for all annotations based on the new group bounding box.
|
|
3500
|
+
*/
|
|
3501
|
+
computeUnifiedResizedRects(participatingAnnotations, newGroupBox, minSize = 10) {
|
|
3502
|
+
const result = /* @__PURE__ */ new Map();
|
|
3503
|
+
for (const anno of participatingAnnotations) {
|
|
3504
|
+
const newWidth = Math.max(minSize, anno.relativeWidth * newGroupBox.size.width);
|
|
3505
|
+
const newHeight = Math.max(minSize, anno.relativeHeight * newGroupBox.size.height);
|
|
3506
|
+
result.set(anno.id, {
|
|
3507
|
+
origin: {
|
|
3508
|
+
x: newGroupBox.origin.x + anno.relativeX * newGroupBox.size.width,
|
|
3509
|
+
y: newGroupBox.origin.y + anno.relativeY * newGroupBox.size.height
|
|
3510
|
+
},
|
|
3511
|
+
size: {
|
|
3512
|
+
width: newWidth,
|
|
3513
|
+
height: newHeight
|
|
3514
|
+
}
|
|
3515
|
+
});
|
|
3516
|
+
}
|
|
3517
|
+
return result;
|
|
3518
|
+
}
|
|
3519
|
+
/**
|
|
3520
|
+
* Compute preview patches for all resize participants.
|
|
3521
|
+
* Uses transformAnnotation to properly handle vertices, inkList, etc.
|
|
3522
|
+
*/
|
|
3523
|
+
computeResizePreviewPatches(computedRects, documentId) {
|
|
3524
|
+
const previewPatches = {};
|
|
3525
|
+
for (const [id, newRect] of computedRects) {
|
|
3526
|
+
const ta = this.getAnnotationById(id, documentId);
|
|
3527
|
+
if (!ta) continue;
|
|
3528
|
+
previewPatches[id] = this.transformAnnotation(ta.object, {
|
|
3529
|
+
type: "resize",
|
|
3530
|
+
changes: { rect: newRect }
|
|
3531
|
+
});
|
|
3532
|
+
}
|
|
3533
|
+
return previewPatches;
|
|
3534
|
+
}
|
|
3535
|
+
/**
|
|
3536
|
+
* Start a unified resize operation.
|
|
3537
|
+
* The plugin automatically expands the selection to include attached links.
|
|
3538
|
+
*
|
|
3539
|
+
* @param documentId - The document ID
|
|
3540
|
+
* @param options - Resize options
|
|
3541
|
+
*/
|
|
3542
|
+
startResize(documentId, options) {
|
|
3543
|
+
const { annotationIds, pageSize, resizeHandle } = options;
|
|
3544
|
+
const attachedLinkIds = [];
|
|
3545
|
+
const annotationsWithLinks = [];
|
|
3546
|
+
for (const id of annotationIds) {
|
|
3547
|
+
const ta = this.getAnnotationById(id, documentId);
|
|
3548
|
+
if (ta) {
|
|
3549
|
+
annotationsWithLinks.push({
|
|
3550
|
+
id,
|
|
3551
|
+
rect: ta.object.rect,
|
|
3552
|
+
pageIndex: ta.object.pageIndex,
|
|
3553
|
+
isAttachedLink: false
|
|
3554
|
+
});
|
|
3555
|
+
const links = this.getAttachedLinksMethod(id, documentId);
|
|
3556
|
+
for (const link of links) {
|
|
3557
|
+
if (!attachedLinkIds.includes(link.object.id)) {
|
|
3558
|
+
attachedLinkIds.push(link.object.id);
|
|
3559
|
+
annotationsWithLinks.push({
|
|
3560
|
+
id: link.object.id,
|
|
3561
|
+
rect: link.object.rect,
|
|
3562
|
+
pageIndex: link.object.pageIndex,
|
|
3563
|
+
isAttachedLink: true,
|
|
3564
|
+
parentId: id
|
|
3565
|
+
});
|
|
3566
|
+
}
|
|
3567
|
+
}
|
|
3568
|
+
}
|
|
3569
|
+
}
|
|
3570
|
+
const allParticipantIds = [...annotationIds, ...attachedLinkIds];
|
|
3571
|
+
const rects = annotationsWithLinks.map((a) => a.rect);
|
|
3572
|
+
const groupBox = this.computeUnifiedGroupBoundingBox(rects);
|
|
3573
|
+
const participatingAnnotations = this.computeUnifiedRelativePositions(
|
|
3574
|
+
annotationsWithLinks,
|
|
3575
|
+
groupBox
|
|
3576
|
+
);
|
|
3577
|
+
const computedRects = this.computeUnifiedResizedRects(participatingAnnotations, groupBox);
|
|
3578
|
+
const state = {
|
|
3579
|
+
documentId,
|
|
3580
|
+
isResizing: true,
|
|
3581
|
+
primaryIds: annotationIds,
|
|
3582
|
+
attachedLinkIds,
|
|
3583
|
+
allParticipantIds,
|
|
3584
|
+
originalGroupBox: groupBox,
|
|
3585
|
+
currentGroupBox: groupBox,
|
|
3586
|
+
participatingAnnotations,
|
|
3587
|
+
resizeHandle,
|
|
3588
|
+
computedRects
|
|
3589
|
+
};
|
|
3590
|
+
this.unifiedResizeStates.set(documentId, state);
|
|
3591
|
+
const startPatches = this.computeResizePreviewPatches(computedRects, documentId);
|
|
3592
|
+
this.unifiedResize$.emit({
|
|
3593
|
+
documentId,
|
|
3594
|
+
type: "start",
|
|
3595
|
+
state,
|
|
3596
|
+
computedRects: Object.fromEntries(computedRects),
|
|
3597
|
+
previewPatches: startPatches
|
|
3598
|
+
});
|
|
3599
|
+
}
|
|
3600
|
+
/**
|
|
3601
|
+
* Update the resize with a new group bounding box.
|
|
3602
|
+
* Returns the computed rects synchronously for immediate preview use.
|
|
3603
|
+
*
|
|
3604
|
+
* @param documentId - The document ID
|
|
3605
|
+
* @param newGroupBox - The new group bounding box
|
|
3606
|
+
* @returns Record of annotation ID to new rect
|
|
3607
|
+
*/
|
|
3608
|
+
updateResize(documentId, newGroupBox) {
|
|
3609
|
+
const state = this.unifiedResizeStates.get(documentId);
|
|
3610
|
+
if (!(state == null ? void 0 : state.isResizing)) {
|
|
3611
|
+
return {};
|
|
3612
|
+
}
|
|
3613
|
+
const computedRects = this.computeUnifiedResizedRects(
|
|
3614
|
+
state.participatingAnnotations,
|
|
3615
|
+
newGroupBox
|
|
3616
|
+
);
|
|
3617
|
+
const newState = {
|
|
3618
|
+
...state,
|
|
3619
|
+
currentGroupBox: newGroupBox,
|
|
3620
|
+
computedRects
|
|
3621
|
+
};
|
|
3622
|
+
this.unifiedResizeStates.set(documentId, newState);
|
|
3623
|
+
const computedRectsObj = Object.fromEntries(computedRects);
|
|
3624
|
+
const previewPatches = this.computeResizePreviewPatches(computedRects, documentId);
|
|
3625
|
+
this.unifiedResize$.emit({
|
|
3626
|
+
documentId,
|
|
3627
|
+
type: "update",
|
|
3628
|
+
state: newState,
|
|
3629
|
+
computedRects: computedRectsObj,
|
|
3630
|
+
previewPatches
|
|
3631
|
+
});
|
|
3632
|
+
return computedRectsObj;
|
|
3633
|
+
}
|
|
3634
|
+
/**
|
|
3635
|
+
* Commit the resize - plugin builds and applies ALL patches.
|
|
3636
|
+
*
|
|
3637
|
+
* @param documentId - The document ID
|
|
3638
|
+
*/
|
|
3639
|
+
commitResize(documentId) {
|
|
3640
|
+
const state = this.unifiedResizeStates.get(documentId);
|
|
3641
|
+
if (!state) return;
|
|
3642
|
+
const computedRects = this.computeUnifiedResizedRects(
|
|
3643
|
+
state.participatingAnnotations,
|
|
3644
|
+
state.currentGroupBox
|
|
3645
|
+
);
|
|
3646
|
+
const patches = [];
|
|
3647
|
+
for (const [id, newRect] of computedRects) {
|
|
3648
|
+
const ta = this.getAnnotationById(id, documentId);
|
|
3649
|
+
if (!ta) continue;
|
|
3650
|
+
const patch = this.transformAnnotation(ta.object, {
|
|
3651
|
+
type: "resize",
|
|
3652
|
+
changes: { rect: newRect }
|
|
3653
|
+
});
|
|
3654
|
+
patches.push({ pageIndex: ta.object.pageIndex, id, patch });
|
|
3655
|
+
}
|
|
3656
|
+
if (patches.length > 0) {
|
|
3657
|
+
this.updateAnnotationsMethod(patches, documentId);
|
|
3658
|
+
}
|
|
3659
|
+
const endPatches = this.computeResizePreviewPatches(computedRects, documentId);
|
|
3660
|
+
this.unifiedResize$.emit({
|
|
3661
|
+
documentId,
|
|
3662
|
+
type: "end",
|
|
3663
|
+
state: { ...state, isResizing: false },
|
|
3664
|
+
computedRects: Object.fromEntries(computedRects),
|
|
3665
|
+
previewPatches: endPatches
|
|
3666
|
+
});
|
|
3667
|
+
this.unifiedResizeStates.delete(documentId);
|
|
3668
|
+
}
|
|
3669
|
+
/**
|
|
3670
|
+
* Cancel the resize without committing.
|
|
3671
|
+
*
|
|
3672
|
+
* @param documentId - The document ID
|
|
3673
|
+
*/
|
|
3674
|
+
cancelResize(documentId) {
|
|
3675
|
+
const state = this.unifiedResizeStates.get(documentId);
|
|
3676
|
+
if (!state) return;
|
|
3677
|
+
const originalRects = this.computeUnifiedResizedRects(
|
|
3678
|
+
state.participatingAnnotations,
|
|
3679
|
+
state.originalGroupBox
|
|
3680
|
+
);
|
|
3681
|
+
this.unifiedResize$.emit({
|
|
3682
|
+
documentId,
|
|
3683
|
+
type: "cancel",
|
|
3684
|
+
state: { ...state, isResizing: false, currentGroupBox: state.originalGroupBox },
|
|
3685
|
+
computedRects: Object.fromEntries(originalRects),
|
|
3686
|
+
previewPatches: {}
|
|
3687
|
+
});
|
|
3688
|
+
this.unifiedResizeStates.delete(documentId);
|
|
3689
|
+
}
|
|
3690
|
+
/**
|
|
3691
|
+
* Get the current unified resize state for a document.
|
|
3692
|
+
*/
|
|
3693
|
+
getResizeState(documentId) {
|
|
3694
|
+
return this.unifiedResizeStates.get(documentId) ?? null;
|
|
3695
|
+
}
|
|
3696
|
+
/**
|
|
3697
|
+
* Subscribe to unified resize state changes.
|
|
3698
|
+
* Framework components use this for preview updates.
|
|
3699
|
+
*/
|
|
3700
|
+
get onResizeChange() {
|
|
3701
|
+
return this.unifiedResize$.on;
|
|
3702
|
+
}
|
|
3703
|
+
updateAnnotationsMethod(patches, documentId) {
|
|
3704
|
+
const docId = documentId ?? this.getActiveDocumentId();
|
|
3705
|
+
if (!this.checkPermission(docId, PdfPermissionFlag.ModifyAnnotations)) {
|
|
3706
|
+
this.logger.debug(
|
|
3707
|
+
"AnnotationPlugin",
|
|
3708
|
+
"UpdateAnnotations",
|
|
3709
|
+
`Cannot update annotations: document ${docId} lacks ModifyAnnotations permission`
|
|
3710
|
+
);
|
|
3711
|
+
return;
|
|
3712
|
+
}
|
|
3713
|
+
const docState = this.getDocumentState(docId);
|
|
3714
|
+
const patchData = patches.map(({ pageIndex, id, patch }) => {
|
|
3715
|
+
var _a;
|
|
3716
|
+
const originalObject = (_a = docState.byUid[id]) == null ? void 0 : _a.object;
|
|
3717
|
+
if (!originalObject) return null;
|
|
3718
|
+
const finalPatch = this.buildPatch(originalObject, {
|
|
3719
|
+
...patch,
|
|
3720
|
+
author: patch.author ?? this.config.annotationAuthor
|
|
3721
|
+
});
|
|
3722
|
+
return { pageIndex, id, patch: finalPatch, originalObject };
|
|
3723
|
+
}).filter((p) => p !== null);
|
|
3724
|
+
if (patchData.length === 0) return;
|
|
3725
|
+
const execute = () => {
|
|
3726
|
+
for (const { pageIndex, id, patch, originalObject } of patchData) {
|
|
3727
|
+
this.dispatch(patchAnnotation(docId, pageIndex, id, patch));
|
|
3728
|
+
this.events$.emit({
|
|
3729
|
+
type: "update",
|
|
3730
|
+
documentId: docId,
|
|
3731
|
+
annotation: originalObject,
|
|
3732
|
+
pageIndex,
|
|
3733
|
+
patch,
|
|
3734
|
+
committed: false
|
|
3735
|
+
});
|
|
3736
|
+
}
|
|
3737
|
+
};
|
|
3738
|
+
if (!this.history) {
|
|
3739
|
+
execute();
|
|
3740
|
+
if (this.config.autoCommit !== false) {
|
|
3741
|
+
this.commit(docId);
|
|
3742
|
+
}
|
|
3743
|
+
return;
|
|
3744
|
+
}
|
|
3745
|
+
const undoData = patchData.map(({ pageIndex, id, patch, originalObject }) => ({
|
|
3746
|
+
pageIndex,
|
|
3747
|
+
id,
|
|
3748
|
+
originalPatch: Object.fromEntries(
|
|
3749
|
+
Object.keys(patch).map((key) => [key, originalObject[key]])
|
|
3750
|
+
),
|
|
3751
|
+
originalObject
|
|
3752
|
+
}));
|
|
3753
|
+
const command = {
|
|
3754
|
+
execute,
|
|
3755
|
+
undo: () => {
|
|
3756
|
+
for (const { pageIndex, id, originalPatch, originalObject } of undoData) {
|
|
3757
|
+
this.dispatch(patchAnnotation(docId, pageIndex, id, originalPatch));
|
|
3758
|
+
this.events$.emit({
|
|
3759
|
+
type: "update",
|
|
3760
|
+
documentId: docId,
|
|
3761
|
+
annotation: originalObject,
|
|
3762
|
+
pageIndex,
|
|
3763
|
+
patch: originalPatch,
|
|
3764
|
+
committed: false
|
|
3765
|
+
});
|
|
3766
|
+
}
|
|
3767
|
+
}
|
|
3768
|
+
};
|
|
3769
|
+
const historyScope = this.history.forDocument(docId);
|
|
3770
|
+
historyScope.register(command, this.ANNOTATION_HISTORY_TOPIC);
|
|
3771
|
+
}
|
|
2781
3772
|
getActiveTool(documentId) {
|
|
2782
3773
|
const docState = this.getDocumentState(documentId);
|
|
2783
3774
|
if (!docState.activeToolId) return null;
|
|
@@ -2819,87 +3810,185 @@ const _AnnotationPlugin = class _AnnotationPlugin extends BasePlugin {
|
|
|
2819
3810
|
}
|
|
2820
3811
|
return bestTool;
|
|
2821
3812
|
}
|
|
2822
|
-
|
|
2823
|
-
|
|
2824
|
-
|
|
3813
|
+
/**
|
|
3814
|
+
* Collects all pending annotation changes for a document into a batch.
|
|
3815
|
+
* This separates the "what to commit" from "how to commit" for cleaner code.
|
|
3816
|
+
*/
|
|
3817
|
+
collectPendingChanges(docId, doc) {
|
|
2825
3818
|
const docState = this.getDocumentState(docId);
|
|
2826
|
-
if (!docState.hasPendingChanges) return PdfTaskHelper.resolve(true);
|
|
2827
|
-
const coreDocState = this.getCoreDocument(docId);
|
|
2828
|
-
const doc = coreDocState == null ? void 0 : coreDocState.document;
|
|
2829
|
-
if (!doc)
|
|
2830
|
-
return PdfTaskHelper.reject({ code: PdfErrorCode.NotFound, message: "Document not found" });
|
|
2831
3819
|
const contexts = this.pendingContexts.get(docId);
|
|
2832
|
-
|
|
2833
|
-
|
|
2834
|
-
|
|
2835
|
-
|
|
3820
|
+
const batch = {
|
|
3821
|
+
creations: [],
|
|
3822
|
+
updates: [],
|
|
3823
|
+
deletions: [],
|
|
3824
|
+
committedUids: [],
|
|
3825
|
+
isEmpty: true
|
|
3826
|
+
};
|
|
2836
3827
|
for (const [uid, ta] of Object.entries(docState.byUid)) {
|
|
2837
3828
|
if (ta.commitState === "synced") continue;
|
|
2838
3829
|
const page = doc.pages.find((p) => p.index === ta.object.pageIndex);
|
|
2839
3830
|
if (!page) continue;
|
|
3831
|
+
batch.committedUids.push(uid);
|
|
3832
|
+
batch.isEmpty = false;
|
|
2840
3833
|
switch (ta.commitState) {
|
|
2841
3834
|
case "new":
|
|
2842
|
-
|
|
2843
|
-
|
|
2844
|
-
|
|
2845
|
-
|
|
2846
|
-
|
|
2847
|
-
documentId: docId,
|
|
2848
|
-
annotation: ta.object,
|
|
2849
|
-
pageIndex: ta.object.pageIndex,
|
|
2850
|
-
ctx,
|
|
2851
|
-
committed: true
|
|
2852
|
-
});
|
|
2853
|
-
contexts.delete(ta.object.id);
|
|
2854
|
-
}, ignore);
|
|
2855
|
-
creations.push(task2);
|
|
3835
|
+
batch.creations.push({
|
|
3836
|
+
uid,
|
|
3837
|
+
ta,
|
|
3838
|
+
ctx: contexts == null ? void 0 : contexts.get(ta.object.id)
|
|
3839
|
+
});
|
|
2856
3840
|
break;
|
|
2857
3841
|
case "dirty":
|
|
2858
|
-
|
|
2859
|
-
updateTask.wait(() => {
|
|
2860
|
-
this.events$.emit({
|
|
2861
|
-
type: "update",
|
|
2862
|
-
documentId: docId,
|
|
2863
|
-
annotation: ta.object,
|
|
2864
|
-
pageIndex: ta.object.pageIndex,
|
|
2865
|
-
patch: ta.object,
|
|
2866
|
-
committed: true
|
|
2867
|
-
});
|
|
2868
|
-
}, ignore);
|
|
2869
|
-
updates.push(updateTask);
|
|
3842
|
+
batch.updates.push({ uid, ta });
|
|
2870
3843
|
break;
|
|
2871
3844
|
case "deleted":
|
|
2872
|
-
deletions.push({
|
|
3845
|
+
batch.deletions.push({ uid, ta });
|
|
2873
3846
|
break;
|
|
2874
3847
|
}
|
|
2875
3848
|
}
|
|
2876
|
-
|
|
2877
|
-
|
|
3849
|
+
return batch;
|
|
3850
|
+
}
|
|
3851
|
+
/**
|
|
3852
|
+
* Executes a batch of pending changes by creating engine tasks.
|
|
3853
|
+
* Returns a task that resolves when all operations complete.
|
|
3854
|
+
*/
|
|
3855
|
+
executeCommitBatch(docId, doc, batch) {
|
|
3856
|
+
const task = new Task();
|
|
3857
|
+
const contexts = this.pendingContexts.get(docId);
|
|
3858
|
+
const pendingOps = [];
|
|
3859
|
+
for (const { uid, ta, ctx } of batch.creations) {
|
|
3860
|
+
const page = doc.pages.find((p) => p.index === ta.object.pageIndex);
|
|
3861
|
+
if (!page) continue;
|
|
3862
|
+
const createTask = this.engine.createPageAnnotation(doc, page, ta.object, ctx);
|
|
3863
|
+
pendingOps.push({ type: "create", task: createTask, ta, uid, ctx });
|
|
3864
|
+
}
|
|
3865
|
+
for (const { uid, ta } of batch.updates) {
|
|
2878
3866
|
const page = doc.pages.find((p) => p.index === ta.object.pageIndex);
|
|
2879
|
-
if (
|
|
2880
|
-
|
|
3867
|
+
if (!page) continue;
|
|
3868
|
+
const updateTask = this.engine.updatePageAnnotation(doc, page, ta.object);
|
|
3869
|
+
pendingOps.push({ type: "update", task: updateTask, ta, uid });
|
|
3870
|
+
}
|
|
3871
|
+
for (const { uid, ta } of batch.deletions) {
|
|
3872
|
+
const page = doc.pages.find((p) => p.index === ta.object.pageIndex);
|
|
3873
|
+
if (!page) continue;
|
|
3874
|
+
if (ta.object.id) {
|
|
3875
|
+
const deleteTask = new Task();
|
|
2881
3876
|
const removeTask = this.engine.removePageAnnotation(doc, page, ta.object);
|
|
2882
|
-
removeTask.wait(() =>
|
|
2883
|
-
|
|
3877
|
+
removeTask.wait(() => deleteTask.resolve(true), deleteTask.fail);
|
|
3878
|
+
pendingOps.push({ type: "delete", task: deleteTask, ta, uid });
|
|
3879
|
+
} else {
|
|
3880
|
+
this.dispatch(purgeAnnotation(docId, uid));
|
|
3881
|
+
}
|
|
3882
|
+
}
|
|
3883
|
+
const allTasks = pendingOps.map((op) => op.task);
|
|
3884
|
+
Task.allSettled(allTasks).wait(
|
|
3885
|
+
() => {
|
|
3886
|
+
this.emitCommitEvents(docId, pendingOps, contexts);
|
|
3887
|
+
this.dispatch(commitPendingChanges(docId, batch.committedUids));
|
|
3888
|
+
task.resolve(true);
|
|
3889
|
+
},
|
|
3890
|
+
(error) => task.fail(error)
|
|
3891
|
+
);
|
|
3892
|
+
return task;
|
|
3893
|
+
}
|
|
3894
|
+
/**
|
|
3895
|
+
* Emits commit events for all completed operations.
|
|
3896
|
+
* Centralizes event emission for cleaner separation of concerns.
|
|
3897
|
+
*/
|
|
3898
|
+
emitCommitEvents(docId, operations, contexts) {
|
|
3899
|
+
for (const op of operations) {
|
|
3900
|
+
if (op.task.state.stage !== TaskStage.Resolved) continue;
|
|
3901
|
+
switch (op.type) {
|
|
3902
|
+
case "create":
|
|
3903
|
+
this.events$.emit({
|
|
3904
|
+
type: "create",
|
|
3905
|
+
documentId: docId,
|
|
3906
|
+
annotation: op.ta.object,
|
|
3907
|
+
pageIndex: op.ta.object.pageIndex,
|
|
3908
|
+
ctx: op.ctx,
|
|
3909
|
+
committed: true
|
|
3910
|
+
});
|
|
3911
|
+
contexts == null ? void 0 : contexts.delete(op.ta.object.id);
|
|
3912
|
+
break;
|
|
3913
|
+
case "update":
|
|
3914
|
+
this.events$.emit({
|
|
3915
|
+
type: "update",
|
|
3916
|
+
documentId: docId,
|
|
3917
|
+
annotation: op.ta.object,
|
|
3918
|
+
pageIndex: op.ta.object.pageIndex,
|
|
3919
|
+
patch: op.ta.object,
|
|
3920
|
+
committed: true
|
|
3921
|
+
});
|
|
3922
|
+
break;
|
|
3923
|
+
case "delete":
|
|
3924
|
+
this.dispatch(purgeAnnotation(docId, op.uid));
|
|
2884
3925
|
this.events$.emit({
|
|
2885
3926
|
type: "delete",
|
|
2886
3927
|
documentId: docId,
|
|
2887
|
-
annotation: ta.object,
|
|
2888
|
-
pageIndex: ta.object.pageIndex,
|
|
3928
|
+
annotation: op.ta.object,
|
|
3929
|
+
pageIndex: op.ta.object.pageIndex,
|
|
2889
3930
|
committed: true
|
|
2890
3931
|
});
|
|
2891
|
-
|
|
2892
|
-
}, task2.fail);
|
|
2893
|
-
deletionTasks.push(task2);
|
|
2894
|
-
} else {
|
|
2895
|
-
this.dispatch(purgeAnnotation(docId, uid));
|
|
3932
|
+
break;
|
|
2896
3933
|
}
|
|
2897
3934
|
}
|
|
2898
|
-
|
|
2899
|
-
|
|
2900
|
-
|
|
2901
|
-
|
|
2902
|
-
|
|
3935
|
+
}
|
|
3936
|
+
/**
|
|
3937
|
+
* Attempts to acquire the commit lock for a document.
|
|
3938
|
+
* Returns true if acquired, false if a commit is already in progress.
|
|
3939
|
+
*/
|
|
3940
|
+
acquireCommitLock(docId) {
|
|
3941
|
+
if (this.commitInProgress.get(docId)) {
|
|
3942
|
+
return false;
|
|
3943
|
+
}
|
|
3944
|
+
this.commitInProgress.set(docId, true);
|
|
3945
|
+
return true;
|
|
3946
|
+
}
|
|
3947
|
+
/**
|
|
3948
|
+
* Releases the commit lock for a document.
|
|
3949
|
+
*/
|
|
3950
|
+
releaseCommitLock(docId) {
|
|
3951
|
+
this.commitInProgress.set(docId, false);
|
|
3952
|
+
}
|
|
3953
|
+
commit(documentId) {
|
|
3954
|
+
const docId = documentId ?? this.getActiveDocumentId();
|
|
3955
|
+
const docState = this.getDocumentState(docId);
|
|
3956
|
+
if (!docState.hasPendingChanges) {
|
|
3957
|
+
return PdfTaskHelper.resolve(true);
|
|
3958
|
+
}
|
|
3959
|
+
if (!this.acquireCommitLock(docId)) {
|
|
3960
|
+
return PdfTaskHelper.resolve(true);
|
|
3961
|
+
}
|
|
3962
|
+
const coreDocState = this.getCoreDocument(docId);
|
|
3963
|
+
const doc = coreDocState == null ? void 0 : coreDocState.document;
|
|
3964
|
+
if (!doc) {
|
|
3965
|
+
this.releaseCommitLock(docId);
|
|
3966
|
+
return PdfTaskHelper.reject({ code: PdfErrorCode.NotFound, message: "Document not found" });
|
|
3967
|
+
}
|
|
3968
|
+
const batch = this.collectPendingChanges(docId, doc);
|
|
3969
|
+
if (batch.isEmpty) {
|
|
3970
|
+
this.releaseCommitLock(docId);
|
|
3971
|
+
return PdfTaskHelper.resolve(true);
|
|
3972
|
+
}
|
|
3973
|
+
const task = new Task();
|
|
3974
|
+
this.executeCommitBatch(docId, doc, batch).wait(
|
|
3975
|
+
() => {
|
|
3976
|
+
this.releaseCommitLock(docId);
|
|
3977
|
+
const updatedDocState = this.getDocumentState(docId);
|
|
3978
|
+
if (updatedDocState.hasPendingChanges) {
|
|
3979
|
+
this.commit(docId).wait(
|
|
3980
|
+
(result) => task.resolve(result),
|
|
3981
|
+
(error) => task.fail(error)
|
|
3982
|
+
);
|
|
3983
|
+
} else {
|
|
3984
|
+
task.resolve(true);
|
|
3985
|
+
}
|
|
3986
|
+
},
|
|
3987
|
+
(error) => {
|
|
3988
|
+
this.releaseCommitLock(docId);
|
|
3989
|
+
task.fail(error);
|
|
3990
|
+
}
|
|
3991
|
+
);
|
|
2903
3992
|
return task;
|
|
2904
3993
|
}
|
|
2905
3994
|
/**
|
|
@@ -2948,11 +4037,23 @@ export {
|
|
|
2948
4037
|
getAnnotationByUid,
|
|
2949
4038
|
getAnnotations,
|
|
2950
4039
|
getAnnotationsByPageIndex,
|
|
4040
|
+
getAttachedLinks,
|
|
4041
|
+
getGroupLeaderId,
|
|
4042
|
+
getGroupMembers,
|
|
4043
|
+
getIRTChildIds,
|
|
4044
|
+
getIRTChildrenByType,
|
|
2951
4045
|
getSelectedAnnotation,
|
|
2952
4046
|
getSelectedAnnotationByPageIndex,
|
|
4047
|
+
getSelectedAnnotationIds,
|
|
4048
|
+
getSelectedAnnotations,
|
|
4049
|
+
getSelectedAnnotationsByPageIndex,
|
|
4050
|
+
getSelectionGroupingAction,
|
|
2953
4051
|
getSidebarAnnotationsWithReplies,
|
|
2954
4052
|
getSidebarAnnotationsWithRepliesGroupedByPage,
|
|
2955
4053
|
getToolDefaultsById,
|
|
4054
|
+
hasAttachedLinks,
|
|
4055
|
+
hasIRTChildren,
|
|
4056
|
+
hasMultipleSelected,
|
|
2956
4057
|
initialDocumentState,
|
|
2957
4058
|
initialState,
|
|
2958
4059
|
isAnnotationSelected,
|
|
@@ -2962,11 +4063,13 @@ export {
|
|
|
2962
4063
|
isFreeTextTool,
|
|
2963
4064
|
isHighlight,
|
|
2964
4065
|
isHighlightTool,
|
|
4066
|
+
isInGroup,
|
|
2965
4067
|
isInk,
|
|
2966
4068
|
isInkHighlighterTool,
|
|
2967
4069
|
isInkTool,
|
|
2968
4070
|
isLine,
|
|
2969
4071
|
isLineTool,
|
|
4072
|
+
isLink,
|
|
2970
4073
|
isPolygon,
|
|
2971
4074
|
isPolygonTool,
|
|
2972
4075
|
isPolyline,
|
|
@@ -2985,6 +4088,7 @@ export {
|
|
|
2985
4088
|
isUnderline,
|
|
2986
4089
|
isUnderlineTool,
|
|
2987
4090
|
manifest,
|
|
2988
|
-
index as patching
|
|
4091
|
+
index as patching,
|
|
4092
|
+
rectsIntersect
|
|
2989
4093
|
};
|
|
2990
4094
|
//# sourceMappingURL=index.js.map
|