b029eeadab
* feat(consistency): use `all: unset` on button * style: improve accessibility and consistency for explorer * fix: localStorage bug with folder name changes * chore: bump quartz version
165 lines
5.4 KiB
TypeScript
165 lines
5.4 KiB
TypeScript
import { FolderState } from "../ExplorerNode"
|
|
|
|
// Current state of folders
|
|
let explorerState: FolderState[]
|
|
|
|
const observer = new IntersectionObserver((entries) => {
|
|
// If last element is observed, remove gradient of "overflow" class so element is visible
|
|
const explorer = document.getElementById("explorer-ul")
|
|
for (const entry of entries) {
|
|
if (entry.isIntersecting) {
|
|
explorer?.classList.add("no-background")
|
|
} else {
|
|
explorer?.classList.remove("no-background")
|
|
}
|
|
}
|
|
})
|
|
|
|
function toggleExplorer(this: HTMLElement) {
|
|
// Toggle collapsed state of entire explorer
|
|
this.classList.toggle("collapsed")
|
|
const content = this.nextElementSibling as HTMLElement
|
|
content.classList.toggle("collapsed")
|
|
content.style.maxHeight = content.style.maxHeight === "0px" ? content.scrollHeight + "px" : "0px"
|
|
}
|
|
|
|
function toggleFolder(evt: MouseEvent) {
|
|
evt.stopPropagation()
|
|
|
|
// Element that was clicked
|
|
const target = evt.target as HTMLElement
|
|
|
|
// Check if target was svg icon or button
|
|
const isSvg = target.nodeName === "svg"
|
|
|
|
// corresponding <ul> element relative to clicked button/folder
|
|
let childFolderContainer: HTMLElement
|
|
|
|
// <li> element of folder (stores folder-path dataset)
|
|
let currentFolderParent: HTMLElement
|
|
|
|
// Get correct relative container and toggle collapsed class
|
|
if (isSvg) {
|
|
childFolderContainer = target.parentElement?.nextSibling as HTMLElement
|
|
currentFolderParent = target.nextElementSibling as HTMLElement
|
|
|
|
childFolderContainer.classList.toggle("open")
|
|
} else {
|
|
childFolderContainer = target.parentElement?.parentElement?.nextElementSibling as HTMLElement
|
|
currentFolderParent = target.parentElement as HTMLElement
|
|
|
|
childFolderContainer.classList.toggle("open")
|
|
}
|
|
if (!childFolderContainer) return
|
|
|
|
// Collapse folder container
|
|
const isCollapsed = childFolderContainer.classList.contains("open")
|
|
setFolderState(childFolderContainer, !isCollapsed)
|
|
|
|
// Save folder state to localStorage
|
|
const clickFolderPath = currentFolderParent.dataset.folderpath as string
|
|
|
|
// Remove leading "/"
|
|
const fullFolderPath = clickFolderPath.substring(1)
|
|
toggleCollapsedByPath(explorerState, fullFolderPath)
|
|
|
|
const stringifiedFileTree = JSON.stringify(explorerState)
|
|
localStorage.setItem("fileTree", stringifiedFileTree)
|
|
}
|
|
|
|
function setupExplorer() {
|
|
// Set click handler for collapsing entire explorer
|
|
const explorer = document.getElementById("explorer")
|
|
|
|
// Get folder state from local storage
|
|
const storageTree = localStorage.getItem("fileTree")
|
|
|
|
// Convert to bool
|
|
const useSavedFolderState = explorer?.dataset.savestate === "true"
|
|
|
|
if (explorer) {
|
|
// Get config
|
|
const collapseBehavior = explorer.dataset.behavior
|
|
|
|
// Add click handlers for all folders (click handler on folder "label")
|
|
if (collapseBehavior === "collapse") {
|
|
Array.prototype.forEach.call(
|
|
document.getElementsByClassName("folder-button"),
|
|
function (item) {
|
|
item.removeEventListener("click", toggleFolder)
|
|
item.addEventListener("click", toggleFolder)
|
|
},
|
|
)
|
|
}
|
|
|
|
// Add click handler to main explorer
|
|
explorer.removeEventListener("click", toggleExplorer)
|
|
explorer.addEventListener("click", toggleExplorer)
|
|
}
|
|
|
|
// Set up click handlers for each folder (click handler on folder "icon")
|
|
Array.prototype.forEach.call(document.getElementsByClassName("folder-icon"), function (item) {
|
|
item.removeEventListener("click", toggleFolder)
|
|
item.addEventListener("click", toggleFolder)
|
|
})
|
|
|
|
if (storageTree && useSavedFolderState) {
|
|
// Get state from localStorage and set folder state
|
|
explorerState = JSON.parse(storageTree)
|
|
explorerState.map((folderUl) => {
|
|
// grab <li> element for matching folder path
|
|
const folderLi = document.querySelector(
|
|
`[data-folderpath='/${folderUl.path}']`,
|
|
) as HTMLElement
|
|
|
|
// Get corresponding content <ul> tag and set state
|
|
if (folderLi) {
|
|
const folderUL = folderLi.parentElement?.nextElementSibling
|
|
if (folderUL) {
|
|
setFolderState(folderUL as HTMLElement, folderUl.collapsed)
|
|
}
|
|
}
|
|
})
|
|
} else {
|
|
// If tree is not in localStorage or config is disabled, use tree passed from Explorer as dataset
|
|
explorerState = JSON.parse(explorer?.dataset.tree as string)
|
|
}
|
|
}
|
|
|
|
window.addEventListener("resize", setupExplorer)
|
|
document.addEventListener("nav", () => {
|
|
setupExplorer()
|
|
|
|
const explorerContent = document.getElementById("explorer-ul")
|
|
// select pseudo element at end of list
|
|
const lastItem = document.getElementById("explorer-end")
|
|
|
|
observer.disconnect()
|
|
observer.observe(lastItem as Element)
|
|
})
|
|
|
|
/**
|
|
* Toggles the state of a given folder
|
|
* @param folderElement <div class="folder-outer"> Element of folder (parent)
|
|
* @param collapsed if folder should be set to collapsed or not
|
|
*/
|
|
function setFolderState(folderElement: HTMLElement, collapsed: boolean) {
|
|
if (collapsed) {
|
|
folderElement?.classList.remove("open")
|
|
} else {
|
|
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
|
|
}
|
|
}
|