From ea92ed4f45e6e863a432447a977c33c6319423bc Mon Sep 17 00:00:00 2001 From: Cao Mingjun Date: Wed, 10 Jul 2024 08:42:33 +0800 Subject: [PATCH] feat: Allow custom sorting of FolderPage and TagPage (#1250) --- quartz/components/PageList.tsx | 6 +- quartz/components/pages/FolderContent.tsx | 3 + quartz/components/pages/TagContent.tsx | 196 +++++++++++----------- quartz/plugins/emitters/folderPage.tsx | 8 +- quartz/plugins/emitters/tagPage.tsx | 8 +- 5 files changed, 116 insertions(+), 105 deletions(-) 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) => { 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 = new Map() - for (const tag of tags) { - tagItemMap.set(tag, allPagesWithTag(tag)) - } - return ( -
-
-

{content}

-
-

{i18n(cfg.locale).pages.tagContent.totalTags({ count: tags.length })}

-
- {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 ( -
-

- - {tag} - -

- {content &&

{content}

} -
-

- {i18n(cfg.locale).pages.tagContent.itemsUnderTag({ count: pages.length })} - {pages.length > numPages && ( - <> - {" "} - - {i18n(cfg.locale).pages.tagContent.showingFirst({ count: numPages })} - - - )} -

- -
-
- ) - })} -
-
- ) - } 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 ( -
-
{content}
-
-

{i18n(cfg.locale).pages.tagContent.itemsUnderTag({ count: pages.length })}

+ 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 = new Map() + for (const tag of tags) { + tagItemMap.set(tag, allPagesWithTag(tag)) + } + return ( +
+
+

{content}

+
+

{i18n(cfg.locale).pages.tagContent.totalTags({ count: tags.length })}

- + {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 ( +
+

+ + {tag} + +

+ {content &&

{content}

} +
+

+ {i18n(cfg.locale).pages.tagContent.itemsUnderTag({ count: pages.length })} + {pages.length > numPages && ( + <> + {" "} + + {i18n(cfg.locale).pages.tagContent.showingFirst({ count: numPages })} + + + )} +

+ +
+
+ ) + })}
-
- ) - } -} + ) + } else { + const pages = allPagesWithTag(tag) + const listProps = { + ...props, + allFiles: pages, + } -TagContent.css = style + PageList.css -export default (() => TagContent) satisfies QuartzComponentConstructor + return ( +
+
{content}
+
+

{i18n(cfg.locale).pages.tagContent.itemsUnderTag({ count: pages.length })}

+
+ +
+
+
+ ) + } + } + + 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> = (userOpts) => { +export const FolderPage: QuartzEmitterPlugin< + Partial & { 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> = (userOpts) => { +export const TagPage: QuartzEmitterPlugin< + Partial & { sort?: (f1: QuartzPluginData, f2: QuartzPluginData) => number } +> = (userOpts) => { const opts: FullPageLayout = { ...sharedPageComponents, ...defaultListPageLayout, - pageBody: TagContent(), + pageBody: TagContent({ sort: userOpts?.sort }), ...userOpts, }