@embedpdf/utils 2.7.0 → 2.9.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.
@@ -18,7 +18,7 @@ function CounterRotate({ children, ...props }) {
18
18
  e.stopPropagation();
19
19
  };
20
20
  element.addEventListener("pointerdown", handlePointerDown, { capture: true });
21
- element.addEventListener("touchstart", handleTouchStart, { capture: true });
21
+ element.addEventListener("touchstart", handleTouchStart, { capture: true, passive: true });
22
22
  return () => {
23
23
  element.removeEventListener("pointerdown", handlePointerDown, { capture: true });
24
24
  element.removeEventListener("touchstart", handleTouchStart, { capture: true });
@@ -50,165 +50,6 @@ function CounterRotate({ children, ...props }) {
50
50
  }
51
51
  }) });
52
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
- }
212
53
  function getAnchor(handle) {
213
54
  return {
214
55
  x: handle.includes("e") ? "left" : handle.includes("w") ? "right" : "center",
@@ -449,6 +290,165 @@ function computeResizedRect(delta, handle, config) {
449
290
  }
450
291
  return computeResizeStep(delta, handle, config, true, false);
451
292
  }
293
+ const ROTATION_HANDLE_MARGIN = 35;
294
+ const HANDLE_BASE_ANGLE = {
295
+ n: 0,
296
+ ne: 45,
297
+ e: 90,
298
+ se: 135,
299
+ s: 180,
300
+ sw: 225,
301
+ w: 270,
302
+ nw: 315
303
+ };
304
+ const SECTOR_CURSORS = [
305
+ "ns-resize",
306
+ // 0: north
307
+ "nesw-resize",
308
+ // 1: NE
309
+ "ew-resize",
310
+ // 2: east
311
+ "nwse-resize",
312
+ // 3: SE
313
+ "ns-resize",
314
+ // 4: south
315
+ "nesw-resize",
316
+ // 5: SW
317
+ "ew-resize",
318
+ // 6: west
319
+ "nwse-resize"
320
+ // 7: NW
321
+ ];
322
+ function diagonalCursor(handle, pageQuarterTurns, annotationRotation = 0) {
323
+ const pageAngle = pageQuarterTurns * 90;
324
+ const totalAngle = HANDLE_BASE_ANGLE[handle] + pageAngle + annotationRotation;
325
+ const normalized = (totalAngle % 360 + 360) % 360;
326
+ const sector = Math.round(normalized / 45) % 8;
327
+ return SECTOR_CURSORS[sector];
328
+ }
329
+ function edgeOffset(k, spacing, mode) {
330
+ const base = -k / 2;
331
+ if (mode === "center") return base;
332
+ return mode === "outside" ? base - spacing : base + spacing;
333
+ }
334
+ function describeResizeFromConfig(cfg, ui = {}) {
335
+ const {
336
+ handleSize = 8,
337
+ spacing = 1,
338
+ offsetMode = "outside",
339
+ includeSides = false,
340
+ zIndex = 3,
341
+ rotationAwareCursor = true
342
+ } = ui;
343
+ const pageQuarterTurns = (cfg.pageRotation ?? 0) % 4;
344
+ const annotationRot = cfg.annotationRotation ?? 0;
345
+ const off = (edge) => ({
346
+ [edge]: edgeOffset(handleSize, spacing, offsetMode) + "px"
347
+ });
348
+ const corners = [
349
+ ["nw", { ...off("top"), ...off("left") }],
350
+ ["ne", { ...off("top"), ...off("right") }],
351
+ ["sw", { ...off("bottom"), ...off("left") }],
352
+ ["se", { ...off("bottom"), ...off("right") }]
353
+ ];
354
+ const sides = includeSides ? [
355
+ ["n", { ...off("top"), left: `calc(50% - ${handleSize / 2}px)` }],
356
+ ["s", { ...off("bottom"), left: `calc(50% - ${handleSize / 2}px)` }],
357
+ ["w", { ...off("left"), top: `calc(50% - ${handleSize / 2}px)` }],
358
+ ["e", { ...off("right"), top: `calc(50% - ${handleSize / 2}px)` }]
359
+ ] : [];
360
+ const all = [...corners, ...sides];
361
+ return all.map(([handle, pos]) => ({
362
+ handle,
363
+ style: {
364
+ position: "absolute",
365
+ width: handleSize + "px",
366
+ height: handleSize + "px",
367
+ borderRadius: "50%",
368
+ zIndex,
369
+ cursor: rotationAwareCursor ? diagonalCursor(handle, pageQuarterTurns, annotationRot) : "default",
370
+ pointerEvents: "auto",
371
+ touchAction: "none",
372
+ ...pos
373
+ },
374
+ attrs: { "data-epdf-handle": handle }
375
+ }));
376
+ }
377
+ function describeVerticesFromConfig(cfg, ui = {}, liveVertices) {
378
+ const { vertexSize = 12, zIndex = 4 } = ui;
379
+ const rect = cfg.element;
380
+ const scale = cfg.scale ?? 1;
381
+ const verts = liveVertices ?? cfg.vertices ?? [];
382
+ return verts.map((v, i) => {
383
+ const left = (v.x - rect.origin.x) * scale - vertexSize / 2;
384
+ const top = (v.y - rect.origin.y) * scale - vertexSize / 2;
385
+ return {
386
+ handle: "nw",
387
+ // not used; kept for type
388
+ style: {
389
+ position: "absolute",
390
+ left: left + "px",
391
+ top: top + "px",
392
+ width: vertexSize + "px",
393
+ height: vertexSize + "px",
394
+ borderRadius: "50%",
395
+ cursor: "pointer",
396
+ zIndex,
397
+ pointerEvents: "auto",
398
+ touchAction: "none"
399
+ },
400
+ attrs: { "data-epdf-vertex": i }
401
+ };
402
+ });
403
+ }
404
+ function describeRotationFromConfig(cfg, ui = {}, currentAngle = 0) {
405
+ const { handleSize = 16, zIndex = 5, showConnector = true, connectorWidth = 1 } = ui;
406
+ const scale = cfg.scale ?? 1;
407
+ const rect = cfg.element;
408
+ const orbitRect = cfg.rotationElement ?? rect;
409
+ const orbitCenter = cfg.rotationCenter ?? {
410
+ x: rect.origin.x + rect.size.width / 2,
411
+ y: rect.origin.y + rect.size.height / 2
412
+ };
413
+ orbitRect.size.width * scale;
414
+ orbitRect.size.height * scale;
415
+ const centerX = (orbitCenter.x - orbitRect.origin.x) * scale;
416
+ const centerY = (orbitCenter.y - orbitRect.origin.y) * scale;
417
+ const angleRad = currentAngle * Math.PI / 180;
418
+ const margin = ui.margin ?? ROTATION_HANDLE_MARGIN;
419
+ const radius = rect.size.height * scale / 2 + margin;
420
+ const handleCenterX = centerX + radius * Math.sin(angleRad);
421
+ const handleCenterY = centerY - radius * Math.cos(angleRad);
422
+ const handleLeft = handleCenterX - handleSize / 2;
423
+ const handleTop = handleCenterY - handleSize / 2;
424
+ return {
425
+ handleStyle: {
426
+ position: "absolute",
427
+ left: handleLeft + "px",
428
+ top: handleTop + "px",
429
+ width: handleSize + "px",
430
+ height: handleSize + "px",
431
+ borderRadius: "50%",
432
+ cursor: "grab",
433
+ zIndex,
434
+ pointerEvents: "auto",
435
+ touchAction: "none"
436
+ },
437
+ connectorStyle: showConnector ? {
438
+ position: "absolute",
439
+ left: centerX - connectorWidth / 2 + "px",
440
+ top: centerY - radius + "px",
441
+ width: connectorWidth + "px",
442
+ height: radius + "px",
443
+ transformOrigin: "center bottom",
444
+ transform: `rotate(${currentAngle}deg)`,
445
+ zIndex: zIndex - 1,
446
+ pointerEvents: "none"
447
+ } : {},
448
+ radius,
449
+ attrs: { "data-epdf-rotation-handle": true }
450
+ };
451
+ }
452
452
  class DragResizeController {
453
453
  constructor(config, onUpdate) {
454
454
  this.config = config;
@@ -572,7 +572,7 @@ class DragResizeController {
572
572
  // ---------------------------------------------------------------------------
573
573
  // Gesture move
574
574
  // ---------------------------------------------------------------------------
575
- move(clientX, clientY, buttons) {
575
+ move(clientX, clientY, buttons, lockAspectRatio) {
576
576
  if (this.state === "idle" || !this.startPoint) return;
577
577
  if (buttons !== void 0 && buttons === 0) {
578
578
  this.end();
@@ -590,7 +590,7 @@ class DragResizeController {
590
590
  const delta = this.calculateLocalDelta(clientX, clientY);
591
591
  const position = computeResizedRect(delta, this.activeHandle, {
592
592
  startRect: this.startElement,
593
- maintainAspectRatio: this.config.maintainAspectRatio,
593
+ maintainAspectRatio: this.config.maintainAspectRatio || !!lockAspectRatio,
594
594
  annotationRotation: this.config.annotationRotation,
595
595
  constraints: this.config.constraints
596
596
  });
@@ -602,7 +602,7 @@ class DragResizeController {
602
602
  changes: { rect: position },
603
603
  metadata: {
604
604
  handle: this.activeHandle,
605
- maintainAspectRatio: this.config.maintainAspectRatio
605
+ maintainAspectRatio: this.config.maintainAspectRatio || !!lockAspectRatio
606
606
  }
607
607
  }
608
608
  });
@@ -1024,7 +1024,7 @@ function useDragResize(options) {
1024
1024
  e.stopPropagation();
1025
1025
  const activePointerId = activePointerIdRef.current;
1026
1026
  if (activePointerId !== null && e.pointerId !== activePointerId) return;
1027
- (_a = controllerRef.current) == null ? void 0 : _a.move(e.clientX, e.clientY, e.buttons);
1027
+ (_a = controllerRef.current) == null ? void 0 : _a.move(e.clientX, e.clientY, e.buttons, e.shiftKey);
1028
1028
  if (activePointerIdRef.current === e.pointerId && e.buttons === 0) {
1029
1029
  endPointerSession(e.pointerId);
1030
1030
  }