import * as React from 'react' ; import useMeasure , { Options as ResizeOptions } from 'react-use-measure' ; import mergeRefs from 'react-merge-refs' ; import { useIsomorphicLayoutEffect } from '../shared/hooks' ; import { SetBlock , ErrorBoundary , Block } from '../shared/components' ; import { createInternals } from '../shared/utils' ; import { filterKeys } from '../utils' ; import { events } from './events' ; import { RESERVED_PROPS } from '../constants' ; import { RenderProps , DPR , RootState } from '../types' ; export interface Props extends Omit < RenderProps , 'size' > , React . HTMLAttributes < HTMLDivElement > { children : React . ReactNode ; fallback ? : React . ReactNode ; resize ? : ResizeOptions ; } /**
 * A list of custom Canvas props.
 */ export const CANVAS_PROPS = [ 'renderer' , 'dpr' , 'camera' , 'orthographic' , 'frameloop' , 'events' , 'onCreated' ] as const ; /**
 * Interpolates DPR from [min, max] based on device capabilities.
 */ const calculateDpr = ( dpr ) => Array . isArray ( dpr ) ? Math . min ( Math . max ( dpr [ 0 ] , window . devicePixelRatio ) , dpr [ 1 ] ) : dpr ; /**
 * A resizeable canvas whose children are declarative OGL elements.
 */ export const Canvas = React . forwardRef < HTMLCanvasElement , Props > ( ( { resize , children , style , fallback , ... rest } , forwardedRef ) => { const internalProps = filterKeys ( rest , false , ... CANVAS_PROPS ) ; const divProps = filterKeys ( rest , true , ... CANVAS_PROPS , ... RESERVED_PROPS ) ; const [ div , { width , height } ] = useMeasure ( { scroll : true , debounce : { scroll : 50 , resize : 0 } , ... resize , } ) ; const canvas = React . useRef < HTMLCanvasElement > ( ) ; const internalState = React . useRef < RootState > ( ) ; const [ block , setBlock ] = React . useState < SetBlock > ( false ) ; const [ error , setError ] = React . useState ( false ) ; if ( block ) throw block ; if ( error ) throw error ; useIsomorphicLayoutEffect ( ( ) => { if ( ! internalState . current ) { internalState . current = createInternals ( canvas . current , { events , ... internalProps , } ) ; } const state = internalState . current ; if ( width > 0 && height > 0 ) { state . renderer . dpr = calculateDpr ( internalProps . dpr || [ 1 , 2 ] ) ; state . renderer . setSize ( width , height ) ; const projection = internalProps . orthographic ? 'orthographic' : 'perspective' ; state . camera [ projection ] ( { aspect : width / height } ) ; state . root . render ( < ErrorBoundary set = { setError } > < React . Suspense fallback = { < Block set = { setBlock } / > } > { children } < /React.Suspense> < /ErrorBoundary>); } } , [ internalProps , width , height , children ] ) ; React . useEffect ( ( ) => { const state = internalState . current ; return ( ) => state . root . unmount ( ) ; } , [ ] ) ; return ( < div { ... divProps } ref = { div } style = { { position : 'relative' , width : '100%' , height : '100%' , overflow : 'hidden' , ... style , } } > < canvas ref = { mergeRefs ( [ canvas , forwardedRef ] ) } style = { { display : 'block' } } > { fallback } < /canvas> < /div>); } ) ;