ZDDC/archive/js/presets.js
2026-06-11 13:32:31 -05:00

158 lines
6 KiB
JavaScript

(function() {
'use strict';
// Project-picker dropdown for the archive browser.
//
// In multi-project mode (HTTP source against zddc-server, OR ?projects=
// present in the URL), this dropdown lets the user toggle which projects
// are scanned. Toggling a checkbox updates window.app.projectFilter, pushes
// the new ?projects= state to the URL, and triggers a re-scan.
//
// In single-project mode the dropdown is hidden — only one project is ever
// in scope, so picking is meaningless.
let isOpen = false;
// The set of project names currently shown in the dropdown.
function getKnownProjects() {
if (window.app.availableProjects && window.app.availableProjects.length > 0) {
return window.app.availableProjects.slice();
}
// Fall back to whatever is in the URL filter — useful when the server's
// ProjectInfo endpoint isn't reachable but ?projects= names the set.
return Array.from(window.app.projectFilter || []);
}
// Visibility-only filter: change visibleProjects, push URL state, re-render
// UI. No rescan — already-scanned data stays in memory. URL is updated via
// history.replaceState (same mechanism as every other UI control).
function applyVisibility(names) {
window.app.visibleProjects = new Set(names);
window.app.modules.urlState.push();
window.app.modules.app.updateUI();
window.app.modules.filtering.applyFilters();
renderDropdown();
}
function escapeHtml(text) {
var div = document.createElement('div');
div.textContent = text;
return div.innerHTML;
}
function renderDropdown() {
var dropdown = document.getElementById('presetDropdown');
if (!dropdown) return;
var selected = new Set(window.app.visibleProjects || []);
var known = getKnownProjects().slice().sort();
// Show the human-friendly title from each project's .zddc
// when present (captured during auto-detect into
// window.app.projectTitles), falling back to the folder name.
// The data-name attribute always carries the canonical folder
// name so URL state stays stable regardless of label.
var titles = window.app.projectTitles || {};
var projectsHtml = known.map(name => {
var checked = selected.has(name) ? ' checked' : '';
var label = titles[name] || name;
var nAttr = escapeHtml(name);
var nLabel = escapeHtml(label);
var hint = (label !== name)
? ' <span class="preset-project-folder">(' + escapeHtml(name) + ')</span>'
: '';
return '<div class="preset-project-item">'
+ '<label class="preset-project-label">'
+ '<input type="checkbox" class="preset-checkbox" data-name="' + nAttr + '"' + checked + '>'
+ ' ' + nLabel + hint
+ '</label>'
+ '</div>';
}).join('');
if (!projectsHtml) {
projectsHtml = '<div class="preset-no-presets"><i>No projects available</i></div>';
}
dropdown.innerHTML =
'<div class="preset-section-bottom">'
+ '<div class="preset-section-label">Projects:</div>'
+ '<div class="preset-projects-list">' + projectsHtml + '</div>'
+ '</div>';
}
function toggleDropdown() {
var dropdown = document.getElementById('presetDropdown');
if (isOpen) { closeDropdown(); return; }
isOpen = true;
if (dropdown) dropdown.classList.remove('hidden');
renderDropdown();
}
function closeDropdown() {
isOpen = false;
var dropdown = document.getElementById('presetDropdown');
if (dropdown) dropdown.classList.add('hidden');
}
function setupDropdownDelegation() {
var dropdown = document.getElementById('presetDropdown');
if (!dropdown) return;
dropdown.addEventListener('click', function(e) {
e.stopPropagation();
var checkbox = e.target.closest('.preset-checkbox');
if (!checkbox) return;
var projectName = checkbox.getAttribute('data-name');
if (!projectName) return;
var sel = new Set(window.app.visibleProjects || []);
if (checkbox.checked) sel.add(projectName);
else sel.delete(projectName);
applyVisibility(Array.from(sel));
});
}
function setupOutsideClickHandler() {
document.addEventListener('click', function(e) {
var section = document.getElementById('presetSection');
var dropdown = document.getElementById('presetDropdown');
if (isOpen && section && dropdown && !section.contains(e.target)) {
closeDropdown();
}
});
}
function init() {
var section = document.getElementById('presetSection');
if (!section) return;
// Hide the dropdown entirely outside multi-project mode.
if (!window.app.isMultiProject) {
section.classList.add('hidden');
return;
}
section.classList.remove('hidden');
var btn = document.getElementById('presetBtn');
if (!btn || btn.dataset.presetInit) return;
btn.dataset.presetInit = '1';
btn.title = 'Project picker';
btn.textContent = '▾ Projects';
btn.addEventListener('click', function(e) {
e.stopPropagation();
toggleDropdown();
});
setupDropdownDelegation();
setupOutsideClickHandler();
}
window.app.modules.presets = {
init: init,
toggleDropdown: toggleDropdown,
closeDropdown: closeDropdown,
// No-op kept so existing callers (events.js after grouping-folder click)
// don't need to null-check; preset dirty state was removed with the
// saved-presets feature.
checkDirty: function() {}
};
})();