Test impl of tree component
This commit is contained in:
parent
f61508d4a8
commit
1b5b644356
|
@ -17,6 +17,7 @@
|
||||||
"dayjs": "^1.11.7",
|
"dayjs": "^1.11.7",
|
||||||
"leaflet": "^1.9.4",
|
"leaflet": "^1.9.4",
|
||||||
"leaflet.markercluster": "^1.5.3",
|
"leaflet.markercluster": "^1.5.3",
|
||||||
|
"purify-ts": "^2.0.1",
|
||||||
"rehype-katex": "^6.0.3",
|
"rehype-katex": "^6.0.3",
|
||||||
"rehype-raw": "^6.1.1",
|
"rehype-raw": "^6.1.1",
|
||||||
"rehype-stringify": "^9.0.3",
|
"rehype-stringify": "^9.0.3",
|
||||||
|
|
|
@ -26,6 +26,9 @@ dependencies:
|
||||||
leaflet.markercluster:
|
leaflet.markercluster:
|
||||||
specifier: ^1.5.3
|
specifier: ^1.5.3
|
||||||
version: 1.5.3(leaflet@1.9.4)
|
version: 1.5.3(leaflet@1.9.4)
|
||||||
|
purify-ts:
|
||||||
|
specifier: ^2.0.1
|
||||||
|
version: 2.0.1
|
||||||
rehype-katex:
|
rehype-katex:
|
||||||
specifier: ^6.0.3
|
specifier: ^6.0.3
|
||||||
version: 6.0.3
|
version: 6.0.3
|
||||||
|
@ -828,6 +831,10 @@ packages:
|
||||||
'@types/unist': 2.0.6
|
'@types/unist': 2.0.6
|
||||||
dev: false
|
dev: false
|
||||||
|
|
||||||
|
/@types/json-schema@7.0.12:
|
||||||
|
resolution: {integrity: sha512-Hr5Jfhc9eYOQNPYO5WLDq/n4jqijdHNlDXjuAQkkt+mWdQR+XJToOHrsD4cPaMXpn6KO7y2+wM8AZEs8VpBLVA==}
|
||||||
|
dev: false
|
||||||
|
|
||||||
/@types/json5@0.0.30:
|
/@types/json5@0.0.30:
|
||||||
resolution: {integrity: sha512-sqm9g7mHlPY/43fcSNrCYfOeX9zkTTK+euO5E6+CVijSMm5tTjkVdwdqRkY3ljjIAf8679vps5jKUoJBCLsMDA==}
|
resolution: {integrity: sha512-sqm9g7mHlPY/43fcSNrCYfOeX9zkTTK+euO5E6+CVijSMm5tTjkVdwdqRkY3ljjIAf8679vps5jKUoJBCLsMDA==}
|
||||||
dev: false
|
dev: false
|
||||||
|
@ -3069,6 +3076,12 @@ packages:
|
||||||
/proxy-from-env@1.1.0:
|
/proxy-from-env@1.1.0:
|
||||||
resolution: {integrity: sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==}
|
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:
|
/queue-microtask@1.2.3:
|
||||||
resolution: {integrity: sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==}
|
resolution: {integrity: sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==}
|
||||||
dev: false
|
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