feat: Better docs nav ()

* DocsNav

* Push

* Nav title on each page

* Install jridgewell sourcemap codec. Why it breaking suddenly

* Use theme store

* Use $nav_title

* use $page.data.nav_title

* Disable global prerendering

* Fix Suppprters section

* use new method

* Initially hidden nav functionality

* Minor fixes

* Simplify into one single nav

* Accomodate to the bottom nav

* Minor fixes

* nit

* Add selected to other pages as well

* New way of passing to navbar

* Code cleanup

* Directly pass list instead of components

* 14 days

* Fix comment

* Discord icon

* Bump site-kit finally
pull/8731/head
Puru Vijay 2 years ago committed by GitHub
parent ad9a672171
commit aa5bb4a3db
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

@ -36,8 +36,6 @@ Svelte uses the `export` keyword to mark a variable declaration as a _property_
</script>
```
---
You can specify a default initial value for a prop. It will be used if the component's consumer doesn't specify the prop on the component (or if its initial value is `undefined`) when instantiating the component. Note that if the values of props are subsequently updated, then any prop whose value is not specified will be set to `undefined` (rather than its initial value).
In development mode (see the [compiler options](/docs/svelte-compiler#svelte-compile)), a warning will be printed if no default initial value is provided and the consumer does not specify a value. To squelch this warning, ensure that a default initial value is specified, even if it is `undefined`.
@ -130,8 +128,6 @@ Svelte's `<script>` blocks are run only when the component is created, so assign
### 3. `$:` marks a statement as reactive
---
Any top-level statement (i.e. not inside a block or a function) can be made reactive by prefixing it with the `$:` [JS label syntax](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/label). Reactive statements run after other script code and before the component markup is rendered, whenever the values that they depend on have changed.
```svelte

@ -191,8 +191,8 @@ importers:
specifier: ^1.20.0
version: 1.20.0(svelte@packages+svelte)(vite@4.3.9)
'@sveltejs/site-kit':
specifier: ^5.2.1
version: 5.2.1(@sveltejs/kit@1.20.0)(svelte@packages+svelte)
specifier: 6.0.0-next.0
version: 6.0.0-next.0(@sveltejs/kit@1.20.0)(svelte@packages+svelte)
'@sveltejs/vite-plugin-svelte':
specifier: ^2.4.1
version: 2.4.1(svelte@packages+svelte)(vite@4.3.9)
@ -1852,6 +1852,19 @@ packages:
esm-env: 1.0.0
svelte: link:packages/svelte
svelte-local-storage-store: 0.4.0(svelte@packages+svelte)
dev: false
/@sveltejs/site-kit@6.0.0-next.0(@sveltejs/kit@1.20.0)(svelte@packages+svelte):
resolution: {integrity: sha512-7zHXRUf4CZPxQs710Qdx2exNyAI/6lLVoTp9AEx90+PSitrLg/lmPss2HmCY0xcZPPoxKE/3ViCFW2BklJjcZA==}
peerDependencies:
'@sveltejs/kit': ^1.0.0
svelte: ^3.54.0 || ^4.0.0-next.0 || ^4.0.0
dependencies:
'@sveltejs/kit': 1.20.0(svelte@packages+svelte)(vite@4.3.9)
esm-env: 1.0.0
svelte: link:packages/svelte
svelte-local-storage-store: 0.4.0(svelte@packages+svelte)
dev: true
/@sveltejs/vite-plugin-svelte-inspector@1.0.2(@sveltejs/vite-plugin-svelte@2.4.1)(svelte@packages+svelte)(vite@4.3.9):
resolution: {integrity: sha512-Cy1dUMcYCnDVV/hPLXa43YZJ2jGKVW5rA0xuNL9dlmYhT0yoS1g7+FOFSRlgk0BXKk/Oc7grs+8BVA5Iz2fr8A==}

@ -30,7 +30,7 @@
"@resvg/resvg-js": "^2.4.1",
"@sveltejs/adapter-vercel": "^3.0.0",
"@sveltejs/kit": "^1.20.0",
"@sveltejs/site-kit": "^5.2.1",
"@sveltejs/site-kit": "6.0.0-next.0",
"@sveltejs/vite-plugin-svelte": "^2.4.1",
"@types/marked": "^5.0.0",
"@types/node": "^20.2.5",

@ -0,0 +1,11 @@
/// <reference types="@sveltejs/kit" />
declare global {
namespace App {
interface PageData {
nav_title: string;
}
}
}
export {};

@ -2,7 +2,7 @@
import { browser } from '$app/environment';
import { process_example } from '$lib/utils/examples';
import Repl from '@sveltejs/repl';
import { theme } from '@sveltejs/site-kit/components';
import { theme } from '@sveltejs/site-kit/stores';
import { onMount } from 'svelte';
export let version = '3';

@ -14,7 +14,7 @@
<style>
.toggle {
position: fixed;
bottom: 0;
bottom: var(--sk-nav-height);
width: 100%;
height: 4.6rem;
display: flex;

@ -1,5 +1,5 @@
// @ts-check
import { modules } from '$lib/generated/type-info';
import { modules } from '$lib/generated/type-info.js';
import fs from 'node:fs';
import { CONTENT_BASE_PATHS } from '../../../constants.js';
import { extract_frontmatter } from '../markdown/index.js';

@ -1,5 +1,5 @@
import { base as app_base } from '$app/paths';
import { modules } from '$lib/generated/type-info';
import { modules } from '$lib/generated/type-info.js';
import fs from 'node:fs';
import { CONTENT_BASE_PATHS } from '../../../constants.js';
import {

@ -2,7 +2,7 @@
import { browser } from '$app/environment';
import { afterNavigate, goto } from '$app/navigation';
import Repl from '@sveltejs/repl';
import { theme } from '@sveltejs/site-kit/components';
import { theme } from '@sveltejs/site-kit/stores';
import { onMount } from 'svelte';
import { mapbox_setup } from '../../../../config.js';
import AppControls from './AppControls.svelte';

@ -0,0 +1,74 @@
import { get_blog_data, get_blog_list } from '$lib/server/blog/index.js';
import { get_docs_data, get_docs_list } from '$lib/server/docs/index.js';
import { get_examples_data, get_examples_list } from '$lib/server/examples/index.js';
import { get_tutorial_data, get_tutorial_list } from '$lib/server/tutorial/index.js';
/** @param {URL} url */
function get_nav_title(url) {
const list = new Map([
[/^docs/, 'Docs'],
[/^repl/, 'REPL'],
[/^blog/, 'Blog'],
[/^faq/, 'FAQ'],
[/^tutorial/, 'Tutorial'],
[/^search/, 'Search'],
[/^examples/, 'Examples']
]);
for (const [regex, title] of list) {
if (regex.test(url.pathname.replace(/^\/(.+)/, '$1'))) {
return title;
}
}
return '';
}
async function get_nav_context_list() {
const docs_list = get_docs_list(get_docs_data());
const processed_docs_list = docs_list.map(({ title, pages }) => ({
title,
sections: pages.map(({ title, path }) => ({ title, path }))
}));
const blog_list = get_blog_list(get_blog_data());
const processed_blog_list = [
{
title: 'Blog',
sections: blog_list.map(({ title, slug, date }) => ({
title,
path: '/blog/' + slug,
// Put a NEW badge on blog posts that are less than 14 days old
badge: (+new Date() - +new Date(date)) / (1000 * 60 * 60 * 24) < 14 ? 'NEW' : undefined
}))
}
];
const tutorial_list = get_tutorial_list(get_tutorial_data());
const processed_tutorial_list = tutorial_list.map(({ title, tutorials }) => ({
title,
sections: tutorials.map(({ title, slug }) => ({ title, path: '/tutorial/' + slug }))
}));
const examples_list = get_examples_list(get_examples_data());
const processed_examples_list = examples_list
.map(({ title, examples }) => ({
title,
sections: examples.map(({ title, slug }) => ({ title, path: '/examples/' + slug }))
}))
.filter(({ title }) => title !== 'Embeds');
return {
docs: processed_docs_list,
blog: processed_blog_list,
tutorial: processed_tutorial_list,
examples: processed_examples_list
};
}
export const load = async ({ url }) => {
return {
nav_title: get_nav_title(url),
nav_context_list: get_nav_context_list()
};
};

@ -1,7 +1,8 @@
<script>
import { browser } from '$app/environment';
import { page } from '$app/stores';
import { Icon, Nav, NavItem, Separator, Shell } from '@sveltejs/site-kit/components';
import { Icon, Shell } from '@sveltejs/site-kit/components';
import { Nav, NavItem, Separator } from '@sveltejs/site-kit/nav';
import { Search, SearchBox } from '@sveltejs/site-kit/search';
import '@sveltejs/site-kit/styles/index.css';
</script>
@ -17,10 +18,14 @@
<div style:display={$page.url.pathname !== '/docs' ? 'contents' : 'none'}>
<Shell nav_visible={$page.url.pathname !== '/repl/embed'}>
<Nav slot="top-nav">
<svelte:fragment slot="home">
<svelte:fragment slot="home-large">
<strong>svelte</strong>.dev
</svelte:fragment>
<svelte:fragment slot="home-small">
<!-- <strong>svelte</strong> -->
</svelte:fragment>
<svelte:fragment slot="nav-center">
{#if $page.url.pathname !== '/search'}
<li><Search /></li>
@ -28,22 +33,52 @@
</svelte:fragment>
<svelte:fragment slot="nav-right">
<NavItem href="/tutorial">Tutorial</NavItem>
<NavItem href="/docs/introduction">Docs</NavItem>
<NavItem href="/examples">Examples</NavItem>
<NavItem href="/repl">REPL</NavItem>
<NavItem href="/blog">Blog</NavItem>
<NavItem
href="/tutorial"
selected={$page.url.pathname.startsWith('/tutorial') || null}
relatedMenuName="tutorial"
>
Tutorial
</NavItem>
<NavItem
href="/docs/introduction"
selected={$page.url.pathname.startsWith('/docs') || null}
relatedMenuName="docs"
>
Docs
</NavItem>
<NavItem
href="/examples"
selected={$page.url.pathname.startsWith('/examples') || null}
relatedMenuName="examples"
>
Examples
</NavItem>
<NavItem href="/repl" selected={$page.url.pathname.startsWith('/repl') || null}>
REPL
</NavItem>
<NavItem
href="/blog"
selected={$page.url.pathname.startsWith('/blog') || null}
relatedMenuName="blog"
>
Blog
</NavItem>
<Separator />
<NavItem external="https://kit.svelte.dev">SvelteKit</NavItem>
<NavItem href="https://kit.svelte.dev" external>SvelteKit</NavItem>
<NavItem external="/chat" title="Discord Chat">
<NavItem href="/chat" external title="Discord Chat">
<span slot="small">Discord</span>
<Icon name="message-square" />
<Icon name="discord" />
</NavItem>
<NavItem external="https://github.com/sveltejs/svelte" title="GitHub Repo">
<NavItem href="https://github.com/sveltejs/svelte" external title="GitHub Repo">
<span slot="small">GitHub</span>
<Icon name="github" />
</NavItem>
@ -63,20 +98,8 @@
color-scheme: light dark;
}
@media (max-width: 830px) {
:global(aside) {
z-index: 9999 !important;
}
}
:global(html, body) {
height: 100%;
width: 100%;
}
@media (max-width: 830px) {
:global(aside) {
z-index: 9999 !important;
}
}
</style>

@ -1,14 +1,14 @@
<script>
import { Blurb, theme } from '@sveltejs/site-kit/components';
import { Blurb } from '@sveltejs/site-kit/components';
import { theme } from '@sveltejs/site-kit/stores';
import Demo from './_components/Demo.svelte';
import Hero from './_components/Hero.svelte';
import Image from './_components/Image.svelte';
import Supporters from './_components/Supporters/index.svelte';
import Try from './_components/Try.svelte';
import WhosUsingSvelte from './_components/WhosUsingSvelte/index.svelte';
import CollectiveLight from './svelte-collective-light.png?big-image';
import CollectiveDark from './svelte-collective-dark.png?big-image';
import CollectiveLight from './svelte-collective-light.png?big-image';
</script>
<svelte:head>

@ -4,7 +4,7 @@
import donors from './donors.js';
</script>
<Section>
<Section --background="var(--sk-back-2">
<p class="intro">
Svelte is free and open source software, made possible <br /> by the work of hundreds of supporters.
</p>

@ -1,7 +1,7 @@
<script>
import { Section } from '@sveltejs/site-kit/components';
import { theme } from '@sveltejs/site-kit/stores';
import { companies } from './companies.js';
import { theme } from '@sveltejs/site-kit/components';
const sorted = companies.sort((a, b) => (a.alt < b.alt ? -1 : 1));
</script>

@ -1,7 +1,6 @@
<script>
import { page } from '$app/stores';
import * as hovers from '$lib/utils/hovers.js';
import '@sveltejs/site-kit/styles/code.css';
export let data;

@ -1,22 +1,15 @@
<script>
import { page } from '$app/stores';
import { TSToggle } from '@sveltejs/site-kit/components';
import Contents from './Contents.svelte';
import { DocsContents } from '@sveltejs/site-kit/docs';
export let data;
$: title = data.sections
.find(({ pages }) => pages.find(({ path }) => path === $page.url.pathname))
?.pages.find(({ path }) => path === $page.url.pathname).title;
$: title = $page.data.page?.title;
</script>
<div class="container">
<div class="toc-container" style="order: 1">
<Contents contents={data.sections} />
<div class="ts-toggle">
<TSToggle />
</div>
<DocsContents contents={data.sections} />
</div>
<div class="page content">
@ -45,51 +38,24 @@
min-width: 0 !important;
}
.page :global(hr) {
display: none;
}
.page :global(:where(h2, h3) code) {
all: unset;
}
/* .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(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);
display: none;
}
@media (min-width: 832px) {
.toc-container {
display: block;
width: var(--sidebar-width);
height: calc(100vh - var(--sk-nav-height));
position: fixed;
@ -112,15 +78,6 @@
.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) {

@ -1,162 +0,0 @@
<script>
import { page } from '$app/stores';
import { SkipLink } from '@sveltejs/site-kit/components';
/** @type {ReturnType<typeof import('$lib/server/docs').get_docs_list>}*/
export let contents = [];
</script>
<SkipLink href="#docs-content">Skip to documentation</SkipLink>
<!-- svelte-ignore a11y-no-noninteractive-tabindex -->
<nav aria-label="Docs">
<ul class="sidebar">
{#each contents as section}
<li>
<span class="section">
{section.title}
</span>
<ul>
{#each section.pages as { title, path }}
<li>
<a
data-sveltekit-preload-data
class="page"
class:active={path === $page.url.pathname}
href={path}
>
{title}
</a>
</li>
{/each}
</ul>
</li>
{/each}
</ul>
</nav>
<style>
nav {
top: 0;
left: 0;
color: var(--sk-text-3);
}
.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: 4rem;
}
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;
}
.section {
display: block;
padding-bottom: 0.8rem;
font-size: var(--sk-text-xs);
text-transform: uppercase;
letter-spacing: 0.1em;
font-weight: 600;
}
.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);
}
ul ul,
ul ul li {
margin: 0;
}
@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,18 @@
import { DocsMobileNav } from '@sveltejs/site-kit/docs';
export const load = async ({ data, parent }) => {
const contents = await parent();
return {
mobile_nav_start: {
label: 'Contents',
icon: 'contents',
component: DocsMobileNav,
props: {
contents: contents.sections,
pageContents: data.page
}
},
...data
};
};

@ -2,7 +2,7 @@
import { page } from '$app/stores';
import * as hovers from '$lib/utils/hovers';
import { Icon } from '@sveltejs/site-kit/components';
import OnThisPage from './OnThisPage.svelte';
import { DocsOnThisPage } from '@sveltejs/site-kit/docs';
export let data;
@ -50,7 +50,7 @@
</div>
</div>
<OnThisPage details={data.page} />
<DocsOnThisPage details={data.page} />
<style>
.edit {

@ -1,163 +0,0 @@
<script>
import { afterNavigate } from '$app/navigation';
import { base } from '$app/paths';
import { page } from '$app/stores';
import { afterUpdate, onMount } from 'svelte';
/** @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 = [];
/** @type {HTMLElement} */
let containerEl;
let show_contents = false;
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 }
);
}
afterUpdate(() => {
// bit of a hack — prevent sidebar scrolling if
// TOC is open on mobile, or scroll came from within sidebar
if (show_contents && window.innerWidth < 832) return;
const active = containerEl.querySelector('.active');
if (active) {
const { top, bottom } = active.getBoundingClientRect();
const min = 100;
const max = window.innerHeight - 100;
if (top > max) {
containerEl.scrollBy({
top: top - max,
left: 0,
behavior: 'smooth'
});
} else if (bottom < min) {
containerEl.scrollBy({
top: bottom - min,
left: 0,
behavior: 'smooth'
});
}
}
});
</script>
<svelte:window on:scroll={highlight} on:resize={update} on:hashchange={() => select($page.url)} />
<aside class="on-this-page" bind:this={containerEl}>
<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: var(--sk-page-padding-top) var(--sk-page-padding-side) 0 0;
width: min(280px, calc(var(--sidebar-width) - var(--sk-page-padding-side)));
height: calc(100vh - var(--sk-nav-height) - var(--sk-page-padding-top));
top: var(--sk-nav-height);
left: calc(100vw - (var(--sidebar-width)));
overflow-y: auto;
}
h2 {
text-transform: uppercase;
font-size: 1.4rem !important;
font-weight: 400;
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;
}
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>

@ -4,7 +4,7 @@
import { navigating } from '$app/stores';
import ScreenToggle from '$lib/components/ScreenToggle.svelte';
import Repl from '@sveltejs/repl';
import { theme } from '@sveltejs/site-kit/components';
import { theme } from '@sveltejs/site-kit/stores';
import { mapbox_setup, svelteUrl } from '../../../config';
import TableOfContents from './TableOfContents.svelte';

@ -2,7 +2,7 @@
import { browser } from '$app/environment';
import ScreenToggle from '$lib/components/ScreenToggle.svelte';
import Repl from '@sveltejs/repl';
import { theme } from '@sveltejs/site-kit/components';
import { theme } from '@sveltejs/site-kit/stores';
import { mapbox_setup, svelteUrl } from '../../../config.js';
import TableOfContents from './TableOfContents.svelte';

Loading…
Cancel
Save