1
1
import { Show , For , createSignal , createEffect , batch , Match , Switch } from 'solid-js' ;
2
2
import { Icon } from 'solid-heroicons' ;
3
3
import { refresh , terminal } from 'solid-heroicons/outline' ;
4
- import { unwrap , createStore } from 'solid-js/store' ;
4
+ import { unwrap } from 'solid-js/store' ;
5
5
import { Preview } from './preview' ;
6
6
import { TabItem } from './tab/item' ;
7
7
import { TabList } from './tab/list' ;
8
8
import { GridResizer } from './gridResizer' ;
9
9
import { Error } from './error' ;
10
- import { debounce } from '@solid-primitives/scheduled' ;
10
+ import { throttle } from '@solid-primitives/scheduled' ;
11
11
import { createMediaQuery } from '@solid-primitives/media' ;
12
12
13
13
import MonacoTabs from './editor/monacoTabs' ;
14
14
import Editor from './editor' ;
15
15
16
- import type { Tab } from '../' ;
17
16
import type { Repl as ReplProps } from '../../types/types' ;
18
17
19
18
const compileMode = {
@@ -22,88 +21,69 @@ const compileMode = {
22
21
HYDRATABLE : { generate : 'dom' , hydratable : true } ,
23
22
} as const ;
24
23
25
- type ValueOf < T > = T [ keyof T ] ;
26
-
27
24
const Repl : ReplProps = ( props ) => {
28
25
const { compiler, formatter } = props ;
29
26
let now : number ;
30
27
31
28
const tabRefs = new Map < string , HTMLSpanElement > ( ) ;
32
29
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
+ }
107
87
108
88
const [ edit , setEdit ] = createSignal ( - 1 ) ;
109
89
const [ outputTab , setOutputTab ] = createSignal ( 0 ) ;
@@ -112,12 +92,11 @@ const Repl: ReplProps = (props) => {
112
92
const { event } = data ;
113
93
114
94
if ( event === 'RESULT' ) {
115
- const { compiled, tabs , error } = data ;
95
+ const { compiled, error } = data ;
116
96
117
- if ( error ) return setStore ( { error } ) ;
118
- if ( ! compiled ) return ;
97
+ if ( error ) return setError ( error ) ;
119
98
120
- actions . setCompiled ( compiled , tabs ) ;
99
+ setCompiled ( compiled ) ;
121
100
122
101
console . log ( `Compilation took: ${ performance . now ( ) - now } ms` ) ;
123
102
}
@@ -128,17 +107,10 @@ const Repl: ReplProps = (props) => {
128
107
* it takes ~15ms to compile with the web worker...
129
108
* Also, real time feedback can be stressful
130
109
*/
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 ) => {
135
111
now = performance . now ( ) ;
136
112
137
- compiler . postMessage ( {
138
- event : 'COMPILE' ,
139
- tabs,
140
- compileOpts,
141
- } ) ;
113
+ compiler . postMessage ( message ) ;
142
114
} , 250 ) ;
143
115
144
116
/**
@@ -147,18 +119,20 @@ const Repl: ReplProps = (props) => {
147
119
*/
148
120
createEffect ( ( ) => {
149
121
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
+ ) ;
151
134
} ) ;
152
135
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
-
162
136
const clampPercentage = ( percentage : number , lowerBound : number , upperBound : number ) => {
163
137
return Math . min ( Math . max ( percentage , lowerBound ) , upperBound ) ;
164
138
} ;
@@ -188,7 +162,6 @@ const Repl: ReplProps = (props) => {
188
162
189
163
const [ reloadSignal , reload ] = createSignal ( false , { equals : false } ) ;
190
164
const [ devtoolsOpen , setDevtoolsOpen ] = createSignal ( true ) ;
191
-
192
165
const [ displayErrors , setDisplayErrors ] = createSignal ( true ) ;
193
166
194
167
return (
@@ -216,7 +189,7 @@ const Repl: ReplProps = (props) => {
216
189
< TabItem active = { props . current === tab . name } class = "mr-2" >
217
190
< button
218
191
type = "button"
219
- onClick = { ( ) => actions . setCurrentTab ( tab . name ) }
192
+ onClick = { ( ) => setCurrentTab ( tab . name ) }
220
193
onDblClick = { ( ) => {
221
194
setEdit ( index ( ) ) ;
222
195
tabRefs . get ( tab . name ) ?. focus ( ) ;
@@ -228,13 +201,13 @@ const Repl: ReplProps = (props) => {
228
201
contentEditable = { edit ( ) == index ( ) }
229
202
onBlur = { ( e ) => {
230
203
setEdit ( - 1 ) ;
231
- actions . setTabName ( tab . name , e . currentTarget . textContent ! ) ;
204
+ setTabName ( tab . name , e . currentTarget . textContent ! ) ;
232
205
} }
233
206
onKeyDown = { ( e ) => {
234
207
if ( e . code === 'Space' ) e . preventDefault ( ) ;
235
208
if ( e . code !== 'Enter' ) return ;
236
209
setEdit ( - 1 ) ;
237
- actions . setTabName ( tab . name , e . currentTarget . textContent ! ) ;
210
+ setTabName ( tab . name , e . currentTarget . textContent ! ) ;
238
211
} }
239
212
>
240
213
{ tab . name }
@@ -246,7 +219,7 @@ const Repl: ReplProps = (props) => {
246
219
type = "button"
247
220
class = "border-0 cursor-pointer -mb-0.5"
248
221
onClick = { ( ) => {
249
- actions . removeTab ( tab . name ) ;
222
+ removeTab ( tab . name ) ;
250
223
} }
251
224
>
252
225
< span class = "sr-only" > Delete this tab</ span >
@@ -260,7 +233,7 @@ const Repl: ReplProps = (props) => {
260
233
</ For >
261
234
262
235
< 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" >
264
237
< span class = "sr-only" > Add a new tab</ span >
265
238
< svg
266
239
viewBox = "0 0 24 24"
@@ -285,17 +258,13 @@ const Repl: ReplProps = (props) => {
285
258
</ TabItem >
286
259
</ TabList >
287
260
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 } />
293
262
294
263
< Show when = { props . current } >
295
264
{ ( current ) => (
296
265
< Editor
297
266
url = { `file:///${ props . id } /${ current } ` }
298
- onDocChange = { handleDocChange }
267
+ onDocChange = { setCurrentSource }
299
268
formatter = { formatter }
300
269
isDark = { props . dark }
301
270
withMinimap = { false }
@@ -305,10 +274,9 @@ const Repl: ReplProps = (props) => {
305
274
) }
306
275
</ Show >
307
276
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 >
312
280
</ div >
313
281
314
282
< GridResizer ref = { ( el ) => ( resizer = el ) } isHorizontal = { isHorizontal ( ) } onResize = { changeLeft } />
@@ -350,7 +318,7 @@ const Repl: ReplProps = (props) => {
350
318
class = "w-full -mb-0.5 py-2"
351
319
onClick = { ( ) => {
352
320
setOutputTab ( 1 ) ;
353
- setStore ( 'mode' , ' DOM' ) ;
321
+ setMode ( compileMode . DOM ) ;
354
322
} }
355
323
>
356
324
Output
@@ -364,12 +332,12 @@ const Repl: ReplProps = (props) => {
364
332
reloadSignal = { reloadSignal ( ) }
365
333
devtools = { devtoolsOpen ( ) }
366
334
isDark = { props . dark }
367
- code = { store . compiled }
335
+ code = { compiled ( ) }
368
336
class = { `h-full w-full bg-white row-start-5 ${ props . isHorizontal ? '' : 'md:row-start-2' } ` }
369
337
/>
370
338
</ Match >
371
339
< 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" >
373
341
< Editor
374
342
url = { `file:///${ props . id } /output_dont_import.tsx` }
375
343
isDark = { props . dark }
@@ -383,10 +351,10 @@ const Repl: ReplProps = (props) => {
383
351
< div class = "mt-1 space-y-1 text-sm" >
384
352
< label class = "block mr-auto cursor-pointer space-x-2" >
385
353
< input
386
- checked = { store . mode === ' DOM' }
354
+ checked = { mode ( ) === compileMode . DOM }
387
355
value = "DOM"
388
356
class = "text-brand-default"
389
- onChange = { ( e ) => setStore ( 'mode' , e . currentTarget . value as any ) }
357
+ onChange = { [ setMode , compileMode . DOM ] }
390
358
type = "radio"
391
359
name = "dom"
392
360
id = "dom"
@@ -396,10 +364,10 @@ const Repl: ReplProps = (props) => {
396
364
397
365
< label class = "block mr-auto cursor-pointer space-x-2" >
398
366
< input
399
- checked = { store . mode === ' SSR' }
367
+ checked = { mode ( ) === compileMode . SSR }
400
368
value = "SSR"
401
369
class = "text-brand-default"
402
- onChange = { ( e ) => setStore ( 'mode' , e . currentTarget . value as any ) }
370
+ onChange = { [ setMode , compileMode . SSR ] }
403
371
type = "radio"
404
372
name = "dom"
405
373
id = "dom"
@@ -409,10 +377,10 @@ const Repl: ReplProps = (props) => {
409
377
410
378
< label class = "block mr-auto cursor-pointer space-x-2" >
411
379
< input
412
- checked = { store . mode === ' HYDRATABLE' }
380
+ checked = { mode ( ) === compileMode . HYDRATABLE }
413
381
value = "HYDRATABLE"
414
382
class = "text-brand-default"
415
- onChange = { ( e ) => setStore ( 'mode' , e . currentTarget . value as any ) }
383
+ onChange = { [ setMode , compileMode . HYDRATABLE ] }
416
384
type = "radio"
417
385
name = "dom"
418
386
id = "dom"
0 commit comments