uix-image-magnify 0.2.2 → 0.2.4

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/ssr/index.js CHANGED
@@ -1,5 +1,9 @@
1
- import { forwardRef, useState, useRef, useCallback, useEffect, useLayoutEffect } from 'react';
2
- import { jsxs, jsx } from 'react/jsx-runtime';
1
+ import { forwardRef, useState, useRef, useMemo, useEffect, useCallback, useLayoutEffect } from 'react';
2
+ import { jsxs, jsx, Fragment } from 'react/jsx-runtime';
3
+ import { Swiper, SwiperSlide } from 'swiper/react';
4
+ import { Navigation, Keyboard, A11y } from 'swiper/modules';
5
+ import 'swiper/css';
6
+ import 'swiper/css/navigation';
3
7
 
4
8
  // src/ssr/index.tsx
5
9
  var clamp = (v, min, max) => Math.max(min, Math.min(max, v));
@@ -75,6 +79,15 @@ function useImageMagnify(options = {}) {
75
79
  isZoomedRef.current = z;
76
80
  setIsZoomed(z);
77
81
  if (z) {
82
+ if (modeRef.current === "inner" && dragModeRef.current === "pan") {
83
+ const container = containerRef.current;
84
+ if (container) {
85
+ const rect = container.getBoundingClientRect();
86
+ const s = scaleRef.current;
87
+ txRef.current = rect.width * (1 - s) / 2;
88
+ tyRef.current = rect.height * (1 - s) / 2;
89
+ }
90
+ }
78
91
  onZoomStart?.();
79
92
  } else {
80
93
  if (zoomType === "wheel") {
@@ -281,8 +294,6 @@ function useImageMagnify(options = {}) {
281
294
  setZoomed(!isZoomedRef.current);
282
295
  } else if (zoomType === "click") {
283
296
  setZoomed(!isZoomedRef.current);
284
- } else if (zoomType === "wheel") {
285
- setZoomed(!isZoomedRef.current);
286
297
  }
287
298
  schedule();
288
299
  },
@@ -499,7 +510,8 @@ var ImageMagnify = forwardRef(
499
510
  const showLens = (mode === "lens" || mode === "follow") && isZoomed;
500
511
  const showPane = mode === "side-by-side" && isZoomed;
501
512
  const showCursor = mode === "side-by-side" && isZoomed;
502
- const cursorStyle = disabled ? "default" : zoomType === "hover" || zoomType === "hold" || zoomType === "wheel" ? "crosshair" : isZoomed ? "zoom-out" : "zoom-in";
513
+ const isInteractive = !disabled && zoomType !== "none";
514
+ const cursorStyle = !isInteractive ? "default" : zoomType === "hover" || zoomType === "hold" || zoomType === "wheel" ? "crosshair" : isZoomed ? "zoom-out" : "zoom-in";
503
515
  const outerWidth = isMobile ? "100%" : width;
504
516
  return /* @__PURE__ */ jsxs(
505
517
  "div",
@@ -519,7 +531,7 @@ var ImageMagnify = forwardRef(
519
531
  "div",
520
532
  {
521
533
  ref: containerRef,
522
- tabIndex: 0,
534
+ tabIndex: isInteractive ? 0 : -1,
523
535
  role: "img",
524
536
  "aria-label": alt,
525
537
  "aria-disabled": disabled || void 0,
@@ -635,6 +647,394 @@ var ImageMagnify = forwardRef(
635
647
  }
636
648
  );
637
649
  ImageMagnify.displayName = "ImageMagnify";
650
+ var ArrowLeftIcon = () => /* @__PURE__ */ jsx(
651
+ "svg",
652
+ {
653
+ viewBox: "0 0 24 24",
654
+ width: "20",
655
+ height: "20",
656
+ fill: "none",
657
+ stroke: "currentColor",
658
+ strokeWidth: "2",
659
+ strokeLinecap: "round",
660
+ strokeLinejoin: "round",
661
+ "aria-hidden": "true",
662
+ children: /* @__PURE__ */ jsx("polyline", { points: "15 18 9 12 15 6" })
663
+ }
664
+ );
665
+ var ArrowRightIcon = () => /* @__PURE__ */ jsx(
666
+ "svg",
667
+ {
668
+ viewBox: "0 0 24 24",
669
+ width: "20",
670
+ height: "20",
671
+ fill: "none",
672
+ stroke: "currentColor",
673
+ strokeWidth: "2",
674
+ strokeLinecap: "round",
675
+ strokeLinejoin: "round",
676
+ "aria-hidden": "true",
677
+ children: /* @__PURE__ */ jsx("polyline", { points: "9 18 15 12 9 6" })
678
+ }
679
+ );
680
+ var defaultArrowBaseStyle = {
681
+ position: "absolute",
682
+ top: "50%",
683
+ transform: "translateY(-50%)",
684
+ width: 40,
685
+ height: 40,
686
+ display: "inline-flex",
687
+ alignItems: "center",
688
+ justifyContent: "center",
689
+ background: "rgba(255,255,255,0.85)",
690
+ border: "1px solid rgba(0,0,0,0.12)",
691
+ borderRadius: 9999,
692
+ boxShadow: "0 4px 12px rgba(0,0,0,0.15)",
693
+ cursor: "pointer",
694
+ zIndex: 10,
695
+ color: "#111",
696
+ padding: 0,
697
+ transition: "background 0.15s ease",
698
+ backdropFilter: "blur(4px)",
699
+ WebkitBackdropFilter: "blur(4px)"
700
+ };
701
+ var defaultThumbBaseStyle = {
702
+ flexShrink: 0,
703
+ padding: 0,
704
+ border: "2px solid #e5e5e5",
705
+ background: "#fff",
706
+ cursor: "pointer",
707
+ overflow: "hidden",
708
+ transition: "border-color 0.15s ease, opacity 0.15s ease",
709
+ borderRadius: 2,
710
+ display: "inline-flex",
711
+ alignItems: "center",
712
+ justifyContent: "center",
713
+ boxSizing: "border-box"
714
+ };
715
+ var defaultThumbActiveStyle = {
716
+ borderColor: "#111"
717
+ };
718
+ var visuallyHidden = {
719
+ position: "absolute",
720
+ width: 1,
721
+ height: 1,
722
+ padding: 0,
723
+ margin: -1,
724
+ overflow: "hidden",
725
+ clip: "rect(0,0,0,0)",
726
+ whiteSpace: "nowrap",
727
+ border: 0
728
+ };
729
+ var ImageMagnifyGallery = forwardRef((props, ref) => {
730
+ const {
731
+ images,
732
+ alt,
733
+ activeIndex: controlledActiveIndex,
734
+ defaultIndex = 0,
735
+ onIndexChange,
736
+ showArrows = true,
737
+ showThumbnails = true,
738
+ showCounter = false,
739
+ loop = true,
740
+ enableKeyboard = true,
741
+ preloadAdjacent = true,
742
+ className,
743
+ style,
744
+ viewportClassName,
745
+ viewportStyle,
746
+ swiperStyle,
747
+ thumbnailContainerClassName,
748
+ thumbnailContainerStyle,
749
+ thumbnailClassName,
750
+ thumbnailActiveClassName,
751
+ thumbnailStyle,
752
+ thumbnailActiveStyle,
753
+ arrowClassName,
754
+ arrowStyle,
755
+ thumbnailSize = 80,
756
+ thumbnailGap = 8,
757
+ prevArrow,
758
+ nextArrow,
759
+ width,
760
+ ...magnifyProps
761
+ } = props;
762
+ const [swiper, setSwiper] = useState(null);
763
+ const [localIndex, setLocalIndex] = useState(() => {
764
+ const safeLength = Math.max(1, images.length);
765
+ return Math.max(0, Math.min(safeLength - 1, defaultIndex));
766
+ });
767
+ const prevRef = useRef(null);
768
+ const nextRef = useRef(null);
769
+ const isControlled = controlledActiveIndex !== void 0;
770
+ const rawIndex = isControlled ? controlledActiveIndex : localIndex;
771
+ const activeIndex = images.length ? Math.max(0, Math.min(images.length - 1, rawIndex)) : 0;
772
+ const hasMultiple = images.length > 1;
773
+ const modules = useMemo(() => [Navigation, Keyboard, A11y], []);
774
+ useEffect(() => {
775
+ if (!swiper || swiper.destroyed) return;
776
+ if (isControlled && swiper.activeIndex !== controlledActiveIndex) {
777
+ swiper.slideTo(controlledActiveIndex);
778
+ }
779
+ }, [isControlled, controlledActiveIndex, swiper]);
780
+ useEffect(() => {
781
+ if (!preloadAdjacent || typeof window === "undefined" || images.length <= 1)
782
+ return;
783
+ const targets = /* @__PURE__ */ new Set();
784
+ if (loop) {
785
+ targets.add((activeIndex + 1) % images.length);
786
+ targets.add((activeIndex - 1 + images.length) % images.length);
787
+ } else {
788
+ if (activeIndex < images.length - 1) targets.add(activeIndex + 1);
789
+ if (activeIndex > 0) targets.add(activeIndex - 1);
790
+ }
791
+ targets.forEach((i) => {
792
+ const src = images[i];
793
+ if (src) {
794
+ const img = new Image();
795
+ img.src = src;
796
+ }
797
+ });
798
+ }, [activeIndex, images, preloadAdjacent, loop]);
799
+ const handleSlideChange = (s) => {
800
+ const idx = s.activeIndex;
801
+ if (!isControlled) setLocalIndex(idx);
802
+ onIndexChange?.(idx);
803
+ };
804
+ const handleThumbClick = (idx) => {
805
+ if (!swiper || swiper.destroyed) return;
806
+ swiper.slideTo(idx);
807
+ };
808
+ if (images.length === 0) {
809
+ return /* @__PURE__ */ jsx("div", { ref, className, style });
810
+ }
811
+ return /* @__PURE__ */ jsxs(
812
+ "div",
813
+ {
814
+ ref,
815
+ className,
816
+ style: { maxWidth: "100%", width, ...style },
817
+ role: "region",
818
+ "aria-roledescription": "carousel",
819
+ "aria-label": `${alt} \u2014 image gallery`,
820
+ children: [
821
+ /* @__PURE__ */ jsxs(
822
+ "div",
823
+ {
824
+ className: viewportClassName,
825
+ style: { position: "relative", width: "100%", ...viewportStyle },
826
+ children: [
827
+ /* @__PURE__ */ jsx(
828
+ Swiper,
829
+ {
830
+ modules,
831
+ onSwiper: setSwiper,
832
+ initialSlide: isControlled ? controlledActiveIndex : defaultIndex,
833
+ onSlideChange: handleSlideChange,
834
+ onBeforeInit: (s) => {
835
+ if (!showArrows || !hasMultiple) return;
836
+ const nav = s.params.navigation;
837
+ if (nav && typeof nav === "object") {
838
+ nav.prevEl = prevRef.current;
839
+ nav.nextEl = nextRef.current;
840
+ }
841
+ },
842
+ navigation: showArrows && hasMultiple ? {} : false,
843
+ keyboard: enableKeyboard ? { enabled: true } : false,
844
+ loop: loop && hasMultiple,
845
+ autoHeight: true,
846
+ spaceBetween: 10,
847
+ observer: true,
848
+ observeParents: true,
849
+ style: { width: "100%", ...swiperStyle },
850
+ children: images.map((src, idx) => /* @__PURE__ */ jsx(SwiperSlide, { children: /* @__PURE__ */ jsx(
851
+ ImageMagnify,
852
+ {
853
+ ...magnifyProps,
854
+ src,
855
+ alt: `${alt} \u2014 image ${idx + 1} of ${images.length}`,
856
+ width: "100%"
857
+ }
858
+ ) }, idx))
859
+ }
860
+ ),
861
+ showArrows && hasMultiple && /* @__PURE__ */ jsxs(Fragment, { children: [
862
+ /* @__PURE__ */ jsx(
863
+ "button",
864
+ {
865
+ ref: prevRef,
866
+ type: "button",
867
+ className: arrowClassName,
868
+ style: { ...defaultArrowBaseStyle, left: 12, ...arrowStyle },
869
+ "aria-label": "Previous image",
870
+ children: prevArrow ?? /* @__PURE__ */ jsx(ArrowLeftIcon, {})
871
+ }
872
+ ),
873
+ /* @__PURE__ */ jsx(
874
+ "button",
875
+ {
876
+ ref: nextRef,
877
+ type: "button",
878
+ className: arrowClassName,
879
+ style: { ...defaultArrowBaseStyle, right: 12, ...arrowStyle },
880
+ "aria-label": "Next image",
881
+ children: nextArrow ?? /* @__PURE__ */ jsx(ArrowRightIcon, {})
882
+ }
883
+ )
884
+ ] }),
885
+ showCounter && hasMultiple && /* @__PURE__ */ jsxs(
886
+ "div",
887
+ {
888
+ "aria-hidden": "true",
889
+ style: {
890
+ position: "absolute",
891
+ top: 12,
892
+ right: 12,
893
+ background: "rgba(0,0,0,0.6)",
894
+ color: "#fff",
895
+ fontSize: 12,
896
+ fontWeight: 600,
897
+ padding: "4px 8px",
898
+ borderRadius: 4,
899
+ zIndex: 10,
900
+ pointerEvents: "none"
901
+ },
902
+ children: [
903
+ activeIndex + 1,
904
+ " / ",
905
+ images.length
906
+ ]
907
+ }
908
+ ),
909
+ /* @__PURE__ */ jsxs("div", { "aria-live": "polite", "aria-atomic": "true", style: visuallyHidden, children: [
910
+ "Image ",
911
+ activeIndex + 1,
912
+ " of ",
913
+ images.length
914
+ ] })
915
+ ]
916
+ }
917
+ ),
918
+ showThumbnails && hasMultiple && /* @__PURE__ */ jsx(
919
+ "div",
920
+ {
921
+ className: thumbnailContainerClassName,
922
+ role: "group",
923
+ "aria-label": "Image thumbnails",
924
+ style: {
925
+ display: "flex",
926
+ gap: thumbnailGap,
927
+ marginTop: 12,
928
+ overflowX: "auto",
929
+ overflowY: "hidden",
930
+ paddingBottom: 4,
931
+ scrollbarWidth: "thin",
932
+ ...thumbnailContainerStyle
933
+ },
934
+ children: images.map((src, idx) => {
935
+ const isActive = idx === activeIndex;
936
+ const mergedClassName = [
937
+ thumbnailClassName,
938
+ isActive ? thumbnailActiveClassName : void 0
939
+ ].filter(Boolean).join(" ") || void 0;
940
+ return /* @__PURE__ */ jsx(
941
+ "button",
942
+ {
943
+ type: "button",
944
+ onClick: () => handleThumbClick(idx),
945
+ className: mergedClassName,
946
+ style: {
947
+ ...defaultThumbBaseStyle,
948
+ width: thumbnailSize,
949
+ height: thumbnailSize,
950
+ ...thumbnailStyle,
951
+ ...isActive ? { ...defaultThumbActiveStyle, ...thumbnailActiveStyle } : void 0
952
+ },
953
+ "aria-label": `View image ${idx + 1}`,
954
+ "aria-current": isActive ? "true" : void 0,
955
+ children: /* @__PURE__ */ jsx(
956
+ "img",
957
+ {
958
+ src,
959
+ alt: "",
960
+ loading: "lazy",
961
+ draggable: false,
962
+ style: {
963
+ width: "100%",
964
+ height: "100%",
965
+ objectFit: "cover",
966
+ display: "block",
967
+ pointerEvents: "none"
968
+ }
969
+ }
970
+ )
971
+ },
972
+ idx
973
+ );
974
+ })
975
+ }
976
+ )
977
+ ]
978
+ }
979
+ );
980
+ });
981
+ ImageMagnifyGallery.displayName = "ImageMagnifyGallery";
982
+ function ImageMagnifyGallerySSR(props) {
983
+ const [mounted, setMounted] = useState(false);
984
+ useEffect(() => {
985
+ setMounted(true);
986
+ }, []);
987
+ if (!mounted) {
988
+ const {
989
+ images,
990
+ alt,
991
+ defaultIndex = 0,
992
+ activeIndex,
993
+ width,
994
+ className,
995
+ style,
996
+ imageClassName,
997
+ imageStyle,
998
+ imageProps,
999
+ lazy
1000
+ } = props;
1001
+ const idx = activeIndex ?? defaultIndex;
1002
+ const src = images?.[idx] ?? images?.[0] ?? "";
1003
+ return /* @__PURE__ */ jsx(
1004
+ "div",
1005
+ {
1006
+ className,
1007
+ style: {
1008
+ position: "relative",
1009
+ display: "inline-block",
1010
+ lineHeight: 0,
1011
+ width,
1012
+ ...style
1013
+ },
1014
+ children: /* @__PURE__ */ jsx(
1015
+ "img",
1016
+ {
1017
+ ...imageProps,
1018
+ src,
1019
+ alt,
1020
+ loading: lazy ? "lazy" : void 0,
1021
+ draggable: false,
1022
+ className: imageClassName,
1023
+ style: {
1024
+ display: "block",
1025
+ width: "100%",
1026
+ height: "auto",
1027
+ userSelect: "none",
1028
+ pointerEvents: "none",
1029
+ ...imageStyle
1030
+ }
1031
+ }
1032
+ )
1033
+ }
1034
+ );
1035
+ }
1036
+ return /* @__PURE__ */ jsx(ImageMagnifyGallery, { ...props });
1037
+ }
638
1038
  function ImageMagnifySSR(props) {
639
1039
  const [mounted, setMounted] = useState(false);
640
1040
  useEffect(() => {
@@ -692,6 +1092,6 @@ function ImageMagnifySSR(props) {
692
1092
  return /* @__PURE__ */ jsx(ImageMagnify, { ...props });
693
1093
  }
694
1094
 
695
- export { ImageMagnify, ImageMagnifySSR };
1095
+ export { ImageMagnify, ImageMagnifyGallery, ImageMagnifyGallerySSR, ImageMagnifySSR };
696
1096
  //# sourceMappingURL=index.js.map
697
1097
  //# sourceMappingURL=index.js.map