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