import { FolderState } from "../ExplorerNode" type MaybeHTMLElement = HTMLElement | undefined let currentExplorerState: FolderState[] const observer = new IntersectionObserver((entries) => { // If last element is observed, remove gradient of "overflow" class so element is visible const explorerUl = document.getElementById("explorer-ul") if (!explorerUl) return for (const entry of entries) { if (entry.isIntersecting) { explorerUl.classList.add("no-background") } else { explorerUl.classList.remove("no-background") } } }) function toggleExplorer(this: HTMLElement) { this.classList.toggle("collapsed") this.setAttribute( "aria-expanded", this.getAttribute("aria-expanded") === "true" ? "false" : "true", ) const content = this.nextElementSibling as MaybeHTMLElement if (!content) return content.classList.toggle("collapsed") } function toggleFolder(evt: MouseEvent) { evt.stopPropagation() const target = evt.target as MaybeHTMLElement if (!target) return const isSvg = target.nodeName === "svg" const childFolderContainer = ( isSvg ? target.parentElement?.nextSibling : target.parentElement?.parentElement?.nextElementSibling ) as MaybeHTMLElement const currentFolderParent = ( isSvg ? target.nextElementSibling : target.parentElement ) as MaybeHTMLElement if (!(childFolderContainer && currentFolderParent)) return childFolderContainer.classList.toggle("open") const isCollapsed = childFolderContainer.classList.contains("open") setFolderState(childFolderContainer, !isCollapsed) const fullFolderPath = currentFolderParent.dataset.folderpath as string toggleCollapsedByPath(currentExplorerState, fullFolderPath) const stringifiedFileTree = JSON.stringify(currentExplorerState) localStorage.setItem("fileTree", stringifiedFileTree) } function setupExplorer() { const explorer = document.getElementById("explorer") if (!explorer) return if (explorer.dataset.behavior === "collapse") { for (const item of document.getElementsByClassName( "folder-button", ) as HTMLCollectionOf) { item.addEventListener("click", toggleFolder) window.addCleanup(() => item.removeEventListener("click", toggleFolder)) } } explorer.addEventListener("click", toggleExplorer) window.addCleanup(() => explorer.removeEventListener("click", toggleExplorer)) // Set up click handlers for each folder (click handler on folder "icon") for (const item of document.getElementsByClassName( "folder-icon", ) as HTMLCollectionOf) { item.addEventListener("click", toggleFolder) window.addCleanup(() => item.removeEventListener("click", toggleFolder)) } // Get folder state from local storage const storageTree = localStorage.getItem("fileTree") const useSavedFolderState = explorer?.dataset.savestate === "true" const oldExplorerState: FolderState[] = storageTree && useSavedFolderState ? JSON.parse(storageTree) : [] const oldIndex = new Map(oldExplorerState.map((entry) => [entry.path, entry.collapsed])) const newExplorerState: FolderState[] = explorer.dataset.tree ? JSON.parse(explorer.dataset.tree) : [] currentExplorerState = [] for (const { path, collapsed } of newExplorerState) { currentExplorerState.push({ path, collapsed: oldIndex.get(path) ?? collapsed }) } currentExplorerState.map((folderState) => { const folderLi = document.querySelector( `[data-folderpath='${folderState.path}']`, ) as MaybeHTMLElement const folderUl = folderLi?.parentElement?.nextElementSibling as MaybeHTMLElement if (folderUl) { setFolderState(folderUl, folderState.collapsed) } }) } window.addEventListener("resize", setupExplorer) document.addEventListener("nav", () => { setupExplorer() observer.disconnect() // select pseudo element at end of list const lastItem = document.getElementById("explorer-end") if (lastItem) { observer.observe(lastItem) } }) /** * Toggles the state of a given folder * @param folderElement
Element of folder (parent) * @param collapsed if folder should be set to collapsed or not */ function setFolderState(folderElement: HTMLElement, collapsed: boolean) { return collapsed ? folderElement.classList.remove("open") : folderElement.classList.add("open") } /** * Toggles visibility of a folder * @param array array of FolderState (`fileTree`, either get from local storage or data attribute) * @param path path to folder (e.g. 'advanced/more/more2') */ function toggleCollapsedByPath(array: FolderState[], path: string) { const entry = array.find((item) => item.path === path) if (entry) { entry.collapsed = !entry.collapsed } }