feat: sort folders on sidebar based on name

This commit is contained in:
Araozu 2024-11-22 06:50:37 -05:00
parent d4baa970d6
commit 4c4be43cb0
42 changed files with 42 additions and 196 deletions

View File

@ -12,9 +12,9 @@
},
"dependencies": {
"@astrojs/check": "^0.5.10",
"@astrojs/mdx": "^2.3.0",
"@astrojs/mdx": "^2.3.1",
"@astrojs/tailwind": "^5.1.0",
"astro": "^4.6.1",
"astro": "^4.6.3",
"codejar": "^4.2.0",
"tailwindcss": "^3.4.3",
"typescript": "^5.4.5",

View File

@ -12,13 +12,13 @@ importers:
specifier: ^0.5.10
version: 0.5.10(typescript@5.4.5)
'@astrojs/mdx':
specifier: ^2.3.0
specifier: ^2.3.1
version: 2.3.1(astro@4.6.3(@types/node@20.11.30)(typescript@5.4.5))
'@astrojs/tailwind':
specifier: ^5.1.0
version: 5.1.0(astro@4.6.3(@types/node@20.11.30)(typescript@5.4.5))(tailwindcss@3.4.3)
astro:
specifier: ^4.6.1
specifier: ^4.6.3
version: 4.6.3(@types/node@20.11.30)(typescript@5.4.5)
codejar:
specifier: ^4.2.0
@ -870,8 +870,8 @@ packages:
resolution: {integrity: sha512-xlx1yCK2Oc1APsPXDL2LdlNP6+uu8OCDdhOBSVT279M/S+y75O30C2VuD8T2ogdePBBl7PfPF4504tnLgX3zfw==}
engines: {node: '>=14.16'}
caniuse-lite@1.0.30001612:
resolution: {integrity: sha512-lFgnZ07UhaCcsSZgWW0K5j4e69dK1u/ltrL9lTUiFOwNHs12S3UMIEYgBV0Z6C6hRDev7iRnMzzYmKabYdXF9g==}
caniuse-lite@1.0.30001683:
resolution: {integrity: sha512-iqmNnThZ0n70mNwvxpEC2nBJ037ZHZUoBI5Gorh1Mw6IlEAZujEoU1tXA628iZfzm7R9FvFzxbfdgml82a3k8Q==}
ccount@2.0.1:
resolution: {integrity: sha512-eyrF0jiFpY+3drT6383f1qhkbGsLSifNAjA61IUjZjmLCWjItY6LB9ft9YhoDgwfmclB2zhu51Lc7+95b8NRAg==}
@ -3394,7 +3394,7 @@ snapshots:
autoprefixer@10.4.19(postcss@8.4.38):
dependencies:
browserslist: 4.23.0
caniuse-lite: 1.0.30001612
caniuse-lite: 1.0.30001683
fraction.js: 4.3.7
normalize-range: 0.1.2
picocolors: 1.0.0
@ -3470,7 +3470,7 @@ snapshots:
browserslist@4.23.0:
dependencies:
caniuse-lite: 1.0.30001612
caniuse-lite: 1.0.30001683
electron-to-chromium: 1.4.746
node-releases: 2.0.14
update-browserslist-db: 1.0.13(browserslist@4.23.0)
@ -3492,7 +3492,7 @@ snapshots:
camelcase@7.0.1: {}
caniuse-lite@1.0.30001612: {}
caniuse-lite@1.0.30001683: {}
ccount@2.0.1: {}

View File

@ -3,6 +3,14 @@ import type { PageEntry } from "../layouts/PagesLayout.astro";
const entry: PageEntry = Astro.props.entry;
const post_url = entry.url + (entry.url.endsWith("/") ? "" : "/");
// this may deal with folders.
// if so, it will turn any `-` into whitespace,
// and remove any leading number
const entry_title = entry.title
.replaceAll("-", " ")
.replaceAll(/\d+_/g, "");
---
{
@ -21,7 +29,7 @@ const post_url = entry.url + (entry.url.endsWith("/") ? "" : "/");
entry.children && (
<>
<div class="mt-6 px-3 py-1 uppercase font-display text-c-text-2 font-medium">
{entry.title.replaceAll("-", " ")}
{entry_title}
</div>
<ul class="my-1">

View File

@ -17,7 +17,7 @@ export function leftTrimDedent(input: string): Array<string> {
// Get indentation level of the first line
let indentationLevel = 0;
for (const char of lines[0]!) {
if (char === " ") {
if (char === " " || char === "\n") {
indentationLevel += 1;
} else {
break;
@ -33,7 +33,7 @@ export function leftTrimDedent(input: string): Array<string> {
output.push(trimWhitespace(line, indentationLevel));
}
if (output.length > 1 && output[output.length - 1] === "") {
if (output.length > 1 && output[output.length - 1] === "") {
output = output.slice(0, -1);
}

View File

@ -1,19 +0,0 @@
---
import PagesLayout from "./PagesLayout.astro";
const { frontmatter, headings } = Astro.props;
const posts = await Astro.glob("@/pages/learn/**/*.{md,mdx}");
const indexSubpath = `/learn/index.mdx`;
---
<PagesLayout
frontmatter={frontmatter}
headings={headings}
posts={posts}
indexSubpath={indexSubpath}
basePath="/learn/"
disable_container={!!frontmatter.disable_container}
>
<slot />
</PagesLayout>

View File

@ -42,16 +42,14 @@ const {
const base_len = base_url.length;
const posts_2 = posts
.map(post => {
return {
.map(post => ({
...post,
title: post.frontmatter.title,
// this should be a path relative to the base url.
// e.g if base_url is `/spec`, then this instead of
// being `/spec/ast/tokens` would be `/ast/tokens`
path: post.url.substring(base_len),
}
})
}))
.sort((p1, p2) => p1.frontmatter.order > p2.frontmatter.order? 1 : -1);
// build a hierarchy of the files
@ -75,39 +73,42 @@ for (const post of posts_2) {
}
}
// transform to the layout that the sidebar expects
// build a folder hierarchy (only 2 levels)
const entries: Array<any> = [];
for (const levels_key in second_level) {
const levels_keys = Object.keys(second_level).toSorted();
// The key `_` contains the top level links. Always insert those
const top_level_posts = second_level["_"]!;
// sort
const sorted_posts = top_level_posts.toSorted(sort_posts);
entries.push(...sorted_posts);
for (const levels_key of levels_keys) {
if (levels_key === "_") {
// top level
const posts = second_level[levels_key]!;
entries.push(...posts)
// top level, already inserted
continue;
}
else {
const posts = second_level[levels_key]!;
const sorted_posts = posts.toSorted(sort_posts);
const parentEntry = {
path: "",
title: levels_key,
children: posts,
children: sorted_posts,
url: "",
};
entries.push(parentEntry);
}
}
/*
const index_page = posts_2.find(post => post.relative_file === "/index.md" || post.relative_file === "/index.mdx");
if (index_page === undefined) {
console.error("\n\nBuilding without an index page");
console.error(import.meta.dirname);
console.error("base_path", base_dir);
console.error("entries len", entries.length);
entries.forEach(e => console.log("\t"+e.url));
throw new Error("No entries");
function sort_posts(p1, p2) {
if (!!p1.frontmatter.order && !!p2.frontmatter.order) {
return p1.frontmatter.order > p2.frontmatter.order ? 1 : -1;
} else {
return p1.title > p2.title? 1 : -1;
}
}
const basePath = index_page?.url ?? "/";
*/
---
<BaseLayout title={frontmatter.title}>

View File

@ -1,126 +0,0 @@
---
import Navbar from "../components/Navbar.astro";
import BaseLayout from "./BaseLayout.astro";
import TOC from "../components/TOC.astro";
import Sidebar from "../components/Sidebar.astro";
const {
frontmatter,
headings,
posts: _posts,
indexSubpath,
basePath,
disable_container,
} = Astro.props;
const posts: Record<string, any>[] = _posts;
const indexPage = posts.find((post) => post.file.endsWith(indexSubpath));
if (indexPage === undefined) {
throw new Error(`No index page found at ${indexSubpath}`);
}
export type PageEntry = {
path: string;
title?: string;
children?: Array<PageEntry>;
};
const pagesIndex: Array<PageEntry> | undefined =
indexPage.frontmatter.pagesLayout;
if (pagesIndex === undefined) {
console.error(indexPage.frontmatter);
throw new Error(`No pagesLayout frontmatter found in ${indexSubpath}`);
}
function validateEntry(entry: PageEntry, basePath: string) {
if (!entry.children) {
// Attempt to get the page title from frontmatter
const pageData = posts.find(
(post) =>
post.file.endsWith(entry.path + ".md") ||
post.file.endsWith(entry.path + ".mdx"),
);
if (pageData === undefined) {
console.error(entry);
console.error(entry.path + ".md");
throw new Error(`No page found at ${entry.path}`);
}
// set the title
entry.title = pageData.frontmatter.title ?? "Title not set";
return;
}
// Recursively search for children
if (!entry.title) {
console.log(entry);
throw new Error(
`No title found in ${basePath + entry.path}, which is a folder and requires it.`,
);
}
const folderName = entry.title;
entry.children.forEach((child) =>
validateEntry(child, basePath + "/" + folderName),
);
}
for (const entry of pagesIndex) {
validateEntry(entry, basePath);
}
---
<BaseLayout title={frontmatter.title}>
<Navbar />
<div
class={`lg:grid lg:grid-cols-[14rem_auto_12rem] ${disable_container? "": "lg:container"} mx-auto font-display`}
>
<div
id="sidebar"
class="pt-12 h-screen lg:sticky top-0 fixed z-10 bg-c-bg w-60 lg:w-auto border-r-2 lg:border-0
border-c-border-1 -translate-x-64 lg:translate-x-0 transition-transform"
>
<nav class="py-4 pr-2 overflow-x-scroll h-[calc(100vh-3rem)]">
{
pagesIndex.map((entry) => (
<Sidebar entry={entry} basePath={basePath} />
))
}
</nav>
</div>
<main
class="pt-[3.5rem] pb-[10rem] lg:pl-12 lg:pr-4 markdown min-w-0 small-container mx-auto"
>
<slot />
</main>
<div
class="lg:pt-12 hidden lg:block pt-4 max-h-screen overflow-x-scroll sticky top-0"
>
<nav class="rounded-md lg:mt-10">
<h2 class="font-display font-medium pb-2 text-c-text-2">
On this page
</h2>
<TOC headings={headings} />
</nav>
</div>
</div>
<script>
import { highlightOnDom } from "./thpHighlighter";
document.addEventListener("DOMContentLoaded", highlightOnDom);
</script>
<script>
import { sidebarHighlight } from "./utils";
// Highlight the current url of the sidebar
document.addEventListener("DOMContentLoaded", sidebarHighlight);
</script>
</BaseLayout>

View File

@ -1,18 +0,0 @@
---
import PagesLayout from "./PagesLayout.astro";
const { frontmatter, headings } = Astro.props;
const posts = await Astro.glob("../pages/spec/**/*.{md,mdx}");
const indexSubpath = `/spec/index.mdx`;
---
<PagesLayout
frontmatter={frontmatter}
headings={headings}
posts={posts}
indexSubpath={indexSubpath}
basePath="/spec/"
>
<slot />
</PagesLayout>