Add desktop sidebar toggle with persistent state, search result highlighting, and improved filter icon positioning
This commit is contained in:
parent
8e1ae4be26
commit
6f694148db
@ -368,6 +368,32 @@
|
|||||||
return res.json();
|
return res.json();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
|
// Sidebar toggle (desktop)
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
|
function initSidebarToggle() {
|
||||||
|
const toggleBtn = document.getElementById("sidebar-toggle-btn");
|
||||||
|
const sidebar = document.getElementById("sidebar");
|
||||||
|
const resizeHandle = document.getElementById("sidebar-resize-handle");
|
||||||
|
|
||||||
|
if (!toggleBtn || !sidebar || !resizeHandle) return;
|
||||||
|
|
||||||
|
// Restore saved state
|
||||||
|
const savedState = localStorage.getItem("obsigate-sidebar-hidden");
|
||||||
|
if (savedState === "true") {
|
||||||
|
sidebar.classList.add("hidden");
|
||||||
|
resizeHandle.classList.add("hidden");
|
||||||
|
toggleBtn.classList.add("active");
|
||||||
|
}
|
||||||
|
|
||||||
|
toggleBtn.addEventListener("click", () => {
|
||||||
|
const isHidden = sidebar.classList.toggle("hidden");
|
||||||
|
resizeHandle.classList.toggle("hidden", isHidden);
|
||||||
|
toggleBtn.classList.toggle("active", isHidden);
|
||||||
|
localStorage.setItem("obsigate-sidebar-hidden", isHidden ? "true" : "false");
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
// ---------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------
|
||||||
// Mobile sidebar
|
// Mobile sidebar
|
||||||
// ---------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------
|
||||||
@ -1580,10 +1606,26 @@
|
|||||||
|
|
||||||
const container = el("div", { class: "search-results" });
|
const container = el("div", { class: "search-results" });
|
||||||
data.results.forEach((r) => {
|
data.results.forEach((r) => {
|
||||||
|
// Create title with highlighting
|
||||||
|
const titleDiv = el("div", { class: "search-result-title" });
|
||||||
|
if (query && query.trim()) {
|
||||||
|
highlightSearchText(titleDiv, r.title, query, searchCaseSensitive);
|
||||||
|
} else {
|
||||||
|
titleDiv.textContent = r.title;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create snippet with highlighting
|
||||||
|
const snippetDiv = el("div", { class: "search-result-snippet" });
|
||||||
|
if (query && query.trim() && r.snippet) {
|
||||||
|
highlightSearchText(snippetDiv, r.snippet, query, searchCaseSensitive);
|
||||||
|
} else {
|
||||||
|
snippetDiv.textContent = r.snippet || "";
|
||||||
|
}
|
||||||
|
|
||||||
const item = el("div", { class: "search-result-item" }, [
|
const item = el("div", { class: "search-result-item" }, [
|
||||||
el("div", { class: "search-result-title" }, [document.createTextNode(r.title)]),
|
titleDiv,
|
||||||
el("div", { class: "search-result-vault" }, [document.createTextNode(r.vault + " / " + r.path)]),
|
el("div", { class: "search-result-vault" }, [document.createTextNode(r.vault + " / " + r.path)]),
|
||||||
el("div", { class: "search-result-snippet" }, [document.createTextNode(r.snippet || "")]),
|
snippetDiv,
|
||||||
]);
|
]);
|
||||||
|
|
||||||
if (r.tags && r.tags.length > 0) {
|
if (r.tags && r.tags.length > 0) {
|
||||||
@ -1764,6 +1806,40 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function highlightSearchText(container, text, query, caseSensitive) {
|
||||||
|
container.textContent = "";
|
||||||
|
if (!query || !text) {
|
||||||
|
container.appendChild(document.createTextNode(text || ""));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const source = caseSensitive ? text : text.toLowerCase();
|
||||||
|
const needle = caseSensitive ? query : query.toLowerCase();
|
||||||
|
let start = 0;
|
||||||
|
let index = source.indexOf(needle, start);
|
||||||
|
|
||||||
|
if (index === -1) {
|
||||||
|
container.appendChild(document.createTextNode(text));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
while (index !== -1) {
|
||||||
|
if (index > start) {
|
||||||
|
container.appendChild(document.createTextNode(text.slice(start, index)));
|
||||||
|
}
|
||||||
|
const mark = el("mark", { class: "search-highlight" }, [
|
||||||
|
document.createTextNode(text.slice(index, index + query.length)),
|
||||||
|
]);
|
||||||
|
container.appendChild(mark);
|
||||||
|
start = index + query.length;
|
||||||
|
index = source.indexOf(needle, start);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (start < text.length) {
|
||||||
|
container.appendChild(document.createTextNode(text.slice(start)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
function showWelcome() {
|
function showWelcome() {
|
||||||
const area = document.getElementById("content-area");
|
const area = document.getElementById("content-area");
|
||||||
area.innerHTML = `
|
area.innerHTML = `
|
||||||
@ -2015,6 +2091,7 @@
|
|||||||
document.getElementById("theme-toggle").addEventListener("click", toggleTheme);
|
document.getElementById("theme-toggle").addEventListener("click", toggleTheme);
|
||||||
document.getElementById("header-logo").addEventListener("click", goHome);
|
document.getElementById("header-logo").addEventListener("click", goHome);
|
||||||
initSearch();
|
initSearch();
|
||||||
|
initSidebarToggle();
|
||||||
initMobile();
|
initMobile();
|
||||||
initVaultContext();
|
initVaultContext();
|
||||||
initSidebarTabs();
|
initSidebarTabs();
|
||||||
|
|||||||
@ -68,6 +68,9 @@
|
|||||||
<button class="hamburger-btn" id="hamburger-btn" title="Menu">
|
<button class="hamburger-btn" id="hamburger-btn" title="Menu">
|
||||||
<i data-lucide="menu" style="width:20px;height:20px"></i>
|
<i data-lucide="menu" style="width:20px;height:20px"></i>
|
||||||
</button>
|
</button>
|
||||||
|
<button class="sidebar-toggle-btn" id="sidebar-toggle-btn" title="Afficher/Masquer la sidebar">
|
||||||
|
<i data-lucide="sidebar" style="width:20px;height:20px"></i>
|
||||||
|
</button>
|
||||||
|
|
||||||
<div class="header-logo" id="header-logo">
|
<div class="header-logo" id="header-logo">
|
||||||
<i data-lucide="book-open" style="width:20px;height:20px"></i>
|
<i data-lucide="book-open" style="width:20px;height:20px"></i>
|
||||||
@ -161,8 +164,8 @@
|
|||||||
|
|
||||||
<!-- Sidebar filter -->
|
<!-- Sidebar filter -->
|
||||||
<div class="sidebar-filter">
|
<div class="sidebar-filter">
|
||||||
<i data-lucide="filter" class="sidebar-filter-icon" style="width:14px;height:14px"></i>
|
|
||||||
<div class="sidebar-filter-input-wrapper">
|
<div class="sidebar-filter-input-wrapper">
|
||||||
|
<i data-lucide="filter" class="sidebar-filter-icon" style="width:14px;height:14px"></i>
|
||||||
<input type="text" id="sidebar-filter-input" placeholder="Filtrer fichiers..." autocomplete="off">
|
<input type="text" id="sidebar-filter-input" placeholder="Filtrer fichiers..." autocomplete="off">
|
||||||
<div class="sidebar-filter-actions">
|
<div class="sidebar-filter-actions">
|
||||||
<button class="sidebar-filter-btn" id="sidebar-filter-case-btn" type="button" title="Respecter la casse" aria-label="Respecter la casse">
|
<button class="sidebar-filter-btn" id="sidebar-filter-case-btn" type="button" title="Respecter la casse" aria-label="Respecter la casse">
|
||||||
|
|||||||
@ -137,6 +137,25 @@ a:hover {
|
|||||||
color: var(--accent);
|
color: var(--accent);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.sidebar-toggle-btn {
|
||||||
|
display: flex;
|
||||||
|
background: none;
|
||||||
|
border: none;
|
||||||
|
color: var(--text-secondary);
|
||||||
|
cursor: pointer;
|
||||||
|
padding: 4px;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
flex-shrink: 0;
|
||||||
|
transition: color 200ms ease;
|
||||||
|
}
|
||||||
|
.sidebar-toggle-btn:hover {
|
||||||
|
color: var(--accent);
|
||||||
|
}
|
||||||
|
.sidebar-toggle-btn.active {
|
||||||
|
color: var(--accent);
|
||||||
|
}
|
||||||
|
|
||||||
.header-logo {
|
.header-logo {
|
||||||
font-family: 'JetBrains Mono', monospace;
|
font-family: 'JetBrains Mono', monospace;
|
||||||
font-weight: 700;
|
font-weight: 700;
|
||||||
@ -544,10 +563,17 @@ select {
|
|||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
transition: background 200ms ease;
|
transition: background 200ms ease, transform 300ms ease;
|
||||||
flex-shrink: 0;
|
flex-shrink: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.sidebar.hidden {
|
||||||
|
transform: translateX(-100%);
|
||||||
|
width: 0;
|
||||||
|
min-width: 0;
|
||||||
|
border-right: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/* --- Sidebar filter --- */
|
/* --- Sidebar filter --- */
|
||||||
.sidebar-filter {
|
.sidebar-filter {
|
||||||
@ -557,7 +583,6 @@ select {
|
|||||||
flex-shrink: 0;
|
flex-shrink: 0;
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
gap: 6px;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.sidebar-filter-input-wrapper {
|
.sidebar-filter-input-wrapper {
|
||||||
@ -863,7 +888,7 @@ select {
|
|||||||
cursor: ew-resize;
|
cursor: ew-resize;
|
||||||
background: transparent;
|
background: transparent;
|
||||||
flex-shrink: 0;
|
flex-shrink: 0;
|
||||||
transition: background 150ms ease;
|
transition: background 150ms ease, opacity 300ms ease;
|
||||||
z-index: 10;
|
z-index: 10;
|
||||||
}
|
}
|
||||||
.sidebar-resize-handle:hover,
|
.sidebar-resize-handle:hover,
|
||||||
@ -871,6 +896,11 @@ select {
|
|||||||
background: var(--accent);
|
background: var(--accent);
|
||||||
opacity: 0.5;
|
opacity: 0.5;
|
||||||
}
|
}
|
||||||
|
.sidebar-resize-handle.hidden {
|
||||||
|
width: 0;
|
||||||
|
opacity: 0;
|
||||||
|
pointer-events: none;
|
||||||
|
}
|
||||||
|
|
||||||
/* --- Content Area --- */
|
/* --- Content Area --- */
|
||||||
.content-area {
|
.content-area {
|
||||||
@ -1238,6 +1268,20 @@ select {
|
|||||||
color: var(--text-secondary);
|
color: var(--text-secondary);
|
||||||
line-height: 1.5;
|
line-height: 1.5;
|
||||||
}
|
}
|
||||||
|
.search-result-snippet .search-highlight {
|
||||||
|
background: color-mix(in srgb, var(--accent) 25%, transparent);
|
||||||
|
color: var(--accent);
|
||||||
|
font-weight: 600;
|
||||||
|
padding: 1px 3px;
|
||||||
|
border-radius: 3px;
|
||||||
|
}
|
||||||
|
.search-result-title .search-highlight {
|
||||||
|
background: color-mix(in srgb, var(--accent) 25%, transparent);
|
||||||
|
color: var(--accent);
|
||||||
|
font-weight: 700;
|
||||||
|
padding: 1px 3px;
|
||||||
|
border-radius: 3px;
|
||||||
|
}
|
||||||
.search-result-tags {
|
.search-result-tags {
|
||||||
margin-top: 6px;
|
margin-top: 6px;
|
||||||
display: flex;
|
display: flex;
|
||||||
@ -1759,6 +1803,10 @@ body.resizing-v {
|
|||||||
display: flex;
|
display: flex;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.sidebar-toggle-btn {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
.header {
|
.header {
|
||||||
gap: 6px;
|
gap: 6px;
|
||||||
padding: 8px 10px;
|
padding: 8px 10px;
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user