This commit is contained in:
Alexei Chernyavsky 2025-12-14 06:17:25 +03:00 committed by GitHub
commit 9518117311
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
3 changed files with 19 additions and 7 deletions

View File

@ -26,6 +26,7 @@ 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")
maxExpandLevel: 0, // number of folder levels expanded initially when folders are open; 0 means no limit (all levels expanded)
useSavedState: true, // whether to use local storage to save "state" (which folders are opened) of explorer
// omitted but shown later
sortFn: ...,
@ -36,6 +37,9 @@ Component.Explorer({
})
```
- When `folderDefaultState` is `"collapsed"`, folders are collapsed by default, and `maxExpandLevel` does not affect initial folder expansion.
- When `folderDefaultState` is `"open"`, `maxExpandLevel` controls how many folder levels are expanded initially.
When passing in your own options, you can omit any or all of these fields if you'd like to keep the default value for that field.
Want to customize it even more?

View File

@ -14,6 +14,7 @@ type OrderEntries = "sort" | "filter" | "map"
export interface Options {
title?: string
folderDefaultState: "collapsed" | "open"
maxExpandLevel: number
folderClickBehavior: "collapse" | "link"
useSavedState: boolean
sortFn: (a: FileTrieNode, b: FileTrieNode) => number
@ -24,6 +25,7 @@ export interface Options {
const defaultOptions: Options = {
folderDefaultState: "collapsed",
maxExpandLevel: 0,
folderClickBehavior: "link",
useSavedState: true,
mapFn: (node) => {
@ -68,6 +70,7 @@ export default ((userOpts?: Partial<Options>) => {
class={classNames(displayClass, "explorer")}
data-behavior={opts.folderClickBehavior}
data-collapsed={opts.folderDefaultState}
data-maxexpandlevel={opts.maxExpandLevel}
data-savestate={opts.useSavedState}
data-data-fns={JSON.stringify({
order: opts.order,

View File

@ -7,6 +7,7 @@ type MaybeHTMLElement = HTMLElement | undefined
interface ParsedOptions {
folderClickBehavior: "collapse" | "link"
folderDefaultState: "collapsed" | "open"
maxExpandLevel: number
useSavedState: boolean
sortFn: (a: FileTrieNode, b: FileTrieNode) => number
filterFn: (node: FileTrieNode) => boolean
@ -99,6 +100,7 @@ function createFolderNode(
currentSlug: FullSlug,
node: FileTrieNode,
opts: ParsedOptions,
level: number = 1,
): HTMLLIElement {
const template = document.getElementById("template-folder") as HTMLTemplateElement
const clone = template.content.cloneNode(true) as DocumentFragment
@ -125,10 +127,8 @@ function createFolderNode(
span.textContent = node.displayName
}
// if the saved state is collapsed or the default state is collapsed
const isCollapsed =
currentExplorerState.find((item) => item.path === folderPath)?.collapsed ??
opts.folderDefaultState === "collapsed"
const savedState = currentExplorerState.find((item) => item.path === folderPath)?.collapsed
const isCollapsedByState = savedState ?? opts.folderDefaultState === "collapsed"
// if this folder is a prefix of the current path we
// want to open it anyways
@ -136,13 +136,17 @@ function createFolderNode(
const folderIsPrefixOfCurrentSlug =
simpleFolderPath === currentSlug.slice(0, simpleFolderPath.length)
if (!isCollapsed || folderIsPrefixOfCurrentSlug) {
const maxLevel = opts.maxExpandLevel ?? 0
if (!isCollapsedByState || folderIsPrefixOfCurrentSlug || level <= maxLevel) {
folderOuter.classList.add("open")
} else {
folderOuter.classList.remove("open")
}
for (const child of node.children) {
const childNode = child.isFolder
? createFolderNode(currentSlug, child, opts)
? createFolderNode(currentSlug, child, opts, level + 1)
: createFileNode(currentSlug, child)
ul.appendChild(childNode)
}
@ -158,6 +162,7 @@ async function setupExplorer(currentSlug: FullSlug) {
const opts: ParsedOptions = {
folderClickBehavior: (explorer.dataset.behavior || "collapse") as "collapse" | "link",
folderDefaultState: (explorer.dataset.collapsed || "collapsed") as "collapsed" | "open",
maxExpandLevel: parseInt(explorer.dataset.maxexpandlevel || "0"),
useSavedState: explorer.dataset.savestate === "true",
order: dataFns.order || ["filter", "map", "sort"],
sortFn: new Function("return " + (dataFns.sortFn || "undefined"))(),
@ -209,7 +214,7 @@ async function setupExplorer(currentSlug: FullSlug) {
const fragment = document.createDocumentFragment()
for (const child of trie.children) {
const node = child.isFolder
? createFolderNode(currentSlug, child, opts)
? createFolderNode(currentSlug, child, opts, 1)
: createFileNode(currentSlug, child)
fragment.appendChild(node)