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:
+2745
-133
File diff suppressed because it is too large
Load Diff
+60
-40
@@ -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');
|
||||
|
||||
@@ -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>
|
||||
|
||||
Reference in New Issue
Block a user