@@ -10,8 +10,8 @@ import type {
1010import { OS } from 'monaco-editor/esm/vs/base/common/platform.js' ;
1111import Prism from 'prismjs' ;
1212import React , { useEffect , Suspense , MutableRefObject , useRef } from 'react' ;
13- import ReactDOM from 'react-dom' ;
14- import { Provider , connect , useStore } from 'react-redux' ;
13+ import { createPortal } from 'react-dom' ;
14+ import { connect } from 'react-redux' ;
1515import { createSelector } from 'reselect' ;
1616import store from 'store' ;
1717
@@ -127,7 +127,6 @@ interface EditorProperties {
127127 outputZoneTop : number ;
128128 outputZoneId : string ;
129129 descriptionNode ?: HTMLDivElement ;
130- outputNode ?: HTMLDivElement ;
131130 descriptionWidget ?: editor . IContentWidget ;
132131 outputWidget ?: editor . IOverlayWidget ;
133132}
@@ -243,7 +242,6 @@ const initialData: EditorProperties = {
243242} ;
244243
245244const Editor = ( props : EditorProps ) : JSX . Element => {
246- const reduxStore = useStore ( ) ;
247245 const { t } = useTranslation ( ) ;
248246 const { editorRef, initTests, resetAttempts, isMobileLayout } = props ;
249247 // These refs are used during initialisation of the editor as well as by
@@ -255,6 +253,8 @@ const Editor = (props: EditorProps): JSX.Element => {
255253 const monacoRef : MutableRefObject < typeof monacoEditor | null > =
256254 useRef < typeof monacoEditor > ( null ) ;
257255 const dataRef = useRef < EditorProperties > ( { ...initialData } ) ;
256+ const [ lowerJawContainer , setLowerJawContainer ] =
257+ React . useState < HTMLDivElement | null > ( null ) ;
258258
259259 const submitChallengeDebounceRef = useRef (
260260 debounce ( props . submitChallenge , 1000 , { leading : true , trailing : false } )
@@ -713,56 +713,23 @@ const Editor = (props: EditorProps): JSX.Element => {
713713
714714 const tryToSubmitChallenge = submitChallengeDebounceRef . current ;
715715
716- function createLowerJaw (
717- outputNode : HTMLDivElement ,
718- editor : editor . IStandaloneCodeEditor
719- ) {
720- const { output } = props ;
721- const isChallengeComplete = challengeIsComplete ( ) ;
722-
723- ReactDOM . render (
724- < Provider store = { reduxStore } >
725- < LowerJaw
726- openHelpModal = { props . openHelpModal }
727- openResetModal = { props . openResetModal }
728- tryToExecuteChallenge = { tryToExecuteChallenge }
729- hint = { output [ 1 ] }
730- testsLength = { props . tests . length }
731- attempts = { attemptsRef . current }
732- challengeIsCompleted = { isChallengeComplete }
733- tryToSubmitChallenge = { tryToSubmitChallenge }
734- isSignedIn = { props . isSignedIn }
735- updateContainer = { ( ) => updateOutputViewZone ( outputNode , editor ) }
736- />
737- </ Provider > ,
738- outputNode
739- ) ;
740- }
741-
742- const updateOutputZone = ( ) => {
743- const editor = dataRef . current . editor ;
744- if ( ! editor || ! dataRef . current . outputNode ) return ;
745-
746- const outputNode = dataRef . current . outputNode ;
747- createLowerJaw ( outputNode , editor ) ;
748- } ;
749-
750716 // TODO: there's a potential performance gain to be had by only updating when
751717 // the outputViewZone has actually changed.
752718 const updateOutputViewZone = (
753- outputNode : HTMLDivElement ,
754- editor : editor . IStandaloneCodeEditor
719+ lowerJawContainer : HTMLDivElement ,
720+ editor ? : editor . IStandaloneCodeEditor
755721 ) => {
722+ if ( ! editor ) return ;
756723 // make sure the overlayWidget has resized before using it to set the height
757- outputNode . style . width = `${ getEditorContentWidth ( editor ) } px` ;
724+ lowerJawContainer . style . width = `${ getEditorContentWidth ( editor ) } px` ;
758725 // We have to wait for the viewZone to finish rendering before adjusting the
759726 // position of the overlayWidget (i.e. trigger it via onComputedHeight). If
760727 // not the editor may report the wrong value for position of the lines.
761- editor ? .changeViewZones ( changeAccessor => {
728+ editor . changeViewZones ( changeAccessor => {
762729 changeAccessor . removeZone ( dataRef . current . outputZoneId ) ;
763730 const viewZone = {
764731 afterLineNumber : getLastLineOfEditableRegion ( ) ,
765- heightInPx : outputNode . offsetHeight ,
732+ heightInPx : lowerJawContainer . offsetHeight ,
766733 domNode : document . createElement ( 'div' ) ,
767734 onComputedHeight : ( ) =>
768735 dataRef . current . outputWidget &&
@@ -829,16 +796,16 @@ const Editor = (props: EditorProps): JSX.Element => {
829796 return editor . getLayoutInfo ( ) . contentWidth - getScrollbarWidth ( ) ;
830797 }
831798
832- function createOutputNode ( editor : editor . IStandaloneCodeEditor ) {
833- if ( dataRef . current . outputNode ) return dataRef . current . outputNode ;
834- const outputNode = document . createElement ( 'div' ) ;
835- outputNode . classList . add ( 'editor-lower-jaw' ) ;
836- outputNode . setAttribute ( 'id' , 'editor-lower-jaw' ) ;
837- outputNode . style . left = `${ editor . getLayoutInfo ( ) . contentLeft } px` ;
838- outputNode . style . width = `${ getEditorContentWidth ( editor ) } px` ;
839- outputNode . style . top = getOutputZoneTop ( ) ;
840- dataRef . current . outputNode = outputNode ;
841- return outputNode ;
799+ function createLowerJawContainer ( editor : editor . IStandaloneCodeEditor ) {
800+ if ( lowerJawContainer ) return lowerJawContainer ;
801+ const container = document . createElement ( 'div' ) ;
802+ container . classList . add ( 'editor-lower-jaw' ) ;
803+ container . setAttribute ( 'id' , 'editor-lower-jaw' ) ;
804+ container . style . left = `${ editor . getLayoutInfo ( ) . contentLeft } px` ;
805+ container . style . width = `${ getEditorContentWidth ( editor ) } px` ;
806+ container . style . top = getOutputZoneTop ( ) ;
807+ setLowerJawContainer ( container ) ;
808+ return container ;
842809 }
843810
844811 function createScrollGutterNode (
@@ -1099,7 +1066,7 @@ const Editor = (props: EditorProps): JSX.Element => {
10991066 function addWidgetsToRegions ( editor : editor . IStandaloneCodeEditor ) {
11001067 const descriptionNode = createDescription ( editor ) ;
11011068
1102- const outputNode = createOutputNode ( editor ) ;
1069+ const lowerJawNode = createLowerJawContainer ( editor ) ;
11031070
11041071 if ( ! dataRef . current . descriptionWidget ) {
11051072 dataRef . current . descriptionWidget = createWidget (
@@ -1125,11 +1092,10 @@ const Editor = (props: EditorProps): JSX.Element => {
11251092 dataRef . current . outputWidget = createWidget (
11261093 editor ,
11271094 'output.widget' ,
1128- outputNode ,
1095+ lowerJawNode ,
11291096 getOutputZoneTop
11301097 ) ;
11311098 editor . addOverlayWidget ( dataRef . current . outputWidget ) ;
1132- editor . changeViewZones ( updateOutputZone ) ;
11331099 }
11341100
11351101 editor . onDidScrollChange ( ( ) => {
@@ -1157,7 +1123,6 @@ const Editor = (props: EditorProps): JSX.Element => {
11571123 // ask monaco to update regardless.
11581124 redecorateEditableRegion ( ) ;
11591125 updateDescriptionZone ( ) ;
1160- updateOutputZone ( ) ;
11611126 } ) ;
11621127 }
11631128
@@ -1263,7 +1228,6 @@ const Editor = (props: EditorProps): JSX.Element => {
12631228
12641229 useEffect ( ( ) => {
12651230 const { model, insideEditDecId } = dataRef . current ;
1266- const lowerJawElement = dataRef . current . outputNode ;
12671231 const isChallengeComplete = challengeIsComplete ( ) ;
12681232 const range = model ?. getDecorationRange ( insideEditDecId ) ;
12691233 if ( range && isChallengeComplete ) {
@@ -1275,8 +1239,6 @@ const Editor = (props: EditorProps): JSX.Element => {
12751239 }
12761240 ) ;
12771241 }
1278- dataRef . current . outputNode = lowerJawElement ;
1279- updateOutputZone ( ) ;
12801242 // eslint-disable-next-line react-hooks/exhaustive-deps
12811243 } , [ props . tests ] ) ;
12821244
@@ -1290,7 +1252,6 @@ const Editor = (props: EditorProps): JSX.Element => {
12901252 }
12911253 if ( hasEditableRegion ( ) ) {
12921254 updateDescriptionZone ( ) ;
1293- updateOutputZone ( ) ;
12941255 }
12951256 // eslint-disable-next-line react-hooks/exhaustive-deps
12961257 } , [ props . dimensions ] ) ;
@@ -1315,6 +1276,7 @@ const Editor = (props: EditorProps): JSX.Element => {
13151276 : theme === Themes . Default
13161277 ? 'vs-custom'
13171278 : editorSystemTheme ;
1279+
13181280 return (
13191281 < Suspense fallback = { < Loader loaderDelay = { 600 } /> } >
13201282 < span className = 'notranslate' >
@@ -1326,6 +1288,24 @@ const Editor = (props: EditorProps): JSX.Element => {
13261288 theme = { editorTheme }
13271289 />
13281290 </ span >
1291+ { lowerJawContainer !== null &&
1292+ createPortal (
1293+ < LowerJaw
1294+ openHelpModal = { props . openHelpModal }
1295+ openResetModal = { props . openResetModal }
1296+ tryToExecuteChallenge = { tryToExecuteChallenge }
1297+ hint = { props . output [ 1 ] }
1298+ testsLength = { props . tests . length }
1299+ attempts = { attemptsRef . current }
1300+ challengeIsCompleted = { challengeIsComplete ( ) }
1301+ tryToSubmitChallenge = { tryToSubmitChallenge }
1302+ isSignedIn = { props . isSignedIn }
1303+ updateContainer = { ( ) =>
1304+ updateOutputViewZone ( lowerJawContainer , dataRef . current . editor )
1305+ }
1306+ /> ,
1307+ lowerJawContainer
1308+ ) }
13291309 </ Suspense >
13301310 ) ;
13311311} ;
0 commit comments