mirror of
https://github.com/jackyzha0/quartz.git
synced 2026-02-04 06:25:41 -06:00
feat: Added ParentBreadcrumbs, which use a property called parent to build a breadcrumbs trail
This commit is contained in:
parent
65c5b27041
commit
5432777c51
118
quartz.layout.ts
118
quartz.layout.ts
@ -1,68 +1,68 @@
|
||||
import { PageLayout, SharedLayout } from "./quartz/cfg"
|
||||
import * as Component from "./quartz/components"
|
||||
import { PageLayout, SharedLayout } from "./quartz/cfg";
|
||||
import * as Component from "./quartz/components";
|
||||
|
||||
// components shared across all pages
|
||||
export const sharedPageComponents: SharedLayout = {
|
||||
head: Component.Head(),
|
||||
header: [],
|
||||
afterBody: [],
|
||||
footer: Component.Footer({
|
||||
links: {
|
||||
GitHub: "https://github.com/jackyzha0/quartz",
|
||||
"Discord Community": "https://discord.gg/cRFFHYye7t",
|
||||
},
|
||||
}),
|
||||
}
|
||||
head: Component.Head(),
|
||||
header: [],
|
||||
afterBody: [],
|
||||
footer: Component.Footer({
|
||||
links: {
|
||||
GitHub: "https://github.com/jackyzha0/quartz",
|
||||
"Discord Community": "https://discord.gg/cRFFHYye7t",
|
||||
},
|
||||
}),
|
||||
};
|
||||
|
||||
// components for pages that display a single page (e.g. a single note)
|
||||
export const defaultContentPageLayout: PageLayout = {
|
||||
beforeBody: [
|
||||
Component.ConditionalRender({
|
||||
component: Component.Breadcrumbs(),
|
||||
condition: (page) => page.fileData.slug !== "index",
|
||||
}),
|
||||
Component.ArticleTitle(),
|
||||
Component.ContentMeta(),
|
||||
Component.TagList(),
|
||||
],
|
||||
left: [
|
||||
Component.PageTitle(),
|
||||
Component.MobileOnly(Component.Spacer()),
|
||||
Component.Flex({
|
||||
components: [
|
||||
{
|
||||
Component: Component.Search(),
|
||||
grow: true,
|
||||
},
|
||||
{ Component: Component.Darkmode() },
|
||||
{ Component: Component.ReaderMode() },
|
||||
],
|
||||
}),
|
||||
Component.Explorer(),
|
||||
],
|
||||
right: [
|
||||
Component.Graph(),
|
||||
Component.DesktopOnly(Component.TableOfContents()),
|
||||
Component.Backlinks(),
|
||||
],
|
||||
}
|
||||
beforeBody: [
|
||||
Component.ConditionalRender({
|
||||
component: Component.ParentBreadcrumbs(),
|
||||
condition: (page) => page.fileData.slug !== "index",
|
||||
}),
|
||||
Component.ArticleTitle(),
|
||||
Component.ContentMeta(),
|
||||
Component.TagList(),
|
||||
],
|
||||
left: [
|
||||
Component.PageTitle(),
|
||||
Component.MobileOnly(Component.Spacer()),
|
||||
Component.Flex({
|
||||
components: [
|
||||
{
|
||||
Component: Component.Search(),
|
||||
grow: true,
|
||||
},
|
||||
{ Component: Component.Darkmode() },
|
||||
{ Component: Component.ReaderMode() },
|
||||
],
|
||||
}),
|
||||
Component.Explorer(),
|
||||
],
|
||||
right: [
|
||||
Component.Graph(),
|
||||
Component.DesktopOnly(Component.TableOfContents()),
|
||||
Component.Backlinks(),
|
||||
],
|
||||
};
|
||||
|
||||
// components for pages that display lists of pages (e.g. tags or folders)
|
||||
export const defaultListPageLayout: PageLayout = {
|
||||
beforeBody: [Component.Breadcrumbs(), Component.ArticleTitle(), Component.ContentMeta()],
|
||||
left: [
|
||||
Component.PageTitle(),
|
||||
Component.MobileOnly(Component.Spacer()),
|
||||
Component.Flex({
|
||||
components: [
|
||||
{
|
||||
Component: Component.Search(),
|
||||
grow: true,
|
||||
},
|
||||
{ Component: Component.Darkmode() },
|
||||
],
|
||||
}),
|
||||
Component.Explorer(),
|
||||
],
|
||||
right: [],
|
||||
}
|
||||
beforeBody: [Component.Breadcrumbs(), Component.ArticleTitle(), Component.ContentMeta()],
|
||||
left: [
|
||||
Component.PageTitle(),
|
||||
Component.MobileOnly(Component.Spacer()),
|
||||
Component.Flex({
|
||||
components: [
|
||||
{
|
||||
Component: Component.Search(),
|
||||
grow: true,
|
||||
},
|
||||
{ Component: Component.Darkmode() },
|
||||
],
|
||||
}),
|
||||
Component.Explorer(),
|
||||
],
|
||||
right: [],
|
||||
};
|
||||
|
||||
97
quartz/components/ParentBreadcrumbs.tsx
Normal file
97
quartz/components/ParentBreadcrumbs.tsx
Normal file
@ -0,0 +1,97 @@
|
||||
import { QuartzComponent, QuartzComponentConstructor, QuartzComponentProps } from "./types";
|
||||
import { QuartzPluginData } from "../plugins/vfile";
|
||||
import { classNames } from "../util/lang";
|
||||
import { resolveRelative, simplifySlug, FullSlug, SimpleSlug } from "../util/path";
|
||||
import style from "./styles/breadcrumbs.scss";
|
||||
|
||||
interface ParentBreadcrumbsOptions {
|
||||
spacerSymbol?: string;
|
||||
rootName?: string;
|
||||
resolveFrontmatterTitle?: boolean;
|
||||
}
|
||||
|
||||
const defaultOptions: ParentBreadcrumbsOptions = {
|
||||
spacerSymbol: "❯",
|
||||
rootName: "Home",
|
||||
resolveFrontmatterTitle: true,
|
||||
};
|
||||
|
||||
export default ((opts?: ParentBreadcrumbsOptions) => {
|
||||
const options = { ...defaultOptions, ...opts };
|
||||
|
||||
const ParentBreadcrumbs: QuartzComponent = ({
|
||||
fileData,
|
||||
allFiles,
|
||||
displayClass,
|
||||
}: QuartzComponentProps) => {
|
||||
|
||||
const parseWikiLink = (content: string): string => {
|
||||
if (!content) return "";
|
||||
let clean = content.trim().replace(/^["']|["']$/g, "");
|
||||
clean = clean.replace(/^\[\[|\]\]$/g, "");
|
||||
return clean.split("|")[0];
|
||||
};
|
||||
|
||||
const findFile = (name: string) => {
|
||||
const targetSlug = simplifySlug(name as FullSlug);
|
||||
|
||||
return allFiles.find((f: QuartzPluginData) => {
|
||||
const fSlug = simplifySlug(f.slug!);
|
||||
return fSlug === targetSlug || fSlug.endsWith(targetSlug) || f.frontmatter?.title === name;
|
||||
});
|
||||
};
|
||||
|
||||
const crumbs: Array<{ displayName: string; path: string; }> = [];
|
||||
let current = fileData;
|
||||
const visited = new Set<string>();
|
||||
if (current.slug) visited.add(current.slug);
|
||||
|
||||
while (current && current.frontmatter?.parent) {
|
||||
const parentLink = parseWikiLink(current.frontmatter.parent as string);
|
||||
const parentFile = findFile(parentLink);
|
||||
|
||||
if (parentFile && parentFile.slug && !visited.has(parentFile.slug)) {
|
||||
visited.add(parentFile.slug);
|
||||
crumbs.push({
|
||||
displayName: options.resolveFrontmatterTitle
|
||||
? parentFile.frontmatter?.title ?? parentFile.slug
|
||||
: parentFile.slug,
|
||||
path: resolveRelative(fileData.slug!, parentFile.slug!)
|
||||
});
|
||||
current = parentFile;
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (current.slug !== "index") {
|
||||
crumbs.push({
|
||||
displayName: options.rootName!,
|
||||
path: resolveRelative(fileData.slug!, "index" as SimpleSlug)
|
||||
});
|
||||
}
|
||||
|
||||
crumbs.reverse();
|
||||
|
||||
if (crumbs.length === 0 && fileData.slug === "index") {
|
||||
return <></>;
|
||||
}
|
||||
|
||||
return (
|
||||
<nav class={classNames(displayClass, "breadcrumb-container")} aria-label="breadcrumbs">
|
||||
{crumbs.map((crumb, index) => (
|
||||
<div class="breadcrumb-element">
|
||||
<a href={crumb.path}>{crumb.displayName}</a>
|
||||
{index !== crumbs.length && <p>{options.spacerSymbol}</p>}
|
||||
</div>
|
||||
))}
|
||||
<div class="breadcrumb-element">
|
||||
<p>{fileData.frontmatter?.title}</p>
|
||||
</div>
|
||||
</nav>
|
||||
);
|
||||
};
|
||||
|
||||
ParentBreadcrumbs.css = style;
|
||||
return ParentBreadcrumbs;
|
||||
}) satisfies QuartzComponentConstructor
|
||||
@ -1,53 +1,55 @@
|
||||
import Content from "./pages/Content"
|
||||
import TagContent from "./pages/TagContent"
|
||||
import FolderContent from "./pages/FolderContent"
|
||||
import NotFound from "./pages/404"
|
||||
import ArticleTitle from "./ArticleTitle"
|
||||
import Darkmode from "./Darkmode"
|
||||
import ReaderMode from "./ReaderMode"
|
||||
import Head from "./Head"
|
||||
import PageTitle from "./PageTitle"
|
||||
import ContentMeta from "./ContentMeta"
|
||||
import Spacer from "./Spacer"
|
||||
import TableOfContents from "./TableOfContents"
|
||||
import Explorer from "./Explorer"
|
||||
import TagList from "./TagList"
|
||||
import Graph from "./Graph"
|
||||
import Backlinks from "./Backlinks"
|
||||
import Search from "./Search"
|
||||
import Footer from "./Footer"
|
||||
import DesktopOnly from "./DesktopOnly"
|
||||
import MobileOnly from "./MobileOnly"
|
||||
import RecentNotes from "./RecentNotes"
|
||||
import Breadcrumbs from "./Breadcrumbs"
|
||||
import Comments from "./Comments"
|
||||
import Flex from "./Flex"
|
||||
import ConditionalRender from "./ConditionalRender"
|
||||
import Content from "./pages/Content";
|
||||
import TagContent from "./pages/TagContent";
|
||||
import FolderContent from "./pages/FolderContent";
|
||||
import NotFound from "./pages/404";
|
||||
import ArticleTitle from "./ArticleTitle";
|
||||
import Darkmode from "./Darkmode";
|
||||
import ReaderMode from "./ReaderMode";
|
||||
import Head from "./Head";
|
||||
import PageTitle from "./PageTitle";
|
||||
import ContentMeta from "./ContentMeta";
|
||||
import Spacer from "./Spacer";
|
||||
import TableOfContents from "./TableOfContents";
|
||||
import Explorer from "./Explorer";
|
||||
import TagList from "./TagList";
|
||||
import Graph from "./Graph";
|
||||
import Backlinks from "./Backlinks";
|
||||
import Search from "./Search";
|
||||
import Footer from "./Footer";
|
||||
import DesktopOnly from "./DesktopOnly";
|
||||
import MobileOnly from "./MobileOnly";
|
||||
import RecentNotes from "./RecentNotes";
|
||||
import Breadcrumbs from "./Breadcrumbs";
|
||||
import Comments from "./Comments";
|
||||
import Flex from "./Flex";
|
||||
import ConditionalRender from "./ConditionalRender";
|
||||
import ParentBreadcrumbs from "./ParentBreadcrumbs";
|
||||
|
||||
export {
|
||||
ArticleTitle,
|
||||
Content,
|
||||
TagContent,
|
||||
FolderContent,
|
||||
Darkmode,
|
||||
ReaderMode,
|
||||
Head,
|
||||
PageTitle,
|
||||
ContentMeta,
|
||||
Spacer,
|
||||
TableOfContents,
|
||||
Explorer,
|
||||
TagList,
|
||||
Graph,
|
||||
Backlinks,
|
||||
Search,
|
||||
Footer,
|
||||
DesktopOnly,
|
||||
MobileOnly,
|
||||
RecentNotes,
|
||||
NotFound,
|
||||
Breadcrumbs,
|
||||
Comments,
|
||||
Flex,
|
||||
ConditionalRender,
|
||||
}
|
||||
ParentBreadcrumbs,
|
||||
ArticleTitle,
|
||||
Content,
|
||||
TagContent,
|
||||
FolderContent,
|
||||
Darkmode,
|
||||
ReaderMode,
|
||||
Head,
|
||||
PageTitle,
|
||||
ContentMeta,
|
||||
Spacer,
|
||||
TableOfContents,
|
||||
Explorer,
|
||||
TagList,
|
||||
Graph,
|
||||
Backlinks,
|
||||
Search,
|
||||
Footer,
|
||||
DesktopOnly,
|
||||
MobileOnly,
|
||||
RecentNotes,
|
||||
NotFound,
|
||||
Breadcrumbs,
|
||||
Comments,
|
||||
Flex,
|
||||
ConditionalRender,
|
||||
};
|
||||
|
||||
Loading…
Reference in New Issue
Block a user