@embedpdf/utils 2.4.1 → 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 -317
- 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 -317
- 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 -288
- 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 -292
- package/dist/vue/index.js.map +1 -1
- package/package.json +2 -2
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,257 +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 diag0 = {
|
|
478
|
-
nw: "nwse-resize",
|
|
479
|
-
ne: "nesw-resize",
|
|
480
|
-
sw: "nesw-resize",
|
|
481
|
-
se: "nwse-resize"
|
|
482
|
-
};
|
|
483
|
-
if (handle === "n" || handle === "s") return "ns-resize";
|
|
484
|
-
if (handle === "e" || handle === "w") return "ew-resize";
|
|
485
|
-
if (rot % 2 === 0) return diag0[handle];
|
|
486
|
-
return { nw: "nesw-resize", ne: "nwse-resize", sw: "nwse-resize", se: "nesw-resize" }[handle];
|
|
487
|
-
}
|
|
488
|
-
function edgeOffset(k, spacing, mode) {
|
|
489
|
-
const base = -k / 2;
|
|
490
|
-
if (mode === "center") return base;
|
|
491
|
-
return mode === "outside" ? base - spacing : base + spacing;
|
|
492
|
-
}
|
|
493
|
-
function describeResizeFromConfig(cfg, ui = {}) {
|
|
494
|
-
const {
|
|
495
|
-
handleSize = 8,
|
|
496
|
-
spacing = 1,
|
|
497
|
-
offsetMode = "outside",
|
|
498
|
-
includeSides = false,
|
|
499
|
-
zIndex = 3,
|
|
500
|
-
rotationAwareCursor = true
|
|
501
|
-
} = ui;
|
|
502
|
-
const rotation = (cfg.pageRotation ?? 0) % 4;
|
|
503
|
-
const off = (edge) => ({
|
|
504
|
-
[edge]: edgeOffset(handleSize, spacing, offsetMode) + "px"
|
|
505
|
-
});
|
|
506
|
-
const corners = [
|
|
507
|
-
["nw", { ...off("top"), ...off("left") }],
|
|
508
|
-
["ne", { ...off("top"), ...off("right") }],
|
|
509
|
-
["sw", { ...off("bottom"), ...off("left") }],
|
|
510
|
-
["se", { ...off("bottom"), ...off("right") }]
|
|
511
|
-
];
|
|
512
|
-
const sides = includeSides ? [
|
|
513
|
-
["n", { ...off("top"), left: `calc(50% - ${handleSize / 2}px)` }],
|
|
514
|
-
["s", { ...off("bottom"), left: `calc(50% - ${handleSize / 2}px)` }],
|
|
515
|
-
["w", { ...off("left"), top: `calc(50% - ${handleSize / 2}px)` }],
|
|
516
|
-
["e", { ...off("right"), top: `calc(50% - ${handleSize / 2}px)` }]
|
|
517
|
-
] : [];
|
|
518
|
-
const all = [...corners, ...sides];
|
|
519
|
-
return all.map(([handle, pos]) => ({
|
|
520
|
-
handle,
|
|
521
|
-
style: {
|
|
522
|
-
position: "absolute",
|
|
523
|
-
width: handleSize + "px",
|
|
524
|
-
height: handleSize + "px",
|
|
525
|
-
borderRadius: "50%",
|
|
526
|
-
zIndex,
|
|
527
|
-
cursor: rotationAwareCursor ? diagonalCursor(handle, rotation) : "default",
|
|
528
|
-
touchAction: "none",
|
|
529
|
-
...pos
|
|
530
|
-
},
|
|
531
|
-
attrs: { "data-epdf-handle": handle }
|
|
532
|
-
}));
|
|
533
|
-
}
|
|
534
|
-
function describeVerticesFromConfig(cfg, ui = {}, liveVertices) {
|
|
535
|
-
const { vertexSize = 12, zIndex = 4 } = ui;
|
|
536
|
-
const rect = cfg.element;
|
|
537
|
-
const scale = cfg.scale ?? 1;
|
|
538
|
-
const verts = liveVertices ?? cfg.vertices ?? [];
|
|
539
|
-
return verts.map((v, i) => {
|
|
540
|
-
const left = (v.x - rect.origin.x) * scale - vertexSize / 2;
|
|
541
|
-
const top = (v.y - rect.origin.y) * scale - vertexSize / 2;
|
|
542
|
-
return {
|
|
543
|
-
handle: "nw",
|
|
544
|
-
// not used; kept for type
|
|
545
|
-
style: {
|
|
546
|
-
position: "absolute",
|
|
547
|
-
left: left + "px",
|
|
548
|
-
top: top + "px",
|
|
549
|
-
width: vertexSize + "px",
|
|
550
|
-
height: vertexSize + "px",
|
|
551
|
-
borderRadius: "50%",
|
|
552
|
-
cursor: "pointer",
|
|
553
|
-
zIndex,
|
|
554
|
-
touchAction: "none"
|
|
555
|
-
},
|
|
556
|
-
attrs: { "data-epdf-vertex": i }
|
|
557
|
-
};
|
|
558
|
-
});
|
|
559
|
-
}
|
|
560
911
|
function useDragResize(options) {
|
|
561
912
|
const { onUpdate, enabled = true, ...config } = options;
|
|
562
913
|
const controllerRef = useRef(null);
|
|
563
914
|
const onUpdateRef = useRef(onUpdate);
|
|
915
|
+
const activePointerIdRef = useRef(null);
|
|
916
|
+
const activeTargetRef = useRef(null);
|
|
564
917
|
useEffect(() => {
|
|
565
918
|
onUpdateRef.current = onUpdate;
|
|
566
919
|
}, [onUpdate]);
|
|
@@ -578,12 +931,81 @@ function useDragResize(options) {
|
|
|
578
931
|
}
|
|
579
932
|
}, [
|
|
580
933
|
config.element,
|
|
934
|
+
config.rotationCenter,
|
|
935
|
+
config.rotationElement,
|
|
581
936
|
config.constraints,
|
|
582
937
|
config.maintainAspectRatio,
|
|
583
938
|
config.pageRotation,
|
|
939
|
+
config.annotationRotation,
|
|
584
940
|
config.scale,
|
|
585
941
|
config.vertices
|
|
586
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]);
|
|
587
1009
|
const handleDragStart = useCallback(
|
|
588
1010
|
(e) => {
|
|
589
1011
|
var _a;
|
|
@@ -591,23 +1013,46 @@ function useDragResize(options) {
|
|
|
591
1013
|
e.preventDefault();
|
|
592
1014
|
e.stopPropagation();
|
|
593
1015
|
(_a = controllerRef.current) == null ? void 0 : _a.startDrag(e.clientX, e.clientY);
|
|
594
|
-
|
|
1016
|
+
startPointerSession(e);
|
|
595
1017
|
},
|
|
596
|
-
[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]
|
|
597
1055
|
);
|
|
598
|
-
const handleMove = useCallback((e) => {
|
|
599
|
-
var _a;
|
|
600
|
-
e.preventDefault();
|
|
601
|
-
e.stopPropagation();
|
|
602
|
-
(_a = controllerRef.current) == null ? void 0 : _a.move(e.clientX, e.clientY);
|
|
603
|
-
}, []);
|
|
604
|
-
const handleEnd = useCallback((e) => {
|
|
605
|
-
var _a, _b, _c;
|
|
606
|
-
e.preventDefault();
|
|
607
|
-
e.stopPropagation();
|
|
608
|
-
(_a = controllerRef.current) == null ? void 0 : _a.end();
|
|
609
|
-
(_c = (_b = e.currentTarget).releasePointerCapture) == null ? void 0 : _c.call(_b, e.pointerId);
|
|
610
|
-
}, []);
|
|
611
1056
|
const createResizeHandler = useCallback(
|
|
612
1057
|
(handle) => ({
|
|
613
1058
|
onPointerDown: (e) => {
|
|
@@ -616,13 +1061,14 @@ function useDragResize(options) {
|
|
|
616
1061
|
e.preventDefault();
|
|
617
1062
|
e.stopPropagation();
|
|
618
1063
|
(_a = controllerRef.current) == null ? void 0 : _a.startResize(handle, e.clientX, e.clientY);
|
|
619
|
-
|
|
1064
|
+
startPointerSession(e);
|
|
620
1065
|
},
|
|
621
1066
|
onPointerMove: handleMove,
|
|
622
|
-
onPointerUp:
|
|
623
|
-
onPointerCancel:
|
|
1067
|
+
onPointerUp: handleEndLike,
|
|
1068
|
+
onPointerCancel: handleEndLike,
|
|
1069
|
+
onLostPointerCapture: handleLostPointerCapture
|
|
624
1070
|
}),
|
|
625
|
-
[enabled, handleMove,
|
|
1071
|
+
[enabled, handleMove, handleEndLike, handleLostPointerCapture, startPointerSession]
|
|
626
1072
|
);
|
|
627
1073
|
const createVertexHandler = useCallback(
|
|
628
1074
|
(vertexIndex) => ({
|
|
@@ -632,41 +1078,74 @@ function useDragResize(options) {
|
|
|
632
1078
|
e.preventDefault();
|
|
633
1079
|
e.stopPropagation();
|
|
634
1080
|
(_a = controllerRef.current) == null ? void 0 : _a.startVertexEdit(vertexIndex, e.clientX, e.clientY);
|
|
635
|
-
|
|
1081
|
+
startPointerSession(e);
|
|
636
1082
|
},
|
|
637
1083
|
onPointerMove: handleMove,
|
|
638
|
-
onPointerUp:
|
|
639
|
-
onPointerCancel:
|
|
1084
|
+
onPointerUp: handleEndLike,
|
|
1085
|
+
onPointerCancel: handleEndLike,
|
|
1086
|
+
onLostPointerCapture: handleLostPointerCapture
|
|
640
1087
|
}),
|
|
641
|
-
[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]
|
|
642
1114
|
);
|
|
643
1115
|
return {
|
|
644
1116
|
dragProps: enabled ? {
|
|
645
1117
|
onPointerDown: handleDragStart,
|
|
646
1118
|
onPointerMove: handleMove,
|
|
647
|
-
onPointerUp:
|
|
648
|
-
onPointerCancel:
|
|
1119
|
+
onPointerUp: handleEndLike,
|
|
1120
|
+
onPointerCancel: handleEndLike,
|
|
1121
|
+
onLostPointerCapture: handleLostPointerCapture
|
|
649
1122
|
} : {},
|
|
650
1123
|
createResizeProps: createResizeHandler,
|
|
651
|
-
createVertexProps: createVertexHandler
|
|
1124
|
+
createVertexProps: createVertexHandler,
|
|
1125
|
+
createRotationProps: createRotationHandler
|
|
652
1126
|
};
|
|
653
1127
|
}
|
|
654
1128
|
function useInteractionHandles(opts) {
|
|
1129
|
+
var _a, _b, _c, _d, _e, _f;
|
|
655
1130
|
const {
|
|
656
1131
|
controller,
|
|
657
1132
|
resizeUI,
|
|
658
1133
|
vertexUI,
|
|
1134
|
+
rotationUI,
|
|
659
1135
|
includeVertices = false,
|
|
1136
|
+
includeRotation = false,
|
|
1137
|
+
currentRotation = 0,
|
|
660
1138
|
handleAttrs,
|
|
661
|
-
vertexAttrs
|
|
1139
|
+
vertexAttrs,
|
|
1140
|
+
rotationAttrs
|
|
662
1141
|
} = opts;
|
|
663
|
-
const { dragProps, createResizeProps, createVertexProps } = useDragResize(controller);
|
|
1142
|
+
const { dragProps, createResizeProps, createVertexProps, createRotationProps } = useDragResize(controller);
|
|
664
1143
|
const resize = useMemo(() => {
|
|
665
1144
|
const desc = describeResizeFromConfig(controller, resizeUI);
|
|
666
1145
|
return desc.map((d) => {
|
|
667
|
-
var
|
|
1146
|
+
var _a2;
|
|
668
1147
|
return {
|
|
669
|
-
key: (
|
|
1148
|
+
key: (_a2 = d.attrs) == null ? void 0 : _a2["data-epdf-handle"],
|
|
670
1149
|
style: d.style,
|
|
671
1150
|
...createResizeProps(d.handle),
|
|
672
1151
|
...d.attrs ?? {},
|
|
@@ -680,6 +1159,7 @@ function useInteractionHandles(opts) {
|
|
|
680
1159
|
controller.element.size.height,
|
|
681
1160
|
controller.scale,
|
|
682
1161
|
controller.pageRotation,
|
|
1162
|
+
controller.annotationRotation,
|
|
683
1163
|
controller.maintainAspectRatio,
|
|
684
1164
|
resizeUI == null ? void 0 : resizeUI.handleSize,
|
|
685
1165
|
resizeUI == null ? void 0 : resizeUI.spacing,
|
|
@@ -714,7 +1194,44 @@ function useInteractionHandles(opts) {
|
|
|
714
1194
|
createVertexProps,
|
|
715
1195
|
vertexAttrs
|
|
716
1196
|
]);
|
|
717
|
-
|
|
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 };
|
|
718
1235
|
}
|
|
719
1236
|
function useDoublePressProps(onDouble, { delay = 300, tolerancePx = 18 } = {}) {
|
|
720
1237
|
const last = useRef({ t: 0, x: 0, y: 0 });
|