@sitebytom/use-zoom-pan 0.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.
package/dist/index.mjs ADDED
@@ -0,0 +1,641 @@
1
+ import React, { useCallback, useState, useRef, useEffect } from 'react';
2
+
3
+ // src/useZoomPan.ts
4
+ var DEFAULT_OPTIONS = {
5
+ minScale: 1,
6
+ maxScale: 6,
7
+ zoomSensitivity: 2e-3,
8
+ clickZoomScale: 2.5,
9
+ dragThresholdMouse: 5,
10
+ dragThresholdTouch: 10,
11
+ swipeThreshold: 50,
12
+ boundsBuffer: 80,
13
+ manageCursor: true,
14
+ enableSwipe: true,
15
+ initialScale: void 0,
16
+ initialPosition: void 0
17
+ };
18
+ var TRANSITION_DURATION = 400;
19
+ var TRANSITION_CURVE = "cubic-bezier(0.2, 0, 0, 1)";
20
+ var calculateBounds = (targetScale, container, element, boundsBuffer) => {
21
+ if (!container || !element) return { xLimit: 0, yLimit: 0 };
22
+ const containerWidth = container.clientWidth;
23
+ const containerHeight = container.clientHeight;
24
+ const elementWidth = element.offsetWidth || containerWidth;
25
+ const elementHeight = element.offsetHeight || containerHeight;
26
+ const scaledWidth = elementWidth * targetScale;
27
+ const scaledHeight = elementHeight * targetScale;
28
+ const xLimit = (scaledWidth <= containerWidth ? 0 : (scaledWidth - containerWidth) / 2) + boundsBuffer;
29
+ const yLimit = (scaledHeight <= containerHeight ? 0 : (scaledHeight - containerHeight) / 2) + boundsBuffer;
30
+ return { xLimit, yLimit };
31
+ };
32
+ var clampPosition = (pos, targetScale, container, element, boundsBuffer) => {
33
+ const { xLimit, yLimit } = calculateBounds(targetScale, container, element, boundsBuffer);
34
+ return {
35
+ x: Math.max(-xLimit, Math.min(xLimit, pos.x)),
36
+ y: Math.max(-yLimit, Math.min(yLimit, pos.y))
37
+ };
38
+ };
39
+ var normalizeWheelDelta = (e, sensitivity) => {
40
+ const factor = e.deltaMode === 1 ? 20 : 1;
41
+ return -e.deltaY * factor * sensitivity;
42
+ };
43
+ var useZoomPan = ({
44
+ containerRef,
45
+ enableZoom = true,
46
+ onNext,
47
+ onPrev,
48
+ options = {}
49
+ }) => {
50
+ const config = React.useMemo(() => ({
51
+ ...DEFAULT_OPTIONS,
52
+ ...options
53
+ }), [
54
+ options.minScale,
55
+ options.maxScale,
56
+ options.zoomSensitivity,
57
+ options.clickZoomScale,
58
+ options.dragThresholdMouse,
59
+ options.dragThresholdTouch,
60
+ options.swipeThreshold,
61
+ options.boundsBuffer,
62
+ options.manageCursor,
63
+ options.initialScale,
64
+ options.initialPosition
65
+ ]);
66
+ const contentRef = React.useRef(null);
67
+ const getContentElement = useCallback(() => {
68
+ return contentRef.current || containerRef.current?.firstElementChild;
69
+ }, [containerRef]);
70
+ const [scale, setScale] = useState(config.initialScale ?? config.minScale);
71
+ const [position, setPosition] = useState(config.initialPosition ?? { x: 0, y: 0 });
72
+ const [isDragging, setIsDragging] = useState(false);
73
+ const [isTransitioning, setIsTransitioning] = useState(false);
74
+ const dragStartRef = useRef({
75
+ x: 0,
76
+ y: 0,
77
+ hasDragged: false,
78
+ startX: 0,
79
+ startY: 0
80
+ });
81
+ const pinchRef = useRef({
82
+ startDist: 0,
83
+ initialScale: config.minScale,
84
+ startX: 0,
85
+ startY: 0,
86
+ startPos: { x: 0, y: 0 }
87
+ });
88
+ const touchStartXRef = useRef(0);
89
+ const swipeBlockedRef = useRef(false);
90
+ const stateRef = useRef({ scale, position, enableZoom, isDragging, config });
91
+ useEffect(() => {
92
+ stateRef.current = { scale, position, enableZoom, isDragging, config };
93
+ }, [scale, position, enableZoom, isDragging, config]);
94
+ useEffect(() => {
95
+ const handleGlobalUp = () => {
96
+ if (stateRef.current.isDragging) {
97
+ setIsDragging(false);
98
+ }
99
+ };
100
+ const handleBlur = () => {
101
+ setIsDragging(false);
102
+ };
103
+ window.addEventListener("mouseup", handleGlobalUp);
104
+ window.addEventListener("touchend", handleGlobalUp);
105
+ window.addEventListener("touchcancel", handleGlobalUp);
106
+ window.addEventListener("blur", handleBlur);
107
+ return () => {
108
+ window.removeEventListener("mouseup", handleGlobalUp);
109
+ window.removeEventListener("touchend", handleGlobalUp);
110
+ window.removeEventListener("touchcancel", handleGlobalUp);
111
+ window.removeEventListener("blur", handleBlur);
112
+ setIsDragging(false);
113
+ dragStartRef.current.hasDragged = false;
114
+ setIsTransitioning(false);
115
+ };
116
+ }, []);
117
+ const updateBoundsAndClamp = useCallback(() => {
118
+ const container = containerRef.current;
119
+ const content = getContentElement();
120
+ if (!container || !content) return;
121
+ const currentPos = { x: stateRef.current.position.x, y: stateRef.current.position.y };
122
+ const clamped = clampPosition(currentPos, stateRef.current.scale, container, content, config.boundsBuffer);
123
+ if (clamped.x !== currentPos.x || clamped.y !== currentPos.y) {
124
+ setPosition(clamped);
125
+ }
126
+ }, [containerRef, config.boundsBuffer, getContentElement]);
127
+ React.useLayoutEffect(() => {
128
+ const container = containerRef.current;
129
+ if (!container) return;
130
+ const observer = new ResizeObserver(() => {
131
+ updateBoundsAndClamp();
132
+ });
133
+ observer.observe(container);
134
+ const content = getContentElement();
135
+ if (content instanceof HTMLImageElement && !content.complete) {
136
+ content.addEventListener("load", updateBoundsAndClamp);
137
+ }
138
+ return () => {
139
+ observer.disconnect();
140
+ if (content instanceof HTMLImageElement) {
141
+ content.removeEventListener("load", updateBoundsAndClamp);
142
+ }
143
+ };
144
+ }, [containerRef, updateBoundsAndClamp, getContentElement]);
145
+ const getClampedPosition = useCallback(
146
+ (pos, targetScale, element) => {
147
+ return clampPosition(
148
+ pos,
149
+ targetScale,
150
+ containerRef.current,
151
+ element,
152
+ config.boundsBuffer
153
+ );
154
+ },
155
+ [containerRef, config.boundsBuffer]
156
+ );
157
+ const handleWheelManual = useCallback(
158
+ (e) => {
159
+ if (!stateRef.current.enableZoom) return;
160
+ setIsTransitioning(false);
161
+ e.preventDefault();
162
+ const { scale: currentScale, position: currentPosition, config: config2 } = stateRef.current;
163
+ const delta = normalizeWheelDelta(e, config2.zoomSensitivity);
164
+ const newScale = Math.min(Math.max(config2.minScale, currentScale + delta), config2.maxScale);
165
+ if (newScale === config2.minScale) {
166
+ setScale(config2.minScale);
167
+ setPosition({ x: 0, y: 0 });
168
+ } else {
169
+ const container = containerRef.current;
170
+ const content = getContentElement();
171
+ if (container && content) {
172
+ const rect = container.getBoundingClientRect();
173
+ const containerWidth = rect.width;
174
+ const containerHeight = rect.height;
175
+ const centerX = containerWidth / 2;
176
+ const centerY = containerHeight / 2;
177
+ const mouseX = e.clientX - (rect.left + centerX);
178
+ const mouseY = e.clientY - (rect.top + centerY);
179
+ const contentX = (mouseX - currentPosition.x) / currentScale;
180
+ const contentY = (mouseY - currentPosition.y) / currentScale;
181
+ const newPosition = {
182
+ x: mouseX - contentX * newScale,
183
+ y: mouseY - contentY * newScale
184
+ };
185
+ const clampedPosition = getClampedPosition(newPosition, newScale, content);
186
+ setPosition(clampedPosition);
187
+ setScale(newScale);
188
+ }
189
+ }
190
+ },
191
+ [getClampedPosition, containerRef, getContentElement, config.zoomSensitivity, config.minScale, config.maxScale]
192
+ );
193
+ useEffect(() => {
194
+ const container = containerRef.current;
195
+ if (!container) return;
196
+ container.addEventListener("wheel", handleWheelManual, { passive: false });
197
+ return () => container.removeEventListener("wheel", handleWheelManual);
198
+ }, [containerRef, handleWheelManual]);
199
+ const reset = useCallback(() => {
200
+ setIsTransitioning(true);
201
+ setScale(config.minScale);
202
+ setPosition({ x: 0, y: 0 });
203
+ setIsDragging(false);
204
+ dragStartRef.current.hasDragged = false;
205
+ pinchRef.current = {
206
+ startDist: 0,
207
+ initialScale: config.minScale,
208
+ startX: 0,
209
+ startY: 0,
210
+ startPos: { x: 0, y: 0 }
211
+ };
212
+ }, [config.minScale]);
213
+ const handleFocalZoom = useCallback(
214
+ (e) => {
215
+ setIsTransitioning(true);
216
+ const container = containerRef.current;
217
+ const target = e.currentTarget;
218
+ if (!container || !target) return;
219
+ const rect = container.getBoundingClientRect();
220
+ const containerWidth = container.clientWidth;
221
+ const containerHeight = container.clientHeight;
222
+ const centerX = containerWidth / 2;
223
+ const centerY = containerHeight / 2;
224
+ const mouseX = e.clientX - (rect.left + centerX);
225
+ const mouseY = e.clientY - (rect.top + centerY);
226
+ const newPosition = {
227
+ x: mouseX * (1 - config.clickZoomScale),
228
+ y: mouseY * (1 - config.clickZoomScale)
229
+ };
230
+ const clampedPosition = getClampedPosition(newPosition, config.clickZoomScale, target);
231
+ setScale(config.clickZoomScale);
232
+ setPosition(clampedPosition);
233
+ },
234
+ [getClampedPosition, config.clickZoomScale, containerRef]
235
+ );
236
+ const onImageClick = useCallback(
237
+ (e) => {
238
+ if (dragStartRef.current.hasDragged) {
239
+ dragStartRef.current.hasDragged = false;
240
+ return;
241
+ }
242
+ if (scale > config.minScale) {
243
+ reset();
244
+ } else {
245
+ handleFocalZoom(e);
246
+ }
247
+ },
248
+ [scale, reset, handleFocalZoom, config.minScale]
249
+ );
250
+ const onImageDoubleClick = useCallback(
251
+ (e) => {
252
+ if (scale > config.minScale) {
253
+ reset();
254
+ } else {
255
+ handleFocalZoom(e);
256
+ }
257
+ },
258
+ [scale, reset, handleFocalZoom, config.minScale]
259
+ );
260
+ const getPinchPosition = useCallback((centerX, centerY, newScale) => {
261
+ const { containerRect, startX, startY, initialScale, startPos } = pinchRef.current;
262
+ if (!containerRect) return { x: 0, y: 0 };
263
+ const containerCenterX = containerRect.width / 2;
264
+ const containerCenterY = containerRect.height / 2;
265
+ const currentPinchX = centerX - (containerRect.left + containerCenterX);
266
+ const currentPinchY = centerY - (containerRect.top + containerCenterY);
267
+ const startPinchX = startX - (containerRect.left + containerCenterX);
268
+ const startPinchY = startY - (containerRect.top + containerCenterY);
269
+ const scaleRatio = newScale / initialScale;
270
+ const pinchImageX = startPinchX - startPos.x;
271
+ const pinchImageY = startPinchY - startPos.y;
272
+ return {
273
+ x: currentPinchX - pinchImageX * scaleRatio,
274
+ y: currentPinchY - pinchImageY * scaleRatio
275
+ };
276
+ }, []);
277
+ const onImageTouchStart = useCallback(
278
+ (e) => {
279
+ setIsTransitioning(false);
280
+ swipeBlockedRef.current = e.touches.length === 2;
281
+ if (e.touches.length === 2) {
282
+ const container = containerRef.current;
283
+ const dist = Math.hypot(
284
+ e.touches[0].clientX - e.touches[1].clientX,
285
+ e.touches[0].clientY - e.touches[1].clientY
286
+ );
287
+ const centerX = (e.touches[0].clientX + e.touches[1].clientX) / 2;
288
+ const centerY = (e.touches[0].clientY + e.touches[1].clientY) / 2;
289
+ pinchRef.current = {
290
+ startDist: dist,
291
+ initialScale: scale,
292
+ startX: centerX,
293
+ startY: centerY,
294
+ startPos: { x: position.x, y: position.y },
295
+ containerRect: container?.getBoundingClientRect()
296
+ };
297
+ } else if (e.touches.length === 1 && scale > config.minScale) {
298
+ setIsDragging(true);
299
+ dragStartRef.current = {
300
+ x: e.touches[0].clientX - position.x,
301
+ y: e.touches[0].clientY - position.y,
302
+ hasDragged: false,
303
+ startX: e.touches[0].clientX,
304
+ startY: e.touches[0].clientY
305
+ };
306
+ }
307
+ },
308
+ [scale, position, config.minScale, containerRef]
309
+ );
310
+ const onImageTouchMove = useCallback(
311
+ (e) => {
312
+ if (e.touches.length === 2) {
313
+ e.preventDefault();
314
+ const dist = Math.hypot(
315
+ e.touches[0].clientX - e.touches[1].clientX,
316
+ e.touches[0].clientY - e.touches[1].clientY
317
+ );
318
+ const centerX = (e.touches[0].clientX + e.touches[1].clientX) / 2;
319
+ const centerY = (e.touches[0].clientY + e.touches[1].clientY) / 2;
320
+ const ratio = dist / pinchRef.current.startDist;
321
+ const newScale = Math.min(Math.max(config.minScale, pinchRef.current.initialScale * ratio), config.maxScale);
322
+ if (pinchRef.current.containerRect && newScale > config.minScale) {
323
+ const newPosition = getPinchPosition(
324
+ centerX,
325
+ centerY,
326
+ newScale
327
+ );
328
+ const clampedPosition = getClampedPosition(
329
+ newPosition,
330
+ newScale,
331
+ getContentElement()
332
+ );
333
+ setPosition(clampedPosition);
334
+ } else {
335
+ setPosition({ x: 0, y: 0 });
336
+ }
337
+ setScale(newScale);
338
+ } else if (e.touches.length === 1 && isDragging && scale > config.minScale) {
339
+ e.preventDefault();
340
+ const touchX = e.touches[0].clientX;
341
+ const touchY = e.touches[0].clientY;
342
+ if (!dragStartRef.current.hasDragged) {
343
+ const moveDist = Math.hypot(
344
+ touchX - dragStartRef.current.startX,
345
+ touchY - dragStartRef.current.startY
346
+ );
347
+ if (moveDist > config.dragThresholdTouch) {
348
+ dragStartRef.current.hasDragged = true;
349
+ }
350
+ }
351
+ if (dragStartRef.current.hasDragged) {
352
+ const newPosition = {
353
+ x: touchX - dragStartRef.current.x,
354
+ y: touchY - dragStartRef.current.y
355
+ };
356
+ const clampedPosition = getClampedPosition(newPosition, scale, e.currentTarget);
357
+ setPosition(clampedPosition);
358
+ }
359
+ }
360
+ },
361
+ [isDragging, scale, getClampedPosition, getContentElement, getPinchPosition, config.minScale, config.maxScale, config.dragThresholdTouch]
362
+ );
363
+ const onImageTouchEnd = useCallback(() => {
364
+ setIsDragging(false);
365
+ pinchRef.current = {
366
+ startDist: 0,
367
+ initialScale: scale,
368
+ startX: 0,
369
+ startY: 0,
370
+ startPos: { x: position.x, y: position.y }
371
+ };
372
+ }, [scale, position]);
373
+ const onImageMouseDown = useCallback(
374
+ (e) => {
375
+ setIsTransitioning(false);
376
+ if (scale > config.minScale) {
377
+ e.preventDefault();
378
+ setIsDragging(true);
379
+ dragStartRef.current = {
380
+ x: e.clientX - position.x,
381
+ y: e.clientY - position.y,
382
+ hasDragged: false,
383
+ startX: e.clientX,
384
+ startY: e.clientY
385
+ };
386
+ }
387
+ },
388
+ [scale, position, config.minScale]
389
+ );
390
+ useCallback(
391
+ (targetX, targetY) => {
392
+ if (!config.enableSwipe) return;
393
+ const distanceX = Math.abs(targetX - dragStartRef.current.startX);
394
+ const distanceY = Math.abs(targetY - dragStartRef.current.startY);
395
+ if (distanceX > config.swipeThreshold && distanceX > distanceY) {
396
+ if (targetX < dragStartRef.current.startX) {
397
+ onNext?.();
398
+ } else {
399
+ onPrev?.();
400
+ }
401
+ }
402
+ },
403
+ [config.enableSwipe, config.swipeThreshold, onNext, onPrev]
404
+ );
405
+ const onImageMouseMove = useCallback(
406
+ (e) => {
407
+ if (isDragging && scale > config.minScale) {
408
+ e.preventDefault();
409
+ if (!dragStartRef.current.hasDragged) {
410
+ const moveDist = Math.hypot(
411
+ e.clientX - dragStartRef.current.startX,
412
+ e.clientY - dragStartRef.current.startY
413
+ );
414
+ if (moveDist > config.dragThresholdMouse) {
415
+ dragStartRef.current.hasDragged = true;
416
+ }
417
+ }
418
+ if (dragStartRef.current.hasDragged) {
419
+ const newPosition = {
420
+ x: e.clientX - dragStartRef.current.x,
421
+ y: e.clientY - dragStartRef.current.y
422
+ };
423
+ const clampedPosition = getClampedPosition(newPosition, scale, e.currentTarget);
424
+ setPosition(clampedPosition);
425
+ }
426
+ }
427
+ },
428
+ [isDragging, scale, getClampedPosition, config.minScale, config.dragThresholdMouse]
429
+ );
430
+ const onImageMouseUp = useCallback(() => {
431
+ setIsDragging(false);
432
+ }, []);
433
+ const onImageMouseLeave = useCallback(() => {
434
+ setIsDragging(false);
435
+ }, []);
436
+ const onContainerTouchStart = useCallback(
437
+ (e) => {
438
+ if (scale > config.minScale) return;
439
+ const touch = e.changedTouches[0];
440
+ touchStartXRef.current = touch.clientX;
441
+ },
442
+ [scale, config.minScale]
443
+ );
444
+ const onContainerTouchEnd = useCallback(
445
+ (e) => {
446
+ if (swipeBlockedRef.current) {
447
+ swipeBlockedRef.current = false;
448
+ return;
449
+ }
450
+ if (scale > config.minScale) return;
451
+ const touch = e.changedTouches[0];
452
+ const startX = touchStartXRef.current;
453
+ const endX = touch.clientX;
454
+ const diff = startX - endX;
455
+ if (Math.abs(diff) > config.swipeThreshold) {
456
+ if (diff > 0) {
457
+ onNext?.();
458
+ } else {
459
+ onPrev?.();
460
+ }
461
+ }
462
+ },
463
+ [scale, onNext, onPrev, config.minScale, config.swipeThreshold]
464
+ );
465
+ const onContainerMouseDown = useCallback(
466
+ (e) => {
467
+ if (scale > config.minScale) return;
468
+ touchStartXRef.current = e.clientX;
469
+ },
470
+ [scale, config.minScale]
471
+ );
472
+ const onContainerMouseUp = useCallback(
473
+ (e) => {
474
+ if (scale > config.minScale) return;
475
+ const startX = touchStartXRef.current;
476
+ const endX = e.clientX;
477
+ const diff = startX - endX;
478
+ if (Math.abs(diff) > config.swipeThreshold) {
479
+ if (diff > 0) {
480
+ onNext?.();
481
+ } else {
482
+ onPrev?.();
483
+ }
484
+ }
485
+ },
486
+ [scale, onNext, onPrev, config.minScale, config.swipeThreshold]
487
+ );
488
+ const zoomTo = useCallback(
489
+ (x, y, targetScale) => {
490
+ setIsTransitioning(true);
491
+ const container = containerRef.current;
492
+ const content = contentRef.current || container?.firstElementChild;
493
+ if (!container || !content) return;
494
+ const scaleToUse = targetScale ?? config.clickZoomScale;
495
+ const contentWidth = content.offsetWidth;
496
+ const contentHeight = content.offsetHeight;
497
+ const newPosition = {
498
+ x: (contentWidth / 2 - x) * scaleToUse,
499
+ y: (contentHeight / 2 - y) * scaleToUse
500
+ };
501
+ const clampedPosition = getClampedPosition(newPosition, scaleToUse, content);
502
+ setScale(scaleToUse);
503
+ setPosition(clampedPosition);
504
+ },
505
+ [getClampedPosition, config.clickZoomScale, containerRef]
506
+ );
507
+ useEffect(() => {
508
+ if (!isTransitioning) return;
509
+ const timer = setTimeout(() => setIsTransitioning(false), TRANSITION_DURATION);
510
+ return () => clearTimeout(timer);
511
+ }, [isTransitioning]);
512
+ const contentStyle = React.useMemo(() => {
513
+ const style = {
514
+ transformOrigin: "center",
515
+ transition: isTransitioning ? `transform ${TRANSITION_DURATION}ms ${TRANSITION_CURVE}` : "none"
516
+ };
517
+ if (config.manageCursor) {
518
+ if (isDragging) {
519
+ style.cursor = "grabbing";
520
+ } else if (scale > config.minScale) {
521
+ style.cursor = "grab";
522
+ } else if (enableZoom) {
523
+ style.cursor = "zoom-in";
524
+ } else {
525
+ style.cursor = "default";
526
+ }
527
+ }
528
+ return style;
529
+ }, [isTransitioning, config.manageCursor, isDragging, scale, config.minScale, enableZoom]);
530
+ const contentProps = React.useMemo(() => ({
531
+ ref: contentRef,
532
+ // Cast to compatible ref type
533
+ style: contentStyle,
534
+ onClick: onImageClick,
535
+ onDoubleClick: onImageDoubleClick,
536
+ onTouchStart: onImageTouchStart,
537
+ onTouchMove: onImageTouchMove,
538
+ onTouchEnd: onImageTouchEnd,
539
+ onMouseDown: onImageMouseDown,
540
+ onMouseMove: onImageMouseMove,
541
+ onMouseUp: onImageMouseUp,
542
+ onMouseLeave: onImageMouseLeave
543
+ }), [
544
+ contentStyle,
545
+ onImageClick,
546
+ onImageDoubleClick,
547
+ onImageTouchStart,
548
+ onImageTouchMove,
549
+ onImageTouchEnd,
550
+ onImageMouseDown,
551
+ onImageMouseMove,
552
+ onImageMouseUp,
553
+ onImageMouseLeave
554
+ ]);
555
+ const containerProps = React.useMemo(() => ({
556
+ onTouchStart: onContainerTouchStart,
557
+ onTouchEnd: onContainerTouchEnd,
558
+ onMouseDown: onContainerMouseDown,
559
+ onMouseUp: onContainerMouseUp
560
+ }), [
561
+ onContainerTouchStart,
562
+ onContainerTouchEnd,
563
+ onContainerMouseDown,
564
+ onContainerMouseUp
565
+ ]);
566
+ return {
567
+ scale,
568
+ position,
569
+ isDragging,
570
+ reset,
571
+ zoomTo,
572
+ contentProps,
573
+ containerProps
574
+ };
575
+ };
576
+ var ZoomPan = ({
577
+ children,
578
+ className = "",
579
+ style = {},
580
+ contentClassName = "",
581
+ contentStyle = {},
582
+ enableZoom = true,
583
+ onNext,
584
+ onPrev,
585
+ options
586
+ }) => {
587
+ const containerRef = useRef(null);
588
+ const { scale, position, contentProps, containerProps } = useZoomPan({
589
+ containerRef,
590
+ enableZoom,
591
+ onNext,
592
+ onPrev,
593
+ options
594
+ });
595
+ const defaultContainerStyle = {
596
+ width: "100%",
597
+ height: "100%",
598
+ overflow: "hidden",
599
+ display: "flex",
600
+ alignItems: "center",
601
+ justifyContent: "center",
602
+ cursor: scale > 1 ? "grab" : enableZoom ? "zoom-in" : "default",
603
+ position: "relative",
604
+ ...style
605
+ };
606
+ const defaultContentStyle = {
607
+ transform: `translate(${position.x}px, ${position.y}px) scale(${scale})`,
608
+ ...contentProps.style,
609
+ userSelect: "none",
610
+ WebkitUserSelect: "none",
611
+ touchAction: "none",
612
+ maxWidth: "100%",
613
+ maxHeight: "100%",
614
+ ...contentStyle
615
+ };
616
+ return /* @__PURE__ */ React.createElement(
617
+ "div",
618
+ {
619
+ ref: containerRef,
620
+ className,
621
+ style: defaultContainerStyle,
622
+ ...containerProps
623
+ },
624
+ /* @__PURE__ */ React.createElement(
625
+ "div",
626
+ {
627
+ className: contentClassName,
628
+ ...contentProps,
629
+ style: {
630
+ ...defaultContentStyle,
631
+ ...contentProps.style
632
+ }
633
+ },
634
+ children
635
+ )
636
+ );
637
+ };
638
+
639
+ export { ZoomPan, useZoomPan };
640
+ //# sourceMappingURL=index.mjs.map
641
+ //# sourceMappingURL=index.mjs.map