uix-image-magnify 0.1.4 → 0.2.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,88 @@
1
+ import * as React from 'react';
2
+ import { CSSProperties, ImgHTMLAttributes } from 'react';
3
+
4
+ type ZoomType = "hover" | "click" | "double-click" | "hold" | "wheel" | "none";
5
+ type PanePosition = "left" | "right" | "top" | "bottom";
6
+ type LensShape = "circle" | "square";
7
+ type MagnifyMode = "lens" | "side-by-side" | "inner" | "follow";
8
+ type DragMode = "follow" | "pan";
9
+ interface UseImageMagnifyOptions {
10
+ /** Base image URL (required when used standalone). */
11
+ src?: string;
12
+ /** High-resolution image used for the zoomed view. Defaults to `src`. */
13
+ zoomSrc?: string;
14
+ /** Magnification factor (2 = 200%). */
15
+ zoomScale?: number;
16
+ /** Minimum scale for wheel/keyboard zoom. Default `1.5`. */
17
+ minZoomScale?: number;
18
+ /** Maximum scale for wheel/keyboard zoom. Default `5`. */
19
+ maxZoomScale?: number;
20
+ /** Interaction that activates zoom. */
21
+ zoomType?: ZoomType;
22
+ /** How the zoomed content is displayed. */
23
+ mode?: MagnifyMode;
24
+ /** Drag interaction for `inner` mode. */
25
+ dragMode?: DragMode;
26
+ /** Where the side-by-side pane sits relative to the image. */
27
+ panePosition?: PanePosition;
28
+ /** Diameter / side length of the lens in px. */
29
+ lensSize?: number;
30
+ /** Lens shape. */
31
+ lensShape?: LensShape;
32
+ /** Offset (both axes, px) for `mode="follow"`. */
33
+ followOffset?: number;
34
+ /** Disable all interaction. */
35
+ disabled?: boolean;
36
+ /** Preload the high-res image to avoid a flash. */
37
+ zoomPreload?: boolean;
38
+ /** Reset zoom when the pointer leaves the container. */
39
+ resetOnLeave?: boolean;
40
+ onZoomStart?: () => void;
41
+ onZoomEnd?: () => void;
42
+ onZoomChange?: (scale: number) => void;
43
+ }
44
+ declare function useImageMagnify(options?: UseImageMagnifyOptions): {
45
+ isZoomed: boolean;
46
+ zoomScale: number;
47
+ bgImage: string | undefined;
48
+ containerRef: React.RefObject<HTMLDivElement>;
49
+ imageRef: React.RefObject<HTMLImageElement>;
50
+ lensRef: React.RefObject<HTMLDivElement>;
51
+ paneRef: React.RefObject<HTMLDivElement>;
52
+ cursorRef: React.RefObject<HTMLDivElement>;
53
+ handlers: {
54
+ onPointerEnter: (e: React.PointerEvent) => void;
55
+ onPointerLeave: (e: React.PointerEvent) => void;
56
+ onPointerDown: (e: React.PointerEvent) => void;
57
+ onPointerMove: (e: React.PointerEvent) => void;
58
+ onPointerUp: (e: React.PointerEvent) => void;
59
+ onPointerCancel: () => void;
60
+ onDoubleClick: (e: React.MouseEvent) => void;
61
+ onKeyDown: (e: React.KeyboardEvent) => void;
62
+ onFocus: () => void;
63
+ onBlur: () => void;
64
+ };
65
+ };
66
+
67
+ interface ImageMagnifyProps extends UseImageMagnifyOptions {
68
+ src: string;
69
+ alt: string;
70
+ width?: number | string;
71
+ className?: string;
72
+ style?: CSSProperties;
73
+ imageClassName?: string;
74
+ imageStyle?: CSSProperties;
75
+ lensClassName?: string;
76
+ lensStyle?: CSSProperties;
77
+ paneClassName?: string;
78
+ paneStyle?: CSSProperties;
79
+ paneWidth?: number | string;
80
+ paneHeight?: number | string;
81
+ lazy?: boolean;
82
+ imageProps?: Omit<ImgHTMLAttributes<HTMLImageElement>, "src" | "alt" | "style">;
83
+ onLoad?: ImgHTMLAttributes<HTMLImageElement>["onLoad"];
84
+ onError?: ImgHTMLAttributes<HTMLImageElement>["onError"];
85
+ }
86
+ declare const ImageMagnify: React.ForwardRefExoticComponent<ImageMagnifyProps & React.RefAttributes<HTMLDivElement>>;
87
+
88
+ export { type DragMode as D, type ImageMagnifyProps as I, type LensShape as L, type MagnifyMode as M, type PanePosition as P, type UseImageMagnifyOptions as U, type ZoomType as Z, ImageMagnify as a, useImageMagnify as u };
@@ -0,0 +1,88 @@
1
+ import * as React from 'react';
2
+ import { CSSProperties, ImgHTMLAttributes } from 'react';
3
+
4
+ type ZoomType = "hover" | "click" | "double-click" | "hold" | "wheel" | "none";
5
+ type PanePosition = "left" | "right" | "top" | "bottom";
6
+ type LensShape = "circle" | "square";
7
+ type MagnifyMode = "lens" | "side-by-side" | "inner" | "follow";
8
+ type DragMode = "follow" | "pan";
9
+ interface UseImageMagnifyOptions {
10
+ /** Base image URL (required when used standalone). */
11
+ src?: string;
12
+ /** High-resolution image used for the zoomed view. Defaults to `src`. */
13
+ zoomSrc?: string;
14
+ /** Magnification factor (2 = 200%). */
15
+ zoomScale?: number;
16
+ /** Minimum scale for wheel/keyboard zoom. Default `1.5`. */
17
+ minZoomScale?: number;
18
+ /** Maximum scale for wheel/keyboard zoom. Default `5`. */
19
+ maxZoomScale?: number;
20
+ /** Interaction that activates zoom. */
21
+ zoomType?: ZoomType;
22
+ /** How the zoomed content is displayed. */
23
+ mode?: MagnifyMode;
24
+ /** Drag interaction for `inner` mode. */
25
+ dragMode?: DragMode;
26
+ /** Where the side-by-side pane sits relative to the image. */
27
+ panePosition?: PanePosition;
28
+ /** Diameter / side length of the lens in px. */
29
+ lensSize?: number;
30
+ /** Lens shape. */
31
+ lensShape?: LensShape;
32
+ /** Offset (both axes, px) for `mode="follow"`. */
33
+ followOffset?: number;
34
+ /** Disable all interaction. */
35
+ disabled?: boolean;
36
+ /** Preload the high-res image to avoid a flash. */
37
+ zoomPreload?: boolean;
38
+ /** Reset zoom when the pointer leaves the container. */
39
+ resetOnLeave?: boolean;
40
+ onZoomStart?: () => void;
41
+ onZoomEnd?: () => void;
42
+ onZoomChange?: (scale: number) => void;
43
+ }
44
+ declare function useImageMagnify(options?: UseImageMagnifyOptions): {
45
+ isZoomed: boolean;
46
+ zoomScale: number;
47
+ bgImage: string | undefined;
48
+ containerRef: React.RefObject<HTMLDivElement>;
49
+ imageRef: React.RefObject<HTMLImageElement>;
50
+ lensRef: React.RefObject<HTMLDivElement>;
51
+ paneRef: React.RefObject<HTMLDivElement>;
52
+ cursorRef: React.RefObject<HTMLDivElement>;
53
+ handlers: {
54
+ onPointerEnter: (e: React.PointerEvent) => void;
55
+ onPointerLeave: (e: React.PointerEvent) => void;
56
+ onPointerDown: (e: React.PointerEvent) => void;
57
+ onPointerMove: (e: React.PointerEvent) => void;
58
+ onPointerUp: (e: React.PointerEvent) => void;
59
+ onPointerCancel: () => void;
60
+ onDoubleClick: (e: React.MouseEvent) => void;
61
+ onKeyDown: (e: React.KeyboardEvent) => void;
62
+ onFocus: () => void;
63
+ onBlur: () => void;
64
+ };
65
+ };
66
+
67
+ interface ImageMagnifyProps extends UseImageMagnifyOptions {
68
+ src: string;
69
+ alt: string;
70
+ width?: number | string;
71
+ className?: string;
72
+ style?: CSSProperties;
73
+ imageClassName?: string;
74
+ imageStyle?: CSSProperties;
75
+ lensClassName?: string;
76
+ lensStyle?: CSSProperties;
77
+ paneClassName?: string;
78
+ paneStyle?: CSSProperties;
79
+ paneWidth?: number | string;
80
+ paneHeight?: number | string;
81
+ lazy?: boolean;
82
+ imageProps?: Omit<ImgHTMLAttributes<HTMLImageElement>, "src" | "alt" | "style">;
83
+ onLoad?: ImgHTMLAttributes<HTMLImageElement>["onLoad"];
84
+ onError?: ImgHTMLAttributes<HTMLImageElement>["onError"];
85
+ }
86
+ declare const ImageMagnify: React.ForwardRefExoticComponent<ImageMagnifyProps & React.RefAttributes<HTMLDivElement>>;
87
+
88
+ export { type DragMode as D, type ImageMagnifyProps as I, type LensShape as L, type MagnifyMode as M, type PanePosition as P, type UseImageMagnifyOptions as U, type ZoomType as Z, ImageMagnify as a, useImageMagnify as u };
package/dist/index.cjs CHANGED
@@ -2,6 +2,11 @@
2
2
 
3
3
  var react = require('react');
4
4
  var jsxRuntime = require('react/jsx-runtime');
5
+ var react$1 = require('swiper/react');
6
+ var modules = require('swiper/modules');
7
+ require('swiper/css');
8
+ require('swiper/css/navigation');
9
+ require('swiper/css/thumbs');
5
10
 
6
11
  // src/components/ImageMagnify.tsx
7
12
  var clamp = (v, min, max) => Math.max(min, Math.min(max, v));
@@ -413,6 +418,19 @@ function useImageMagnify(options = {}) {
413
418
  handlers
414
419
  };
415
420
  }
421
+ var useIsoLayoutEffect2 = typeof window !== "undefined" ? react.useLayoutEffect : react.useEffect;
422
+ function useMediaQuery(query) {
423
+ const [matches, setMatches] = react.useState(false);
424
+ useIsoLayoutEffect2(() => {
425
+ const media = window.matchMedia(query);
426
+ const listener = () => setMatches(media.matches);
427
+ listener();
428
+ media.addEventListener("change", listener);
429
+ return () => media.removeEventListener("change", listener);
430
+ }, [query]);
431
+ return matches;
432
+ }
433
+ var MOBILE_QUERY = "(max-width: 768px)";
416
434
  var ImageMagnify = react.forwardRef(
417
435
  (props, ref) => {
418
436
  const {
@@ -441,6 +459,8 @@ var ImageMagnify = react.forwardRef(
441
459
  onError,
442
460
  ...hookOptions
443
461
  } = props;
462
+ const isMobile = useMediaQuery(MOBILE_QUERY);
463
+ const responsivePanePosition = isMobile ? "bottom" : panePosition;
444
464
  const {
445
465
  isZoomed,
446
466
  bgImage,
@@ -458,25 +478,36 @@ var ImageMagnify = react.forwardRef(
458
478
  disabled,
459
479
  ...hookOptions
460
480
  });
461
- const paneOffset = {};
462
- if (panePosition === "right") {
463
- paneOffset.left = "calc(100% + 12px)";
464
- paneOffset.top = 0;
465
- } else if (panePosition === "left") {
466
- paneOffset.right = "calc(100% + 12px)";
467
- paneOffset.top = 0;
468
- } else if (panePosition === "top") {
469
- paneOffset.left = 0;
470
- paneOffset.bottom = "calc(100% + 12px)";
481
+ const panePositionStyle = {};
482
+ if (responsivePanePosition === "right") {
483
+ panePositionStyle.position = "absolute";
484
+ panePositionStyle.left = "calc(100% + 12px)";
485
+ panePositionStyle.top = 0;
486
+ } else if (responsivePanePosition === "left") {
487
+ panePositionStyle.position = "absolute";
488
+ panePositionStyle.right = "calc(100% + 12px)";
489
+ panePositionStyle.top = 0;
490
+ } else if (responsivePanePosition === "top") {
491
+ panePositionStyle.position = "absolute";
492
+ panePositionStyle.left = 0;
493
+ panePositionStyle.bottom = "calc(100% + 12px)";
494
+ } else if (isMobile) {
495
+ panePositionStyle.position = "static";
496
+ panePositionStyle.display = "block";
497
+ panePositionStyle.marginTop = 12;
498
+ panePositionStyle.width = "100%";
499
+ panePositionStyle.boxSizing = "border-box";
471
500
  } else {
472
- paneOffset.left = 0;
473
- paneOffset.top = "calc(100% + 12px)";
501
+ panePositionStyle.position = "absolute";
502
+ panePositionStyle.left = 0;
503
+ panePositionStyle.top = "calc(100% + 12px)";
474
504
  }
475
505
  const isInner = mode === "inner";
476
506
  const showLens = (mode === "lens" || mode === "follow") && isZoomed;
477
507
  const showPane = mode === "side-by-side" && isZoomed;
478
508
  const showCursor = mode === "side-by-side" && isZoomed;
479
509
  const cursorStyle = disabled ? "default" : zoomType === "hover" || zoomType === "hold" || zoomType === "wheel" ? "crosshair" : isZoomed ? "zoom-out" : "zoom-in";
510
+ const outerWidth = isMobile ? "100%" : width;
480
511
  return /* @__PURE__ */ jsxRuntime.jsxs(
481
512
  "div",
482
513
  {
@@ -486,7 +517,8 @@ var ImageMagnify = react.forwardRef(
486
517
  position: "relative",
487
518
  display: "inline-block",
488
519
  lineHeight: 0,
489
- width,
520
+ width: outerWidth,
521
+ maxWidth: "100%",
490
522
  ...style
491
523
  },
492
524
  children: [
@@ -503,7 +535,6 @@ var ImageMagnify = react.forwardRef(
503
535
  overflow: "hidden",
504
536
  width: "100%",
505
537
  cursor: cursorStyle,
506
- // Only block scrolling while zoomed so page scrolling still works.
507
538
  touchAction: isZoomed ? "none" : "pan-y",
508
539
  outline: "none",
509
540
  lineHeight: 0
@@ -590,16 +621,17 @@ var ImageMagnify = react.forwardRef(
590
621
  className: paneClassName,
591
622
  "aria-hidden": "true",
592
623
  style: {
593
- position: "absolute",
594
- width: paneWidth,
624
+ width: isMobile && responsivePanePosition === "bottom" ? "100%" : paneWidth,
595
625
  height: paneHeight,
626
+ maxWidth: "100%",
627
+ boxSizing: "border-box",
596
628
  backgroundImage: bgImage ? `url("${bgImage}")` : void 0,
597
629
  backgroundRepeat: "no-repeat",
598
630
  border: "1px solid #e5e5e5",
599
631
  borderRadius: 4,
600
632
  boxShadow: "0 8px 24px rgba(0,0,0,0.12)",
601
633
  pointerEvents: "none",
602
- ...paneOffset,
634
+ ...panePositionStyle,
603
635
  ...paneStyle
604
636
  }
605
637
  }
@@ -610,8 +642,336 @@ var ImageMagnify = react.forwardRef(
610
642
  }
611
643
  );
612
644
  ImageMagnify.displayName = "ImageMagnify";
645
+ var ArrowLeftIcon = () => /* @__PURE__ */ jsxRuntime.jsx(
646
+ "svg",
647
+ {
648
+ viewBox: "0 0 24 24",
649
+ width: "20",
650
+ height: "20",
651
+ fill: "none",
652
+ stroke: "currentColor",
653
+ strokeWidth: "2",
654
+ strokeLinecap: "round",
655
+ strokeLinejoin: "round",
656
+ "aria-hidden": "true",
657
+ children: /* @__PURE__ */ jsxRuntime.jsx("polyline", { points: "15 18 9 12 15 6" })
658
+ }
659
+ );
660
+ var ArrowRightIcon = () => /* @__PURE__ */ jsxRuntime.jsx(
661
+ "svg",
662
+ {
663
+ viewBox: "0 0 24 24",
664
+ width: "20",
665
+ height: "20",
666
+ fill: "none",
667
+ stroke: "currentColor",
668
+ strokeWidth: "2",
669
+ strokeLinecap: "round",
670
+ strokeLinejoin: "round",
671
+ "aria-hidden": "true",
672
+ children: /* @__PURE__ */ jsxRuntime.jsx("polyline", { points: "9 18 15 12 9 6" })
673
+ }
674
+ );
675
+ var defaultArrowBaseStyle = {
676
+ position: "absolute",
677
+ top: "50%",
678
+ transform: "translateY(-50%)",
679
+ width: 40,
680
+ height: 40,
681
+ display: "inline-flex",
682
+ alignItems: "center",
683
+ justifyContent: "center",
684
+ background: "rgba(255,255,255,0.85)",
685
+ border: "1px solid rgba(0,0,0,0.12)",
686
+ borderRadius: 9999,
687
+ boxShadow: "0 4px 12px rgba(0,0,0,0.15)",
688
+ cursor: "pointer",
689
+ zIndex: 10,
690
+ color: "#111",
691
+ padding: 0,
692
+ transition: "background 0.15s ease",
693
+ backdropFilter: "blur(4px)",
694
+ WebkitBackdropFilter: "blur(4px)"
695
+ };
696
+ var visuallyHidden = {
697
+ position: "absolute",
698
+ width: 1,
699
+ height: 1,
700
+ padding: 0,
701
+ margin: -1,
702
+ overflow: "hidden",
703
+ clip: "rect(0,0,0,0)",
704
+ whiteSpace: "nowrap",
705
+ border: 0
706
+ };
707
+ var ImageMagnifyGallery = react.forwardRef((props, ref) => {
708
+ const {
709
+ images,
710
+ alt,
711
+ activeIndex: controlledActiveIndex,
712
+ defaultIndex = 0,
713
+ onIndexChange,
714
+ showArrows = true,
715
+ showThumbnails = true,
716
+ showCounter = false,
717
+ loop = true,
718
+ enableKeyboard = true,
719
+ preloadAdjacent = true,
720
+ className,
721
+ style,
722
+ viewportClassName,
723
+ viewportStyle,
724
+ thumbnailContainerClassName,
725
+ thumbnailContainerStyle,
726
+ thumbnailClassName,
727
+ thumbnailActiveClassName,
728
+ thumbnailStyle,
729
+ thumbnailActiveStyle,
730
+ arrowClassName,
731
+ arrowStyle,
732
+ thumbnailSize = 80,
733
+ thumbnailGap = 8,
734
+ prevArrow,
735
+ nextArrow,
736
+ width,
737
+ ...magnifyProps
738
+ } = props;
739
+ const [swiper, setSwiper] = react.useState(null);
740
+ const [thumbsSwiper, setThumbsSwiper] = react.useState(null);
741
+ const [localIndex, setLocalIndex] = react.useState(defaultIndex);
742
+ const prevRef = react.useRef(null);
743
+ const nextRef = react.useRef(null);
744
+ const isControlled = controlledActiveIndex !== void 0;
745
+ const activeIndex = isControlled ? controlledActiveIndex : localIndex;
746
+ const hasMultiple = images.length > 1;
747
+ react.useEffect(() => {
748
+ if (!swiper || swiper.destroyed) return;
749
+ if (isControlled && swiper.activeIndex !== controlledActiveIndex) {
750
+ swiper.slideTo(controlledActiveIndex);
751
+ }
752
+ }, [isControlled, controlledActiveIndex, swiper]);
753
+ react.useEffect(() => {
754
+ if (!preloadAdjacent || typeof window === "undefined" || images.length <= 1)
755
+ return;
756
+ const targets = /* @__PURE__ */ new Set([
757
+ (activeIndex + 1) % images.length,
758
+ (activeIndex - 1 + images.length) % images.length
759
+ ]);
760
+ targets.forEach((i) => {
761
+ if (images[i]) {
762
+ const img = new Image();
763
+ img.src = images[i];
764
+ }
765
+ });
766
+ }, [activeIndex, images, preloadAdjacent]);
767
+ const modules$1 = react.useMemo(() => {
768
+ const m = [modules.Navigation, modules.Thumbs];
769
+ if (enableKeyboard) m.push(modules.Keyboard);
770
+ return m;
771
+ }, [enableKeyboard]);
772
+ if (images.length === 0) {
773
+ return /* @__PURE__ */ jsxRuntime.jsx("div", { ref, className, style });
774
+ }
775
+ return /* @__PURE__ */ jsxRuntime.jsxs(
776
+ "div",
777
+ {
778
+ ref,
779
+ className,
780
+ style: { maxWidth: "100%", width, ...style },
781
+ role: "region",
782
+ "aria-roledescription": "carousel",
783
+ "aria-label": `${alt} \u2014 image gallery`,
784
+ children: [
785
+ /* @__PURE__ */ jsxRuntime.jsxs(
786
+ "div",
787
+ {
788
+ className: viewportClassName,
789
+ style: { position: "relative", width: "100%", ...viewportStyle },
790
+ children: [
791
+ /* @__PURE__ */ jsxRuntime.jsx(
792
+ react$1.Swiper,
793
+ {
794
+ modules: modules$1,
795
+ onSwiper: setSwiper,
796
+ initialSlide: isControlled ? controlledActiveIndex : defaultIndex,
797
+ onSlideChange: (s) => {
798
+ if (!isControlled) setLocalIndex(s.activeIndex);
799
+ onIndexChange?.(s.activeIndex);
800
+ },
801
+ onBeforeInit: (s) => {
802
+ if (!showArrows || !hasMultiple) return;
803
+ if (s.params.navigation && typeof s.params.navigation === "object") {
804
+ s.params.navigation.prevEl = prevRef.current;
805
+ s.params.navigation.nextEl = nextRef.current;
806
+ }
807
+ },
808
+ navigation: showArrows && hasMultiple ? {} : false,
809
+ keyboard: enableKeyboard ? { enabled: true } : void 0,
810
+ thumbs: {
811
+ swiper: thumbsSwiper && !thumbsSwiper.destroyed ? thumbsSwiper : null
812
+ },
813
+ loop: loop && hasMultiple,
814
+ autoHeight: true,
815
+ spaceBetween: 10,
816
+ style: {
817
+ width: "100%",
818
+ "--swiper-navigation-size": "20px",
819
+ ...arrowStyle
820
+ },
821
+ children: images.map((src, idx) => /* @__PURE__ */ jsxRuntime.jsx(react$1.SwiperSlide, { children: /* @__PURE__ */ jsxRuntime.jsx(
822
+ ImageMagnify,
823
+ {
824
+ ...magnifyProps,
825
+ src,
826
+ alt,
827
+ width: "100%"
828
+ }
829
+ ) }, idx))
830
+ }
831
+ ),
832
+ showArrows && hasMultiple && /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
833
+ /* @__PURE__ */ jsxRuntime.jsx(
834
+ "button",
835
+ {
836
+ ref: prevRef,
837
+ type: "button",
838
+ className: arrowClassName,
839
+ style: { ...defaultArrowBaseStyle, left: 12 },
840
+ "aria-label": "Previous image",
841
+ children: prevArrow ?? /* @__PURE__ */ jsxRuntime.jsx(ArrowLeftIcon, {})
842
+ }
843
+ ),
844
+ /* @__PURE__ */ jsxRuntime.jsx(
845
+ "button",
846
+ {
847
+ ref: nextRef,
848
+ type: "button",
849
+ className: arrowClassName,
850
+ style: { ...defaultArrowBaseStyle, right: 12 },
851
+ "aria-label": "Next image",
852
+ children: nextArrow ?? /* @__PURE__ */ jsxRuntime.jsx(ArrowRightIcon, {})
853
+ }
854
+ )
855
+ ] }),
856
+ showCounter && hasMultiple && /* @__PURE__ */ jsxRuntime.jsxs(
857
+ "div",
858
+ {
859
+ "aria-hidden": "true",
860
+ style: {
861
+ position: "absolute",
862
+ top: 12,
863
+ right: 12,
864
+ background: "rgba(0,0,0,0.6)",
865
+ color: "#fff",
866
+ fontSize: 12,
867
+ fontWeight: 600,
868
+ padding: "4px 8px",
869
+ borderRadius: 4,
870
+ zIndex: 10,
871
+ pointerEvents: "none"
872
+ },
873
+ children: [
874
+ activeIndex + 1,
875
+ " / ",
876
+ images.length
877
+ ]
878
+ }
879
+ ),
880
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { "aria-live": "polite", "aria-atomic": "true", style: visuallyHidden, children: [
881
+ "Image ",
882
+ activeIndex + 1,
883
+ " of ",
884
+ images.length
885
+ ] })
886
+ ]
887
+ }
888
+ ),
889
+ showThumbnails && hasMultiple && /* @__PURE__ */ jsxRuntime.jsx(
890
+ react$1.Swiper,
891
+ {
892
+ modules: modules$1,
893
+ onSwiper: setSwiper,
894
+ initialSlide: isControlled ? controlledActiveIndex : defaultIndex,
895
+ onSlideChange: (s) => {
896
+ if (!isControlled) setLocalIndex(s.activeIndex);
897
+ onIndexChange?.(s.activeIndex);
898
+ },
899
+ onBeforeInit: (s) => {
900
+ if (!showArrows || !hasMultiple) return;
901
+ const nav = s.params.navigation;
902
+ if (nav && typeof nav === "object") {
903
+ nav.prevEl = prevRef.current;
904
+ nav.nextEl = nextRef.current;
905
+ }
906
+ },
907
+ navigation: showArrows && hasMultiple ? {} : false,
908
+ keyboard: enableKeyboard ? { enabled: true } : void 0,
909
+ thumbs: {
910
+ swiper: thumbsSwiper && !thumbsSwiper.destroyed ? thumbsSwiper : null
911
+ },
912
+ loop: loop && hasMultiple,
913
+ autoHeight: true,
914
+ spaceBetween: 10,
915
+ style: { width: "100%" },
916
+ children: images.map((src, idx) => /* @__PURE__ */ jsxRuntime.jsx(react$1.SwiperSlide, { children: /* @__PURE__ */ jsxRuntime.jsx(
917
+ ImageMagnify,
918
+ {
919
+ ...magnifyProps,
920
+ src,
921
+ alt,
922
+ width: "100%"
923
+ }
924
+ ) }, idx))
925
+ }
926
+ )
927
+ ]
928
+ }
929
+ );
930
+ });
931
+ ImageMagnifyGallery.displayName = "ImageMagnifyGallery";
932
+ function useImageSlider({
933
+ length,
934
+ defaultIndex = 0,
935
+ index: controlledIndex,
936
+ onIndexChange,
937
+ loop = true
938
+ }) {
939
+ const [internalIndex, setInternalIndex] = react.useState(defaultIndex);
940
+ const safeLength = Math.max(1, length);
941
+ const rawIndex = controlledIndex ?? internalIndex;
942
+ const index = Math.max(0, Math.min(safeLength - 1, rawIndex));
943
+ const setIndex = react.useCallback(
944
+ (next2) => {
945
+ if (length <= 0) return;
946
+ let resolved;
947
+ if (loop) {
948
+ resolved = (next2 % safeLength + safeLength) % safeLength;
949
+ } else {
950
+ resolved = Math.max(0, Math.min(safeLength - 1, next2));
951
+ }
952
+ if (resolved === index) return;
953
+ if (controlledIndex === void 0) {
954
+ setInternalIndex(resolved);
955
+ }
956
+ onIndexChange?.(resolved);
957
+ },
958
+ [length, safeLength, loop, index, controlledIndex, onIndexChange]
959
+ );
960
+ const prev = react.useCallback(() => setIndex(index - 1), [setIndex, index]);
961
+ const next = react.useCallback(() => setIndex(index + 1), [setIndex, index]);
962
+ return {
963
+ index,
964
+ canPrev: loop || index > 0,
965
+ canNext: loop || index < safeLength - 1,
966
+ setIndex,
967
+ prev,
968
+ next
969
+ };
970
+ }
613
971
 
614
972
  exports.ImageMagnify = ImageMagnify;
973
+ exports.ImageMagnifyGallery = ImageMagnifyGallery;
615
974
  exports.useImageMagnify = useImageMagnify;
975
+ exports.useImageSlider = useImageSlider;
616
976
  //# sourceMappingURL=index.cjs.map
617
977
  //# sourceMappingURL=index.cjs.map