Make navigation mode switchable
This commit is contained in:
parent
a22fed7728
commit
05dd90ec80
1
public/static/svg/change.svg
Normal file
1
public/static/svg/change.svg
Normal 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 |
49
src/components/tree/Headings.astro
Normal file
49
src/components/tree/Headings.astro
Normal 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()
|
||||
}
|
|
@ -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>
|
||||
)}
|
|
@ -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>
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in a new issue