diff --git a/quartz/components/PageList.tsx b/quartz/components/PageList.tsx index 1e5d232..2512b62 100644 --- a/quartz/components/PageList.tsx +++ b/quartz/components/PageList.tsx @@ -27,10 +27,12 @@ export function byDateAndAlphabetical( type Props = { limit?: number + sort?: (f1: QuartzPluginData, f2: QuartzPluginData) => number } & QuartzComponentProps -export const PageList: QuartzComponent = ({ cfg, fileData, allFiles, limit }: Props) => { - let list = allFiles.sort(byDateAndAlphabetical(cfg)) +export const PageList: QuartzComponent = ({ cfg, fileData, allFiles, limit, sort }: Props) => { + const sorter = sort ?? byDateAndAlphabetical(cfg) + let list = allFiles.sort(sorter) if (limit) { list = list.slice(0, limit) } diff --git a/quartz/components/pages/FolderContent.tsx b/quartz/components/pages/FolderContent.tsx index a13f135..e01496c 100644 --- a/quartz/components/pages/FolderContent.tsx +++ b/quartz/components/pages/FolderContent.tsx @@ -7,12 +7,14 @@ import { stripSlashes, simplifySlug } from "../../util/path" import { Root } from "hast" import { htmlToJsx } from "../../util/jsx" import { i18n } from "../../i18n" +import { QuartzPluginData } from "../../plugins/vfile" interface FolderContentOptions { /** * Whether to display number of folders */ showFolderCount: boolean + sort?: (f1: QuartzPluginData, f2: QuartzPluginData) => number } const defaultOptions: FolderContentOptions = { @@ -37,6 +39,7 @@ export default ((opts?: Partial<FolderContentOptions>) => { const classes = ["popover-hint", ...cssClasses].join(" ") const listProps = { ...props, + sort: options.sort, allFiles: allPagesInFolder, } diff --git a/quartz/components/pages/TagContent.tsx b/quartz/components/pages/TagContent.tsx index 9e04359..7598b13 100644 --- a/quartz/components/pages/TagContent.tsx +++ b/quartz/components/pages/TagContent.tsx @@ -7,107 +7,109 @@ import { Root } from "hast" import { htmlToJsx } from "../../util/jsx" import { i18n } from "../../i18n" -const numPages = 10 -const TagContent: QuartzComponent = (props: QuartzComponentProps) => { - const { tree, fileData, allFiles, cfg } = props - const slug = fileData.slug +export default ((opts?: { sort?: (f1: QuartzPluginData, f2: QuartzPluginData) => number }) => { + const numPages = 10 + const TagContent: QuartzComponent = (props: QuartzComponentProps) => { + const { tree, fileData, allFiles, cfg } = props + const slug = fileData.slug - if (!(slug?.startsWith("tags/") || slug === "tags")) { - throw new Error(`Component "TagContent" tried to render a non-tag page: ${slug}`) - } - - const tag = simplifySlug(slug.slice("tags/".length) as FullSlug) - const allPagesWithTag = (tag: string) => - allFiles.filter((file) => - (file.frontmatter?.tags ?? []).flatMap(getAllSegmentPrefixes).includes(tag), - ) - - const content = - (tree as Root).children.length === 0 - ? fileData.description - : htmlToJsx(fileData.filePath!, tree) - const cssClasses: string[] = fileData.frontmatter?.cssclasses ?? [] - const classes = ["popover-hint", ...cssClasses].join(" ") - if (tag === "/") { - const tags = [ - ...new Set( - allFiles.flatMap((data) => data.frontmatter?.tags ?? []).flatMap(getAllSegmentPrefixes), - ), - ].sort((a, b) => a.localeCompare(b)) - const tagItemMap: Map<string, QuartzPluginData[]> = new Map() - for (const tag of tags) { - tagItemMap.set(tag, allPagesWithTag(tag)) - } - return ( - <div class={classes}> - <article> - <p>{content}</p> - </article> - <p>{i18n(cfg.locale).pages.tagContent.totalTags({ count: tags.length })}</p> - <div> - {tags.map((tag) => { - const pages = tagItemMap.get(tag)! - const listProps = { - ...props, - allFiles: pages, - } - - const contentPage = allFiles.filter((file) => file.slug === `tags/${tag}`).at(0) - - const root = contentPage?.htmlAst - const content = - !root || root?.children.length === 0 - ? contentPage?.description - : htmlToJsx(contentPage.filePath!, root) - - return ( - <div> - <h2> - <a class="internal tag-link" href={`../tags/${tag}`}> - {tag} - </a> - </h2> - {content && <p>{content}</p>} - <div class="page-listing"> - <p> - {i18n(cfg.locale).pages.tagContent.itemsUnderTag({ count: pages.length })} - {pages.length > numPages && ( - <> - {" "} - <span> - {i18n(cfg.locale).pages.tagContent.showingFirst({ count: numPages })} - </span> - </> - )} - </p> - <PageList limit={numPages} {...listProps} /> - </div> - </div> - ) - })} - </div> - </div> - ) - } else { - const pages = allPagesWithTag(tag) - const listProps = { - ...props, - allFiles: pages, + if (!(slug?.startsWith("tags/") || slug === "tags")) { + throw new Error(`Component "TagContent" tried to render a non-tag page: ${slug}`) } - return ( - <div class={classes}> - <article>{content}</article> - <div class="page-listing"> - <p>{i18n(cfg.locale).pages.tagContent.itemsUnderTag({ count: pages.length })}</p> + const tag = simplifySlug(slug.slice("tags/".length) as FullSlug) + const allPagesWithTag = (tag: string) => + allFiles.filter((file) => + (file.frontmatter?.tags ?? []).flatMap(getAllSegmentPrefixes).includes(tag), + ) + + const content = + (tree as Root).children.length === 0 + ? fileData.description + : htmlToJsx(fileData.filePath!, tree) + const cssClasses: string[] = fileData.frontmatter?.cssclasses ?? [] + const classes = ["popover-hint", ...cssClasses].join(" ") + if (tag === "/") { + const tags = [ + ...new Set( + allFiles.flatMap((data) => data.frontmatter?.tags ?? []).flatMap(getAllSegmentPrefixes), + ), + ].sort((a, b) => a.localeCompare(b)) + const tagItemMap: Map<string, QuartzPluginData[]> = new Map() + for (const tag of tags) { + tagItemMap.set(tag, allPagesWithTag(tag)) + } + return ( + <div class={classes}> + <article> + <p>{content}</p> + </article> + <p>{i18n(cfg.locale).pages.tagContent.totalTags({ count: tags.length })}</p> <div> - <PageList {...listProps} /> + {tags.map((tag) => { + const pages = tagItemMap.get(tag)! + const listProps = { + ...props, + allFiles: pages, + } + + const contentPage = allFiles.filter((file) => file.slug === `tags/${tag}`).at(0) + + const root = contentPage?.htmlAst + const content = + !root || root?.children.length === 0 + ? contentPage?.description + : htmlToJsx(contentPage.filePath!, root) + + return ( + <div> + <h2> + <a class="internal tag-link" href={`../tags/${tag}`}> + {tag} + </a> + </h2> + {content && <p>{content}</p>} + <div class="page-listing"> + <p> + {i18n(cfg.locale).pages.tagContent.itemsUnderTag({ count: pages.length })} + {pages.length > numPages && ( + <> + {" "} + <span> + {i18n(cfg.locale).pages.tagContent.showingFirst({ count: numPages })} + </span> + </> + )} + </p> + <PageList limit={numPages} {...listProps} sort={opts?.sort} /> + </div> + </div> + ) + })} </div> </div> - </div> - ) - } -} + ) + } else { + const pages = allPagesWithTag(tag) + const listProps = { + ...props, + allFiles: pages, + } -TagContent.css = style + PageList.css -export default (() => TagContent) satisfies QuartzComponentConstructor + return ( + <div class={classes}> + <article>{content}</article> + <div class="page-listing"> + <p>{i18n(cfg.locale).pages.tagContent.itemsUnderTag({ count: pages.length })}</p> + <div> + <PageList {...listProps} /> + </div> + </div> + </div> + ) + } + } + + TagContent.css = style + PageList.css + return TagContent +}) satisfies QuartzComponentConstructor diff --git a/quartz/plugins/emitters/folderPage.tsx b/quartz/plugins/emitters/folderPage.tsx index d892b28..bd17e57 100644 --- a/quartz/plugins/emitters/folderPage.tsx +++ b/quartz/plugins/emitters/folderPage.tsx @@ -3,7 +3,7 @@ import { QuartzComponentProps } from "../../components/types" import HeaderConstructor from "../../components/Header" import BodyConstructor from "../../components/Body" import { pageResources, renderPage } from "../../components/renderPage" -import { ProcessedContent, defaultProcessedContent } from "../vfile" +import { ProcessedContent, QuartzPluginData, defaultProcessedContent } from "../vfile" import { FullPageLayout } from "../../cfg" import path from "path" import { @@ -21,11 +21,13 @@ import { write } from "./helpers" import { i18n } from "../../i18n" import DepGraph from "../../depgraph" -export const FolderPage: QuartzEmitterPlugin<Partial<FullPageLayout>> = (userOpts) => { +export const FolderPage: QuartzEmitterPlugin< + Partial<FullPageLayout> & { sort?: (f1: QuartzPluginData, f2: QuartzPluginData) => number } +> = (userOpts) => { const opts: FullPageLayout = { ...sharedPageComponents, ...defaultListPageLayout, - pageBody: FolderContent(), + pageBody: FolderContent({ sort: userOpts?.sort }), ...userOpts, } diff --git a/quartz/plugins/emitters/tagPage.tsx b/quartz/plugins/emitters/tagPage.tsx index d88d072..3994140 100644 --- a/quartz/plugins/emitters/tagPage.tsx +++ b/quartz/plugins/emitters/tagPage.tsx @@ -3,7 +3,7 @@ import { QuartzComponentProps } from "../../components/types" import HeaderConstructor from "../../components/Header" import BodyConstructor from "../../components/Body" import { pageResources, renderPage } from "../../components/renderPage" -import { ProcessedContent, defaultProcessedContent } from "../vfile" +import { ProcessedContent, QuartzPluginData, defaultProcessedContent } from "../vfile" import { FullPageLayout } from "../../cfg" import { FilePath, @@ -18,11 +18,13 @@ import { write } from "./helpers" import { i18n } from "../../i18n" import DepGraph from "../../depgraph" -export const TagPage: QuartzEmitterPlugin<Partial<FullPageLayout>> = (userOpts) => { +export const TagPage: QuartzEmitterPlugin< + Partial<FullPageLayout> & { sort?: (f1: QuartzPluginData, f2: QuartzPluginData) => number } +> = (userOpts) => { const opts: FullPageLayout = { ...sharedPageComponents, ...defaultListPageLayout, - pageBody: TagContent(), + pageBody: TagContent({ sort: userOpts?.sort }), ...userOpts, }