feat(site-2): Local examples (#8431)

* feat(docs): Local tutorial

* Refactor some stuff

* Better error handling

* Fix search imports

* Prerender tutorial

* try prerendering hack

* fix super stupid display hidden bug

* Shorten the rendered URL

* Shorten URL code even more

* Prerender in svelte.config.js

* Refactor

* Fix ScreenToggle dark mode styles

* Initial POC

* Rvert to old hack

* Fix svelte-document.jpg
pull/8453/head
Puru Vijay 2 years ago committed by GitHub
parent 35e7a852fc
commit 9793b41817
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

@ -20,8 +20,8 @@
display: flex;
justify-content: center;
align-items: center;
border-top: 1px solid var(--second);
background-color: white;
border-top: 1px solid var(--sk-theme-2);
background-color: var(--sk-back-4);
}
button {
@ -29,15 +29,15 @@
width: 4em;
height: 1em;
padding: 0.3em 0.4em;
border-radius: var(--border-r);
border-radius: var(--sk-border-radius);
line-height: 1em;
box-sizing: content-box;
color: #888;
border: 1px solid var(--back-light);
color: var(--sk-text-3);
border: 1px solid var(--sk-back-3);
}
.selected {
background-color: var(--prime);
background-color: var(--sk-theme-1);
color: white;
}
</style>

@ -0,0 +1,72 @@
// @ts-check
import fs from 'node:fs';
const base = '../../site/content/examples/';
/**
* @returns {import('./types').ExamplesData}
*/
export function get_examples_data() {
const examples = [];
for (const subdir of fs.readdirSync(base)) {
// Exclude embeds
if (subdir.endsWith('99-embeds')) continue;
const section = {
title: '', // Initialise with empty
slug: subdir.split('-').slice(1).join('-'),
examples: [],
};
if (!(fs.statSync(`${base}/${subdir}`).isDirectory() || subdir.endsWith('meta.json'))) continue;
if (!subdir.endsWith('meta.json'))
section.title = JSON.parse(fs.readFileSync(`${base}/${subdir}/meta.json`, 'utf-8')).title;
for (const section_dir of fs.readdirSync(`${base}/${subdir}`)) {
const match = /\d{2}-(.+)/.exec(section_dir);
if (!match) continue;
const slug = match[1];
const example_base_dir = `${base}/${subdir}/${section_dir}`;
// Get title for
const example_title = JSON.parse(
fs.readFileSync(`${example_base_dir}/meta.json`, 'utf-8')
).title;
const files = [];
for (const file of fs
.readdirSync(example_base_dir)
.filter((file) => !file.endsWith('meta.json'))) {
files.push({
filename: file,
type: file.split('.').at(-1),
content: fs.readFileSync(`${example_base_dir}/${file}`, 'utf-8'),
});
}
section.examples.push({ title: example_title, slug, files });
}
examples.push(section);
}
return examples;
}
/**
* @param {import('./types').ExamplesData} examples_data
* @returns {import('./types').ExamplesList}
*/
export function get_examples_list(examples_data) {
return examples_data.map((section) => ({
title: section.title,
examples: section.examples.map((example) => ({
title: example.title,
slug: example.slug,
})),
}));
}

@ -0,0 +1,11 @@
/**
* @param {import('./types').ExamplesData} examples_data
* @param {string} slug
*/
export function get_example(examples_data, slug) {
const example = examples_data
.find((section) => section.examples.find((example) => example.slug === slug))
?.examples.find((example) => example.slug === slug);
return example;
}

@ -0,0 +1,25 @@
export type ExamplesData = {
title: string;
slug: string;
examples: {
title: string;
slug: string;
files: {
content: string;
type: 'svelte' | 'js';
filename: string;
}[];
}[];
}[];
export interface Example {
title: string;
slug: string;
}
export interface ExampleSection {
title: string;
examples: Example[];
}
export type ExamplesList = ExampleSection[];

@ -1,6 +0,0 @@
import { PUBLIC_API_BASE } from '$env/static/public';
export async function load({ fetch }) {
const examples = await fetch(`${PUBLIC_API_BASE}/docs/svelte/examples`).then((r) => r.json());
return { examples };
}

@ -1,10 +0,0 @@
<script>
import { setContext } from 'svelte';
/** @type {import('./$types').PageData} */
export let data;
setContext('examples', { sections: data.examples });
</script>
<slot />

@ -1,5 +1,7 @@
import { redirect } from '@sveltejs/kit';
export const prerender = true;
/** @type {import('./$types').PageLoad} */
export function load() {
throw redirect(301, 'examples/hello-world');

@ -1,17 +0,0 @@
import { PUBLIC_API_BASE } from '$env/static/public';
/** @type {import('./$types').PageLoad} */
export async function load({ fetch, params, setHeaders }) {
const example = await fetch(`${PUBLIC_API_BASE}/docs/svelte/examples/${params.slug}`, {
credentials: 'omit'
});
setHeaders({
'cache-control': 'public, max-age=60'
});
return {
example: await example.json(),
slug: params.slug
};
}

@ -0,0 +1,17 @@
import { get_example } from '$lib/server/examples';
import { get_examples_data, get_examples_list } from '$lib/server/examples/get-examples';
export const prerender = true;
export async function load({ params }) {
const examples_data = get_examples_data();
const examples_list = get_examples_list(examples_data);
const example = get_example(examples_data, params.slug);
return {
examples_list,
example,
slug: params.slug,
};
}

@ -1,16 +1,12 @@
<!-- FIXME sometimes it adds a trailing slash when landing -->
<script>
import { getContext } from 'svelte';
// @ts-check
import { navigating } from '$app/stores';
import Repl from '@sveltejs/repl';
import ScreenToggle from '$lib/components/ScreenToggle.svelte';
import {
mapbox_setup, // see site/content/examples/15-context/00-context-api
svelteUrl,
} from '../../../config';
import TableOfContents from './_TableOfContents.svelte';
import Repl from '@sveltejs/repl';
import { mapbox_setup, svelteUrl } from '../../../config';
import TableOfContents from './TableOfContents.svelte';
/** @type {import('./$types').PageData} */
export let data;
/** @type {number} */
@ -19,10 +15,8 @@
/** @type {import('@sveltejs/repl').default} */
let repl;
const { sections } = getContext('examples');
const clone = (file) => ({
name: file.name.replace(/.\w+$/, ''),
name: file.filename.replace(/.\w+$/, ''),
type: file.type,
source: file.content,
});
@ -34,7 +28,7 @@
</script>
<svelte:head>
<title>{data.example.name} {data.example.name ? '•' : ''} Svelte Examples</title>
<title>{data.example.title} {data.example.title ? '•' : ''} Svelte Examples</title>
<meta name="twitter:title" content="Svelte examples" />
<meta name="twitter:description" content="Cybernetically enhanced web apps" />
@ -44,7 +38,11 @@
<h1 class="visually-hidden">Examples</h1>
<div class="examples-container" bind:clientWidth={width}>
<div class="viewport offset-{offset}">
<TableOfContents {sections} active_section={data.example.slug} isLoading={!!$navigating} />
<TableOfContents
sections={data.examples_list}
active_section={data.example.slug}
isLoading={!!$navigating}
/>
<div class="repl-container" class:loading={$navigating}>
<Repl
bind:this={repl}

@ -15,7 +15,7 @@
<ul class="examples-toc">
{#each sections as section}
<li>
<span class="section-title">{section.name}</span>
<span class="section-title">{section.title}</span>
{#each section.examples as example}
<div class="row" class:active={example.slug === active_section} class:loading={isLoading}>
@ -31,7 +31,7 @@
src="/examples/thumbnails/{example.slug}.jpg"
/>
<span>{example.name}</span>
<span>{example.title}</span>
</a>
{#if example.slug === active_section}
<a bind:this={active_el} href="/repl/{example.slug}" class="repl-link">REPL</a>
@ -109,7 +109,7 @@
}
.thumbnail {
background-color: var(--sk-back-1);
background-color: #fff;
object-fit: contain;
width: 5rem;
height: 5rem;

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.5 KiB

Loading…
Cancel
Save