@mmmmzxe/react-360-viewer 0.1.2 → 0.1.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/index.d.ts CHANGED
@@ -1,5 +1,5 @@
1
1
  import * as React$1 from 'react';
2
- import { ReactNode, CSSProperties, MouseEvent, JSX, RefObject, PointerEvent, WheelEvent } from 'react';
2
+ import { ReactNode, MouseEvent, CSSProperties, JSX, RefObject, PointerEvent, WheelEvent } from 'react';
3
3
  import { ClassValue } from 'clsx';
4
4
  import * as class_variance_authority_types from 'class-variance-authority/types';
5
5
  import { VariantProps } from 'class-variance-authority';
@@ -104,6 +104,7 @@ type Viewer360MarkerPinProps<TData = unknown> = {
104
104
  topPercent: number;
105
105
  onDelete?: (id: string) => void;
106
106
  isDeletePending?: boolean;
107
+ onClick?: (event: MouseEvent<HTMLButtonElement>) => void;
107
108
  renderTag?: (props: Viewer360MarkerPinRenderProps<TData>) => ReactNode;
108
109
  classNames?: Viewer360MarkerPinClassNames;
109
110
  labels?: Viewer360MarkerPinLabels;
@@ -230,7 +231,7 @@ type Viewer360LoadingOverlayProps = {
230
231
  };
231
232
  declare function Viewer360LoadingOverlay({ className, textClassName, label }: Viewer360LoadingOverlayProps): JSX.Element;
232
233
 
233
- declare function Viewer360MarkerPin<TData = unknown>({ marker, hotspot, leftPercent, topPercent, onDelete, isDeletePending, renderTag, classNames, labels, }: Viewer360MarkerPinProps<TData>): JSX.Element;
234
+ declare function Viewer360MarkerPin<TData = unknown>({ marker, hotspot, leftPercent, topPercent, onDelete, isDeletePending, onClick, renderTag, classNames, labels, }: Viewer360MarkerPinProps<TData>): JSX.Element;
234
235
 
235
236
  type Viewer360ToolbarProps = Viewer360ToolbarRenderProps;
236
237
  declare function Viewer360Toolbar({ showDragHint, showHotspotModeControl, showZoomControls, showResetControl, labels, isHotspotMode, zoom, minZoom, maxZoom, isResetDisabled, onHotspotModeChange, onZoomIn, onZoomOut, onResetView, }: Viewer360ToolbarProps): JSX.Element;
package/dist/index.js CHANGED
@@ -1,13 +1,13 @@
1
1
  "use client";
2
2
 
3
3
  // src/feature/Viewer360.tsx
4
- import { useEffect as useEffect2, useMemo as useMemo2, useState as useState3 } from "react";
4
+ import { useEffect as useEffect2, useMemo as useMemo2, useState as useState2 } from "react";
5
5
 
6
6
  // src/constants/viewer360Labels.ts
7
7
  var defaultViewer360Labels = {
8
8
  loading: "Loading images\u2026",
9
9
  dragHint: "Drag to rotate \u2022 Scroll to zoom",
10
- frameIndicator: ({ current, total, label }) => label ? `${label} (${current}/${total})` : `${current} / ${total}`,
10
+ frameIndicator: ({ current, total, label }) => label ? `${label} \xB7 ${current} / ${total}` : `${current} / ${total}`,
11
11
  zoom: (percent) => `${percent}%`,
12
12
  hotspotModeActive: "Click on the image to place a hotspot",
13
13
  addHotspot: "Add hotspot",
@@ -25,7 +25,7 @@ var viewer360ClassNames = {
25
25
  overlay: "pointer-events-none absolute inset-0 overflow-hidden",
26
26
  loading: "absolute inset-0 flex items-center justify-center bg-muted/80",
27
27
  loadingText: "text-sm text-muted-foreground",
28
- frameIndicator: "pointer-events-none absolute bottom-4 start-1/2 z-20 -translate-x-1/2 rounded-full border bg-background px-4 py-1.5 text-xs font-medium shadow-sm",
28
+ frameIndicator: "pointer-events-none absolute bottom-4 start-4 z-20 rounded-full border bg-background px-4 py-1.5 text-xs font-medium shadow-sm",
29
29
  hotspotModeBanner: "pointer-events-none absolute top-4 start-1/2 z-20 -translate-x-1/2 rounded-full border border-amber-200 bg-amber-50 px-4 py-1.5 text-xs font-medium text-amber-800 dark:border-amber-500/30 dark:bg-amber-500/10 dark:text-amber-400",
30
30
  toolbar: "flex flex-wrap items-center justify-between gap-2 border-t px-4 py-3",
31
31
  dragHint: "hidden text-xs text-muted-foreground sm:block",
@@ -577,73 +577,20 @@ function toViewer360Hotspots(markers, mapData) {
577
577
  }));
578
578
  }
579
579
 
580
- // src/components/ui/Button/index.tsx
580
+ // src/components/ui/Item/index.tsx
581
581
  import { cva as cva2 } from "class-variance-authority";
582
582
  import { Slot as Slot2 } from "radix-ui";
583
- import { jsx as jsx5 } from "react/jsx-runtime";
584
- var buttonVariants = cva2(
585
- "focus-visible:border-ring focus-visible:ring-ring/50 aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive dark:aria-invalid:border-destructive/50 rounded-md border border-transparent bg-clip-padding text-sm font-medium focus-visible:ring-[3px] aria-invalid:ring-[3px] inline-flex items-center justify-center whitespace-nowrap transition-all disabled:pointer-events-none disabled:opacity-50 [&_svg]:pointer-events-none shrink-0 [&_svg]:shrink-0 outline-none group/button select-none",
586
- {
587
- variants: {
588
- variant: {
589
- default: "bg-primary text-primary-foreground hover:bg-primary/80",
590
- outline: "border-border bg-background hover:bg-muted hover:text-foreground dark:bg-input/30 dark:border-input dark:hover:bg-input/50 aria-expanded:bg-muted aria-expanded:text-foreground shadow-xs",
591
- secondary: "bg-secondary text-secondary-foreground hover:bg-secondary/80 aria-expanded:bg-secondary aria-expanded:text-secondary-foreground",
592
- ghost: "hover:bg-muted hover:text-foreground dark:hover:bg-muted/50 aria-expanded:bg-muted aria-expanded:text-foreground",
593
- destructive: "bg-destructive/10 hover:bg-destructive/20 focus-visible:ring-destructive/20 dark:focus-visible:ring-destructive/40 dark:bg-destructive/20 text-destructive focus-visible:border-destructive/40 dark:hover:bg-destructive/30",
594
- link: "text-primary underline-offset-4 hover:underline"
595
- },
596
- size: {
597
- default: "h-9 gap-1.5 px-2.5 in-data-[slot=button-group]:rounded-md has-data-[icon=inline-end]:pe-2 has-data-[icon=inline-start]:ps-2",
598
- xs: "h-6 gap-1 rounded-[min(var(--radius-md),8px)] px-2 text-xs in-data-[slot=button-group]:rounded-md has-data-[icon=inline-end]:pe-1.5 has-data-[icon=inline-start]:ps-1.5",
599
- sm: "h-8 gap-1 rounded-[min(var(--radius-md),10px)] px-2.5 in-data-[slot=button-group]:rounded-md has-data-[icon=inline-end]:pe-1.5 has-data-[icon=inline-start]:ps-1.5",
600
- lg: "h-10 gap-1.5 px-2.5 has-data-[icon=inline-end]:pe-3 has-data-[icon=inline-start]:ps-3",
601
- icon: "size-9",
602
- "icon-xs": "size-6 rounded-[min(var(--radius-md),8px)] in-data-[slot=button-group]:rounded-md",
603
- "icon-sm": "size-8 rounded-[min(var(--radius-md),10px)] in-data-[slot=button-group]:rounded-md",
604
- "icon-lg": "size-10"
605
- }
606
- },
607
- defaultVariants: {
608
- variant: "default",
609
- size: "default"
610
- }
611
- }
612
- );
613
- function Button({
614
- className,
615
- variant = "default",
616
- size = "default",
617
- asChild = false,
618
- ...props
619
- }) {
620
- const Comp = asChild ? Slot2.Root : "button";
621
- return /* @__PURE__ */ jsx5(
622
- Comp,
623
- {
624
- "data-slot": "button",
625
- "data-variant": variant,
626
- "data-size": size,
627
- className: cn(buttonVariants({ variant, size, className })),
628
- ...props
629
- }
630
- );
631
- }
632
-
633
- // src/components/ui/Item/index.tsx
634
- import { cva as cva3 } from "class-variance-authority";
635
- import { Slot as Slot3 } from "radix-ui";
636
583
 
637
584
  // src/components/ui/Separator/index.tsx
638
585
  import { Separator as SeparatorPrimitive } from "radix-ui";
639
- import { jsx as jsx6 } from "react/jsx-runtime";
586
+ import { jsx as jsx5 } from "react/jsx-runtime";
640
587
  function Separator({
641
588
  className,
642
589
  orientation = "horizontal",
643
590
  decorative = true,
644
591
  ...props
645
592
  }) {
646
- return /* @__PURE__ */ jsx6(
593
+ return /* @__PURE__ */ jsx5(
647
594
  SeparatorPrimitive.Root,
648
595
  {
649
596
  "data-slot": "separator",
@@ -659,8 +606,8 @@ function Separator({
659
606
  }
660
607
 
661
608
  // src/components/ui/Item/index.tsx
662
- import { jsx as jsx7 } from "react/jsx-runtime";
663
- var itemVariants = cva3(
609
+ import { jsx as jsx6 } from "react/jsx-runtime";
610
+ var itemVariants = cva2(
664
611
  "[a]:hover:bg-muted rounded-md border text-sm w-full group/item focus-visible:border-ring focus-visible:ring-ring/50 flex items-center flex-wrap outline-none transition-colors duration-100 focus-visible:ring-[3px] [a]:transition-colors",
665
612
  {
666
613
  variants: {
@@ -688,8 +635,8 @@ function Item({
688
635
  asChild = false,
689
636
  ...props
690
637
  }) {
691
- const Comp = asChild ? Slot3.Root : "div";
692
- return /* @__PURE__ */ jsx7(
638
+ const Comp = asChild ? Slot2.Root : "div";
639
+ return /* @__PURE__ */ jsx6(
693
640
  Comp,
694
641
  {
695
642
  "data-slot": "item",
@@ -700,7 +647,7 @@ function Item({
700
647
  }
701
648
  );
702
649
  }
703
- var itemMediaVariants = cva3(
650
+ var itemMediaVariants = cva2(
704
651
  "gap-2 group-has-[[data-slot=item-description]]/item:translate-y-0.5 group-has-[[data-slot=item-description]]/item:self-start flex shrink-0 items-center justify-center [&_svg]:pointer-events-none",
705
652
  {
706
653
  variants: {
@@ -716,7 +663,7 @@ var itemMediaVariants = cva3(
716
663
  }
717
664
  );
718
665
  function ItemContent({ className, ...props }) {
719
- return /* @__PURE__ */ jsx7(
666
+ return /* @__PURE__ */ jsx6(
720
667
  "div",
721
668
  {
722
669
  "data-slot": "item-content",
@@ -726,7 +673,7 @@ function ItemContent({ className, ...props }) {
726
673
  );
727
674
  }
728
675
  function ItemTitle({ className, ...props }) {
729
- return /* @__PURE__ */ jsx7(
676
+ return /* @__PURE__ */ jsx6(
730
677
  "div",
731
678
  {
732
679
  "data-slot": "item-title",
@@ -736,7 +683,7 @@ function ItemTitle({ className, ...props }) {
736
683
  );
737
684
  }
738
685
  function ItemDescription({ className, ...props }) {
739
- return /* @__PURE__ */ jsx7(
686
+ return /* @__PURE__ */ jsx6(
740
687
  "p",
741
688
  {
742
689
  "data-slot": "item-description",
@@ -749,11 +696,10 @@ function ItemDescription({ className, ...props }) {
749
696
  );
750
697
  }
751
698
  function ItemActions({ className, ...props }) {
752
- return /* @__PURE__ */ jsx7("div", { "data-slot": "item-actions", className: cn("gap-2 flex items-center", className), ...props });
699
+ return /* @__PURE__ */ jsx6("div", { "data-slot": "item-actions", className: cn("gap-2 flex items-center", className), ...props });
753
700
  }
754
701
 
755
702
  // src/feature/Viewer360MarkerPin.tsx
756
- import { useState as useState2 } from "react";
757
703
  import { Trash2 } from "lucide-react";
758
704
 
759
705
  // src/constants/viewer360MarkerLabels.ts
@@ -761,38 +707,61 @@ var defaultViewer360MarkerPinLabels = {
761
707
  delete: "Remove marker"
762
708
  };
763
709
 
764
- // src/components/ui/Popover/index.tsx
765
- import { Popover as PopoverPrimitive } from "radix-ui";
766
- import { jsx as jsx8 } from "react/jsx-runtime";
767
- function Popover({ ...props }) {
768
- return /* @__PURE__ */ jsx8(PopoverPrimitive.Root, { "data-slot": "popover", ...props });
769
- }
770
- function PopoverTrigger({ ...props }) {
771
- return /* @__PURE__ */ jsx8(PopoverPrimitive.Trigger, { "data-slot": "popover-trigger", ...props });
772
- }
773
- function PopoverContent({
710
+ // src/components/ui/Button/index.tsx
711
+ import { cva as cva3 } from "class-variance-authority";
712
+ import { Slot as Slot3 } from "radix-ui";
713
+ import { jsx as jsx7 } from "react/jsx-runtime";
714
+ var buttonVariants = cva3(
715
+ "focus-visible:border-ring focus-visible:ring-ring/50 aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive dark:aria-invalid:border-destructive/50 rounded-md border border-transparent bg-clip-padding text-sm font-medium focus-visible:ring-[3px] aria-invalid:ring-[3px] inline-flex items-center justify-center whitespace-nowrap transition-all disabled:pointer-events-none disabled:opacity-50 [&_svg]:pointer-events-none shrink-0 [&_svg]:shrink-0 outline-none group/button select-none",
716
+ {
717
+ variants: {
718
+ variant: {
719
+ default: "bg-primary text-primary-foreground hover:bg-primary/80",
720
+ outline: "border-border bg-background hover:bg-muted hover:text-foreground dark:bg-input/30 dark:border-input dark:hover:bg-input/50 aria-expanded:bg-muted aria-expanded:text-foreground shadow-xs",
721
+ secondary: "bg-secondary text-secondary-foreground hover:bg-secondary/80 aria-expanded:bg-secondary aria-expanded:text-secondary-foreground",
722
+ ghost: "hover:bg-muted hover:text-foreground dark:hover:bg-muted/50 aria-expanded:bg-muted aria-expanded:text-foreground",
723
+ destructive: "bg-destructive/10 hover:bg-destructive/20 focus-visible:ring-destructive/20 dark:focus-visible:ring-destructive/40 dark:bg-destructive/20 text-destructive focus-visible:border-destructive/40 dark:hover:bg-destructive/30",
724
+ link: "text-primary underline-offset-4 hover:underline"
725
+ },
726
+ size: {
727
+ default: "h-9 gap-1.5 px-2.5 in-data-[slot=button-group]:rounded-md has-data-[icon=inline-end]:pe-2 has-data-[icon=inline-start]:ps-2",
728
+ xs: "h-6 gap-1 rounded-[min(var(--radius-md),8px)] px-2 text-xs in-data-[slot=button-group]:rounded-md has-data-[icon=inline-end]:pe-1.5 has-data-[icon=inline-start]:ps-1.5",
729
+ sm: "h-8 gap-1 rounded-[min(var(--radius-md),10px)] px-2.5 in-data-[slot=button-group]:rounded-md has-data-[icon=inline-end]:pe-1.5 has-data-[icon=inline-start]:ps-1.5",
730
+ lg: "h-10 gap-1.5 px-2.5 has-data-[icon=inline-end]:pe-3 has-data-[icon=inline-start]:ps-3",
731
+ icon: "size-9",
732
+ "icon-xs": "size-6 rounded-[min(var(--radius-md),8px)] in-data-[slot=button-group]:rounded-md",
733
+ "icon-sm": "size-8 rounded-[min(var(--radius-md),10px)] in-data-[slot=button-group]:rounded-md",
734
+ "icon-lg": "size-10"
735
+ }
736
+ },
737
+ defaultVariants: {
738
+ variant: "default",
739
+ size: "default"
740
+ }
741
+ }
742
+ );
743
+ function Button({
774
744
  className,
775
- align = "center",
776
- sideOffset = 4,
745
+ variant = "default",
746
+ size = "default",
747
+ asChild = false,
777
748
  ...props
778
749
  }) {
779
- return /* @__PURE__ */ jsx8(PopoverPrimitive.Portal, { children: /* @__PURE__ */ jsx8(
780
- PopoverPrimitive.Content,
750
+ const Comp = asChild ? Slot3.Root : "button";
751
+ return /* @__PURE__ */ jsx7(
752
+ Comp,
781
753
  {
782
- "data-slot": "popover-content",
783
- align,
784
- sideOffset,
785
- className: cn(
786
- "bg-popover text-popover-foreground data-open:animate-in data-closed:animate-out data-closed:fade-out-0 data-open:fade-in-0 data-closed:zoom-out-95 data-open:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=start]:slide-in-from-end-2 data-[side=end]:slide-in-from-start-2 data-[side=top]:slide-in-from-bottom-2 ring-foreground/10 flex flex-col gap-4 rounded-md p-4 text-sm shadow-md ring-1 duration-100 z-50 w-72 origin-(--radix-popover-content-transform-origin) outline-hidden",
787
- className
788
- ),
754
+ "data-slot": "button",
755
+ "data-variant": variant,
756
+ "data-size": size,
757
+ className: cn(buttonVariants({ variant, size, className })),
789
758
  ...props
790
759
  }
791
- ) });
760
+ );
792
761
  }
793
762
 
794
763
  // src/feature/Viewer360MarkerPin.tsx
795
- import { jsx as jsx9, jsxs } from "react/jsx-runtime";
764
+ import { jsx as jsx8, jsxs } from "react/jsx-runtime";
796
765
  function Viewer360MarkerPin({
797
766
  marker,
798
767
  hotspot,
@@ -800,23 +769,26 @@ function Viewer360MarkerPin({
800
769
  topPercent,
801
770
  onDelete,
802
771
  isDeletePending = false,
772
+ onClick,
803
773
  renderTag,
804
774
  classNames,
805
775
  labels
806
776
  }) {
807
- const [isOpen, setIsOpen] = useState2(false);
808
777
  const deleteLabel = labels?.delete ?? defaultViewer360MarkerPinLabels.delete;
809
- return /* @__PURE__ */ jsx9(Popover, { open: isOpen, onOpenChange: setIsOpen, children: /* @__PURE__ */ jsxs(
778
+ const showTooltip = Boolean(marker.title || marker.description || onDelete || renderTag);
779
+ return /* @__PURE__ */ jsxs(
810
780
  Item,
811
781
  {
812
782
  size: "xs",
813
783
  variant: "default",
814
- className: cn(viewer360MarkerPinClassNames.root, classNames?.root, "w-auto border-transparent p-0"),
784
+ className: cn(
785
+ viewer360MarkerPinClassNames.root,
786
+ classNames?.root,
787
+ "group/marker w-auto border-transparent p-0"
788
+ ),
815
789
  style: { left: `${leftPercent}%`, top: `${topPercent}%` },
816
- onMouseEnter: () => setIsOpen(true),
817
- onMouseLeave: () => setIsOpen(false),
818
790
  children: [
819
- /* @__PURE__ */ jsx9(
791
+ /* @__PURE__ */ jsx8(
820
792
  Badge,
821
793
  {
822
794
  variant: "destructive",
@@ -824,7 +796,7 @@ function Viewer360MarkerPin({
824
796
  "aria-hidden": "true"
825
797
  }
826
798
  ),
827
- /* @__PURE__ */ jsx9(PopoverTrigger, { asChild: true, children: /* @__PURE__ */ jsx9(
799
+ /* @__PURE__ */ jsx8(
828
800
  Button,
829
801
  {
830
802
  type: "button",
@@ -835,16 +807,18 @@ function Viewer360MarkerPin({
835
807
  classNames?.dot,
836
808
  "size-4 min-h-4 min-w-4 rounded-full border-2 border-background bg-destructive p-0 shadow-md hover:scale-125 hover:bg-destructive"
837
809
  ),
838
- "aria-label": marker.title
810
+ "aria-label": marker.title,
811
+ onClick
839
812
  }
840
- ) }),
841
- /* @__PURE__ */ jsx9(
842
- PopoverContent,
813
+ ),
814
+ showTooltip && /* @__PURE__ */ jsx8(
815
+ "div",
843
816
  {
844
- className: cn(viewer360MarkerPinClassNames.tooltip, classNames?.tooltip, "w-64 p-0"),
845
- side: "top",
846
- align: "center",
847
- onOpenAutoFocus: (event) => event.preventDefault(),
817
+ className: cn(
818
+ viewer360MarkerPinClassNames.tooltip,
819
+ classNames?.tooltip,
820
+ "pointer-events-none opacity-0 transition-opacity duration-150 group-hover/marker:pointer-events-auto group-hover/marker:opacity-100 group-focus-within/marker:pointer-events-auto group-focus-within/marker:opacity-100"
821
+ ),
848
822
  children: /* @__PURE__ */ jsxs(
849
823
  Item,
850
824
  {
@@ -853,11 +827,11 @@ function Viewer360MarkerPin({
853
827
  className: cn(viewer360MarkerPinClassNames.tooltipHeader, classNames?.tooltipHeader, "w-full border-transparent"),
854
828
  children: [
855
829
  /* @__PURE__ */ jsxs(ItemContent, { className: cn(viewer360MarkerPinClassNames.tooltipBody, classNames?.tooltipBody), children: [
856
- /* @__PURE__ */ jsx9(ItemTitle, { className: cn(viewer360MarkerPinClassNames.tooltipTitle, classNames?.tooltipTitle), children: marker.title }),
830
+ /* @__PURE__ */ jsx8(ItemTitle, { className: cn(viewer360MarkerPinClassNames.tooltipTitle, classNames?.tooltipTitle), children: marker.title }),
857
831
  renderTag?.({ marker, hotspot }),
858
- marker.description && /* @__PURE__ */ jsx9(ItemDescription, { className: cn(viewer360MarkerPinClassNames.tooltipDescription, classNames?.tooltipDescription), children: marker.description })
832
+ marker.description && /* @__PURE__ */ jsx8(ItemDescription, { className: cn(viewer360MarkerPinClassNames.tooltipDescription, classNames?.tooltipDescription), children: marker.description })
859
833
  ] }),
860
- onDelete && /* @__PURE__ */ jsx9(ItemActions, { children: /* @__PURE__ */ jsx9(
834
+ onDelete && /* @__PURE__ */ jsx8(ItemActions, { children: /* @__PURE__ */ jsx8(
861
835
  Button,
862
836
  {
863
837
  variant: "ghost",
@@ -869,7 +843,7 @@ function Viewer360MarkerPin({
869
843
  event.stopPropagation();
870
844
  onDelete(marker.id);
871
845
  },
872
- children: /* @__PURE__ */ jsx9(Trash2, { className: "size-4" })
846
+ children: /* @__PURE__ */ jsx8(Trash2, { className: "size-4" })
873
847
  }
874
848
  ) })
875
849
  ]
@@ -879,11 +853,11 @@ function Viewer360MarkerPin({
879
853
  )
880
854
  ]
881
855
  }
882
- ) });
856
+ );
883
857
  }
884
858
 
885
859
  // src/feature/Viewer360HotspotOverlay.tsx
886
- import { jsx as jsx10 } from "react/jsx-runtime";
860
+ import { jsx as jsx9 } from "react/jsx-runtime";
887
861
  function Viewer360HotspotOverlay({
888
862
  hotspot,
889
863
  leftPercent,
@@ -893,45 +867,30 @@ function Viewer360HotspotOverlay({
893
867
  onHotspotClick
894
868
  }) {
895
869
  if (renderHotspot) {
896
- return /* @__PURE__ */ jsx10(Item, { size: "xs", variant: "default", className: "pointer-events-auto w-auto border-transparent p-0", children: renderHotspot({ hotspot, leftPercent, topPercent }) });
897
- }
898
- if (hotspotPin) {
899
- const marker = hotspotPin.getMarker?.(hotspot) ?? hotspotToViewer360Marker(hotspot);
900
- return /* @__PURE__ */ jsx10(
901
- Viewer360MarkerPin,
902
- {
903
- marker,
904
- hotspot,
905
- leftPercent,
906
- topPercent,
907
- onDelete: hotspotPin.onDelete,
908
- isDeletePending: hotspotPin.deletingMarkerId === hotspot.id,
909
- renderTag: hotspotPin.renderTag,
910
- classNames: hotspotPin.classNames,
911
- labels: hotspotPin.labels
912
- }
913
- );
870
+ return /* @__PURE__ */ jsx9(Item, { size: "xs", variant: "default", className: "pointer-events-auto w-auto border-transparent p-0", children: renderHotspot({ hotspot, leftPercent, topPercent }) });
914
871
  }
915
- return /* @__PURE__ */ jsx10(
916
- Button,
872
+ const marker = hotspotPin?.getMarker?.(hotspot) ?? hotspotToViewer360Marker(hotspot);
873
+ return /* @__PURE__ */ jsx9(
874
+ Viewer360MarkerPin,
917
875
  {
918
- type: "button",
919
- variant: "destructive",
920
- size: "icon-xs",
921
- className: cn(
922
- "pointer-events-auto absolute z-30 size-4 min-h-4 min-w-4 -translate-x-1/2 -translate-y-1/2 rounded-full border-2 border-background bg-destructive p-0 shadow-md hover:bg-destructive"
923
- ),
924
- style: { left: `${leftPercent}%`, top: `${topPercent}%` },
925
- "aria-label": `Hotspot ${hotspot.id}`,
926
- onClick: (event) => onHotspotClick?.(hotspot, event)
876
+ marker,
877
+ hotspot,
878
+ leftPercent,
879
+ topPercent,
880
+ onDelete: hotspotPin?.onDelete,
881
+ isDeletePending: hotspotPin?.deletingMarkerId === hotspot.id,
882
+ renderTag: hotspotPin?.renderTag,
883
+ classNames: hotspotPin?.classNames,
884
+ labels: hotspotPin?.labels,
885
+ onClick: onHotspotClick ? (event) => onHotspotClick(hotspot, event) : void 0
927
886
  }
928
887
  );
929
888
  }
930
889
 
931
890
  // src/components/ui/Label/index.tsx
932
- import { jsx as jsx11 } from "react/jsx-runtime";
891
+ import { jsx as jsx10 } from "react/jsx-runtime";
933
892
  function Label({ className, ...props }) {
934
- return /* @__PURE__ */ jsx11(
893
+ return /* @__PURE__ */ jsx10(
935
894
  "label",
936
895
  {
937
896
  "data-slot": "label",
@@ -946,13 +905,13 @@ function Label({ className, ...props }) {
946
905
 
947
906
  // src/components/ui/Spinner/index.tsx
948
907
  import { Loader2Icon } from "lucide-react";
949
- import { jsx as jsx12 } from "react/jsx-runtime";
908
+ import { jsx as jsx11 } from "react/jsx-runtime";
950
909
  function Spinner({ className, ...props }) {
951
- return /* @__PURE__ */ jsx12(Loader2Icon, { role: "status", "aria-label": "Loading", className: cn("size-4 animate-spin", className), ...props });
910
+ return /* @__PURE__ */ jsx11(Loader2Icon, { role: "status", "aria-label": "Loading", className: cn("size-4 animate-spin", className), ...props });
952
911
  }
953
912
 
954
913
  // src/feature/Viewer360LoadingOverlay.tsx
955
- import { jsx as jsx13, jsxs as jsxs2 } from "react/jsx-runtime";
914
+ import { jsx as jsx12, jsxs as jsxs2 } from "react/jsx-runtime";
956
915
  function Viewer360LoadingOverlay({ className, textClassName, label }) {
957
916
  return /* @__PURE__ */ jsxs2(
958
917
  Item,
@@ -961,8 +920,8 @@ function Viewer360LoadingOverlay({ className, textClassName, label }) {
961
920
  variant: "muted",
962
921
  className: cn("pointer-events-none w-auto justify-center border-transparent bg-muted/80", className),
963
922
  children: [
964
- /* @__PURE__ */ jsx13(Spinner, { className: "size-5 text-muted-foreground" }),
965
- /* @__PURE__ */ jsx13(Label, { className: cn("font-normal text-muted-foreground", textClassName), children: label })
923
+ /* @__PURE__ */ jsx12(Spinner, { className: "size-5 text-muted-foreground" }),
924
+ /* @__PURE__ */ jsx12(Label, { className: cn("font-normal text-muted-foreground", textClassName), children: label })
966
925
  ]
967
926
  }
968
927
  );
@@ -970,7 +929,7 @@ function Viewer360LoadingOverlay({ className, textClassName, label }) {
970
929
 
971
930
  // src/feature/Viewer360Toolbar.tsx
972
931
  import { Crosshair, Minus, Plus, RotateCcw, ZoomIn } from "lucide-react";
973
- import { Fragment, jsx as jsx14, jsxs as jsxs3 } from "react/jsx-runtime";
932
+ import { Fragment, jsx as jsx13, jsxs as jsxs3 } from "react/jsx-runtime";
974
933
  function Viewer360Toolbar({
975
934
  showDragHint,
976
935
  showHotspotModeControl,
@@ -988,30 +947,30 @@ function Viewer360Toolbar({
988
947
  onResetView
989
948
  }) {
990
949
  return /* @__PURE__ */ jsxs3(CardFooter, { className: cn(viewer360ClassNames.toolbar, "gap-2 border-t px-4 py-3 pt-3"), children: [
991
- showDragHint && /* @__PURE__ */ jsx14(Label, { className: cn(viewer360ClassNames.dragHint, "font-normal text-muted-foreground"), children: labels.dragHint }),
950
+ showDragHint && /* @__PURE__ */ jsx13(Label, { className: cn(viewer360ClassNames.dragHint, "font-normal text-muted-foreground"), children: labels.dragHint }),
992
951
  /* @__PURE__ */ jsxs3("div", { className: viewer360ClassNames.controls, children: [
993
952
  showHotspotModeControl && /* @__PURE__ */ jsxs3(Fragment, { children: [
994
953
  /* @__PURE__ */ jsxs3(Button, { variant: isHotspotMode ? "default" : "outline", size: "sm", onClick: () => onHotspotModeChange(!isHotspotMode), children: [
995
- /* @__PURE__ */ jsx14(Crosshair, { className: "me-1.5 size-4" }),
954
+ /* @__PURE__ */ jsx13(Crosshair, { className: "me-1.5 size-4" }),
996
955
  labels.addHotspot
997
956
  ] }),
998
- /* @__PURE__ */ jsx14(Separator, { orientation: "vertical", className: cn(viewer360ClassNames.divider, "h-6") })
957
+ /* @__PURE__ */ jsx13(Separator, { orientation: "vertical", className: cn(viewer360ClassNames.divider, "h-6") })
999
958
  ] }),
1000
959
  showZoomControls && /* @__PURE__ */ jsxs3(Fragment, { children: [
1001
- /* @__PURE__ */ jsx14(Button, { variant: "outline", size: "icon-sm", disabled: zoom <= minZoom, "aria-label": labels.zoomOut, onClick: onZoomOut, children: /* @__PURE__ */ jsx14(Minus, { className: "size-4" }) }),
960
+ /* @__PURE__ */ jsx13(Button, { variant: "outline", size: "icon-sm", disabled: zoom <= minZoom, "aria-label": labels.zoomOut, onClick: onZoomOut, children: /* @__PURE__ */ jsx13(Minus, { className: "size-4" }) }),
1002
961
  /* @__PURE__ */ jsxs3(Badge, { variant: "outline", className: cn(viewer360ClassNames.zoomDisplay, "h-8 gap-1 px-2 py-1"), children: [
1003
- /* @__PURE__ */ jsx14(ZoomIn, { className: "size-3 text-muted-foreground" }),
962
+ /* @__PURE__ */ jsx13(ZoomIn, { className: "size-3 text-muted-foreground" }),
1004
963
  labels.zoom(Math.round(zoom * 100))
1005
964
  ] }),
1006
- /* @__PURE__ */ jsx14(Button, { variant: "outline", size: "icon-sm", disabled: zoom >= maxZoom, "aria-label": labels.zoomIn, onClick: onZoomIn, children: /* @__PURE__ */ jsx14(Plus, { className: "size-4" }) })
965
+ /* @__PURE__ */ jsx13(Button, { variant: "outline", size: "icon-sm", disabled: zoom >= maxZoom, "aria-label": labels.zoomIn, onClick: onZoomIn, children: /* @__PURE__ */ jsx13(Plus, { className: "size-4" }) })
1007
966
  ] }),
1008
- showResetControl && /* @__PURE__ */ jsx14(Button, { variant: "outline", size: "icon-sm", disabled: isResetDisabled2, "aria-label": labels.resetView, onClick: onResetView, children: /* @__PURE__ */ jsx14(RotateCcw, { className: "size-4" }) })
967
+ showResetControl && /* @__PURE__ */ jsx13(Button, { variant: "outline", size: "icon-sm", disabled: isResetDisabled2, "aria-label": labels.resetView, onClick: onResetView, children: /* @__PURE__ */ jsx13(RotateCcw, { className: "size-4" }) })
1009
968
  ] })
1010
969
  ] });
1011
970
  }
1012
971
 
1013
972
  // src/feature/Viewer360.tsx
1014
- import { jsx as jsx15, jsxs as jsxs4 } from "react/jsx-runtime";
973
+ import { jsx as jsx14, jsxs as jsxs4 } from "react/jsx-runtime";
1015
974
  function Viewer360({
1016
975
  frames,
1017
976
  currentFrameIndex: controlledFrameIndex,
@@ -1046,8 +1005,8 @@ function Viewer360({
1046
1005
  const mergedLabels = useMemo2(() => mergeViewer360Labels(labels), [labels]);
1047
1006
  const mergedClassNames = useMemo2(() => mergeViewer360ClassNames(classNames), [classNames]);
1048
1007
  const themeStyle = useMemo2(() => buildViewer360ThemeStyle(theme), [theme]);
1049
- const [internalFrameIndex, setInternalFrameIndex] = useState3(defaultFrameIndex);
1050
- const [internalHotspotMode, setInternalHotspotMode] = useState3(defaultHotspotMode);
1008
+ const [internalFrameIndex, setInternalFrameIndex] = useState2(defaultFrameIndex);
1009
+ const [internalHotspotMode, setInternalHotspotMode] = useState2(defaultHotspotMode);
1051
1010
  const currentFrameIndex = controlledFrameIndex ?? internalFrameIndex;
1052
1011
  const hotspotMode = controlledHotspotMode ?? internalHotspotMode;
1053
1012
  function handleFrameChange(index) {
@@ -1137,11 +1096,11 @@ function Viewer360({
1137
1096
  onWheel: handleWheel,
1138
1097
  onClick: handleCanvasClick,
1139
1098
  children: [
1140
- /* @__PURE__ */ jsx15("canvas", { ref: canvasRef, className: mergedClassNames.canvas }),
1099
+ /* @__PURE__ */ jsx14("canvas", { ref: canvasRef, className: mergedClassNames.canvas }),
1141
1100
  /* @__PURE__ */ jsxs4("div", { className: mergedClassNames.overlay, children: [
1142
1101
  currentFrameHotspots.map((hotspot) => {
1143
1102
  const position = getHotspotScreenPosition(hotspot);
1144
- return /* @__PURE__ */ jsx15(
1103
+ return /* @__PURE__ */ jsx14(
1145
1104
  Viewer360HotspotOverlay,
1146
1105
  {
1147
1106
  hotspot,
@@ -1156,7 +1115,7 @@ function Viewer360({
1156
1115
  }),
1157
1116
  children
1158
1117
  ] }),
1159
- !imagesLoaded && (renderLoading ? renderLoading() : /* @__PURE__ */ jsx15(
1118
+ !imagesLoaded && (renderLoading ? renderLoading() : /* @__PURE__ */ jsx14(
1160
1119
  Viewer360LoadingOverlay,
1161
1120
  {
1162
1121
  className: mergedClassNames.loading,
@@ -1164,7 +1123,7 @@ function Viewer360({
1164
1123
  label: mergedLabels.loading
1165
1124
  }
1166
1125
  )),
1167
- showFrameIndicator && frames.length > 0 && (renderFrameIndicator ? renderFrameIndicator(overlayProps) : /* @__PURE__ */ jsx15(
1126
+ showFrameIndicator && frames.length > 0 && (renderFrameIndicator ? renderFrameIndicator(overlayProps) : /* @__PURE__ */ jsx14(
1168
1127
  Viewer360FrameIndicator,
1169
1128
  {
1170
1129
  className: mergedClassNames.frameIndicator,
@@ -1175,11 +1134,11 @@ function Viewer360({
1175
1134
  })
1176
1135
  }
1177
1136
  )),
1178
- isHotspotMode && onHotspotAdd && (renderHotspotModeBanner ? renderHotspotModeBanner({ labels: mergedLabels }) : /* @__PURE__ */ jsx15(Viewer360AddModeBanner, { className: mergedClassNames.hotspotModeBanner, label: mergedLabels.hotspotModeActive }))
1137
+ isHotspotMode && onHotspotAdd && (renderHotspotModeBanner ? renderHotspotModeBanner({ labels: mergedLabels }) : /* @__PURE__ */ jsx14(Viewer360AddModeBanner, { className: mergedClassNames.hotspotModeBanner, label: mergedLabels.hotspotModeActive }))
1179
1138
  ]
1180
1139
  }
1181
1140
  ),
1182
- renderToolbar ? renderToolbar(toolbarProps) : showDefaultToolbar ? /* @__PURE__ */ jsx15(Viewer360Toolbar, { ...toolbarProps }) : null
1141
+ renderToolbar ? renderToolbar(toolbarProps) : showDefaultToolbar ? /* @__PURE__ */ jsx14(Viewer360Toolbar, { ...toolbarProps }) : null
1183
1142
  ] });
1184
1143
  }
1185
1144