Consolidate duplicate collapsible state management
This commit is contained in:
184
static/scripts/collapsible-state.js
Normal file
184
static/scripts/collapsible-state.js
Normal file
@@ -0,0 +1,184 @@
|
||||
/**
|
||||
* Shared collapsible state management for filters and sort sections
|
||||
* Handles BK_SHOW_GRID_FILTERS and BK_SHOW_GRID_SORT configuration with user preferences
|
||||
*/
|
||||
|
||||
// Generic state management for collapsible sections (filter and sort)
|
||||
function initializeCollapsibleState(elementId, storageKey) {
|
||||
const element = document.getElementById(elementId);
|
||||
const toggleButton = document.querySelector(`[data-bs-target="#${elementId}"]`);
|
||||
|
||||
if (!element || !toggleButton) return;
|
||||
|
||||
// Restore state on page load
|
||||
const savedState = sessionStorage.getItem(storageKey);
|
||||
if (savedState === 'open') {
|
||||
// User explicitly opened it
|
||||
element.classList.add('show');
|
||||
toggleButton.setAttribute('aria-expanded', 'true');
|
||||
} else if (savedState === 'closed') {
|
||||
// User explicitly closed it, override template state
|
||||
element.classList.remove('show');
|
||||
toggleButton.setAttribute('aria-expanded', 'false');
|
||||
}
|
||||
// If no saved state, keep the template state (respects BK_SHOW_GRID_FILTERS/BK_SHOW_GRID_SORT)
|
||||
|
||||
// Listen for toggle events
|
||||
element.addEventListener('show.bs.collapse', () => {
|
||||
sessionStorage.setItem(storageKey, 'open');
|
||||
});
|
||||
|
||||
element.addEventListener('hide.bs.collapse', () => {
|
||||
sessionStorage.setItem(storageKey, 'closed');
|
||||
});
|
||||
}
|
||||
|
||||
// Initialize filter and sort states for a specific page
|
||||
function initializePageCollapsibleStates(pagePrefix, filterElementId = 'table-filter', sortElementId = 'table-sort') {
|
||||
initializeCollapsibleState(filterElementId, `${pagePrefix}-filter-state`);
|
||||
initializeCollapsibleState(sortElementId, `${pagePrefix}-sort-state`);
|
||||
}
|
||||
|
||||
// Shared function to preserve filter state during filter changes
|
||||
function preserveCollapsibleStateOnChange(elementId, storageKey) {
|
||||
const element = document.getElementById(elementId);
|
||||
const wasOpen = element && element.classList.contains('show');
|
||||
|
||||
// Store the state to restore after page reload
|
||||
if (wasOpen) {
|
||||
sessionStorage.setItem(storageKey, 'open');
|
||||
}
|
||||
}
|
||||
|
||||
// Setup color dropdown with visual indicators (shared implementation)
|
||||
function setupColorDropdown() {
|
||||
const colorSelect = document.getElementById('filter-color');
|
||||
if (!colorSelect) return;
|
||||
|
||||
// Add color squares to option text
|
||||
const options = colorSelect.querySelectorAll('option[data-color-rgb]');
|
||||
options.forEach(option => {
|
||||
const colorRgb = option.dataset.colorRgb;
|
||||
const colorId = option.dataset.colorId;
|
||||
const colorName = option.textContent.trim();
|
||||
|
||||
if (colorRgb && colorId !== '9999') {
|
||||
// Create a visual indicator (using Unicode square)
|
||||
option.textContent = `${colorName}`; //■
|
||||
//option.style.color = `#${colorRgb}`;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// Check if pagination mode is enabled for a specific table
|
||||
function isPaginationModeForTable(tableId) {
|
||||
const tableElement = document.querySelector(`#${tableId}`);
|
||||
return tableElement && tableElement.getAttribute('data-table') === 'false';
|
||||
}
|
||||
|
||||
// Shared sort button setup function
|
||||
window.setupSharedSortButtons = function(tableId, tableInstanceGlobal, columnMap) {
|
||||
const sortButtons = document.querySelectorAll('[data-sort-attribute]');
|
||||
const clearButton = document.querySelector('[data-sort-clear]');
|
||||
const isPaginationMode = isPaginationModeForTable(tableId);
|
||||
|
||||
sortButtons.forEach(button => {
|
||||
button.addEventListener('click', () => {
|
||||
const attribute = button.dataset.sortAttribute;
|
||||
const isDesc = button.dataset.sortDesc === 'true';
|
||||
|
||||
if (isPaginationMode) {
|
||||
// PAGINATION MODE - Server-side sorting via URL parameters
|
||||
const currentUrl = new URL(window.location);
|
||||
const currentSort = currentUrl.searchParams.get('sort');
|
||||
const currentOrder = currentUrl.searchParams.get('order');
|
||||
const isCurrentlyActive = currentSort === attribute;
|
||||
|
||||
let newDirection;
|
||||
if (isCurrentlyActive) {
|
||||
// Toggle direction if same attribute
|
||||
newDirection = currentOrder === 'asc' ? 'desc' : 'asc';
|
||||
} else {
|
||||
// Use default direction for new attribute
|
||||
newDirection = isDesc ? 'desc' : 'asc';
|
||||
}
|
||||
|
||||
// Set sort parameters and reset to first page
|
||||
currentUrl.searchParams.set('sort', attribute);
|
||||
currentUrl.searchParams.set('order', newDirection);
|
||||
currentUrl.searchParams.set('page', '1');
|
||||
|
||||
// Navigate to sorted results
|
||||
window.location.href = currentUrl.toString();
|
||||
|
||||
} else {
|
||||
// ORIGINAL MODE - Client-side sorting via Simple DataTables
|
||||
const columnIndex = columnMap[attribute];
|
||||
const tableInstance = window[tableInstanceGlobal];
|
||||
|
||||
if (columnIndex !== undefined && tableInstance) {
|
||||
// Determine sort direction
|
||||
const isCurrentlyActive = button.classList.contains('btn-primary');
|
||||
const currentDirection = button.dataset.currentDirection || (isDesc ? 'desc' : 'asc');
|
||||
const newDirection = isCurrentlyActive ?
|
||||
(currentDirection === 'asc' ? 'desc' : 'asc') :
|
||||
(isDesc ? 'desc' : 'asc');
|
||||
|
||||
// Clear other active buttons
|
||||
sortButtons.forEach(btn => {
|
||||
btn.classList.remove('btn-primary');
|
||||
btn.classList.add('btn-outline-primary');
|
||||
btn.removeAttribute('data-current-direction');
|
||||
});
|
||||
|
||||
// Mark this button as active
|
||||
button.classList.remove('btn-outline-primary');
|
||||
button.classList.add('btn-primary');
|
||||
button.dataset.currentDirection = newDirection;
|
||||
|
||||
// Apply sort using Simple DataTables API
|
||||
tableInstance.table.columns.sort(columnIndex, newDirection);
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
if (clearButton) {
|
||||
clearButton.addEventListener('click', () => {
|
||||
if (isPaginationMode) {
|
||||
// PAGINATION MODE - Clear server-side sorting via URL parameters
|
||||
const currentUrl = new URL(window.location);
|
||||
currentUrl.searchParams.delete('sort');
|
||||
currentUrl.searchParams.delete('order');
|
||||
currentUrl.searchParams.set('page', '1');
|
||||
window.location.href = currentUrl.toString();
|
||||
|
||||
} else {
|
||||
// ORIGINAL MODE - Clear client-side sorting
|
||||
// Clear all sort buttons
|
||||
sortButtons.forEach(btn => {
|
||||
btn.classList.remove('btn-primary');
|
||||
btn.classList.add('btn-outline-primary');
|
||||
btn.removeAttribute('data-current-direction');
|
||||
});
|
||||
|
||||
// Reset table sort - remove all sorting
|
||||
const tableInstance = window[tableInstanceGlobal];
|
||||
if (tableInstance) {
|
||||
const tableElement = document.querySelector(`#${tableId}`);
|
||||
const currentPerPage = tableInstance.table.options.perPage;
|
||||
tableInstance.table.destroy();
|
||||
|
||||
setTimeout(() => {
|
||||
// Create new instance using the globally available BrickTable class
|
||||
const newInstance = new window.BrickTable(tableElement, currentPerPage);
|
||||
window[tableInstanceGlobal] = newInstance;
|
||||
|
||||
// Re-enable search functionality
|
||||
newInstance.table.searchable = true;
|
||||
}, 50);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
};
|
||||
@@ -25,18 +25,15 @@ function filterByOwner() {
|
||||
window.location.href = currentUrl.toString();
|
||||
}
|
||||
|
||||
// Initialize filter and sort states for minifigures page
|
||||
function initializeCollapsibleStates() {
|
||||
initializePageCollapsibleStates('minifigures');
|
||||
}
|
||||
|
||||
// Keep filters expanded after selection
|
||||
function filterByOwnerAndKeepOpen() {
|
||||
// Remember if filters were open
|
||||
const filterSection = document.getElementById('table-filter');
|
||||
const wasOpen = filterSection && filterSection.classList.contains('show');
|
||||
|
||||
preserveCollapsibleStateOnChange('table-filter', 'minifigures-filter-state');
|
||||
filterByOwner();
|
||||
|
||||
// Store the state to restore after page reload
|
||||
if (wasOpen) {
|
||||
sessionStorage.setItem('keepFiltersOpen', 'true');
|
||||
}
|
||||
}
|
||||
|
||||
// Setup table search and sort functionality
|
||||
@@ -44,18 +41,8 @@ document.addEventListener("DOMContentLoaded", () => {
|
||||
const searchInput = document.getElementById('table-search');
|
||||
const searchClear = document.getElementById('table-search-clear');
|
||||
|
||||
// Restore filter state after page load
|
||||
if (sessionStorage.getItem('keepFiltersOpen') === 'true') {
|
||||
const filterSection = document.getElementById('table-filter');
|
||||
const filterButton = document.querySelector('[data-bs-target="#table-filter"]');
|
||||
|
||||
if (filterSection && filterButton) {
|
||||
filterSection.classList.add('show');
|
||||
filterButton.setAttribute('aria-expanded', 'true');
|
||||
}
|
||||
|
||||
sessionStorage.removeItem('keepFiltersOpen');
|
||||
}
|
||||
// Initialize collapsible states (filter and sort)
|
||||
initializeCollapsibleStates();
|
||||
|
||||
if (searchInput && searchClear) {
|
||||
if (isPaginationMode()) {
|
||||
|
||||
@@ -33,154 +33,31 @@ function applyFilters() {
|
||||
window.location.href = currentUrl.toString();
|
||||
}
|
||||
|
||||
function setupColorDropdown() {
|
||||
const colorSelect = document.getElementById('filter-color');
|
||||
if (!colorSelect) return;
|
||||
// setupColorDropdown is now in shared collapsible-state.js
|
||||
|
||||
// Add color squares to option text
|
||||
const options = colorSelect.querySelectorAll('option[data-color-rgb]');
|
||||
options.forEach(option => {
|
||||
const colorRgb = option.dataset.colorRgb;
|
||||
const colorId = option.dataset.colorId;
|
||||
const colorName = option.textContent.trim();
|
||||
|
||||
if (colorRgb && colorId !== '9999') {
|
||||
// Create a visual indicator (using Unicode square)
|
||||
option.textContent = `${colorName}`; //■
|
||||
//option.style.color = `#${colorRgb}`;
|
||||
}
|
||||
});
|
||||
// Initialize filter and sort states for parts page
|
||||
function initializeCollapsibleStates() {
|
||||
initializePageCollapsibleStates('parts');
|
||||
}
|
||||
|
||||
// Keep filters expanded after selection
|
||||
function applyFiltersAndKeepOpen() {
|
||||
// Remember if filters were open
|
||||
const filterSection = document.getElementById('table-filter');
|
||||
const wasOpen = filterSection && filterSection.classList.contains('show');
|
||||
|
||||
preserveCollapsibleStateOnChange('table-filter', 'parts-filter-state');
|
||||
applyFilters();
|
||||
|
||||
// Store the state to restore after page reload
|
||||
if (wasOpen) {
|
||||
sessionStorage.setItem('keepFiltersOpen', 'true');
|
||||
}
|
||||
}
|
||||
|
||||
function setupSortButtons() {
|
||||
// Sort button functionality
|
||||
const sortButtons = document.querySelectorAll('[data-sort-attribute]');
|
||||
const clearButton = document.querySelector('[data-sort-clear]');
|
||||
|
||||
sortButtons.forEach(button => {
|
||||
button.addEventListener('click', () => {
|
||||
const attribute = button.dataset.sortAttribute;
|
||||
const isDesc = button.dataset.sortDesc === 'true';
|
||||
|
||||
if (isPaginationMode()) {
|
||||
// PAGINATION MODE - Server-side sorting via URL parameters
|
||||
const currentUrl = new URL(window.location);
|
||||
|
||||
// Determine sort direction
|
||||
const currentSort = currentUrl.searchParams.get('sort');
|
||||
const currentOrder = currentUrl.searchParams.get('order');
|
||||
const isCurrentlyActive = currentSort === attribute;
|
||||
|
||||
let newDirection;
|
||||
if (isCurrentlyActive) {
|
||||
// Toggle direction if same attribute
|
||||
newDirection = currentOrder === 'asc' ? 'desc' : 'asc';
|
||||
} else {
|
||||
// Use default direction for new attribute
|
||||
newDirection = isDesc ? 'desc' : 'asc';
|
||||
}
|
||||
|
||||
// Set sort parameters and reset to first page
|
||||
currentUrl.searchParams.set('sort', attribute);
|
||||
currentUrl.searchParams.set('order', newDirection);
|
||||
currentUrl.searchParams.set('page', '1');
|
||||
|
||||
// Navigate to sorted results
|
||||
window.location.href = currentUrl.toString();
|
||||
|
||||
} else {
|
||||
// ORIGINAL MODE - Client-side sorting via Simple DataTables
|
||||
// Get column index based on attribute
|
||||
const columnMap = {
|
||||
'name': 1,
|
||||
'color': 2,
|
||||
'quantity': 3,
|
||||
'missing': 4,
|
||||
'damaged': 5,
|
||||
'sets': 6,
|
||||
'minifigures': 7
|
||||
};
|
||||
|
||||
const columnIndex = columnMap[attribute];
|
||||
if (columnIndex !== undefined && window.partsTableInstance) {
|
||||
// Determine sort direction
|
||||
const isCurrentlyActive = button.classList.contains('btn-primary');
|
||||
const currentDirection = button.dataset.currentDirection || (isDesc ? 'desc' : 'asc');
|
||||
const newDirection = isCurrentlyActive ?
|
||||
(currentDirection === 'asc' ? 'desc' : 'asc') :
|
||||
(isDesc ? 'desc' : 'asc');
|
||||
|
||||
// Clear other active buttons
|
||||
sortButtons.forEach(btn => {
|
||||
btn.classList.remove('btn-primary');
|
||||
btn.classList.add('btn-outline-primary');
|
||||
btn.removeAttribute('data-current-direction');
|
||||
});
|
||||
|
||||
// Mark this button as active
|
||||
button.classList.remove('btn-outline-primary');
|
||||
button.classList.add('btn-primary');
|
||||
button.dataset.currentDirection = newDirection;
|
||||
|
||||
// Apply sort using Simple DataTables API
|
||||
window.partsTableInstance.table.columns.sort(columnIndex, newDirection);
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
if (clearButton) {
|
||||
clearButton.addEventListener('click', () => {
|
||||
if (isPaginationMode()) {
|
||||
// PAGINATION MODE - Clear server-side sorting via URL parameters
|
||||
const currentUrl = new URL(window.location);
|
||||
currentUrl.searchParams.delete('sort');
|
||||
currentUrl.searchParams.delete('order');
|
||||
currentUrl.searchParams.set('page', '1');
|
||||
window.location.href = currentUrl.toString();
|
||||
|
||||
} else {
|
||||
// ORIGINAL MODE - Clear client-side sorting
|
||||
// Clear all sort buttons
|
||||
sortButtons.forEach(btn => {
|
||||
btn.classList.remove('btn-primary');
|
||||
btn.classList.add('btn-outline-primary');
|
||||
btn.removeAttribute('data-current-direction');
|
||||
});
|
||||
|
||||
// Reset table sort - remove all sorting
|
||||
if (window.partsTableInstance) {
|
||||
// Destroy and recreate to clear sorting
|
||||
const tableElement = document.querySelector('#parts');
|
||||
const currentPerPage = window.partsTableInstance.table.options.perPage;
|
||||
window.partsTableInstance.table.destroy();
|
||||
|
||||
setTimeout(() => {
|
||||
// Create new instance using the globally available BrickTable class
|
||||
const newInstance = new window.BrickTable(tableElement, currentPerPage);
|
||||
window.partsTableInstance = newInstance;
|
||||
|
||||
// Re-enable search functionality
|
||||
newInstance.table.searchable = true;
|
||||
}, 50);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
const columnMap = {
|
||||
'name': 1,
|
||||
'color': 2,
|
||||
'quantity': 3,
|
||||
'missing': 4,
|
||||
'damaged': 5,
|
||||
'sets': 6,
|
||||
'minifigures': 7
|
||||
};
|
||||
// Use shared sort buttons setup from collapsible-state.js
|
||||
window.setupSharedSortButtons('parts', 'partsTableInstance', columnMap);
|
||||
}
|
||||
|
||||
// Check if pagination mode is enabled
|
||||
@@ -220,22 +97,12 @@ document.addEventListener("DOMContentLoaded", () => {
|
||||
const searchInput = document.getElementById('table-search');
|
||||
const searchClear = document.getElementById('table-search-clear');
|
||||
|
||||
// Initialize collapsible states (filter and sort)
|
||||
initializeCollapsibleStates();
|
||||
|
||||
// Setup color dropdown with color squares
|
||||
setupColorDropdown();
|
||||
|
||||
// Restore filter state after page load
|
||||
if (sessionStorage.getItem('keepFiltersOpen') === 'true') {
|
||||
const filterSection = document.getElementById('table-filter');
|
||||
const filterButton = document.querySelector('[data-bs-target="#table-filter"]');
|
||||
|
||||
if (filterSection && filterButton) {
|
||||
filterSection.classList.add('show');
|
||||
filterButton.setAttribute('aria-expanded', 'true');
|
||||
}
|
||||
|
||||
sessionStorage.removeItem('keepFiltersOpen');
|
||||
}
|
||||
|
||||
if (searchInput && searchClear) {
|
||||
if (isPaginationMode()) {
|
||||
// PAGINATION MODE - Server-side search with Enter key
|
||||
|
||||
@@ -1,3 +1,71 @@
|
||||
// Check if we're in pagination mode (server-side) or original mode (client-side)
|
||||
function isPaginationMode() {
|
||||
const tableElement = document.querySelector('#problems');
|
||||
return tableElement && tableElement.getAttribute('data-table') === 'false';
|
||||
}
|
||||
|
||||
// Initialize filter and sort states for problems page
|
||||
function initializeCollapsibleStates() {
|
||||
initializePageCollapsibleStates('problems');
|
||||
}
|
||||
|
||||
// Apply filters with current state
|
||||
function applyFilters() {
|
||||
const ownerSelect = document.getElementById('filter-owner');
|
||||
const colorSelect = document.getElementById('filter-color');
|
||||
const currentUrl = new URL(window.location);
|
||||
|
||||
// Reset to first page when filters change (only for pagination mode)
|
||||
if (isPaginationMode()) {
|
||||
currentUrl.searchParams.set('page', '1');
|
||||
}
|
||||
|
||||
// Handle owner filter
|
||||
if (ownerSelect) {
|
||||
const selectedOwner = ownerSelect.value;
|
||||
if (selectedOwner === 'all') {
|
||||
currentUrl.searchParams.delete('owner');
|
||||
} else {
|
||||
currentUrl.searchParams.set('owner', selectedOwner);
|
||||
}
|
||||
}
|
||||
|
||||
// Handle color filter
|
||||
if (colorSelect) {
|
||||
const selectedColor = colorSelect.value;
|
||||
if (selectedColor === 'all') {
|
||||
currentUrl.searchParams.delete('color');
|
||||
} else {
|
||||
currentUrl.searchParams.set('color', selectedColor);
|
||||
}
|
||||
}
|
||||
|
||||
window.location.href = currentUrl.toString();
|
||||
}
|
||||
|
||||
// Keep filters expanded after selection
|
||||
function applyFiltersAndKeepOpen() {
|
||||
preserveCollapsibleStateOnChange('table-filter', 'problems-filter-state');
|
||||
applyFilters();
|
||||
}
|
||||
|
||||
// setupColorDropdown is now in shared collapsible-state.js
|
||||
|
||||
// Setup sort button functionality
|
||||
function setupSortButtons() {
|
||||
const columnMap = {
|
||||
'name': 1,
|
||||
'color': 2,
|
||||
'quantity': 3,
|
||||
'missing': 4,
|
||||
'damaged': 5,
|
||||
'sets': 6,
|
||||
'minifigures': 7
|
||||
};
|
||||
// Use shared sort buttons setup from collapsible-state.js
|
||||
window.setupSharedSortButtons('problems', 'problemsTableInstance', columnMap);
|
||||
}
|
||||
|
||||
// Problems page functionality
|
||||
function setupProblemsPage() {
|
||||
// Handle search input
|
||||
@@ -60,20 +128,13 @@ function setupProblemsPage() {
|
||||
});
|
||||
}
|
||||
|
||||
// Initialize collapsible states (filter and sort)
|
||||
initializeCollapsibleStates();
|
||||
|
||||
// Setup sort and filter functionality (from parts.js)
|
||||
setupSortButtons();
|
||||
setupColorDropdown();
|
||||
|
||||
// Restore filter state if needed
|
||||
const keepFiltersOpen = sessionStorage.getItem('keepFiltersOpen');
|
||||
if (keepFiltersOpen === 'true') {
|
||||
const filterSection = document.getElementById('table-filter');
|
||||
if (filterSection && !filterSection.classList.contains('show')) {
|
||||
filterSection.classList.add('show');
|
||||
}
|
||||
sessionStorage.removeItem('keepFiltersOpen');
|
||||
}
|
||||
|
||||
// Update active sort button based on current URL parameters
|
||||
const urlParams = new URLSearchParams(window.location.search);
|
||||
const currentSort = urlParams.get('sort');
|
||||
|
||||
@@ -6,51 +6,15 @@ function isPaginationMode() {
|
||||
return gridElement && gridElement.getAttribute('data-grid') === 'false';
|
||||
}
|
||||
|
||||
// Filter state management
|
||||
function initializeFilterState() {
|
||||
const filterElement = document.getElementById('grid-filter');
|
||||
const filterToggleButton = document.querySelector('[data-bs-target="#grid-filter"]');
|
||||
|
||||
if (!filterElement || !filterToggleButton) return;
|
||||
|
||||
// Key for localStorage specific to sets page
|
||||
const FILTER_STATE_KEY = 'sets-filter-state';
|
||||
|
||||
// Restore filter state on page load
|
||||
const savedState = localStorage.getItem(FILTER_STATE_KEY);
|
||||
if (savedState === 'open') {
|
||||
// Override the template state and show the filter
|
||||
filterElement.classList.add('show');
|
||||
filterToggleButton.setAttribute('aria-expanded', 'true');
|
||||
} else if (savedState === 'closed') {
|
||||
// Explicitly hide if it was closed by user
|
||||
filterElement.classList.remove('show');
|
||||
filterToggleButton.setAttribute('aria-expanded', 'false');
|
||||
}
|
||||
|
||||
// Listen for filter toggle events
|
||||
filterElement.addEventListener('show.bs.collapse', () => {
|
||||
localStorage.setItem(FILTER_STATE_KEY, 'open');
|
||||
});
|
||||
|
||||
filterElement.addEventListener('hide.bs.collapse', () => {
|
||||
localStorage.setItem(FILTER_STATE_KEY, 'closed');
|
||||
});
|
||||
|
||||
// Clear state when leaving the page
|
||||
window.addEventListener('beforeunload', () => {
|
||||
// Only clear if user is navigating away from sets page
|
||||
const currentPath = window.location.pathname;
|
||||
if (!currentPath.includes('/sets')) {
|
||||
localStorage.removeItem(FILTER_STATE_KEY);
|
||||
}
|
||||
});
|
||||
// Initialize filter and sort states for sets page
|
||||
function initializeCollapsibleStates() {
|
||||
initializePageCollapsibleStates('sets', 'grid-filter', 'grid-sort');
|
||||
}
|
||||
|
||||
// Setup page functionality
|
||||
document.addEventListener("DOMContentLoaded", () => {
|
||||
// Initialize filter state management
|
||||
initializeFilterState();
|
||||
// Initialize collapsible states (filter and sort)
|
||||
initializeCollapsibleStates();
|
||||
|
||||
const searchInput = document.getElementById('grid-search');
|
||||
const searchClear = document.getElementById('grid-search-clear');
|
||||
|
||||
Reference in New Issue
Block a user