import rxjs, { effect } from "../lib/rx.js"; import { createElement, createRender } from "../lib/skeleton/index.js"; import { toHref } from "../lib/skeleton/router.js"; import { qs, qsa, safe } from "../lib/dom.js"; import { forwardURLParams } from "../lib/path.js"; import cache from "../pages/filespage/cache.js"; import { extractPath, isDir, isNativeFileUpload } from "../pages/filespage/helper.js"; import { mv as mvVL, withVirtualLayer } from "../pages/filespage/model_virtual_layer.js"; import { hooks, mv as mv$ } from "../pages/filespage/model_files.js"; import ctrlError from "../pages/ctrl_error.js"; export default async function ctrlNavigationPane(render, { $sidebar, path }) { // feature: init dom const $fs = document.createDocumentFragment(); const dirname = path.replace(new RegExp("[^\/]*$"), ""); const chunks = dirname.split("/"); for (let i=1; i { const cleaners = [ hooks.ls.listen(({ path }) => subscriber.next(path)), hooks.mutation.listen(async({ op, path }) => { if (["mv", "mkdir", "rm"].indexOf(op) === -1) return; subscriber.next(path); }), ]; return () => cleaners.map((fn) => fn()); }).pipe( rxjs.mergeMap(async(path) => { const display = path === "/" ? render : createRender(qs($sidebar, `[data-path="${CSS.escape(path)}"] ul`)); display(await _createListOfFiles(path, {})); }), rxjs.catchError((err) => { if (err instanceof DOMException) return rxjs.EMPTY; return ctrlError()(err); }), )); // feature: highlight current selection try { const $active = qs($sidebar, `[data-path="${dirname}"] a`); const checkVisible = ($el) => { const rect = $el.getBoundingClientRect(); return rect.top >= 0 && rect.left >= 0 && rect.bottom <= (window.innerHeight || document.documentElement.clientHeight) && rect.right <= (window.innerWidth || document.documentElement.clientWidth); }; $active.setAttribute("aria-selected", "true"); const tags = new URLSearchParams(location.search).getAll("tag").length; if (checkVisible($active) === false && tags === 0) { $active.offsetTop < window.innerHeight ? $sidebar.firstChild.scrollTo({ top: 0, behavior: "smooth" }) : $active.scrollIntoView({ behavior: "smooth", block: "nearest" }); } } catch (err) {} // feature: quick search effect(rxjs.fromEvent(qs($sidebar, "h3 input"), "keydown").pipe( rxjs.debounceTime(200), rxjs.tap((e) => { const inputValue = e.target.value.toLowerCase(); qsa($sidebar, "[data-bind=\"your-files\"] li a").forEach(($li) => { if (inputValue === "") { $li.classList.remove("hidden"); $sidebar.classList.remove("search"); return; } $sidebar.classList.add("search"); qs($li, "div").textContent.toLowerCase().indexOf(inputValue) === -1 ? $li.classList.add("hidden") : $li.classList.remove("hidden"); }); }), rxjs.finalize(() => $sidebar.classList.remove("search")), )); } const mv = (from, to) => withVirtualLayer( mv$(from, to), mvVL(from, to), ); async function _createListOfFiles(path, { basename = null, dirname = null }) { const MAX_DISPLAY = 100; const r = await cache().get(path); const whats = r === null ? (basename ? [basename] : []) : r.files .filter(({ type, name }) => type === "directory" && name[0] !== ".") .map(({ name }) => name) .sort(); const $lis = document.createDocumentFragment(); const $fragment = document.createDocumentFragment(); const $ul = document.createElement("ul"); for (let i=0; i directory
${safe(whats[i])}
`); const $link = qs($li, "a"); if ($link.getAttribute("href") === "/files" + dirname) { $link.removeAttribute("href", ""); $link.removeAttribute("data-link"); } else { $link.ondrop = async(e) => { $link.classList.remove("highlight"); const from = e.dataTransfer.getData("path"); let to = $link.parentElement.getAttribute("data-path"); const [, fromName] = extractPath(from); to += fromName; if (isDir(from)) to += "/"; if (from === to) return; await mv(from, to).toPromise(); }; $link.ondragover = (e) => { if (isNativeFileUpload(e)) return; e.preventDefault(); $link.classList.add("highlight"); }; $link.ondragleave = () => { $link.classList.remove("highlight"); }; } if (i <= MAX_DISPLAY) $lis.appendChild($li); else $fragment.appendChild($li); if (i === MAX_DISPLAY) { const $more = createElement(`
  • ...
  • `); $lis.appendChild($more); $more.onclick = () => { $ul.appendChild($fragment); $more.remove(); }; } } $ul.appendChild($lis); return $ul; }