@embedpdf/plugin-annotation 2.2.0 → 2.4.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 +1232 -101
- package/dist/index.js.map +1 -1
- package/dist/lib/actions.d.ts +33 -3
- package/dist/lib/annotation-plugin.d.ts +178 -2
- package/dist/lib/handlers/types.d.ts +1 -1
- package/dist/lib/helpers.d.ts +8 -2
- 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 +22 -4
- package/dist/lib/types.d.ts +278 -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 +1133 -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 +1132 -402
- package/dist/react/index.js.map +1 -1
- package/dist/shared/components/annotation-container.d.ts +13 -2
- package/dist/shared/components/annotation-layer.d.ts +6 -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 +4 -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 +50 -1
- package/dist/shared/context/index.d.ts +1 -0
- package/dist/shared/context/renderer-registry.d.ts +21 -0
- package/dist/shared/index.d.ts +2 -0
- package/dist/shared-preact/components/annotation-container.d.ts +13 -2
- package/dist/shared-preact/components/annotation-layer.d.ts +6 -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 +4 -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 +50 -1
- package/dist/shared-preact/context/index.d.ts +1 -0
- package/dist/shared-preact/context/renderer-registry.d.ts +21 -0
- package/dist/shared-preact/index.d.ts +2 -0
- package/dist/shared-react/components/annotation-container.d.ts +13 -2
- package/dist/shared-react/components/annotation-layer.d.ts +6 -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 +4 -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 +50 -1
- package/dist/shared-react/context/index.d.ts +1 -0
- package/dist/shared-react/context/renderer-registry.d.ts +21 -0
- package/dist/shared-react/index.d.ts +2 -0
- package/dist/svelte/components/AnnotationLayer.svelte.d.ts +8 -1
- package/dist/svelte/components/Annotations.svelte.d.ts +8 -1
- package/dist/svelte/components/GroupSelectionBox.svelte.d.ts +32 -0
- package/dist/svelte/components/RendererRegistryProvider.svelte.d.ts +7 -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 +2 -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/context/index.d.ts +2 -0
- package/dist/svelte/context/renderer-registry.svelte.d.ts +20 -0
- package/dist/svelte/context/types.d.ts +33 -0
- package/dist/svelte/index.cjs +1 -1
- package/dist/svelte/index.cjs.map +1 -1
- package/dist/svelte/index.d.ts +2 -0
- package/dist/svelte/index.js +1215 -394
- 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 +3 -1
- package/dist/vue/components/annotation-layer.vue.d.ts +31 -5
- package/dist/vue/components/annotations/free-text.vue.d.ts +1 -1
- 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/stamp.vue.d.ts +1 -1
- package/dist/vue/components/annotations.vue.d.ts +151 -53
- package/dist/vue/components/group-selection-box.vue.d.ts +73 -0
- package/dist/vue/components/index.d.ts +2 -0
- package/dist/vue/components/preview-renderer.vue.d.ts +1 -1
- package/dist/vue/components/renderer-registry-provider.vue.d.ts +13 -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/context/index.d.ts +2 -0
- package/dist/vue/context/renderer-registry.d.ts +26 -0
- package/dist/vue/context/types.d.ts +33 -0
- package/dist/vue/hooks/use-annotation.d.ts +7 -5
- package/dist/vue/index.cjs +1 -1
- package/dist/vue/index.cjs.map +1 -1
- package/dist/vue/index.d.ts +3 -1
- package/dist/vue/index.js +1124 -459
- package/dist/vue/index.js.map +1 -1
- package/dist/vue/types.d.ts +8 -1
- 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,13 +79,13 @@ 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
|
-
const purgeAnnotation = (documentId, uid) => ({
|
|
86
|
+
const purgeAnnotation = (documentId, pageIndex, uid) => ({
|
|
72
87
|
type: PURGE_ANNOTATION,
|
|
73
|
-
payload: { documentId, uid }
|
|
88
|
+
payload: { documentId, pageIndex, uid }
|
|
74
89
|
});
|
|
75
90
|
const addColorPreset = (c) => ({
|
|
76
91
|
type: ADD_COLOR_PRESET,
|
|
@@ -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,8 +144,14 @@ 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
|
+
}
|
|
150
|
+
function isRedact(a) {
|
|
151
|
+
return a.object.type === PdfAnnotationSubtype.REDACT;
|
|
152
|
+
}
|
|
129
153
|
function isSidebarAnnotation(a) {
|
|
130
|
-
return isTextMarkup(a) || isInk(a) || isSquare(a) || isCircle(a) || isPolygon(a) || isLine(a) || isPolyline(a) || isFreeText(a) || isStamp(a);
|
|
154
|
+
return isTextMarkup(a) || isInk(a) || isSquare(a) || isCircle(a) || isPolygon(a) || isLine(a) || isPolyline(a) || isFreeText(a) || isStamp(a) || isRedact(a);
|
|
131
155
|
}
|
|
132
156
|
const getAnnotationsByPageIndex = (s, page) => (s.pages[page] ?? []).map((uid) => s.byUid[uid]);
|
|
133
157
|
const getAnnotations = (s) => {
|
|
@@ -135,17 +159,25 @@ const getAnnotations = (s) => {
|
|
|
135
159
|
for (const p of Object.keys(s.pages).map(Number)) out[p] = getAnnotationsByPageIndex(s, p);
|
|
136
160
|
return out;
|
|
137
161
|
};
|
|
138
|
-
const getSelectedAnnotation = (s) => s.
|
|
162
|
+
const getSelectedAnnotation = (s) => s.selectedUids.length > 0 ? s.byUid[s.selectedUids[0]] ?? null : null;
|
|
163
|
+
const getSelectedAnnotations = (s) => s.selectedUids.map((uid) => s.byUid[uid]).filter((ta) => ta !== void 0);
|
|
164
|
+
const getSelectedAnnotationIds = (s) => s.selectedUids;
|
|
139
165
|
const getAnnotationByUid = (s, uid) => s.byUid[uid] ?? null;
|
|
140
166
|
const getSelectedAnnotationByPageIndex = (s, pageIndex) => {
|
|
141
|
-
if (!s.selectedUid) return null;
|
|
142
167
|
const pageUids = s.pages[pageIndex] ?? [];
|
|
143
|
-
|
|
144
|
-
|
|
168
|
+
for (const uid of s.selectedUids) {
|
|
169
|
+
if (pageUids.includes(uid)) {
|
|
170
|
+
return s.byUid[uid] ?? null;
|
|
171
|
+
}
|
|
145
172
|
}
|
|
146
173
|
return null;
|
|
147
174
|
};
|
|
148
|
-
const
|
|
175
|
+
const getSelectedAnnotationsByPageIndex = (s, pageIndex) => {
|
|
176
|
+
const pageUids = new Set(s.pages[pageIndex] ?? []);
|
|
177
|
+
return s.selectedUids.filter((uid) => pageUids.has(uid)).map((uid) => s.byUid[uid]).filter((ta) => ta !== void 0);
|
|
178
|
+
};
|
|
179
|
+
const isAnnotationSelected = (s, id) => s.selectedUids.includes(id);
|
|
180
|
+
const hasMultipleSelected = (s) => s.selectedUids.length > 1;
|
|
149
181
|
function getToolDefaultsById(state, toolId) {
|
|
150
182
|
const tool = state.tools.find((t) => t.id === toolId);
|
|
151
183
|
return tool == null ? void 0 : tool.defaults;
|
|
@@ -190,6 +222,101 @@ const getSidebarAnnotationsWithReplies = (s) => {
|
|
|
190
222
|
}
|
|
191
223
|
return out;
|
|
192
224
|
};
|
|
225
|
+
const getIRTChildIds = (s, parentId) => {
|
|
226
|
+
const children = [];
|
|
227
|
+
for (const uidList of Object.values(s.pages)) {
|
|
228
|
+
for (const uid of uidList) {
|
|
229
|
+
const ta = s.byUid[uid];
|
|
230
|
+
if (ta && "inReplyToId" in ta.object && ta.object.inReplyToId === parentId) {
|
|
231
|
+
children.push({ id: ta.object.id, pageIndex: ta.object.pageIndex });
|
|
232
|
+
}
|
|
233
|
+
}
|
|
234
|
+
}
|
|
235
|
+
return children;
|
|
236
|
+
};
|
|
237
|
+
const hasIRTChildren = (s, parentId) => {
|
|
238
|
+
for (const uidList of Object.values(s.pages)) {
|
|
239
|
+
for (const uid of uidList) {
|
|
240
|
+
const ta = s.byUid[uid];
|
|
241
|
+
if (ta && "inReplyToId" in ta.object && ta.object.inReplyToId === parentId) {
|
|
242
|
+
return true;
|
|
243
|
+
}
|
|
244
|
+
}
|
|
245
|
+
}
|
|
246
|
+
return false;
|
|
247
|
+
};
|
|
248
|
+
const getIRTChildrenByType = (s, parentId, types) => {
|
|
249
|
+
const children = [];
|
|
250
|
+
for (const uidList of Object.values(s.pages)) {
|
|
251
|
+
for (const uid of uidList) {
|
|
252
|
+
const ta = s.byUid[uid];
|
|
253
|
+
if (ta && "inReplyToId" in ta.object && ta.object.inReplyToId === parentId && types.includes(ta.object.type)) {
|
|
254
|
+
children.push(ta);
|
|
255
|
+
}
|
|
256
|
+
}
|
|
257
|
+
}
|
|
258
|
+
return children;
|
|
259
|
+
};
|
|
260
|
+
const getAttachedLinks = (s, parentId) => getIRTChildrenByType(s, parentId, [PdfAnnotationSubtype.LINK]);
|
|
261
|
+
const hasAttachedLinks = (s, parentId) => getAttachedLinks(s, parentId).length > 0;
|
|
262
|
+
const getGroupLeaderId = (s, annotationId) => {
|
|
263
|
+
const ta = s.byUid[annotationId];
|
|
264
|
+
if (!ta) return void 0;
|
|
265
|
+
if (ta.object.inReplyToId && ta.object.replyType === PdfAnnotationReplyType.Group) {
|
|
266
|
+
return ta.object.inReplyToId;
|
|
267
|
+
}
|
|
268
|
+
return annotationId;
|
|
269
|
+
};
|
|
270
|
+
const getGroupMembers = (s, annotationId) => {
|
|
271
|
+
const leaderId = getGroupLeaderId(s, annotationId);
|
|
272
|
+
if (!leaderId) return [];
|
|
273
|
+
const members = [];
|
|
274
|
+
const leader = s.byUid[leaderId];
|
|
275
|
+
if (leader && leader.object.type !== PdfAnnotationSubtype.LINK) {
|
|
276
|
+
members.push(leader);
|
|
277
|
+
}
|
|
278
|
+
for (const uidList of Object.values(s.pages)) {
|
|
279
|
+
for (const uid of uidList) {
|
|
280
|
+
const ta = s.byUid[uid];
|
|
281
|
+
if (ta && ta.object.inReplyToId === leaderId && ta.object.replyType === PdfAnnotationReplyType.Group && ta.object.type !== PdfAnnotationSubtype.LINK) {
|
|
282
|
+
members.push(ta);
|
|
283
|
+
}
|
|
284
|
+
}
|
|
285
|
+
}
|
|
286
|
+
return members;
|
|
287
|
+
};
|
|
288
|
+
const isInGroup = (s, annotationId) => {
|
|
289
|
+
const ta = s.byUid[annotationId];
|
|
290
|
+
if (!ta) return false;
|
|
291
|
+
if (ta.object.type === PdfAnnotationSubtype.LINK) {
|
|
292
|
+
return false;
|
|
293
|
+
}
|
|
294
|
+
if (ta.object.inReplyToId && ta.object.replyType === PdfAnnotationReplyType.Group) {
|
|
295
|
+
return true;
|
|
296
|
+
}
|
|
297
|
+
for (const uidList of Object.values(s.pages)) {
|
|
298
|
+
for (const uid of uidList) {
|
|
299
|
+
const other = s.byUid[uid];
|
|
300
|
+
if (other && other.object.inReplyToId === annotationId && other.object.replyType === PdfAnnotationReplyType.Group && other.object.type !== PdfAnnotationSubtype.LINK) {
|
|
301
|
+
return true;
|
|
302
|
+
}
|
|
303
|
+
}
|
|
304
|
+
}
|
|
305
|
+
return false;
|
|
306
|
+
};
|
|
307
|
+
const getSelectionGroupingAction = (s) => {
|
|
308
|
+
const selected = getSelectedAnnotations(s);
|
|
309
|
+
if (selected.length === 0) return "disabled";
|
|
310
|
+
const firstId = selected[0].object.id;
|
|
311
|
+
if (isInGroup(s, firstId)) {
|
|
312
|
+
const members = getGroupMembers(s, firstId);
|
|
313
|
+
const memberIds = new Set(members.map((m) => m.object.id));
|
|
314
|
+
if (selected.every((ta) => memberIds.has(ta.object.id))) {
|
|
315
|
+
return "ungroup";
|
|
316
|
+
}
|
|
317
|
+
}
|
|
318
|
+
return selected.length >= 2 ? "group" : "disabled";
|
|
319
|
+
};
|
|
193
320
|
const defaultTools = [
|
|
194
321
|
// Text Markup Tools
|
|
195
322
|
{
|
|
@@ -200,11 +327,16 @@ const defaultTools = [
|
|
|
200
327
|
exclusive: false,
|
|
201
328
|
textSelection: true,
|
|
202
329
|
isDraggable: false,
|
|
203
|
-
isResizable: false
|
|
330
|
+
isResizable: false,
|
|
331
|
+
// Text markup annotations are anchored to text and should not move/resize in groups
|
|
332
|
+
isGroupDraggable: false,
|
|
333
|
+
isGroupResizable: false
|
|
204
334
|
},
|
|
205
335
|
defaults: {
|
|
206
336
|
type: PdfAnnotationSubtype.HIGHLIGHT,
|
|
337
|
+
strokeColor: "#FFCD45",
|
|
207
338
|
color: "#FFCD45",
|
|
339
|
+
// deprecated alias
|
|
208
340
|
opacity: 1,
|
|
209
341
|
blendMode: PdfBlendMode.Multiply
|
|
210
342
|
}
|
|
@@ -217,11 +349,15 @@ const defaultTools = [
|
|
|
217
349
|
exclusive: false,
|
|
218
350
|
textSelection: true,
|
|
219
351
|
isDraggable: false,
|
|
220
|
-
isResizable: false
|
|
352
|
+
isResizable: false,
|
|
353
|
+
isGroupDraggable: false,
|
|
354
|
+
isGroupResizable: false
|
|
221
355
|
},
|
|
222
356
|
defaults: {
|
|
223
357
|
type: PdfAnnotationSubtype.UNDERLINE,
|
|
358
|
+
strokeColor: "#E44234",
|
|
224
359
|
color: "#E44234",
|
|
360
|
+
// deprecated alias
|
|
225
361
|
opacity: 1
|
|
226
362
|
}
|
|
227
363
|
},
|
|
@@ -231,11 +367,17 @@ const defaultTools = [
|
|
|
231
367
|
matchScore: (a) => a.type === PdfAnnotationSubtype.STRIKEOUT ? 1 : 0,
|
|
232
368
|
interaction: {
|
|
233
369
|
exclusive: false,
|
|
234
|
-
textSelection: true
|
|
370
|
+
textSelection: true,
|
|
371
|
+
isDraggable: false,
|
|
372
|
+
isResizable: false,
|
|
373
|
+
isGroupDraggable: false,
|
|
374
|
+
isGroupResizable: false
|
|
235
375
|
},
|
|
236
376
|
defaults: {
|
|
237
377
|
type: PdfAnnotationSubtype.STRIKEOUT,
|
|
378
|
+
strokeColor: "#E44234",
|
|
238
379
|
color: "#E44234",
|
|
380
|
+
// deprecated alias
|
|
239
381
|
opacity: 1
|
|
240
382
|
}
|
|
241
383
|
},
|
|
@@ -247,11 +389,15 @@ const defaultTools = [
|
|
|
247
389
|
exclusive: false,
|
|
248
390
|
textSelection: true,
|
|
249
391
|
isDraggable: false,
|
|
250
|
-
isResizable: false
|
|
392
|
+
isResizable: false,
|
|
393
|
+
isGroupDraggable: false,
|
|
394
|
+
isGroupResizable: false
|
|
251
395
|
},
|
|
252
396
|
defaults: {
|
|
253
397
|
type: PdfAnnotationSubtype.SQUIGGLY,
|
|
398
|
+
strokeColor: "#E44234",
|
|
254
399
|
color: "#E44234",
|
|
400
|
+
// deprecated alias
|
|
255
401
|
opacity: 1
|
|
256
402
|
}
|
|
257
403
|
},
|
|
@@ -269,7 +415,9 @@ const defaultTools = [
|
|
|
269
415
|
},
|
|
270
416
|
defaults: {
|
|
271
417
|
type: PdfAnnotationSubtype.INK,
|
|
418
|
+
strokeColor: "#E44234",
|
|
272
419
|
color: "#E44234",
|
|
420
|
+
// deprecated alias
|
|
273
421
|
opacity: 1,
|
|
274
422
|
strokeWidth: 6
|
|
275
423
|
}
|
|
@@ -288,7 +436,9 @@ const defaultTools = [
|
|
|
288
436
|
defaults: {
|
|
289
437
|
type: PdfAnnotationSubtype.INK,
|
|
290
438
|
intent: "InkHighlight",
|
|
439
|
+
strokeColor: "#FFCD45",
|
|
291
440
|
color: "#FFCD45",
|
|
441
|
+
// deprecated alias
|
|
292
442
|
opacity: 1,
|
|
293
443
|
strokeWidth: 14,
|
|
294
444
|
blendMode: PdfBlendMode.Multiply
|
|
@@ -352,7 +502,10 @@ const defaultTools = [
|
|
|
352
502
|
cursor: "crosshair",
|
|
353
503
|
isDraggable: true,
|
|
354
504
|
isResizable: false,
|
|
355
|
-
|
|
505
|
+
// Uses vertex editing when selected individually
|
|
506
|
+
lockAspectRatio: false,
|
|
507
|
+
isGroupResizable: true
|
|
508
|
+
// Scales proportionally in a group
|
|
356
509
|
},
|
|
357
510
|
defaults: {
|
|
358
511
|
type: PdfAnnotationSubtype.LINE,
|
|
@@ -376,7 +529,10 @@ const defaultTools = [
|
|
|
376
529
|
cursor: "crosshair",
|
|
377
530
|
isDraggable: true,
|
|
378
531
|
isResizable: false,
|
|
379
|
-
|
|
532
|
+
// Uses vertex editing when selected individually
|
|
533
|
+
lockAspectRatio: false,
|
|
534
|
+
isGroupResizable: true
|
|
535
|
+
// Scales proportionally in a group
|
|
380
536
|
},
|
|
381
537
|
defaults: {
|
|
382
538
|
type: PdfAnnotationSubtype.LINE,
|
|
@@ -405,7 +561,10 @@ const defaultTools = [
|
|
|
405
561
|
cursor: "crosshair",
|
|
406
562
|
isDraggable: true,
|
|
407
563
|
isResizable: false,
|
|
408
|
-
|
|
564
|
+
// Uses vertex editing when selected individually
|
|
565
|
+
lockAspectRatio: false,
|
|
566
|
+
isGroupResizable: true
|
|
567
|
+
// Scales proportionally in a group
|
|
409
568
|
},
|
|
410
569
|
defaults: {
|
|
411
570
|
type: PdfAnnotationSubtype.POLYLINE,
|
|
@@ -424,7 +583,10 @@ const defaultTools = [
|
|
|
424
583
|
cursor: "crosshair",
|
|
425
584
|
isDraggable: true,
|
|
426
585
|
isResizable: false,
|
|
427
|
-
|
|
586
|
+
// Uses vertex editing when selected individually
|
|
587
|
+
lockAspectRatio: false,
|
|
588
|
+
isGroupResizable: true
|
|
589
|
+
// Scales proportionally in a group
|
|
428
590
|
},
|
|
429
591
|
defaults: {
|
|
430
592
|
type: PdfAnnotationSubtype.POLYGON,
|
|
@@ -454,7 +616,10 @@ const defaultTools = [
|
|
|
454
616
|
fontFamily: PdfStandardFont.Helvetica,
|
|
455
617
|
textAlign: PdfTextAlignment.Left,
|
|
456
618
|
verticalAlign: PdfVerticalAlignment.Top,
|
|
619
|
+
color: "transparent",
|
|
620
|
+
// fill color (matches shape convention)
|
|
457
621
|
backgroundColor: "transparent",
|
|
622
|
+
// deprecated alias
|
|
458
623
|
opacity: 1
|
|
459
624
|
},
|
|
460
625
|
clickBehavior: {
|
|
@@ -492,9 +657,11 @@ const DEFAULT_COLORS = [
|
|
|
492
657
|
"#000000",
|
|
493
658
|
"#FFFFFF"
|
|
494
659
|
];
|
|
660
|
+
const computeSelectedUid = (selectedUids) => selectedUids.length === 1 ? selectedUids[0] : null;
|
|
495
661
|
const initialDocumentState = () => ({
|
|
496
662
|
pages: {},
|
|
497
663
|
byUid: {},
|
|
664
|
+
selectedUids: [],
|
|
498
665
|
selectedUid: null,
|
|
499
666
|
activeToolId: null,
|
|
500
667
|
hasPendingChanges: false
|
|
@@ -595,7 +762,7 @@ const reducer = (state, action) => {
|
|
|
595
762
|
...state,
|
|
596
763
|
documents: {
|
|
597
764
|
...state.documents,
|
|
598
|
-
[documentId]: { ...docState, selectedUid: id }
|
|
765
|
+
[documentId]: { ...docState, selectedUids: [id], selectedUid: id }
|
|
599
766
|
}
|
|
600
767
|
};
|
|
601
768
|
}
|
|
@@ -607,7 +774,58 @@ const reducer = (state, action) => {
|
|
|
607
774
|
...state,
|
|
608
775
|
documents: {
|
|
609
776
|
...state.documents,
|
|
610
|
-
[documentId]: { ...docState, selectedUid: null }
|
|
777
|
+
[documentId]: { ...docState, selectedUids: [], selectedUid: null }
|
|
778
|
+
}
|
|
779
|
+
};
|
|
780
|
+
}
|
|
781
|
+
case ADD_TO_SELECTION: {
|
|
782
|
+
const { documentId, id } = action.payload;
|
|
783
|
+
const docState = state.documents[documentId];
|
|
784
|
+
if (!docState) return state;
|
|
785
|
+
if (docState.selectedUids.includes(id)) return state;
|
|
786
|
+
const newSelectedUids = [...docState.selectedUids, id];
|
|
787
|
+
return {
|
|
788
|
+
...state,
|
|
789
|
+
documents: {
|
|
790
|
+
...state.documents,
|
|
791
|
+
[documentId]: {
|
|
792
|
+
...docState,
|
|
793
|
+
selectedUids: newSelectedUids,
|
|
794
|
+
selectedUid: computeSelectedUid(newSelectedUids)
|
|
795
|
+
}
|
|
796
|
+
}
|
|
797
|
+
};
|
|
798
|
+
}
|
|
799
|
+
case REMOVE_FROM_SELECTION: {
|
|
800
|
+
const { documentId, id } = action.payload;
|
|
801
|
+
const docState = state.documents[documentId];
|
|
802
|
+
if (!docState) return state;
|
|
803
|
+
const newSelectedUids = docState.selectedUids.filter((uid) => uid !== id);
|
|
804
|
+
return {
|
|
805
|
+
...state,
|
|
806
|
+
documents: {
|
|
807
|
+
...state.documents,
|
|
808
|
+
[documentId]: {
|
|
809
|
+
...docState,
|
|
810
|
+
selectedUids: newSelectedUids,
|
|
811
|
+
selectedUid: computeSelectedUid(newSelectedUids)
|
|
812
|
+
}
|
|
813
|
+
}
|
|
814
|
+
};
|
|
815
|
+
}
|
|
816
|
+
case SET_SELECTION: {
|
|
817
|
+
const { documentId, ids } = action.payload;
|
|
818
|
+
const docState = state.documents[documentId];
|
|
819
|
+
if (!docState) return state;
|
|
820
|
+
return {
|
|
821
|
+
...state,
|
|
822
|
+
documents: {
|
|
823
|
+
...state.documents,
|
|
824
|
+
[documentId]: {
|
|
825
|
+
...docState,
|
|
826
|
+
selectedUids: ids,
|
|
827
|
+
selectedUid: computeSelectedUid(ids)
|
|
828
|
+
}
|
|
611
829
|
}
|
|
612
830
|
};
|
|
613
831
|
}
|
|
@@ -683,34 +901,50 @@ const reducer = (state, action) => {
|
|
|
683
901
|
};
|
|
684
902
|
}
|
|
685
903
|
case COMMIT_PENDING_CHANGES: {
|
|
686
|
-
const { documentId } = action.payload;
|
|
904
|
+
const { documentId, committedUids } = action.payload;
|
|
687
905
|
const docState = state.documents[documentId];
|
|
688
906
|
if (!docState) return state;
|
|
907
|
+
const committedSet = new Set(committedUids);
|
|
689
908
|
const cleaned = {};
|
|
909
|
+
let stillHasPending = false;
|
|
690
910
|
for (const [uid, ta] of Object.entries(docState.byUid)) {
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
|
|
911
|
+
if (committedSet.has(uid)) {
|
|
912
|
+
cleaned[uid] = {
|
|
913
|
+
...ta,
|
|
914
|
+
commitState: ta.commitState === "dirty" || ta.commitState === "new" ? "synced" : ta.commitState
|
|
915
|
+
};
|
|
916
|
+
} else {
|
|
917
|
+
cleaned[uid] = ta;
|
|
918
|
+
if (ta.commitState === "new" || ta.commitState === "dirty" || ta.commitState === "deleted") {
|
|
919
|
+
stillHasPending = true;
|
|
920
|
+
}
|
|
921
|
+
}
|
|
695
922
|
}
|
|
696
923
|
return {
|
|
697
924
|
...state,
|
|
698
925
|
documents: {
|
|
699
926
|
...state.documents,
|
|
700
|
-
[documentId]: { ...docState, byUid: cleaned, hasPendingChanges:
|
|
927
|
+
[documentId]: { ...docState, byUid: cleaned, hasPendingChanges: stillHasPending }
|
|
701
928
|
}
|
|
702
929
|
};
|
|
703
930
|
}
|
|
704
931
|
case PURGE_ANNOTATION: {
|
|
705
|
-
const { documentId, uid } = action.payload;
|
|
932
|
+
const { documentId, pageIndex, uid } = action.payload;
|
|
706
933
|
const docState = state.documents[documentId];
|
|
707
|
-
if (!docState) return state;
|
|
934
|
+
if (!docState || !docState.byUid[uid]) return state;
|
|
708
935
|
const { [uid]: _gone, ...rest } = docState.byUid;
|
|
709
936
|
return {
|
|
710
937
|
...state,
|
|
711
938
|
documents: {
|
|
712
939
|
...state.documents,
|
|
713
|
-
[documentId]: {
|
|
940
|
+
[documentId]: {
|
|
941
|
+
...docState,
|
|
942
|
+
pages: {
|
|
943
|
+
...docState.pages,
|
|
944
|
+
[pageIndex]: (docState.pages[pageIndex] ?? []).filter((u) => u !== uid)
|
|
945
|
+
},
|
|
946
|
+
byUid: rest
|
|
947
|
+
}
|
|
714
948
|
}
|
|
715
949
|
};
|
|
716
950
|
}
|
|
@@ -763,7 +997,7 @@ const inkHandlerFactory = {
|
|
|
763
997
|
return {
|
|
764
998
|
...tool.defaults,
|
|
765
999
|
strokeWidth: tool.defaults.strokeWidth ?? 1,
|
|
766
|
-
|
|
1000
|
+
strokeColor: tool.defaults.strokeColor ?? tool.defaults.color ?? "#000000",
|
|
767
1001
|
opacity: tool.defaults.opacity ?? 1,
|
|
768
1002
|
flags: tool.defaults.flags ?? ["print"]
|
|
769
1003
|
};
|
|
@@ -899,7 +1133,7 @@ const freeTextHandlerFactory = {
|
|
|
899
1133
|
opacity: tool.defaults.opacity ?? 1,
|
|
900
1134
|
fontSize: tool.defaults.fontSize ?? 12,
|
|
901
1135
|
fontFamily: tool.defaults.fontFamily ?? PdfStandardFont.Helvetica,
|
|
902
|
-
|
|
1136
|
+
color: tool.defaults.color ?? tool.defaults.backgroundColor ?? "transparent",
|
|
903
1137
|
textAlign: tool.defaults.textAlign ?? PdfTextAlignment.Left,
|
|
904
1138
|
verticalAlign: tool.defaults.verticalAlign ?? PdfVerticalAlignment.Top,
|
|
905
1139
|
contents: tool.defaults.contents ?? "Insert text here",
|
|
@@ -1435,7 +1669,10 @@ const polylineHandlerFactory = {
|
|
|
1435
1669
|
};
|
|
1436
1670
|
};
|
|
1437
1671
|
return {
|
|
1438
|
-
onClick: (pos) => {
|
|
1672
|
+
onClick: (pos, evt) => {
|
|
1673
|
+
if (evt.metaKey || evt.ctrlKey) {
|
|
1674
|
+
return;
|
|
1675
|
+
}
|
|
1439
1676
|
const clampedPos = clampToPage(pos);
|
|
1440
1677
|
const vertices = getVertices();
|
|
1441
1678
|
const lastVertex = vertices[vertices.length - 1];
|
|
@@ -1537,7 +1774,10 @@ const polygonHandlerFactory = {
|
|
|
1537
1774
|
};
|
|
1538
1775
|
};
|
|
1539
1776
|
return {
|
|
1540
|
-
onClick: (pos) => {
|
|
1777
|
+
onClick: (pos, evt) => {
|
|
1778
|
+
if (evt.metaKey || evt.ctrlKey) {
|
|
1779
|
+
return;
|
|
1780
|
+
}
|
|
1541
1781
|
const clampedPos = clampToPage(pos);
|
|
1542
1782
|
if (isInsideStartHandle(clampedPos) && getVertices().length >= 3) {
|
|
1543
1783
|
commitPolygon();
|
|
@@ -2234,11 +2474,16 @@ const _AnnotationPlugin = class _AnnotationPlugin extends BasePlugin {
|
|
|
2234
2474
|
this.pendingContexts = /* @__PURE__ */ new Map();
|
|
2235
2475
|
this.isInitialLoadComplete = /* @__PURE__ */ new Map();
|
|
2236
2476
|
this.importQueue = /* @__PURE__ */ new Map();
|
|
2477
|
+
this.commitInProgress = /* @__PURE__ */ new Map();
|
|
2237
2478
|
this.handlerFactories = /* @__PURE__ */ new Map();
|
|
2238
2479
|
this.activeTool$ = createBehaviorEmitter();
|
|
2239
2480
|
this.events$ = createBehaviorEmitter();
|
|
2240
2481
|
this.toolsChange$ = createBehaviorEmitter();
|
|
2241
2482
|
this.patchRegistry = new PatchRegistry();
|
|
2483
|
+
this.unifiedDragStates = /* @__PURE__ */ new Map();
|
|
2484
|
+
this.unifiedDrag$ = createBehaviorEmitter();
|
|
2485
|
+
this.unifiedResizeStates = /* @__PURE__ */ new Map();
|
|
2486
|
+
this.unifiedResize$ = createBehaviorEmitter();
|
|
2242
2487
|
this.config = config;
|
|
2243
2488
|
this.selection = ((_a = registry.getPlugin("selection")) == null ? void 0 : _a.provides()) ?? null;
|
|
2244
2489
|
this.history = ((_b = registry.getPlugin("history")) == null ? void 0 : _b.provides()) ?? null;
|
|
@@ -2268,7 +2513,7 @@ const _AnnotationPlugin = class _AnnotationPlugin extends BasePlugin {
|
|
|
2268
2513
|
if (this.selection) {
|
|
2269
2514
|
for (const tool of this.state.tools) {
|
|
2270
2515
|
if (tool.interaction.textSelection) {
|
|
2271
|
-
this.selection.enableForMode(tool.interaction.mode ?? tool.id);
|
|
2516
|
+
this.selection.enableForMode(tool.interaction.mode ?? tool.id, { showRects: false });
|
|
2272
2517
|
}
|
|
2273
2518
|
}
|
|
2274
2519
|
}
|
|
@@ -2301,7 +2546,7 @@ const _AnnotationPlugin = class _AnnotationPlugin extends BasePlugin {
|
|
|
2301
2546
|
this.patchRegistry.register(PdfAnnotationSubtype.POLYGON, patchPolygon);
|
|
2302
2547
|
}
|
|
2303
2548
|
async initialize() {
|
|
2304
|
-
var _a, _b;
|
|
2549
|
+
var _a, _b, _c;
|
|
2305
2550
|
this.state.tools.forEach((tool) => this.registerInteractionForTool(tool));
|
|
2306
2551
|
if (this.history) {
|
|
2307
2552
|
this.history.onHistoryChange((event) => {
|
|
@@ -2318,8 +2563,28 @@ const _AnnotationPlugin = class _AnnotationPlugin extends BasePlugin {
|
|
|
2318
2563
|
this.dispatch(setActiveToolId(s.documentId, newToolId));
|
|
2319
2564
|
}
|
|
2320
2565
|
});
|
|
2321
|
-
(_b = this.selection) == null ? void 0 : _b.
|
|
2322
|
-
|
|
2566
|
+
(_b = this.selection) == null ? void 0 : _b.onMarqueeEnd(({ documentId, pageIndex, rect }) => {
|
|
2567
|
+
const docState = this.state.documents[documentId];
|
|
2568
|
+
if (!docState) return;
|
|
2569
|
+
const pageAnnotations = (docState.pages[pageIndex] ?? []).map((uid) => docState.byUid[uid]).filter((ta) => ta !== void 0).filter((ta) => !isLink(ta));
|
|
2570
|
+
const selectedIds = pageAnnotations.filter((ta) => rectsIntersect(rect, ta.object.rect)).map((ta) => ta.object.id);
|
|
2571
|
+
if (selectedIds.length > 0) {
|
|
2572
|
+
const expandedIds = /* @__PURE__ */ new Set();
|
|
2573
|
+
for (const id of selectedIds) {
|
|
2574
|
+
if (this.isInGroupMethod(id, documentId)) {
|
|
2575
|
+
const members = this.getGroupMembersMethod(id, documentId);
|
|
2576
|
+
for (const member of members) {
|
|
2577
|
+
expandedIds.add(member.object.id);
|
|
2578
|
+
}
|
|
2579
|
+
} else {
|
|
2580
|
+
expandedIds.add(id);
|
|
2581
|
+
}
|
|
2582
|
+
}
|
|
2583
|
+
this.setSelectionMethod([...expandedIds], documentId);
|
|
2584
|
+
}
|
|
2585
|
+
});
|
|
2586
|
+
(_c = this.selection) == null ? void 0 : _c.onEndSelection(({ documentId }) => {
|
|
2587
|
+
var _a2, _b2, _c2;
|
|
2323
2588
|
if (!this.checkPermission(documentId, PdfPermissionFlag.ModifyAnnotations)) {
|
|
2324
2589
|
return;
|
|
2325
2590
|
}
|
|
@@ -2355,7 +2620,7 @@ const _AnnotationPlugin = class _AnnotationPlugin extends BasePlugin {
|
|
|
2355
2620
|
}
|
|
2356
2621
|
}, ignore);
|
|
2357
2622
|
}
|
|
2358
|
-
(
|
|
2623
|
+
(_c2 = this.selection) == null ? void 0 : _c2.clear();
|
|
2359
2624
|
});
|
|
2360
2625
|
}
|
|
2361
2626
|
registerInteractionForTool(tool) {
|
|
@@ -2375,15 +2640,33 @@ const _AnnotationPlugin = class _AnnotationPlugin extends BasePlugin {
|
|
|
2375
2640
|
getState: () => this.getDocumentState(),
|
|
2376
2641
|
getPageAnnotations: (options) => this.getPageAnnotations(options),
|
|
2377
2642
|
getSelectedAnnotation: () => this.getSelectedAnnotation(),
|
|
2643
|
+
getSelectedAnnotations: () => this.getSelectedAnnotationsMethod(),
|
|
2644
|
+
getSelectedAnnotationIds: () => this.getSelectedAnnotationIdsMethod(),
|
|
2378
2645
|
getAnnotationById: (id) => this.getAnnotationById(id),
|
|
2379
2646
|
selectAnnotation: (pageIndex, id) => this.selectAnnotation(pageIndex, id),
|
|
2647
|
+
toggleSelection: (pageIndex, id) => this.toggleSelectionMethod(pageIndex, id),
|
|
2648
|
+
addToSelection: (pageIndex, id) => this.addToSelectionMethod(pageIndex, id),
|
|
2649
|
+
removeFromSelection: (id) => this.removeFromSelectionMethod(id),
|
|
2650
|
+
setSelection: (ids) => this.setSelectionMethod(ids),
|
|
2380
2651
|
deselectAnnotation: () => this.deselectAnnotation(),
|
|
2381
2652
|
importAnnotations: (items) => this.importAnnotations(items),
|
|
2382
2653
|
createAnnotation: (pageIndex, anno, ctx) => this.createAnnotation(pageIndex, anno, ctx),
|
|
2383
2654
|
updateAnnotation: (pageIndex, id, patch) => this.updateAnnotation(pageIndex, id, patch),
|
|
2655
|
+
updateAnnotations: (patches) => this.updateAnnotationsMethod(patches),
|
|
2384
2656
|
deleteAnnotation: (pageIndex, id) => this.deleteAnnotation(pageIndex, id),
|
|
2657
|
+
deleteAnnotations: (annotations, documentId) => this.deleteAnnotationsMethod(annotations, documentId),
|
|
2658
|
+
purgeAnnotation: (pageIndex, id, documentId) => this.purgeAnnotationMethod(pageIndex, id, documentId),
|
|
2385
2659
|
renderAnnotation: (options) => this.renderAnnotation(options),
|
|
2386
2660
|
commit: () => this.commit(),
|
|
2661
|
+
// Attached links (IRT link children)
|
|
2662
|
+
getAttachedLinks: (id, documentId) => this.getAttachedLinksMethod(id, documentId),
|
|
2663
|
+
hasAttachedLinks: (id, documentId) => this.hasAttachedLinksMethod(id, documentId),
|
|
2664
|
+
deleteAttachedLinks: (id, documentId) => this.deleteAttachedLinksMethod(id, documentId),
|
|
2665
|
+
// Annotation grouping (RT = Group)
|
|
2666
|
+
groupAnnotations: (documentId) => this.groupAnnotationsMethod(documentId),
|
|
2667
|
+
ungroupAnnotations: (id, documentId) => this.ungroupAnnotationsMethod(id, documentId),
|
|
2668
|
+
getGroupMembers: (id, documentId) => this.getGroupMembersMethod(id, documentId),
|
|
2669
|
+
isInGroup: (id, documentId) => this.isInGroupMethod(id, documentId),
|
|
2387
2670
|
// Document-scoped operations
|
|
2388
2671
|
forDocument: (documentId) => this.createAnnotationScope(documentId),
|
|
2389
2672
|
// Global operations
|
|
@@ -2414,8 +2697,14 @@ const _AnnotationPlugin = class _AnnotationPlugin extends BasePlugin {
|
|
|
2414
2697
|
getState: () => this.getDocumentState(documentId),
|
|
2415
2698
|
getPageAnnotations: (options) => this.getPageAnnotations(options, documentId),
|
|
2416
2699
|
getSelectedAnnotation: () => this.getSelectedAnnotation(documentId),
|
|
2700
|
+
getSelectedAnnotations: () => this.getSelectedAnnotationsMethod(documentId),
|
|
2701
|
+
getSelectedAnnotationIds: () => this.getSelectedAnnotationIdsMethod(documentId),
|
|
2417
2702
|
getAnnotationById: (id) => this.getAnnotationById(id, documentId),
|
|
2418
2703
|
selectAnnotation: (pageIndex, id) => this.selectAnnotation(pageIndex, id, documentId),
|
|
2704
|
+
toggleSelection: (pageIndex, id) => this.toggleSelectionMethod(pageIndex, id, documentId),
|
|
2705
|
+
addToSelection: (pageIndex, id) => this.addToSelectionMethod(pageIndex, id, documentId),
|
|
2706
|
+
removeFromSelection: (id) => this.removeFromSelectionMethod(id, documentId),
|
|
2707
|
+
setSelection: (ids) => this.setSelectionMethod(ids, documentId),
|
|
2419
2708
|
deselectAnnotation: () => this.deselectAnnotation(documentId),
|
|
2420
2709
|
getActiveTool: () => this.getActiveTool(documentId),
|
|
2421
2710
|
setActiveTool: (toolId) => this.setActiveTool(toolId, documentId),
|
|
@@ -2423,9 +2712,20 @@ const _AnnotationPlugin = class _AnnotationPlugin extends BasePlugin {
|
|
|
2423
2712
|
importAnnotations: (items) => this.importAnnotations(items, documentId),
|
|
2424
2713
|
createAnnotation: (pageIndex, anno, ctx) => this.createAnnotation(pageIndex, anno, ctx, documentId),
|
|
2425
2714
|
updateAnnotation: (pageIndex, id, patch) => this.updateAnnotation(pageIndex, id, patch, documentId),
|
|
2715
|
+
updateAnnotations: (patches) => this.updateAnnotationsMethod(patches, documentId),
|
|
2426
2716
|
deleteAnnotation: (pageIndex, id) => this.deleteAnnotation(pageIndex, id, documentId),
|
|
2717
|
+
deleteAnnotations: (annotations) => this.deleteAnnotationsMethod(annotations, documentId),
|
|
2718
|
+
purgeAnnotation: (pageIndex, id) => this.purgeAnnotationMethod(pageIndex, id, documentId),
|
|
2427
2719
|
renderAnnotation: (options) => this.renderAnnotation(options, documentId),
|
|
2428
2720
|
commit: () => this.commit(documentId),
|
|
2721
|
+
getAttachedLinks: (id) => this.getAttachedLinksMethod(id, documentId),
|
|
2722
|
+
hasAttachedLinks: (id) => this.hasAttachedLinksMethod(id, documentId),
|
|
2723
|
+
deleteAttachedLinks: (id) => this.deleteAttachedLinksMethod(id, documentId),
|
|
2724
|
+
groupAnnotations: () => this.groupAnnotationsMethod(documentId),
|
|
2725
|
+
ungroupAnnotations: (id) => this.ungroupAnnotationsMethod(id, documentId),
|
|
2726
|
+
getGroupMembers: (id) => this.getGroupMembersMethod(id, documentId),
|
|
2727
|
+
isInGroup: (id) => this.isInGroupMethod(id, documentId),
|
|
2728
|
+
getGroupingAction: () => this.getGroupingActionMethod(documentId),
|
|
2429
2729
|
onStateChange: (listener) => this.state$.on((event) => {
|
|
2430
2730
|
if (event.documentId === documentId) listener(event.state);
|
|
2431
2731
|
}),
|
|
@@ -2658,7 +2958,8 @@ const _AnnotationPlugin = class _AnnotationPlugin extends BasePlugin {
|
|
|
2658
2958
|
pageIndex,
|
|
2659
2959
|
committed: false
|
|
2660
2960
|
});
|
|
2661
|
-
}
|
|
2961
|
+
},
|
|
2962
|
+
metadata: { annotationIds: [id] }
|
|
2662
2963
|
};
|
|
2663
2964
|
const historyScope = this.history.forDocument(docId);
|
|
2664
2965
|
historyScope.register(command, this.ANNOTATION_HISTORY_TOPIC);
|
|
@@ -2719,7 +3020,8 @@ const _AnnotationPlugin = class _AnnotationPlugin extends BasePlugin {
|
|
|
2719
3020
|
patch: originalPatch,
|
|
2720
3021
|
committed: false
|
|
2721
3022
|
});
|
|
2722
|
-
}
|
|
3023
|
+
},
|
|
3024
|
+
metadata: { annotationIds: [id] }
|
|
2723
3025
|
};
|
|
2724
3026
|
const historyScope = this.history.forDocument(docId);
|
|
2725
3027
|
historyScope.register(command, this.ANNOTATION_HISTORY_TOPIC);
|
|
@@ -2738,7 +3040,26 @@ const _AnnotationPlugin = class _AnnotationPlugin extends BasePlugin {
|
|
|
2738
3040
|
const docState = this.getDocumentState(docId);
|
|
2739
3041
|
const originalAnnotation = (_a = docState.byUid[id]) == null ? void 0 : _a.object;
|
|
2740
3042
|
if (!originalAnnotation) return;
|
|
3043
|
+
const irtChildren = getIRTChildIds(docState, id);
|
|
3044
|
+
const childAnnotations = irtChildren.map((child) => {
|
|
3045
|
+
var _a2;
|
|
3046
|
+
return (_a2 = docState.byUid[child.id]) == null ? void 0 : _a2.object;
|
|
3047
|
+
}).filter((obj) => obj !== void 0);
|
|
2741
3048
|
const execute = () => {
|
|
3049
|
+
var _a2;
|
|
3050
|
+
for (const child of irtChildren) {
|
|
3051
|
+
const childObj = (_a2 = docState.byUid[child.id]) == null ? void 0 : _a2.object;
|
|
3052
|
+
if (childObj) {
|
|
3053
|
+
this.dispatch(deleteAnnotation(docId, child.pageIndex, child.id));
|
|
3054
|
+
this.events$.emit({
|
|
3055
|
+
type: "delete",
|
|
3056
|
+
documentId: docId,
|
|
3057
|
+
annotation: childObj,
|
|
3058
|
+
pageIndex: child.pageIndex,
|
|
3059
|
+
committed: false
|
|
3060
|
+
});
|
|
3061
|
+
}
|
|
3062
|
+
}
|
|
2742
3063
|
this.dispatch(deselectAnnotation(docId));
|
|
2743
3064
|
this.dispatch(deleteAnnotation(docId, pageIndex, id));
|
|
2744
3065
|
this.events$.emit({
|
|
@@ -2765,19 +3086,709 @@ const _AnnotationPlugin = class _AnnotationPlugin extends BasePlugin {
|
|
|
2765
3086
|
pageIndex,
|
|
2766
3087
|
committed: false
|
|
2767
3088
|
});
|
|
2768
|
-
|
|
3089
|
+
for (const childObj of childAnnotations) {
|
|
3090
|
+
this.dispatch(createAnnotation(docId, childObj.pageIndex, childObj));
|
|
3091
|
+
this.events$.emit({
|
|
3092
|
+
type: "create",
|
|
3093
|
+
documentId: docId,
|
|
3094
|
+
annotation: childObj,
|
|
3095
|
+
pageIndex: childObj.pageIndex,
|
|
3096
|
+
committed: false
|
|
3097
|
+
});
|
|
3098
|
+
}
|
|
3099
|
+
},
|
|
3100
|
+
metadata: { annotationIds: [id, ...irtChildren.map((c) => c.id)] }
|
|
2769
3101
|
};
|
|
2770
3102
|
const historyScope = this.history.forDocument(docId);
|
|
2771
3103
|
historyScope.register(command, this.ANNOTATION_HISTORY_TOPIC);
|
|
2772
3104
|
}
|
|
3105
|
+
deleteAnnotationsMethod(annotations, documentId) {
|
|
3106
|
+
for (const { pageIndex, id } of annotations) {
|
|
3107
|
+
this.deleteAnnotation(pageIndex, id, documentId);
|
|
3108
|
+
}
|
|
3109
|
+
}
|
|
3110
|
+
purgeAnnotationMethod(pageIndex, id, documentId) {
|
|
3111
|
+
const docId = documentId ?? this.getActiveDocumentId();
|
|
3112
|
+
this.dispatch(purgeAnnotation(docId, pageIndex, id));
|
|
3113
|
+
}
|
|
2773
3114
|
selectAnnotation(pageIndex, id, documentId) {
|
|
2774
3115
|
const docId = documentId ?? this.getActiveDocumentId();
|
|
2775
|
-
this.
|
|
3116
|
+
if (this.isInGroupMethod(id, docId)) {
|
|
3117
|
+
const members = this.getGroupMembersMethod(id, docId);
|
|
3118
|
+
const memberIds = members.map((m) => m.object.id);
|
|
3119
|
+
this.dispatch(setSelection(docId, memberIds));
|
|
3120
|
+
} else {
|
|
3121
|
+
this.dispatch(selectAnnotation(docId, pageIndex, id));
|
|
3122
|
+
}
|
|
2776
3123
|
}
|
|
2777
3124
|
deselectAnnotation(documentId) {
|
|
2778
3125
|
const docId = documentId ?? this.getActiveDocumentId();
|
|
2779
3126
|
this.dispatch(deselectAnnotation(docId));
|
|
2780
3127
|
}
|
|
3128
|
+
// ─────────────────────────────────────────────────────────
|
|
3129
|
+
// Multi-Select Methods
|
|
3130
|
+
// ─────────────────────────────────────────────────────────
|
|
3131
|
+
getSelectedAnnotationsMethod(documentId) {
|
|
3132
|
+
return getSelectedAnnotations(this.getDocumentState(documentId));
|
|
3133
|
+
}
|
|
3134
|
+
getSelectedAnnotationIdsMethod(documentId) {
|
|
3135
|
+
return getSelectedAnnotationIds(this.getDocumentState(documentId));
|
|
3136
|
+
}
|
|
3137
|
+
toggleSelectionMethod(pageIndex, id, documentId) {
|
|
3138
|
+
const docId = documentId ?? this.getActiveDocumentId();
|
|
3139
|
+
const docState = this.getDocumentState(docId);
|
|
3140
|
+
if (docState.selectedUids.includes(id)) {
|
|
3141
|
+
this.dispatch(removeFromSelection(docId, id));
|
|
3142
|
+
} else {
|
|
3143
|
+
if (this.isInGroupMethod(id, docId)) {
|
|
3144
|
+
const members = this.getGroupMembersMethod(id, docId);
|
|
3145
|
+
for (const member of members) {
|
|
3146
|
+
if (!docState.selectedUids.includes(member.object.id)) {
|
|
3147
|
+
this.dispatch(addToSelection(docId, member.object.pageIndex, member.object.id));
|
|
3148
|
+
}
|
|
3149
|
+
}
|
|
3150
|
+
} else {
|
|
3151
|
+
this.dispatch(addToSelection(docId, pageIndex, id));
|
|
3152
|
+
}
|
|
3153
|
+
}
|
|
3154
|
+
}
|
|
3155
|
+
addToSelectionMethod(pageIndex, id, documentId) {
|
|
3156
|
+
const docId = documentId ?? this.getActiveDocumentId();
|
|
3157
|
+
this.dispatch(addToSelection(docId, pageIndex, id));
|
|
3158
|
+
}
|
|
3159
|
+
removeFromSelectionMethod(id, documentId) {
|
|
3160
|
+
const docId = documentId ?? this.getActiveDocumentId();
|
|
3161
|
+
this.dispatch(removeFromSelection(docId, id));
|
|
3162
|
+
}
|
|
3163
|
+
setSelectionMethod(ids, documentId) {
|
|
3164
|
+
const docId = documentId ?? this.getActiveDocumentId();
|
|
3165
|
+
this.dispatch(setSelection(docId, ids));
|
|
3166
|
+
}
|
|
3167
|
+
// ─────────────────────────────────────────────────────────
|
|
3168
|
+
// Attached Links Methods
|
|
3169
|
+
// ─────────────────────────────────────────────────────────
|
|
3170
|
+
getAttachedLinksMethod(annotationId, documentId) {
|
|
3171
|
+
return getAttachedLinks(this.getDocumentState(documentId), annotationId);
|
|
3172
|
+
}
|
|
3173
|
+
hasAttachedLinksMethod(annotationId, documentId) {
|
|
3174
|
+
return this.getAttachedLinksMethod(annotationId, documentId).length > 0;
|
|
3175
|
+
}
|
|
3176
|
+
deleteAttachedLinksMethod(annotationId, documentId) {
|
|
3177
|
+
const links = this.getAttachedLinksMethod(annotationId, documentId);
|
|
3178
|
+
for (const link of links) {
|
|
3179
|
+
this.deleteAnnotation(link.object.pageIndex, link.object.id, documentId);
|
|
3180
|
+
}
|
|
3181
|
+
}
|
|
3182
|
+
// ─────────────────────────────────────────────────────────
|
|
3183
|
+
// Annotation Grouping Methods
|
|
3184
|
+
// ─────────────────────────────────────────────────────────
|
|
3185
|
+
/**
|
|
3186
|
+
* Group the currently selected annotations.
|
|
3187
|
+
* The first selected annotation becomes the group leader.
|
|
3188
|
+
* All other selected annotations get their IRT set to the leader's ID with RT = Group.
|
|
3189
|
+
*/
|
|
3190
|
+
groupAnnotationsMethod(documentId) {
|
|
3191
|
+
const docId = documentId ?? this.getActiveDocumentId();
|
|
3192
|
+
if (!this.checkPermission(docId, PdfPermissionFlag.ModifyAnnotations)) {
|
|
3193
|
+
this.logger.debug(
|
|
3194
|
+
"AnnotationPlugin",
|
|
3195
|
+
"GroupAnnotations",
|
|
3196
|
+
`Cannot group annotations: document ${docId} lacks ModifyAnnotations permission`
|
|
3197
|
+
);
|
|
3198
|
+
return;
|
|
3199
|
+
}
|
|
3200
|
+
const selected = this.getSelectedAnnotationsMethod(docId);
|
|
3201
|
+
if (selected.length < 2) {
|
|
3202
|
+
this.logger.debug(
|
|
3203
|
+
"AnnotationPlugin",
|
|
3204
|
+
"GroupAnnotations",
|
|
3205
|
+
"Need at least 2 annotations to group"
|
|
3206
|
+
);
|
|
3207
|
+
return;
|
|
3208
|
+
}
|
|
3209
|
+
const leader = selected[0];
|
|
3210
|
+
const members = selected.slice(1);
|
|
3211
|
+
const patches = members.map((ta) => ({
|
|
3212
|
+
pageIndex: ta.object.pageIndex,
|
|
3213
|
+
id: ta.object.id,
|
|
3214
|
+
patch: {
|
|
3215
|
+
inReplyToId: leader.object.id,
|
|
3216
|
+
replyType: PdfAnnotationReplyType.Group
|
|
3217
|
+
}
|
|
3218
|
+
}));
|
|
3219
|
+
this.updateAnnotationsMethod(patches, docId);
|
|
3220
|
+
}
|
|
3221
|
+
/**
|
|
3222
|
+
* Ungroup all annotations in the group containing the specified annotation.
|
|
3223
|
+
* Clears IRT and RT from all group members (the leader doesn't have them).
|
|
3224
|
+
*/
|
|
3225
|
+
ungroupAnnotationsMethod(annotationId, documentId) {
|
|
3226
|
+
const docId = documentId ?? this.getActiveDocumentId();
|
|
3227
|
+
if (!this.checkPermission(docId, PdfPermissionFlag.ModifyAnnotations)) {
|
|
3228
|
+
this.logger.debug(
|
|
3229
|
+
"AnnotationPlugin",
|
|
3230
|
+
"UngroupAnnotations",
|
|
3231
|
+
`Cannot ungroup annotations: document ${docId} lacks ModifyAnnotations permission`
|
|
3232
|
+
);
|
|
3233
|
+
return;
|
|
3234
|
+
}
|
|
3235
|
+
const members = this.getGroupMembersMethod(annotationId, docId);
|
|
3236
|
+
const patches = members.filter((ta) => ta.object.inReplyToId && ta.object.replyType === PdfAnnotationReplyType.Group).map((ta) => ({
|
|
3237
|
+
pageIndex: ta.object.pageIndex,
|
|
3238
|
+
id: ta.object.id,
|
|
3239
|
+
patch: {
|
|
3240
|
+
inReplyToId: void 0,
|
|
3241
|
+
replyType: void 0
|
|
3242
|
+
}
|
|
3243
|
+
}));
|
|
3244
|
+
if (patches.length > 0) {
|
|
3245
|
+
this.updateAnnotationsMethod(patches, docId);
|
|
3246
|
+
}
|
|
3247
|
+
}
|
|
3248
|
+
/**
|
|
3249
|
+
* Get all annotations in the same group as the specified annotation.
|
|
3250
|
+
*/
|
|
3251
|
+
getGroupMembersMethod(annotationId, documentId) {
|
|
3252
|
+
return getGroupMembers(this.getDocumentState(documentId), annotationId);
|
|
3253
|
+
}
|
|
3254
|
+
/**
|
|
3255
|
+
* Check if an annotation is part of a group.
|
|
3256
|
+
*/
|
|
3257
|
+
isInGroupMethod(annotationId, documentId) {
|
|
3258
|
+
return isInGroup(this.getDocumentState(documentId), annotationId);
|
|
3259
|
+
}
|
|
3260
|
+
/**
|
|
3261
|
+
* Get the available grouping action for the current selection.
|
|
3262
|
+
*/
|
|
3263
|
+
getGroupingActionMethod(documentId) {
|
|
3264
|
+
return getSelectionGroupingAction(this.getDocumentState(documentId));
|
|
3265
|
+
}
|
|
3266
|
+
// ─────────────────────────────────────────────────────────
|
|
3267
|
+
// Multi-Drag Coordination (Internal API for framework components)
|
|
3268
|
+
// ─────────────────────────────────────────────────────────
|
|
3269
|
+
/**
|
|
3270
|
+
* Compute combined constraints from all selected annotations.
|
|
3271
|
+
* This finds the "weakest link" in each direction - the annotation with the least
|
|
3272
|
+
* room to move determines the group's limit.
|
|
3273
|
+
*/
|
|
3274
|
+
computeCombinedConstraints(annotations) {
|
|
3275
|
+
let maxUp = Infinity;
|
|
3276
|
+
let maxDown = Infinity;
|
|
3277
|
+
let maxLeft = Infinity;
|
|
3278
|
+
let maxRight = Infinity;
|
|
3279
|
+
for (const anno of annotations) {
|
|
3280
|
+
const upLimit = anno.rect.origin.y;
|
|
3281
|
+
const downLimit = anno.pageSize.height - (anno.rect.origin.y + anno.rect.size.height);
|
|
3282
|
+
const leftLimit = anno.rect.origin.x;
|
|
3283
|
+
const rightLimit = anno.pageSize.width - (anno.rect.origin.x + anno.rect.size.width);
|
|
3284
|
+
maxUp = Math.min(maxUp, upLimit);
|
|
3285
|
+
maxDown = Math.min(maxDown, downLimit);
|
|
3286
|
+
maxLeft = Math.min(maxLeft, leftLimit);
|
|
3287
|
+
maxRight = Math.min(maxRight, rightLimit);
|
|
3288
|
+
}
|
|
3289
|
+
if (!isFinite(maxUp)) maxUp = 0;
|
|
3290
|
+
if (!isFinite(maxDown)) maxDown = 0;
|
|
3291
|
+
if (!isFinite(maxLeft)) maxLeft = 0;
|
|
3292
|
+
if (!isFinite(maxRight)) maxRight = 0;
|
|
3293
|
+
return { maxUp, maxDown, maxLeft, maxRight };
|
|
3294
|
+
}
|
|
3295
|
+
/**
|
|
3296
|
+
* Clamp a delta to the combined constraints.
|
|
3297
|
+
* Negative y = moving up, positive y = moving down
|
|
3298
|
+
* Negative x = moving left, positive x = moving right
|
|
3299
|
+
*/
|
|
3300
|
+
clampDelta(rawDelta, constraints) {
|
|
3301
|
+
return {
|
|
3302
|
+
x: Math.max(-constraints.maxLeft, Math.min(constraints.maxRight, rawDelta.x)),
|
|
3303
|
+
y: Math.max(-constraints.maxUp, Math.min(constraints.maxDown, rawDelta.y))
|
|
3304
|
+
};
|
|
3305
|
+
}
|
|
3306
|
+
// ─────────────────────────────────────────────────────────
|
|
3307
|
+
// Unified Drag API (Plugin owns all logic - framework just calls these)
|
|
3308
|
+
// ─────────────────────────────────────────────────────────
|
|
3309
|
+
/**
|
|
3310
|
+
* Start a unified drag operation.
|
|
3311
|
+
* The plugin automatically expands the selection to include attached links.
|
|
3312
|
+
* Framework components should call this instead of building their own logic.
|
|
3313
|
+
*
|
|
3314
|
+
* @param documentId - The document ID
|
|
3315
|
+
* @param options - Drag options (annotationIds and pageSize)
|
|
3316
|
+
*/
|
|
3317
|
+
startDrag(documentId, options) {
|
|
3318
|
+
const { annotationIds, pageSize } = options;
|
|
3319
|
+
const attachedLinkIds = [];
|
|
3320
|
+
for (const id of annotationIds) {
|
|
3321
|
+
const links = this.getAttachedLinksMethod(id, documentId);
|
|
3322
|
+
for (const link of links) {
|
|
3323
|
+
if (!attachedLinkIds.includes(link.object.id)) {
|
|
3324
|
+
attachedLinkIds.push(link.object.id);
|
|
3325
|
+
}
|
|
3326
|
+
}
|
|
3327
|
+
}
|
|
3328
|
+
const allParticipantIds = [...annotationIds, ...attachedLinkIds];
|
|
3329
|
+
const originalRects = /* @__PURE__ */ new Map();
|
|
3330
|
+
const constraints = [];
|
|
3331
|
+
for (const id of allParticipantIds) {
|
|
3332
|
+
const ta = this.getAnnotationById(id, documentId);
|
|
3333
|
+
if (ta) {
|
|
3334
|
+
originalRects.set(id, { ...ta.object.rect });
|
|
3335
|
+
constraints.push({
|
|
3336
|
+
id,
|
|
3337
|
+
rect: ta.object.rect,
|
|
3338
|
+
pageIndex: ta.object.pageIndex,
|
|
3339
|
+
pageSize
|
|
3340
|
+
});
|
|
3341
|
+
}
|
|
3342
|
+
}
|
|
3343
|
+
const combinedConstraints = this.computeCombinedConstraints(constraints);
|
|
3344
|
+
const state = {
|
|
3345
|
+
documentId,
|
|
3346
|
+
isDragging: true,
|
|
3347
|
+
primaryIds: annotationIds,
|
|
3348
|
+
attachedLinkIds,
|
|
3349
|
+
allParticipantIds,
|
|
3350
|
+
originalRects,
|
|
3351
|
+
delta: { x: 0, y: 0 },
|
|
3352
|
+
combinedConstraints
|
|
3353
|
+
};
|
|
3354
|
+
this.unifiedDragStates.set(documentId, state);
|
|
3355
|
+
this.unifiedDrag$.emit({ documentId, type: "start", state, previewPatches: {} });
|
|
3356
|
+
}
|
|
3357
|
+
/**
|
|
3358
|
+
* Compute preview patches for all drag participants.
|
|
3359
|
+
* Uses transformAnnotation to properly handle vertices, inkList, etc.
|
|
3360
|
+
*/
|
|
3361
|
+
computeDragPreviewPatches(state, documentId) {
|
|
3362
|
+
const previewPatches = {};
|
|
3363
|
+
for (const id of state.allParticipantIds) {
|
|
3364
|
+
const ta = this.getAnnotationById(id, documentId);
|
|
3365
|
+
if (!ta) continue;
|
|
3366
|
+
const originalRect = state.originalRects.get(id);
|
|
3367
|
+
if (!originalRect) continue;
|
|
3368
|
+
const newRect = {
|
|
3369
|
+
...originalRect,
|
|
3370
|
+
origin: {
|
|
3371
|
+
x: originalRect.origin.x + state.delta.x,
|
|
3372
|
+
y: originalRect.origin.y + state.delta.y
|
|
3373
|
+
}
|
|
3374
|
+
};
|
|
3375
|
+
previewPatches[id] = this.transformAnnotation(ta.object, {
|
|
3376
|
+
type: "move",
|
|
3377
|
+
changes: { rect: newRect }
|
|
3378
|
+
});
|
|
3379
|
+
}
|
|
3380
|
+
return previewPatches;
|
|
3381
|
+
}
|
|
3382
|
+
/**
|
|
3383
|
+
* Update the drag delta during a unified drag operation.
|
|
3384
|
+
* Returns the clamped delta synchronously for the caller's preview.
|
|
3385
|
+
*
|
|
3386
|
+
* @param documentId - The document ID
|
|
3387
|
+
* @param rawDelta - The unconstrained delta from the drag gesture
|
|
3388
|
+
* @returns The clamped delta
|
|
3389
|
+
*/
|
|
3390
|
+
updateDrag(documentId, rawDelta) {
|
|
3391
|
+
const state = this.unifiedDragStates.get(documentId);
|
|
3392
|
+
if (!(state == null ? void 0 : state.isDragging)) {
|
|
3393
|
+
return { x: 0, y: 0 };
|
|
3394
|
+
}
|
|
3395
|
+
const clampedDelta = this.clampDelta(rawDelta, state.combinedConstraints);
|
|
3396
|
+
const newState = {
|
|
3397
|
+
...state,
|
|
3398
|
+
delta: clampedDelta
|
|
3399
|
+
};
|
|
3400
|
+
this.unifiedDragStates.set(documentId, newState);
|
|
3401
|
+
const previewPatches = this.computeDragPreviewPatches(newState, documentId);
|
|
3402
|
+
this.unifiedDrag$.emit({ documentId, type: "update", state: newState, previewPatches });
|
|
3403
|
+
return clampedDelta;
|
|
3404
|
+
}
|
|
3405
|
+
/**
|
|
3406
|
+
* Commit the drag - plugin builds and applies ALL patches.
|
|
3407
|
+
* This is the key method that centralizes patch building in the plugin.
|
|
3408
|
+
*
|
|
3409
|
+
* @param documentId - The document ID
|
|
3410
|
+
*/
|
|
3411
|
+
commitDrag(documentId) {
|
|
3412
|
+
const state = this.unifiedDragStates.get(documentId);
|
|
3413
|
+
if (!state) return;
|
|
3414
|
+
const finalDelta = state.delta;
|
|
3415
|
+
if (finalDelta.x !== 0 || finalDelta.y !== 0) {
|
|
3416
|
+
const patches = [];
|
|
3417
|
+
for (const id of state.allParticipantIds) {
|
|
3418
|
+
const ta = this.getAnnotationById(id, documentId);
|
|
3419
|
+
if (!ta) continue;
|
|
3420
|
+
const originalRect = state.originalRects.get(id) ?? ta.object.rect;
|
|
3421
|
+
const newRect = {
|
|
3422
|
+
...originalRect,
|
|
3423
|
+
origin: {
|
|
3424
|
+
x: originalRect.origin.x + finalDelta.x,
|
|
3425
|
+
y: originalRect.origin.y + finalDelta.y
|
|
3426
|
+
}
|
|
3427
|
+
};
|
|
3428
|
+
const patch = this.transformAnnotation(ta.object, {
|
|
3429
|
+
type: "move",
|
|
3430
|
+
changes: { rect: newRect }
|
|
3431
|
+
});
|
|
3432
|
+
patches.push({ pageIndex: ta.object.pageIndex, id, patch });
|
|
3433
|
+
}
|
|
3434
|
+
if (patches.length > 0) {
|
|
3435
|
+
this.updateAnnotationsMethod(patches, documentId);
|
|
3436
|
+
}
|
|
3437
|
+
}
|
|
3438
|
+
const endPatches = this.computeDragPreviewPatches(state, documentId);
|
|
3439
|
+
this.unifiedDrag$.emit({
|
|
3440
|
+
documentId,
|
|
3441
|
+
type: "end",
|
|
3442
|
+
state: { ...state, isDragging: false },
|
|
3443
|
+
previewPatches: endPatches
|
|
3444
|
+
});
|
|
3445
|
+
this.unifiedDragStates.delete(documentId);
|
|
3446
|
+
}
|
|
3447
|
+
/**
|
|
3448
|
+
* Cancel the drag without committing.
|
|
3449
|
+
*
|
|
3450
|
+
* @param documentId - The document ID
|
|
3451
|
+
*/
|
|
3452
|
+
cancelDrag(documentId) {
|
|
3453
|
+
const state = this.unifiedDragStates.get(documentId);
|
|
3454
|
+
if (!state) return;
|
|
3455
|
+
this.unifiedDrag$.emit({
|
|
3456
|
+
documentId,
|
|
3457
|
+
type: "cancel",
|
|
3458
|
+
state: { ...state, isDragging: false, delta: { x: 0, y: 0 } },
|
|
3459
|
+
previewPatches: {}
|
|
3460
|
+
});
|
|
3461
|
+
this.unifiedDragStates.delete(documentId);
|
|
3462
|
+
}
|
|
3463
|
+
/**
|
|
3464
|
+
* Get the current unified drag state for a document.
|
|
3465
|
+
*/
|
|
3466
|
+
getDragState(documentId) {
|
|
3467
|
+
return this.unifiedDragStates.get(documentId) ?? null;
|
|
3468
|
+
}
|
|
3469
|
+
/**
|
|
3470
|
+
* Subscribe to unified drag state changes.
|
|
3471
|
+
* Framework components use this for preview updates.
|
|
3472
|
+
*/
|
|
3473
|
+
get onDragChange() {
|
|
3474
|
+
return this.unifiedDrag$.on;
|
|
3475
|
+
}
|
|
3476
|
+
// ─────────────────────────────────────────────────────────
|
|
3477
|
+
// Unified Resize API (Plugin owns all logic - framework just calls these)
|
|
3478
|
+
// ─────────────────────────────────────────────────────────
|
|
3479
|
+
/**
|
|
3480
|
+
* Compute the union bounding box of multiple rects.
|
|
3481
|
+
*/
|
|
3482
|
+
computeUnifiedGroupBoundingBox(rects) {
|
|
3483
|
+
if (rects.length === 0) {
|
|
3484
|
+
return { origin: { x: 0, y: 0 }, size: { width: 0, height: 0 } };
|
|
3485
|
+
}
|
|
3486
|
+
let minX = Infinity;
|
|
3487
|
+
let minY = Infinity;
|
|
3488
|
+
let maxX = -Infinity;
|
|
3489
|
+
let maxY = -Infinity;
|
|
3490
|
+
for (const rect of rects) {
|
|
3491
|
+
minX = Math.min(minX, rect.origin.x);
|
|
3492
|
+
minY = Math.min(minY, rect.origin.y);
|
|
3493
|
+
maxX = Math.max(maxX, rect.origin.x + rect.size.width);
|
|
3494
|
+
maxY = Math.max(maxY, rect.origin.y + rect.size.height);
|
|
3495
|
+
}
|
|
3496
|
+
return {
|
|
3497
|
+
origin: { x: minX, y: minY },
|
|
3498
|
+
size: { width: maxX - minX, height: maxY - minY }
|
|
3499
|
+
};
|
|
3500
|
+
}
|
|
3501
|
+
/**
|
|
3502
|
+
* Compute relative positions for annotations within a group bounding box.
|
|
3503
|
+
*/
|
|
3504
|
+
computeUnifiedRelativePositions(annotations, groupBox) {
|
|
3505
|
+
return annotations.map((anno) => ({
|
|
3506
|
+
id: anno.id,
|
|
3507
|
+
originalRect: anno.rect,
|
|
3508
|
+
pageIndex: anno.pageIndex,
|
|
3509
|
+
isAttachedLink: anno.isAttachedLink,
|
|
3510
|
+
parentId: anno.parentId,
|
|
3511
|
+
relativeX: groupBox.size.width > 0 ? (anno.rect.origin.x - groupBox.origin.x) / groupBox.size.width : 0,
|
|
3512
|
+
relativeY: groupBox.size.height > 0 ? (anno.rect.origin.y - groupBox.origin.y) / groupBox.size.height : 0,
|
|
3513
|
+
relativeWidth: groupBox.size.width > 0 ? anno.rect.size.width / groupBox.size.width : 1,
|
|
3514
|
+
relativeHeight: groupBox.size.height > 0 ? anno.rect.size.height / groupBox.size.height : 1
|
|
3515
|
+
}));
|
|
3516
|
+
}
|
|
3517
|
+
/**
|
|
3518
|
+
* Compute new rects for all annotations based on the new group bounding box.
|
|
3519
|
+
*/
|
|
3520
|
+
computeUnifiedResizedRects(participatingAnnotations, newGroupBox, minSize = 10) {
|
|
3521
|
+
const result = /* @__PURE__ */ new Map();
|
|
3522
|
+
for (const anno of participatingAnnotations) {
|
|
3523
|
+
const newWidth = Math.max(minSize, anno.relativeWidth * newGroupBox.size.width);
|
|
3524
|
+
const newHeight = Math.max(minSize, anno.relativeHeight * newGroupBox.size.height);
|
|
3525
|
+
result.set(anno.id, {
|
|
3526
|
+
origin: {
|
|
3527
|
+
x: newGroupBox.origin.x + anno.relativeX * newGroupBox.size.width,
|
|
3528
|
+
y: newGroupBox.origin.y + anno.relativeY * newGroupBox.size.height
|
|
3529
|
+
},
|
|
3530
|
+
size: {
|
|
3531
|
+
width: newWidth,
|
|
3532
|
+
height: newHeight
|
|
3533
|
+
}
|
|
3534
|
+
});
|
|
3535
|
+
}
|
|
3536
|
+
return result;
|
|
3537
|
+
}
|
|
3538
|
+
/**
|
|
3539
|
+
* Compute preview patches for all resize participants.
|
|
3540
|
+
* Uses transformAnnotation to properly handle vertices, inkList, etc.
|
|
3541
|
+
*/
|
|
3542
|
+
computeResizePreviewPatches(computedRects, documentId) {
|
|
3543
|
+
const previewPatches = {};
|
|
3544
|
+
for (const [id, newRect] of computedRects) {
|
|
3545
|
+
const ta = this.getAnnotationById(id, documentId);
|
|
3546
|
+
if (!ta) continue;
|
|
3547
|
+
previewPatches[id] = this.transformAnnotation(ta.object, {
|
|
3548
|
+
type: "resize",
|
|
3549
|
+
changes: { rect: newRect }
|
|
3550
|
+
});
|
|
3551
|
+
}
|
|
3552
|
+
return previewPatches;
|
|
3553
|
+
}
|
|
3554
|
+
/**
|
|
3555
|
+
* Start a unified resize operation.
|
|
3556
|
+
* The plugin automatically expands the selection to include attached links.
|
|
3557
|
+
*
|
|
3558
|
+
* @param documentId - The document ID
|
|
3559
|
+
* @param options - Resize options
|
|
3560
|
+
*/
|
|
3561
|
+
startResize(documentId, options) {
|
|
3562
|
+
const { annotationIds, pageSize, resizeHandle } = options;
|
|
3563
|
+
const attachedLinkIds = [];
|
|
3564
|
+
const annotationsWithLinks = [];
|
|
3565
|
+
for (const id of annotationIds) {
|
|
3566
|
+
const ta = this.getAnnotationById(id, documentId);
|
|
3567
|
+
if (ta) {
|
|
3568
|
+
annotationsWithLinks.push({
|
|
3569
|
+
id,
|
|
3570
|
+
rect: ta.object.rect,
|
|
3571
|
+
pageIndex: ta.object.pageIndex,
|
|
3572
|
+
isAttachedLink: false
|
|
3573
|
+
});
|
|
3574
|
+
const links = this.getAttachedLinksMethod(id, documentId);
|
|
3575
|
+
for (const link of links) {
|
|
3576
|
+
if (!attachedLinkIds.includes(link.object.id)) {
|
|
3577
|
+
attachedLinkIds.push(link.object.id);
|
|
3578
|
+
annotationsWithLinks.push({
|
|
3579
|
+
id: link.object.id,
|
|
3580
|
+
rect: link.object.rect,
|
|
3581
|
+
pageIndex: link.object.pageIndex,
|
|
3582
|
+
isAttachedLink: true,
|
|
3583
|
+
parentId: id
|
|
3584
|
+
});
|
|
3585
|
+
}
|
|
3586
|
+
}
|
|
3587
|
+
}
|
|
3588
|
+
}
|
|
3589
|
+
const allParticipantIds = [...annotationIds, ...attachedLinkIds];
|
|
3590
|
+
const rects = annotationsWithLinks.map((a) => a.rect);
|
|
3591
|
+
const groupBox = this.computeUnifiedGroupBoundingBox(rects);
|
|
3592
|
+
const participatingAnnotations = this.computeUnifiedRelativePositions(
|
|
3593
|
+
annotationsWithLinks,
|
|
3594
|
+
groupBox
|
|
3595
|
+
);
|
|
3596
|
+
const computedRects = this.computeUnifiedResizedRects(participatingAnnotations, groupBox);
|
|
3597
|
+
const state = {
|
|
3598
|
+
documentId,
|
|
3599
|
+
isResizing: true,
|
|
3600
|
+
primaryIds: annotationIds,
|
|
3601
|
+
attachedLinkIds,
|
|
3602
|
+
allParticipantIds,
|
|
3603
|
+
originalGroupBox: groupBox,
|
|
3604
|
+
currentGroupBox: groupBox,
|
|
3605
|
+
participatingAnnotations,
|
|
3606
|
+
resizeHandle,
|
|
3607
|
+
computedRects
|
|
3608
|
+
};
|
|
3609
|
+
this.unifiedResizeStates.set(documentId, state);
|
|
3610
|
+
const startPatches = this.computeResizePreviewPatches(computedRects, documentId);
|
|
3611
|
+
this.unifiedResize$.emit({
|
|
3612
|
+
documentId,
|
|
3613
|
+
type: "start",
|
|
3614
|
+
state,
|
|
3615
|
+
computedRects: Object.fromEntries(computedRects),
|
|
3616
|
+
previewPatches: startPatches
|
|
3617
|
+
});
|
|
3618
|
+
}
|
|
3619
|
+
/**
|
|
3620
|
+
* Update the resize with a new group bounding box.
|
|
3621
|
+
* Returns the computed rects synchronously for immediate preview use.
|
|
3622
|
+
*
|
|
3623
|
+
* @param documentId - The document ID
|
|
3624
|
+
* @param newGroupBox - The new group bounding box
|
|
3625
|
+
* @returns Record of annotation ID to new rect
|
|
3626
|
+
*/
|
|
3627
|
+
updateResize(documentId, newGroupBox) {
|
|
3628
|
+
const state = this.unifiedResizeStates.get(documentId);
|
|
3629
|
+
if (!(state == null ? void 0 : state.isResizing)) {
|
|
3630
|
+
return {};
|
|
3631
|
+
}
|
|
3632
|
+
const computedRects = this.computeUnifiedResizedRects(
|
|
3633
|
+
state.participatingAnnotations,
|
|
3634
|
+
newGroupBox
|
|
3635
|
+
);
|
|
3636
|
+
const newState = {
|
|
3637
|
+
...state,
|
|
3638
|
+
currentGroupBox: newGroupBox,
|
|
3639
|
+
computedRects
|
|
3640
|
+
};
|
|
3641
|
+
this.unifiedResizeStates.set(documentId, newState);
|
|
3642
|
+
const computedRectsObj = Object.fromEntries(computedRects);
|
|
3643
|
+
const previewPatches = this.computeResizePreviewPatches(computedRects, documentId);
|
|
3644
|
+
this.unifiedResize$.emit({
|
|
3645
|
+
documentId,
|
|
3646
|
+
type: "update",
|
|
3647
|
+
state: newState,
|
|
3648
|
+
computedRects: computedRectsObj,
|
|
3649
|
+
previewPatches
|
|
3650
|
+
});
|
|
3651
|
+
return computedRectsObj;
|
|
3652
|
+
}
|
|
3653
|
+
/**
|
|
3654
|
+
* Commit the resize - plugin builds and applies ALL patches.
|
|
3655
|
+
*
|
|
3656
|
+
* @param documentId - The document ID
|
|
3657
|
+
*/
|
|
3658
|
+
commitResize(documentId) {
|
|
3659
|
+
const state = this.unifiedResizeStates.get(documentId);
|
|
3660
|
+
if (!state) return;
|
|
3661
|
+
const computedRects = this.computeUnifiedResizedRects(
|
|
3662
|
+
state.participatingAnnotations,
|
|
3663
|
+
state.currentGroupBox
|
|
3664
|
+
);
|
|
3665
|
+
const patches = [];
|
|
3666
|
+
for (const [id, newRect] of computedRects) {
|
|
3667
|
+
const ta = this.getAnnotationById(id, documentId);
|
|
3668
|
+
if (!ta) continue;
|
|
3669
|
+
const patch = this.transformAnnotation(ta.object, {
|
|
3670
|
+
type: "resize",
|
|
3671
|
+
changes: { rect: newRect }
|
|
3672
|
+
});
|
|
3673
|
+
patches.push({ pageIndex: ta.object.pageIndex, id, patch });
|
|
3674
|
+
}
|
|
3675
|
+
if (patches.length > 0) {
|
|
3676
|
+
this.updateAnnotationsMethod(patches, documentId);
|
|
3677
|
+
}
|
|
3678
|
+
const endPatches = this.computeResizePreviewPatches(computedRects, documentId);
|
|
3679
|
+
this.unifiedResize$.emit({
|
|
3680
|
+
documentId,
|
|
3681
|
+
type: "end",
|
|
3682
|
+
state: { ...state, isResizing: false },
|
|
3683
|
+
computedRects: Object.fromEntries(computedRects),
|
|
3684
|
+
previewPatches: endPatches
|
|
3685
|
+
});
|
|
3686
|
+
this.unifiedResizeStates.delete(documentId);
|
|
3687
|
+
}
|
|
3688
|
+
/**
|
|
3689
|
+
* Cancel the resize without committing.
|
|
3690
|
+
*
|
|
3691
|
+
* @param documentId - The document ID
|
|
3692
|
+
*/
|
|
3693
|
+
cancelResize(documentId) {
|
|
3694
|
+
const state = this.unifiedResizeStates.get(documentId);
|
|
3695
|
+
if (!state) return;
|
|
3696
|
+
const originalRects = this.computeUnifiedResizedRects(
|
|
3697
|
+
state.participatingAnnotations,
|
|
3698
|
+
state.originalGroupBox
|
|
3699
|
+
);
|
|
3700
|
+
this.unifiedResize$.emit({
|
|
3701
|
+
documentId,
|
|
3702
|
+
type: "cancel",
|
|
3703
|
+
state: { ...state, isResizing: false, currentGroupBox: state.originalGroupBox },
|
|
3704
|
+
computedRects: Object.fromEntries(originalRects),
|
|
3705
|
+
previewPatches: {}
|
|
3706
|
+
});
|
|
3707
|
+
this.unifiedResizeStates.delete(documentId);
|
|
3708
|
+
}
|
|
3709
|
+
/**
|
|
3710
|
+
* Get the current unified resize state for a document.
|
|
3711
|
+
*/
|
|
3712
|
+
getResizeState(documentId) {
|
|
3713
|
+
return this.unifiedResizeStates.get(documentId) ?? null;
|
|
3714
|
+
}
|
|
3715
|
+
/**
|
|
3716
|
+
* Subscribe to unified resize state changes.
|
|
3717
|
+
* Framework components use this for preview updates.
|
|
3718
|
+
*/
|
|
3719
|
+
get onResizeChange() {
|
|
3720
|
+
return this.unifiedResize$.on;
|
|
3721
|
+
}
|
|
3722
|
+
updateAnnotationsMethod(patches, documentId) {
|
|
3723
|
+
const docId = documentId ?? this.getActiveDocumentId();
|
|
3724
|
+
if (!this.checkPermission(docId, PdfPermissionFlag.ModifyAnnotations)) {
|
|
3725
|
+
this.logger.debug(
|
|
3726
|
+
"AnnotationPlugin",
|
|
3727
|
+
"UpdateAnnotations",
|
|
3728
|
+
`Cannot update annotations: document ${docId} lacks ModifyAnnotations permission`
|
|
3729
|
+
);
|
|
3730
|
+
return;
|
|
3731
|
+
}
|
|
3732
|
+
const docState = this.getDocumentState(docId);
|
|
3733
|
+
const patchData = patches.map(({ pageIndex, id, patch }) => {
|
|
3734
|
+
var _a;
|
|
3735
|
+
const originalObject = (_a = docState.byUid[id]) == null ? void 0 : _a.object;
|
|
3736
|
+
if (!originalObject) return null;
|
|
3737
|
+
const finalPatch = this.buildPatch(originalObject, {
|
|
3738
|
+
...patch,
|
|
3739
|
+
author: patch.author ?? this.config.annotationAuthor
|
|
3740
|
+
});
|
|
3741
|
+
return { pageIndex, id, patch: finalPatch, originalObject };
|
|
3742
|
+
}).filter((p) => p !== null);
|
|
3743
|
+
if (patchData.length === 0) return;
|
|
3744
|
+
const execute = () => {
|
|
3745
|
+
for (const { pageIndex, id, patch, originalObject } of patchData) {
|
|
3746
|
+
this.dispatch(patchAnnotation(docId, pageIndex, id, patch));
|
|
3747
|
+
this.events$.emit({
|
|
3748
|
+
type: "update",
|
|
3749
|
+
documentId: docId,
|
|
3750
|
+
annotation: originalObject,
|
|
3751
|
+
pageIndex,
|
|
3752
|
+
patch,
|
|
3753
|
+
committed: false
|
|
3754
|
+
});
|
|
3755
|
+
}
|
|
3756
|
+
};
|
|
3757
|
+
if (!this.history) {
|
|
3758
|
+
execute();
|
|
3759
|
+
if (this.config.autoCommit !== false) {
|
|
3760
|
+
this.commit(docId);
|
|
3761
|
+
}
|
|
3762
|
+
return;
|
|
3763
|
+
}
|
|
3764
|
+
const undoData = patchData.map(({ pageIndex, id, patch, originalObject }) => ({
|
|
3765
|
+
pageIndex,
|
|
3766
|
+
id,
|
|
3767
|
+
originalPatch: Object.fromEntries(
|
|
3768
|
+
Object.keys(patch).map((key) => [key, originalObject[key]])
|
|
3769
|
+
),
|
|
3770
|
+
originalObject
|
|
3771
|
+
}));
|
|
3772
|
+
const command = {
|
|
3773
|
+
execute,
|
|
3774
|
+
undo: () => {
|
|
3775
|
+
for (const { pageIndex, id, originalPatch, originalObject } of undoData) {
|
|
3776
|
+
this.dispatch(patchAnnotation(docId, pageIndex, id, originalPatch));
|
|
3777
|
+
this.events$.emit({
|
|
3778
|
+
type: "update",
|
|
3779
|
+
documentId: docId,
|
|
3780
|
+
annotation: originalObject,
|
|
3781
|
+
pageIndex,
|
|
3782
|
+
patch: originalPatch,
|
|
3783
|
+
committed: false
|
|
3784
|
+
});
|
|
3785
|
+
}
|
|
3786
|
+
},
|
|
3787
|
+
metadata: { annotationIds: patchData.map((p) => p.id) }
|
|
3788
|
+
};
|
|
3789
|
+
const historyScope = this.history.forDocument(docId);
|
|
3790
|
+
historyScope.register(command, this.ANNOTATION_HISTORY_TOPIC);
|
|
3791
|
+
}
|
|
2781
3792
|
getActiveTool(documentId) {
|
|
2782
3793
|
const docState = this.getDocumentState(documentId);
|
|
2783
3794
|
if (!docState.activeToolId) return null;
|
|
@@ -2819,87 +3830,185 @@ const _AnnotationPlugin = class _AnnotationPlugin extends BasePlugin {
|
|
|
2819
3830
|
}
|
|
2820
3831
|
return bestTool;
|
|
2821
3832
|
}
|
|
2822
|
-
|
|
2823
|
-
|
|
2824
|
-
|
|
3833
|
+
/**
|
|
3834
|
+
* Collects all pending annotation changes for a document into a batch.
|
|
3835
|
+
* This separates the "what to commit" from "how to commit" for cleaner code.
|
|
3836
|
+
*/
|
|
3837
|
+
collectPendingChanges(docId, doc) {
|
|
2825
3838
|
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
3839
|
const contexts = this.pendingContexts.get(docId);
|
|
2832
|
-
|
|
2833
|
-
|
|
2834
|
-
|
|
2835
|
-
|
|
3840
|
+
const batch = {
|
|
3841
|
+
creations: [],
|
|
3842
|
+
updates: [],
|
|
3843
|
+
deletions: [],
|
|
3844
|
+
committedUids: [],
|
|
3845
|
+
isEmpty: true
|
|
3846
|
+
};
|
|
2836
3847
|
for (const [uid, ta] of Object.entries(docState.byUid)) {
|
|
2837
3848
|
if (ta.commitState === "synced") continue;
|
|
2838
3849
|
const page = doc.pages.find((p) => p.index === ta.object.pageIndex);
|
|
2839
3850
|
if (!page) continue;
|
|
3851
|
+
batch.committedUids.push(uid);
|
|
3852
|
+
batch.isEmpty = false;
|
|
2840
3853
|
switch (ta.commitState) {
|
|
2841
3854
|
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);
|
|
3855
|
+
batch.creations.push({
|
|
3856
|
+
uid,
|
|
3857
|
+
ta,
|
|
3858
|
+
ctx: contexts == null ? void 0 : contexts.get(ta.object.id)
|
|
3859
|
+
});
|
|
2856
3860
|
break;
|
|
2857
3861
|
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);
|
|
3862
|
+
batch.updates.push({ uid, ta });
|
|
2870
3863
|
break;
|
|
2871
3864
|
case "deleted":
|
|
2872
|
-
deletions.push({
|
|
3865
|
+
batch.deletions.push({ uid, ta });
|
|
2873
3866
|
break;
|
|
2874
3867
|
}
|
|
2875
3868
|
}
|
|
2876
|
-
|
|
2877
|
-
|
|
3869
|
+
return batch;
|
|
3870
|
+
}
|
|
3871
|
+
/**
|
|
3872
|
+
* Executes a batch of pending changes by creating engine tasks.
|
|
3873
|
+
* Returns a task that resolves when all operations complete.
|
|
3874
|
+
*/
|
|
3875
|
+
executeCommitBatch(docId, doc, batch) {
|
|
3876
|
+
const task = new Task();
|
|
3877
|
+
const contexts = this.pendingContexts.get(docId);
|
|
3878
|
+
const pendingOps = [];
|
|
3879
|
+
for (const { uid, ta, ctx } of batch.creations) {
|
|
2878
3880
|
const page = doc.pages.find((p) => p.index === ta.object.pageIndex);
|
|
2879
|
-
if (
|
|
2880
|
-
|
|
3881
|
+
if (!page) continue;
|
|
3882
|
+
const createTask = this.engine.createPageAnnotation(doc, page, ta.object, ctx);
|
|
3883
|
+
pendingOps.push({ type: "create", task: createTask, ta, uid, ctx });
|
|
3884
|
+
}
|
|
3885
|
+
for (const { uid, ta } of batch.updates) {
|
|
3886
|
+
const page = doc.pages.find((p) => p.index === ta.object.pageIndex);
|
|
3887
|
+
if (!page) continue;
|
|
3888
|
+
const updateTask = this.engine.updatePageAnnotation(doc, page, ta.object);
|
|
3889
|
+
pendingOps.push({ type: "update", task: updateTask, ta, uid });
|
|
3890
|
+
}
|
|
3891
|
+
for (const { uid, ta } of batch.deletions) {
|
|
3892
|
+
const page = doc.pages.find((p) => p.index === ta.object.pageIndex);
|
|
3893
|
+
if (!page) continue;
|
|
3894
|
+
if (ta.object.id) {
|
|
3895
|
+
const deleteTask = new Task();
|
|
2881
3896
|
const removeTask = this.engine.removePageAnnotation(doc, page, ta.object);
|
|
2882
|
-
removeTask.wait(() =>
|
|
2883
|
-
|
|
3897
|
+
removeTask.wait(() => deleteTask.resolve(true), deleteTask.fail);
|
|
3898
|
+
pendingOps.push({ type: "delete", task: deleteTask, ta, uid });
|
|
3899
|
+
} else {
|
|
3900
|
+
this.dispatch(purgeAnnotation(docId, ta.object.pageIndex, uid));
|
|
3901
|
+
}
|
|
3902
|
+
}
|
|
3903
|
+
const allTasks = pendingOps.map((op) => op.task);
|
|
3904
|
+
Task.allSettled(allTasks).wait(
|
|
3905
|
+
() => {
|
|
3906
|
+
this.emitCommitEvents(docId, pendingOps, contexts);
|
|
3907
|
+
this.dispatch(commitPendingChanges(docId, batch.committedUids));
|
|
3908
|
+
task.resolve(true);
|
|
3909
|
+
},
|
|
3910
|
+
(error) => task.fail(error)
|
|
3911
|
+
);
|
|
3912
|
+
return task;
|
|
3913
|
+
}
|
|
3914
|
+
/**
|
|
3915
|
+
* Emits commit events for all completed operations.
|
|
3916
|
+
* Centralizes event emission for cleaner separation of concerns.
|
|
3917
|
+
*/
|
|
3918
|
+
emitCommitEvents(docId, operations, contexts) {
|
|
3919
|
+
for (const op of operations) {
|
|
3920
|
+
if (op.task.state.stage !== TaskStage.Resolved) continue;
|
|
3921
|
+
switch (op.type) {
|
|
3922
|
+
case "create":
|
|
3923
|
+
this.events$.emit({
|
|
3924
|
+
type: "create",
|
|
3925
|
+
documentId: docId,
|
|
3926
|
+
annotation: op.ta.object,
|
|
3927
|
+
pageIndex: op.ta.object.pageIndex,
|
|
3928
|
+
ctx: op.ctx,
|
|
3929
|
+
committed: true
|
|
3930
|
+
});
|
|
3931
|
+
contexts == null ? void 0 : contexts.delete(op.ta.object.id);
|
|
3932
|
+
break;
|
|
3933
|
+
case "update":
|
|
3934
|
+
this.events$.emit({
|
|
3935
|
+
type: "update",
|
|
3936
|
+
documentId: docId,
|
|
3937
|
+
annotation: op.ta.object,
|
|
3938
|
+
pageIndex: op.ta.object.pageIndex,
|
|
3939
|
+
patch: op.ta.object,
|
|
3940
|
+
committed: true
|
|
3941
|
+
});
|
|
3942
|
+
break;
|
|
3943
|
+
case "delete":
|
|
3944
|
+
this.dispatch(purgeAnnotation(docId, op.ta.object.pageIndex, op.uid));
|
|
2884
3945
|
this.events$.emit({
|
|
2885
3946
|
type: "delete",
|
|
2886
3947
|
documentId: docId,
|
|
2887
|
-
annotation: ta.object,
|
|
2888
|
-
pageIndex: ta.object.pageIndex,
|
|
3948
|
+
annotation: op.ta.object,
|
|
3949
|
+
pageIndex: op.ta.object.pageIndex,
|
|
2889
3950
|
committed: true
|
|
2890
3951
|
});
|
|
2891
|
-
|
|
2892
|
-
}, task2.fail);
|
|
2893
|
-
deletionTasks.push(task2);
|
|
2894
|
-
} else {
|
|
2895
|
-
this.dispatch(purgeAnnotation(docId, uid));
|
|
3952
|
+
break;
|
|
2896
3953
|
}
|
|
2897
3954
|
}
|
|
2898
|
-
|
|
2899
|
-
|
|
2900
|
-
|
|
2901
|
-
|
|
2902
|
-
|
|
3955
|
+
}
|
|
3956
|
+
/**
|
|
3957
|
+
* Attempts to acquire the commit lock for a document.
|
|
3958
|
+
* Returns true if acquired, false if a commit is already in progress.
|
|
3959
|
+
*/
|
|
3960
|
+
acquireCommitLock(docId) {
|
|
3961
|
+
if (this.commitInProgress.get(docId)) {
|
|
3962
|
+
return false;
|
|
3963
|
+
}
|
|
3964
|
+
this.commitInProgress.set(docId, true);
|
|
3965
|
+
return true;
|
|
3966
|
+
}
|
|
3967
|
+
/**
|
|
3968
|
+
* Releases the commit lock for a document.
|
|
3969
|
+
*/
|
|
3970
|
+
releaseCommitLock(docId) {
|
|
3971
|
+
this.commitInProgress.set(docId, false);
|
|
3972
|
+
}
|
|
3973
|
+
commit(documentId) {
|
|
3974
|
+
const docId = documentId ?? this.getActiveDocumentId();
|
|
3975
|
+
const docState = this.getDocumentState(docId);
|
|
3976
|
+
if (!docState.hasPendingChanges) {
|
|
3977
|
+
return PdfTaskHelper.resolve(true);
|
|
3978
|
+
}
|
|
3979
|
+
if (!this.acquireCommitLock(docId)) {
|
|
3980
|
+
return PdfTaskHelper.resolve(true);
|
|
3981
|
+
}
|
|
3982
|
+
const coreDocState = this.getCoreDocument(docId);
|
|
3983
|
+
const doc = coreDocState == null ? void 0 : coreDocState.document;
|
|
3984
|
+
if (!doc) {
|
|
3985
|
+
this.releaseCommitLock(docId);
|
|
3986
|
+
return PdfTaskHelper.reject({ code: PdfErrorCode.NotFound, message: "Document not found" });
|
|
3987
|
+
}
|
|
3988
|
+
const batch = this.collectPendingChanges(docId, doc);
|
|
3989
|
+
if (batch.isEmpty) {
|
|
3990
|
+
this.releaseCommitLock(docId);
|
|
3991
|
+
return PdfTaskHelper.resolve(true);
|
|
3992
|
+
}
|
|
3993
|
+
const task = new Task();
|
|
3994
|
+
this.executeCommitBatch(docId, doc, batch).wait(
|
|
3995
|
+
() => {
|
|
3996
|
+
this.releaseCommitLock(docId);
|
|
3997
|
+
const updatedDocState = this.getDocumentState(docId);
|
|
3998
|
+
if (updatedDocState.hasPendingChanges) {
|
|
3999
|
+
this.commit(docId).wait(
|
|
4000
|
+
(result) => task.resolve(result),
|
|
4001
|
+
(error) => task.fail(error)
|
|
4002
|
+
);
|
|
4003
|
+
} else {
|
|
4004
|
+
task.resolve(true);
|
|
4005
|
+
}
|
|
4006
|
+
},
|
|
4007
|
+
(error) => {
|
|
4008
|
+
this.releaseCommitLock(docId);
|
|
4009
|
+
task.fail(error);
|
|
4010
|
+
}
|
|
4011
|
+
);
|
|
2903
4012
|
return task;
|
|
2904
4013
|
}
|
|
2905
4014
|
/**
|
|
@@ -2916,6 +4025,11 @@ const _AnnotationPlugin = class _AnnotationPlugin extends BasePlugin {
|
|
|
2916
4025
|
};
|
|
2917
4026
|
_AnnotationPlugin.id = "annotation";
|
|
2918
4027
|
let AnnotationPlugin = _AnnotationPlugin;
|
|
4028
|
+
function resolveInteractionProp(prop, annotation, defaultValue) {
|
|
4029
|
+
if (prop === void 0) return defaultValue;
|
|
4030
|
+
if (typeof prop === "function") return prop(annotation);
|
|
4031
|
+
return prop;
|
|
4032
|
+
}
|
|
2919
4033
|
function createToolPredicate(id) {
|
|
2920
4034
|
return (tool) => {
|
|
2921
4035
|
return (tool == null ? void 0 : tool.id) === id;
|
|
@@ -2948,11 +4062,23 @@ export {
|
|
|
2948
4062
|
getAnnotationByUid,
|
|
2949
4063
|
getAnnotations,
|
|
2950
4064
|
getAnnotationsByPageIndex,
|
|
4065
|
+
getAttachedLinks,
|
|
4066
|
+
getGroupLeaderId,
|
|
4067
|
+
getGroupMembers,
|
|
4068
|
+
getIRTChildIds,
|
|
4069
|
+
getIRTChildrenByType,
|
|
2951
4070
|
getSelectedAnnotation,
|
|
2952
4071
|
getSelectedAnnotationByPageIndex,
|
|
4072
|
+
getSelectedAnnotationIds,
|
|
4073
|
+
getSelectedAnnotations,
|
|
4074
|
+
getSelectedAnnotationsByPageIndex,
|
|
4075
|
+
getSelectionGroupingAction,
|
|
2953
4076
|
getSidebarAnnotationsWithReplies,
|
|
2954
4077
|
getSidebarAnnotationsWithRepliesGroupedByPage,
|
|
2955
4078
|
getToolDefaultsById,
|
|
4079
|
+
hasAttachedLinks,
|
|
4080
|
+
hasIRTChildren,
|
|
4081
|
+
hasMultipleSelected,
|
|
2956
4082
|
initialDocumentState,
|
|
2957
4083
|
initialState,
|
|
2958
4084
|
isAnnotationSelected,
|
|
@@ -2962,15 +4088,18 @@ export {
|
|
|
2962
4088
|
isFreeTextTool,
|
|
2963
4089
|
isHighlight,
|
|
2964
4090
|
isHighlightTool,
|
|
4091
|
+
isInGroup,
|
|
2965
4092
|
isInk,
|
|
2966
4093
|
isInkHighlighterTool,
|
|
2967
4094
|
isInkTool,
|
|
2968
4095
|
isLine,
|
|
2969
4096
|
isLineTool,
|
|
4097
|
+
isLink,
|
|
2970
4098
|
isPolygon,
|
|
2971
4099
|
isPolygonTool,
|
|
2972
4100
|
isPolyline,
|
|
2973
4101
|
isPolylineTool,
|
|
4102
|
+
isRedact,
|
|
2974
4103
|
isSidebarAnnotation,
|
|
2975
4104
|
isSquare,
|
|
2976
4105
|
isSquareTool,
|
|
@@ -2985,6 +4114,8 @@ export {
|
|
|
2985
4114
|
isUnderline,
|
|
2986
4115
|
isUnderlineTool,
|
|
2987
4116
|
manifest,
|
|
2988
|
-
index as patching
|
|
4117
|
+
index as patching,
|
|
4118
|
+
rectsIntersect,
|
|
4119
|
+
resolveInteractionProp
|
|
2989
4120
|
};
|
|
2990
4121
|
//# sourceMappingURL=index.js.map
|