11import { Show , For , createSignal , createEffect , batch , Match , Switch } from 'solid-js' ;
22import { Icon } from 'solid-heroicons' ;
33import { refresh , terminal } from 'solid-heroicons/outline' ;
4- import { unwrap , createStore } from 'solid-js/store' ;
4+ import { unwrap } from 'solid-js/store' ;
55import { Preview } from './preview' ;
66import { TabItem } from './tab/item' ;
77import { TabList } from './tab/list' ;
88import { GridResizer } from './gridResizer' ;
99import { Error } from './error' ;
10- import { debounce } from '@solid-primitives/scheduled' ;
10+ import { throttle } from '@solid-primitives/scheduled' ;
1111import { createMediaQuery } from '@solid-primitives/media' ;
1212
1313import MonacoTabs from './editor/monacoTabs' ;
1414import Editor from './editor' ;
1515
16- import type { Tab } from '../' ;
1716import type { Repl as ReplProps } from '../../types/types' ;
1817
1918const compileMode = {
@@ -22,88 +21,69 @@ const compileMode = {
2221 HYDRATABLE : { generate : 'dom' , hydratable : true } ,
2322} as const ;
2423
25- type ValueOf < T > = T [ keyof T ] ;
26-
2724const Repl : ReplProps = ( props ) => {
2825 const { compiler, formatter } = props ;
2926 let now : number ;
3027
3128 const tabRefs = new Map < string , HTMLSpanElement > ( ) ;
3229
33- const [ store , setStore ] = createStore ( {
34- error : '' ,
35- compiled : '' ,
36- compiledTabs : props . current
37- ? {
38- [ `./${ props . current } ` ] : '' ,
39- }
40- : { } ,
41- mode : 'DOM' as keyof typeof compileMode ,
42- isCompiling : false ,
43- get compileMode ( ) : ValueOf < typeof compileMode > {
44- return compileMode [ this . mode ] ;
45- } ,
46- } ) ;
47-
48- const actions = {
49- resetError : ( ) => setStore ( 'error' , '' ) ,
50- setCurrentTab ( current : string ) {
51- const idx = props . tabs . findIndex ( ( tab ) => tab . name === current ) ;
52- if ( idx < 0 ) return ;
53- props . setCurrent ( current ) ;
54- } ,
55- setCompiled ( compiled : string , tabs : Record < string , string > ) {
56- setStore ( { compiled, isCompiling : false , compiledTabs : tabs } ) ;
57- } ,
58- setTabName ( id1 : string , name : string ) {
59- const idx = props . tabs . findIndex ( ( tab ) => tab . name === id1 ) ;
60- if ( idx < 0 ) return ;
61-
62- const tabs = props . tabs ;
63- tabs [ idx ] = { ...tabs [ idx ] , name } ;
64- batch ( ( ) => {
65- props . setTabs ( tabs ) ;
66- if ( props . current === id1 ) {
67- props . setCurrent ( tabs [ idx ] . name ) ;
68- }
69- } ) ;
70- } ,
71- removeTab ( id1 : string ) {
72- const tabs = props . tabs ;
73- const idx = tabs . findIndex ( ( tab ) => tab . name === id1 ) ;
74- const tab = tabs [ idx ] ;
75-
76- if ( ! tab ) return ;
77-
78- const confirmDeletion = confirm ( `Are you sure you want to delete ${ tab . name } ?` ) ;
79- if ( ! confirmDeletion ) return ;
80-
81- batch ( ( ) => {
82- props . setTabs ( [ ...tabs . slice ( 0 , idx ) , ...tabs . slice ( idx + 1 ) ] ) ;
83- // We want to redirect to another tab if we are deleting the current one
84- if ( props . current === id1 ) {
85- props . setCurrent ( tabs [ idx - 1 ] . name ) ;
86- }
87- } ) ;
88- } ,
89- setCurrentSource ( source : string ) {
90- const idx = props . tabs . findIndex ( ( tab ) => tab . name === props . current ) ;
91- if ( idx < 0 ) return ;
92-
93- const tabs = props . tabs ;
94- tabs [ idx ] . source = source ;
95- } ,
96- addTab ( ) {
97- const newTab = {
98- name : `tab${ props . tabs . length } .tsx` ,
99- source : '' ,
100- } ;
101- batch ( ( ) => {
102- props . setTabs ( props . tabs . concat ( newTab ) ) ;
103- props . setCurrent ( newTab . name ) ;
104- } ) ;
105- } ,
106- } ;
30+ const [ error , setError ] = createSignal ( '' ) ;
31+ const [ compiled , setCompiled ] = createSignal ( '' ) ;
32+ const [ mode , setMode ] = createSignal < typeof compileMode [ keyof typeof compileMode ] > ( compileMode . SSR ) ;
33+
34+ function setCurrentTab ( current : string ) {
35+ const idx = props . tabs . findIndex ( ( tab ) => tab . name === current ) ;
36+ if ( idx < 0 ) return ;
37+ props . setCurrent ( current ) ;
38+ }
39+ function setTabName ( id1 : string , name : string ) {
40+ const idx = props . tabs . findIndex ( ( tab ) => tab . name === id1 ) ;
41+ if ( idx < 0 ) return ;
42+
43+ const tabs = props . tabs ;
44+ tabs [ idx ] = { ...tabs [ idx ] , name } ;
45+ batch ( ( ) => {
46+ props . setTabs ( tabs ) ;
47+ if ( props . current === id1 ) {
48+ props . setCurrent ( tabs [ idx ] . name ) ;
49+ }
50+ } ) ;
51+ }
52+ function removeTab ( id1 : string ) {
53+ const tabs = props . tabs ;
54+ const idx = tabs . findIndex ( ( tab ) => tab . name === id1 ) ;
55+ const tab = tabs [ idx ] ;
56+
57+ if ( ! tab ) return ;
58+
59+ const confirmDeletion = confirm ( `Are you sure you want to delete ${ tab . name } ?` ) ;
60+ if ( ! confirmDeletion ) return ;
61+
62+ batch ( ( ) => {
63+ props . setTabs ( [ ...tabs . slice ( 0 , idx ) , ...tabs . slice ( idx + 1 ) ] ) ;
64+ // We want to redirect to another tab if we are deleting the current one
65+ if ( props . current === id1 ) {
66+ props . setCurrent ( tabs [ idx - 1 ] . name ) ;
67+ }
68+ } ) ;
69+ }
70+ function setCurrentSource ( source : string ) {
71+ const idx = props . tabs . findIndex ( ( tab ) => tab . name === props . current ) ;
72+ if ( idx < 0 ) return ;
73+
74+ const tabs = props . tabs ;
75+ tabs [ idx ] . source = source ;
76+ }
77+ function addTab ( ) {
78+ const newTab = {
79+ name : `tab${ props . tabs . length } .tsx` ,
80+ source : '' ,
81+ } ;
82+ batch ( ( ) => {
83+ props . setTabs ( props . tabs . concat ( newTab ) ) ;
84+ props . setCurrent ( newTab . name ) ;
85+ } ) ;
86+ }
10787
10888 const [ edit , setEdit ] = createSignal ( - 1 ) ;
10989 const [ outputTab , setOutputTab ] = createSignal ( 0 ) ;
@@ -112,12 +92,11 @@ const Repl: ReplProps = (props) => {
11292 const { event } = data ;
11393
11494 if ( event === 'RESULT' ) {
115- const { compiled, tabs , error } = data ;
95+ const { compiled, error } = data ;
11696
117- if ( error ) return setStore ( { error } ) ;
118- if ( ! compiled ) return ;
97+ if ( error ) return setError ( error ) ;
11998
120- actions . setCompiled ( compiled , tabs ) ;
99+ setCompiled ( compiled ) ;
121100
122101 console . log ( `Compilation took: ${ performance . now ( ) - now } ms` ) ;
123102 }
@@ -128,17 +107,10 @@ const Repl: ReplProps = (props) => {
128107 * it takes ~15ms to compile with the web worker...
129108 * Also, real time feedback can be stressful
130109 */
131- const applyCompilation = debounce ( ( tabs : Tab [ ] , compileOpts : Record < string , any > ) => {
132- if ( ! tabs . length ) return ;
133-
134- setStore ( 'isCompiling' , true ) ;
110+ const applyCompilation = throttle ( ( message : any ) => {
135111 now = performance . now ( ) ;
136112
137- compiler . postMessage ( {
138- event : 'COMPILE' ,
139- tabs,
140- compileOpts,
141- } ) ;
113+ compiler . postMessage ( message ) ;
142114 } , 250 ) ;
143115
144116 /**
@@ -147,18 +119,20 @@ const Repl: ReplProps = (props) => {
147119 */
148120 createEffect ( ( ) => {
149121 for ( const tab of props . tabs ) tab . source ;
150- applyCompilation ( unwrap ( props . tabs ) , unwrap ( compileMode [ store . mode ] ) ) ;
122+ applyCompilation (
123+ outputTab ( ) == 0
124+ ? {
125+ event : 'ROLLUP' ,
126+ tabs : unwrap ( props . tabs ) ,
127+ }
128+ : {
129+ event : 'BABEL' ,
130+ tab : unwrap ( props . tabs . find ( ( tab ) => tab . name == props . current ) ) ,
131+ compileOpts : mode ( ) ,
132+ } ,
133+ ) ;
151134 } ) ;
152135
153- /**
154- * This sync the editor state with the current selected tab.
155- *
156- * @param source {string} - The source code from the editor
157- */
158- const handleDocChange = ( source : string ) => {
159- actions . setCurrentSource ( source ) ;
160- } ;
161-
162136 const clampPercentage = ( percentage : number , lowerBound : number , upperBound : number ) => {
163137 return Math . min ( Math . max ( percentage , lowerBound ) , upperBound ) ;
164138 } ;
@@ -188,7 +162,6 @@ const Repl: ReplProps = (props) => {
188162
189163 const [ reloadSignal , reload ] = createSignal ( false , { equals : false } ) ;
190164 const [ devtoolsOpen , setDevtoolsOpen ] = createSignal ( true ) ;
191-
192165 const [ displayErrors , setDisplayErrors ] = createSignal ( true ) ;
193166
194167 return (
@@ -216,7 +189,7 @@ const Repl: ReplProps = (props) => {
216189 < TabItem active = { props . current === tab . name } class = "mr-2" >
217190 < button
218191 type = "button"
219- onClick = { ( ) => actions . setCurrentTab ( tab . name ) }
192+ onClick = { ( ) => setCurrentTab ( tab . name ) }
220193 onDblClick = { ( ) => {
221194 setEdit ( index ( ) ) ;
222195 tabRefs . get ( tab . name ) ?. focus ( ) ;
@@ -228,13 +201,13 @@ const Repl: ReplProps = (props) => {
228201 contentEditable = { edit ( ) == index ( ) }
229202 onBlur = { ( e ) => {
230203 setEdit ( - 1 ) ;
231- actions . setTabName ( tab . name , e . currentTarget . textContent ! ) ;
204+ setTabName ( tab . name , e . currentTarget . textContent ! ) ;
232205 } }
233206 onKeyDown = { ( e ) => {
234207 if ( e . code === 'Space' ) e . preventDefault ( ) ;
235208 if ( e . code !== 'Enter' ) return ;
236209 setEdit ( - 1 ) ;
237- actions . setTabName ( tab . name , e . currentTarget . textContent ! ) ;
210+ setTabName ( tab . name , e . currentTarget . textContent ! ) ;
238211 } }
239212 >
240213 { tab . name }
@@ -246,7 +219,7 @@ const Repl: ReplProps = (props) => {
246219 type = "button"
247220 class = "border-0 cursor-pointer -mb-0.5"
248221 onClick = { ( ) => {
249- actions . removeTab ( tab . name ) ;
222+ removeTab ( tab . name ) ;
250223 } }
251224 >
252225 < span class = "sr-only" > Delete this tab</ span >
@@ -260,7 +233,7 @@ const Repl: ReplProps = (props) => {
260233 </ For >
261234
262235 < li class = "inline-flex items-center m-0 border-b-2 border-transparent" >
263- < button type = "button" onClick = { actions . addTab } title = "Add a new tab" >
236+ < button type = "button" onClick = { addTab } title = "Add a new tab" >
264237 < span class = "sr-only" > Add a new tab</ span >
265238 < svg
266239 viewBox = "0 0 24 24"
@@ -285,17 +258,13 @@ const Repl: ReplProps = (props) => {
285258 </ TabItem >
286259 </ TabList >
287260
288- < MonacoTabs
289- tabs = { props . tabs }
290- compiled = { props . current ? store . compiledTabs [ `./${ props . current } ` ] : '' }
291- folder = { props . id }
292- />
261+ < MonacoTabs tabs = { props . tabs } compiled = { compiled ( ) } folder = { props . id } />
293262
294263 < Show when = { props . current } >
295264 { ( current ) => (
296265 < Editor
297266 url = { `file:///${ props . id } /${ current } ` }
298- onDocChange = { handleDocChange }
267+ onDocChange = { setCurrentSource }
299268 formatter = { formatter }
300269 isDark = { props . dark }
301270 withMinimap = { false }
@@ -305,10 +274,9 @@ const Repl: ReplProps = (props) => {
305274 ) }
306275 </ Show >
307276
308- < Show
309- when = { displayErrors ( ) && store . error }
310- children = { < Error onDismiss = { actions . resetError } message = { store . error } /> }
311- />
277+ < Show when = { displayErrors ( ) && error ( ) } >
278+ < Error onDismiss = { ( ) => setError ( '' ) } message = { error ( ) } />
279+ </ Show >
312280 </ div >
313281
314282 < GridResizer ref = { ( el ) => ( resizer = el ) } isHorizontal = { isHorizontal ( ) } onResize = { changeLeft } />
@@ -350,7 +318,7 @@ const Repl: ReplProps = (props) => {
350318 class = "w-full -mb-0.5 py-2"
351319 onClick = { ( ) => {
352320 setOutputTab ( 1 ) ;
353- setStore ( 'mode' , ' DOM' ) ;
321+ setMode ( compileMode . DOM ) ;
354322 } }
355323 >
356324 Output
@@ -364,12 +332,12 @@ const Repl: ReplProps = (props) => {
364332 reloadSignal = { reloadSignal ( ) }
365333 devtools = { devtoolsOpen ( ) }
366334 isDark = { props . dark }
367- code = { store . compiled }
335+ code = { compiled ( ) }
368336 class = { `h-full w-full bg-white row-start-5 ${ props . isHorizontal ? '' : 'md:row-start-2' } ` }
369337 />
370338 </ Match >
371339 < Match when = { outputTab ( ) == 1 } >
372- < section class = "h-full relative divide-y-2 divide-slate-200 dark:divide-neutral-800" >
340+ < section class = "h-full flex flex-col relative divide-y-2 divide-slate-200 dark:divide-neutral-800" >
373341 < Editor
374342 url = { `file:///${ props . id } /output_dont_import.tsx` }
375343 isDark = { props . dark }
@@ -383,10 +351,10 @@ const Repl: ReplProps = (props) => {
383351 < div class = "mt-1 space-y-1 text-sm" >
384352 < label class = "block mr-auto cursor-pointer space-x-2" >
385353 < input
386- checked = { store . mode === ' DOM' }
354+ checked = { mode ( ) === compileMode . DOM }
387355 value = "DOM"
388356 class = "text-brand-default"
389- onChange = { ( e ) => setStore ( 'mode' , e . currentTarget . value as any ) }
357+ onChange = { [ setMode , compileMode . DOM ] }
390358 type = "radio"
391359 name = "dom"
392360 id = "dom"
@@ -396,10 +364,10 @@ const Repl: ReplProps = (props) => {
396364
397365 < label class = "block mr-auto cursor-pointer space-x-2" >
398366 < input
399- checked = { store . mode === ' SSR' }
367+ checked = { mode ( ) === compileMode . SSR }
400368 value = "SSR"
401369 class = "text-brand-default"
402- onChange = { ( e ) => setStore ( 'mode' , e . currentTarget . value as any ) }
370+ onChange = { [ setMode , compileMode . SSR ] }
403371 type = "radio"
404372 name = "dom"
405373 id = "dom"
@@ -409,10 +377,10 @@ const Repl: ReplProps = (props) => {
409377
410378 < label class = "block mr-auto cursor-pointer space-x-2" >
411379 < input
412- checked = { store . mode === ' HYDRATABLE' }
380+ checked = { mode ( ) === compileMode . HYDRATABLE }
413381 value = "HYDRATABLE"
414382 class = "text-brand-default"
415- onChange = { ( e ) => setStore ( 'mode' , e . currentTarget . value as any ) }
383+ onChange = { [ setMode , compileMode . HYDRATABLE ] }
416384 type = "radio"
417385 name = "dom"
418386 id = "dom"
0 commit comments