Make navigation mode switchable

This commit is contained in:
Maciej Jur 2023-06-08 14:00:17 +02:00
parent a22fed7728
commit 05dd90ec80
7 changed files with 158 additions and 39 deletions

View file

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" height="48" viewBox="0 -960 960 960" width="48"><path d="M480-40q-112 0-216-66T100-257v137H40v-240h240v60H143q51 77 145.5 138.5T480-100q78 0 147.5-30t121-81.5Q800-263 830-332.5T860-480h60q0 91-34.5 171T791-169q-60 60-140 94.5T480-40ZM40-480q0-91 34.5-171T169-791q60-60 140-94.5T480-920q112 0 216 66t164 151v-137h60v240H680v-60h137q-51-77-145-138.5T480-860q-78 0-147.5 30t-121 81.5Q160-697 130-627.5T100-480H40Zm440 175-54-121-121-54 121-55 54-121 55 121 121 55-121 54-55 121Z"/></svg>

After

Width:  |  Height:  |  Size: 524 B

View file

@ -0,0 +1,49 @@
---
import type { MarkdownHeading } from 'astro';
import { Maybe } from 'purify-ts';
interface Props {
headings: Maybe<MarkdownHeading[]>;
}
type Nested = MarkdownHeading & { children?: MarkdownHeading[] };
const { headings } = Astro.props;
function fold(headings: MarkdownHeading[]) {
const toc = [] as Nested[];
const map = new Map<number, Nested>();
for (const h of headings) {
const heading = { ...h };
map.set(heading.depth, heading);
if (heading.depth === 2)
toc.push(heading)
else {
const backref = map.get(heading.depth - 1)!;
backref.children
? backref.children.push(heading)
: backref.children = [heading];
}
}
return toc;
}
---
{headings
.map(fold)
.map(headings =>
<ul class="link-tree__nav-list">
{headings.map(heading =>
<li class="link-tree__nav-list-item">
<a class="link-tree__nav-list-text link" href={`#${heading.slug}`}>
{heading.text}
</a>
<Astro.self headings={Maybe.fromNullable(heading.children)}/>
</li>
)}
</ul>
)
.extract()
}

View file

@ -3,12 +3,14 @@ import { PageTree, pathify } from "@utils/tree";
import { Maybe } from "purify-ts";
interface Props {
export interface PagesProps {
tree: PageTree;
slug: Maybe<string>;
prefix: Maybe<string>;
}
type Props = PagesProps;
const { tree, slug, prefix } = Astro.props;
function compare(a: {title: string}, b: {title: string}) {
@ -37,12 +39,11 @@ function checkCurrent(checked: Maybe<string>) {
<li class="link-tree__nav-list-item">
{page.slug
.chain(slug => prefix.map(prefix => pathify(prefix, slug)))
.mapOrDefault(href =>
<a class="link-tree__nav-list-text link" class:list={{ current: page.current }} href={href}>
{page.title}
</a>,
<span class="link-tree__nav-list-text">{page.title}</span>
)}
.map(href => (page.current)
? <button class="link-tree__nav-list-text current">{page.title}</button>
: <a class="link-tree__nav-list-text link" href={href}>{page.title}</a>
)
.orDefault(<span class="link-tree__nav-list-text">{page.title}</span>)}
<Astro.self tree={page} slug={slug} prefix={prefix} />
</li>
)}

View file

@ -1,27 +1,65 @@
---
import List from "./List.astro";
import { PageTree, pathify } from "@utils/tree";
import Pages from "./Pages.astro";
import Headings from "./Headings.astro";
import { pathify } from "@utils/tree";
import type { Maybe } from "purify-ts";
import type { MarkdownHeading } from "astro";
import type { PagesProps } from "./Pages.astro";
interface Props {
heading: string;
tree: PageTree;
slug: Maybe<string>;
prefix: Maybe<string>;
pages: Maybe<PagesProps>;
headings: Maybe<MarkdownHeading[]>;
}
const { heading, tree, slug, prefix } = Astro.props;
const { heading, pages, headings } = Astro.props;
---
<section class="link-tree">
<!-- Nav mode switch -->
<input id="link-tree-mode" type="checkbox" hidden />
<label id="link-tree-switch" class="link-tree__switch"
for="link-tree-mode"
role="button"
tabindex="0"
title="Switch navigation mode">
<img src="/static/svg/change.svg" alt="Switch navigation mode" width="24" height="24"/>
</label>
<!-- Primary view displaying current headings -->
<div class="v-alt">
<h2 class="link-tree__heading">
{prefix.map(pathify).mapOrDefault(href =>
<span class="link-tree__heading-text">Herein</span>
</h2>
<nav class="link-tree__nav">
<Headings headings={headings} />
</nav>
</div>
<!-- Optional view displaying page tree -->
<div class="v-prime">
<h2 class="link-tree__heading">
{pages.chain(x => x.prefix)
.map(pathify)
.mapOrDefault(href =>
<a class="link-tree__heading-text" href={href}>{heading}</a>,
<span class="link-tree__heading-text">{heading}</span>
)}
</h2>
<nav class="link-tree__nav">
<List tree={tree} slug={slug} prefix={prefix} />
{pages.map(pages => <Pages {...pages} />).extract()}
</nav>
</div>
<script>
const button = document.getElementById("link-tree-switch");
button?.addEventListener("keypress", e => {
if (e.key === "Enter") {
e.preventDefault();
button!.click();
}
})
</script>
</section>

View file

@ -4,9 +4,11 @@ import Tree from "@components/tree/Tree.astro";
import { getCollection } from "astro:content";
import { Maybe, MaybeAsync } from "purify-ts";
import { collapse, type PageTree } from "@utils/tree";
import type { MarkdownHeading } from "astro";
interface Props {
headings?: MarkdownHeading[];
frontmatter: {
title: string;
};
@ -14,11 +16,7 @@ interface Props {
tree?: PageTree;
}
const {
frontmatter,
slug,
tree,
} = Astro.props;
const { frontmatter, slug } = Astro.props;
function constructTree(tree?: PageTree) {
@ -29,19 +27,21 @@ function constructTree(tree?: PageTree) {
.map(collapse));
}
const pageTree = (await constructTree(tree))
.orDefaultLazy(() => { throw new Error("Couldn't load page tree") });
const headings = Maybe.fromNullable(Astro.props.headings);
const pages = (await constructTree(Astro.props.tree))
.map(tree => ({
tree,
slug: Maybe.fromNullable(slug),
prefix: Maybe.of("/wiki/")
}));
---
<Base>
<main class="wiki-main">
<Tree heading="Personal Wiki"
tree={pageTree}
slug={Maybe.fromNullable(slug)}
prefix={Maybe.of("/wiki/")}
/>
<Tree heading="Personal Wiki" pages={pages} headings={headings} />
<article class="wiki-article markdown">
<h1>{frontmatter.title}</h1>
<h1>{frontmatter?.title}</h1>
<slot />
</article>
</main>

View file

@ -1,8 +1,8 @@
---
import Wiki from '@layouts/Wiki.astro';
import type { InferGetStaticPropsType } from 'astro';
import { getCollection } from 'astro:content';
import { collapse } from '@utils/tree';
import Wiki from '@layouts/Wiki.astro';
type Props = InferGetStaticPropsType<typeof getStaticPaths>;
@ -14,9 +14,14 @@ export async function getStaticPaths() {
}
const { tree, entry } = Astro.props;
const { Content } = await entry.render();
const { headings, Content } = await entry.render();
---
<Wiki slug={entry.slug} frontmatter={entry.data} tree={tree}>
<Wiki
frontmatter={entry.data}
headings={headings}
slug={entry.slug}
tree={tree}
>
<Content />
</Wiki>

View file

@ -1,8 +1,21 @@
.link-tree {
position: relative;
padding: 1em;
overflow-y: auto;
&__switch {
position: absolute;
top: 0.5em;
left: 0.5em;
cursor: pointer;
img {
max-height: 1.5em;
}
}
&__heading {
margin-bottom: 0.5em;
border-bottom: 1px solid;
border-image: linear-gradient(to right, transparent, lightgray, transparent) 1;
@ -43,9 +56,21 @@
&.current {
background-color: var(--c-primary);
border: unset;
border-radius: 0.33em;
color: white;
cursor: pointer;
}
}
}
}
#link-tree-mode {
&:checked ~ .v-prime {
display: none;
}
&:not(:checked) ~ .v-alt {
display: none;
}
}