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.
- package/README.md +972 -276
- package/dist/js-cloudimage-360-view.min.js +29 -2105
- package/dist/react/assets/canvas.worker-Cg0fkpD1.js.map +1 -0
- package/dist/react/ci360-COjOXkWS.js +35 -0
- package/dist/react/ci360-COjOXkWS.js.map +1 -0
- package/dist/react/ci360-CbNlMnNZ.mjs +2700 -0
- package/dist/react/ci360-CbNlMnNZ.mjs.map +1 -0
- package/dist/react/index.cjs +2 -0
- package/dist/react/index.cjs.map +1 -0
- package/dist/react/index.d.ts +228 -0
- package/dist/react/index.js +273 -0
- package/dist/react/index.js.map +1 -0
- package/dist/react/style.css +1 -0
- package/package.json +49 -10
- package/src/react/types.d.ts +228 -0
- package/src/types/ci360.d.ts +66 -0
- package/.prettierrc +0 -9
- package/dist/js-cloudimage-360-view.min.css +0 -1
|
@@ -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}
|