feat: Introduce Shaarli Professional theme with modern sidebar layout, light/dark mode, and associated JavaScript.
This commit is contained in:
parent
0bdf95e00a
commit
646f92005f
@ -3093,3 +3093,23 @@ select:focus {
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
|
||||
/* Filter Badge Indicator */
|
||||
.filter-badge {
|
||||
position: absolute;
|
||||
top: 2px;
|
||||
right: 2px;
|
||||
width: 8px;
|
||||
height: 8px;
|
||||
background: #ef4444;
|
||||
border-radius: 50%;
|
||||
border: 2px solid var(--header-bg);
|
||||
}
|
||||
|
||||
.header-action-btn {
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.header-action-btn.has-active-filter {
|
||||
background: rgba(239, 68, 68, 0.2);
|
||||
}
|
||||
|
||||
@ -410,15 +410,7 @@ document.addEventListener('DOMContentLoaded', () => {
|
||||
let basePath = (typeof shaarli !== 'undefined' && shaarli.basePath) ? shaarli.basePath : '';
|
||||
let url = basePath + '/';
|
||||
|
||||
// Save filter state to localStorage before navigation
|
||||
const filterState = {
|
||||
private: isPrivate,
|
||||
public: isPublic,
|
||||
untagged: isUntagged,
|
||||
timestamp: Date.now()
|
||||
};
|
||||
localStorage.setItem('shaarliFilterState', JSON.stringify(filterState));
|
||||
console.log('[Filter] Saved filter state:', filterState);
|
||||
console.log('[Filter] Applying filters - private:', isPrivate, 'public:', isPublic, 'untagged:', isUntagged);
|
||||
|
||||
// Build the URL based on selected filters using the correct Shaarli admin paths
|
||||
if (isPrivate && isUntagged) {
|
||||
@ -430,22 +422,18 @@ document.addEventListener('DOMContentLoaded', () => {
|
||||
} else if (isPublic) {
|
||||
url = basePath + '/admin/visibility/public';
|
||||
} else if (isUntagged) {
|
||||
url = basePath + '/untagged-only';
|
||||
} else {
|
||||
// No filter selected - clear the state
|
||||
localStorage.removeItem('shaarliFilterState');
|
||||
url = basePath + '/?searchtags=';
|
||||
}
|
||||
// else: no filter, url stays as basePath + '/'
|
||||
|
||||
console.log('[Filter] Navigating to:', url);
|
||||
window.location.href = url;
|
||||
}
|
||||
|
||||
// Clear filters function
|
||||
function clearFilters() {
|
||||
localStorage.removeItem('shaarliFilterState');
|
||||
if (filterPrivate) filterPrivate.checked = false;
|
||||
if (filterPublic) filterPublic.checked = false;
|
||||
if (filterUntagged) filterUntagged.checked = false;
|
||||
// Clear all filters and go to home
|
||||
function clearAllFilters() {
|
||||
const basePath = (typeof shaarli !== 'undefined' && shaarli.basePath) ? shaarli.basePath : '';
|
||||
window.location.href = basePath + '/';
|
||||
}
|
||||
|
||||
// Private and public are mutually exclusive
|
||||
@ -467,34 +455,24 @@ document.addEventListener('DOMContentLoaded', () => {
|
||||
applyFilters();
|
||||
});
|
||||
|
||||
// Initialize filter states from localStorage (since Shaarli redirects after applying filters)
|
||||
// Initialize filter states from URL (detect current filter state from page URL)
|
||||
(function initFilterStates() {
|
||||
console.log('[Filter Debug] ========================================');
|
||||
|
||||
// Try to read filter state from localStorage
|
||||
let savedState = null;
|
||||
try {
|
||||
const savedJson = localStorage.getItem('shaarliFilterState');
|
||||
if (savedJson) {
|
||||
savedState = JSON.parse(savedJson);
|
||||
console.log('[Filter Debug] Found saved filter state:', savedState);
|
||||
// Detect filter state from current URL path
|
||||
const currentPath = window.location.pathname;
|
||||
const currentSearch = window.location.search;
|
||||
|
||||
// Check if state is not too old (30 seconds max)
|
||||
const age = Date.now() - (savedState.timestamp || 0);
|
||||
if (age > 30000) {
|
||||
console.log('[Filter Debug] Saved state is too old, ignoring');
|
||||
localStorage.removeItem('shaarliFilterState');
|
||||
savedState = null;
|
||||
}
|
||||
}
|
||||
} catch (e) {
|
||||
console.log('[Filter Debug] Error reading saved state:', e);
|
||||
}
|
||||
// Detect visibility filter from URL path
|
||||
let isPrivateActive = currentPath.includes('/visibility/private') || currentPath.includes('/admin/visibility/private');
|
||||
let isPublicActive = currentPath.includes('/visibility/public') || currentPath.includes('/admin/visibility/public');
|
||||
|
||||
// Determine active filters from saved state
|
||||
let isPrivateActive = savedState?.private || false;
|
||||
let isPublicActive = savedState?.public || false;
|
||||
let isUntaggedActive = savedState?.untagged || false;
|
||||
// Detect untagged filter
|
||||
let isUntaggedActive = currentPath.includes('/untagged-only') ||
|
||||
(currentSearch.includes('searchtags=') && !currentSearch.match(/searchtags=[^&]+/));
|
||||
|
||||
console.log('[Filter Debug] URL path:', currentPath);
|
||||
console.log('[Filter Debug] URL search:', currentSearch);
|
||||
|
||||
console.log('[Filter Debug] isPrivateActive:', isPrivateActive);
|
||||
console.log('[Filter Debug] isPublicActive:', isPublicActive);
|
||||
@ -534,24 +512,31 @@ document.addEventListener('DOMContentLoaded', () => {
|
||||
|
||||
// Create and display the filter info banner
|
||||
if (hasActiveFilter && !document.getElementById('filter-info-banner')) {
|
||||
// Build the message - describe the active filter without count
|
||||
// Get result count from page
|
||||
let resultCount = '';
|
||||
const pagingStats = document.querySelector('.paging-stats strong:last-child');
|
||||
if (pagingStats) {
|
||||
resultCount = pagingStats.textContent;
|
||||
}
|
||||
|
||||
// Build the message with result count like "X results with status private"
|
||||
let filterParts = [];
|
||||
|
||||
if (isPrivateActive) {
|
||||
filterParts.push('<strong>private</strong> links');
|
||||
filterParts.push('with status <strong>private</strong>');
|
||||
} else if (isPublicActive) {
|
||||
filterParts.push('<strong>public</strong> links');
|
||||
filterParts.push('with status <strong>public</strong>');
|
||||
}
|
||||
|
||||
if (isUntaggedActive) {
|
||||
if (filterParts.length > 0) {
|
||||
filterParts.push('without tags');
|
||||
filterParts.push('and <strong>untagged</strong>');
|
||||
} else {
|
||||
filterParts.push('links <strong>without tags</strong>');
|
||||
filterParts.push('<strong>untagged</strong>');
|
||||
}
|
||||
}
|
||||
|
||||
let message = 'Showing ' + filterParts.join(' ');
|
||||
let message = resultCount ? resultCount + ' results ' + filterParts.join(' ') : 'Showing ' + filterParts.join(' ') + ' links';
|
||||
|
||||
console.log('[Filter Debug] filterParts:', filterParts);
|
||||
console.log('[Filter Debug] message:', message);
|
||||
@ -599,8 +584,7 @@ document.addEventListener('DOMContentLoaded', () => {
|
||||
|
||||
// Add click handler to clear button
|
||||
document.getElementById('filter-clear-btn')?.addEventListener('click', () => {
|
||||
localStorage.removeItem('shaarliFilterState');
|
||||
console.log('[Filter] Cleared filter state from localStorage');
|
||||
console.log('[Filter] Clear button clicked, navigating to home');
|
||||
window.location.href = basePath + '/';
|
||||
});
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user