diff --git a/packages/react/xr/src/dom-overlay.tsx b/packages/react/xr/src/dom-overlay.tsx index 39a0975d..301aa93e 100644 --- a/packages/react/xr/src/dom-overlay.tsx +++ b/packages/react/xr/src/dom-overlay.tsx @@ -10,6 +10,9 @@ export const XRDomOverlay = forwardRef const domOverlayRoot = useXR((xr) => xr.domOverlayRoot) const { In, Out } = useMemo(tunnel, []) useEffect(() => { + if (domOverlayRoot == null) { + return + } const root = createRoot(domOverlayRoot) root.render() return () => root.unmount() diff --git a/packages/xr/src/init.ts b/packages/xr/src/init.ts index ca641937..adbafddb 100644 --- a/packages/xr/src/init.ts +++ b/packages/xr/src/init.ts @@ -51,7 +51,7 @@ export type XRSessionInitOptions = { export function buildXRSessionInit( mode: XRSessionMode, - domOverlayRoot: Element, + domOverlayRoot: Element | undefined, { anchors = true, handTracking = true, @@ -90,7 +90,10 @@ export function buildXRSessionInit( const init: XRSessionInit = { requiredFeatures, optionalFeatures, - domOverlay: { root: domOverlayRoot }, + } + + if (domOverlayRoot != null) { + init.domOverlay = { root: domOverlayRoot } } //TODO: replace with call to isSupportedFeature (unbounded, ...) diff --git a/packages/xr/src/store.ts b/packages/xr/src/store.ts index b8e74036..43a80c70 100644 --- a/packages/xr/src/store.ts +++ b/packages/xr/src/store.ts @@ -37,7 +37,7 @@ export type XRState = Readonly< /** * the HTML element for doing dom overlays in handheld AR experiences */ - domOverlayRoot: Element + domOverlayRoot?: Element /** * the session visibility state * e.g. `"visible-blurred"` typically occurs when the user sees an OS overlay @@ -309,7 +309,12 @@ export function createXRStore(options?: XRSt window.addEventListener('keydown', keydownListener) cleanupEmulate = () => window.removeEventListener('keydown', keydownListener) } - const domOverlayRoot = options?.domOverlay instanceof HTMLElement ? options.domOverlay : document.createElement('div') + const domOverlayRoot = + typeof HTMLElement === 'undefined' + ? undefined + : options?.domOverlay instanceof HTMLElement + ? options.domOverlay + : document.createElement('div') const store = createStore>(() => ({ ...baseInitialState, controller: options?.controller, @@ -321,19 +326,21 @@ export function createXRStore(options?: XRSt })) let cleanupDomOverlayRoot: (() => void) | undefined - if (domOverlayRoot.parentNode == null) { - const setupDisplay = (state: XRState) => { - domOverlayRoot.style.display = state.session != null ? 'block' : 'none' - } - const unsubscribe = store.subscribe(setupDisplay) - setupDisplay(store.getState()) - document.body.appendChild(domOverlayRoot) - cleanupDomOverlayRoot = () => { - domOverlayRoot.remove() - unsubscribe() + if (domOverlayRoot != null) { + if (domOverlayRoot.parentNode == null) { + const setupDisplay = (state: XRState) => { + domOverlayRoot.style.display = state.session != null ? 'block' : 'none' + } + const unsubscribe = store.subscribe(setupDisplay) + setupDisplay(store.getState()) + document.body.appendChild(domOverlayRoot) + cleanupDomOverlayRoot = () => { + domOverlayRoot.remove() + unsubscribe() + } } + document.body.append(domOverlayRoot) } - document.body.append(domOverlayRoot) const syncXRInputSourceStates = createSyncXRInputSourceStates( (state) => store.setState({ inputSourceStates: [...store.getState().inputSourceStates, state] }), @@ -548,7 +555,7 @@ async function setFrameRate(session: XRSession, frameRate: FrameRateOption): Pro } async function enterXR( - domOverlayRoot: Element, + domOverlayRoot: Element | undefined, mode: XRSessionMode, options: XRStoreOptions | undefined, xrManager: WebXRManager | undefined,