svelte/site/src/routes/repl/index.svelte

207 lines
4.1 KiB

<script context="module">
export function preload({ query }) {
return {
version: query.version || 'beta',
gist_id: query.gist,
demo: query.demo || 'hello-world'
};
}
</script>
<script>
import { onMount } from 'svelte';
import { locate } from 'locate-character';
import * as fleece from 'golden-fleece';
import AppControls from './_components/AppControls/index.svelte';
import Repl from '../../components/Repl/index.svelte';
import examples from '../../../content/examples/manifest.json';
export let version, demo, gist_id;
let repl;
let gist;
let name = 'loading...';
let zen_mode = false;
$: if (typeof history !== 'undefined') {
const params = [];
if (version !== 'latest') params.push(`version=${version}`);
if (gist_id) params.push(`gist=${gist_id}`);
else if (demo) params.push(`demo=${demo}`);
const url = params.length > 0
? `repl?${params.join('&')}`
: 'repl';
history.replaceState({}, 'x', url);
}
const slugs = new Set();
examples.forEach(group => {
group.examples.forEach(example => {
slugs.add(example.slug);
});
});
onMount(() => {
if (version !== 'local') {
fetch(`https://unpkg.com/svelte@${version || 'beta'}/package.json`)
.then(r => r.json())
.then(pkg => {
version = pkg.version;
});
}
if (gist_id) {
fetch(`gist/${gist_id}`).then(r => r.json()).then(data => {
gist = data;
const { id, description, files } = data;
name = description;
let values = {};
const components = Object.keys(files)
.map(file => {
const dot = file.lastIndexOf('.');
if (!~dot) return;
const source = files[file].content;
// while we're here...
if (file === 'data.json' || file === 'data.json5') {
values = tryParseData(source) || {};
}
let type = file.slice(dot + 1);
if (type === 'html') type = 'svelte';
return {
name: file.slice(0, dot),
type,
source
};
})
.filter(x => x.type === 'svelte' || x.type === 'js')
.sort((a, b) => {
if (a.name === 'App' && a.type === 'svelte') return -1;
if (b.name === 'App' && b.type === 'svelte') return 1;
if (a.type !== b.type) return a.type === 'svelte' ? -1 : 1;
return a.name < b.name ? -1 : 1;
});
repl.set({ components, values });
});
}
});
function load_demo(slug) {
const url = slugs.has(slug)
? `api/examples/${slug}`
: `guide/demo/${slug}.json`;
fetch(url).then(async response => {
if (response.ok) {
const data = await response.json();
name = data.title;
console.log(data.components);
repl.set({
values: tryParseData(data.json5) || {}, // TODO make this more error-resistant
components: data.components
});
gist = null;
}
});
}
function handle_fork(event) {
console.log(event);
demo = null;
gist = event.detail.gist;
gist_id = gist.id;
}
function tryParseData(json5) {
try {
return fleece.evaluate(json5);
} catch (err) {
return null;
}
}
$: if (process.browser && demo) {
load_demo(demo);
}
</script>
<style>
.repl-outer {
position: relative;
height: calc(100vh - var(--nav-h));
--app-controls-h: 5.6rem;
--pane-controls-h: 4.2rem;
overflow: hidden;
background-color: var(--back);
padding: var(--app-controls-h) 0 0 0;
margin: 0 calc(var(--side-nav) * -1);
box-sizing: border-box;
}
.zen-mode {
position: fixed;
width: 100%;
height: 100%;
top: 0;
z-index: 11;
}
.pane { width: 100%; height: 100% }
.loading {
text-align: center;
color: var(--second);
font-weight: 400;
margin: 2em 0 0 0;
opacity: 0;
animation: fade-in .4s;
animation-delay: .2s;
animation-fill-mode: both;
}
@keyframes fade-in {
0% { opacity: 0 }
100% { opacity: 1 }
}
.input {
padding: 2.4em 0 0 0;
}
</style>
<svelte:head>
<title>Svelte REPL</title>
</svelte:head>
<div class="repl-outer {zen_mode ? 'zen-mode' : ''}">
<AppControls
{examples}
{name}
{gist}
{repl}
bind:zen_mode
on:select="{e => (demo = e.detail.slug, gist = null)}"
on:forked={handle_fork}
/>
{#if process.browser}
<Repl bind:this={repl} {version}/>
{/if}
</div>