| | |
| | | |
| | | let currentExplorerState: Array<FolderState> |
| | | function toggleExplorer(this: HTMLElement) { |
| | | const explorers = document.querySelectorAll(".explorer") |
| | | for (const explorer of explorers) { |
| | | explorer.classList.toggle("collapsed") |
| | | explorer.setAttribute( |
| | | "aria-expanded", |
| | | explorer.getAttribute("aria-expanded") === "true" ? "false" : "true", |
| | | ) |
| | | } |
| | | const nearestExplorer = this.closest(".explorer") as HTMLElement |
| | | if (!nearestExplorer) return |
| | | nearestExplorer.classList.toggle("collapsed") |
| | | nearestExplorer.setAttribute( |
| | | "aria-expanded", |
| | | nearestExplorer.getAttribute("aria-expanded") === "true" ? "false" : "true", |
| | | ) |
| | | } |
| | | |
| | | function toggleFolder(evt: MouseEvent) { |
| | |
| | | } |
| | | |
| | | for (const child of node.children) { |
| | | const childNode = child.data |
| | | ? createFileNode(currentSlug, child) |
| | | : createFolderNode(currentSlug, child, opts) |
| | | const childNode = child.isFolder |
| | | ? createFolderNode(currentSlug, child, opts) |
| | | : createFileNode(currentSlug, child) |
| | | ul.appendChild(childNode) |
| | | } |
| | | |
| | |
| | | } |
| | | |
| | | async function setupExplorer(currentSlug: FullSlug) { |
| | | const allExplorers = document.querySelectorAll(".explorer") as NodeListOf<HTMLElement> |
| | | const allExplorers = document.querySelectorAll("div.explorer") as NodeListOf<HTMLElement> |
| | | |
| | | for (const explorer of allExplorers) { |
| | | const dataFns = JSON.parse(explorer.dataset.dataFns || "{}") |
| | |
| | | // Get folder state from local storage |
| | | const storageTree = localStorage.getItem("fileTree") |
| | | const serializedExplorerState = storageTree && opts.useSavedState ? JSON.parse(storageTree) : [] |
| | | const oldIndex = new Map( |
| | | const oldIndex = new Map<string, boolean>( |
| | | serializedExplorerState.map((entry: FolderState) => [entry.path, entry.collapsed]), |
| | | ) |
| | | |
| | |
| | | |
| | | // Get folder paths for state management |
| | | const folderPaths = trie.getFolderPaths() |
| | | currentExplorerState = folderPaths.map((path) => ({ |
| | | path, |
| | | collapsed: oldIndex.get(path) === true, |
| | | })) |
| | | currentExplorerState = folderPaths.map((path) => { |
| | | const previousState = oldIndex.get(path) |
| | | return { |
| | | path, |
| | | collapsed: |
| | | previousState === undefined ? opts.folderDefaultState === "collapsed" : previousState, |
| | | } |
| | | }) |
| | | |
| | | const explorerUl = document.getElementById("explorer-ul") |
| | | const explorerUl = explorer.querySelector(".explorer-ul") |
| | | if (!explorerUl) continue |
| | | |
| | | // Create and insert new content |
| | |
| | | } |
| | | |
| | | // Set up event handlers |
| | | const explorerButtons = explorer.querySelectorAll( |
| | | "button.explorer-toggle", |
| | | ) as NodeListOf<HTMLElement> |
| | | if (explorerButtons) { |
| | | window.addCleanup(() => |
| | | explorerButtons.forEach((button) => button.removeEventListener("click", toggleExplorer)), |
| | | ) |
| | | explorerButtons.forEach((button) => button.addEventListener("click", toggleExplorer)) |
| | | const explorerButtons = explorer.getElementsByClassName( |
| | | "explorer-toggle", |
| | | ) as HTMLCollectionOf<HTMLElement> |
| | | for (const button of explorerButtons) { |
| | | button.addEventListener("click", toggleExplorer) |
| | | window.addCleanup(() => button.removeEventListener("click", toggleExplorer)) |
| | | } |
| | | |
| | | // Set up folder click handlers |
| | |
| | | "folder-button", |
| | | ) as HTMLCollectionOf<HTMLElement> |
| | | for (const button of folderButtons) { |
| | | window.addCleanup(() => button.removeEventListener("click", toggleFolder)) |
| | | button.addEventListener("click", toggleFolder) |
| | | window.addCleanup(() => button.removeEventListener("click", toggleFolder)) |
| | | } |
| | | } |
| | | |
| | |
| | | "folder-icon", |
| | | ) as HTMLCollectionOf<HTMLElement> |
| | | for (const icon of folderIcons) { |
| | | window.addCleanup(() => icon.removeEventListener("click", toggleFolder)) |
| | | icon.addEventListener("click", toggleFolder) |
| | | window.addCleanup(() => icon.removeEventListener("click", toggleFolder)) |
| | | } |
| | | } |
| | | } |
| | | |
| | | document.addEventListener("prenav", async (e: CustomEventMap["prenav"]) => { |
| | | document.addEventListener("prenav", async () => { |
| | | // save explorer scrollTop position |
| | | const explorer = document.getElementById("explorer-ul") |
| | | const explorer = document.querySelector(".explorer-ul") |
| | | if (!explorer) return |
| | | sessionStorage.setItem("explorerScrollTop", explorer.scrollTop.toString()) |
| | | }) |
| | |
| | | await setupExplorer(currentSlug) |
| | | |
| | | // if mobile hamburger is visible, collapse by default |
| | | const mobileExplorer = document.getElementById("mobile-explorer") |
| | | if (mobileExplorer && mobileExplorer.checkVisibility()) { |
| | | for (const explorer of document.querySelectorAll(".explorer")) { |
| | | for (const explorer of document.getElementsByClassName("explorer")) { |
| | | const mobileExplorer = explorer.querySelector(".mobile-explorer") |
| | | if (!mobileExplorer) return |
| | | |
| | | if (mobileExplorer.checkVisibility()) { |
| | | explorer.classList.add("collapsed") |
| | | explorer.setAttribute("aria-expanded", "false") |
| | | } |
| | | } |
| | | |
| | | const hiddenUntilDoneLoading = document.querySelector("#mobile-explorer") |
| | | hiddenUntilDoneLoading?.classList.remove("hide-until-loaded") |
| | | mobileExplorer.classList.remove("hide-until-loaded") |
| | | } |
| | | }) |
| | | |
| | | function setFolderState(folderElement: HTMLElement, collapsed: boolean) { |