import React , { useRef , useState , useEffect } from 'react' ; import SidebarRight from './SidebarRight' ; import SidebarLeft from './SidebarLeft' ; import Canvas from './Canvas' ; import TopBar from './TopBar' ; import ThemeProvider from './ThemeProvider' ; import ThemeContext , { SRM_DEFAULT_PRIMARY } from './ThemeContext' ; import chroma , { Color } from 'chroma-js' ; interface AppProps { artboard : next . Artboard ; images : next . ImgAsset [ ] ; svgs : next . SvgAsset [ ] ; notes : next . Note [ ] ; theme : next . Theme ; artboardImage : HTMLImageElement ; composing : boolean ; } const App = ( props ) => { const app = useRef < HTMLDivElement > ( null ) ; const [ ready , setReady ] = useState < boolean > ( false ) ; const [ appTheme , setAppTheme ] = useState < next . Theme > ( props . theme ) ; const [ avgColor , setAvgColor ] = useState < Color > ( SRM_DEFAULT_PRIMARY ) ; const [ groupSelectionNest , setGroupSelectionNest ] = useState < next . Group [ ] | null > ( null ) ; const [ groupSelection , setGroupSelection ] = useState < next . Group | null > ( null ) ; const [ selection , setSelection ] = useState < next . AppLayer | null > ( null ) ; const [ hover , setHover ] = useState < next . AppLayer | null > ( null ) ; const [ zoom , setZoom ] = useState < number > ( 1 ) ; const [ baseZoom , setBaseZoom ] = useState < number > ( 1 ) ; const canvasSize = 20000 ; const sidebarSize = 320 ; const [ centerScroll , setCenterScroll ] = useState < { x : number ; y : number ; } > ( { x : 0 , y : 0 } ) ; const [ viewPortSize , setViewPortSize ] = useState < { width : number ; height : number ; } > ( { width : 0 , height : 0 } ) ; const [ notes , setNotes ] = useState < next . Note [ ] > ( props . notes ) ; const scaleArtboardForViewport = ( ) : number => { const artboardWidth = props . artboard . frame . width ; const artboardHeight = props . artboard . frame . height ; const maxWidth = Math . min ( viewPortSize . width , artboardWidth ) ; const maxHeight = Math . min ( viewPortSize . height , artboardHeight ) ; const maxRatio = maxWidth / maxHeight ; const artboardRatio = artboardWidth / artboardHeight ; if ( maxRatio > artboardRatio ) { return maxHeight / artboardHeight ; } else { return maxWidth / artboardWidth ; } } ; const getAvgColor = ( ) => { const colors = [ ] ; const canvas = document . createElement ( 'canvas' ) ; const context = canvas . getContext ( '2d' ) ; const width = props . artboard . frame . width * 0.10 ; const height = props . artboard . frame . height * 0.10 ; canvas . width = width ; canvas . height = height ; if ( context ) { context . drawImage ( props . artboardImage , 0 , 0 ) ; const pixels = context . getImageData ( 0 , 0 , width , height ) . data ; for ( let i = 0 , n = pixels . length ; i < n ; i += 4 ) { let r = pixels [ i ] ; let g = pixels [ i + 1 ] ; let b = pixels [ i + 2 ] ; colors . push ( `rgb(${ r }, ${ g }, ${ b })` ) ; } } return chroma . average ( colors , 'lch' ) ; } ; const getViewPortSize = ( ) : { width : number ; height : number ; } => { return { width : window . innerWidth - sidebarSize * 2 , height : window . innerHeight - 48 } ; } ; const handleResize = ( ) : void => { setViewPortSize ( getViewPortSize ( ) ) ; } ; const handleInitialRender = ( callback ) : void => { setAvgColor ( getAvgColor ( ) ) ; handleResize ( ) ; callback ( ) ; } ; const scrollToCenter = ( ) : void => { window . scrollTo ( centerScroll . x , centerScroll . y ) ; } ; const handleKeyPress = ( e ) : void => { if ( e . key === '-' && e . metaKey && e . altKey && e . ctrlKey ) { e . preventDefault ( ) ; setZoom ( zoom - 0.1 ) ; } else if ( e . key === '=' && e . metaKey && e . altKey && e . ctrlKey ) { e . preventDefault ( ) ; setZoom ( zoom + 0.1 ) ; } else if ( e . key === 'Enter' && e . metaKey && e . altKey && e . ctrlKey ) { e . preventDefault ( ) ; setZoom ( baseZoom ) ; scrollToCenter ( ) ; } } ; useEffect ( ( ) => { if ( groupSelection ) { if ( groupSelectionNest ) { const nestContainsGroup = groupSelectionNest . find ( ( group ) => { return group . id === groupSelection . id ; } ) ; if ( nestContainsGroup ) { let i = 0 ; let newNest = [ ] ; while ( groupSelectionNest [ i ] . id !== groupSelection . id ) { newNest . push ( groupSelectionNest [ i ] ) ; i ++ ; } setGroupSelectionNest ( [ ... newNest , groupSelection ] ) ; } else { setGroupSelectionNest ( [ ... groupSelectionNest , groupSelection ] ) ; } } else { setGroupSelectionNest ( [ groupSelection ] ) ; } } } , [ groupSelection ] ) ; useEffect ( ( ) => { app . current ?. focus ( ) ; window . addEventListener ( 'resize' , handleResize ) ; handleInitialRender ( ( ) => setReady ( true ) ) ; } , [ ] ) ; useEffect ( ( ) => { const artboardScale = scaleArtboardForViewport ( ) ; setZoom ( artboardScale ) ; setBaseZoom ( artboardScale ) ; const artboardHeight = props . artboard . frame . height * artboardScale ; const artboardWidth = props . artboard . frame . width * artboardScale ; const artboardHeightMid = artboardHeight / 2 ; const artboardWidthMid = artboardWidth / 2 ; const canvasCenter = canvasSize / 2 ; const leftOffset = canvasCenter - artboardWidthMid - sidebarSize ; const topOffset = canvasCenter - artboardHeightMid ; const rightRemainder = viewPortSize . width - artboardWidth ; const bottomRemainder = viewPortSize . height - artboardHeight ; window . scrollTo ( leftOffset - ( rightRemainder / 2 ) , topOffset - ( bottomRemainder / 2 ) ) ; setCenterScroll ( { x : leftOffset - ( rightRemainder / 2 ) , y : topOffset - ( bottomRemainder / 2 ) } ) ; } , [ viewPortSize ] ) ; return ( < ThemeProvider theme = { appTheme } avgColor = { avgColor } > < ThemeContext . Consumer > { ( theme ) => ( < div className = 'c-app' tabIndex = { - 1 } ref = { app } onKeyDown = { handleKeyPress } style = { { background : theme . background . z1 } } > < TopBar zoom = { zoom } setZoom = { setZoom } baseZoom = { baseZoom } notes = { notes } scrollToCenter = { scrollToCenter } appTheme = { appTheme } setAppTheme = { setAppTheme } composing = { props . composing } / > < SidebarLeft selection = { selection } setSelection = { setSelection } hover = { hover } setHover = { setHover } notes = { notes } groupSelection = { groupSelection } setGroupSelection = { setGroupSelection } groupSelectionNest = { groupSelectionNest } setGroupSelectionNest = { setGroupSelectionNest } artboard = { props . artboard } / > < SidebarRight ready = { ready } selection = { selection } images = { props . images } svgs = { props . svgs } notes = { notes } setNotes = { setNotes } composing = { props . composing } / > < Canvas { ... props } ready = { ready } zoom = { zoom } setZoom = { setZoom } selection = { selection } setSelection = { setSelection } groupSelection = { groupSelection } setGroupSelection = { setGroupSelection } groupSelectionNest = { groupSelectionNest } setGroupSelectionNest = { setGroupSelectionNest } hover = { hover } setHover = { setHover } / > < /div>)} < /ThemeContext.Consumer> < /ThemeProvider>); } ; export default App ;