From 05ef06f95c02fe60a00075ca4c4973d17e373a9f Mon Sep 17 00:00:00 2001 From: Martin Berg Alstad <git@martials.no> Date: Sat, 15 Mar 2025 15:34:24 +0100 Subject: [PATCH] :sparkles: Breadcrumbs with navigation --- TODO.md | 2 +- src/components/Breadcrumb.astro | 38 +++++++++++++++++++++++++++++++++ src/layouts/Layout.astro | 4 ++-- src/utils/linking.ts | 21 ++++++++++++++++++ 4 files changed, 62 insertions(+), 3 deletions(-) create mode 100644 src/components/Breadcrumb.astro diff --git a/TODO.md b/TODO.md index e4cf9cd..418b854 100644 --- a/TODO.md +++ b/TODO.md @@ -13,7 +13,7 @@ ## Layout - [ ] Dark mode toggle -- [ ] Navigate using pathname / breadcrumbs +- [x] Navigate using pathname / breadcrumbs - [ ] Better style for \<code /> blocks ## Accessibility diff --git a/src/components/Breadcrumb.astro b/src/components/Breadcrumb.astro new file mode 100644 index 0000000..19ab7c6 --- /dev/null +++ b/src/components/Breadcrumb.astro @@ -0,0 +1,38 @@ +--- +import { type NavLink, resolvePathname } from "@/utils/linking" +import LocaleLink from "@/components/links/LocaleLink.astro" + +const pathname = resolvePathname(Astro.originPathname) + +let paths: string[] +if (pathname === "/") { + paths = ["~"] +} else { + paths = ["~", ...pathname.split("/").slice(1)] +} + +function getLink(path: string): NavLink { + switch (path) { + case "~": + return "/" + default: + return `/${path}` as NavLink + } +} +--- + +<div> + { + paths.map((path, index) => ( + <span> + {index != paths.length - 1 ? ( + <span> + <LocaleLink to={getLink(path)}>{path}</LocaleLink>/ + </span> + ) : ( + path + )} + </span> + )) + } +</div> diff --git a/src/layouts/Layout.astro b/src/layouts/Layout.astro index 0f1a302..285e2e9 100644 --- a/src/layouts/Layout.astro +++ b/src/layouts/Layout.astro @@ -1,8 +1,8 @@ --- import Footer from "@/components/Footer.astro" import Header from "@/components/header/Header.astro" +import Breadcrumb from "@/components/Breadcrumb.astro" import { languageTag } from "@/paraglide/runtime" -import { resolvePathname } from "@/utils/linking" interface Props { title: string @@ -33,7 +33,7 @@ const mainClass = <Header /> <main class:list={[mainClass, clazz]}> <h1 class="text-center not-sm:hidden"> - ~{resolvePathname(Astro.originPathname)} + <Breadcrumb /> </h1> <div class="my-5"> <slot /> diff --git a/src/utils/linking.ts b/src/utils/linking.ts index aac1a06..0f8ddaf 100644 --- a/src/utils/linking.ts +++ b/src/utils/linking.ts @@ -22,6 +22,8 @@ const paths: Set<NavLink> = new Set([ "/uses", ]) +const projectPaths: Set<string> = new Set<string>(["homepage", "sb1budget"]) + /** * Defines the localized pathnames for the site. * The key must be used to navigate to the correct path. @@ -63,3 +65,22 @@ export function resolvePathname(pathname: string): AbsolutePathname { } return pathname as AbsolutePathname } + +export function isAbsolutePathname(path: string): path is AbsolutePathname { + return path.startsWith("/") +} + +export function isNavLink(path: string): path is NavLink { + if (path.startsWith("/en")) { + path = path.slice(2) + } + if (paths.has(path as NavLink)) { + return true + } + const pathSplit = path.split("/").slice(1) + return ( + pathSplit.length === 2 && + pathSplit[0] === "projects" && + projectPaths.has(pathSplit[1]) + ) +}