mirror of https://github.com/sveltejs/svelte
feat: multi-page docs (#8253)
* Rename markdown to blog * Multi-page docs * Ignore missing ID for now * heading * Add code snippets(temporary) * /docs redirects to introduction * Remove ukraine petition * OnThisPage * Remove console.log * Update deps * Fix accessibility warning * Update site-kitpull/8274/head
parent
7afee765fc
commit
69349319eb
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,73 @@
|
|||||||
|
import fs from 'fs';
|
||||||
|
import { extract_frontmatter } from '../markdown';
|
||||||
|
import { transform } from './marked';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @returns {import('./types').BlogPostSummary[]}
|
||||||
|
*/
|
||||||
|
export function get_index() {
|
||||||
|
return fs
|
||||||
|
.readdirSync('content/blog')
|
||||||
|
.reverse()
|
||||||
|
.map((file) => {
|
||||||
|
if (!file.endsWith('.md')) return;
|
||||||
|
|
||||||
|
const { date, slug } = get_date_and_slug(file);
|
||||||
|
|
||||||
|
const content = fs.readFileSync(`content/blog/${file}`, 'utf-8');
|
||||||
|
const { metadata } = extract_frontmatter(content);
|
||||||
|
|
||||||
|
return {
|
||||||
|
slug,
|
||||||
|
date,
|
||||||
|
title: metadata.title,
|
||||||
|
description: metadata.description,
|
||||||
|
draft: !!metadata.draft,
|
||||||
|
};
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {string} slug
|
||||||
|
* @returns {import('./types').BlogPost}
|
||||||
|
*/
|
||||||
|
export function get_post(slug) {
|
||||||
|
for (const file of fs.readdirSync('content/blog')) {
|
||||||
|
if (!file.endsWith('.md')) continue;
|
||||||
|
if (file.slice(11, -3) !== slug) continue;
|
||||||
|
|
||||||
|
const { date, date_formatted } = get_date_and_slug(file);
|
||||||
|
|
||||||
|
const content = fs.readFileSync(`content/blog/${file}`, 'utf-8');
|
||||||
|
const { metadata, body } = extract_frontmatter(content);
|
||||||
|
|
||||||
|
return {
|
||||||
|
date,
|
||||||
|
date_formatted,
|
||||||
|
title: metadata.title,
|
||||||
|
description: metadata.description,
|
||||||
|
author: {
|
||||||
|
name: metadata.author,
|
||||||
|
url: metadata.authorURL,
|
||||||
|
},
|
||||||
|
draft: !!metadata.draft,
|
||||||
|
content: transform(body),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @param {string} filename */
|
||||||
|
function get_date_and_slug(filename) {
|
||||||
|
const match = /^(\d{4}-\d{2}-\d{2})-(.+)\.md$/.exec(filename);
|
||||||
|
if (!match) throw new Error(`Invalid filename for blog: '${filename}'`);
|
||||||
|
|
||||||
|
const [, date, slug] = match;
|
||||||
|
const [y, m, d] = date.split('-');
|
||||||
|
const date_formatted = `${months[+m - 1]} ${+d} ${y}`;
|
||||||
|
|
||||||
|
return { date, date_formatted, slug };
|
||||||
|
}
|
||||||
|
|
||||||
|
const months = 'Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec'.split(' ');
|
||||||
|
|
||||||
|
function format_date(date) {}
|
@ -0,0 +1,13 @@
|
|||||||
|
export interface Section {
|
||||||
|
title: string;
|
||||||
|
slug: string;
|
||||||
|
sections?: Section[];
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface Type {
|
||||||
|
name: string;
|
||||||
|
comment: string;
|
||||||
|
snippet: string;
|
||||||
|
bullets: string[];
|
||||||
|
children: Type[];
|
||||||
|
}
|
@ -1,9 +1,9 @@
|
|||||||
import { get_index } from '$lib/server/markdown';
|
import { get_index } from '$lib/server/blog';
|
||||||
|
|
||||||
export const prerender = true;
|
export const prerender = true;
|
||||||
|
|
||||||
export async function load() {
|
export async function load() {
|
||||||
return {
|
return {
|
||||||
posts: get_index()
|
posts: get_index(),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,23 @@
|
|||||||
|
import { extract_frontmatter } from '$lib/server/markdown';
|
||||||
|
import fs from 'fs';
|
||||||
|
import { base } from '$app/paths';
|
||||||
|
|
||||||
|
export const prerender = true;
|
||||||
|
|
||||||
|
const base_dir = '../../site/content/docs/';
|
||||||
|
|
||||||
|
/** @type {import('./$types').LayoutServerLoad} */
|
||||||
|
export function load() {
|
||||||
|
const sections = fs.readdirSync(base_dir).map((file) => {
|
||||||
|
const { title } = extract_frontmatter(fs.readFileSync(`${base_dir}/${file}`, 'utf-8')).metadata;
|
||||||
|
|
||||||
|
return {
|
||||||
|
title,
|
||||||
|
path: `${base}/docs/${file.slice(3, -3)}`,
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
return {
|
||||||
|
sections,
|
||||||
|
};
|
||||||
|
}
|
@ -0,0 +1,337 @@
|
|||||||
|
<script>
|
||||||
|
import { page } from '$app/stores';
|
||||||
|
import Contents from './Contents.svelte';
|
||||||
|
import '@sveltejs/site-kit/styles/code.css';
|
||||||
|
|
||||||
|
/** @type {import('./$types').LayoutServerData}*/
|
||||||
|
export let data;
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<div class="container">
|
||||||
|
<div class="page content">
|
||||||
|
<h1>{data.sections.find((val) => val.path === $page.url.pathname)?.title}</h1>
|
||||||
|
<slot />
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="toc-container">
|
||||||
|
<Contents contents={data.sections} />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
.container {
|
||||||
|
--sidebar-menu-width: 20rem;
|
||||||
|
--sidebar-width: var(--sidebar-menu-width);
|
||||||
|
--ts-toggle-height: 4.2rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.page {
|
||||||
|
--on-this-page-display: none;
|
||||||
|
padding: var(--sk-page-padding-top) var(--sk-page-padding-side);
|
||||||
|
}
|
||||||
|
|
||||||
|
.page :global(hr) {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.content {
|
||||||
|
width: 100%;
|
||||||
|
margin: 0;
|
||||||
|
padding: var(--sk-page-padding-top) var(--sk-page-padding-side);
|
||||||
|
tab-size: 2;
|
||||||
|
-moz-tab-size: 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (min-width: 832px) {
|
||||||
|
/* can't use vars in @media :( */
|
||||||
|
.content {
|
||||||
|
padding-left: calc(var(--sidebar-width) + var(--sk-page-padding-side));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.content :global(h1) {
|
||||||
|
font-size: 3.2rem;
|
||||||
|
margin: 0 0 0.5em 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.content :global(h2) {
|
||||||
|
margin-top: 8rem;
|
||||||
|
padding: 2rem 1.6rem 2rem 0.2rem;
|
||||||
|
border-bottom: 1px solid hsl(0, 0%, 87%, 0.2);
|
||||||
|
line-height: 1;
|
||||||
|
font-size: var(--sk-text-m);
|
||||||
|
letter-spacing: 0.05em;
|
||||||
|
text-transform: uppercase;
|
||||||
|
}
|
||||||
|
|
||||||
|
.content :global(section):first-of-type > :global(h2) {
|
||||||
|
margin-top: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.content :global(h4) {
|
||||||
|
margin: 2em 0 1em 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.content :global(.offset-anchor) {
|
||||||
|
position: relative;
|
||||||
|
display: block;
|
||||||
|
top: calc(-1 * var(--sk-page-padding-top));
|
||||||
|
width: 0;
|
||||||
|
height: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.content :global(.anchor) {
|
||||||
|
position: absolute;
|
||||||
|
display: block;
|
||||||
|
background: url(../icons/link.svg) 0 50% no-repeat;
|
||||||
|
background-size: 1em 1em;
|
||||||
|
width: 1.4em;
|
||||||
|
height: 1em;
|
||||||
|
left: -1.3em;
|
||||||
|
bottom: 0.3rem;
|
||||||
|
opacity: 0;
|
||||||
|
transition: opacity 0.2s;
|
||||||
|
user-select: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.content :global(h2) :global(.anchor) {
|
||||||
|
bottom: 4rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.content :global(h3) :global(.anchor) {
|
||||||
|
bottom: 1rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (min-width: 400px) {
|
||||||
|
.content :global(h1) {
|
||||||
|
font-size: 4.2rem;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (min-width: 768px) {
|
||||||
|
.content :global(h1) {
|
||||||
|
font-size: 5.4rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.content :global(.anchor:focus),
|
||||||
|
.content :global(h2):hover :global(.anchor),
|
||||||
|
.content :global(h3):hover :global(.anchor),
|
||||||
|
.content :global(h4):hover :global(.anchor),
|
||||||
|
.content :global(h5):hover :global(.anchor),
|
||||||
|
.content :global(h6):hover :global(.anchor) {
|
||||||
|
opacity: 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.content :global(h3),
|
||||||
|
.content :global(h3 > code) {
|
||||||
|
margin: 6.4rem 0 1rem 0;
|
||||||
|
padding: 0 0 1rem 0;
|
||||||
|
color: var(--sk-text-2);
|
||||||
|
max-width: var(--sk-line-max-width);
|
||||||
|
border-bottom: 1px solid hsl(0, 0%, 87%, 0.2);
|
||||||
|
background: transparent;
|
||||||
|
line-height: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.content :global(h3):first-child {
|
||||||
|
border: none;
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* avoid doubled border-top */
|
||||||
|
.content :global(h3 > code) {
|
||||||
|
border-radius: 0 0 0 0;
|
||||||
|
border: none;
|
||||||
|
font-size: inherit;
|
||||||
|
}
|
||||||
|
|
||||||
|
.content :global(h4),
|
||||||
|
.content :global(h4 > code) {
|
||||||
|
font-family: inherit;
|
||||||
|
font-weight: 600;
|
||||||
|
font-size: 2.4rem;
|
||||||
|
color: var(--sk-text-2);
|
||||||
|
margin: 6.4rem 0 1.6rem 0;
|
||||||
|
padding-left: 0;
|
||||||
|
background: transparent;
|
||||||
|
line-height: 1;
|
||||||
|
padding-top: 0;
|
||||||
|
top: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.content :global(h4::before) {
|
||||||
|
display: inline;
|
||||||
|
content: ' ';
|
||||||
|
block-size: var(--sk-nav-height);
|
||||||
|
margin-block-start: calc(-1 * var(--sk-nav-height));
|
||||||
|
}
|
||||||
|
|
||||||
|
.content :global(h4 > em) {
|
||||||
|
opacity: 0.7;
|
||||||
|
}
|
||||||
|
|
||||||
|
.content :global(h4 > .anchor) {
|
||||||
|
top: 0.05em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.content :global(h5) {
|
||||||
|
font-size: 2.4rem;
|
||||||
|
margin: 2em 0 0.5em 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.content :global(code) {
|
||||||
|
padding: 0.4rem;
|
||||||
|
margin: 0 0.2rem;
|
||||||
|
top: -0.1rem;
|
||||||
|
background: var(--sk-back-4);
|
||||||
|
}
|
||||||
|
|
||||||
|
.content :global(pre) :global(code) {
|
||||||
|
padding: 0;
|
||||||
|
margin: 0;
|
||||||
|
top: 0;
|
||||||
|
background: transparent;
|
||||||
|
}
|
||||||
|
|
||||||
|
.content :global(pre) {
|
||||||
|
margin: 0 0 2rem 0;
|
||||||
|
width: 100%;
|
||||||
|
max-width: var(--sk-line-max-width);
|
||||||
|
padding: 1rem 1rem;
|
||||||
|
box-shadow: inset 1px 1px 6px hsla(205.7, 63.6%, 30.8%, 0.06);
|
||||||
|
}
|
||||||
|
|
||||||
|
.content :global(.icon) {
|
||||||
|
width: 2rem;
|
||||||
|
height: 2rem;
|
||||||
|
stroke: currentColor;
|
||||||
|
stroke-width: 2;
|
||||||
|
stroke-linecap: round;
|
||||||
|
stroke-linejoin: round;
|
||||||
|
fill: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.content :global(table) {
|
||||||
|
margin: 0 0 2em 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.content :global(section) :global(p) {
|
||||||
|
max-width: var(--sk-line-max-width);
|
||||||
|
margin: 1em 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.content :global(small) {
|
||||||
|
font-size: var(--sk-text-s);
|
||||||
|
float: right;
|
||||||
|
pointer-events: all;
|
||||||
|
color: var(--sk-theme-1);
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
.content :global(blockquote) {
|
||||||
|
color: var(--sk-text-1);
|
||||||
|
background-color: rgba(255, 62, 0, 0.1);
|
||||||
|
border-left: 4px solid var(--sk-theme-1-variant);
|
||||||
|
padding: 1rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.content :global(blockquote) :global(:first-child) {
|
||||||
|
margin-top: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.content :global(blockquote) :global(:last-child) {
|
||||||
|
margin-bottom: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.content :global(blockquote) :global(code) {
|
||||||
|
background: var(--sk-code-bg);
|
||||||
|
}
|
||||||
|
|
||||||
|
.content :global(section) :global(a):hover {
|
||||||
|
text-decoration: underline;
|
||||||
|
}
|
||||||
|
|
||||||
|
.content :global(section) :global(a) :global(code) {
|
||||||
|
color: inherit;
|
||||||
|
background: rgba(255, 62, 0, 0.1) !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* this replaces the offset-anchor hack, which we should remove from this CSS
|
||||||
|
once https://github.com/sveltejs/action-deploy-docs/issues/1 is closed */
|
||||||
|
.content :global(h2[id]),
|
||||||
|
.content :global(h3[id]) {
|
||||||
|
padding-top: 10rem;
|
||||||
|
margin-top: -2rem;
|
||||||
|
border-top: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* .content :global(h2[id])::after {
|
||||||
|
content: '';
|
||||||
|
position: absolute;
|
||||||
|
width: 100%;
|
||||||
|
left: 0;
|
||||||
|
top: 8rem;
|
||||||
|
height: 2px;
|
||||||
|
background: #ddd;
|
||||||
|
} */
|
||||||
|
|
||||||
|
.toc-container {
|
||||||
|
background: var(--sk-back-3);
|
||||||
|
}
|
||||||
|
/*
|
||||||
|
.ts-toggle {
|
||||||
|
width: 100%;
|
||||||
|
border-top: 1px solid var(--sk-back-4);
|
||||||
|
background-color: var(--sk-back-3);
|
||||||
|
} */
|
||||||
|
|
||||||
|
@media (min-width: 832px) {
|
||||||
|
.toc-container {
|
||||||
|
width: var(--sidebar-width);
|
||||||
|
height: calc(100vh - var(--sk-nav-height) - var(--ts-toggle-height));
|
||||||
|
position: fixed;
|
||||||
|
left: 0;
|
||||||
|
top: var(--sk-nav-height);
|
||||||
|
overflow-x: hidden;
|
||||||
|
overflow-y: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
.toc-container::before {
|
||||||
|
content: '';
|
||||||
|
position: fixed;
|
||||||
|
width: 0;
|
||||||
|
height: 100%;
|
||||||
|
top: 0;
|
||||||
|
left: calc(var(--sidebar-width) - 1px);
|
||||||
|
border-right: 1px solid var(--sk-back-5);
|
||||||
|
}
|
||||||
|
|
||||||
|
.page {
|
||||||
|
padding-left: calc(var(--sidebar-width) + var(--sk-page-padding-side));
|
||||||
|
}
|
||||||
|
|
||||||
|
/* .ts-toggle {
|
||||||
|
position: fixed;
|
||||||
|
width: var(--sidebar-width);
|
||||||
|
bottom: 0;
|
||||||
|
z-index: 1;
|
||||||
|
margin-right: 0;
|
||||||
|
border-right: 1px solid var(--sk-back-5);
|
||||||
|
} */
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (min-width: 1200px) {
|
||||||
|
.container {
|
||||||
|
--sidebar-width: max(20rem, 18vw);
|
||||||
|
}
|
||||||
|
|
||||||
|
.page {
|
||||||
|
--on-this-page-display: block;
|
||||||
|
padding: var(--sk-page-padding-top) calc(var(--sidebar-width) + var(--sk-page-padding-side));
|
||||||
|
margin: 0 auto;
|
||||||
|
max-width: var(--sk-line-max-width);
|
||||||
|
box-sizing: content-box;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
@ -1,12 +1,7 @@
|
|||||||
import { PUBLIC_API_BASE } from '$env/static/public';
|
import { base } from '$app/paths';
|
||||||
|
import { redirect } from '@sveltejs/kit';
|
||||||
|
|
||||||
/** @type {import('./$types').PageLoad} */
|
/** @type {import('./$types').PageLoad} */
|
||||||
export async function load({ fetch, setHeaders }) {
|
export async function load() {
|
||||||
const sections = await (await fetch(`${PUBLIC_API_BASE}/docs/svelte/docs?content`)).json();
|
throw redirect(307, `${base}/docs/introduction`);
|
||||||
|
|
||||||
setHeaders({
|
|
||||||
'cache-control': 'public, max-age=60'
|
|
||||||
});
|
|
||||||
|
|
||||||
return { sections };
|
|
||||||
}
|
}
|
||||||
|
@ -1,43 +0,0 @@
|
|||||||
<script>
|
|
||||||
// import { Contents, Main, Section } from '@sveltejs/site-kit/docs';
|
|
||||||
|
|
||||||
/** @type {import('./$types').PageData} */
|
|
||||||
export let data;
|
|
||||||
|
|
||||||
let path;
|
|
||||||
|
|
||||||
$: contents = data.sections.map((section) => ({
|
|
||||||
path: `/docs#${section.slug}`,
|
|
||||||
title: section.title,
|
|
||||||
sections: section.sections.map((subsection) => ({
|
|
||||||
path: `/docs#${subsection.slug}`,
|
|
||||||
title: subsection.title,
|
|
||||||
sections: subsection.sections.map((subsection) => ({
|
|
||||||
path: `/docs#${subsection.slug}`,
|
|
||||||
title: subsection.title,
|
|
||||||
})),
|
|
||||||
})),
|
|
||||||
}));
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<svelte:head>
|
|
||||||
<title>Docs • Svelte</title>
|
|
||||||
|
|
||||||
<meta name="twitter:title" content="Svelte docs" />
|
|
||||||
<meta name="twitter:description" content="Complete documentation for Svelte" />
|
|
||||||
<meta name="Description" content="Complete documentation for Svelte" />
|
|
||||||
</svelte:head>
|
|
||||||
|
|
||||||
<!-- <Main bind:path>
|
|
||||||
<h1>Documentation</h1>
|
|
||||||
|
|
||||||
{#each data.sections as section}
|
|
||||||
<Section
|
|
||||||
{section}
|
|
||||||
edit="https://github.com/sveltejs/svelte/edit/master/site/content/docs/{section.file}"
|
|
||||||
base="/docs"
|
|
||||||
/>
|
|
||||||
{/each}
|
|
||||||
</Main>
|
|
||||||
|
|
||||||
<Contents {contents} {path} /> -->
|
|
@ -0,0 +1,137 @@
|
|||||||
|
<script>
|
||||||
|
import { page } from '$app/stores';
|
||||||
|
|
||||||
|
/** @type {{title: string, path: string}[]} */
|
||||||
|
export let contents;
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<nav aria-label="Docs">
|
||||||
|
<ul class="sidebar">
|
||||||
|
{#each contents as { title, path }}
|
||||||
|
<li>
|
||||||
|
<a
|
||||||
|
data-sveltekit-preload-data
|
||||||
|
class="page"
|
||||||
|
class:active={path === $page.url.pathname}
|
||||||
|
href={path}
|
||||||
|
>
|
||||||
|
{title}
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
|
{/each}
|
||||||
|
</ul>
|
||||||
|
</nav>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
nav {
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
color: var(--sk-text-3);
|
||||||
|
|
||||||
|
display: grid;
|
||||||
|
justify-content: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sidebar {
|
||||||
|
padding: var(--sk-page-padding-top) 0 var(--sk-page-padding-top) 3.2rem;
|
||||||
|
font-family: var(--sk-font);
|
||||||
|
height: 100%;
|
||||||
|
bottom: auto;
|
||||||
|
width: 100%;
|
||||||
|
columns: 2;
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
li {
|
||||||
|
display: block;
|
||||||
|
line-height: 1.2;
|
||||||
|
margin: 0;
|
||||||
|
margin-bottom: 1rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
li:last-child {
|
||||||
|
margin-bottom: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
a {
|
||||||
|
position: relative;
|
||||||
|
transition: color 0.2s;
|
||||||
|
border-bottom: none;
|
||||||
|
padding: 0;
|
||||||
|
color: var(--sk-text-3);
|
||||||
|
user-select: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.page {
|
||||||
|
display: block;
|
||||||
|
font-size: 1.6rem;
|
||||||
|
font-family: var(--sk-font);
|
||||||
|
padding-bottom: 0.6em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.active {
|
||||||
|
font-weight: 700;
|
||||||
|
color: var(--sk-text-1);
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (min-width: 600px) {
|
||||||
|
.sidebar {
|
||||||
|
columns: 2;
|
||||||
|
padding-left: var(--sk-page-padding-side);
|
||||||
|
padding-right: var(--sk-page-padding-side);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (min-width: 700px) {
|
||||||
|
.sidebar {
|
||||||
|
/* columns: 3; */
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (min-width: 832px) {
|
||||||
|
.sidebar {
|
||||||
|
columns: 1;
|
||||||
|
padding-left: 3.2rem;
|
||||||
|
padding-right: 0;
|
||||||
|
width: var(--sidebar-menu-width);
|
||||||
|
margin: 0 0 0 auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
nav {
|
||||||
|
min-height: calc(100vh - var(--ts-toggle-height));
|
||||||
|
}
|
||||||
|
|
||||||
|
nav::after {
|
||||||
|
content: '';
|
||||||
|
position: fixed;
|
||||||
|
left: 0;
|
||||||
|
bottom: var(--ts-toggle-height);
|
||||||
|
width: calc(var(--sidebar-width) - 1px);
|
||||||
|
height: 2em;
|
||||||
|
pointer-events: none;
|
||||||
|
background: linear-gradient(
|
||||||
|
to bottom,
|
||||||
|
hsla(var(--sk-back-3-hsl), 0) 0%,
|
||||||
|
hsla(var(--sk-back-3-hsl), 0.7) 50%,
|
||||||
|
hsl(var(--sk-back-3-hsl)) 100%
|
||||||
|
);
|
||||||
|
background-repeat: no-repeat;
|
||||||
|
background-size: calc(100% - 3rem) 100%; /* cover text but not scrollbar */
|
||||||
|
}
|
||||||
|
|
||||||
|
.active::after {
|
||||||
|
--size: 1rem;
|
||||||
|
content: '';
|
||||||
|
position: absolute;
|
||||||
|
width: var(--size);
|
||||||
|
height: var(--size);
|
||||||
|
top: -0.1rem;
|
||||||
|
right: calc(-0.5 * var(--size));
|
||||||
|
background-color: var(--sk-back-1);
|
||||||
|
border-left: 1px solid var(--sk-back-5);
|
||||||
|
border-bottom: 1px solid var(--sk-back-5);
|
||||||
|
transform: translateY(0.2rem) rotate(45deg);
|
||||||
|
z-index: 2;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
@ -0,0 +1,24 @@
|
|||||||
|
import fs from 'fs';
|
||||||
|
// import { read_file } from '$lib/server/docs';
|
||||||
|
import { error } from '@sveltejs/kit';
|
||||||
|
import { read_file } from '$lib/server/docs';
|
||||||
|
|
||||||
|
export const prerender = true;
|
||||||
|
|
||||||
|
const base = '../../site/content/docs/';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* ASSUMPTION FOR FUTURE: This assumes the directory structure of docs is flat. AKA, no nested folders
|
||||||
|
*/
|
||||||
|
/** @type {import('./$types').PageServerLoad} */
|
||||||
|
export async function load({ params }) {
|
||||||
|
for (const file of fs.readdirSync(`${base}`)) {
|
||||||
|
if (file.slice(3, -3) === params.slug) {
|
||||||
|
return {
|
||||||
|
page: await read_file(file),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
throw error(404);
|
||||||
|
}
|
@ -0,0 +1,16 @@
|
|||||||
|
<script>
|
||||||
|
import OnThisPage from './OnThisPage.svelte';
|
||||||
|
|
||||||
|
/** @type {import('./$types').PageData}*/
|
||||||
|
export let data;
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<svelte:head>
|
||||||
|
<title>{data.page.title} - Svelte</title>
|
||||||
|
</svelte:head>
|
||||||
|
|
||||||
|
{@html data.page.content}
|
||||||
|
|
||||||
|
{#if data.page.sections.length !== 0}
|
||||||
|
<OnThisPage details={data.page} />
|
||||||
|
{/if}
|
@ -0,0 +1,141 @@
|
|||||||
|
<script>
|
||||||
|
import { onMount } from 'svelte';
|
||||||
|
import { afterNavigate } from '$app/navigation';
|
||||||
|
import { base } from '$app/paths';
|
||||||
|
import { page } from '$app/stores';
|
||||||
|
|
||||||
|
/** @type {import('./$types').PageData['page']} */
|
||||||
|
export let details;
|
||||||
|
|
||||||
|
/** @type {string} */
|
||||||
|
let hash = '';
|
||||||
|
|
||||||
|
/** @type {number} */
|
||||||
|
let height = 0;
|
||||||
|
|
||||||
|
/** @type {HTMLElement} */
|
||||||
|
let content;
|
||||||
|
|
||||||
|
/** @type {NodeListOf<HTMLElement>} */
|
||||||
|
let headings;
|
||||||
|
|
||||||
|
/** @type {number[]} */
|
||||||
|
let positions = [];
|
||||||
|
|
||||||
|
onMount(async () => {
|
||||||
|
await document.fonts.ready;
|
||||||
|
|
||||||
|
update();
|
||||||
|
highlight();
|
||||||
|
});
|
||||||
|
|
||||||
|
afterNavigate(() => {
|
||||||
|
update();
|
||||||
|
highlight();
|
||||||
|
});
|
||||||
|
|
||||||
|
function update() {
|
||||||
|
content = document.querySelector('.content');
|
||||||
|
const { top } = content.getBoundingClientRect();
|
||||||
|
|
||||||
|
headings = content.querySelectorAll('h2[id]');
|
||||||
|
|
||||||
|
positions = Array.from(headings).map((heading) => {
|
||||||
|
const style = getComputedStyle(heading);
|
||||||
|
return heading.getBoundingClientRect().top - parseFloat(style.scrollMarginTop) - top;
|
||||||
|
});
|
||||||
|
|
||||||
|
height = window.innerHeight;
|
||||||
|
}
|
||||||
|
|
||||||
|
function highlight() {
|
||||||
|
const { top, bottom } = content.getBoundingClientRect();
|
||||||
|
let i = headings.length;
|
||||||
|
|
||||||
|
while (i--) {
|
||||||
|
if (bottom - height < 50 || positions[i] + top < 100) {
|
||||||
|
const heading = headings[i];
|
||||||
|
hash = `#${heading.id}`;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
hash = '';
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @param {URL} url */
|
||||||
|
function select(url) {
|
||||||
|
// belt...
|
||||||
|
setTimeout(() => {
|
||||||
|
hash = url.hash;
|
||||||
|
});
|
||||||
|
|
||||||
|
// ...and braces
|
||||||
|
window.addEventListener(
|
||||||
|
'scroll',
|
||||||
|
() => {
|
||||||
|
hash = url.hash;
|
||||||
|
},
|
||||||
|
{ once: true }
|
||||||
|
);
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<svelte:window on:scroll={highlight} on:resize={update} on:hashchange={() => select($page.url)} />
|
||||||
|
|
||||||
|
<aside class="on-this-page">
|
||||||
|
<h2>On this page</h2>
|
||||||
|
<nav>
|
||||||
|
<ul>
|
||||||
|
<li><a href="{base}/docs/{details.slug}" class:active={hash === ''}>{details.title}</a></li>
|
||||||
|
{#each details.sections as { title, slug }}
|
||||||
|
<li><a href={`#${slug}`} class:active={`#${slug}` === hash}>{title}</a></li>
|
||||||
|
{/each}
|
||||||
|
</ul>
|
||||||
|
</nav>
|
||||||
|
</aside>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
.on-this-page {
|
||||||
|
display: var(--on-this-page-display);
|
||||||
|
position: fixed;
|
||||||
|
padding: 0 var(--sk-page-padding-side) 0 0;
|
||||||
|
width: min(280px, calc(var(--sidebar-width) - var(--sk-page-padding-side)));
|
||||||
|
/* top: calc(var(--sk-page-padding-top) + var(--sk-nav-height)); */
|
||||||
|
top: var(--sk-nav-height);
|
||||||
|
left: calc(100vw - (var(--sidebar-width)));
|
||||||
|
}
|
||||||
|
|
||||||
|
h2 {
|
||||||
|
text-transform: uppercase;
|
||||||
|
font-size: 1.4rem;
|
||||||
|
font-weight: 400;
|
||||||
|
line-height: 0;
|
||||||
|
margin: 0 0 1rem 0 !important;
|
||||||
|
padding: 0 0 0 0.6rem;
|
||||||
|
color: var(--sk-text-3);
|
||||||
|
}
|
||||||
|
|
||||||
|
ul {
|
||||||
|
list-style: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
a {
|
||||||
|
display: block;
|
||||||
|
padding: 0.3rem 0.5rem;
|
||||||
|
color: var(--sk-text-3);
|
||||||
|
border-left: 2px solid transparent;
|
||||||
|
|
||||||
|
font-size: 1.3rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
a:hover {
|
||||||
|
text-decoration: none;
|
||||||
|
background: var(--sk-back-3);
|
||||||
|
}
|
||||||
|
|
||||||
|
a.active {
|
||||||
|
background: var(--sk-back-3);
|
||||||
|
border-left-color: var(--sk-theme-1);
|
||||||
|
}
|
||||||
|
</style>
|
@ -1,3 +1,8 @@
|
|||||||
{
|
{
|
||||||
"extends": "./.svelte-kit/tsconfig.json",
|
"extends": "./.svelte-kit/tsconfig.json",
|
||||||
|
"compilerOptions": {
|
||||||
|
"allowJs": true,
|
||||||
|
"checkJs": true,
|
||||||
|
"allowSyntheticDefaultImports": true
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in new issue