Skip to content

Commit 4d9d97b

Browse files
committed
compile more tabs
1 parent 1512585 commit 4d9d97b

File tree

2 files changed

+155
-179
lines changed

2 files changed

+155
-179
lines changed

src/components/repl.tsx

Lines changed: 95 additions & 127 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,18 @@
11
import { Show, For, createSignal, createEffect, batch, Match, Switch } from 'solid-js';
22
import { Icon } from 'solid-heroicons';
33
import { refresh, terminal } from 'solid-heroicons/outline';
4-
import { unwrap, createStore } from 'solid-js/store';
4+
import { unwrap } from 'solid-js/store';
55
import { Preview } from './preview';
66
import { TabItem } from './tab/item';
77
import { TabList } from './tab/list';
88
import { GridResizer } from './gridResizer';
99
import { Error } from './error';
10-
import { debounce } from '@solid-primitives/scheduled';
10+
import { throttle } from '@solid-primitives/scheduled';
1111
import { createMediaQuery } from '@solid-primitives/media';
1212

1313
import MonacoTabs from './editor/monacoTabs';
1414
import Editor from './editor';
1515

16-
import type { Tab } from '../';
1716
import type { Repl as ReplProps } from '../../types/types';
1817

1918
const 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-
2724
const 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

Comments
 (0)