import * as React from "react" ; import { GlobalState } from "./globalState" ; import { NodeMaterialBlock } from "babylonjs/Materials/Node/nodeMaterialBlock" ; import { NodeListComponent } from "./components/nodeList/nodeListComponent" ; import { PropertyTabComponent } from "./components/propertyTab/propertyTabComponent" ; import { Portal } from "./portal" ; import { LogComponent , LogEntry } from "./components/log/logComponent" ; import { DataStorage } from "babylonjs/Misc/dataStorage" ; import { NodeMaterialBlockConnectionPointTypes } from "babylonjs/Materials/Node/Enums/nodeMaterialBlockConnectionPointTypes" ; import { CustomBlock } from "babylonjs/Materials/Node/Blocks/customBlock" ; import { InputBlock } from "babylonjs/Materials/Node/Blocks/Input/inputBlock" ; import { Nullable } from "babylonjs/types" ; import { MessageDialogComponent } from "./sharedComponents/messageDialog" ; import { BlockTools } from "./blockTools" ; import { PreviewManager } from "./components/preview/previewManager" ; import { IEditorData } from "./nodeLocationInfo" ; import { PreviewMeshControlComponent } from "./components/preview/previewMeshControlComponent" ; import { PreviewAreaComponent } from "./components/preview/previewAreaComponent" ; import { SerializationTools } from "./serializationTools" ; import { GraphCanvasComponent } from "./diagram/graphCanvas" ; import { GraphNode } from "./diagram/graphNode" ; import { GraphFrame } from "./diagram/graphFrame" ; import * as ReactDOM from "react-dom" ; import { IInspectorOptions } from "babylonjs/Debug/debugLayer" ; import { Popup } from "./sharedComponents/popup" ; require ( "./main.scss" ) ; interface IGraphEditorProps { globalState : GlobalState ; } interface IGraphEditorState { showPreviewPopUp : boolean ; } interface IInternalPreviewAreaOptions extends IInspectorOptions { popup : boolean ; original : boolean ; explorerWidth ? : string ; inspectorWidth ? : string ; embedHostWidth ? : string ; } export class GraphEditor extends React . Component < IGraphEditorProps , IGraphEditorState > { private readonly NodeWidth = 100 ; private _graphCanvas : GraphCanvasComponent ; private _startX : number ; private _moveInProgress : boolean ; private _leftWidth = DataStorage . ReadNumber ( "LeftWidth" , 200 ) ; private _rightWidth = DataStorage . ReadNumber ( "RightWidth" , 300 ) ; private _blocks = new Array < NodeMaterialBlock > ( ) ; private _previewManager : PreviewManager ; private _copiedNodes : GraphNode [ ] = [ ] ; private _copiedFrame : Nullable < GraphFrame > = null ; private _mouseLocationX = 0 ; private _mouseLocationY = 0 ; private _onWidgetKeyUpPointer : any ; private _previewHost : Nullable < HTMLElement > ; private _popUpWindow : Window ; param nodeMaterialBlock public createNodeFromObject ( block , recursion = true ) { if ( this . _blocks . indexOf ( block ) !== - 1 ) { return this . _graphCanvas . nodes . filter ( ( n ) => n . block === block ) [ 0 ] ; } this . _blocks . push ( block ) ; if ( this . props . globalState . nodeMaterial ! . attachedBlocks . indexOf ( block ) === - 1 ) { this . props . globalState . nodeMaterial ! . attachedBlocks . push ( block ) ; } if ( block . isFinalMerger ) { this . props . globalState . nodeMaterial ! . addOutputNode ( block ) ; } if ( block . inputs . length ) { for ( var input of block . inputs ) { if ( input . isConnected && recursion ) { this . createNodeFromObject ( input . sourceBlock ! ) ; } } } const node = this . _graphCanvas . appendBlock ( block ) ; if ( block . inputs . length && recursion ) { for ( var input of block . inputs ) { if ( input . isConnected ) { this . _graphCanvas . connectPorts ( input . connectedPoint ! , input ) ; } } } return node ; } addValueNode ( type ) { let nodeType = BlockTools . GetConnectionNodeTypeFromString ( type ) ; let newInputBlock = new InputBlock ( type , undefined , nodeType ) ; return this . createNodeFromObject ( newInputBlock ) ; } componentDidMount ( ) { if ( this . props . globalState . hostDocument ) { this . _graphCanvas = this . refs [ "graphCanvas" ] as GraphCanvasComponent ; this . _previewManager = new PreviewManager ( this . props . globalState . hostDocument . getElementById ( "preview-canvas" ) as HTMLCanvasElement , this . props . globalState ) ; ( this . props . globalState as any ) . _previewManager = this . _previewManager ; } if ( navigator . userAgent . indexOf ( "Mobile" ) !== - 1 ) { ( ( this . props . globalState . hostDocument || document ) . querySelector ( ".blocker" ) as HTMLElement ) . style . visibility = "visible" ; } this . props . globalState . onPopupClosedObservable . addOnce ( ( ) => { this . componentWillUnmount ( ) ; } ) ; this . build ( ) ; } componentWillUnmount ( ) { if ( this . props . globalState . hostDocument ) { this . props . globalState . hostDocument ! . removeEventListener ( "keyup" , this . _onWidgetKeyUpPointer , false ) ; } if ( this . _previewManager ) { this . _previewManager . dispose ( ) ; this . _previewManager = null as any ; } } constructor ( props ) { super ( props ) ; this . state = { showPreviewPopUp : false , } ; this . props . globalState . onRebuildRequiredObservable . add ( ( autoConfigure ) => { if ( this . props . globalState . nodeMaterial ) { this . buildMaterial ( autoConfigure ) ; } } ) ; this . props . globalState . onResetRequiredObservable . add ( ( ) => { this . build ( ) ; if ( this . props . globalState . nodeMaterial ) { this . buildMaterial ( ) ; } } ) ; this . props . globalState . onImportFrameObservable . add ( ( source ) => { const frameData = source . editorData . frames [ 0 ] ; this . props . globalState . nodeMaterial . attachedBlocks . slice ( - frameData . blocks . length ) . forEach ( ( block ) => { this . createNodeFromObject ( block ) ; } ) ; this . _graphCanvas . addFrame ( frameData ) ; this . reOrganize ( this . props . globalState . nodeMaterial . editorData , true ) ; } ) ; this . props . globalState . onZoomToFitRequiredObservable . add ( ( ) => { this . zoomToFit ( ) ; } ) ; this . props . globalState . onReOrganizedRequiredObservable . add ( ( ) => { this . reOrganize ( ) ; } ) ; this . props . globalState . onGetNodeFromBlock = ( block ) => { return this . _graphCanvas . findNodeFromBlock ( block ) ; } ; this . props . globalState . hostDocument ! . addEventListener ( "keydown" , ( evt ) => { if ( ( evt . keyCode === 46 || evt . keyCode === 8 ) && ! this . props . globalState . blockKeyboardEvents ) { let selectedItems = this . _graphCanvas . selectedNodes ; for ( var selectedItem of selectedItems ) { selectedItem . dispose ( ) ; let targetBlock = selectedItem . block ; this . props . globalState . nodeMaterial ! . removeBlock ( targetBlock ) ; let blockIndex = this . _blocks . indexOf ( targetBlock ) ; if ( blockIndex > - 1 ) { this . _blocks . splice ( blockIndex , 1 ) ; } } if ( this . _graphCanvas . selectedLink ) { this . _graphCanvas . selectedLink . dispose ( ) ; } if ( this . _graphCanvas . selectedFrame ) { var frame = this . _graphCanvas . selectedFrame ; if ( frame . isCollapsed ) { while ( frame . nodes . length > 0 ) { let targetBlock = frame . nodes [ 0 ] . block ; this . props . globalState . nodeMaterial ! . removeBlock ( targetBlock ) ; let blockIndex = this . _blocks . indexOf ( targetBlock ) ; if ( blockIndex > - 1 ) { this . _blocks . splice ( blockIndex , 1 ) ; } frame . nodes [ 0 ] . dispose ( ) ; } frame . isCollapsed = false ; } else { frame . nodes . forEach ( ( node ) => { node . enclosingFrameId = - 1 ; } ) ; } this . _graphCanvas . selectedFrame . dispose ( ) ; } this . props . globalState . onSelectionChangedObservable . notifyObservers ( null ) ; this . props . globalState . onRebuildRequiredObservable . notifyObservers ( false ) ; return ; } if ( ! evt . ctrlKey || this . props . globalState . blockKeyboardEvents ) { return ; } if ( evt . key === "c" || evt . key === "C" ) { this . _copiedNodes = [ ] ; this . _copiedFrame = null ; if ( this . _graphCanvas . selectedFrame ) { this . _copiedFrame = this . _graphCanvas . selectedFrame ; this . _copiedFrame . serialize ( true ) ; return ; } let selectedItems = this . _graphCanvas . selectedNodes ; if ( ! selectedItems . length ) { return ; } let selectedItem = selectedItems [ 0 ] as GraphNode ; if ( ! selectedItem . block ) { return ; } this . _copiedNodes = selectedItems . slice ( 0 ) ; } else if ( evt . key === "v" || evt . key === "V" ) { const rootElement = this . props . globalState . hostDocument ! . querySelector ( ".diagram-container" ) as HTMLDivElement ; const zoomLevel = this . _graphCanvas . zoom ; let currentY = ( this . _mouseLocationY - rootElement . offsetTop - this . _graphCanvas . y - 20 ) / zoomLevel ; if ( this . _copiedFrame ) { let newFrame = new GraphFrame ( null , this . _graphCanvas , true ) ; this . _graphCanvas . frames . push ( newFrame ) ; newFrame . width = this . _copiedFrame . width ; newFrame . height = this . _copiedFrame . height ; newFrame . width / 2 ; newFrame . name = this . _copiedFrame . name ; newFrame . color = this . _copiedFrame . color ; let currentX = ( this . _mouseLocationX - rootElement . offsetLeft - this . _graphCanvas . x ) / zoomLevel ; newFrame . x = currentX - newFrame . width / 2 ; newFrame . y = currentY ; if ( this . _copiedFrame . nodes . length ) { currentX = newFrame . x + this . _copiedFrame . nodes [ 0 ] . x - this . _copiedFrame . x ; currentY = newFrame . y + this . _copiedFrame . nodes [ 0 ] . y - this . _copiedFrame . y ; this . _graphCanvas . _frameIsMoving = true ; let newNodes = this . pasteSelection ( this . _copiedFrame . nodes , currentX , currentY ) ; if ( newNodes ) { for ( var node of newNodes ) { newFrame . syncNode ( node ) ; } } this . _graphCanvas . _frameIsMoving = false ; } newFrame . adjustPorts ( ) ; if ( this . _copiedFrame . isCollapsed ) { newFrame . isCollapsed = true ; } this . props . globalState . onSelectionChangedObservable . notifyObservers ( newFrame ) ; return ; } if ( ! this . _copiedNodes . length ) { return ; } let currentX = ( this . _mouseLocationX - rootElement . offsetLeft - this . _graphCanvas . x - this . NodeWidth ) / zoomLevel ; this . pasteSelection ( this . _copiedNodes , currentX , currentY , true ) ; } } , false ) ; } reconnectNewNodes ( nodeIndex , newNodes , sourceNodes , done ) { if ( done [ nodeIndex ] ) { return ; } const currentNode = newNodes [ nodeIndex ] ; const block = currentNode . block ; const sourceNode = sourceNodes [ nodeIndex ] ; for ( var inputIndex = 0 ; inputIndex < sourceNode . block . inputs . length ; inputIndex ++ ) { let sourceInput = sourceNode . block . inputs [ inputIndex ] ; const currentInput = block . inputs [ inputIndex ] ; if ( ! sourceInput . isConnected ) { continue ; } const sourceBlock = sourceInput . connectedPoint ! . ownerBlock ; const activeNodes = sourceNodes . filter ( ( s ) => s . block === sourceBlock ) ; if ( activeNodes . length > 0 ) { const activeNode = activeNodes [ 0 ] ; let indexInList = sourceNodes . indexOf ( activeNode ) ; this . reconnectNewNodes ( indexInList , newNodes , sourceNodes , done ) ; const outputIndex = sourceBlock . outputs . indexOf ( sourceInput . connectedPoint ! ) ; const newOutput = newNodes [ indexInList ] . block . outputs [ outputIndex ] ; newOutput . connectTo ( currentInput ) ; } else { sourceInput . _connectedPoint ! . connectTo ( currentInput ) ; } this . _graphCanvas . connectPorts ( currentInput . connectedPoint ! , currentInput ) ; } currentNode . refresh ( ) ; done [ nodeIndex ] = true ; } pasteSelection ( copiedNodes , currentX , currentY , selectNew = false ) { let originalNode = null ; let newNodes = [ ] ; copiedNodes = copiedNodes . slice ( ) ; this . props . globalState . onSelectionChangedObservable . notifyObservers ( null ) ; for ( var node of copiedNodes ) { let block = node . block ; if ( ! block ) { continue ; } let clone = block . clone ( this . props . globalState . nodeMaterial . getScene ( ) ) ; if ( ! clone ) { return ; } let newNode = this . createNodeFromObject ( clone , false ) ; let x = 0 ; let y = 0 ; if ( originalNode ) { x = currentX + node . x - originalNode . x ; y = currentY + node . y - originalNode . y ; } else { originalNode = node ; x = currentX ; y = currentY ; } newNode . x = x ; newNode . y = y ; newNode . cleanAccumulation ( ) ; newNodes . push ( newNode ) ; if ( selectNew ) { this . props . globalState . onSelectionChangedObservable . notifyObservers ( newNode ) ; } } let done = new Array < boolean > ( newNodes . length ) ; for ( var index = 0 ; index < newNodes . length ; index ++ ) { this . reconnectNewNodes ( index , newNodes , copiedNodes , done ) ; } return newNodes ; } zoomToFit ( ) { this . _graphCanvas . zoomToFit ( ) ; } buildMaterial ( autoConfigure = true ) { if ( ! this . props . globalState . nodeMaterial ) { return ; } try { this . props . globalState . nodeMaterial . options . emitComments = true ; this . props . globalState . nodeMaterial . build ( true , undefined , autoConfigure ) ; this . props . globalState . onLogRequiredObservable . notifyObservers ( new LogEntry ( "Node material build successful" , false ) ) ; } catch ( err ) { this . props . globalState . onLogRequiredObservable . notifyObservers ( new LogEntry ( err , true ) ) ; } SerializationTools . UpdateLocations ( this . props . globalState . nodeMaterial , this . props . globalState ) ; this . props . globalState . onBuiltObservable . notifyObservers ( ) ; } build ( ) { let editorData = this . props . globalState . nodeMaterial . editorData ; this . _graphCanvas . _isLoading = true ; if ( editorData instanceof Array ) { editorData = { locations : editorData , } ; } this . _blocks = [ ] ; this . _graphCanvas . reset ( ) ; if ( this . props . globalState . nodeMaterial ) { this . loadGraph ( ) ; } this . reOrganize ( editorData ) ; } loadGraph ( ) { var material = this . props . globalState . nodeMaterial ; material . _vertexOutputNodes . forEach ( ( n ) => { this . createNodeFromObject ( n , true ) ; } ) ; material . _fragmentOutputNodes . forEach ( ( n ) => { this . createNodeFromObject ( n , true ) ; } ) ; material . attachedBlocks . forEach ( ( n ) => { this . createNodeFromObject ( n , true ) ; } ) ; material . attachedBlocks . forEach ( ( n ) => { if ( n . inputs . length ) { for ( var input of n . inputs ) { if ( input . isConnected ) { this . _graphCanvas . connectPorts ( input . connectedPoint ! , input ) ; } } } } ) ; } showWaitScreen ( ) { this . props . globalState . hostDocument . querySelector ( ".wait-screen" ) ?. classList . remove ( "hidden" ) ; } hideWaitScreen ( ) { this . props . globalState . hostDocument . querySelector ( ".wait-screen" ) ?. classList . add ( "hidden" ) ; } reOrganize ( editorData = null , isImportingAFrame = false ) { this . showWaitScreen ( ) ; this . _graphCanvas . _isLoading = true ; setTimeout ( ( ) => { if ( ! editorData || ! editorData . locations ) { this . _graphCanvas . distributeGraph ( ) ; } else { for ( var location of editorData . locations ) { for ( var node of this . _graphCanvas . nodes ) { if ( node . block && node . block . uniqueId === location . blockId ) { node . x = location . x ; node . y = location . y ; node . cleanAccumulation ( ) ; break ; } } } if ( ! isImportingAFrame ) { this . _graphCanvas . processEditorData ( editorData ) ; } } this . _graphCanvas . _isLoading = false ; for ( var node of this . _graphCanvas . nodes ) { node . _refreshLinks ( ) ; } this . hideWaitScreen ( ) ; } ) ; } onPointerDown ( evt ) { this . _startX = evt . clientX ; this . _moveInProgress = true ; evt . currentTarget . setPointerCapture ( evt . pointerId ) ; } onPointerUp ( evt ) { this . _moveInProgress = false ; evt . currentTarget . releasePointerCapture ( evt . pointerId ) ; } resizeColumns ( evt , forLeft = true ) { if ( ! this . _moveInProgress ) { return ; } const deltaX = evt . clientX - this . _startX ; const rootElement = evt . currentTarget . ownerDocument ! . getElementById ( "node-editor-graph-root" ) as HTMLDivElement ; if ( forLeft ) { this . _leftWidth += deltaX ; this . _leftWidth = Math . max ( 150 , Math . min ( 400 , this . _leftWidth ) ) ; DataStorage . WriteNumber ( "LeftWidth" , this . _leftWidth ) ; } else { this . _rightWidth -= deltaX ; this . _rightWidth = Math . max ( 250 , Math . min ( 500 , this . _rightWidth ) ) ; DataStorage . WriteNumber ( "RightWidth" , this . _rightWidth ) ; rootElement . ownerDocument ! . getElementById ( "preview" ) ! . style . height = this . _rightWidth + "px" ; } rootElement . style . gridTemplateColumns = this . buildColumnLayout ( ) ; this . _startX = evt . clientX ; } buildColumnLayout ( ) { return `${ this . _leftWidth }px 4px calc(100% - ${ this . _leftWidth + 8 + this . _rightWidth }px) 4px ${ this . _rightWidth }px` ; } emitNewBlock ( event ) { var data = event . dataTransfer . getData ( "babylonjs-material-node" ) as string ; let newNode ; let customBlockData ; if ( data . indexOf ( "CustomBlock" ) > - 1 ) { let storageData = localStorage . getItem ( data ) ; if ( ! storageData ) { this . props . globalState . onErrorMessageDialogRequiredObservable . notifyObservers ( `Error loading custom block` ) ; return ; } customBlockData = JSON . parse ( storageData ) ; if ( ! customBlockData ) { this . props . globalState . onErrorMessageDialogRequiredObservable . notifyObservers ( `Error parsing custom block` ) ; return ; } } else if ( data . indexOf ( "Custom" ) > - 1 ) { let storageData = localStorage . getItem ( data ) ; if ( storageData ) { let frameData = JSON . parse ( storageData ) ; let newX = ( event . clientX - event . currentTarget . offsetLeft - this . _graphCanvas . x - this . NodeWidth ) / this . _graphCanvas . zoom ; let newY = ( event . clientY - event . currentTarget . offsetTop - this . _graphCanvas . y - 20 ) / this . _graphCanvas . zoom ; let oldX = frameData . editorData . frames [ 0 ] . x ; let oldY = frameData . editorData . frames [ 0 ] . y ; frameData . editorData . frames [ 0 ] . x = newX ; frameData . editorData . frames [ 0 ] . y = newY ; for ( var location of frameData . editorData . locations ) { location . x += newX - oldX ; location . y += newY - oldY ; } SerializationTools . AddFrameToMaterial ( frameData , this . props . globalState , this . props . globalState . nodeMaterial ) ; this . _graphCanvas . frames [ this . _graphCanvas . frames . length - 1 ] . cleanAccumulation ( ) ; this . forceUpdate ( ) ; return ; } } if ( data . indexOf ( "Block" ) === - 1 ) { newNode = this . addValueNode ( data ) ; } else { let block ; if ( customBlockData ) { block = new CustomBlock ( "" ) ; ( block as CustomBlock ) . options = customBlockData ; } else { block = BlockTools . GetBlockFromString ( data , this . props . globalState . nodeMaterial . getScene ( ) , this . props . globalState . nodeMaterial ) ! ; } if ( block . isUnique ) { const className = block . getClassName ( ) ; for ( var other of this . _blocks ) { if ( other !== block && other . getClassName ( ) === className ) { this . props . globalState . onErrorMessageDialogRequiredObservable . notifyObservers ( `You can only have one ${ className } per graph` ) ; return ; } } } block . autoConfigure ( this . props . globalState . nodeMaterial ) ; newNode = this . createNodeFromObject ( block ) ; } let x = event . clientX - event . currentTarget . offsetLeft - this . _graphCanvas . x - this . NodeWidth ; let y = event . clientY - event . currentTarget . offsetTop - this . _graphCanvas . y - 20 ; newNode . x = x / this . _graphCanvas . zoom ; newNode . y = y / this . _graphCanvas . zoom ; newNode . cleanAccumulation ( ) ; this . props . globalState . onSelectionChangedObservable . notifyObservers ( null ) ; this . props . globalState . onSelectionChangedObservable . notifyObservers ( newNode ) ; let block = newNode . block ; x -= this . NodeWidth + 150 ; block . inputs . forEach ( ( connection ) => { if ( connection . connectedPoint ) { var existingNodes = this . _graphCanvas . nodes . filter ( ( n ) => { return n . block === ( connection as any ) . connectedPoint . ownerBlock ; } ) ; let connectedNode = existingNodes [ 0 ] ; if ( connectedNode . x === 0 && connectedNode . y === 0 ) { connectedNode . x = x / this . _graphCanvas . zoom ; connectedNode . y = y / this . _graphCanvas . zoom ; connectedNode . cleanAccumulation ( ) ; y += 80 ; } } } ) ; this . forceUpdate ( ) ; } handlePopUp = ( ) => { this . setState ( { showPreviewPopUp : true , } ) ; this . createPopUp ( ) ; this . props . globalState . hostWindow . addEventListener ( "beforeunload" , this . handleClosingPopUp ) ; } ; handleClosingPopUp = ( ) => { if ( this . _previewManager ) { this . _previewManager . dispose ( ) ; } this . _popUpWindow . close ( ) ; this . setState ( { showPreviewPopUp : false , } , ( ) => this . initiatePreviewArea ( ) ) ; } ; initiatePreviewArea = ( canvas = this . props . globalState . hostDocument . getElementById ( "preview-canvas" ) as HTMLCanvasElement ) => { this . _previewManager = new PreviewManager ( canvas , this . props . globalState ) ; } ; createPopUp = ( ) => { const userOptions = { original : true , popup : true , overlay : false , embedMode : false , enableClose : true , handleResize : true , enablePopup : true , } ; const options = { embedHostWidth : "100%" , ... userOptions , } ; const popUpWindow = this . createPopupWindow ( "PREVIEW AREA" , "_PreviewHostWindow" ) ; if ( popUpWindow ) { popUpWindow . addEventListener ( "beforeunload" , this . handleClosingPopUp ) ; const parentControl = popUpWindow . document . getElementById ( "node-editor-graph-root" ) ; this . createPreviewMeshControlHost ( options , parentControl ) ; this . createPreviewHost ( options , parentControl ) ; if ( parentControl ) { this . fixPopUpStyles ( parentControl . ownerDocument ! ) ; this . initiatePreviewArea ( parentControl . ownerDocument ! . getElementById ( "preview-canvas" ) as HTMLCanvasElement ) ; } } } ; createPopupWindow = ( title , windowVariableName , width = 500 , height = 500 ) : Window | null => { const windowCreationOptionsList = { width : width , height : height , top : ( this . props . globalState . hostWindow . innerHeight - width ) / 2 + window . screenY , left : ( this . props . globalState . hostWindow . innerWidth - height ) / 2 + window . screenX , } ; var windowCreationOptions = Object . keys ( windowCreationOptionsList ) . map ( ( key ) => key + "=" + ( windowCreationOptionsList as any ) [ key ] ) . join ( "," ) ; const popupWindow = this . props . globalState . hostWindow . open ( "" , title , windowCreationOptions ) ; if ( ! popupWindow ) { return null ; } const parentDocument = popupWindow . document ; parentDocument . title = title ; parentDocument . body . style . width = "100%" ; parentDocument . body . style . height = "100%" ; parentDocument . body . style . margin = "0" ; parentDocument . body . style . padding = "0" ; let parentControl = parentDocument . createElement ( "div" ) ; parentControl . style . width = "100%" ; parentControl . style . height = "100%" ; parentControl . style . margin = "0" ; parentControl . style . padding = "0" ; parentControl . style . display = "grid" ; parentControl . style . gridTemplateRows = "40px auto" ; parentControl . id = "node-editor-graph-root" ; parentControl . className = "right-panel popup" ; popupWindow . document . body . appendChild ( parentControl ) ; Popup . _CopyStyles ( this . props . globalState . hostWindow . document , parentDocument ) ; ( this as any ) [ windowVariableName ] = popupWindow ; this . _popUpWindow = popupWindow ; return popupWindow ; } ; createPreviewMeshControlHost = ( options , parentControl ) => { if ( parentControl ) { const host = parentControl . ownerDocument ! . createElement ( "div" ) ; host . id = "PreviewMeshControl-host" ; host . style . width = options . embedHostWidth || "auto" ; parentControl . appendChild ( host ) ; const PreviewMeshControlComponentHost = React . createElement ( PreviewMeshControlComponent , { globalState : this . props . globalState , togglePreviewAreaComponent : this . handlePopUp , } ) ; ReactDOM . render ( PreviewMeshControlComponentHost , host ) ; } } ; createPreviewHost = ( options , parentControl ) => { if ( parentControl ) { const host = parentControl . ownerDocument ! . createElement ( "div" ) ; host . id = "PreviewAreaComponent-host" ; host . style . width = options . embedHostWidth || "auto" ; host . style . height = "100%" ; host . style . overflow = "hidden" ; host . style . display = "grid" ; host . style . gridRow = "2" ; host . style . gridTemplateRows = "auto 40px" ; host . style . gridTemplateRows = "calc(100% - 40px) 40px" ; parentControl . appendChild ( host ) ; this . _previewHost = host ; if ( ! options . overlay ) { this . _previewHost . style . position = "relative" ; } } if ( this . _previewHost ) { const PreviewAreaComponentHost = React . createElement ( PreviewAreaComponent , { globalState : this . props . globalState , width : 200 , } ) ; ReactDOM . render ( PreviewAreaComponentHost , this . _previewHost ) ; } } ; fixPopUpStyles = ( document ) => { const previewContainer = document . getElementById ( "preview" ) ; if ( previewContainer ) { previewContainer . style . height = "auto" ; previewContainer . style . gridRow = "1" ; } const previewConfigBar = document . getElementById ( "preview-config-bar" ) ; if ( previewConfigBar ) { previewConfigBar . style . gridRow = "2" ; } const newWindowButton = document . getElementById ( "preview-new-window" ) ; if ( newWindowButton ) { newWindowButton . style . display = "none" ; } const previewMeshBar = document . getElementById ( "preview-mesh-bar" ) ; if ( previewMeshBar ) { previewMeshBar . style . gridTemplateColumns = "auto 1fr 40px 40px" ; } } ; render ( ) { return ( < Portal globalState = { this . props . globalState } > < div id = "node-editor-graph-root" style = { { gridTemplateColumns : this . buildColumnLayout ( ) , } } onMouseMove = { ( evt ) => { this . _mouseLocationX = evt . pageX ; this . _mouseLocationY = evt . pageY ; } } onMouseDown = { ( evt ) => { if ( ( evt . target as HTMLElement ) . nodeName === "INPUT" ) { return ; } this . props . globalState . blockKeyboardEvents = false ; } } > { } < NodeListComponent globalState = { this . props . globalState } / > < div id = "leftGrab" onPointerDown = { ( evt ) => this . onPointerDown ( evt ) } onPointerUp = { ( evt ) => this . onPointerUp ( evt ) } onPointerMove = { ( evt ) => this . resizeColumns ( evt ) } > < /div> { } < div className = "diagram-container" onDrop = { ( event ) => { this . emitNewBlock ( event ) ; } } onDragOver = { ( event ) => { event . preventDefault ( ) ; } } > < GraphCanvasComponent ref = { "graphCanvas" } globalState = { this . props . globalState } onEmitNewBlock = { ( block ) => { return this . createNodeFromObject ( block ) ; } } /> < /div> < div id = "rightGrab" onPointerDown = { ( evt ) => this . onPointerDown ( evt ) } onPointerUp = { ( evt ) => this . onPointerUp ( evt ) } onPointerMove = { ( evt ) => this . resizeColumns ( evt , false ) } > < /div> { } < div className = "right-panel" > < PropertyTabComponent globalState = { this . props . globalState } /> { ! this . state . showPreviewPopUp ? < PreviewMeshControlComponent globalState = { this . props . globalState } togglePreviewAreaComponent = { this . handlePopUp } / > : null } { ! this . state . showPreviewPopUp ? < PreviewAreaComponent globalState = { this . props . globalState } width = { this . _rightWidth } / > : null } < /div> < LogComponent globalState = { this . props . globalState } / > < /div> < MessageDialogComponent globalState = { this . props . globalState } / > < div className = "blocker" > Node Material Editor runs only on desktop < /div> < div className = "wait-screen hidden" > Processing ... please wait < /div> < /Portal>); } }