From ea92ed4f45e6e863a432447a977c33c6319423bc Mon Sep 17 00:00:00 2001
From: Cao Mingjun <me@caomingjun.com>
Date: Wed, 10 Jul 2024 00:42:33 +0000
Subject: [PATCH] feat: Allow custom sorting of FolderPage and TagPage (#1250)
---
quartz/components/PageList.tsx | 6 +
quartz/plugins/emitters/tagPage.tsx | 8 +
quartz/plugins/emitters/folderPage.tsx | 8 +
quartz/components/pages/TagContent.tsx | 196 ++++++++++++++++++++++++------------------------
quartz/components/pages/FolderContent.tsx | 3
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 @@
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 { 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 @@
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 { 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 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 { 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 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 { 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,
}
--
Gitblit v1.10.0