@embedpdf/utils 2.5.0 → 2.6.1
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/preact/index.cjs +1 -1
- package/dist/preact/index.cjs.map +1 -1
- package/dist/preact/index.js +834 -322
- package/dist/preact/index.js.map +1 -1
- package/dist/react/index.cjs +1 -1
- package/dist/react/index.cjs.map +1 -1
- package/dist/react/index.js +834 -322
- package/dist/react/index.js.map +1 -1
- package/dist/shared/hooks/use-drag-resize.d.ts +4 -0
- package/dist/shared/hooks/use-interaction-handles.d.ts +18 -2
- package/dist/shared/plugin-interaction-primitives/drag-resize-controller.d.ts +49 -23
- package/dist/shared/plugin-interaction-primitives/index.d.ts +1 -0
- package/dist/shared/plugin-interaction-primitives/resize-geometry.d.ts +72 -0
- package/dist/shared/plugin-interaction-primitives/utils.d.ts +33 -0
- package/dist/shared-preact/hooks/use-drag-resize.d.ts +4 -0
- package/dist/shared-preact/hooks/use-interaction-handles.d.ts +18 -2
- package/dist/shared-preact/plugin-interaction-primitives/drag-resize-controller.d.ts +49 -23
- package/dist/shared-preact/plugin-interaction-primitives/index.d.ts +1 -0
- package/dist/shared-preact/plugin-interaction-primitives/resize-geometry.d.ts +72 -0
- package/dist/shared-preact/plugin-interaction-primitives/utils.d.ts +33 -0
- package/dist/shared-react/hooks/use-drag-resize.d.ts +4 -0
- package/dist/shared-react/hooks/use-interaction-handles.d.ts +18 -2
- package/dist/shared-react/plugin-interaction-primitives/drag-resize-controller.d.ts +49 -23
- package/dist/shared-react/plugin-interaction-primitives/index.d.ts +1 -0
- package/dist/shared-react/plugin-interaction-primitives/resize-geometry.d.ts +72 -0
- package/dist/shared-react/plugin-interaction-primitives/utils.d.ts +33 -0
- package/dist/shared-svelte/plugin-interaction-primitives/drag-resize-controller.d.ts +49 -23
- package/dist/shared-svelte/plugin-interaction-primitives/index.d.ts +1 -0
- package/dist/shared-svelte/plugin-interaction-primitives/resize-geometry.d.ts +72 -0
- package/dist/shared-svelte/plugin-interaction-primitives/utils.d.ts +33 -0
- package/dist/shared-vue/plugin-interaction-primitives/drag-resize-controller.d.ts +49 -23
- package/dist/shared-vue/plugin-interaction-primitives/index.d.ts +1 -0
- package/dist/shared-vue/plugin-interaction-primitives/resize-geometry.d.ts +72 -0
- package/dist/shared-vue/plugin-interaction-primitives/utils.d.ts +33 -0
- package/dist/svelte/hooks/use-drag-resize.svelte.d.ts +1 -0
- package/dist/svelte/hooks/use-interaction-handles.svelte.d.ts +16 -2
- package/dist/svelte/index.cjs +1 -1
- package/dist/svelte/index.cjs.map +1 -1
- package/dist/svelte/index.js +680 -293
- package/dist/svelte/index.js.map +1 -1
- package/dist/vue/hooks/use-drag-resize.d.ts +9 -0
- package/dist/vue/hooks/use-interaction-handles.d.ts +17 -2
- package/dist/vue/index.cjs +1 -1
- package/dist/vue/index.cjs.map +1 -1
- package/dist/vue/index.js +716 -297
- package/dist/vue/index.js.map +1 -1
- package/package.json +3 -3
package/dist/react/index.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { jsx } from "react/jsx-runtime";
|
|
2
2
|
import { getCounterRotation } from "@embedpdf/utils";
|
|
3
3
|
import { useRef, useEffect, Fragment, useCallback, useMemo } from "react";
|
|
4
|
+
import { rotatePointAround, calculateRotatedRectAABB, normalizeAngle } from "@embedpdf/models";
|
|
4
5
|
const dblClickProp = "onDoubleClick";
|
|
5
6
|
function CounterRotate({ children, ...props }) {
|
|
6
7
|
const { rect, rotation } = props;
|
|
@@ -48,12 +49,405 @@ function CounterRotate({ children, ...props }) {
|
|
|
48
49
|
}
|
|
49
50
|
}) });
|
|
50
51
|
}
|
|
52
|
+
const ROTATION_HANDLE_MARGIN = 35;
|
|
53
|
+
const HANDLE_BASE_ANGLE = {
|
|
54
|
+
n: 0,
|
|
55
|
+
ne: 45,
|
|
56
|
+
e: 90,
|
|
57
|
+
se: 135,
|
|
58
|
+
s: 180,
|
|
59
|
+
sw: 225,
|
|
60
|
+
w: 270,
|
|
61
|
+
nw: 315
|
|
62
|
+
};
|
|
63
|
+
const SECTOR_CURSORS = [
|
|
64
|
+
"ns-resize",
|
|
65
|
+
// 0: north
|
|
66
|
+
"nesw-resize",
|
|
67
|
+
// 1: NE
|
|
68
|
+
"ew-resize",
|
|
69
|
+
// 2: east
|
|
70
|
+
"nwse-resize",
|
|
71
|
+
// 3: SE
|
|
72
|
+
"ns-resize",
|
|
73
|
+
// 4: south
|
|
74
|
+
"nesw-resize",
|
|
75
|
+
// 5: SW
|
|
76
|
+
"ew-resize",
|
|
77
|
+
// 6: west
|
|
78
|
+
"nwse-resize"
|
|
79
|
+
// 7: NW
|
|
80
|
+
];
|
|
81
|
+
function diagonalCursor(handle, pageQuarterTurns, annotationRotation = 0) {
|
|
82
|
+
const pageAngle = pageQuarterTurns * 90;
|
|
83
|
+
const totalAngle = HANDLE_BASE_ANGLE[handle] + pageAngle + annotationRotation;
|
|
84
|
+
const normalized = (totalAngle % 360 + 360) % 360;
|
|
85
|
+
const sector = Math.round(normalized / 45) % 8;
|
|
86
|
+
return SECTOR_CURSORS[sector];
|
|
87
|
+
}
|
|
88
|
+
function edgeOffset(k, spacing, mode) {
|
|
89
|
+
const base = -k / 2;
|
|
90
|
+
if (mode === "center") return base;
|
|
91
|
+
return mode === "outside" ? base - spacing : base + spacing;
|
|
92
|
+
}
|
|
93
|
+
function describeResizeFromConfig(cfg, ui = {}) {
|
|
94
|
+
const {
|
|
95
|
+
handleSize = 8,
|
|
96
|
+
spacing = 1,
|
|
97
|
+
offsetMode = "outside",
|
|
98
|
+
includeSides = false,
|
|
99
|
+
zIndex = 3,
|
|
100
|
+
rotationAwareCursor = true
|
|
101
|
+
} = ui;
|
|
102
|
+
const pageQuarterTurns = (cfg.pageRotation ?? 0) % 4;
|
|
103
|
+
const annotationRot = cfg.annotationRotation ?? 0;
|
|
104
|
+
const off = (edge) => ({
|
|
105
|
+
[edge]: edgeOffset(handleSize, spacing, offsetMode) + "px"
|
|
106
|
+
});
|
|
107
|
+
const corners = [
|
|
108
|
+
["nw", { ...off("top"), ...off("left") }],
|
|
109
|
+
["ne", { ...off("top"), ...off("right") }],
|
|
110
|
+
["sw", { ...off("bottom"), ...off("left") }],
|
|
111
|
+
["se", { ...off("bottom"), ...off("right") }]
|
|
112
|
+
];
|
|
113
|
+
const sides = includeSides ? [
|
|
114
|
+
["n", { ...off("top"), left: `calc(50% - ${handleSize / 2}px)` }],
|
|
115
|
+
["s", { ...off("bottom"), left: `calc(50% - ${handleSize / 2}px)` }],
|
|
116
|
+
["w", { ...off("left"), top: `calc(50% - ${handleSize / 2}px)` }],
|
|
117
|
+
["e", { ...off("right"), top: `calc(50% - ${handleSize / 2}px)` }]
|
|
118
|
+
] : [];
|
|
119
|
+
const all = [...corners, ...sides];
|
|
120
|
+
return all.map(([handle, pos]) => ({
|
|
121
|
+
handle,
|
|
122
|
+
style: {
|
|
123
|
+
position: "absolute",
|
|
124
|
+
width: handleSize + "px",
|
|
125
|
+
height: handleSize + "px",
|
|
126
|
+
borderRadius: "50%",
|
|
127
|
+
zIndex,
|
|
128
|
+
cursor: rotationAwareCursor ? diagonalCursor(handle, pageQuarterTurns, annotationRot) : "default",
|
|
129
|
+
pointerEvents: "auto",
|
|
130
|
+
touchAction: "none",
|
|
131
|
+
...pos
|
|
132
|
+
},
|
|
133
|
+
attrs: { "data-epdf-handle": handle }
|
|
134
|
+
}));
|
|
135
|
+
}
|
|
136
|
+
function describeVerticesFromConfig(cfg, ui = {}, liveVertices) {
|
|
137
|
+
const { vertexSize = 12, zIndex = 4 } = ui;
|
|
138
|
+
const rect = cfg.element;
|
|
139
|
+
const scale = cfg.scale ?? 1;
|
|
140
|
+
const verts = liveVertices ?? cfg.vertices ?? [];
|
|
141
|
+
return verts.map((v, i) => {
|
|
142
|
+
const left = (v.x - rect.origin.x) * scale - vertexSize / 2;
|
|
143
|
+
const top = (v.y - rect.origin.y) * scale - vertexSize / 2;
|
|
144
|
+
return {
|
|
145
|
+
handle: "nw",
|
|
146
|
+
// not used; kept for type
|
|
147
|
+
style: {
|
|
148
|
+
position: "absolute",
|
|
149
|
+
left: left + "px",
|
|
150
|
+
top: top + "px",
|
|
151
|
+
width: vertexSize + "px",
|
|
152
|
+
height: vertexSize + "px",
|
|
153
|
+
borderRadius: "50%",
|
|
154
|
+
cursor: "pointer",
|
|
155
|
+
zIndex,
|
|
156
|
+
pointerEvents: "auto",
|
|
157
|
+
touchAction: "none"
|
|
158
|
+
},
|
|
159
|
+
attrs: { "data-epdf-vertex": i }
|
|
160
|
+
};
|
|
161
|
+
});
|
|
162
|
+
}
|
|
163
|
+
function describeRotationFromConfig(cfg, ui = {}, currentAngle = 0) {
|
|
164
|
+
const { handleSize = 16, zIndex = 5, showConnector = true, connectorWidth = 1 } = ui;
|
|
165
|
+
const scale = cfg.scale ?? 1;
|
|
166
|
+
const rect = cfg.element;
|
|
167
|
+
const orbitRect = cfg.rotationElement ?? rect;
|
|
168
|
+
const orbitCenter = cfg.rotationCenter ?? {
|
|
169
|
+
x: rect.origin.x + rect.size.width / 2,
|
|
170
|
+
y: rect.origin.y + rect.size.height / 2
|
|
171
|
+
};
|
|
172
|
+
orbitRect.size.width * scale;
|
|
173
|
+
orbitRect.size.height * scale;
|
|
174
|
+
const centerX = (orbitCenter.x - orbitRect.origin.x) * scale;
|
|
175
|
+
const centerY = (orbitCenter.y - orbitRect.origin.y) * scale;
|
|
176
|
+
const angleRad = currentAngle * Math.PI / 180;
|
|
177
|
+
const margin = ui.margin ?? ROTATION_HANDLE_MARGIN;
|
|
178
|
+
const radius = rect.size.height * scale / 2 + margin;
|
|
179
|
+
const handleCenterX = centerX + radius * Math.sin(angleRad);
|
|
180
|
+
const handleCenterY = centerY - radius * Math.cos(angleRad);
|
|
181
|
+
const handleLeft = handleCenterX - handleSize / 2;
|
|
182
|
+
const handleTop = handleCenterY - handleSize / 2;
|
|
183
|
+
return {
|
|
184
|
+
handleStyle: {
|
|
185
|
+
position: "absolute",
|
|
186
|
+
left: handleLeft + "px",
|
|
187
|
+
top: handleTop + "px",
|
|
188
|
+
width: handleSize + "px",
|
|
189
|
+
height: handleSize + "px",
|
|
190
|
+
borderRadius: "50%",
|
|
191
|
+
cursor: "grab",
|
|
192
|
+
zIndex,
|
|
193
|
+
pointerEvents: "auto",
|
|
194
|
+
touchAction: "none"
|
|
195
|
+
},
|
|
196
|
+
connectorStyle: showConnector ? {
|
|
197
|
+
position: "absolute",
|
|
198
|
+
left: centerX - connectorWidth / 2 + "px",
|
|
199
|
+
top: centerY - radius + "px",
|
|
200
|
+
width: connectorWidth + "px",
|
|
201
|
+
height: radius + "px",
|
|
202
|
+
transformOrigin: "center bottom",
|
|
203
|
+
transform: `rotate(${currentAngle}deg)`,
|
|
204
|
+
zIndex: zIndex - 1,
|
|
205
|
+
pointerEvents: "none"
|
|
206
|
+
} : {},
|
|
207
|
+
radius,
|
|
208
|
+
attrs: { "data-epdf-rotation-handle": true }
|
|
209
|
+
};
|
|
210
|
+
}
|
|
51
211
|
function getAnchor(handle) {
|
|
52
212
|
return {
|
|
53
213
|
x: handle.includes("e") ? "left" : handle.includes("w") ? "right" : "center",
|
|
54
214
|
y: handle.includes("s") ? "top" : handle.includes("n") ? "bottom" : "center"
|
|
55
215
|
};
|
|
56
216
|
}
|
|
217
|
+
function getAnchorPoint(rect, anchor) {
|
|
218
|
+
const x = anchor.x === "left" ? rect.origin.x : anchor.x === "right" ? rect.origin.x + rect.size.width : rect.origin.x + rect.size.width / 2;
|
|
219
|
+
const y = anchor.y === "top" ? rect.origin.y : anchor.y === "bottom" ? rect.origin.y + rect.size.height : rect.origin.y + rect.size.height / 2;
|
|
220
|
+
return { x, y };
|
|
221
|
+
}
|
|
222
|
+
function applyResizeDelta(startRect, delta, anchor) {
|
|
223
|
+
let x = startRect.origin.x;
|
|
224
|
+
let y = startRect.origin.y;
|
|
225
|
+
let width = startRect.size.width;
|
|
226
|
+
let height = startRect.size.height;
|
|
227
|
+
if (anchor.x === "left") {
|
|
228
|
+
width += delta.x;
|
|
229
|
+
} else if (anchor.x === "right") {
|
|
230
|
+
x += delta.x;
|
|
231
|
+
width -= delta.x;
|
|
232
|
+
}
|
|
233
|
+
if (anchor.y === "top") {
|
|
234
|
+
height += delta.y;
|
|
235
|
+
} else if (anchor.y === "bottom") {
|
|
236
|
+
y += delta.y;
|
|
237
|
+
height -= delta.y;
|
|
238
|
+
}
|
|
239
|
+
return { origin: { x, y }, size: { width, height } };
|
|
240
|
+
}
|
|
241
|
+
function enforceAspectRatio(rect, startRect, anchor, aspectRatio) {
|
|
242
|
+
let { x, y } = rect.origin;
|
|
243
|
+
let { width, height } = rect.size;
|
|
244
|
+
const isEdgeHandle = anchor.x === "center" || anchor.y === "center";
|
|
245
|
+
if (isEdgeHandle) {
|
|
246
|
+
if (anchor.y === "center") {
|
|
247
|
+
height = width / aspectRatio;
|
|
248
|
+
y = startRect.origin.y + (startRect.size.height - height) / 2;
|
|
249
|
+
} else {
|
|
250
|
+
width = height * aspectRatio;
|
|
251
|
+
x = startRect.origin.x + (startRect.size.width - width) / 2;
|
|
252
|
+
}
|
|
253
|
+
} else {
|
|
254
|
+
const dw = Math.abs(width - startRect.size.width);
|
|
255
|
+
const dh = Math.abs(height - startRect.size.height);
|
|
256
|
+
const total = dw + dh;
|
|
257
|
+
if (total === 0) {
|
|
258
|
+
width = startRect.size.width;
|
|
259
|
+
height = startRect.size.height;
|
|
260
|
+
} else {
|
|
261
|
+
const wWeight = dw / total;
|
|
262
|
+
const hWeight = dh / total;
|
|
263
|
+
const wFromW = width;
|
|
264
|
+
const hFromW = width / aspectRatio;
|
|
265
|
+
const wFromH = height * aspectRatio;
|
|
266
|
+
const hFromH = height;
|
|
267
|
+
width = wWeight * wFromW + hWeight * wFromH;
|
|
268
|
+
height = wWeight * hFromW + hWeight * hFromH;
|
|
269
|
+
}
|
|
270
|
+
}
|
|
271
|
+
if (anchor.x === "right") {
|
|
272
|
+
x = startRect.origin.x + startRect.size.width - width;
|
|
273
|
+
}
|
|
274
|
+
if (anchor.y === "bottom") {
|
|
275
|
+
y = startRect.origin.y + startRect.size.height - height;
|
|
276
|
+
}
|
|
277
|
+
return { origin: { x, y }, size: { width, height } };
|
|
278
|
+
}
|
|
279
|
+
function clampToBounds(rect, startRect, anchor, bbox, maintainAspectRatio) {
|
|
280
|
+
if (!bbox) return rect;
|
|
281
|
+
let { x, y } = rect.origin;
|
|
282
|
+
let { width, height } = rect.size;
|
|
283
|
+
width = Math.max(1, width);
|
|
284
|
+
height = Math.max(1, height);
|
|
285
|
+
const anchorX = anchor.x === "left" ? startRect.origin.x : startRect.origin.x + startRect.size.width;
|
|
286
|
+
const anchorY = anchor.y === "top" ? startRect.origin.y : startRect.origin.y + startRect.size.height;
|
|
287
|
+
const maxW = anchor.x === "left" ? bbox.width - anchorX : anchor.x === "right" ? anchorX : Math.min(startRect.origin.x, bbox.width - startRect.origin.x - startRect.size.width) * 2 + startRect.size.width;
|
|
288
|
+
const maxH = anchor.y === "top" ? bbox.height - anchorY : anchor.y === "bottom" ? anchorY : Math.min(startRect.origin.y, bbox.height - startRect.origin.y - startRect.size.height) * 2 + startRect.size.height;
|
|
289
|
+
if (maintainAspectRatio) {
|
|
290
|
+
const scaleW = width > maxW ? maxW / width : 1;
|
|
291
|
+
const scaleH = height > maxH ? maxH / height : 1;
|
|
292
|
+
const scale = Math.min(scaleW, scaleH);
|
|
293
|
+
if (scale < 1) {
|
|
294
|
+
width *= scale;
|
|
295
|
+
height *= scale;
|
|
296
|
+
}
|
|
297
|
+
} else {
|
|
298
|
+
width = Math.min(width, maxW);
|
|
299
|
+
height = Math.min(height, maxH);
|
|
300
|
+
}
|
|
301
|
+
if (anchor.x === "left") {
|
|
302
|
+
x = anchorX;
|
|
303
|
+
} else if (anchor.x === "right") {
|
|
304
|
+
x = anchorX - width;
|
|
305
|
+
} else {
|
|
306
|
+
x = startRect.origin.x + (startRect.size.width - width) / 2;
|
|
307
|
+
}
|
|
308
|
+
if (anchor.y === "top") {
|
|
309
|
+
y = anchorY;
|
|
310
|
+
} else if (anchor.y === "bottom") {
|
|
311
|
+
y = anchorY - height;
|
|
312
|
+
} else {
|
|
313
|
+
y = startRect.origin.y + (startRect.size.height - height) / 2;
|
|
314
|
+
}
|
|
315
|
+
x = Math.max(0, Math.min(x, bbox.width - width));
|
|
316
|
+
y = Math.max(0, Math.min(y, bbox.height - height));
|
|
317
|
+
return { origin: { x, y }, size: { width, height } };
|
|
318
|
+
}
|
|
319
|
+
function reanchorRect(rect, startRect, anchor) {
|
|
320
|
+
let x;
|
|
321
|
+
let y;
|
|
322
|
+
if (anchor.x === "left") {
|
|
323
|
+
x = startRect.origin.x;
|
|
324
|
+
} else if (anchor.x === "right") {
|
|
325
|
+
x = startRect.origin.x + startRect.size.width - rect.size.width;
|
|
326
|
+
} else {
|
|
327
|
+
x = startRect.origin.x + (startRect.size.width - rect.size.width) / 2;
|
|
328
|
+
}
|
|
329
|
+
if (anchor.y === "top") {
|
|
330
|
+
y = startRect.origin.y;
|
|
331
|
+
} else if (anchor.y === "bottom") {
|
|
332
|
+
y = startRect.origin.y + startRect.size.height - rect.size.height;
|
|
333
|
+
} else {
|
|
334
|
+
y = startRect.origin.y + (startRect.size.height - rect.size.height) / 2;
|
|
335
|
+
}
|
|
336
|
+
return { origin: { x, y }, size: rect.size };
|
|
337
|
+
}
|
|
338
|
+
function applyConstraints(position, constraints, maintainAspectRatio, skipBoundingClamp = false) {
|
|
339
|
+
if (!constraints) return position;
|
|
340
|
+
let {
|
|
341
|
+
origin: { x, y },
|
|
342
|
+
size: { width, height }
|
|
343
|
+
} = position;
|
|
344
|
+
const minW = constraints.minWidth ?? 1;
|
|
345
|
+
const minH = constraints.minHeight ?? 1;
|
|
346
|
+
const maxW = constraints.maxWidth;
|
|
347
|
+
const maxH = constraints.maxHeight;
|
|
348
|
+
if (maintainAspectRatio && width > 0 && height > 0) {
|
|
349
|
+
const ratio = width / height;
|
|
350
|
+
if (width < minW) {
|
|
351
|
+
width = minW;
|
|
352
|
+
height = width / ratio;
|
|
353
|
+
}
|
|
354
|
+
if (height < minH) {
|
|
355
|
+
height = minH;
|
|
356
|
+
width = height * ratio;
|
|
357
|
+
}
|
|
358
|
+
if (maxW !== void 0 && width > maxW) {
|
|
359
|
+
width = maxW;
|
|
360
|
+
height = width / ratio;
|
|
361
|
+
}
|
|
362
|
+
if (maxH !== void 0 && height > maxH) {
|
|
363
|
+
height = maxH;
|
|
364
|
+
width = height * ratio;
|
|
365
|
+
}
|
|
366
|
+
} else {
|
|
367
|
+
width = Math.max(minW, width);
|
|
368
|
+
height = Math.max(minH, height);
|
|
369
|
+
if (maxW !== void 0) width = Math.min(maxW, width);
|
|
370
|
+
if (maxH !== void 0) height = Math.min(maxH, height);
|
|
371
|
+
}
|
|
372
|
+
if (constraints.boundingBox && !skipBoundingClamp) {
|
|
373
|
+
x = Math.max(0, Math.min(x, constraints.boundingBox.width - width));
|
|
374
|
+
y = Math.max(0, Math.min(y, constraints.boundingBox.height - height));
|
|
375
|
+
}
|
|
376
|
+
return { origin: { x, y }, size: { width, height } };
|
|
377
|
+
}
|
|
378
|
+
function isRectWithinRotatedBounds(rect, angleDegrees, bbox) {
|
|
379
|
+
const eps = 1e-6;
|
|
380
|
+
const aabb = calculateRotatedRectAABB(rect, angleDegrees);
|
|
381
|
+
return aabb.origin.x >= -eps && aabb.origin.y >= -eps && aabb.origin.x + aabb.size.width <= bbox.width + eps && aabb.origin.y + aabb.size.height <= bbox.height + eps;
|
|
382
|
+
}
|
|
383
|
+
function computeResizeStep(delta, handle, config, clampLocalBounds, skipConstraintBoundingClamp) {
|
|
384
|
+
const { startRect, maintainAspectRatio = false, annotationRotation = 0, constraints } = config;
|
|
385
|
+
const anchor = getAnchor(handle);
|
|
386
|
+
const aspectRatio = startRect.size.width / startRect.size.height || 1;
|
|
387
|
+
let rect = applyResizeDelta(startRect, delta, anchor);
|
|
388
|
+
if (maintainAspectRatio) {
|
|
389
|
+
rect = enforceAspectRatio(rect, startRect, anchor, aspectRatio);
|
|
390
|
+
}
|
|
391
|
+
if (clampLocalBounds) {
|
|
392
|
+
rect = clampToBounds(rect, startRect, anchor, constraints == null ? void 0 : constraints.boundingBox, maintainAspectRatio);
|
|
393
|
+
}
|
|
394
|
+
rect = applyConstraints(rect, constraints, maintainAspectRatio, skipConstraintBoundingClamp);
|
|
395
|
+
if (skipConstraintBoundingClamp) {
|
|
396
|
+
rect = reanchorRect(rect, startRect, anchor);
|
|
397
|
+
}
|
|
398
|
+
if (annotationRotation !== 0) {
|
|
399
|
+
const anchorPt = getAnchorPoint(startRect, anchor);
|
|
400
|
+
const oldCenter = {
|
|
401
|
+
x: startRect.origin.x + startRect.size.width / 2,
|
|
402
|
+
y: startRect.origin.y + startRect.size.height / 2
|
|
403
|
+
};
|
|
404
|
+
const newCenter = {
|
|
405
|
+
x: rect.origin.x + rect.size.width / 2,
|
|
406
|
+
y: rect.origin.y + rect.size.height / 2
|
|
407
|
+
};
|
|
408
|
+
const oldVisual = rotatePointAround(anchorPt, oldCenter, annotationRotation);
|
|
409
|
+
const newVisual = rotatePointAround(anchorPt, newCenter, annotationRotation);
|
|
410
|
+
rect = {
|
|
411
|
+
origin: {
|
|
412
|
+
x: rect.origin.x + (oldVisual.x - newVisual.x),
|
|
413
|
+
y: rect.origin.y + (oldVisual.y - newVisual.y)
|
|
414
|
+
},
|
|
415
|
+
size: rect.size
|
|
416
|
+
};
|
|
417
|
+
}
|
|
418
|
+
return rect;
|
|
419
|
+
}
|
|
420
|
+
function computeResizedRect(delta, handle, config) {
|
|
421
|
+
const { annotationRotation = 0, constraints } = config;
|
|
422
|
+
const bbox = constraints == null ? void 0 : constraints.boundingBox;
|
|
423
|
+
if (annotationRotation !== 0 && bbox) {
|
|
424
|
+
const target = computeResizeStep(delta, handle, config, false, true);
|
|
425
|
+
if (isRectWithinRotatedBounds(target, annotationRotation, bbox)) {
|
|
426
|
+
return target;
|
|
427
|
+
}
|
|
428
|
+
let best = computeResizeStep({ x: 0, y: 0 }, handle, config, false, true);
|
|
429
|
+
let low = 0;
|
|
430
|
+
let high = 1;
|
|
431
|
+
for (let i = 0; i < 20; i += 1) {
|
|
432
|
+
const mid = (low + high) / 2;
|
|
433
|
+
const trial = computeResizeStep(
|
|
434
|
+
{ x: delta.x * mid, y: delta.y * mid },
|
|
435
|
+
handle,
|
|
436
|
+
config,
|
|
437
|
+
false,
|
|
438
|
+
true
|
|
439
|
+
);
|
|
440
|
+
if (isRectWithinRotatedBounds(trial, annotationRotation, bbox)) {
|
|
441
|
+
best = trial;
|
|
442
|
+
low = mid;
|
|
443
|
+
} else {
|
|
444
|
+
high = mid;
|
|
445
|
+
}
|
|
446
|
+
}
|
|
447
|
+
return best;
|
|
448
|
+
}
|
|
449
|
+
return computeResizeStep(delta, handle, config, true, false);
|
|
450
|
+
}
|
|
57
451
|
class DragResizeController {
|
|
58
452
|
constructor(config, onUpdate) {
|
|
59
453
|
this.config = config;
|
|
@@ -61,29 +455,41 @@ class DragResizeController {
|
|
|
61
455
|
this.state = "idle";
|
|
62
456
|
this.startPoint = null;
|
|
63
457
|
this.startElement = null;
|
|
458
|
+
this.startRotationElement = null;
|
|
459
|
+
this.gestureRotationCenter = null;
|
|
64
460
|
this.activeHandle = null;
|
|
65
461
|
this.currentPosition = null;
|
|
66
462
|
this.activeVertexIndex = null;
|
|
67
463
|
this.startVertices = [];
|
|
68
464
|
this.currentVertices = [];
|
|
465
|
+
this.rotationCenter = null;
|
|
466
|
+
this.centerScreen = null;
|
|
467
|
+
this.initialRotation = 0;
|
|
468
|
+
this.lastComputedRotation = 0;
|
|
469
|
+
this.rotationDelta = 0;
|
|
470
|
+
this.rotationSnappedAngle = null;
|
|
69
471
|
this.currentVertices = config.vertices || [];
|
|
70
472
|
}
|
|
71
473
|
updateConfig(config) {
|
|
72
474
|
this.config = { ...this.config, ...config };
|
|
73
|
-
this.
|
|
475
|
+
if (this.state !== "vertex-editing") {
|
|
476
|
+
this.currentVertices = config.vertices || [];
|
|
477
|
+
}
|
|
74
478
|
}
|
|
479
|
+
// ---------------------------------------------------------------------------
|
|
480
|
+
// Gesture start
|
|
481
|
+
// ---------------------------------------------------------------------------
|
|
75
482
|
startDrag(clientX, clientY) {
|
|
76
483
|
this.state = "dragging";
|
|
77
484
|
this.startPoint = { x: clientX, y: clientY };
|
|
78
485
|
this.startElement = { ...this.config.element };
|
|
486
|
+
this.startRotationElement = this.config.rotationElement ? { ...this.config.rotationElement } : null;
|
|
79
487
|
this.currentPosition = { ...this.config.element };
|
|
80
488
|
this.onUpdate({
|
|
81
489
|
state: "start",
|
|
82
490
|
transformData: {
|
|
83
491
|
type: "move",
|
|
84
|
-
changes: {
|
|
85
|
-
rect: this.startElement
|
|
86
|
-
}
|
|
492
|
+
changes: { rect: this.startElement }
|
|
87
493
|
}
|
|
88
494
|
});
|
|
89
495
|
}
|
|
@@ -97,9 +503,7 @@ class DragResizeController {
|
|
|
97
503
|
state: "start",
|
|
98
504
|
transformData: {
|
|
99
505
|
type: "resize",
|
|
100
|
-
changes: {
|
|
101
|
-
rect: this.startElement
|
|
102
|
-
},
|
|
506
|
+
changes: { rect: this.startElement },
|
|
103
507
|
metadata: {
|
|
104
508
|
handle: this.activeHandle,
|
|
105
509
|
maintainAspectRatio: this.config.maintainAspectRatio
|
|
@@ -114,45 +518,87 @@ class DragResizeController {
|
|
|
114
518
|
this.activeVertexIndex = vertexIndex;
|
|
115
519
|
this.startPoint = { x: clientX, y: clientY };
|
|
116
520
|
this.startVertices = [...this.currentVertices];
|
|
521
|
+
this.gestureRotationCenter = this.config.rotationCenter ?? {
|
|
522
|
+
x: this.config.element.origin.x + this.config.element.size.width / 2,
|
|
523
|
+
y: this.config.element.origin.y + this.config.element.size.height / 2
|
|
524
|
+
};
|
|
117
525
|
this.onUpdate({
|
|
118
526
|
state: "start",
|
|
119
527
|
transformData: {
|
|
120
528
|
type: "vertex-edit",
|
|
121
|
-
changes: {
|
|
122
|
-
|
|
123
|
-
|
|
529
|
+
changes: { vertices: this.startVertices },
|
|
530
|
+
metadata: { vertexIndex }
|
|
531
|
+
}
|
|
532
|
+
});
|
|
533
|
+
}
|
|
534
|
+
startRotation(clientX, clientY, initialRotation = 0, orbitRadiusPx) {
|
|
535
|
+
this.state = "rotating";
|
|
536
|
+
this.startPoint = { x: clientX, y: clientY };
|
|
537
|
+
this.startElement = { ...this.config.element };
|
|
538
|
+
this.rotationCenter = this.config.rotationCenter ?? {
|
|
539
|
+
x: this.config.element.origin.x + this.config.element.size.width / 2,
|
|
540
|
+
y: this.config.element.origin.y + this.config.element.size.height / 2
|
|
541
|
+
};
|
|
542
|
+
const { scale = 1 } = this.config;
|
|
543
|
+
const orbitRect = this.config.rotationElement ?? this.config.element;
|
|
544
|
+
const sw = orbitRect.size.width * scale;
|
|
545
|
+
const sh = orbitRect.size.height * scale;
|
|
546
|
+
const radius = orbitRadiusPx ?? Math.max(sw, sh) / 2 + ROTATION_HANDLE_MARGIN;
|
|
547
|
+
const pageRotOffset = (this.config.pageRotation ?? 0) * 90;
|
|
548
|
+
const screenAngleRad = (initialRotation + pageRotOffset) * Math.PI / 180;
|
|
549
|
+
this.centerScreen = {
|
|
550
|
+
x: clientX - radius * Math.sin(screenAngleRad),
|
|
551
|
+
y: clientY + radius * Math.cos(screenAngleRad)
|
|
552
|
+
};
|
|
553
|
+
this.initialRotation = initialRotation;
|
|
554
|
+
this.lastComputedRotation = initialRotation;
|
|
555
|
+
this.rotationDelta = 0;
|
|
556
|
+
this.rotationSnappedAngle = null;
|
|
557
|
+
this.onUpdate({
|
|
558
|
+
state: "start",
|
|
559
|
+
transformData: {
|
|
560
|
+
type: "rotate",
|
|
561
|
+
changes: { rotation: initialRotation },
|
|
124
562
|
metadata: {
|
|
125
|
-
|
|
563
|
+
rotationAngle: initialRotation,
|
|
564
|
+
rotationDelta: 0,
|
|
565
|
+
rotationCenter: this.rotationCenter,
|
|
566
|
+
isSnapped: false
|
|
126
567
|
}
|
|
127
568
|
}
|
|
128
569
|
});
|
|
129
570
|
}
|
|
130
|
-
|
|
571
|
+
// ---------------------------------------------------------------------------
|
|
572
|
+
// Gesture move
|
|
573
|
+
// ---------------------------------------------------------------------------
|
|
574
|
+
move(clientX, clientY, buttons) {
|
|
131
575
|
if (this.state === "idle" || !this.startPoint) return;
|
|
576
|
+
if (buttons !== void 0 && buttons === 0) {
|
|
577
|
+
this.end();
|
|
578
|
+
return;
|
|
579
|
+
}
|
|
132
580
|
if (this.state === "dragging" && this.startElement) {
|
|
133
581
|
const delta = this.calculateDelta(clientX, clientY);
|
|
134
582
|
const position = this.calculateDragPosition(delta);
|
|
135
583
|
this.currentPosition = position;
|
|
136
584
|
this.onUpdate({
|
|
137
585
|
state: "move",
|
|
138
|
-
transformData: {
|
|
139
|
-
type: "move",
|
|
140
|
-
changes: {
|
|
141
|
-
rect: position
|
|
142
|
-
}
|
|
143
|
-
}
|
|
586
|
+
transformData: { type: "move", changes: { rect: position } }
|
|
144
587
|
});
|
|
145
588
|
} else if (this.state === "resizing" && this.activeHandle && this.startElement) {
|
|
146
|
-
const delta = this.
|
|
147
|
-
const position =
|
|
589
|
+
const delta = this.calculateLocalDelta(clientX, clientY);
|
|
590
|
+
const position = computeResizedRect(delta, this.activeHandle, {
|
|
591
|
+
startRect: this.startElement,
|
|
592
|
+
maintainAspectRatio: this.config.maintainAspectRatio,
|
|
593
|
+
annotationRotation: this.config.annotationRotation,
|
|
594
|
+
constraints: this.config.constraints
|
|
595
|
+
});
|
|
148
596
|
this.currentPosition = position;
|
|
149
597
|
this.onUpdate({
|
|
150
598
|
state: "move",
|
|
151
599
|
transformData: {
|
|
152
600
|
type: "resize",
|
|
153
|
-
changes: {
|
|
154
|
-
rect: position
|
|
155
|
-
},
|
|
601
|
+
changes: { rect: position },
|
|
156
602
|
metadata: {
|
|
157
603
|
handle: this.activeHandle,
|
|
158
604
|
maintainAspectRatio: this.config.maintainAspectRatio
|
|
@@ -166,16 +612,40 @@ class DragResizeController {
|
|
|
166
612
|
state: "move",
|
|
167
613
|
transformData: {
|
|
168
614
|
type: "vertex-edit",
|
|
169
|
-
changes: {
|
|
170
|
-
|
|
171
|
-
|
|
615
|
+
changes: { vertices },
|
|
616
|
+
metadata: { vertexIndex: this.activeVertexIndex }
|
|
617
|
+
}
|
|
618
|
+
});
|
|
619
|
+
} else if (this.state === "rotating" && this.rotationCenter) {
|
|
620
|
+
const absoluteAngle = this.calculateAngleFromMouse(clientX, clientY);
|
|
621
|
+
const snapResult = this.applyRotationSnapping(absoluteAngle);
|
|
622
|
+
const snappedAngle = normalizeAngle(snapResult.angle);
|
|
623
|
+
const previousAngle = this.lastComputedRotation;
|
|
624
|
+
const rawDelta = snappedAngle - previousAngle;
|
|
625
|
+
const adjustedDelta = rawDelta > 180 ? rawDelta - 360 : rawDelta < -180 ? rawDelta + 360 : rawDelta;
|
|
626
|
+
this.rotationDelta += adjustedDelta;
|
|
627
|
+
this.lastComputedRotation = snappedAngle;
|
|
628
|
+
this.rotationSnappedAngle = snapResult.isSnapped ? snappedAngle : null;
|
|
629
|
+
this.onUpdate({
|
|
630
|
+
state: "move",
|
|
631
|
+
transformData: {
|
|
632
|
+
type: "rotate",
|
|
633
|
+
changes: { rotation: snappedAngle },
|
|
172
634
|
metadata: {
|
|
173
|
-
|
|
635
|
+
rotationAngle: snappedAngle,
|
|
636
|
+
rotationDelta: this.rotationDelta,
|
|
637
|
+
rotationCenter: this.rotationCenter,
|
|
638
|
+
isSnapped: snapResult.isSnapped,
|
|
639
|
+
snappedAngle: this.rotationSnappedAngle ?? void 0,
|
|
640
|
+
cursorPosition: { clientX, clientY }
|
|
174
641
|
}
|
|
175
642
|
}
|
|
176
643
|
});
|
|
177
644
|
}
|
|
178
645
|
}
|
|
646
|
+
// ---------------------------------------------------------------------------
|
|
647
|
+
// Gesture end / cancel
|
|
648
|
+
// ---------------------------------------------------------------------------
|
|
179
649
|
end() {
|
|
180
650
|
if (this.state === "idle") return;
|
|
181
651
|
const wasState = this.state;
|
|
@@ -186,23 +656,32 @@ class DragResizeController {
|
|
|
186
656
|
state: "end",
|
|
187
657
|
transformData: {
|
|
188
658
|
type: "vertex-edit",
|
|
189
|
-
changes: {
|
|
190
|
-
|
|
191
|
-
|
|
659
|
+
changes: { vertices: this.currentVertices },
|
|
660
|
+
metadata: { vertexIndex: vertexIndex || void 0 }
|
|
661
|
+
}
|
|
662
|
+
});
|
|
663
|
+
} else if (wasState === "rotating") {
|
|
664
|
+
this.onUpdate({
|
|
665
|
+
state: "end",
|
|
666
|
+
transformData: {
|
|
667
|
+
type: "rotate",
|
|
668
|
+
changes: { rotation: this.lastComputedRotation },
|
|
192
669
|
metadata: {
|
|
193
|
-
|
|
670
|
+
rotationAngle: this.lastComputedRotation,
|
|
671
|
+
rotationDelta: this.rotationDelta,
|
|
672
|
+
rotationCenter: this.rotationCenter || void 0,
|
|
673
|
+
isSnapped: this.rotationSnappedAngle !== null,
|
|
674
|
+
snappedAngle: this.rotationSnappedAngle ?? void 0
|
|
194
675
|
}
|
|
195
676
|
}
|
|
196
677
|
});
|
|
197
678
|
} else {
|
|
198
|
-
const finalPosition = this.
|
|
679
|
+
const finalPosition = this.currentPosition || this.config.element;
|
|
199
680
|
this.onUpdate({
|
|
200
681
|
state: "end",
|
|
201
682
|
transformData: {
|
|
202
683
|
type: wasState === "dragging" ? "move" : "resize",
|
|
203
|
-
changes: {
|
|
204
|
-
rect: finalPosition
|
|
205
|
-
},
|
|
684
|
+
changes: { rect: finalPosition },
|
|
206
685
|
metadata: wasState === "dragging" ? void 0 : {
|
|
207
686
|
handle: handle || void 0,
|
|
208
687
|
maintainAspectRatio: this.config.maintainAspectRatio
|
|
@@ -219,11 +698,21 @@ class DragResizeController {
|
|
|
219
698
|
state: "end",
|
|
220
699
|
transformData: {
|
|
221
700
|
type: "vertex-edit",
|
|
222
|
-
changes: {
|
|
223
|
-
|
|
224
|
-
|
|
701
|
+
changes: { vertices: this.startVertices },
|
|
702
|
+
metadata: { vertexIndex: this.activeVertexIndex || void 0 }
|
|
703
|
+
}
|
|
704
|
+
});
|
|
705
|
+
} else if (this.state === "rotating") {
|
|
706
|
+
this.onUpdate({
|
|
707
|
+
state: "end",
|
|
708
|
+
transformData: {
|
|
709
|
+
type: "rotate",
|
|
710
|
+
changes: { rotation: this.initialRotation },
|
|
225
711
|
metadata: {
|
|
226
|
-
|
|
712
|
+
rotationAngle: this.initialRotation,
|
|
713
|
+
rotationDelta: 0,
|
|
714
|
+
rotationCenter: this.rotationCenter || void 0,
|
|
715
|
+
isSnapped: false
|
|
227
716
|
}
|
|
228
717
|
}
|
|
229
718
|
});
|
|
@@ -232,9 +721,7 @@ class DragResizeController {
|
|
|
232
721
|
state: "end",
|
|
233
722
|
transformData: {
|
|
234
723
|
type: this.state === "dragging" ? "move" : "resize",
|
|
235
|
-
changes: {
|
|
236
|
-
rect: this.startElement
|
|
237
|
-
},
|
|
724
|
+
changes: { rect: this.startElement },
|
|
238
725
|
metadata: this.state === "dragging" ? void 0 : {
|
|
239
726
|
handle: this.activeHandle || void 0,
|
|
240
727
|
maintainAspectRatio: this.config.maintainAspectRatio
|
|
@@ -244,18 +731,29 @@ class DragResizeController {
|
|
|
244
731
|
}
|
|
245
732
|
this.reset();
|
|
246
733
|
}
|
|
734
|
+
// ---------------------------------------------------------------------------
|
|
735
|
+
// Private: state management
|
|
736
|
+
// ---------------------------------------------------------------------------
|
|
247
737
|
reset() {
|
|
248
738
|
this.state = "idle";
|
|
249
739
|
this.startPoint = null;
|
|
250
740
|
this.startElement = null;
|
|
741
|
+
this.startRotationElement = null;
|
|
742
|
+
this.gestureRotationCenter = null;
|
|
251
743
|
this.activeHandle = null;
|
|
252
744
|
this.currentPosition = null;
|
|
253
745
|
this.activeVertexIndex = null;
|
|
254
746
|
this.startVertices = [];
|
|
747
|
+
this.rotationCenter = null;
|
|
748
|
+
this.centerScreen = null;
|
|
749
|
+
this.initialRotation = 0;
|
|
750
|
+
this.lastComputedRotation = 0;
|
|
751
|
+
this.rotationDelta = 0;
|
|
752
|
+
this.rotationSnappedAngle = null;
|
|
255
753
|
}
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
754
|
+
// ---------------------------------------------------------------------------
|
|
755
|
+
// Private: coordinate transformation (screen → page → local)
|
|
756
|
+
// ---------------------------------------------------------------------------
|
|
259
757
|
calculateDelta(clientX, clientY) {
|
|
260
758
|
if (!this.startPoint) return { x: 0, y: 0 };
|
|
261
759
|
const rawDelta = {
|
|
@@ -276,18 +774,50 @@ class DragResizeController {
|
|
|
276
774
|
y: -sin * scaledX + cos * scaledY
|
|
277
775
|
};
|
|
278
776
|
}
|
|
777
|
+
/**
|
|
778
|
+
* Calculate delta projected into the annotation's local (unrotated) coordinate space.
|
|
779
|
+
* Used for resize and vertex-edit where mouse movement must be mapped to the
|
|
780
|
+
* annotation's own axes, accounting for its rotation.
|
|
781
|
+
*/
|
|
782
|
+
calculateLocalDelta(clientX, clientY) {
|
|
783
|
+
const pageDelta = this.calculateDelta(clientX, clientY);
|
|
784
|
+
const { annotationRotation = 0 } = this.config;
|
|
785
|
+
if (annotationRotation === 0) return pageDelta;
|
|
786
|
+
const rad = annotationRotation * Math.PI / 180;
|
|
787
|
+
const cos = Math.cos(rad);
|
|
788
|
+
const sin = Math.sin(rad);
|
|
789
|
+
return {
|
|
790
|
+
x: cos * pageDelta.x + sin * pageDelta.y,
|
|
791
|
+
y: -sin * pageDelta.x + cos * pageDelta.y
|
|
792
|
+
};
|
|
793
|
+
}
|
|
794
|
+
// ---------------------------------------------------------------------------
|
|
795
|
+
// Private: vertex clamping
|
|
796
|
+
// ---------------------------------------------------------------------------
|
|
279
797
|
clampPoint(p) {
|
|
280
798
|
var _a;
|
|
281
799
|
const bbox = (_a = this.config.constraints) == null ? void 0 : _a.boundingBox;
|
|
282
800
|
if (!bbox) return p;
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
801
|
+
const { annotationRotation = 0 } = this.config;
|
|
802
|
+
if (annotationRotation === 0) {
|
|
803
|
+
return {
|
|
804
|
+
x: Math.max(0, Math.min(p.x, bbox.width)),
|
|
805
|
+
y: Math.max(0, Math.min(p.y, bbox.height))
|
|
806
|
+
};
|
|
807
|
+
}
|
|
808
|
+
const center = this.gestureRotationCenter ?? this.config.rotationCenter ?? {
|
|
809
|
+
x: this.config.element.origin.x + this.config.element.size.width / 2,
|
|
810
|
+
y: this.config.element.origin.y + this.config.element.size.height / 2
|
|
286
811
|
};
|
|
812
|
+
const visual = rotatePointAround(p, center, annotationRotation);
|
|
813
|
+
const clampedX = Math.max(0, Math.min(visual.x, bbox.width));
|
|
814
|
+
const clampedY = Math.max(0, Math.min(visual.y, bbox.height));
|
|
815
|
+
if (clampedX === visual.x && clampedY === visual.y) return p;
|
|
816
|
+
return rotatePointAround({ x: clampedX, y: clampedY }, center, -annotationRotation);
|
|
287
817
|
}
|
|
288
818
|
calculateVertexPosition(clientX, clientY) {
|
|
289
819
|
if (this.activeVertexIndex === null) return this.startVertices;
|
|
290
|
-
const delta = this.
|
|
820
|
+
const delta = this.calculateLocalDelta(clientX, clientY);
|
|
291
821
|
const newVertices = [...this.startVertices];
|
|
292
822
|
const currentVertex = newVertices[this.activeVertexIndex];
|
|
293
823
|
const moved = {
|
|
@@ -297,6 +827,9 @@ class DragResizeController {
|
|
|
297
827
|
newVertices[this.activeVertexIndex] = this.clampPoint(moved);
|
|
298
828
|
return newVertices;
|
|
299
829
|
}
|
|
830
|
+
// ---------------------------------------------------------------------------
|
|
831
|
+
// Private: drag position
|
|
832
|
+
// ---------------------------------------------------------------------------
|
|
300
833
|
calculateDragPosition(delta) {
|
|
301
834
|
if (!this.startElement) return this.config.element;
|
|
302
835
|
const position = {
|
|
@@ -309,262 +842,77 @@ class DragResizeController {
|
|
|
309
842
|
height: this.startElement.size.height
|
|
310
843
|
}
|
|
311
844
|
};
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
rect = this.enforceAspectRatio(rect, anchor, aspectRatio);
|
|
325
|
-
}
|
|
326
|
-
rect = this.clampToBounds(rect, anchor, aspectRatio);
|
|
327
|
-
return this.applyConstraints(rect);
|
|
328
|
-
}
|
|
329
|
-
/**
|
|
330
|
-
* Apply the mouse delta to produce a raw (unconstrained) resized rect.
|
|
331
|
-
*/
|
|
332
|
-
applyResizeDelta(delta, anchor) {
|
|
333
|
-
const start = this.startElement;
|
|
334
|
-
let x = start.origin.x;
|
|
335
|
-
let y = start.origin.y;
|
|
336
|
-
let width = start.size.width;
|
|
337
|
-
let height = start.size.height;
|
|
338
|
-
if (anchor.x === "left") {
|
|
339
|
-
width += delta.x;
|
|
340
|
-
} else if (anchor.x === "right") {
|
|
341
|
-
x += delta.x;
|
|
342
|
-
width -= delta.x;
|
|
343
|
-
}
|
|
344
|
-
if (anchor.y === "top") {
|
|
345
|
-
height += delta.y;
|
|
346
|
-
} else if (anchor.y === "bottom") {
|
|
347
|
-
y += delta.y;
|
|
348
|
-
height -= delta.y;
|
|
349
|
-
}
|
|
350
|
-
return { origin: { x, y }, size: { width, height } };
|
|
351
|
-
}
|
|
352
|
-
/**
|
|
353
|
-
* Enforce aspect ratio while respecting the anchor.
|
|
354
|
-
* For edge handles (center anchor on one axis), the rect expands symmetrically on that axis.
|
|
355
|
-
* For corner handles, the anchor corner stays fixed.
|
|
356
|
-
*/
|
|
357
|
-
enforceAspectRatio(rect, anchor, aspectRatio) {
|
|
358
|
-
const start = this.startElement;
|
|
359
|
-
let { x, y } = rect.origin;
|
|
360
|
-
let { width, height } = rect.size;
|
|
361
|
-
const isEdgeHandle = anchor.x === "center" || anchor.y === "center";
|
|
362
|
-
if (isEdgeHandle) {
|
|
363
|
-
if (anchor.y === "center") {
|
|
364
|
-
height = width / aspectRatio;
|
|
365
|
-
y = start.origin.y + (start.size.height - height) / 2;
|
|
845
|
+
const { annotationRotation = 0, constraints } = this.config;
|
|
846
|
+
const bbox = constraints == null ? void 0 : constraints.boundingBox;
|
|
847
|
+
if (annotationRotation !== 0 && bbox) {
|
|
848
|
+
let aabbW;
|
|
849
|
+
let aabbH;
|
|
850
|
+
let offsetX;
|
|
851
|
+
let offsetY;
|
|
852
|
+
if (this.startRotationElement) {
|
|
853
|
+
aabbW = this.startRotationElement.size.width;
|
|
854
|
+
aabbH = this.startRotationElement.size.height;
|
|
855
|
+
offsetX = this.startRotationElement.origin.x - this.startElement.origin.x;
|
|
856
|
+
offsetY = this.startRotationElement.origin.y - this.startElement.origin.y;
|
|
366
857
|
} else {
|
|
367
|
-
|
|
368
|
-
|
|
858
|
+
const rad = Math.abs(annotationRotation * Math.PI / 180);
|
|
859
|
+
const cos = Math.abs(Math.cos(rad));
|
|
860
|
+
const sin = Math.abs(Math.sin(rad));
|
|
861
|
+
const w = position.size.width;
|
|
862
|
+
const h = position.size.height;
|
|
863
|
+
aabbW = w * cos + h * sin;
|
|
864
|
+
aabbH = w * sin + h * cos;
|
|
865
|
+
offsetX = (w - aabbW) / 2;
|
|
866
|
+
offsetY = (h - aabbH) / 2;
|
|
369
867
|
}
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
height = width / aspectRatio;
|
|
375
|
-
} else {
|
|
376
|
-
width = height * aspectRatio;
|
|
377
|
-
}
|
|
378
|
-
}
|
|
379
|
-
if (anchor.x === "right") {
|
|
380
|
-
x = start.origin.x + start.size.width - width;
|
|
381
|
-
}
|
|
382
|
-
if (anchor.y === "bottom") {
|
|
383
|
-
y = start.origin.y + start.size.height - height;
|
|
868
|
+
let { x, y } = position.origin;
|
|
869
|
+
x = Math.max(-offsetX, Math.min(x, bbox.width - aabbW - offsetX));
|
|
870
|
+
y = Math.max(-offsetY, Math.min(y, bbox.height - aabbH - offsetY));
|
|
871
|
+
return { origin: { x, y }, size: position.size };
|
|
384
872
|
}
|
|
385
|
-
return
|
|
873
|
+
return applyConstraints(position, constraints, this.config.maintainAspectRatio ?? false);
|
|
386
874
|
}
|
|
875
|
+
// ---------------------------------------------------------------------------
|
|
876
|
+
// Private: rotation
|
|
877
|
+
// ---------------------------------------------------------------------------
|
|
387
878
|
/**
|
|
388
|
-
*
|
|
879
|
+
* Calculate the angle from the center to a point in screen coordinates.
|
|
389
880
|
*/
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
const
|
|
393
|
-
|
|
394
|
-
const
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
const
|
|
402
|
-
const
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
const
|
|
406
|
-
const
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
}
|
|
415
|
-
if (anchor.x === "left") {
|
|
416
|
-
x = anchorX;
|
|
417
|
-
} else if (anchor.x === "right") {
|
|
418
|
-
x = anchorX - width;
|
|
419
|
-
} else {
|
|
420
|
-
x = start.origin.x + (start.size.width - width) / 2;
|
|
421
|
-
}
|
|
422
|
-
if (anchor.y === "top") {
|
|
423
|
-
y = anchorY;
|
|
424
|
-
} else if (anchor.y === "bottom") {
|
|
425
|
-
y = anchorY - height;
|
|
426
|
-
} else {
|
|
427
|
-
y = start.origin.y + (start.size.height - height) / 2;
|
|
428
|
-
}
|
|
429
|
-
x = Math.max(0, Math.min(x, bbox.width - width));
|
|
430
|
-
y = Math.max(0, Math.min(y, bbox.height - height));
|
|
431
|
-
return { origin: { x, y }, size: { width, height } };
|
|
432
|
-
}
|
|
433
|
-
applyConstraints(position) {
|
|
434
|
-
const { constraints } = this.config;
|
|
435
|
-
if (!constraints) return position;
|
|
436
|
-
let {
|
|
437
|
-
origin: { x, y },
|
|
438
|
-
size: { width, height }
|
|
439
|
-
} = position;
|
|
440
|
-
const minW = constraints.minWidth ?? 1;
|
|
441
|
-
const minH = constraints.minHeight ?? 1;
|
|
442
|
-
const maxW = constraints.maxWidth;
|
|
443
|
-
const maxH = constraints.maxHeight;
|
|
444
|
-
if (this.config.maintainAspectRatio && width > 0 && height > 0) {
|
|
445
|
-
const ratio = width / height;
|
|
446
|
-
if (width < minW) {
|
|
447
|
-
width = minW;
|
|
448
|
-
height = width / ratio;
|
|
449
|
-
}
|
|
450
|
-
if (height < minH) {
|
|
451
|
-
height = minH;
|
|
452
|
-
width = height * ratio;
|
|
453
|
-
}
|
|
454
|
-
if (maxW !== void 0 && width > maxW) {
|
|
455
|
-
width = maxW;
|
|
456
|
-
height = width / ratio;
|
|
457
|
-
}
|
|
458
|
-
if (maxH !== void 0 && height > maxH) {
|
|
459
|
-
height = maxH;
|
|
460
|
-
width = height * ratio;
|
|
881
|
+
calculateAngleFromMouse(clientX, clientY) {
|
|
882
|
+
if (!this.centerScreen) return this.initialRotation;
|
|
883
|
+
const dx = clientX - this.centerScreen.x;
|
|
884
|
+
const dy = clientY - this.centerScreen.y;
|
|
885
|
+
const dist = Math.sqrt(dx * dx + dy * dy);
|
|
886
|
+
if (dist < 10) return this.lastComputedRotation;
|
|
887
|
+
const pageRotOffset = (this.config.pageRotation ?? 0) * 90;
|
|
888
|
+
const angleDeg = Math.atan2(dy, dx) * (180 / Math.PI) + 90 - pageRotOffset;
|
|
889
|
+
return normalizeAngle(Math.round(angleDeg));
|
|
890
|
+
}
|
|
891
|
+
applyRotationSnapping(angle) {
|
|
892
|
+
const snapAngles = this.config.rotationSnapAngles ?? [0, 90, 180, 270];
|
|
893
|
+
const threshold = this.config.rotationSnapThreshold ?? 4;
|
|
894
|
+
const normalizedAngle = normalizeAngle(angle);
|
|
895
|
+
for (const candidate of snapAngles) {
|
|
896
|
+
const normalizedCandidate = normalizeAngle(candidate);
|
|
897
|
+
const diff = Math.abs(normalizedAngle - normalizedCandidate);
|
|
898
|
+
const minimalDiff = Math.min(diff, 360 - diff);
|
|
899
|
+
if (minimalDiff <= threshold) {
|
|
900
|
+
return {
|
|
901
|
+
angle: normalizedCandidate,
|
|
902
|
+
isSnapped: true,
|
|
903
|
+
snapTarget: normalizedCandidate
|
|
904
|
+
};
|
|
461
905
|
}
|
|
462
|
-
} else {
|
|
463
|
-
width = Math.max(minW, width);
|
|
464
|
-
height = Math.max(minH, height);
|
|
465
|
-
if (maxW !== void 0) width = Math.min(maxW, width);
|
|
466
|
-
if (maxH !== void 0) height = Math.min(maxH, height);
|
|
467
|
-
}
|
|
468
|
-
if (constraints.boundingBox) {
|
|
469
|
-
x = Math.max(0, Math.min(x, constraints.boundingBox.width - width));
|
|
470
|
-
y = Math.max(0, Math.min(y, constraints.boundingBox.height - height));
|
|
471
906
|
}
|
|
472
|
-
return {
|
|
907
|
+
return { angle: normalizedAngle, isSnapped: false };
|
|
473
908
|
}
|
|
474
909
|
}
|
|
475
|
-
function diagonalCursor(handle, rot) {
|
|
476
|
-
const isOddRotation = rot % 2 === 1;
|
|
477
|
-
if (handle === "n" || handle === "s") {
|
|
478
|
-
return isOddRotation ? "ew-resize" : "ns-resize";
|
|
479
|
-
}
|
|
480
|
-
if (handle === "e" || handle === "w") {
|
|
481
|
-
return isOddRotation ? "ns-resize" : "ew-resize";
|
|
482
|
-
}
|
|
483
|
-
const diag0 = {
|
|
484
|
-
nw: "nwse-resize",
|
|
485
|
-
ne: "nesw-resize",
|
|
486
|
-
sw: "nesw-resize",
|
|
487
|
-
se: "nwse-resize"
|
|
488
|
-
};
|
|
489
|
-
if (!isOddRotation) return diag0[handle];
|
|
490
|
-
return { nw: "nesw-resize", ne: "nwse-resize", sw: "nwse-resize", se: "nesw-resize" }[handle];
|
|
491
|
-
}
|
|
492
|
-
function edgeOffset(k, spacing, mode) {
|
|
493
|
-
const base = -k / 2;
|
|
494
|
-
if (mode === "center") return base;
|
|
495
|
-
return mode === "outside" ? base - spacing : base + spacing;
|
|
496
|
-
}
|
|
497
|
-
function describeResizeFromConfig(cfg, ui = {}) {
|
|
498
|
-
const {
|
|
499
|
-
handleSize = 8,
|
|
500
|
-
spacing = 1,
|
|
501
|
-
offsetMode = "outside",
|
|
502
|
-
includeSides = false,
|
|
503
|
-
zIndex = 3,
|
|
504
|
-
rotationAwareCursor = true
|
|
505
|
-
} = ui;
|
|
506
|
-
const rotation = (cfg.pageRotation ?? 0) % 4;
|
|
507
|
-
const off = (edge) => ({
|
|
508
|
-
[edge]: edgeOffset(handleSize, spacing, offsetMode) + "px"
|
|
509
|
-
});
|
|
510
|
-
const corners = [
|
|
511
|
-
["nw", { ...off("top"), ...off("left") }],
|
|
512
|
-
["ne", { ...off("top"), ...off("right") }],
|
|
513
|
-
["sw", { ...off("bottom"), ...off("left") }],
|
|
514
|
-
["se", { ...off("bottom"), ...off("right") }]
|
|
515
|
-
];
|
|
516
|
-
const sides = includeSides ? [
|
|
517
|
-
["n", { ...off("top"), left: `calc(50% - ${handleSize / 2}px)` }],
|
|
518
|
-
["s", { ...off("bottom"), left: `calc(50% - ${handleSize / 2}px)` }],
|
|
519
|
-
["w", { ...off("left"), top: `calc(50% - ${handleSize / 2}px)` }],
|
|
520
|
-
["e", { ...off("right"), top: `calc(50% - ${handleSize / 2}px)` }]
|
|
521
|
-
] : [];
|
|
522
|
-
const all = [...corners, ...sides];
|
|
523
|
-
return all.map(([handle, pos]) => ({
|
|
524
|
-
handle,
|
|
525
|
-
style: {
|
|
526
|
-
position: "absolute",
|
|
527
|
-
width: handleSize + "px",
|
|
528
|
-
height: handleSize + "px",
|
|
529
|
-
borderRadius: "50%",
|
|
530
|
-
zIndex,
|
|
531
|
-
cursor: rotationAwareCursor ? diagonalCursor(handle, rotation) : "default",
|
|
532
|
-
touchAction: "none",
|
|
533
|
-
...pos
|
|
534
|
-
},
|
|
535
|
-
attrs: { "data-epdf-handle": handle }
|
|
536
|
-
}));
|
|
537
|
-
}
|
|
538
|
-
function describeVerticesFromConfig(cfg, ui = {}, liveVertices) {
|
|
539
|
-
const { vertexSize = 12, zIndex = 4 } = ui;
|
|
540
|
-
const rect = cfg.element;
|
|
541
|
-
const scale = cfg.scale ?? 1;
|
|
542
|
-
const verts = liveVertices ?? cfg.vertices ?? [];
|
|
543
|
-
return verts.map((v, i) => {
|
|
544
|
-
const left = (v.x - rect.origin.x) * scale - vertexSize / 2;
|
|
545
|
-
const top = (v.y - rect.origin.y) * scale - vertexSize / 2;
|
|
546
|
-
return {
|
|
547
|
-
handle: "nw",
|
|
548
|
-
// not used; kept for type
|
|
549
|
-
style: {
|
|
550
|
-
position: "absolute",
|
|
551
|
-
left: left + "px",
|
|
552
|
-
top: top + "px",
|
|
553
|
-
width: vertexSize + "px",
|
|
554
|
-
height: vertexSize + "px",
|
|
555
|
-
borderRadius: "50%",
|
|
556
|
-
cursor: "pointer",
|
|
557
|
-
zIndex,
|
|
558
|
-
touchAction: "none"
|
|
559
|
-
},
|
|
560
|
-
attrs: { "data-epdf-vertex": i }
|
|
561
|
-
};
|
|
562
|
-
});
|
|
563
|
-
}
|
|
564
910
|
function useDragResize(options) {
|
|
565
911
|
const { onUpdate, enabled = true, ...config } = options;
|
|
566
912
|
const controllerRef = useRef(null);
|
|
567
913
|
const onUpdateRef = useRef(onUpdate);
|
|
914
|
+
const activePointerIdRef = useRef(null);
|
|
915
|
+
const activeTargetRef = useRef(null);
|
|
568
916
|
useEffect(() => {
|
|
569
917
|
onUpdateRef.current = onUpdate;
|
|
570
918
|
}, [onUpdate]);
|
|
@@ -582,12 +930,81 @@ function useDragResize(options) {
|
|
|
582
930
|
}
|
|
583
931
|
}, [
|
|
584
932
|
config.element,
|
|
933
|
+
config.rotationCenter,
|
|
934
|
+
config.rotationElement,
|
|
585
935
|
config.constraints,
|
|
586
936
|
config.maintainAspectRatio,
|
|
587
937
|
config.pageRotation,
|
|
938
|
+
config.annotationRotation,
|
|
588
939
|
config.scale,
|
|
589
940
|
config.vertices
|
|
590
941
|
]);
|
|
942
|
+
const endPointerSession = useCallback((pointerId) => {
|
|
943
|
+
var _a, _b;
|
|
944
|
+
const activePointerId = activePointerIdRef.current;
|
|
945
|
+
const target = activeTargetRef.current;
|
|
946
|
+
const id = pointerId ?? activePointerId;
|
|
947
|
+
if (target && id !== null) {
|
|
948
|
+
try {
|
|
949
|
+
if ((_a = target.hasPointerCapture) == null ? void 0 : _a.call(target, id)) {
|
|
950
|
+
(_b = target.releasePointerCapture) == null ? void 0 : _b.call(target, id);
|
|
951
|
+
}
|
|
952
|
+
} catch {
|
|
953
|
+
}
|
|
954
|
+
}
|
|
955
|
+
activePointerIdRef.current = null;
|
|
956
|
+
activeTargetRef.current = null;
|
|
957
|
+
}, []);
|
|
958
|
+
const startPointerSession = useCallback(
|
|
959
|
+
(e) => {
|
|
960
|
+
var _a;
|
|
961
|
+
if (activePointerIdRef.current !== null && activePointerIdRef.current !== e.pointerId) {
|
|
962
|
+
(_a = controllerRef.current) == null ? void 0 : _a.end();
|
|
963
|
+
endPointerSession(activePointerIdRef.current);
|
|
964
|
+
}
|
|
965
|
+
const target = e.currentTarget;
|
|
966
|
+
activePointerIdRef.current = e.pointerId;
|
|
967
|
+
activeTargetRef.current = target;
|
|
968
|
+
try {
|
|
969
|
+
target.setPointerCapture(e.pointerId);
|
|
970
|
+
} catch {
|
|
971
|
+
}
|
|
972
|
+
},
|
|
973
|
+
[endPointerSession]
|
|
974
|
+
);
|
|
975
|
+
useEffect(() => {
|
|
976
|
+
const eventTarget = globalThis;
|
|
977
|
+
const handleGlobalPointerEnd = (e) => {
|
|
978
|
+
var _a;
|
|
979
|
+
const activePointerId = activePointerIdRef.current;
|
|
980
|
+
if (activePointerId === null || e.pointerId !== activePointerId) return;
|
|
981
|
+
(_a = controllerRef.current) == null ? void 0 : _a.end();
|
|
982
|
+
endPointerSession(e.pointerId);
|
|
983
|
+
};
|
|
984
|
+
const handleWindowBlur = () => {
|
|
985
|
+
var _a;
|
|
986
|
+
if (activePointerIdRef.current === null) return;
|
|
987
|
+
(_a = controllerRef.current) == null ? void 0 : _a.end();
|
|
988
|
+
endPointerSession();
|
|
989
|
+
};
|
|
990
|
+
eventTarget.addEventListener("pointerup", handleGlobalPointerEnd, true);
|
|
991
|
+
eventTarget.addEventListener("pointercancel", handleGlobalPointerEnd, true);
|
|
992
|
+
eventTarget.addEventListener("blur", handleWindowBlur, true);
|
|
993
|
+
return () => {
|
|
994
|
+
eventTarget.removeEventListener("pointerup", handleGlobalPointerEnd, true);
|
|
995
|
+
eventTarget.removeEventListener("pointercancel", handleGlobalPointerEnd, true);
|
|
996
|
+
eventTarget.removeEventListener("blur", handleWindowBlur, true);
|
|
997
|
+
};
|
|
998
|
+
}, [endPointerSession]);
|
|
999
|
+
useEffect(() => {
|
|
1000
|
+
return () => {
|
|
1001
|
+
var _a;
|
|
1002
|
+
if (activePointerIdRef.current !== null) {
|
|
1003
|
+
(_a = controllerRef.current) == null ? void 0 : _a.end();
|
|
1004
|
+
endPointerSession();
|
|
1005
|
+
}
|
|
1006
|
+
};
|
|
1007
|
+
}, [endPointerSession]);
|
|
591
1008
|
const handleDragStart = useCallback(
|
|
592
1009
|
(e) => {
|
|
593
1010
|
var _a;
|
|
@@ -595,23 +1012,46 @@ function useDragResize(options) {
|
|
|
595
1012
|
e.preventDefault();
|
|
596
1013
|
e.stopPropagation();
|
|
597
1014
|
(_a = controllerRef.current) == null ? void 0 : _a.startDrag(e.clientX, e.clientY);
|
|
598
|
-
|
|
1015
|
+
startPointerSession(e);
|
|
599
1016
|
},
|
|
600
|
-
[enabled]
|
|
1017
|
+
[enabled, startPointerSession]
|
|
1018
|
+
);
|
|
1019
|
+
const handleMove = useCallback(
|
|
1020
|
+
(e) => {
|
|
1021
|
+
var _a;
|
|
1022
|
+
e.preventDefault();
|
|
1023
|
+
e.stopPropagation();
|
|
1024
|
+
const activePointerId = activePointerIdRef.current;
|
|
1025
|
+
if (activePointerId !== null && e.pointerId !== activePointerId) return;
|
|
1026
|
+
(_a = controllerRef.current) == null ? void 0 : _a.move(e.clientX, e.clientY, e.buttons);
|
|
1027
|
+
if (activePointerIdRef.current === e.pointerId && e.buttons === 0) {
|
|
1028
|
+
endPointerSession(e.pointerId);
|
|
1029
|
+
}
|
|
1030
|
+
},
|
|
1031
|
+
[endPointerSession]
|
|
1032
|
+
);
|
|
1033
|
+
const handleEndLike = useCallback(
|
|
1034
|
+
(e) => {
|
|
1035
|
+
var _a;
|
|
1036
|
+
e.preventDefault();
|
|
1037
|
+
e.stopPropagation();
|
|
1038
|
+
const activePointerId = activePointerIdRef.current;
|
|
1039
|
+
if (activePointerId !== null && e.pointerId !== activePointerId) return;
|
|
1040
|
+
(_a = controllerRef.current) == null ? void 0 : _a.end();
|
|
1041
|
+
endPointerSession(e.pointerId);
|
|
1042
|
+
},
|
|
1043
|
+
[endPointerSession]
|
|
1044
|
+
);
|
|
1045
|
+
const handleLostPointerCapture = useCallback(
|
|
1046
|
+
(e) => {
|
|
1047
|
+
var _a;
|
|
1048
|
+
const activePointerId = activePointerIdRef.current;
|
|
1049
|
+
if (activePointerId === null || e.pointerId !== activePointerId) return;
|
|
1050
|
+
(_a = controllerRef.current) == null ? void 0 : _a.end();
|
|
1051
|
+
endPointerSession(e.pointerId);
|
|
1052
|
+
},
|
|
1053
|
+
[endPointerSession]
|
|
601
1054
|
);
|
|
602
|
-
const handleMove = useCallback((e) => {
|
|
603
|
-
var _a;
|
|
604
|
-
e.preventDefault();
|
|
605
|
-
e.stopPropagation();
|
|
606
|
-
(_a = controllerRef.current) == null ? void 0 : _a.move(e.clientX, e.clientY);
|
|
607
|
-
}, []);
|
|
608
|
-
const handleEnd = useCallback((e) => {
|
|
609
|
-
var _a, _b, _c;
|
|
610
|
-
e.preventDefault();
|
|
611
|
-
e.stopPropagation();
|
|
612
|
-
(_a = controllerRef.current) == null ? void 0 : _a.end();
|
|
613
|
-
(_c = (_b = e.currentTarget).releasePointerCapture) == null ? void 0 : _c.call(_b, e.pointerId);
|
|
614
|
-
}, []);
|
|
615
1055
|
const createResizeHandler = useCallback(
|
|
616
1056
|
(handle) => ({
|
|
617
1057
|
onPointerDown: (e) => {
|
|
@@ -620,13 +1060,14 @@ function useDragResize(options) {
|
|
|
620
1060
|
e.preventDefault();
|
|
621
1061
|
e.stopPropagation();
|
|
622
1062
|
(_a = controllerRef.current) == null ? void 0 : _a.startResize(handle, e.clientX, e.clientY);
|
|
623
|
-
|
|
1063
|
+
startPointerSession(e);
|
|
624
1064
|
},
|
|
625
1065
|
onPointerMove: handleMove,
|
|
626
|
-
onPointerUp:
|
|
627
|
-
onPointerCancel:
|
|
1066
|
+
onPointerUp: handleEndLike,
|
|
1067
|
+
onPointerCancel: handleEndLike,
|
|
1068
|
+
onLostPointerCapture: handleLostPointerCapture
|
|
628
1069
|
}),
|
|
629
|
-
[enabled, handleMove,
|
|
1070
|
+
[enabled, handleMove, handleEndLike, handleLostPointerCapture, startPointerSession]
|
|
630
1071
|
);
|
|
631
1072
|
const createVertexHandler = useCallback(
|
|
632
1073
|
(vertexIndex) => ({
|
|
@@ -636,41 +1077,74 @@ function useDragResize(options) {
|
|
|
636
1077
|
e.preventDefault();
|
|
637
1078
|
e.stopPropagation();
|
|
638
1079
|
(_a = controllerRef.current) == null ? void 0 : _a.startVertexEdit(vertexIndex, e.clientX, e.clientY);
|
|
639
|
-
|
|
1080
|
+
startPointerSession(e);
|
|
640
1081
|
},
|
|
641
1082
|
onPointerMove: handleMove,
|
|
642
|
-
onPointerUp:
|
|
643
|
-
onPointerCancel:
|
|
1083
|
+
onPointerUp: handleEndLike,
|
|
1084
|
+
onPointerCancel: handleEndLike,
|
|
1085
|
+
onLostPointerCapture: handleLostPointerCapture
|
|
644
1086
|
}),
|
|
645
|
-
[enabled, handleMove,
|
|
1087
|
+
[enabled, handleMove, handleEndLike, handleLostPointerCapture, startPointerSession]
|
|
1088
|
+
);
|
|
1089
|
+
const createRotationHandler = useCallback(
|
|
1090
|
+
(initialRotation = 0, orbitRadiusPx) => ({
|
|
1091
|
+
onPointerDown: (e) => {
|
|
1092
|
+
var _a;
|
|
1093
|
+
if (!enabled) return;
|
|
1094
|
+
e.preventDefault();
|
|
1095
|
+
e.stopPropagation();
|
|
1096
|
+
const handleRect = e.currentTarget.getBoundingClientRect();
|
|
1097
|
+
const handleCenterX = handleRect.left + handleRect.width / 2;
|
|
1098
|
+
const handleCenterY = handleRect.top + handleRect.height / 2;
|
|
1099
|
+
(_a = controllerRef.current) == null ? void 0 : _a.startRotation(
|
|
1100
|
+
handleCenterX,
|
|
1101
|
+
handleCenterY,
|
|
1102
|
+
initialRotation,
|
|
1103
|
+
orbitRadiusPx
|
|
1104
|
+
);
|
|
1105
|
+
startPointerSession(e);
|
|
1106
|
+
},
|
|
1107
|
+
onPointerMove: handleMove,
|
|
1108
|
+
onPointerUp: handleEndLike,
|
|
1109
|
+
onPointerCancel: handleEndLike,
|
|
1110
|
+
onLostPointerCapture: handleLostPointerCapture
|
|
1111
|
+
}),
|
|
1112
|
+
[enabled, handleMove, handleEndLike, handleLostPointerCapture, startPointerSession]
|
|
646
1113
|
);
|
|
647
1114
|
return {
|
|
648
1115
|
dragProps: enabled ? {
|
|
649
1116
|
onPointerDown: handleDragStart,
|
|
650
1117
|
onPointerMove: handleMove,
|
|
651
|
-
onPointerUp:
|
|
652
|
-
onPointerCancel:
|
|
1118
|
+
onPointerUp: handleEndLike,
|
|
1119
|
+
onPointerCancel: handleEndLike,
|
|
1120
|
+
onLostPointerCapture: handleLostPointerCapture
|
|
653
1121
|
} : {},
|
|
654
1122
|
createResizeProps: createResizeHandler,
|
|
655
|
-
createVertexProps: createVertexHandler
|
|
1123
|
+
createVertexProps: createVertexHandler,
|
|
1124
|
+
createRotationProps: createRotationHandler
|
|
656
1125
|
};
|
|
657
1126
|
}
|
|
658
1127
|
function useInteractionHandles(opts) {
|
|
1128
|
+
var _a, _b, _c, _d, _e, _f;
|
|
659
1129
|
const {
|
|
660
1130
|
controller,
|
|
661
1131
|
resizeUI,
|
|
662
1132
|
vertexUI,
|
|
1133
|
+
rotationUI,
|
|
663
1134
|
includeVertices = false,
|
|
1135
|
+
includeRotation = false,
|
|
1136
|
+
currentRotation = 0,
|
|
664
1137
|
handleAttrs,
|
|
665
|
-
vertexAttrs
|
|
1138
|
+
vertexAttrs,
|
|
1139
|
+
rotationAttrs
|
|
666
1140
|
} = opts;
|
|
667
|
-
const { dragProps, createResizeProps, createVertexProps } = useDragResize(controller);
|
|
1141
|
+
const { dragProps, createResizeProps, createVertexProps, createRotationProps } = useDragResize(controller);
|
|
668
1142
|
const resize = useMemo(() => {
|
|
669
1143
|
const desc = describeResizeFromConfig(controller, resizeUI);
|
|
670
1144
|
return desc.map((d) => {
|
|
671
|
-
var
|
|
1145
|
+
var _a2;
|
|
672
1146
|
return {
|
|
673
|
-
key: (
|
|
1147
|
+
key: (_a2 = d.attrs) == null ? void 0 : _a2["data-epdf-handle"],
|
|
674
1148
|
style: d.style,
|
|
675
1149
|
...createResizeProps(d.handle),
|
|
676
1150
|
...d.attrs ?? {},
|
|
@@ -684,6 +1158,7 @@ function useInteractionHandles(opts) {
|
|
|
684
1158
|
controller.element.size.height,
|
|
685
1159
|
controller.scale,
|
|
686
1160
|
controller.pageRotation,
|
|
1161
|
+
controller.annotationRotation,
|
|
687
1162
|
controller.maintainAspectRatio,
|
|
688
1163
|
resizeUI == null ? void 0 : resizeUI.handleSize,
|
|
689
1164
|
resizeUI == null ? void 0 : resizeUI.spacing,
|
|
@@ -718,7 +1193,44 @@ function useInteractionHandles(opts) {
|
|
|
718
1193
|
createVertexProps,
|
|
719
1194
|
vertexAttrs
|
|
720
1195
|
]);
|
|
721
|
-
|
|
1196
|
+
const rotation = useMemo(() => {
|
|
1197
|
+
if (!includeRotation) return null;
|
|
1198
|
+
const desc = describeRotationFromConfig(controller, rotationUI, currentRotation);
|
|
1199
|
+
return {
|
|
1200
|
+
handle: {
|
|
1201
|
+
style: desc.handleStyle,
|
|
1202
|
+
...createRotationProps(currentRotation, desc.radius),
|
|
1203
|
+
...desc.attrs ?? {},
|
|
1204
|
+
...(rotationAttrs == null ? void 0 : rotationAttrs()) ?? {}
|
|
1205
|
+
},
|
|
1206
|
+
connector: {
|
|
1207
|
+
style: desc.connectorStyle,
|
|
1208
|
+
"data-epdf-rotation-connector": true
|
|
1209
|
+
}
|
|
1210
|
+
};
|
|
1211
|
+
}, [
|
|
1212
|
+
includeRotation,
|
|
1213
|
+
controller.element.origin.x,
|
|
1214
|
+
controller.element.origin.y,
|
|
1215
|
+
controller.element.size.width,
|
|
1216
|
+
controller.element.size.height,
|
|
1217
|
+
(_a = controller.rotationCenter) == null ? void 0 : _a.x,
|
|
1218
|
+
(_b = controller.rotationCenter) == null ? void 0 : _b.y,
|
|
1219
|
+
(_c = controller.rotationElement) == null ? void 0 : _c.origin.x,
|
|
1220
|
+
(_d = controller.rotationElement) == null ? void 0 : _d.origin.y,
|
|
1221
|
+
(_e = controller.rotationElement) == null ? void 0 : _e.size.width,
|
|
1222
|
+
(_f = controller.rotationElement) == null ? void 0 : _f.size.height,
|
|
1223
|
+
controller.scale,
|
|
1224
|
+
currentRotation,
|
|
1225
|
+
rotationUI == null ? void 0 : rotationUI.handleSize,
|
|
1226
|
+
rotationUI == null ? void 0 : rotationUI.margin,
|
|
1227
|
+
rotationUI == null ? void 0 : rotationUI.zIndex,
|
|
1228
|
+
rotationUI == null ? void 0 : rotationUI.showConnector,
|
|
1229
|
+
rotationUI == null ? void 0 : rotationUI.connectorWidth,
|
|
1230
|
+
createRotationProps,
|
|
1231
|
+
rotationAttrs
|
|
1232
|
+
]);
|
|
1233
|
+
return { dragProps, resize, vertices, rotation };
|
|
722
1234
|
}
|
|
723
1235
|
function useDoublePressProps(onDouble, { delay = 300, tolerancePx = 18 } = {}) {
|
|
724
1236
|
const last = useRef({ t: 0, x: 0, y: 0 });
|