Files
datascape/assets/tree-picker.js
T
luxickandClaude Opus 5.5 4365fa2567 Render the folder tree on the server
The sidebar tree is part of the page (chain to the current page
expanded, active row marked), so it no longer paints late and the
sessionStorage snapshot is gone; only the rail's scroll position is
kept. Folders are native <details> that lazy-load their children as
HTML via htmx. The tree picker hosts the same server-rendered rows with
radio inputs, so ?tree returns HTML and the JSON listing is removed.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-30 16:51:20 +02:00

68 lines
2.6 KiB
JavaScript
Raw Blame History

This file contains invisible Unicode characters
This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
// openTreePicker shows a modal to pick a wiki path. The tree is server-rendered
// (?tree&pick=…, see tree.go and tree.html): each selectable row carries a
// radio named "pick", folders expand natively and lazy-load via htmx. This
// file only hosts the tree in a modal and reports the checked row.
//
// opts: title, confirmLabel, mode ('folder' | 'file' | 'any'), initialPath
// (expanded on open), preselect (checked on open), hideFiles, allowRoot
// (default true: offer "/" in folder modes), onSelect(path, kind).
(function () {
function openTreePicker(opts) {
opts = opts || {};
var mode = opts.mode || 'folder';
var container = document.createElement('div');
var treeEl = document.createElement('div');
treeEl.className = 'tree-picker panel';
treeEl.textContent = '…';
var selectedPathEl = document.createElement('div');
selectedPathEl.className = 'tree-selected-path muted';
selectedPathEl.textContent = ' ';
container.appendChild(treeEl);
container.appendChild(selectedPathEl);
function checked() {
return treeEl.querySelector('input[name="pick"]:checked');
}
var handle = openModal({
title: opts.title || 'Pick',
body: container,
confirm: {
label: opts.confirmLabel || 'SELECT',
initiallyDisabled: true,
onConfirm: function () {
var pick = checked();
if (!pick) return;
handle.close();
if (opts.onSelect) opts.onSelect(pick.value, pick.dataset.kind);
}
}
});
function sync() {
var pick = checked();
selectedPathEl.textContent = pick ? pick.value : ' ';
handle.setConfirmDisabled(!pick);
}
treeEl.addEventListener('change', sync);
var url = '/?tree&pick=' + mode +
(opts.hideFiles ? '' : '&files=1') +
(opts.allowRoot !== false ? '&root=1' : '') +
'&expandTo=' + encodeURIComponent(opts.initialPath || '/') +
(opts.preselect ? '&select=' + encodeURIComponent(opts.preselect) : '');
htmx.ajax('GET', url, { target: treeEl, swap: 'innerHTML' }).then(function () {
sync();
var pick = checked();
if (pick) {
try { pick.closest('.tree-row').scrollIntoView({ block: 'nearest' }); } catch (e) {}
}
});
return handle;
}
window.openTreePicker = openTreePicker;
})();