js-cloudimage-360-view 4.0.0 → 4.1.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,2 @@
1
+ "use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const fe=require("react/jsx-runtime"),n=require("react");let I=null;function C(d,t){const[p,f]=n.useState(!1),r=n.useRef(null),l=n.useRef(null),u=n.useId();return n.useEffect(()=>{if(typeof window>"u"||!d.current||t.autoInit===!1)return;let i=!0;const a=d.current;return(async()=>{try{if(I||(I=(await Promise.resolve().then(()=>require("./ci360-COjOXkWS.js"))).default),!a||!i)return;a.id||(a.id=`ci360-${u.replace(/:/g,"")}`);const s={...t,onReady:m=>{var c;i&&(f(!0),(c=t.onReady)==null||c.call(t,m))}};l.current=new I,r.current=l.current.init(a,s)}catch(s){console.error("Failed to initialize CI360 viewer:",s)}})(),()=>{if(i=!1,r.current){try{r.current.destroy()}catch{}r.current=null}l.current=null,f(!1)}},[t.folder,t.filenameX,t.filenameY,t.imageListX,t.imageListY,t.amountX,t.amountY,t.theme,u]),{viewer:r.current,isReady:p}}const pe=(d,t)=>{const{id:p,className:f,style:r,folder:l,apiVersion:u,filenameX:i,filenameY:a,imageListX:y,imageListY:s,indexZeroBase:m,amountX:c,amountY:h,draggable:g,swipeable:S,keys:V,keysReverse:L,autoplay:T,autoplayBehavior:O,playOnce:Z,speed:b,autoplayReverse:F,dragSpeed:X,dragReverse:Y,stopAtEdges:k,inertia:q,fullscreen:z,magnifier:A,pointerZoom:B,pinchZoom:D,bottomCircle:E,bottomCircleOffset:j,initialIconShown:v,hide360Logo:x,logoSrc:M,imageInfo:P,hints:w,theme:N,ciToken:H,ciFilters:$,ciTransformation:G,lazyload:J,hotspots:K,hotspotTimelineOnClick:Q,onReady:U,onLoad:W,onSpin:_,onAutoplayStart:ee,onAutoplayStop:te,onFullscreenOpen:oe,onFullscreenClose:ne,onZoomIn:re,onZoomOut:ae,onDragStart:se,onDragEnd:le,onError:ie,...me}=d,ue=n.useRef(null),ce=n.useMemo(()=>({folder:l,apiVersion:u,filenameX:i,filenameY:a,imageListX:y,imageListY:s,indexZeroBase:m,amountX:c,amountY:h,draggable:g,swipeable:S,keys:V,keysReverse:L,autoplay:T,autoplayBehavior:O,playOnce:Z,speed:b,autoplayReverse:F,dragSpeed:X,dragReverse:Y,stopAtEdges:k,inertia:q,fullscreen:z,magnifier:A,pointerZoom:B,pinchZoom:D,bottomCircle:E,bottomCircleOffset:j,initialIconShown:v,hide360Logo:x,logoSrc:M,imageInfo:P,hints:w,theme:N,ciToken:H,ciFilters:$,ciTransformation:G,lazyload:J,hotspots:K,hotspotTimelineOnClick:Q,onReady:U,onLoad:W,onSpin:_,onAutoplayStart:ee,onAutoplayStop:te,onFullscreenOpen:oe,onFullscreenClose:ne,onZoomIn:re,onZoomOut:ae,onDragStart:se,onDragEnd:le,onError:ie}),[l,u,i,a,y,s,m,c,h,g,S,V,L,T,O,Z,b,F,X,Y,k,q,z,A,B,D,E,j,v,x,M,P,w,N,H,$,G,J,K,Q,U,W,_,ee,te,oe,ne,re,ae,se,le,ie]),{viewer:e}=C(ue,ce);return n.useImperativeHandle(t,()=>({moveLeft:(o=1)=>e==null?void 0:e.moveLeft(!1,o),moveRight:(o=1)=>e==null?void 0:e.moveRight(!1,o),moveTop:(o=1)=>e==null?void 0:e.moveTop(!1,o),moveBottom:(o=1)=>e==null?void 0:e.moveBottom(!1,o),play:()=>e==null?void 0:e.play(),stop:()=>e==null?void 0:e.stopAutoplay(),zoomIn:()=>e==null?void 0:e.toggleZoom(),zoomOut:()=>e==null?void 0:e.removeZoom(),goToFrame:(o,de)=>e==null?void 0:e.animateToFrame(o,de),getViewer:()=>e}),[e]),fe.jsx("div",{ref:ue,id:p,className:f,style:r,...me})},R=n.forwardRef(pe);R.displayName="CI360Viewer";exports.CI360Viewer=R;exports.CI360ViewerDefault=R;exports.useCI360=C;exports.useCI360Default=C;
2
+ //# sourceMappingURL=index.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.cjs","sources":["../../src/react/useCI360.ts","../../src/react/CI360Viewer.tsx"],"sourcesContent":["import { useEffect, useRef, useState, useId, type RefObject } from 'react';\nimport type {\n CI360Config,\n CI360ViewerInstance,\n UseCI360Return,\n UseCI360Options,\n} from './types';\n\n// Import CI360 class dynamically to avoid SSR issues\nlet CI360Class: any = null;\n\n/**\n * Custom hook for integrating CI360 viewer with React\n *\n * @param containerRef - React ref to the container element\n * @param config - CI360 configuration options\n * @returns Object containing viewer instance and ready state\n *\n * @example\n * ```tsx\n * function MyComponent() {\n * const containerRef = useRef<HTMLDivElement>(null);\n * const { viewer, isReady } = useCI360(containerRef, {\n * folder: 'https://example.com/images/',\n * filenameX: 'image-{index}.jpg',\n * amountX: 36,\n * });\n *\n * return <div ref={containerRef} />;\n * }\n * ```\n */\nexport function useCI360(\n containerRef: RefObject<HTMLDivElement | null>,\n config: UseCI360Options\n): UseCI360Return {\n const [isReady, setIsReady] = useState(false);\n const viewerRef = useRef<CI360ViewerInstance | null>(null);\n const ci360Ref = useRef<any>(null);\n const uniqueId = useId();\n\n // Initialize viewer\n useEffect(() => {\n // SSR guard\n if (typeof window === 'undefined') return;\n if (!containerRef.current) return;\n if (config.autoInit === false) return;\n\n let isMounted = true;\n const container = containerRef.current;\n\n const initViewer = async () => {\n try {\n // Dynamically import CI360 to avoid SSR issues\n if (!CI360Class) {\n const module = await import('../ci360');\n CI360Class = module.default;\n }\n\n if (!container || !isMounted) return;\n\n // Set a unique ID on the container if not present\n if (!container.id) {\n container.id = `ci360-${uniqueId.replace(/:/g, '')}`;\n }\n\n // Wrap user callbacks to update React state\n const wrappedConfig: CI360Config = {\n ...config,\n onReady: (data) => {\n if (isMounted) {\n setIsReady(true);\n config.onReady?.(data);\n }\n },\n };\n\n // Create CI360 instance and initialize viewer\n ci360Ref.current = new CI360Class();\n viewerRef.current = ci360Ref.current.init(container, wrappedConfig);\n } catch (error) {\n console.error('Failed to initialize CI360 viewer:', error);\n }\n };\n\n initViewer();\n\n // Cleanup on unmount or when dependencies change\n return () => {\n isMounted = false;\n if (viewerRef.current) {\n try {\n viewerRef.current.destroy();\n } catch (e) {\n // Ignore errors during cleanup - element may already be detached\n }\n viewerRef.current = null;\n }\n ci360Ref.current = null;\n setIsReady(false);\n };\n }, [\n config.folder,\n config.filenameX,\n config.filenameY,\n config.imageListX,\n config.imageListY,\n config.amountX,\n config.amountY,\n config.theme,\n uniqueId,\n ]);\n\n return {\n viewer: viewerRef.current,\n isReady,\n };\n}\n\nexport default useCI360;\n","import {\n useRef,\n useImperativeHandle,\n forwardRef,\n useMemo,\n type ForwardRefRenderFunction,\n} from 'react';\nimport { useCI360 } from './useCI360';\nimport type {\n CI360ViewerProps,\n CI360ViewerRef,\n CI360Config,\n} from './types';\n\n/**\n * CI360Viewer React Component\n *\n * A declarative React wrapper for the CI360 360-degree image viewer.\n *\n * @example\n * ```tsx\n * import { CI360Viewer } from 'js-cloudimage-360-view/react';\n * import 'js-cloudimage-360-view/css';\n *\n * function ProductView() {\n * return (\n * <CI360Viewer\n * folder=\"https://example.com/images/\"\n * filenameX=\"product-{index}.jpg\"\n * amountX={36}\n * autoplay\n * fullscreen\n * />\n * );\n * }\n * ```\n *\n * @example With ref for imperative control\n * ```tsx\n * import { useRef } from 'react';\n * import { CI360Viewer, CI360ViewerRef } from 'js-cloudimage-360-view/react';\n *\n * function ProductView() {\n * const viewerRef = useRef<CI360ViewerRef>(null);\n *\n * return (\n * <>\n * <CI360Viewer\n * ref={viewerRef}\n * folder=\"https://example.com/images/\"\n * filenameX=\"{index}.jpg\"\n * amountX={36}\n * onSpin={(e) => console.log(`Frame: ${e.activeImageX}`)}\n * />\n * <button onClick={() => viewerRef.current?.play()}>Play</button>\n * <button onClick={() => viewerRef.current?.stop()}>Stop</button>\n * </>\n * );\n * }\n * ```\n */\nconst CI360ViewerComponent: ForwardRefRenderFunction<\n CI360ViewerRef,\n CI360ViewerProps\n> = (props, ref) => {\n const {\n // Container props\n id,\n className,\n style,\n\n // Image source\n folder,\n apiVersion,\n filenameX,\n filenameY,\n imageListX,\n imageListY,\n indexZeroBase,\n amountX,\n amountY,\n\n // Behavior\n draggable,\n swipeable,\n keys,\n keysReverse,\n autoplay,\n autoplayBehavior,\n playOnce,\n speed,\n autoplayReverse,\n dragSpeed,\n dragReverse,\n stopAtEdges,\n inertia,\n\n // UI Features\n fullscreen,\n magnifier,\n pointerZoom,\n pinchZoom,\n bottomCircle,\n bottomCircleOffset,\n initialIconShown,\n hide360Logo,\n logoSrc,\n imageInfo,\n hints,\n theme,\n\n // Cloudimage CDN\n ciToken,\n ciFilters,\n ciTransformation,\n\n // Loading\n lazyload,\n\n // Hotspots\n hotspots,\n hotspotTimelineOnClick,\n\n // Event callbacks\n onReady,\n onLoad,\n onSpin,\n onAutoplayStart,\n onAutoplayStop,\n onFullscreenOpen,\n onFullscreenClose,\n onZoomIn,\n onZoomOut,\n onDragStart,\n onDragEnd,\n onError,\n\n ...restProps\n } = props;\n\n const containerRef = useRef<HTMLDivElement>(null);\n\n // Memoize config to prevent unnecessary re-initializations\n const config = useMemo<CI360Config>(\n () => ({\n // Image source\n folder,\n apiVersion,\n filenameX,\n filenameY,\n imageListX,\n imageListY,\n indexZeroBase,\n amountX,\n amountY,\n\n // Behavior\n draggable,\n swipeable,\n keys,\n keysReverse,\n autoplay,\n autoplayBehavior,\n playOnce,\n speed,\n autoplayReverse,\n dragSpeed,\n dragReverse,\n stopAtEdges,\n inertia,\n\n // UI Features\n fullscreen,\n magnifier,\n pointerZoom,\n pinchZoom,\n bottomCircle,\n bottomCircleOffset,\n initialIconShown,\n hide360Logo,\n logoSrc,\n imageInfo,\n hints,\n theme,\n\n // Cloudimage CDN\n ciToken,\n ciFilters,\n ciTransformation,\n\n // Loading\n lazyload,\n\n // Hotspots\n hotspots,\n hotspotTimelineOnClick,\n\n // Event callbacks\n onReady,\n onLoad,\n onSpin,\n onAutoplayStart,\n onAutoplayStop,\n onFullscreenOpen,\n onFullscreenClose,\n onZoomIn,\n onZoomOut,\n onDragStart,\n onDragEnd,\n onError,\n }),\n [\n // Image source\n folder,\n apiVersion,\n filenameX,\n filenameY,\n imageListX,\n imageListY,\n indexZeroBase,\n amountX,\n amountY,\n\n // Behavior\n draggable,\n swipeable,\n keys,\n keysReverse,\n autoplay,\n autoplayBehavior,\n playOnce,\n speed,\n autoplayReverse,\n dragSpeed,\n dragReverse,\n stopAtEdges,\n inertia,\n\n // UI Features\n fullscreen,\n magnifier,\n pointerZoom,\n pinchZoom,\n bottomCircle,\n bottomCircleOffset,\n initialIconShown,\n hide360Logo,\n logoSrc,\n imageInfo,\n hints,\n theme,\n\n // Cloudimage CDN\n ciToken,\n ciFilters,\n ciTransformation,\n\n // Loading\n lazyload,\n\n // Hotspots\n hotspots,\n hotspotTimelineOnClick,\n\n // Event callbacks\n onReady,\n onLoad,\n onSpin,\n onAutoplayStart,\n onAutoplayStop,\n onFullscreenOpen,\n onFullscreenClose,\n onZoomIn,\n onZoomOut,\n onDragStart,\n onDragEnd,\n onError,\n ]\n );\n\n const { viewer } = useCI360(containerRef, config);\n\n // Expose imperative methods via ref\n useImperativeHandle(\n ref,\n () => ({\n moveLeft: (steps = 1) => viewer?.moveLeft(false, steps),\n moveRight: (steps = 1) => viewer?.moveRight(false, steps),\n moveTop: (steps = 1) => viewer?.moveTop(false, steps),\n moveBottom: (steps = 1) => viewer?.moveBottom(false, steps),\n play: () => viewer?.play(),\n stop: () => viewer?.stopAutoplay(),\n zoomIn: () => viewer?.toggleZoom(),\n zoomOut: () => viewer?.removeZoom(),\n goToFrame: (frame: number, hotspotId?: string) =>\n viewer?.animateToFrame(frame, hotspotId),\n getViewer: () => viewer,\n }),\n [viewer]\n );\n\n return (\n <div\n ref={containerRef}\n id={id}\n className={className}\n style={style}\n {...restProps}\n />\n );\n};\n\nexport const CI360Viewer = forwardRef(CI360ViewerComponent);\nCI360Viewer.displayName = 'CI360Viewer';\n\nexport default CI360Viewer;\n"],"names":["CI360Class","useCI360","containerRef","config","isReady","setIsReady","useState","viewerRef","useRef","ci360Ref","uniqueId","useId","useEffect","isMounted","container","wrappedConfig","data","_a","error","CI360ViewerComponent","props","ref","id","className","style","folder","apiVersion","filenameX","filenameY","imageListX","imageListY","indexZeroBase","amountX","amountY","draggable","swipeable","keys","keysReverse","autoplay","autoplayBehavior","playOnce","speed","autoplayReverse","dragSpeed","dragReverse","stopAtEdges","inertia","fullscreen","magnifier","pointerZoom","pinchZoom","bottomCircle","bottomCircleOffset","initialIconShown","hide360Logo","logoSrc","imageInfo","hints","theme","ciToken","ciFilters","ciTransformation","lazyload","hotspots","hotspotTimelineOnClick","onReady","onLoad","onSpin","onAutoplayStart","onAutoplayStop","onFullscreenOpen","onFullscreenClose","onZoomIn","onZoomOut","onDragStart","onDragEnd","onError","restProps","useMemo","viewer","useImperativeHandle","steps","frame","hotspotId","jsx","CI360Viewer","forwardRef"],"mappings":"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"}
@@ -0,0 +1,228 @@
1
+ import { CSSProperties, RefObject, ForwardRefExoticComponent, RefAttributes } from 'react';
2
+
3
+ /**
4
+ * Autoplay behavior options
5
+ */
6
+ export type AutoplayBehavior = 'spin-x' | 'spin-y' | 'spin-xy' | 'spin-yx';
7
+
8
+ /**
9
+ * Theme options for the viewer
10
+ */
11
+ export type Theme = 'light' | 'dark';
12
+
13
+ /**
14
+ * Hotspot orientation
15
+ */
16
+ export type HotspotOrientation = 'x' | 'y';
17
+
18
+ /**
19
+ * Hotspot position
20
+ */
21
+ export interface HotspotPosition {
22
+ x: number;
23
+ y: number;
24
+ }
25
+
26
+ /**
27
+ * Hotspot configuration
28
+ */
29
+ export interface Hotspot {
30
+ id: string;
31
+ label?: string;
32
+ orientation?: HotspotOrientation;
33
+ containerSize?: [number, number];
34
+ positions: Record<number, HotspotPosition>;
35
+ content?: string;
36
+ className?: string;
37
+ }
38
+
39
+ /**
40
+ * Hint configuration
41
+ */
42
+ export interface Hint {
43
+ text: string;
44
+ icon?: 'drag' | 'scroll' | 'pinch' | 'keys';
45
+ }
46
+
47
+ /**
48
+ * Spin event data
49
+ */
50
+ export interface SpinEventData {
51
+ viewerId: string;
52
+ direction: 'left' | 'right' | 'up' | 'down';
53
+ activeImageX: number;
54
+ activeImageY: number;
55
+ amountX: number;
56
+ amountY: number;
57
+ }
58
+
59
+ /**
60
+ * Load event data
61
+ */
62
+ export interface LoadEventData {
63
+ viewerId: string;
64
+ imagesX: number;
65
+ imagesY: number;
66
+ }
67
+
68
+ /**
69
+ * Zoom event data
70
+ */
71
+ export interface ZoomEventData {
72
+ viewerId: string;
73
+ zoomLevel: number;
74
+ }
75
+
76
+ /**
77
+ * Error event data
78
+ */
79
+ export interface ErrorEventData {
80
+ viewerId: string;
81
+ error: { message: string; url?: string };
82
+ errorCount: number;
83
+ totalImages: number;
84
+ errors: Array<{ message: string; url?: string }>;
85
+ }
86
+
87
+ /**
88
+ * Base event data (common to all events)
89
+ */
90
+ export interface BaseEventData {
91
+ viewerId: string;
92
+ }
93
+
94
+ /**
95
+ * CI360 Configuration options
96
+ */
97
+ export interface CI360Config {
98
+ folder?: string;
99
+ apiVersion?: string;
100
+ filenameX?: string;
101
+ filenameY?: string | null;
102
+ imageListX?: string | string[] | null;
103
+ imageListY?: string | string[] | null;
104
+ indexZeroBase?: number;
105
+ amountX?: number;
106
+ amountY?: number;
107
+ draggable?: boolean;
108
+ swipeable?: boolean;
109
+ keys?: boolean;
110
+ keysReverse?: boolean;
111
+ autoplay?: boolean;
112
+ autoplayBehavior?: AutoplayBehavior;
113
+ playOnce?: boolean;
114
+ speed?: number;
115
+ autoplayReverse?: boolean;
116
+ dragSpeed?: number;
117
+ dragReverse?: boolean;
118
+ stopAtEdges?: boolean;
119
+ inertia?: boolean;
120
+ fullscreen?: boolean;
121
+ magnifier?: number | null;
122
+ pointerZoom?: number;
123
+ pinchZoom?: boolean;
124
+ bottomCircle?: boolean;
125
+ bottomCircleOffset?: number;
126
+ initialIconShown?: boolean;
127
+ hide360Logo?: boolean;
128
+ logoSrc?: string;
129
+ imageInfo?: boolean;
130
+ hints?: boolean | Hint[];
131
+ theme?: Theme;
132
+ ciToken?: string | null;
133
+ ciFilters?: string | null;
134
+ ciTransformation?: string | null;
135
+ lazyload?: boolean;
136
+ hotspots?: Hotspot[] | null;
137
+ hotspotTimelineOnClick?: boolean;
138
+ onReady?: (data: BaseEventData) => void;
139
+ onLoad?: (data: LoadEventData) => void;
140
+ onSpin?: (data: SpinEventData) => void;
141
+ onAutoplayStart?: (data: BaseEventData) => void;
142
+ onAutoplayStop?: (data: BaseEventData) => void;
143
+ onFullscreenOpen?: (data: BaseEventData) => void;
144
+ onFullscreenClose?: (data: BaseEventData) => void;
145
+ onZoomIn?: (data: ZoomEventData) => void;
146
+ onZoomOut?: (data: BaseEventData) => void;
147
+ onDragStart?: (data: BaseEventData) => void;
148
+ onDragEnd?: (data: BaseEventData) => void;
149
+ onError?: (data: ErrorEventData) => void;
150
+ }
151
+
152
+ /**
153
+ * CI360 Viewer Instance methods available from the native viewer
154
+ */
155
+ export interface CI360ViewerInstance {
156
+ moveLeft: (stopAtEdges?: boolean, steps?: number) => void;
157
+ moveRight: (stopAtEdges?: boolean, steps?: number) => void;
158
+ moveTop: (stopAtEdges?: boolean, steps?: number) => void;
159
+ moveBottom: (stopAtEdges?: boolean, steps?: number) => void;
160
+ play: () => void;
161
+ stopAutoplay: () => void;
162
+ toggleZoom: (event?: MouseEvent) => void;
163
+ removeZoom: () => void;
164
+ animateToFrame: (frame: number, hotspotId?: string) => void;
165
+ destroy: () => void;
166
+ update: (config: Partial<CI360Config>) => void;
167
+ isReady: boolean;
168
+ activeImageX: number;
169
+ activeImageY: number;
170
+ amountX: number;
171
+ amountY: number;
172
+ isZoomed: boolean;
173
+ viewerConfig: CI360Config;
174
+ }
175
+
176
+ /**
177
+ * Props for the CI360Viewer React component
178
+ */
179
+ export interface CI360ViewerProps extends CI360Config {
180
+ id?: string;
181
+ className?: string;
182
+ style?: CSSProperties;
183
+ }
184
+
185
+ /**
186
+ * Ref methods exposed by CI360Viewer component
187
+ */
188
+ export interface CI360ViewerRef {
189
+ moveLeft: (steps?: number) => void;
190
+ moveRight: (steps?: number) => void;
191
+ moveTop: (steps?: number) => void;
192
+ moveBottom: (steps?: number) => void;
193
+ play: () => void;
194
+ stop: () => void;
195
+ zoomIn: () => void;
196
+ zoomOut: () => void;
197
+ goToFrame: (frame: number, hotspotId?: string) => void;
198
+ getViewer: () => CI360ViewerInstance | null;
199
+ }
200
+
201
+ /**
202
+ * Return type for useCI360 hook
203
+ */
204
+ export interface UseCI360Return {
205
+ viewer: CI360ViewerInstance | null;
206
+ isReady: boolean;
207
+ }
208
+
209
+ /**
210
+ * Options for useCI360 hook
211
+ */
212
+ export interface UseCI360Options extends CI360Config {
213
+ autoInit?: boolean;
214
+ }
215
+
216
+ /**
217
+ * CI360Viewer React Component
218
+ */
219
+ export declare const CI360Viewer: ForwardRefExoticComponent<CI360ViewerProps & RefAttributes<CI360ViewerRef>>;
220
+ export default CI360Viewer;
221
+
222
+ /**
223
+ * useCI360 Hook for advanced control
224
+ */
225
+ export declare function useCI360(
226
+ containerRef: RefObject<HTMLDivElement | null>,
227
+ config: UseCI360Options
228
+ ): UseCI360Return;
@@ -0,0 +1,273 @@
1
+ import { jsx as ce } from "react/jsx-runtime";
2
+ import { useState as de, useRef as I, useId as pe, useEffect as fe, forwardRef as ye, useMemo as Ie, useImperativeHandle as Ce } from "react";
3
+ let y = null;
4
+ function Re(c, t) {
5
+ const [p, d] = de(!1), n = I(null), s = I(null), i = pe();
6
+ return fe(() => {
7
+ if (typeof window > "u" || !c.current || t.autoInit === !1) return;
8
+ let l = !0;
9
+ const a = c.current;
10
+ return (async () => {
11
+ try {
12
+ if (y || (y = (await import("./ci360-CbNlMnNZ.mjs")).default), !a || !l) return;
13
+ a.id || (a.id = `ci360-${i.replace(/:/g, "")}`);
14
+ const r = {
15
+ ...t,
16
+ onReady: (u) => {
17
+ var m;
18
+ l && (d(!0), (m = t.onReady) == null || m.call(t, u));
19
+ }
20
+ };
21
+ s.current = new y(), n.current = s.current.init(a, r);
22
+ } catch (r) {
23
+ console.error("Failed to initialize CI360 viewer:", r);
24
+ }
25
+ })(), () => {
26
+ if (l = !1, n.current) {
27
+ try {
28
+ n.current.destroy();
29
+ } catch {
30
+ }
31
+ n.current = null;
32
+ }
33
+ s.current = null, d(!1);
34
+ };
35
+ }, [
36
+ t.folder,
37
+ t.filenameX,
38
+ t.filenameY,
39
+ t.imageListX,
40
+ t.imageListY,
41
+ t.amountX,
42
+ t.amountY,
43
+ t.theme,
44
+ i
45
+ ]), {
46
+ viewer: n.current,
47
+ isReady: p
48
+ };
49
+ }
50
+ const he = (c, t) => {
51
+ const {
52
+ // Container props
53
+ id: p,
54
+ className: d,
55
+ style: n,
56
+ // Image source
57
+ folder: s,
58
+ apiVersion: i,
59
+ filenameX: l,
60
+ filenameY: a,
61
+ imageListX: f,
62
+ imageListY: r,
63
+ indexZeroBase: u,
64
+ amountX: m,
65
+ amountY: C,
66
+ // Behavior
67
+ draggable: R,
68
+ swipeable: h,
69
+ keys: g,
70
+ keysReverse: L,
71
+ autoplay: S,
72
+ autoplayBehavior: V,
73
+ playOnce: T,
74
+ speed: Z,
75
+ autoplayReverse: F,
76
+ dragSpeed: O,
77
+ dragReverse: X,
78
+ stopAtEdges: Y,
79
+ inertia: b,
80
+ // UI Features
81
+ fullscreen: k,
82
+ magnifier: z,
83
+ pointerZoom: A,
84
+ pinchZoom: B,
85
+ bottomCircle: D,
86
+ bottomCircleOffset: E,
87
+ initialIconShown: x,
88
+ hide360Logo: M,
89
+ logoSrc: N,
90
+ imageInfo: j,
91
+ hints: q,
92
+ theme: v,
93
+ // Cloudimage CDN
94
+ ciToken: w,
95
+ ciFilters: H,
96
+ ciTransformation: P,
97
+ // Loading
98
+ lazyload: $,
99
+ // Hotspots
100
+ hotspots: G,
101
+ hotspotTimelineOnClick: J,
102
+ // Event callbacks
103
+ onReady: K,
104
+ onLoad: Q,
105
+ onSpin: U,
106
+ onAutoplayStart: W,
107
+ onAutoplayStop: _,
108
+ onFullscreenOpen: ee,
109
+ onFullscreenClose: te,
110
+ onZoomIn: oe,
111
+ onZoomOut: ne,
112
+ onDragStart: ae,
113
+ onDragEnd: re,
114
+ onError: se,
115
+ ...ie
116
+ } = c, le = I(null), ue = Ie(
117
+ () => ({
118
+ // Image source
119
+ folder: s,
120
+ apiVersion: i,
121
+ filenameX: l,
122
+ filenameY: a,
123
+ imageListX: f,
124
+ imageListY: r,
125
+ indexZeroBase: u,
126
+ amountX: m,
127
+ amountY: C,
128
+ // Behavior
129
+ draggable: R,
130
+ swipeable: h,
131
+ keys: g,
132
+ keysReverse: L,
133
+ autoplay: S,
134
+ autoplayBehavior: V,
135
+ playOnce: T,
136
+ speed: Z,
137
+ autoplayReverse: F,
138
+ dragSpeed: O,
139
+ dragReverse: X,
140
+ stopAtEdges: Y,
141
+ inertia: b,
142
+ // UI Features
143
+ fullscreen: k,
144
+ magnifier: z,
145
+ pointerZoom: A,
146
+ pinchZoom: B,
147
+ bottomCircle: D,
148
+ bottomCircleOffset: E,
149
+ initialIconShown: x,
150
+ hide360Logo: M,
151
+ logoSrc: N,
152
+ imageInfo: j,
153
+ hints: q,
154
+ theme: v,
155
+ // Cloudimage CDN
156
+ ciToken: w,
157
+ ciFilters: H,
158
+ ciTransformation: P,
159
+ // Loading
160
+ lazyload: $,
161
+ // Hotspots
162
+ hotspots: G,
163
+ hotspotTimelineOnClick: J,
164
+ // Event callbacks
165
+ onReady: K,
166
+ onLoad: Q,
167
+ onSpin: U,
168
+ onAutoplayStart: W,
169
+ onAutoplayStop: _,
170
+ onFullscreenOpen: ee,
171
+ onFullscreenClose: te,
172
+ onZoomIn: oe,
173
+ onZoomOut: ne,
174
+ onDragStart: ae,
175
+ onDragEnd: re,
176
+ onError: se
177
+ }),
178
+ [
179
+ // Image source
180
+ s,
181
+ i,
182
+ l,
183
+ a,
184
+ f,
185
+ r,
186
+ u,
187
+ m,
188
+ C,
189
+ // Behavior
190
+ R,
191
+ h,
192
+ g,
193
+ L,
194
+ S,
195
+ V,
196
+ T,
197
+ Z,
198
+ F,
199
+ O,
200
+ X,
201
+ Y,
202
+ b,
203
+ // UI Features
204
+ k,
205
+ z,
206
+ A,
207
+ B,
208
+ D,
209
+ E,
210
+ x,
211
+ M,
212
+ N,
213
+ j,
214
+ q,
215
+ v,
216
+ // Cloudimage CDN
217
+ w,
218
+ H,
219
+ P,
220
+ // Loading
221
+ $,
222
+ // Hotspots
223
+ G,
224
+ J,
225
+ // Event callbacks
226
+ K,
227
+ Q,
228
+ U,
229
+ W,
230
+ _,
231
+ ee,
232
+ te,
233
+ oe,
234
+ ne,
235
+ ae,
236
+ re,
237
+ se
238
+ ]
239
+ ), { viewer: e } = Re(le, ue);
240
+ return Ce(
241
+ t,
242
+ () => ({
243
+ moveLeft: (o = 1) => e == null ? void 0 : e.moveLeft(!1, o),
244
+ moveRight: (o = 1) => e == null ? void 0 : e.moveRight(!1, o),
245
+ moveTop: (o = 1) => e == null ? void 0 : e.moveTop(!1, o),
246
+ moveBottom: (o = 1) => e == null ? void 0 : e.moveBottom(!1, o),
247
+ play: () => e == null ? void 0 : e.play(),
248
+ stop: () => e == null ? void 0 : e.stopAutoplay(),
249
+ zoomIn: () => e == null ? void 0 : e.toggleZoom(),
250
+ zoomOut: () => e == null ? void 0 : e.removeZoom(),
251
+ goToFrame: (o, me) => e == null ? void 0 : e.animateToFrame(o, me),
252
+ getViewer: () => e
253
+ }),
254
+ [e]
255
+ ), /* @__PURE__ */ ce(
256
+ "div",
257
+ {
258
+ ref: le,
259
+ id: p,
260
+ className: d,
261
+ style: n,
262
+ ...ie
263
+ }
264
+ );
265
+ }, ge = ye(he);
266
+ ge.displayName = "CI360Viewer";
267
+ export {
268
+ ge as CI360Viewer,
269
+ ge as CI360ViewerDefault,
270
+ Re as useCI360,
271
+ Re as useCI360Default
272
+ };
273
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sources":["../../src/react/useCI360.ts","../../src/react/CI360Viewer.tsx"],"sourcesContent":["import { useEffect, useRef, useState, useId, type RefObject } from 'react';\nimport type {\n CI360Config,\n CI360ViewerInstance,\n UseCI360Return,\n UseCI360Options,\n} from './types';\n\n// Import CI360 class dynamically to avoid SSR issues\nlet CI360Class: any = null;\n\n/**\n * Custom hook for integrating CI360 viewer with React\n *\n * @param containerRef - React ref to the container element\n * @param config - CI360 configuration options\n * @returns Object containing viewer instance and ready state\n *\n * @example\n * ```tsx\n * function MyComponent() {\n * const containerRef = useRef<HTMLDivElement>(null);\n * const { viewer, isReady } = useCI360(containerRef, {\n * folder: 'https://example.com/images/',\n * filenameX: 'image-{index}.jpg',\n * amountX: 36,\n * });\n *\n * return <div ref={containerRef} />;\n * }\n * ```\n */\nexport function useCI360(\n containerRef: RefObject<HTMLDivElement | null>,\n config: UseCI360Options\n): UseCI360Return {\n const [isReady, setIsReady] = useState(false);\n const viewerRef = useRef<CI360ViewerInstance | null>(null);\n const ci360Ref = useRef<any>(null);\n const uniqueId = useId();\n\n // Initialize viewer\n useEffect(() => {\n // SSR guard\n if (typeof window === 'undefined') return;\n if (!containerRef.current) return;\n if (config.autoInit === false) return;\n\n let isMounted = true;\n const container = containerRef.current;\n\n const initViewer = async () => {\n try {\n // Dynamically import CI360 to avoid SSR issues\n if (!CI360Class) {\n const module = await import('../ci360');\n CI360Class = module.default;\n }\n\n if (!container || !isMounted) return;\n\n // Set a unique ID on the container if not present\n if (!container.id) {\n container.id = `ci360-${uniqueId.replace(/:/g, '')}`;\n }\n\n // Wrap user callbacks to update React state\n const wrappedConfig: CI360Config = {\n ...config,\n onReady: (data) => {\n if (isMounted) {\n setIsReady(true);\n config.onReady?.(data);\n }\n },\n };\n\n // Create CI360 instance and initialize viewer\n ci360Ref.current = new CI360Class();\n viewerRef.current = ci360Ref.current.init(container, wrappedConfig);\n } catch (error) {\n console.error('Failed to initialize CI360 viewer:', error);\n }\n };\n\n initViewer();\n\n // Cleanup on unmount or when dependencies change\n return () => {\n isMounted = false;\n if (viewerRef.current) {\n try {\n viewerRef.current.destroy();\n } catch (e) {\n // Ignore errors during cleanup - element may already be detached\n }\n viewerRef.current = null;\n }\n ci360Ref.current = null;\n setIsReady(false);\n };\n }, [\n config.folder,\n config.filenameX,\n config.filenameY,\n config.imageListX,\n config.imageListY,\n config.amountX,\n config.amountY,\n config.theme,\n uniqueId,\n ]);\n\n return {\n viewer: viewerRef.current,\n isReady,\n };\n}\n\nexport default useCI360;\n","import {\n useRef,\n useImperativeHandle,\n forwardRef,\n useMemo,\n type ForwardRefRenderFunction,\n} from 'react';\nimport { useCI360 } from './useCI360';\nimport type {\n CI360ViewerProps,\n CI360ViewerRef,\n CI360Config,\n} from './types';\n\n/**\n * CI360Viewer React Component\n *\n * A declarative React wrapper for the CI360 360-degree image viewer.\n *\n * @example\n * ```tsx\n * import { CI360Viewer } from 'js-cloudimage-360-view/react';\n * import 'js-cloudimage-360-view/css';\n *\n * function ProductView() {\n * return (\n * <CI360Viewer\n * folder=\"https://example.com/images/\"\n * filenameX=\"product-{index}.jpg\"\n * amountX={36}\n * autoplay\n * fullscreen\n * />\n * );\n * }\n * ```\n *\n * @example With ref for imperative control\n * ```tsx\n * import { useRef } from 'react';\n * import { CI360Viewer, CI360ViewerRef } from 'js-cloudimage-360-view/react';\n *\n * function ProductView() {\n * const viewerRef = useRef<CI360ViewerRef>(null);\n *\n * return (\n * <>\n * <CI360Viewer\n * ref={viewerRef}\n * folder=\"https://example.com/images/\"\n * filenameX=\"{index}.jpg\"\n * amountX={36}\n * onSpin={(e) => console.log(`Frame: ${e.activeImageX}`)}\n * />\n * <button onClick={() => viewerRef.current?.play()}>Play</button>\n * <button onClick={() => viewerRef.current?.stop()}>Stop</button>\n * </>\n * );\n * }\n * ```\n */\nconst CI360ViewerComponent: ForwardRefRenderFunction<\n CI360ViewerRef,\n CI360ViewerProps\n> = (props, ref) => {\n const {\n // Container props\n id,\n className,\n style,\n\n // Image source\n folder,\n apiVersion,\n filenameX,\n filenameY,\n imageListX,\n imageListY,\n indexZeroBase,\n amountX,\n amountY,\n\n // Behavior\n draggable,\n swipeable,\n keys,\n keysReverse,\n autoplay,\n autoplayBehavior,\n playOnce,\n speed,\n autoplayReverse,\n dragSpeed,\n dragReverse,\n stopAtEdges,\n inertia,\n\n // UI Features\n fullscreen,\n magnifier,\n pointerZoom,\n pinchZoom,\n bottomCircle,\n bottomCircleOffset,\n initialIconShown,\n hide360Logo,\n logoSrc,\n imageInfo,\n hints,\n theme,\n\n // Cloudimage CDN\n ciToken,\n ciFilters,\n ciTransformation,\n\n // Loading\n lazyload,\n\n // Hotspots\n hotspots,\n hotspotTimelineOnClick,\n\n // Event callbacks\n onReady,\n onLoad,\n onSpin,\n onAutoplayStart,\n onAutoplayStop,\n onFullscreenOpen,\n onFullscreenClose,\n onZoomIn,\n onZoomOut,\n onDragStart,\n onDragEnd,\n onError,\n\n ...restProps\n } = props;\n\n const containerRef = useRef<HTMLDivElement>(null);\n\n // Memoize config to prevent unnecessary re-initializations\n const config = useMemo<CI360Config>(\n () => ({\n // Image source\n folder,\n apiVersion,\n filenameX,\n filenameY,\n imageListX,\n imageListY,\n indexZeroBase,\n amountX,\n amountY,\n\n // Behavior\n draggable,\n swipeable,\n keys,\n keysReverse,\n autoplay,\n autoplayBehavior,\n playOnce,\n speed,\n autoplayReverse,\n dragSpeed,\n dragReverse,\n stopAtEdges,\n inertia,\n\n // UI Features\n fullscreen,\n magnifier,\n pointerZoom,\n pinchZoom,\n bottomCircle,\n bottomCircleOffset,\n initialIconShown,\n hide360Logo,\n logoSrc,\n imageInfo,\n hints,\n theme,\n\n // Cloudimage CDN\n ciToken,\n ciFilters,\n ciTransformation,\n\n // Loading\n lazyload,\n\n // Hotspots\n hotspots,\n hotspotTimelineOnClick,\n\n // Event callbacks\n onReady,\n onLoad,\n onSpin,\n onAutoplayStart,\n onAutoplayStop,\n onFullscreenOpen,\n onFullscreenClose,\n onZoomIn,\n onZoomOut,\n onDragStart,\n onDragEnd,\n onError,\n }),\n [\n // Image source\n folder,\n apiVersion,\n filenameX,\n filenameY,\n imageListX,\n imageListY,\n indexZeroBase,\n amountX,\n amountY,\n\n // Behavior\n draggable,\n swipeable,\n keys,\n keysReverse,\n autoplay,\n autoplayBehavior,\n playOnce,\n speed,\n autoplayReverse,\n dragSpeed,\n dragReverse,\n stopAtEdges,\n inertia,\n\n // UI Features\n fullscreen,\n magnifier,\n pointerZoom,\n pinchZoom,\n bottomCircle,\n bottomCircleOffset,\n initialIconShown,\n hide360Logo,\n logoSrc,\n imageInfo,\n hints,\n theme,\n\n // Cloudimage CDN\n ciToken,\n ciFilters,\n ciTransformation,\n\n // Loading\n lazyload,\n\n // Hotspots\n hotspots,\n hotspotTimelineOnClick,\n\n // Event callbacks\n onReady,\n onLoad,\n onSpin,\n onAutoplayStart,\n onAutoplayStop,\n onFullscreenOpen,\n onFullscreenClose,\n onZoomIn,\n onZoomOut,\n onDragStart,\n onDragEnd,\n onError,\n ]\n );\n\n const { viewer } = useCI360(containerRef, config);\n\n // Expose imperative methods via ref\n useImperativeHandle(\n ref,\n () => ({\n moveLeft: (steps = 1) => viewer?.moveLeft(false, steps),\n moveRight: (steps = 1) => viewer?.moveRight(false, steps),\n moveTop: (steps = 1) => viewer?.moveTop(false, steps),\n moveBottom: (steps = 1) => viewer?.moveBottom(false, steps),\n play: () => viewer?.play(),\n stop: () => viewer?.stopAutoplay(),\n zoomIn: () => viewer?.toggleZoom(),\n zoomOut: () => viewer?.removeZoom(),\n goToFrame: (frame: number, hotspotId?: string) =>\n viewer?.animateToFrame(frame, hotspotId),\n getViewer: () => viewer,\n }),\n [viewer]\n );\n\n return (\n <div\n ref={containerRef}\n id={id}\n className={className}\n style={style}\n {...restProps}\n />\n );\n};\n\nexport const CI360Viewer = forwardRef(CI360ViewerComponent);\nCI360Viewer.displayName = 'CI360Viewer';\n\nexport default CI360Viewer;\n"],"names":["CI360Class","useCI360","containerRef","config","isReady","setIsReady","useState","viewerRef","useRef","ci360Ref","uniqueId","useId","useEffect","isMounted","container","wrappedConfig","data","_a","error","CI360ViewerComponent","props","ref","id","className","style","folder","apiVersion","filenameX","filenameY","imageListX","imageListY","indexZeroBase","amountX","amountY","draggable","swipeable","keys","keysReverse","autoplay","autoplayBehavior","playOnce","speed","autoplayReverse","dragSpeed","dragReverse","stopAtEdges","inertia","fullscreen","magnifier","pointerZoom","pinchZoom","bottomCircle","bottomCircleOffset","initialIconShown","hide360Logo","logoSrc","imageInfo","hints","theme","ciToken","ciFilters","ciTransformation","lazyload","hotspots","hotspotTimelineOnClick","onReady","onLoad","onSpin","onAutoplayStart","onAutoplayStop","onFullscreenOpen","onFullscreenClose","onZoomIn","onZoomOut","onDragStart","onDragEnd","onError","restProps","useMemo","viewer","useImperativeHandle","steps","frame","hotspotId","jsx","CI360Viewer","forwardRef"],"mappings":"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"}
@@ -0,0 +1 @@
1
+ :root{--ci360-button-bg: rgba(255, 255, 255, .9);--ci360-button-bg-hover: rgba(255, 255, 255, 1);--ci360-button-size: 40px;--ci360-button-border-radius: 6px;--ci360-button-shadow: 0 2px 8px rgba(0, 0, 0, .15);--ci360-icon-color: #505050;--ci360-icon-color-hover: #333333;--ci360-icon-size: 20px;--ci360-initial-icon-bg: rgba(255, 255, 255, .9);--ci360-initial-icon-color: #505050;--ci360-initial-icon-size: 80px;--ci360-initial-icon-shadow: 0 4px 20px rgba(0, 0, 0, .15);--ci360-spinner-color: rgba(0, 0, 0, .2);--ci360-spinner-accent: #505050;--ci360-spinner-size: 30px;--ci360-fullscreen-bg: #ffffff;--ci360-magnifier-size: 250px;--ci360-magnifier-border: 2px solid rgba(0, 0, 0, .2);--ci360-magnifier-shadow: 0 8px 16px rgba(0, 0, 0, .3);--ci360-focus-color: #0066cc;--ci360-overlay-bg: rgba(255, 255, 255, 1);--ci360-hotspot-color: #00aaff;--ci360-hotspot-border: 2px solid #fff;--ci360-hotspot-size: 18px;--ci360-popper-bg: rgba(255, 255, 255, .95);--ci360-popper-color: #333;--ci360-popper-shadow: 0px 4px 16px rgba(0, 0, 0, .15);--ci360-popper-border-radius: 8px;--ci360-hints-bg: rgba(255, 255, 255, .9);--ci360-hints-color: #333333;--ci360-hints-font-size: 13px;--ci360-hints-border-radius: 20px;--ci360-hints-shadow: 0 2px 12px rgba(0, 0, 0, .1);--ci360-circle-color: #888888;--ci360-timeline-height: 6px;--ci360-timeline-track-bg: rgba(0, 0, 0, .12);--ci360-timeline-dot-size: 18px;--ci360-timeline-dot-color: var(--ci360-hotspot-color);--ci360-timeline-dot-border: 2px solid #fff;--ci360-timeline-indicator-size: 12px;--ci360-timeline-indicator-color: #333333;--ci360-timeline-tooltip-bg: rgba(255, 255, 255, .95);--ci360-timeline-tooltip-color: #333333}.ci360-theme-dark{--ci360-button-bg: rgba(40, 40, 45, .9);--ci360-button-bg-hover: rgba(55, 55, 60, .95);--ci360-button-shadow: 0 2px 8px rgba(0, 0, 0, .4);--ci360-icon-color: #e0e0e0;--ci360-icon-color-hover: #ffffff;--ci360-initial-icon-bg: rgba(40, 40, 45, .9);--ci360-initial-icon-color: #f5f5f5;--ci360-initial-icon-shadow: 0 4px 20px rgba(0, 0, 0, .4);--ci360-spinner-color: rgba(255, 255, 255, .2);--ci360-spinner-accent: #e0e0e0;--ci360-fullscreen-bg: #1a1a1f;--ci360-overlay-bg: rgba(26, 26, 31, 1);--ci360-magnifier-border: 2px solid rgba(255, 255, 255, .2);--ci360-magnifier-shadow: 0 8px 16px rgba(0, 0, 0, .5);--ci360-popper-bg: rgba(40, 40, 45, .95);--ci360-popper-color: #e0e0e0;--ci360-popper-shadow: 0px 4px 16px rgba(0, 0, 0, .4);--ci360-hints-bg: rgba(40, 40, 45, .9);--ci360-hints-color: #e0e0e0;--ci360-hints-shadow: 0 2px 12px rgba(0, 0, 0, .3);--ci360-circle-color: #b0b0b0;--ci360-timeline-track-bg: rgba(255, 255, 255, .15);--ci360-timeline-dot-border: 2px solid rgba(255, 255, 255, .9);--ci360-timeline-indicator-color: #e0e0e0;--ci360-timeline-tooltip-bg: rgba(40, 40, 45, .95);--ci360-timeline-tooltip-color: #e0e0e0}@keyframes rotation{0%{transform:rotate(0)}to{transform:rotate(360deg)}}.cloudimage-360{width:100%;position:relative}.cloudimage-360-inner-box{width:100%;height:100%;position:relative}.cloudimage-360-icons-container{position:absolute;display:flex;top:15px;right:10px;height:100%;flex-direction:column;align-items:center;z-index:100;gap:8px}.cloudimage-360-transition-overlay{position:absolute;top:0;left:0;width:100%;height:100%;background-color:var(--ci360-overlay-bg);opacity:0;transition:all 1s ease-out;z-index:10}.cloudimage-360-button{width:var(--ci360-button-size);height:var(--ci360-button-size);cursor:pointer;transition:transform .15s ease-out,background-color .15s ease-out,box-shadow .15s ease-out;display:flex;align-items:center;justify-content:center;border-radius:var(--ci360-button-border-radius);border:none;background-color:var(--ci360-button-bg);box-shadow:var(--ci360-button-shadow);padding:6px;backdrop-filter:blur(8px);-webkit-backdrop-filter:blur(8px)}.cloudimage-360-button:hover{transform:scale(1.05);background-color:var(--ci360-button-bg-hover)}.cloudimage-360-button:focus{outline:none}.cloudimage-360-button:focus-visible{outline:2px solid var(--ci360-focus-color);outline-offset:2px}.cloudimage-360-button svg{width:var(--ci360-icon-size);height:var(--ci360-icon-size);stroke:var(--ci360-icon-color);transition:stroke .15s ease-out}.cloudimage-360-button:hover svg{stroke:var(--ci360-icon-color-hover)}.cloudimage-initial-icon{position:absolute;top:50%;left:50%;transform:translate(-50%,-50%);width:var(--ci360-initial-icon-size);height:var(--ci360-initial-icon-size);border-radius:50%;background-color:var(--ci360-initial-icon-bg);box-shadow:var(--ci360-initial-icon-shadow);transition:all .3s ease;color:var(--ci360-initial-icon-color);display:flex;align-items:center;justify-content:center;z-index:2;-webkit-user-select:none;-moz-user-select:none;user-select:none;backdrop-filter:blur(8px);-webkit-backdrop-filter:blur(8px)}.cloudimage-initial-icon svg{width:70%;height:70%;color:var(--ci360-initial-icon-color);fill:var(--ci360-initial-icon-color);stroke:var(--ci360-initial-icon-color)}.cloudimage-initial-icon svg path{stroke:var(--ci360-initial-icon-color)}.cloudimage-initial-icon svg text{fill:var(--ci360-initial-icon-color)}.cloudimage-initial-icon:hover{transform:translate(-50%,-50%) scale(1.08)}.cloudimage-loading-spinner{width:var(--ci360-spinner-size);height:var(--ci360-spinner-size);transform:translate(-50%,-50%);border:3px solid var(--ci360-spinner-color);position:absolute;top:15px;left:15px;border-bottom-color:var(--ci360-spinner-accent);border-radius:50%;display:inline-block;box-sizing:border-box;opacity:0;animation:rotation .7s linear infinite}.cloudimage-360-view-360-circle{position:absolute;left:0;bottom:0;width:100%;height:auto;margin:auto;pointer-events:none;-webkit-user-select:none;-moz-user-select:none;user-select:none;transition:.2s all;z-index:2;color:var(--ci360-circle-color)}.cloudimage-360-view-360-circle svg{display:block;width:100%;height:auto}.cloudimage-360-fullscreen-modal{position:fixed;top:0;bottom:0;left:0;right:0;width:100%;height:100%;z-index:999;background-color:var(--ci360-fullscreen-bg)}.cloudimage-360-sr-only{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0,0,0,0);white-space:nowrap;border:0}.cloudimage-360-hints-overlay{position:absolute;bottom:0;left:0;width:100%;display:flex;align-items:flex-end;justify-content:center;padding-bottom:16px;z-index:50;pointer-events:none;opacity:0;transition:opacity .3s ease}.cloudimage-360-hints-overlay.visible{opacity:1}.cloudimage-360-hints-overlay.hiding{opacity:0}.cloudimage-360-hints-container{display:flex;flex-direction:row;gap:20px;padding:10px 20px;background:var(--ci360-hints-bg);border-radius:var(--ci360-hints-border-radius);box-shadow:var(--ci360-hints-shadow);backdrop-filter:blur(8px);-webkit-backdrop-filter:blur(8px)}.cloudimage-360-hint-item{display:flex;align-items:center;gap:8px;color:var(--ci360-hints-color);font-size:var(--ci360-hints-font-size);font-family:system-ui,-apple-system,sans-serif;white-space:nowrap}.cloudimage-360-hint-item svg{width:16px;height:16px;flex-shrink:0;stroke:var(--ci360-hints-color)}.cloudimage-360-hint-item span{opacity:.9}@media (max-width: 480px){.cloudimage-360-hints-container{flex-direction:column;gap:8px;padding:12px 16px;border-radius:12px}.cloudimage-360-hint-item{font-size:12px}.cloudimage-360-hint-item svg{width:14px;height:14px}.cloudimage-360-initial-icon{width:calc(var(--ci360-initial-icon-size) * .8);height:calc(var(--ci360-initial-icon-size) * .8)}}.cloudimage-360-img-magnifier-glass{background-color:#fff;background-image:radial-gradient(circle at center,#fffc,#f0f0f0e6);background-repeat:no-repeat;position:absolute;border:var(--ci360-magnifier-border);border-radius:50%;line-height:200px;text-align:center;z-index:1000;width:var(--ci360-magnifier-size);height:var(--ci360-magnifier-size);top:-75px;right:-85px;box-shadow:var(--ci360-magnifier-shadow);transition:box-shadow .2s ease;overflow:hidden;pointer-events:none}.cloudimage-360-hotspot-timeline{width:100%;max-width:500px;margin:16px auto 8px;padding:12px 20px;opacity:0;transition:opacity .3s ease;pointer-events:none}.cloudimage-360-hotspot-timeline.visible{opacity:1;pointer-events:auto}.cloudimage-360-hotspot-timeline-track{position:relative;width:100%;height:var(--ci360-timeline-height);background:var(--ci360-timeline-track-bg);border-radius:calc(var(--ci360-timeline-height) / 2)}.cloudimage-360-hotspot-timeline-indicator{position:absolute;top:50%;left:0;width:var(--ci360-timeline-indicator-size);height:var(--ci360-timeline-indicator-size);background:var(--ci360-timeline-indicator-color);border-radius:50%;transform:translate(-50%,-50%);transition:left .1s ease-out;pointer-events:none;box-shadow:0 2px 4px #0003}.cloudimage-360-hotspot-timeline-dot{position:absolute;top:50%;width:var(--ci360-timeline-dot-size);height:var(--ci360-timeline-dot-size);background:var(--ci360-timeline-dot-color);border:var(--ci360-timeline-dot-border);border-radius:50%;transform:translate(-50%,-50%);cursor:pointer;transition:transform .15s ease,box-shadow .15s ease;box-shadow:0 2px 6px #00000040;padding:0;font:inherit;outline:none}.cloudimage-360-hotspot-timeline-dot:hover{transform:translate(-50%,-50%) scale(1.25);box-shadow:0 3px 10px #0000004d}.cloudimage-360-hotspot-timeline-dot:focus-visible{outline:2px solid var(--ci360-focus-color);outline-offset:2px}.cloudimage-360-hotspot-timeline-tooltip{position:absolute;bottom:calc(100% + 10px);left:50%;transform:translate(-50%);background:var(--ci360-timeline-tooltip-bg);color:var(--ci360-timeline-tooltip-color);padding:6px 12px;border-radius:6px;font-size:13px;font-weight:500;white-space:nowrap;opacity:0;visibility:hidden;transition:opacity .2s ease,visibility .2s ease;pointer-events:none;box-shadow:0 2px 8px #00000026;z-index:10}.cloudimage-360-hotspot-timeline-tooltip:after{content:"";position:absolute;top:100%;left:50%;transform:translate(-50%);border:6px solid transparent;border-top-color:var(--ci360-timeline-tooltip-bg)}.cloudimage-360-hotspot-timeline-tooltip.visible{opacity:1;visibility:visible}@media (max-width: 480px){.cloudimage-360-hotspot-timeline{max-width:none;padding:10px 16px;margin:12px auto 6px}}.cloudimage-360-hotspot-container{position:absolute;top:0;right:0;bottom:0;left:0;width:100%;height:100%;z-index:20}.cloudimage-360-popper{opacity:1;transition:opacity .2s ease-in-out}.cloudimage-360-hotspot{display:inline-block;position:absolute;top:0;right:0;bottom:0;left:0;transform:translate(-50%,-50%);background:var(--ci360-hotspot-color);border:var(--ci360-hotspot-border);border-radius:50%;height:var(--ci360-hotspot-size);width:var(--ci360-hotspot-size);box-shadow:0 0 #0af9,0 2px 6px #0003;opacity:0;animation:pulse 2s infinite;transition:transform .2s ease,box-shadow .2s ease;padding:0;cursor:pointer;font:inherit;outline:none}.cloudimage-360-hotspot:focus-visible{outline:2px solid var(--ci360-focus-color, #0066cc);outline-offset:2px;animation:none}.cloudimage-360-hotspot.visible{opacity:1}@keyframes pulse{0%{transform:scale(1);box-shadow:0 0 #0af9,0 0 0 10px #0af6,0 0 0 20px #0af3}50%{transform:scale(1.1);box-shadow:0 0 0 10px #0af0,0 0 0 20px #00aaff1a,0 0 0 30px #0af0}to{transform:scale(1);box-shadow:0 0 #0af0,0 0 0 10px #0af0,0 0 0 20px #0af0}}.cloudimage-360-hotspot:hover{transform:scale(1.2);box-shadow:0 0 0 5px #00aaff80,0 4px 12px #0000004d}.cloudimage-360-popper{background-color:var(--ci360-popper-bg);color:var(--ci360-popper-color);padding:10px 15px;border-radius:var(--ci360-popper-border-radius);box-shadow:var(--ci360-popper-shadow);font-size:14px;max-width:220px;z-index:9999;text-align:center;transition:opacity .2s ease,translate .2s ease;opacity:0;translate:0 -10px}.cloudimage-360-popper[data-show]{opacity:1;translate:0}