Skip to content

Commit f1e777d

Browse files
authored
Merge pull request #38 from PaleHazy/main
imperative handle: less re-creating and better typing
2 parents c682859 + 0b7a95c commit f1e777d

File tree

1 file changed

+109
-23
lines changed

1 file changed

+109
-23
lines changed

src/index.tsx

Lines changed: 109 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,4 @@
1-
import React, {
2-
useState,
3-
useEffect,
4-
useImperativeHandle,
5-
forwardRef,
6-
createRef,
7-
Ref,
8-
useRef
9-
} from "react"
1+
import React, { useState, useEffect, useImperativeHandle, forwardRef, useRef, useCallback, useMemo } from "react"
102
import {
113
Viewer,
124
ViewerConfig,
@@ -75,8 +67,8 @@ const omittedProps = [
7567
"onDblclick",
7668
"onReady",
7769
]
78-
79-
export interface Props extends ViewerConfig {
70+
type MakeOptional<T, K extends keyof T> = Omit<T, K> & Partial<Pick<T, K>>;
71+
export interface Props extends MakeOptional<ViewerConfig, "container"> {
8072
src: string;
8173
navbar?: boolean | string | Array<string | NavbarCustomButton>;
8274
height: string;
@@ -140,8 +132,92 @@ function filterNavbar(navbar?: boolean | string | Array<string | NavbarCustomBut
140132
return navbar
141133
}
142134

143-
const ReactPhotoSphereViewer = forwardRef((options: Props, ref: unknown): React.ReactElement => {
144-
const sphereElementRef = createRef<HTMLDivElement>()
135+
function useDomElement(): [HTMLDivElement | undefined, (r: HTMLDivElement) => void] {
136+
const [element, setElement] = useState<HTMLDivElement>()
137+
const ref = useCallback(
138+
(r: HTMLDivElement) => {
139+
if (r && r !== element) {
140+
setElement(r)
141+
}
142+
},
143+
[element]
144+
)
145+
return [element, ref]
146+
}
147+
148+
export interface ViewerAPI {
149+
animate(options: AnimateOptions): void;
150+
destroy(): void;
151+
rotate(options: { x: number; y: number }): void;
152+
setOption(option: keyof ViewerConfig, value: unknown): void;
153+
setOptions(options: ViewerConfig): void;
154+
getCurrentNavbar(): (string | object)[] | void;
155+
zoom(value: number): void;
156+
zoomIn(): void;
157+
zoomOut(): void;
158+
resize(size: CssSize): void;
159+
enterFullscreen(): void;
160+
exitFullscreen(): void;
161+
toggleFullscreen(): void;
162+
isFullscreenEnabled(): boolean | void;
163+
startAutoRotate(): void;
164+
stopAutoRotate(): void;
165+
getPlugin(pluginName: string): unknown | void;
166+
getPosition(): unknown | void; // Specify the return type
167+
getZoomLevel(): unknown | void; // Specify the return type
168+
getSize(): unknown | void; // Specify the return type
169+
needsUpdate(): boolean | void;
170+
autoSize(): void;
171+
setPanorama(path: string, options?: object): void;
172+
setOverlay(path: string, opacity?: number): void;
173+
toggleAutorotate(): void;
174+
showError(message: string): void;
175+
hideError(): void;
176+
startKeyboardControl(): void;
177+
stopKeyboardControl(): void;
178+
}
179+
180+
const ReactPhotoSphereViewer = forwardRef<ViewerAPI, Props>((props, ref): React.ReactElement => {
181+
const [sphereElement, setRef] = useDomElement()
182+
const options = useMemo(
183+
() => props,
184+
[
185+
// recreate options when individual props change
186+
props.src,
187+
props.navbar,
188+
props.height,
189+
props.width,
190+
props.containerClass,
191+
props.littlePlanet,
192+
props.fishEye,
193+
props.lang,
194+
props.onPositionChange,
195+
props.onZoomChange,
196+
props.onClick,
197+
props.onDblclick,
198+
props.onReady,
199+
props.moveSpeed,
200+
props.zoomSpeed,
201+
props.moveInertia,
202+
props.mousewheel,
203+
props.mousemove,
204+
props.mousewheelCtrlKey,
205+
props.touchmoveTwoFingers,
206+
props.useXmpData,
207+
props.panoData,
208+
props.requestHeaders,
209+
props.canvasBackground,
210+
props.withCredentials,
211+
props.keyboard,
212+
props.plugins,
213+
props.sphereCorrection,
214+
props.minFov,
215+
props.maxFov,
216+
props.defaultZoomLvl,
217+
props.defaultYaw,
218+
props.defaultPitch,
219+
]
220+
)
145221
const spherePlayerInstance = useRef<Viewer | null>(null)
146222
let LITTLEPLANET_MAX_ZOOM = 130
147223
const [LITTLEPLANET_DEF_LAT] = useState(-90)
@@ -164,10 +240,10 @@ const ReactPhotoSphereViewer = forwardRef((options: Props, ref: unknown): React.
164240

165241
useEffect(() => {
166242
let littlePlanetEnabled = true
167-
if (sphereElementRef.current && !spherePlayerInstance.current) {
243+
if (sphereElement && !spherePlayerInstance.current) {
168244
const _c = new Viewer({
169245
...adaptOptions(options),
170-
container: sphereElementRef.current,
246+
container: sphereElement,
171247
panorama: options.src,
172248
size: {
173249
height: options.height,
@@ -318,10 +394,15 @@ const ReactPhotoSphereViewer = forwardRef((options: Props, ref: unknown): React.
318394

319395
spherePlayerInstance.current = _c
320396
}
321-
}, [sphereElementRef, options])
397+
}, [sphereElement, options])
322398

323-
// Methods
324-
useImperativeHandle(ref as Ref<unknown>, () => ({
399+
useEffect(() => {
400+
if (spherePlayerInstance.current) {
401+
spherePlayerInstance.current.setPanorama(options.src, options)
402+
}
403+
}, [options.src])
404+
405+
const _imperativeHandle = () => ({
325406
animate(options: AnimateOptions) {
326407
Emitter.emit("animate", options)
327408
},
@@ -408,12 +489,17 @@ const ReactPhotoSphereViewer = forwardRef((options: Props, ref: unknown): React.
408489
},
409490
stopKeyboardControl() {
410491
return spherePlayerInstance.current?.stopKeyboardControl()
411-
}
412-
}), [spherePlayerInstance.current, sphereElementRef, options, ref])
492+
},
493+
})
494+
// Methods
495+
useImperativeHandle(ref, _imperativeHandle, [
496+
spherePlayerInstance.current,
497+
sphereElement,
498+
options,
499+
ref,
500+
])
413501

414-
return (
415-
<div className={options.containerClass || "view-container"} ref={sphereElementRef} />
416-
)
502+
return <div className={options.containerClass || "view-container"} ref={setRef} />
417503
})
418504

419505
export {

0 commit comments

Comments
 (0)