Add custom scroll-into-view for tree items with smooth positioning and improve sidebar panel collapse animations with flex-based layout transitions

This commit is contained in:
Bruno Charest 2026-03-21 23:41:49 -04:00
parent a42f61e59a
commit 7aec49776e
2 changed files with 90 additions and 4 deletions

View File

@ -232,6 +232,30 @@
if (quickSelect) quickSelect.value = selectedContextVault; if (quickSelect) quickSelect.value = selectedContextVault;
} }
function scrollTreeItemIntoView(element, alignToTop) {
if (!element) return;
const scrollContainer = document.getElementById("vault-panel-content");
if (!scrollContainer) return;
const containerRect = scrollContainer.getBoundingClientRect();
const elementRect = element.getBoundingClientRect();
const isAbove = elementRect.top < containerRect.top;
const isBelow = elementRect.bottom > containerRect.bottom;
if (!isAbove && !isBelow && !alignToTop) return;
const currentTop = scrollContainer.scrollTop;
const offsetTop = element.offsetTop;
const targetTop = alignToTop
? Math.max(0, offsetTop - 8)
: Math.max(0, currentTop + (elementRect.top - containerRect.top) - (containerRect.height * 0.35));
scrollContainer.scrollTo({
top: targetTop,
behavior: "smooth",
});
}
async function refreshSidebarForContext() { async function refreshSidebarForContext() {
const container = document.getElementById("vault-tree"); const container = document.getElementById("vault-tree");
container.innerHTML = ""; container.innerHTML = "";
@ -267,7 +291,7 @@
if (childContainer && childContainer.classList.contains("collapsed")) { if (childContainer && childContainer.classList.contains("collapsed")) {
await toggleVault(vaultItem, vaultName, true); await toggleVault(vaultItem, vaultName, true);
} }
vaultItem.scrollIntoView({ block: "nearest" }); scrollTreeItemIntoView(vaultItem, false);
} }
async function refreshTagsForContext() { async function refreshTagsForContext() {
@ -323,6 +347,8 @@
const childContainer = document.getElementById(`vault-children-${vaultName}`); const childContainer = document.getElementById(`vault-children-${vaultName}`);
if (!childContainer) return; if (!childContainer) return;
scrollTreeItemIntoView(itemEl, false);
const shouldExpand = forceExpand || childContainer.classList.contains("collapsed"); const shouldExpand = forceExpand || childContainer.classList.contains("collapsed");
if (shouldExpand) { if (shouldExpand) {
@ -364,6 +390,7 @@
fragment.appendChild(subContainer); fragment.appendChild(subContainer);
dirItem.addEventListener("click", async () => { dirItem.addEventListener("click", async () => {
scrollTreeItemIntoView(dirItem, false);
if (subContainer.classList.contains("collapsed")) { if (subContainer.classList.contains("collapsed")) {
if (subContainer.children.length === 0) { if (subContainer.children.length === 0) {
await loadDirectory(vaultName, item.path, subContainer); await loadDirectory(vaultName, item.path, subContainer);
@ -387,6 +414,7 @@
document.createTextNode(` ${displayName}`), document.createTextNode(` ${displayName}`),
]); ]);
fileItem.addEventListener("click", () => { fileItem.addEventListener("click", () => {
scrollTreeItemIntoView(fileItem, false);
openFile(vaultName, item.path); openFile(vaultName, item.path);
closeMobileSidebar(); closeMobileSidebar();
}); });
@ -747,6 +775,7 @@
function setPanelExpanded(panelKey, expanded) { function setPanelExpanded(panelKey, expanded) {
panelState[panelKey] = expanded; panelState[panelKey] = expanded;
const sidebar = document.getElementById("sidebar");
const toggle = document.getElementById(`${panelKey}-panel-toggle`); const toggle = document.getElementById(`${panelKey}-panel-toggle`);
const content = document.getElementById(`${panelKey}-panel-content`); const content = document.getElementById(`${panelKey}-panel-content`);
if (!toggle || !content) return; if (!toggle || !content) return;
@ -762,6 +791,10 @@
if (tagSection) tagSection.classList.toggle("collapsed-panel", !expanded); if (tagSection) tagSection.classList.toggle("collapsed-panel", !expanded);
if (resizeHandle) resizeHandle.classList.toggle("hidden", !expanded); if (resizeHandle) resizeHandle.classList.toggle("hidden", !expanded);
} }
if (sidebar) {
sidebar.classList.toggle("vault-collapsed", !panelState.vault);
sidebar.classList.toggle("tag-collapsed", !panelState.tag);
}
safeCreateIcons(); safeCreateIcons();
} }

View File

@ -314,6 +314,17 @@ a:hover {
flex-shrink: 0; flex-shrink: 0;
} }
.sidebar.vault-collapsed .tag-cloud-section {
flex: 1 1 auto;
min-height: 0;
max-height: none;
}
.sidebar.tag-collapsed .sidebar-tree {
flex: 1 1 auto;
min-height: 0;
}
/* --- Sidebar filter --- */ /* --- Sidebar filter --- */
.sidebar-filter { .sidebar-filter {
padding: 10px 12px; padding: 10px 12px;
@ -347,9 +358,11 @@ a:hover {
.sidebar-tree { .sidebar-tree {
flex: 1; flex: 1;
overflow-y: auto; overflow: hidden;
padding: 12px 0; padding: 12px 0;
min-height: 80px; min-height: 80px;
display: flex;
flex-direction: column;
} }
.sidebar-tree::-webkit-scrollbar { .sidebar-tree::-webkit-scrollbar {
@ -396,7 +409,29 @@ a:hover {
} }
.sidebar-panel-content.collapsed { .sidebar-panel-content.collapsed {
display: none; max-height: 0 !important;
opacity: 0;
overflow: hidden;
pointer-events: none;
}
.sidebar-panel-content {
min-height: 0;
overflow: hidden;
opacity: 1;
transition: max-height 340ms ease, opacity 220ms ease;
}
#vault-panel-content {
flex: 1 1 auto;
min-height: 0;
overflow-y: auto;
max-height: 1000px;
scroll-behavior: smooth;
}
#tag-panel-content {
max-height: 320px;
} }
.tree-item { .tree-item {
@ -446,6 +481,7 @@ a:hover {
.tree-children { .tree-children {
padding-left: 16px; padding-left: 16px;
overflow: hidden;
} }
.tree-children.collapsed { .tree-children.collapsed {
@ -476,14 +512,18 @@ a:hover {
height: 180px; height: 180px;
min-height: 60px; min-height: 60px;
max-height: 400px; max-height: 400px;
overflow-y: auto; overflow: hidden;
flex-shrink: 0; flex-shrink: 0;
display: flex;
flex-direction: column;
transition: height 340ms ease, min-height 340ms ease, max-height 340ms ease, flex 340ms ease;
} }
.tag-cloud-section.collapsed-panel { .tag-cloud-section.collapsed-panel {
height: auto; height: auto;
min-height: 0; min-height: 0;
max-height: none; max-height: none;
overflow: hidden; overflow: hidden;
flex: 0 0 auto;
} }
.tag-cloud-section::-webkit-scrollbar { .tag-cloud-section::-webkit-scrollbar {
width: 6px; width: 6px;
@ -510,6 +550,11 @@ a:hover {
padding: 0 16px 12px; padding: 0 16px 12px;
} }
.tag-cloud-section:not(.collapsed-panel) #tag-panel-content {
overflow-y: auto;
flex: 1 1 auto;
}
.tag-item { .tag-item {
display: inline-block; display: inline-block;
padding: 2px 8px; padding: 2px 8px;
@ -1281,6 +1326,14 @@ body.resizing-v {
min-height: 80px; min-height: 80px;
} }
#vault-panel-content {
max-height: none;
}
#tag-panel-content {
max-height: none;
}
.help-content { .help-content {
padding: 20px 16px 28px; padding: 20px 16px 28px;
} }