|
|
@ -15,6 +15,7 @@
|
|
|
|
|
|
|
|
|
|
|
|
<script>
|
|
|
|
<script>
|
|
|
|
import TableOfContents from './_components/TableOfContents.svelte';
|
|
|
|
import TableOfContents from './_components/TableOfContents.svelte';
|
|
|
|
|
|
|
|
import ScreenToggle from './_components/ScreenToggle.svelte';
|
|
|
|
import Icon from '../../../components/Icon.svelte';
|
|
|
|
import Icon from '../../../components/Icon.svelte';
|
|
|
|
import Repl from '@sveltejs/svelte-repl';
|
|
|
|
import Repl from '@sveltejs/svelte-repl';
|
|
|
|
import { getContext } from 'svelte';
|
|
|
|
import { getContext } from 'svelte';
|
|
|
@ -29,6 +30,9 @@
|
|
|
|
let scrollable;
|
|
|
|
let scrollable;
|
|
|
|
const lookup = new Map();
|
|
|
|
const lookup = new Map();
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
let width = process.browser ? window.innerWidth : 1000;
|
|
|
|
|
|
|
|
let offset = 0;
|
|
|
|
|
|
|
|
|
|
|
|
sections.forEach(section => {
|
|
|
|
sections.forEach(section => {
|
|
|
|
section.chapters.forEach(chapter => {
|
|
|
|
section.chapters.forEach(chapter => {
|
|
|
|
const obj = {
|
|
|
|
const obj = {
|
|
|
@ -70,6 +74,8 @@
|
|
|
|
});
|
|
|
|
});
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
$: mobile = width < 768;
|
|
|
|
|
|
|
|
|
|
|
|
function reset() {
|
|
|
|
function reset() {
|
|
|
|
repl.update({
|
|
|
|
repl.update({
|
|
|
|
components: chapter.app_a.map(clone)
|
|
|
|
components: chapter.app_a.map(clone)
|
|
|
@ -110,11 +116,27 @@
|
|
|
|
padding: 0;
|
|
|
|
padding: 0;
|
|
|
|
/* margin: 0 calc(var(--side-nav) * -1); */
|
|
|
|
/* margin: 0 calc(var(--side-nav) * -1); */
|
|
|
|
box-sizing: border-box;
|
|
|
|
box-sizing: border-box;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
.viewport {
|
|
|
|
|
|
|
|
width: 100%;
|
|
|
|
|
|
|
|
height: 100%;
|
|
|
|
display: grid;
|
|
|
|
display: grid;
|
|
|
|
grid-template-columns: minmax(33.333%, 480px) auto;
|
|
|
|
grid-template-columns: minmax(33.333%, 480px) auto;
|
|
|
|
grid-auto-rows: 100%;
|
|
|
|
grid-auto-rows: 100%;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
.mobile .viewport {
|
|
|
|
|
|
|
|
width: 300%;
|
|
|
|
|
|
|
|
height: calc(100% - 42px);
|
|
|
|
|
|
|
|
grid-template-columns: 33.333% 66.666%;
|
|
|
|
|
|
|
|
transition: transform 0.3s;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
.mobile .offset-1 { transform: translate(-33.333%, 0); }
|
|
|
|
|
|
|
|
.mobile .offset-2 { transform: translate(-66.666%, 0); }
|
|
|
|
|
|
|
|
|
|
|
|
.tutorial-text {
|
|
|
|
.tutorial-text {
|
|
|
|
display: flex;
|
|
|
|
display: flex;
|
|
|
|
flex-direction: column;
|
|
|
|
flex-direction: column;
|
|
|
@ -215,44 +237,53 @@
|
|
|
|
<title>{selected.section.title} / {selected.chapter.title} • Svelte Tutorial</title>
|
|
|
|
<title>{selected.section.title} / {selected.chapter.title} • Svelte Tutorial</title>
|
|
|
|
</svelte:head>
|
|
|
|
</svelte:head>
|
|
|
|
|
|
|
|
|
|
|
|
<div class="tutorial-outer">
|
|
|
|
<svelte:window bind:innerWidth={width}/>
|
|
|
|
<div class="tutorial-text">
|
|
|
|
|
|
|
|
<div class="table-of-contents">
|
|
|
|
|
|
|
|
<TableOfContents {sections} {slug} {selected}/>
|
|
|
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
<div class="chapter-markup" bind:this={scrollable}>
|
|
|
|
<div class="tutorial-outer" class:mobile>
|
|
|
|
{@html chapter.html}
|
|
|
|
<div class="viewport offset-{offset}">
|
|
|
|
|
|
|
|
<div class="tutorial-text">
|
|
|
|
<div class="controls">
|
|
|
|
<div class="table-of-contents">
|
|
|
|
{#if chapter.app_b}
|
|
|
|
<TableOfContents {sections} {slug} {selected}/>
|
|
|
|
<!-- TODO disable this button when the contents of the REPL
|
|
|
|
|
|
|
|
matches the expected end result -->
|
|
|
|
|
|
|
|
<button class="show" on:click="{() => completed ? reset() : complete()}">
|
|
|
|
|
|
|
|
{completed ? 'Reset' : 'Show me'}
|
|
|
|
|
|
|
|
</button>
|
|
|
|
|
|
|
|
{/if}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
{#if selected.next}
|
|
|
|
|
|
|
|
<a class="next" href="tutorial/{selected.next.slug}">Next <Icon name="arrow-right" /></a>
|
|
|
|
|
|
|
|
{/if}
|
|
|
|
|
|
|
|
</div>
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
|
|
<div class="improve-chapter">
|
|
|
|
<div class="chapter-markup" bind:this={scrollable}>
|
|
|
|
<a href={improve_link}><Icon name="edit" size={14}/> Edit this chapter</a>
|
|
|
|
{@html chapter.html}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
<div class="controls">
|
|
|
|
|
|
|
|
{#if chapter.app_b}
|
|
|
|
|
|
|
|
<!-- TODO disable this button when the contents of the REPL
|
|
|
|
|
|
|
|
matches the expected end result -->
|
|
|
|
|
|
|
|
<button class="show" on:click="{() => completed ? reset() : complete()}">
|
|
|
|
|
|
|
|
{completed ? 'Reset' : 'Show me'}
|
|
|
|
|
|
|
|
</button>
|
|
|
|
|
|
|
|
{/if}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
{#if selected.next}
|
|
|
|
|
|
|
|
<a class="next" href="tutorial/{selected.next.slug}">Next <Icon name="arrow-right" /></a>
|
|
|
|
|
|
|
|
{/if}
|
|
|
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
<div class="improve-chapter">
|
|
|
|
|
|
|
|
<a href={improve_link}><Icon name="edit" size={14}/> Edit this chapter</a>
|
|
|
|
|
|
|
|
</div>
|
|
|
|
</div>
|
|
|
|
</div>
|
|
|
|
</div>
|
|
|
|
</div>
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
<div class="tutorial-repl">
|
|
|
|
<div class="tutorial-repl">
|
|
|
|
<Repl
|
|
|
|
<Repl
|
|
|
|
bind:this={repl}
|
|
|
|
bind:this={repl}
|
|
|
|
{svelteUrl}
|
|
|
|
{svelteUrl}
|
|
|
|
{rollupUrl}
|
|
|
|
{rollupUrl}
|
|
|
|
orientation="rows"
|
|
|
|
orientation={mobile ? 'columns' : 'rows'}
|
|
|
|
on:change={handle_change}
|
|
|
|
fixed={mobile}
|
|
|
|
setup={mapbox_setup}
|
|
|
|
on:change={handle_change}
|
|
|
|
relaxed
|
|
|
|
setup={mapbox_setup}
|
|
|
|
/>
|
|
|
|
relaxed
|
|
|
|
|
|
|
|
/>
|
|
|
|
|
|
|
|
</div>
|
|
|
|
</div>
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
{#if mobile}
|
|
|
|
|
|
|
|
<ScreenToggle bind:offset/>
|
|
|
|
|
|
|
|
{/if}
|
|
|
|
</div>
|
|
|
|
</div>
|
|
|
|