feat: implement file explorer component (closes #201) (#452)
* feat: add basic explorer structure„
* feat: integrate new component/plugin
* feat: add basic explorer structure
* feat: add sort to FileNodes
* style: improve style for explorer
* refactor: remove unused explorer plugin
* refactor: clean explorer structure, fix base (toc)
* refactor: clean css, respect displayClass
* style: add styling to chevron
* refactor: clean up debug statements
* refactor: remove unused import
* fix: clicking folder icon sometimes turns invisible
* refactor: clean css
* feat(explorer): add config for title
* feat: add config for folder click behavior
* fix: `no-pointer` not being set for all elements
new approach, have one `no-pointer` class, that removes pointer events and one `clickable` class on the svg and button (everything that can normally be clicked). then, find all children with `clickable` and toggle `no-pointer`
* fix: bug where nested folders got incorrect height
this fixes the bug where nested folders weren't calculating their total height correctly. done by adding class to main container of all children and calculating total
* feat: introduce `folderDefaultState` config
* feat: store depth for explorer nodes
* feat: implement option for collapsed state + bug fixes
folderBehavior: "link" still has bad styling, but major bugs with pointers fixed (not clean yet, but working)
* fix: default folder icon rotation
* fix: hitbox problem with folder links, fix style
* fix: redirect url for nested folders
* fix: inconsistent behavior with 'collapseFolders' opt
* chore: add comments to `ExplorerNode`
* feat: save explorer state to local storage (not clean)
* feat: rework `getFolders()`, fix localstorage read + write
* feat: set folder state from localStorage
needs serious refactoring but functional (except folder icon orientation)
* fix: folder icon orientation after local storage
* feat: add config for `useSavedState`
* refactor: clean `explorer.inline.ts`
remove unused functions, comments, unused code, add types to EventHandler
* refactor: clean explorer
merge `isSvg` paths, remove console logs
* refactor: add documentation, remove unused funcs
* feat: rework folder collapse logic
use grids instead of jank scuffed solution with calculating total heights
* refactor: remove depth arg from insert
* feat: restore collapse functionality to clicks
allow folder icon + folder label to collapse folders again
* refactor: remove `pointer-event` jank
* feat: improve svg viewbox + remove unused props
* feat: use css selector to toggle icon
rework folder icon to work purely with css instead of JS manipulation
* refactor: remove unused cfg
* feat: move TOC to right sidebar
* refactor: clean css
* style: fix overflow + overflow margin
* fix: use `resolveRelative` to resolve file paths
* fix: `defaultFolderState` config option
* refactor: rename import, rename `folderLi` + ul
* fix: use `QuartzPluginData` type
* docs: add explorer documentation
2023-09-15 16:39:16 +00:00
|
|
|
import { QuartzComponentConstructor, QuartzComponentProps } from "./types"
|
|
|
|
import explorerStyle from "./styles/explorer.scss"
|
|
|
|
|
|
|
|
// @ts-ignore
|
|
|
|
import script from "./scripts/explorer.inline"
|
|
|
|
import { ExplorerNode, FileNode, Options } from "./ExplorerNode"
|
|
|
|
|
|
|
|
// Options interface defined in `ExplorerNode` to avoid circular dependency
|
|
|
|
const defaultOptions = (): Options => ({
|
|
|
|
title: "Explorer",
|
|
|
|
folderClickBehavior: "collapse",
|
|
|
|
folderDefaultState: "collapsed",
|
|
|
|
useSavedState: true,
|
2023-09-16 10:40:19 +00:00
|
|
|
// Sort order: folders first, then files. Sort folders and files alphabetically
|
|
|
|
sortFn: (a, b) => {
|
|
|
|
if ((!a.file && !b.file) || (a.file && b.file)) {
|
|
|
|
return a.name.localeCompare(b.name)
|
|
|
|
}
|
|
|
|
if (a.file && !b.file) {
|
|
|
|
return 1
|
|
|
|
} else {
|
|
|
|
return -1
|
|
|
|
}
|
|
|
|
},
|
2023-09-16 19:58:38 +00:00
|
|
|
order: ["filter", "map", "sort"],
|
feat: implement file explorer component (closes #201) (#452)
* feat: add basic explorer structure„
* feat: integrate new component/plugin
* feat: add basic explorer structure
* feat: add sort to FileNodes
* style: improve style for explorer
* refactor: remove unused explorer plugin
* refactor: clean explorer structure, fix base (toc)
* refactor: clean css, respect displayClass
* style: add styling to chevron
* refactor: clean up debug statements
* refactor: remove unused import
* fix: clicking folder icon sometimes turns invisible
* refactor: clean css
* feat(explorer): add config for title
* feat: add config for folder click behavior
* fix: `no-pointer` not being set for all elements
new approach, have one `no-pointer` class, that removes pointer events and one `clickable` class on the svg and button (everything that can normally be clicked). then, find all children with `clickable` and toggle `no-pointer`
* fix: bug where nested folders got incorrect height
this fixes the bug where nested folders weren't calculating their total height correctly. done by adding class to main container of all children and calculating total
* feat: introduce `folderDefaultState` config
* feat: store depth for explorer nodes
* feat: implement option for collapsed state + bug fixes
folderBehavior: "link" still has bad styling, but major bugs with pointers fixed (not clean yet, but working)
* fix: default folder icon rotation
* fix: hitbox problem with folder links, fix style
* fix: redirect url for nested folders
* fix: inconsistent behavior with 'collapseFolders' opt
* chore: add comments to `ExplorerNode`
* feat: save explorer state to local storage (not clean)
* feat: rework `getFolders()`, fix localstorage read + write
* feat: set folder state from localStorage
needs serious refactoring but functional (except folder icon orientation)
* fix: folder icon orientation after local storage
* feat: add config for `useSavedState`
* refactor: clean `explorer.inline.ts`
remove unused functions, comments, unused code, add types to EventHandler
* refactor: clean explorer
merge `isSvg` paths, remove console logs
* refactor: add documentation, remove unused funcs
* feat: rework folder collapse logic
use grids instead of jank scuffed solution with calculating total heights
* refactor: remove depth arg from insert
* feat: restore collapse functionality to clicks
allow folder icon + folder label to collapse folders again
* refactor: remove `pointer-event` jank
* feat: improve svg viewbox + remove unused props
* feat: use css selector to toggle icon
rework folder icon to work purely with css instead of JS manipulation
* refactor: remove unused cfg
* feat: move TOC to right sidebar
* refactor: clean css
* style: fix overflow + overflow margin
* fix: use `resolveRelative` to resolve file paths
* fix: `defaultFolderState` config option
* refactor: rename import, rename `folderLi` + ul
* fix: use `QuartzPluginData` type
* docs: add explorer documentation
2023-09-15 16:39:16 +00:00
|
|
|
})
|
|
|
|
export default ((userOpts?: Partial<Options>) => {
|
|
|
|
function Explorer({ allFiles, displayClass, fileData }: QuartzComponentProps) {
|
|
|
|
// Parse config
|
|
|
|
const opts: Options = { ...defaultOptions(), ...userOpts }
|
|
|
|
|
|
|
|
// Construct tree from allFiles
|
|
|
|
const fileTree = new FileNode("")
|
|
|
|
allFiles.forEach((file) => fileTree.add(file, 1))
|
|
|
|
|
2023-09-16 19:58:38 +00:00
|
|
|
/**
|
|
|
|
* Keys of this object must match corresponding function name of `FileNode`,
|
|
|
|
* while values must be the argument that will be passed to the function.
|
|
|
|
*
|
|
|
|
* e.g. entry for FileNode.sort: `sort: opts.sortFn` (value is sort function from options)
|
|
|
|
*/
|
|
|
|
const functions = {
|
|
|
|
map: opts.mapFn,
|
|
|
|
sort: opts.sortFn,
|
|
|
|
filter: opts.filterFn,
|
2023-09-16 17:18:59 +00:00
|
|
|
}
|
|
|
|
|
2023-09-16 19:58:38 +00:00
|
|
|
// Execute all functions (sort, filter, map) that were provided (if none were provided, only default "sort" is applied)
|
|
|
|
if (opts.order) {
|
|
|
|
// Order is important, use loop with index instead of order.map()
|
|
|
|
for (let i = 0; i < opts.order.length; i++) {
|
|
|
|
const functionName = opts.order[i]
|
|
|
|
if (functions[functionName]) {
|
|
|
|
// for every entry in order, call matching function in FileNode and pass matching argument
|
|
|
|
// e.g. i = 0; functionName = "filter"
|
|
|
|
// converted to: (if opts.filterFn) => fileTree.filter(opts.filterFn)
|
|
|
|
|
|
|
|
// @ts-ignore
|
|
|
|
// typescript cant statically check these dynamic references, so manually make sure reference is valid and ignore warning
|
|
|
|
fileTree[functionName].call(fileTree, functions[functionName])
|
|
|
|
}
|
|
|
|
}
|
2023-09-16 17:35:27 +00:00
|
|
|
}
|
|
|
|
|
feat: implement file explorer component (closes #201) (#452)
* feat: add basic explorer structure„
* feat: integrate new component/plugin
* feat: add basic explorer structure
* feat: add sort to FileNodes
* style: improve style for explorer
* refactor: remove unused explorer plugin
* refactor: clean explorer structure, fix base (toc)
* refactor: clean css, respect displayClass
* style: add styling to chevron
* refactor: clean up debug statements
* refactor: remove unused import
* fix: clicking folder icon sometimes turns invisible
* refactor: clean css
* feat(explorer): add config for title
* feat: add config for folder click behavior
* fix: `no-pointer` not being set for all elements
new approach, have one `no-pointer` class, that removes pointer events and one `clickable` class on the svg and button (everything that can normally be clicked). then, find all children with `clickable` and toggle `no-pointer`
* fix: bug where nested folders got incorrect height
this fixes the bug where nested folders weren't calculating their total height correctly. done by adding class to main container of all children and calculating total
* feat: introduce `folderDefaultState` config
* feat: store depth for explorer nodes
* feat: implement option for collapsed state + bug fixes
folderBehavior: "link" still has bad styling, but major bugs with pointers fixed (not clean yet, but working)
* fix: default folder icon rotation
* fix: hitbox problem with folder links, fix style
* fix: redirect url for nested folders
* fix: inconsistent behavior with 'collapseFolders' opt
* chore: add comments to `ExplorerNode`
* feat: save explorer state to local storage (not clean)
* feat: rework `getFolders()`, fix localstorage read + write
* feat: set folder state from localStorage
needs serious refactoring but functional (except folder icon orientation)
* fix: folder icon orientation after local storage
* feat: add config for `useSavedState`
* refactor: clean `explorer.inline.ts`
remove unused functions, comments, unused code, add types to EventHandler
* refactor: clean explorer
merge `isSvg` paths, remove console logs
* refactor: add documentation, remove unused funcs
* feat: rework folder collapse logic
use grids instead of jank scuffed solution with calculating total heights
* refactor: remove depth arg from insert
* feat: restore collapse functionality to clicks
allow folder icon + folder label to collapse folders again
* refactor: remove `pointer-event` jank
* feat: improve svg viewbox + remove unused props
* feat: use css selector to toggle icon
rework folder icon to work purely with css instead of JS manipulation
* refactor: remove unused cfg
* feat: move TOC to right sidebar
* refactor: clean css
* style: fix overflow + overflow margin
* fix: use `resolveRelative` to resolve file paths
* fix: `defaultFolderState` config option
* refactor: rename import, rename `folderLi` + ul
* fix: use `QuartzPluginData` type
* docs: add explorer documentation
2023-09-15 16:39:16 +00:00
|
|
|
// Get all folders of tree. Initialize with collapsed state
|
|
|
|
const folders = fileTree.getFolderPaths(opts.folderDefaultState === "collapsed")
|
|
|
|
|
|
|
|
// Stringify to pass json tree as data attribute ([data-tree])
|
|
|
|
const jsonTree = JSON.stringify(folders)
|
|
|
|
|
|
|
|
return (
|
|
|
|
<div class={`explorer ${displayClass}`}>
|
|
|
|
<button
|
|
|
|
type="button"
|
|
|
|
id="explorer"
|
|
|
|
data-behavior={opts.folderClickBehavior}
|
|
|
|
data-collapsed={opts.folderDefaultState}
|
|
|
|
data-savestate={opts.useSavedState}
|
|
|
|
data-tree={jsonTree}
|
|
|
|
>
|
|
|
|
<h3>{opts.title}</h3>
|
|
|
|
<svg
|
|
|
|
xmlns="http://www.w3.org/2000/svg"
|
|
|
|
width="14"
|
|
|
|
height="14"
|
|
|
|
viewBox="5 8 14 8"
|
|
|
|
fill="none"
|
|
|
|
stroke="currentColor"
|
|
|
|
stroke-width="2"
|
|
|
|
stroke-linecap="round"
|
|
|
|
stroke-linejoin="round"
|
|
|
|
class="fold"
|
|
|
|
>
|
|
|
|
<polyline points="6 9 12 15 18 9"></polyline>
|
|
|
|
</svg>
|
|
|
|
</button>
|
|
|
|
<div id="explorer-content">
|
|
|
|
<ul class="overflow">
|
|
|
|
<ExplorerNode node={fileTree} opts={opts} fileData={fileData} />
|
|
|
|
</ul>
|
|
|
|
</div>
|
|
|
|
</div>
|
|
|
|
)
|
|
|
|
}
|
|
|
|
Explorer.css = explorerStyle
|
|
|
|
Explorer.afterDOMLoaded = script
|
|
|
|
return Explorer
|
|
|
|
}) satisfies QuartzComponentConstructor
|