Add collapsible sidebar panels, vault quick-select dropdown, help modal, and vault focus feature with improved navigation UX
This commit is contained in:
+138
-8
@@ -18,6 +18,10 @@
|
||||
let editorVault = null;
|
||||
let editorPath = null;
|
||||
let fallbackEditorEl = null;
|
||||
const panelState = {
|
||||
vault: true,
|
||||
tag: true,
|
||||
};
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// File extension → Lucide icon mapping
|
||||
@@ -148,6 +152,14 @@
|
||||
});
|
||||
}
|
||||
|
||||
function closeHeaderMenu() {
|
||||
const menuBtn = document.getElementById("header-menu-btn");
|
||||
const menuDropdown = document.getElementById("header-menu-dropdown");
|
||||
if (!menuBtn || !menuDropdown) return;
|
||||
menuBtn.classList.remove("active");
|
||||
menuDropdown.classList.remove("active");
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// API helpers
|
||||
// ---------------------------------------------------------------------------
|
||||
@@ -188,14 +200,36 @@
|
||||
// ---------------------------------------------------------------------------
|
||||
function initVaultContext() {
|
||||
const filter = document.getElementById("vault-filter");
|
||||
const quickSelect = document.getElementById("vault-quick-select");
|
||||
if (!filter || !quickSelect) return;
|
||||
|
||||
filter.addEventListener("change", async () => {
|
||||
selectedContextVault = filter.value;
|
||||
showingSource = false;
|
||||
cachedRawSource = null;
|
||||
await refreshSidebarForContext();
|
||||
await refreshTagsForContext();
|
||||
showWelcome();
|
||||
await setSelectedVaultContext(filter.value, { focusVault: filter.value !== "all" });
|
||||
});
|
||||
|
||||
quickSelect.addEventListener("change", async () => {
|
||||
await setSelectedVaultContext(quickSelect.value, { focusVault: quickSelect.value !== "all" });
|
||||
});
|
||||
}
|
||||
|
||||
async function setSelectedVaultContext(vaultName, options) {
|
||||
selectedContextVault = vaultName;
|
||||
showingSource = false;
|
||||
cachedRawSource = null;
|
||||
syncVaultSelectors();
|
||||
await refreshSidebarForContext();
|
||||
await refreshTagsForContext();
|
||||
showWelcome();
|
||||
if (options && options.focusVault && vaultName !== "all") {
|
||||
await focusVaultInSidebar(vaultName);
|
||||
}
|
||||
}
|
||||
|
||||
function syncVaultSelectors() {
|
||||
const filter = document.getElementById("vault-filter");
|
||||
const quickSelect = document.getElementById("vault-quick-select");
|
||||
if (filter) filter.value = selectedContextVault;
|
||||
if (quickSelect) quickSelect.value = selectedContextVault;
|
||||
}
|
||||
|
||||
async function refreshSidebarForContext() {
|
||||
@@ -223,6 +257,19 @@
|
||||
safeCreateIcons();
|
||||
}
|
||||
|
||||
async function focusVaultInSidebar(vaultName) {
|
||||
setPanelExpanded("vault", true);
|
||||
const vaultItem = document.querySelector(`.vault-item[data-vault="${CSS.escape(vaultName)}"]`);
|
||||
if (!vaultItem) return;
|
||||
document.querySelectorAll(".vault-item.focused").forEach((el) => el.classList.remove("focused"));
|
||||
vaultItem.classList.add("focused");
|
||||
const childContainer = document.getElementById(`vault-children-${vaultName}`);
|
||||
if (childContainer && childContainer.classList.contains("collapsed")) {
|
||||
await toggleVault(vaultItem, vaultName, true);
|
||||
}
|
||||
vaultItem.scrollIntoView({ block: "nearest" });
|
||||
}
|
||||
|
||||
async function refreshTagsForContext() {
|
||||
const vaultParam = selectedContextVault === "all" ? "" : `?vault=${encodeURIComponent(selectedContextVault)}`;
|
||||
const data = await api(`/api/tags${vaultParam}`);
|
||||
@@ -237,7 +284,10 @@
|
||||
allVaults = vaults;
|
||||
const container = document.getElementById("vault-tree");
|
||||
const filter = document.getElementById("vault-filter");
|
||||
const quickSelect = document.getElementById("vault-quick-select");
|
||||
container.innerHTML = "";
|
||||
filter.innerHTML = '<option value="all">Tous les vaults</option>';
|
||||
quickSelect.innerHTML = '<option value="all">Tous les vaults</option>';
|
||||
|
||||
vaults.forEach((v) => {
|
||||
// Sidebar tree entry
|
||||
@@ -258,16 +308,24 @@
|
||||
opt.value = v.name;
|
||||
opt.textContent = v.name;
|
||||
filter.appendChild(opt);
|
||||
|
||||
const quickOpt = document.createElement("option");
|
||||
quickOpt.value = v.name;
|
||||
quickOpt.textContent = v.name;
|
||||
quickSelect.appendChild(quickOpt);
|
||||
});
|
||||
|
||||
syncVaultSelectors();
|
||||
safeCreateIcons();
|
||||
}
|
||||
|
||||
async function toggleVault(itemEl, vaultName) {
|
||||
async function toggleVault(itemEl, vaultName, forceExpand) {
|
||||
const childContainer = document.getElementById(`vault-children-${vaultName}`);
|
||||
if (!childContainer) return;
|
||||
|
||||
if (childContainer.classList.contains("collapsed")) {
|
||||
const shouldExpand = forceExpand || childContainer.classList.contains("collapsed");
|
||||
|
||||
if (shouldExpand) {
|
||||
// Expand — load children if empty
|
||||
if (childContainer.children.length === 0) {
|
||||
await loadDirectory(vaultName, "", childContainer);
|
||||
@@ -668,6 +726,76 @@
|
||||
area.scrollTop = 0;
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Collapsible panels and help modal
|
||||
// ---------------------------------------------------------------------------
|
||||
function initCollapsiblePanels() {
|
||||
bindPanelToggle("vault", "vault-panel-toggle", "vault-panel-content");
|
||||
bindPanelToggle("tag", "tag-panel-toggle", "tag-panel-content");
|
||||
setPanelExpanded("vault", true);
|
||||
setPanelExpanded("tag", true);
|
||||
}
|
||||
|
||||
function bindPanelToggle(panelKey, toggleId, contentId) {
|
||||
const toggle = document.getElementById(toggleId);
|
||||
const content = document.getElementById(contentId);
|
||||
if (!toggle || !content) return;
|
||||
toggle.addEventListener("click", () => {
|
||||
setPanelExpanded(panelKey, !panelState[panelKey]);
|
||||
});
|
||||
}
|
||||
|
||||
function setPanelExpanded(panelKey, expanded) {
|
||||
panelState[panelKey] = expanded;
|
||||
const toggle = document.getElementById(`${panelKey}-panel-toggle`);
|
||||
const content = document.getElementById(`${panelKey}-panel-content`);
|
||||
if (!toggle || !content) return;
|
||||
toggle.setAttribute("aria-expanded", expanded ? "true" : "false");
|
||||
content.classList.toggle("collapsed", !expanded);
|
||||
const iconEl = toggle.querySelector("[data-lucide]");
|
||||
if (iconEl) {
|
||||
iconEl.setAttribute("data-lucide", expanded ? "chevron-down" : "chevron-right");
|
||||
}
|
||||
if (panelKey === "tag") {
|
||||
const tagSection = document.getElementById("tag-cloud-section");
|
||||
const resizeHandle = document.getElementById("tag-resize-handle");
|
||||
if (tagSection) tagSection.classList.toggle("collapsed-panel", !expanded);
|
||||
if (resizeHandle) resizeHandle.classList.toggle("hidden", !expanded);
|
||||
}
|
||||
safeCreateIcons();
|
||||
}
|
||||
|
||||
function initHelpModal() {
|
||||
const openBtn = document.getElementById("help-open-btn");
|
||||
const closeBtn = document.getElementById("help-close");
|
||||
const modal = document.getElementById("help-modal");
|
||||
if (!openBtn || !closeBtn || !modal) return;
|
||||
|
||||
openBtn.addEventListener("click", () => {
|
||||
modal.classList.add("active");
|
||||
closeHeaderMenu();
|
||||
safeCreateIcons();
|
||||
});
|
||||
|
||||
closeBtn.addEventListener("click", closeHelpModal);
|
||||
modal.addEventListener("click", (e) => {
|
||||
if (e.target === modal) {
|
||||
closeHelpModal();
|
||||
}
|
||||
});
|
||||
|
||||
document.addEventListener("keydown", (e) => {
|
||||
if (e.key === "Escape" && modal.classList.contains("active")) {
|
||||
closeHelpModal();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function closeHelpModal() {
|
||||
const modal = document.getElementById("help-modal");
|
||||
if (modal) modal.classList.remove("active");
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Search
|
||||
// ---------------------------------------------------------------------------
|
||||
@@ -1112,6 +1240,8 @@
|
||||
initSearch();
|
||||
initMobile();
|
||||
initVaultContext();
|
||||
initCollapsiblePanels();
|
||||
initHelpModal();
|
||||
initSidebarFilter();
|
||||
initSidebarResize();
|
||||
initTagResize();
|
||||
|
||||
Reference in New Issue
Block a user