Test impl of tree component
This commit is contained in:
parent
f61508d4a8
commit
1b5b644356
|
@ -17,6 +17,7 @@
|
|||
"dayjs": "^1.11.7",
|
||||
"leaflet": "^1.9.4",
|
||||
"leaflet.markercluster": "^1.5.3",
|
||||
"purify-ts": "^2.0.1",
|
||||
"rehype-katex": "^6.0.3",
|
||||
"rehype-raw": "^6.1.1",
|
||||
"rehype-stringify": "^9.0.3",
|
||||
|
|
|
@ -26,6 +26,9 @@ dependencies:
|
|||
leaflet.markercluster:
|
||||
specifier: ^1.5.3
|
||||
version: 1.5.3(leaflet@1.9.4)
|
||||
purify-ts:
|
||||
specifier: ^2.0.1
|
||||
version: 2.0.1
|
||||
rehype-katex:
|
||||
specifier: ^6.0.3
|
||||
version: 6.0.3
|
||||
|
@ -828,6 +831,10 @@ packages:
|
|||
'@types/unist': 2.0.6
|
||||
dev: false
|
||||
|
||||
/@types/json-schema@7.0.12:
|
||||
resolution: {integrity: sha512-Hr5Jfhc9eYOQNPYO5WLDq/n4jqijdHNlDXjuAQkkt+mWdQR+XJToOHrsD4cPaMXpn6KO7y2+wM8AZEs8VpBLVA==}
|
||||
dev: false
|
||||
|
||||
/@types/json5@0.0.30:
|
||||
resolution: {integrity: sha512-sqm9g7mHlPY/43fcSNrCYfOeX9zkTTK+euO5E6+CVijSMm5tTjkVdwdqRkY3ljjIAf8679vps5jKUoJBCLsMDA==}
|
||||
dev: false
|
||||
|
@ -3069,6 +3076,12 @@ packages:
|
|||
/proxy-from-env@1.1.0:
|
||||
resolution: {integrity: sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==}
|
||||
|
||||
/purify-ts@2.0.1:
|
||||
resolution: {integrity: sha512-g17AdcyNtXyUuLgMfQN8bS2xPiTrxxkebe5Ssy234NX8ZpE2Nbk6bIXO5MNapFVb77jcv7a4Xt9NfPtuCLGiBQ==}
|
||||
dependencies:
|
||||
'@types/json-schema': 7.0.12
|
||||
dev: false
|
||||
|
||||
/queue-microtask@1.2.3:
|
||||
resolution: {integrity: sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==}
|
||||
dev: false
|
||||
|
|
34
src/components/tree/List.astro
Normal file
34
src/components/tree/List.astro
Normal file
|
@ -0,0 +1,34 @@
|
|||
---
|
||||
import { Tree, pathify } from "@utils/tree";
|
||||
import { Maybe } from "purify-ts";
|
||||
|
||||
|
||||
interface Props {
|
||||
tree: Tree;
|
||||
prefix: Maybe<string>;
|
||||
}
|
||||
|
||||
const { tree, prefix } = Astro.props;
|
||||
---
|
||||
|
||||
{tree.children
|
||||
.map(m => Object.values(m))
|
||||
.filter(xs => xs.length > 0)
|
||||
.map(pages =>
|
||||
<ul>
|
||||
{pages.map(page =>
|
||||
<li>
|
||||
{Maybe.of(Maybe.catMaybes([prefix, page.slug]))
|
||||
.filter(xs => xs.length > 0)
|
||||
.map(xs => pathify(...xs))
|
||||
.mapOrDefault(href =>
|
||||
<a href={href}>{page.title}</a>,
|
||||
<span>{page.title}</span>
|
||||
)}
|
||||
<Astro.self tree={page} prefix={prefix} />
|
||||
</li>
|
||||
)}
|
||||
</ul>
|
||||
).extract()}
|
||||
|
||||
|
26
src/components/tree/Tree.astro
Normal file
26
src/components/tree/Tree.astro
Normal file
|
@ -0,0 +1,26 @@
|
|||
---
|
||||
import List from "./List.astro";
|
||||
import { Tree, pathify } from "@utils/tree";
|
||||
import type { Maybe } from "purify-ts";
|
||||
|
||||
|
||||
interface Props {
|
||||
tree: Tree;
|
||||
prefix: Maybe<string>;
|
||||
}
|
||||
|
||||
const { tree, prefix } = Astro.props;
|
||||
|
||||
---
|
||||
|
||||
<aside class="link-tree">
|
||||
<h2 class="link-tree__heading">
|
||||
{prefix.map(pathify).mapOrDefault(href =>
|
||||
<a href={href}>Personal wiki</a>,
|
||||
<span>Personal Wiki</span>
|
||||
)}
|
||||
</h2>
|
||||
<section>
|
||||
<List tree={tree} prefix={prefix} />
|
||||
</section>
|
||||
</aside>
|
5
src/content/wiki/typescript/index.md
Normal file
5
src/content/wiki/typescript/index.md
Normal file
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
title: "Hello"
|
||||
slug: "typescript"
|
||||
---
|
||||
Hello
|
35
src/content/wiki/typescript/type-challenges.md
Normal file
35
src/content/wiki/typescript/type-challenges.md
Normal file
|
@ -0,0 +1,35 @@
|
|||
---
|
||||
title: "Hello"
|
||||
---
|
||||
|
||||
## 4・Pick
|
||||
|
||||
> Implement the built-in `Pick<T, K>` generic without using it. Constructs a type by picking the set of properties `K` from `T`.
|
||||
|
||||
```ts
|
||||
type MyPick<T, K extends keyof T> = {
|
||||
[key in K]: k extends keyof T ? T[key] : never;
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
## 7・Readonly
|
||||
|
||||
> Implement the built-in `Readonly<T>` generic without using it. Constructs a type with all properties of T set to readonly, meaning the properties of the constructed type cannot be reassigned.
|
||||
|
||||
```ts
|
||||
type MyReadonly<T> = {
|
||||
readonly [key in keyof T]: T[key];
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
## 11・Tuple to Object
|
||||
|
||||
> Given an array, transform it into an object type and the key/value must be in the provided array.
|
||||
|
||||
```ts
|
||||
type TupleToObject<T extends readonly (string | number)[]> = {
|
||||
[val in T[number]]: val;
|
||||
}
|
||||
```
|
27
src/pages/wiki/[...slug].astro
Normal file
27
src/pages/wiki/[...slug].astro
Normal file
|
@ -0,0 +1,27 @@
|
|||
---
|
||||
import Tree from '@components/tree/Tree.astro';
|
||||
import Base from '@layouts/Base.astro';
|
||||
import type { InferGetStaticPropsType } from 'astro';
|
||||
import { getCollection } from 'astro:content';
|
||||
import { Maybe } from 'purify-ts';
|
||||
import { collapse } from '@utils/tree';
|
||||
|
||||
|
||||
type Props = InferGetStaticPropsType<typeof getStaticPaths>;
|
||||
export async function getStaticPaths() {
|
||||
const pages = await getCollection('wiki');
|
||||
const tree = collapse(pages);
|
||||
|
||||
return pages.map(entry => ({params: {slug: entry.slug}, props: {tree, entry}}));
|
||||
}
|
||||
|
||||
const { tree, entry } = Astro.props;
|
||||
const { Content } = await entry.render();
|
||||
---
|
||||
|
||||
<Base>
|
||||
<Tree tree={tree} prefix={Maybe.of("/wiki/")}/>
|
||||
<main>
|
||||
<Content />
|
||||
</main>
|
||||
</Base>
|
44
src/utils/tree.ts
Normal file
44
src/utils/tree.ts
Normal file
|
@ -0,0 +1,44 @@
|
|||
import { Maybe } from "purify-ts";
|
||||
|
||||
|
||||
interface Page {
|
||||
slug: string;
|
||||
data: { title: string };
|
||||
}
|
||||
|
||||
export interface Tree {
|
||||
title: string;
|
||||
slug: Maybe<string>;
|
||||
children: Maybe<{ [key: string]: Tree }>;
|
||||
}
|
||||
|
||||
export function collapse(pages: Page[]): Tree {
|
||||
const root: Tree = { title: '', slug: Maybe.empty(), children: Maybe.empty() };
|
||||
|
||||
for (const page of pages) {
|
||||
const ptr = page.slug.split('/')
|
||||
.reduce((ptr, slug) => {
|
||||
// acquire pointer on next node in tree
|
||||
const next = ptr.children
|
||||
.chainNullable(trie => trie[slug])
|
||||
.orDefaultLazy(() => ({ title: slug, slug: Maybe.empty(), children: Maybe.empty() }));
|
||||
|
||||
// update tree refs
|
||||
ptr.children = ptr.children
|
||||
.ifJust(trie => trie[slug] = next)
|
||||
.altLazy(() => Maybe.of({[slug]: next}));
|
||||
|
||||
return next;
|
||||
}, root);
|
||||
|
||||
ptr.slug = Maybe.of(`/${page.slug}/`);
|
||||
ptr.title = page.data.title;
|
||||
}
|
||||
|
||||
return root;
|
||||
}
|
||||
|
||||
export function pathify(...slugs: string[]): string {
|
||||
const path = slugs.map(part => part.trim().replace(/(^[\/]*|[\/]*$)/g, '')).join('/');
|
||||
return `/${path}/`;
|
||||
}
|
Loading…
Reference in a new issue