mirror of https://github.com/sveltejs/svelte
- move tutorial's ScreenToggle.svelte to src/components and makes it generic (it now accept a list of labels) - add a navigation util to export getFragment() - move mapbox_setup, svelteUrl, rollupUrl in a config - docs/index.svelte: removes an unused import, use getFragment, use onscroll in the second setTimeout - global.css: add a couple of vars and set width/height on the page Closes #2460pull/2466/head
parent
bb888f69d2
commit
39c172ef83
@ -0,0 +1,5 @@
|
||||
// REPL props
|
||||
|
||||
export const svelteUrl = `https://unpkg.com/svelte@beta`;
|
||||
export const rollupUrl = `https://unpkg.com/rollup@1/dist/rollup.browser.js`;
|
||||
export const mapbox_setup = `window.MAPBOX_ACCESS_TOKEN = process.env.MAPBOX_ACCESS_TOKEN;`;
|
@ -0,0 +1,143 @@
|
||||
<script>
|
||||
import { afterUpdate } from 'svelte';
|
||||
import Icon from '../../components/Icon.svelte';
|
||||
|
||||
export let sections = [];
|
||||
export let active_section = null;
|
||||
|
||||
let ul;
|
||||
|
||||
afterUpdate(() => {
|
||||
const active = ul.querySelector('.active');
|
||||
|
||||
if (active) {
|
||||
const { top, bottom } = active.getBoundingClientRect();
|
||||
|
||||
const min = 200;
|
||||
const max = window.innerHeight - 200;
|
||||
|
||||
if (top > max) {
|
||||
ul.parentNode.scrollBy({
|
||||
top: top - max,
|
||||
left: 0,
|
||||
behavior: 'smooth'
|
||||
});
|
||||
} else if (bottom < min) {
|
||||
ul.parentNode.scrollBy({
|
||||
top: bottom - min,
|
||||
left: 0,
|
||||
behavior: 'smooth'
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
<ul
|
||||
bind:this={ul}
|
||||
class="examples-toc"
|
||||
on:mouseenter="{() => prevent_sidebar_scroll = true}"
|
||||
on:mouseleave="{() => prevent_sidebar_scroll = false}"
|
||||
>
|
||||
{#each sections as section}
|
||||
<li>
|
||||
<span class="section-title">
|
||||
{section.title}
|
||||
</span>
|
||||
|
||||
{#each section.examples as example}
|
||||
<a href="examples#{example.slug}">
|
||||
<div
|
||||
class="row"
|
||||
class:active="{example.slug === active_section}"
|
||||
>
|
||||
<div class="info">
|
||||
<div
|
||||
class="thumbnail"
|
||||
style="background-image: url(examples/thumbnails/{example.slug}.png)"
|
||||
></div>
|
||||
<div class="example-title">
|
||||
{example.title}
|
||||
</div>
|
||||
</div>
|
||||
{#if example.slug === active_section}
|
||||
<Icon name="arrow-right" />
|
||||
{/if}
|
||||
</div>
|
||||
</a>
|
||||
{/each}
|
||||
</li>
|
||||
{/each}
|
||||
</ul>
|
||||
|
||||
<style>
|
||||
.examples-toc {
|
||||
overflow-y: auto;
|
||||
height: 100%;
|
||||
border-right: 1px solid var(--second);
|
||||
background-color: var(--second);
|
||||
color: white;
|
||||
padding: 2em 2em 0 2em;
|
||||
}
|
||||
|
||||
.examples-toc li {
|
||||
display: block;
|
||||
line-height: 1.2;
|
||||
margin: 0 0 4.8rem 0;
|
||||
}
|
||||
|
||||
a {
|
||||
position: relative;
|
||||
opacity: 0.7;
|
||||
transition: opacity 0.2s;
|
||||
}
|
||||
|
||||
.section-title {
|
||||
display: block;
|
||||
padding: 0 0 .8rem 0;
|
||||
font: 400 var(--h6) var(--font);
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 0.12em;
|
||||
font-weight: 700;
|
||||
}
|
||||
|
||||
.example-title {
|
||||
display: block;
|
||||
font-size: 1.6rem;
|
||||
font-family: var(--font);
|
||||
padding: 0 0 0.2em 0.6em;
|
||||
}
|
||||
|
||||
.example-title:hover {
|
||||
color: var(--flash);
|
||||
opacity: 1
|
||||
}
|
||||
|
||||
.active {
|
||||
opacity: 1;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.row {
|
||||
position: relative;
|
||||
margin: 0.5em 0;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
}
|
||||
|
||||
.info {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.thumbnail {
|
||||
background: white 50% 50% no-repeat;
|
||||
background-size: contain;
|
||||
width: 5rem;
|
||||
height: 5rem;
|
||||
border: 1px solid #ccc;
|
||||
border-radius: 2px;
|
||||
box-shadow: 1px 1px 3px rgba(0,0,0,0.13);
|
||||
}
|
||||
</style>
|
@ -1,101 +1,137 @@
|
||||
<!-- FIXME sometimes it adds a trailing slash when landing -->
|
||||
<script context="module">
|
||||
export async function preload() {
|
||||
const groups = await this.fetch(`examples.json`).then(r => r.json());
|
||||
export async function preload({params, query}) {
|
||||
const sections = await this.fetch(`examples.json`).then(r => r.json());
|
||||
const title_by_slug = sections.reduce((acc, {examples}) => {
|
||||
examples.forEach(({slug, title}) => {
|
||||
acc[slug] = title;
|
||||
});
|
||||
|
||||
return {
|
||||
groups
|
||||
};
|
||||
return acc;
|
||||
}, {});
|
||||
|
||||
return {sections, title_by_slug};
|
||||
}
|
||||
</script>
|
||||
|
||||
<script>
|
||||
export let groups;
|
||||
</script>
|
||||
|
||||
<style>
|
||||
.content {
|
||||
max-width: 120rem;
|
||||
padding: 0 var(--side-nav);
|
||||
margin: 0 auto;
|
||||
import { onMount } from 'svelte';
|
||||
import { goto } from '@sapper/app';
|
||||
import Repl from '@sveltejs/svelte-repl';
|
||||
|
||||
import ScreenToggle from '../../components/ScreenToggle.svelte';
|
||||
import {
|
||||
mapbox_setup, // needed for context API example
|
||||
rollupUrl,
|
||||
svelteUrl
|
||||
} from '../../config';
|
||||
import { process_example } from '../../utils/examples';
|
||||
import { getFragment } from '../../utils/navigation';
|
||||
import TableOfContents from './_TableOfContents.svelte';
|
||||
|
||||
export let sections;
|
||||
export let title_by_slug;
|
||||
|
||||
let active_slug;
|
||||
let width;
|
||||
let offset = 1;
|
||||
let repl;
|
||||
|
||||
$: title = title_by_slug[active_slug] || '';
|
||||
$: first_slug = sections[0].examples[0].slug;
|
||||
$: if (repl) {
|
||||
fetch(`examples/${active_slug}.json`)
|
||||
.then(async response => {
|
||||
if (response.ok) {
|
||||
const data = await response.json();
|
||||
|
||||
repl.set({
|
||||
components: process_example(data.files)
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
$: mobile = width < 768; // note: same as per media query below
|
||||
|
||||
h1 { margin: 6rem 0 6rem -0.05em }
|
||||
onMount(() => {
|
||||
const onhashchange = () => {
|
||||
active_slug = getFragment();
|
||||
offset = 1;
|
||||
};
|
||||
window.addEventListener('hashchange', onhashchange, false);
|
||||
|
||||
h2 {
|
||||
margin: 0 0 1em 0;
|
||||
font: 600 var(--h4) var(--font);
|
||||
border-bottom: var(--border-w) solid #eee;
|
||||
text-transform: uppercase;
|
||||
letter-spacing: .05em;
|
||||
padding: 0 0 0.2em 0;
|
||||
if (getFragment()) {
|
||||
active_slug = getFragment();
|
||||
} else {
|
||||
active_slug = first_slug;
|
||||
goto(`examples#${active_slug}`);
|
||||
}
|
||||
|
||||
section { margin: 0 0 4rem 0 }
|
||||
return () => {
|
||||
window.removeEventListener('hashchange', onhashchange, false);
|
||||
};
|
||||
});
|
||||
</script>
|
||||
|
||||
.example {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
text-align: center;
|
||||
font: 300 var(--h5) var(--font);
|
||||
}
|
||||
<svelte:head>
|
||||
<title>{title} {title ? '•' : ''} Svelte Examples</title>
|
||||
|
||||
<meta name="twitter:title" content="Svelte examples">
|
||||
<meta name="twitter:description" content="Cybernetically enhanced web apps">
|
||||
<meta name="Description" content="Interactive example Svelte apps">
|
||||
</svelte:head>
|
||||
|
||||
<div class='examples-container' bind:clientWidth={width}>
|
||||
<div class="viewport offset-{offset}">
|
||||
<TableOfContents {sections} active_section={active_slug} />
|
||||
<Repl
|
||||
bind:this={repl}
|
||||
{svelteUrl}
|
||||
{rollupUrl}
|
||||
orientation={mobile ? 'columns' : 'rows'}
|
||||
fixed={mobile}
|
||||
relaxed
|
||||
injectedJS={mapbox_setup}
|
||||
/>
|
||||
</div>
|
||||
{#if mobile}
|
||||
<ScreenToggle bind:offset labels={['index', 'input', 'output']}/>
|
||||
{/if}
|
||||
</div>
|
||||
|
||||
.thumbnail {
|
||||
<style>
|
||||
.examples-container {
|
||||
position: relative;
|
||||
background: white 50% 50% no-repeat;
|
||||
background-size: contain;
|
||||
width: 5rem;
|
||||
height: 5rem;
|
||||
margin: .8rem 33%;
|
||||
border: 1px solid #ccc;
|
||||
border-radius: var(--border-r);
|
||||
box-shadow: 1px 1px 2px rgba(0,0,0,0.13);
|
||||
height: calc(100vh - var(--nav-h));
|
||||
overflow: hidden;
|
||||
padding: 0 0 42px 0;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.e-grid {
|
||||
.viewport {
|
||||
display: grid;
|
||||
/* grid-gap: 2.4rem; */
|
||||
grid-template-columns: repeat(2, 1fr);
|
||||
align-items: center;
|
||||
width: 300%;
|
||||
height: 100%;
|
||||
grid-template-columns: 33.333% 66.666%;
|
||||
transition: transform .3s;
|
||||
grid-auto-rows: 100%;
|
||||
}
|
||||
|
||||
@media (min-width: 720px) {
|
||||
.e-grid {
|
||||
grid-template-columns: repeat(4, 1fr);
|
||||
}
|
||||
}
|
||||
.offset-1 { transform: translate(-33.333%, 0); }
|
||||
.offset-2 { transform: translate(-66.666%, 0); }
|
||||
|
||||
@media (min-width: 1080px) {
|
||||
.e-grid {
|
||||
grid-template-columns: repeat(6, 1fr);
|
||||
@media (min-width: 768px) {
|
||||
.examples-container { padding: 0 }
|
||||
|
||||
.viewport {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
display: grid;
|
||||
grid-template-columns: var(--sidebar-mid-w) auto;
|
||||
grid-auto-rows: 100%;
|
||||
transition: none;
|
||||
}
|
||||
|
||||
.offset-1, .offset-2 { transform: none; }
|
||||
}
|
||||
</style>
|
||||
|
||||
<svelte:head>
|
||||
<title>Examples • Svelte</title>
|
||||
|
||||
<meta name="twitter:title" content="Svelte examples">
|
||||
<meta name="twitter:description" content="Cybernetically enhanced web apps">
|
||||
<meta name="Description" content="Interactive example Svelte apps">
|
||||
</svelte:head>
|
||||
|
||||
<div class="content">
|
||||
<h1>Examples</h1>
|
||||
|
||||
{#each groups as group}
|
||||
<section class="">
|
||||
<h2>{group.title}</h2>
|
||||
|
||||
<div class="e-grid">
|
||||
{#each group.examples as example}
|
||||
<a class="example" href="repl?example={example.slug}">
|
||||
<div class="thumbnail" style="background-image: url(examples/thumbnails/{example.slug}.png)"></div>
|
||||
<p>{example.title}</p>
|
||||
</a>
|
||||
{/each}
|
||||
</div>
|
||||
</section>
|
||||
{/each}
|
||||
</div>
|
@ -1,6 +1,6 @@
|
||||
<script>
|
||||
import { goto } from '@sapper/app';
|
||||
import Icon from '../../../../components/Icon.svelte';
|
||||
import Icon from '../../../components/Icon.svelte';
|
||||
|
||||
export let sections;
|
||||
export let slug;
|
@ -0,0 +1 @@
|
||||
export const getFragment = () => window.location.hash.slice(1);
|
Loading…
Reference in new issue