feat: refactorer l'interface avec système de vues grid/list/compact, toolbar moderne avec toggle de vue et pagination intégrée, styles hover unifiés pour header-nav-link avec état active, suppression des pills Notes obsolètes, ajout content-container responsive, link cards avec sélection visuelle et checkbox repositionné, badges visibility/readlater optimisés, et restructuration majeure du CSS avec 2500+ lignes de nouveaux styles pour grilles adaptatives, modales, formulaires, et compos

This commit is contained in:
2026-02-20 10:31:41 -05:00
parent 83d9a7425e
commit f230ff0acd
9 changed files with 15500 additions and 189 deletions
+2745 -133
View File
File diff suppressed because it is too large Load Diff
+60 -40
View File
@@ -85,20 +85,21 @@ document.addEventListener('DOMContentLoaded', () => {
});
// ===== Search Overlay (Spotlight Style) =====
const searchOverlay = document.getElementById('search-overlay');
const searchToggleBtns = document.querySelectorAll('[data-search-toggle]');
const searchModalInput = document.getElementById('search-modal-input');
const searchResults = document.getElementById('search-results');
const searchTagsBtn = document.getElementById('search-tags-btn');
const searchAllBtn = document.getElementById('search-all-btn');
const searchNotesBtn = document.getElementById('search-notes-btn');
const searchForm = document.getElementById('search-form');
try {
const searchOverlay = document.getElementById('search-overlay');
const searchToggleBtns = document.querySelectorAll('[data-search-toggle]');
const searchModalInput = document.getElementById('search-modal-input');
const searchResults = document.getElementById('search-results');
const searchTagsBtn = document.getElementById('search-tags-btn');
const searchAllBtn = document.getElementById('search-all-btn');
const searchNotesBtn = document.getElementById('search-notes-btn');
const searchForm = document.getElementById('search-form');
let searchMode = 'search'; // 'search' | 'tags' | 'notes'
let selectedIndex = -1;
let searchTimeout = null;
let cachedBookmarks = null;
let cachedTags = null;
let searchMode = 'search'; // 'search' | 'tags' | 'notes'
let selectedIndex = -1;
let searchTimeout = null;
let cachedBookmarks = null;
let cachedTags = null;
// Escape HTML to prevent XSS
function escapeHtml(text) {
@@ -185,12 +186,12 @@ document.addEventListener('DOMContentLoaded', () => {
function getSearchHint(mode) {
if (mode === 'tags') {
return 'Type to search in tags...';
return 'Rechercher dans les tags...';
}
if (mode === 'notes') {
return 'Type to search in notes...';
return 'Rechercher dans les notes...';
}
return 'Type to search in bookmarks and notes...';
return 'Rechercher dans les bookmarks et notes...';
}
// Render search results (tags, global bookmarks, or notes)
@@ -375,6 +376,36 @@ document.addEventListener('DOMContentLoaded', () => {
selectedIndex = -1;
}
function submitSearchByMode() {
const basePath = (typeof shaarli !== 'undefined' && shaarli.basePath) ? shaarli.basePath : '';
const query = (searchModalInput?.value || '').trim();
if (selectedIndex >= 0 && navigateToSelected()) {
return;
}
if (!query) {
if (searchMode === 'tags') {
window.location.href = `${basePath}/tags/cloud`;
return;
}
window.location.href = `${basePath}/`;
return;
}
if (searchMode === 'tags') {
window.location.href = `${basePath}/?searchtags=${encodeURIComponent(query)}`;
return;
}
if (searchMode === 'notes') {
window.location.href = `${basePath}/?searchterm=${encodeURIComponent(query)}&searchtags=note`;
return;
}
window.location.href = `${basePath}/?searchterm=${encodeURIComponent(query)}`;
}
// Toggle search mode (tags vs search)
function setSearchMode(mode) {
if (!['search', 'tags', 'notes'].includes(mode)) {
@@ -391,6 +422,10 @@ document.addEventListener('DOMContentLoaded', () => {
searchTagsBtn?.setAttribute('aria-pressed', String(mode === 'tags'));
searchNotesBtn?.setAttribute('aria-pressed', String(mode === 'notes'));
searchAllBtn?.setAttribute('aria-selected', String(mode === 'search'));
searchTagsBtn?.setAttribute('aria-selected', String(mode === 'tags'));
searchNotesBtn?.setAttribute('aria-selected', String(mode === 'notes'));
if (searchModalInput) {
searchModalInput.name = mode === 'tags' ? 'searchtags' : 'searchterm';
searchModalInput.placeholder = getSearchHint(mode);
@@ -427,19 +462,8 @@ document.addEventListener('DOMContentLoaded', () => {
});
searchForm?.addEventListener('submit', (e) => {
if (searchMode === 'notes') {
e.preventDefault();
if (selectedIndex < 0) {
updateSelection(0);
}
navigateToSelected();
return;
}
if (selectedIndex >= 0) {
e.preventDefault();
navigateToSelected();
}
e.preventDefault();
submitSearchByMode();
});
// Live search on input
@@ -455,7 +479,7 @@ document.addEventListener('DOMContentLoaded', () => {
});
// Keyboard shortcuts
document.addEventListener('keydown', (e) => {
document.addEventListener('keydown', (e) => {
const isSearchOpen = searchOverlay?.classList.contains('show');
const target = e.target;
const isTyping = Boolean(
@@ -490,15 +514,8 @@ document.addEventListener('DOMContentLoaded', () => {
updateSelection(selectedIndex - 1);
break;
case 'Enter':
// If an item is selected, navigate to it
if (selectedIndex >= 0 && navigateToSelected()) {
e.preventDefault();
} else if (searchMode === 'notes') {
e.preventDefault();
updateSelection(0);
navigateToSelected();
}
// Otherwise, submit the form normally
e.preventDefault();
submitSearchByMode();
break;
}
return;
@@ -521,7 +538,10 @@ document.addEventListener('DOMContentLoaded', () => {
e.preventDefault();
openSearch('notes');
}
});
});
} catch (error) {
console.error('Search overlay initialization failed:', error);
}
// ===== Filter Panel =====
const filterToggleBtn = document.getElementById('filter-toggle-btn');
+26 -16
View File
@@ -264,20 +264,30 @@ Bookmarklet detection logic
<div class="search-modal">
<form method="GET" action="{$base_path}/" id="search-form">
<div class="search-modal-header">
<input type="text" name="searchterm" class="search-modal-input" id="search-modal-input"
placeholder="Type to search..." autocomplete="off" value="{if="isset($search_term)"}{$search_term}{/if}">
<div class="search-modal-actions">
<button type="button" class="search-pill-btn search-pill-notes" id="search-notes-btn" aria-pressed="false">
<i class="mdi mdi-note-text-outline"></i>
<span>notes</span>
<div class="search-input-row">
<i class="mdi mdi-magnify search-input-icon" aria-hidden="true"></i>
<input type="text" name="searchterm" class="search-modal-input" id="search-modal-input"
placeholder="Rechercher des notes, tags..." autocomplete="off" value="{if="isset($search_term)"}{$search_term}{/if}">
<button type="submit" class="search-submit-btn" id="search-submit-btn" aria-label="Lancer la recherche">
<i class="mdi mdi-arrow-right"></i>
</button>
<button type="button" class="search-pill-btn search-pill-tags" id="search-tags-btn">
<i class="mdi mdi-tag-outline"></i>
<span>tags</span>
</button>
<button type="button" class="search-pill-btn search-pill-search active" id="search-all-btn" aria-pressed="true">
</div>
<div class="search-mode-bar" role="tablist" aria-label="Modes de recherche">
<button type="button" class="search-mode-btn active" id="search-all-btn" aria-pressed="true" role="tab">
<i class="mdi mdi-magnify"></i>
<span>search</span>
<span>Globale</span>
<kbd class="search-mode-kbd">S</kbd>
</button>
<button type="button" class="search-mode-btn" id="search-tags-btn" aria-pressed="false" role="tab">
<i class="mdi mdi-pound"></i>
<span>Tags</span>
<kbd class="search-mode-kbd">T</kbd>
</button>
<button type="button" class="search-mode-btn" id="search-notes-btn" aria-pressed="false" role="tab">
<i class="mdi mdi-note-text-outline"></i>
<span>Notes</span>
<kbd class="search-mode-kbd">N</kbd>
</button>
</div>
</div>
@@ -285,7 +295,7 @@ Bookmarklet detection logic
<div class="search-separator"></div>
<div class="search-results" id="search-results">
<div class="search-results-empty">
<span class="search-results-hint">Start typing to see suggestions...</span>
<span class="search-results-hint">Tapez pour voir des suggestions...</span>
</div>
</div>
<div class="search-footer">
@@ -293,9 +303,9 @@ Bookmarklet detection logic
<kbd class="search-kbd">S</kbd> global
<kbd class="search-kbd">T</kbd> tags
<kbd class="search-kbd">N</kbd> notes
<kbd class="search-kbd">↑</kbd><kbd class="search-kbd">↓</kbd> to navigate
<kbd class="search-kbd">Enter</kbd> to select
<kbd class="search-kbd">ESC</kbd> to close
<kbd class="search-kbd">↑</kbd><kbd class="search-kbd">↓</kbd> naviguer
<kbd class="search-kbd">Enter</kbd> sélectionner
<kbd class="search-kbd">Esc</kbd> fermer
</span>
</div>
</div>