Quartz 内置一个资源管理器组件,可让您浏览站点上的所有文件和文件夹。它支持嵌套文件夹结构,并具有高度可定制性。
默认情况下,资源管理器会显示页面上的所有文件夹和文件。若要在不同位置显示资源管理器,可以编辑layout布局文件。
文件夹的显示名称由folder/index.md
中的title
前端元数据字段决定(更多细节详见创建内容)。如果该文件不存在或未包含前端元数据,则会使用本地文件夹名称替代。
Info
资源管理器默认使用本地存储来保存状态,这是为了确保在不同页面间导航时的流畅体验。
要清除本地存储中的资源管理器状态,请删除
fileTree
条目(基于Chromium的浏览器中删除本地存储键值的指南可参考此处)。您可以通过传入参数useSavedState: false
来禁用此功能。
自定义配置
大部分配置可以通过向Component.Explorer()
传递选项来实现。
例如,以下是默认配置的示例:
Component.Explorer({
title: "Explorer", // title of the explorer component
folderClickBehavior: "collapse", // what happens when you click a folder ("link" to navigate to folder page on click or "collapse" to collapse folder on click)
folderDefaultState: "collapsed", // default state of folders ("collapsed" or "open")
useSavedState: true, // whether to use local storage to save "state" (which folders are opened) of explorer
// Sort order: folders first, then files. Sort folders and files alphabetically
sortFn: (a, b) => {
... // default implementation shown later
},
filterFn: filterFn: (node) => node.name !== "tags", // filters out 'tags' folder
mapFn: undefined,
// what order to apply functions in
order: ["filter", "map", "sort"],
})
当传入自定义选项时,如果您希望保留某个字段的默认值,可以省略该字段的全部或部分配置。
想要进行更深入的定制?
- 移除资源管理器:在
quartz.layout.ts
中删除Component.Explorer()
- (可选):移除资源管理器组件后,您可以将Table of Contents组件移回布局的
left
部分
- (可选):移除资源管理器组件后,您可以将Table of Contents组件移回布局的
- 修改排序、过滤和映射行为:详见高级定制
- 组件构成:
- 包装器组件(外层组件,生成文件树等):
quartz/components/Explorer.tsx
- 资源管理器节点(递归结构,可以是文件夹或文件):
quartz/components/ExplorerNode.tsx
- 包装器组件(外层组件,生成文件树等):
- 样式:
quartz/components/styles/explorer.scss
- 脚本:
quartz/components/scripts/explorer.inline.ts
高级定制
该组件允许您完全自定义其所有行为。您可以传入自定义的 sort
(排序)、filter
(过滤)和 map
(映射)函数。
所有可传入的函数都作用于 FileNode
类,该类具有以下属性:
export class FileNode {
children: FileNode[] // children of current node
name: string // last part of slug
displayName: string // what actually should be displayed in the explorer
file: QuartzPluginData | null // if node is a file, this is the file's metadata. See `QuartzPluginData` for more detail
depth: number // depth of current node
... // rest of implementation
}
所有可传递的函数均为可选。默认情况下,仅会使用 sort
函数:
// Sort order: folders first, then files. Sort folders and files alphabetically
Component.Explorer({
sortFn: (a, b) => {
if ((!a.file && !b.file) || (a.file && b.file)) {
// sensitivity: "base": Only strings that differ in base letters compare as unequal. Examples: a ≠ b, a = á, a = A
// numeric: true: Whether numeric collation should be used, such that "1" < "2" < "10"
return a.displayName.localeCompare(b.displayName, undefined, {
numeric: true,
sensitivity: "base",
})
}
if (a.file && !b.file) {
return 1
} else {
return -1
}
},
})
你可以为 sortFn
、filterFn
和 mapFn
传递自定义函数。所有函数都将按照 order
选项指定的顺序执行(详见自定义)。这些函数的行为类似于它们对应的 Array.prototype
方法,不同之处在于它们会直接修改整个 FileNode
树,而不是返回新的树。
如需了解如何使用 sort
、filter
和 map
的更多信息,可参考:
类型定义如下所示:
interface FileNode {
name: string
children?: FileNode[]
meta?: {
[key: string]: any
isRoot?: boolean
isDirectory?: boolean
}
}
sortFn: (a: FileNode, b: FileNode) => number
filterFn: (node: FileNode) => boolean
mapFn: (node: FileNode) => void
Tip
你可以像这样检查一个
FileNode
是文件夹还是文件:if (node.file) { // node 是文件 } else { // node 是文件夹 }
基础示例
这些示例展示了sort
、map
和filter
的基本用法。
使用sort
将文件排在前面
通过这个示例,资源管理器会按字母顺序排序所有内容,但将所有文件排列在文件夹之上。
Component.Explorer({
sortFn: (a, b) => {
if ((!a.file && !b.file) || (a.file && b.file)) {
return a.displayName.localeCompare(b.displayName)
}
if (a.file && !b.file) {
return -1
} else {
return 1
}
},
})
更改显示名称(map
)
使用此示例,所有 FileNodes
(文件夹 + 文件)的显示名称将被转换为全大写。
const transformedTree = {
name: "root",
children: [
{ name: "folder1", children: [{ name: "file1.txt" }] },
{ name: "folder2", children: [{ name: "file2.txt" }] },
],
}
const result = map(transformedTree, (node) => ({
...node,
displayName: node.name.toUpperCase(),
}))
Component.Explorer({
mapFn: (node) => {
node.displayName = node.displayName.toUpperCase()
},
})
移除元素列表 (filter
)
通过此示例,您可以通过使用 omit
集合提供文件夹/文件数组来从资源管理器中移除元素。
Component.Explorer({
filterFn: (node) => {
// set containing names of everything you want to filter out
const omit = new Set(["authoring content", "tags", "hosting"])
return !omit.has(node.name.toLowerCase())
},
})
您可以通过修改 omit
集合中的条目来自定义此功能。只需添加所有要移除的文件夹或文件名即可。
按标签移除文件
您可以通过 node.file?.frontmatter?
访问文件的 frontmatter。这样您就可以根据 frontmatter 来筛选文件,例如根据标签进行筛选。
Component.Explorer({
filterFn: (node) => {
// exclude files with the tag "explorerexclude"
return node.file?.frontmatter?.tags?.includes("explorerexclude") !== true
},
})
在资源管理器中显示所有元素
要覆盖默认过滤函数(该函数会从资源管理器中移除 tags
文件夹),可以将过滤函数设置为 undefined
。
Component.Explorer({
filterFn: undefined, // apply no filter function, every file and folder will visible
})
Advanced examples
Tip
When writing more complicated functions, the
layout
file can start to look very cramped. You can fix this by defining your functions in another file.functions.ts import { Options } from "./quartz/components/ExplorerNode" export const mapFn: Options["mapFn"] = (node) => { // implement your function here } export const filterFn: Options["filterFn"] = (node) => { // implement your function here } export const sortFn: Options["sortFn"] = (a, b) => { // implement your function here }
You can then import them like this:
quartz.layout.ts import { mapFn, filterFn, sortFn } from "./functions.ts" Component.Explorer({ mapFn: mapFn, filterFn: filterFn, sortFn: sortFn, })
Add emoji prefix
To add emoji prefixes (📁 for folders, 📄 for files), you could use a map function like this:
Component.Explorer({
mapFn: (node) => {
// dont change name of root node
if (node.depth > 0) {
// set emoji for file/folder
if (node.file) {
node.displayName = "📄 " + node.displayName
} else {
node.displayName = "📁 " + node.displayName
}
}
},
})
综合应用
在本示例中,我们将通过组合使用前文示例中的功能来定制文件浏览器:添加表情符号前缀、过滤特定文件夹以及排序时将文件放在文件夹前面。
Component.Explorer({
filterFn: sampleFilterFn,
mapFn: sampleMapFn,
sortFn: sampleSortFn,
order: ["filter", "sort", "map"],
})
注意我们在此处如何自定义 order
数组。这样做是因为默认排序会将 sort
函数最后应用。虽然这通常效果良好,但在此处会导致意外行为,因为我们修改了所有显示名称的首字符。在我们的示例中,sort
将基于表情符号前缀而非第一个_实际_字符进行排序。
为解决这个问题,我们调整了顺序,在 map
函数修改显示名称之前先应用 sort
函数。
使用预定义排序规则的 sort
函数
以下是另一个示例,其中使用包含文件/文件夹名称(作为 slug)的映射来定义 Quartz 资源管理器的排序顺序。所有未在 nameOrderMap
中列出的文件/文件夹将出现在该文件夹层级的顶部。
值得一提的是,nameOrderMap
中设置的数字越小,该条目在资源管理器中的位置就越靠前。通过将每个文件夹/文件递增 100,可以更轻松地在文件夹内排序文件。最后,此示例仍允许您使用 mapFn
或 frontmatter 标题来更改显示名称,因为它使用 slug 作为 nameOrderMap
的基准(这不会受显示名称更改的影响)。
Component.Explorer({
sortFn: (a, b) => {
const nameOrderMap: Record<string, number> = {
"poetry-folder": 100,
"essay-folder": 200,
"research-paper-file": 201,
"dinosaur-fossils-file": 300,
"other-folder": 400,
}
let orderA = 0
let orderB = 0
if (a.file && a.file.slug) {
orderA = nameOrderMap[a.file.slug] || 0
} else if (a.name) {
orderA = nameOrderMap[a.name] || 0
}
if (b.file && b.file.slug) {
orderB = nameOrderMap[b.file.slug] || 0
} else if (b.name) {
orderB = nameOrderMap[b.name] || 0
}
return orderA - orderB
},
})
作为参考,以下示例展示了石英资源管理器窗口的显示效果:
📖 Poetry Folder
📑 Essay Folder
⚗️ Research Paper File
🦴 Dinosaur Fossils File
🔮 Other Folder
文件结构如下所示:
index.md
poetry-folder
index.md
essay-folder
index.md
research-paper-file.md
dinosaur-fossils-file.md
other-folder
index.md