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