Refactor panel position management and enhance recipe card handling

- Removed redundant updatePanelPositions calls from various components and centralized the logic in the uiHelpers.js for better maintainability.
- Introduced appendRecipeCards function in RecipeManager to streamline the addition of recipe cards from search results.
- Cleaned up unused code related to search input handling and recipe loading, improving overall code clarity and performance.
- Updated HeaderManager and SearchManager to utilize the new updatePanelPositions function, ensuring consistent panel positioning across the application.
This commit is contained in:
Will Miao
2025-03-20 09:54:13 +08:00
parent caf5b1528c
commit a88b0239eb
9 changed files with 56 additions and 202 deletions

View File

@@ -101,53 +101,4 @@ export class HeaderManager {
});
}
}
// Helper method to update panel positions (can be called from window resize handlers)
updatePanelPositions() {
if (this.searchManager && typeof this.searchManager.updatePanelPositions === 'function') {
this.searchManager.updatePanelPositions();
} else {
const searchOptionsPanel = document.getElementById('searchOptionsPanel');
const filterPanel = document.getElementById('filterPanel');
if (!searchOptionsPanel && !filterPanel) return;
// Get the header element
const header = document.querySelector('.app-header');
if (!header) return;
// Calculate the position based on the bottom of the header
const headerRect = header.getBoundingClientRect();
const topPosition = headerRect.bottom + 5; // Add 5px padding
// Set the positions
if (searchOptionsPanel) {
searchOptionsPanel.style.top = `${topPosition}px`;
}
if (filterPanel) {
filterPanel.style.top = `${topPosition}px`;
}
// Adjust panel horizontal position based on the search container
const searchContainer = document.querySelector('.header-search');
if (searchContainer) {
const searchRect = searchContainer.getBoundingClientRect();
// Position the search options panel aligned with the search container
if (searchOptionsPanel) {
searchOptionsPanel.style.right = `${window.innerWidth - searchRect.right}px`;
}
// Position the filter panel aligned with the filter button
if (filterPanel) {
const filterButton = document.getElementById('filterButton');
if (filterButton) {
const filterRect = filterButton.getBoundingClientRect();
filterPanel.style.right = `${window.innerWidth - filterRect.right}px`;
}
}
}
}
}
}
}

View File

@@ -5,7 +5,7 @@ import { modalManager } from './managers/ModalManager.js';
import { updateService } from './managers/UpdateService.js';
import { HeaderManager } from './components/Header.js';
import { SettingsManager } from './managers/SettingsManager.js';
import { showToast, initTheme, initBackToTop, updatePanelPositions, lazyLoadImages } from './utils/uiHelpers.js';
import { showToast, initTheme, initBackToTop, lazyLoadImages } from './utils/uiHelpers.js';
import { initializeInfiniteScroll } from './utils/infiniteScroll.js';
// Core application class
@@ -30,12 +30,6 @@ export class AppCore {
initTheme();
initBackToTop();
// Set up event listeners
window.addEventListener('resize', updatePanelPositions);
// Initial positioning
updatePanelPositions();
// Mark as initialized
this.initialized = true;
@@ -66,9 +60,6 @@ export class AppCore {
initializeInfiniteScroll(pageType);
}
// Update panel positions
updatePanelPositions();
return this;
}
}

View File

@@ -1,6 +1,6 @@
import { BASE_MODELS, BASE_MODEL_CLASSES } from '../utils/constants.js';
import { state, getCurrentPageState } from '../state/index.js';
import { showToast } from '../utils/uiHelpers.js';
import { showToast, updatePanelPositions } from '../utils/uiHelpers.js';
import { resetAndReload } from '../api/loraApi.js';
export class FilterManager {
@@ -155,11 +155,7 @@ export class FilterManager {
if (isHidden) {
// Update panel positions before showing
if (window.searchManager && typeof window.searchManager.updatePanelPositions === 'function') {
window.searchManager.updatePanelPositions();
} else if (typeof updatePanelPositions === 'function') {
updatePanelPositions();
}
updatePanelPositions();
this.filterPanel.classList.remove('hidden');
this.filterButton.classList.add('active');

View File

@@ -1,4 +1,4 @@
import { showToast } from '../utils/uiHelpers.js';
import { showToast, updatePanelPositions } from '../utils/uiHelpers.js';
export class RecipeFilterManager {
constructor() {
@@ -165,11 +165,7 @@ export class RecipeFilterManager {
if (isHidden) {
// Update panel positions before showing
if (window.searchManager && typeof window.searchManager.updatePanelPositions === 'function') {
window.searchManager.updatePanelPositions();
} else if (typeof updatePanelPositions === 'function') {
updatePanelPositions();
}
updatePanelPositions();
this.filterPanel.classList.remove('hidden');
this.filterButton.classList.add('active');

View File

@@ -33,10 +33,7 @@ export class RecipeSearchManager extends SearchManager {
const grid = document.getElementById('recipeGrid');
if (!searchTerm) {
if (state) {
state.pages.recipes.currentPage = 1;
}
this.resetAndReloadRecipes();
window.recipeManager.loadRecipes();
return;
}
@@ -112,34 +109,12 @@ export class RecipeSearchManager extends SearchManager {
}
}
resetAndReloadRecipes() {
if (window.recipeManager && typeof window.recipeManager.loadRecipes === 'function') {
window.recipeManager.loadRecipes();
} else {
// Fallback to reloading the page
window.location.reload();
}
}
appendRecipeCards(recipes) {
// This function would be implemented in the recipes page
// Similar to appendLoraCards for loras
const grid = document.getElementById('recipeGrid');
if (!grid) return;
if (typeof window.appendRecipeCards === 'function') {
window.appendRecipeCards(recipes);
} else {
// Fallback implementation
recipes.forEach(recipe => {
const card = document.createElement('div');
card.className = 'recipe-card';
card.innerHTML = `
<h3>${recipe.name}</h3>
<p>${recipe.description || 'No description'}</p>
`;
grid.appendChild(card);
});
}
// Create data object in the format expected by the RecipeManager
const data = { items: recipes, has_more: false };
window.recipeManager.updateRecipesGrid(data, false);
}
}

View File

@@ -1,3 +1,4 @@
import { updatePanelPositions } from "../utils/uiHelpers.js";
/**
* SearchManager - Handles search functionality across different pages
* Each page can extend or customize this base functionality
@@ -27,11 +28,10 @@ export class SearchManager {
this.initEventListeners();
this.loadSearchPreferences();
// Initialize panel positions
this.updatePanelPositions();
updatePanelPositions();
// Add resize listener
window.addEventListener('resize', this.updatePanelPositions.bind(this));
window.addEventListener('resize', updatePanelPositions);
}
initEventListeners() {
@@ -161,7 +161,7 @@ export class SearchManager {
const isHidden = this.searchOptionsPanel.classList.contains('hidden');
if (isHidden) {
// Update position before showing
this.updatePanelPositions();
updatePanelPositions();
this.searchOptionsPanel.classList.remove('hidden');
this.searchOptionsToggle.classList.add('active');
@@ -267,56 +267,6 @@ export class SearchManager {
return options;
}
updatePanelPositions() {
const searchOptionsPanel = document.getElementById('searchOptionsPanel');
const filterPanel = document.getElementById('filterPanel');
if (!searchOptionsPanel && !filterPanel) return;
// Get the header element
const header = document.querySelector('.app-header');
if (!header) return;
// Calculate the position based on the bottom of the header
const headerRect = header.getBoundingClientRect();
const topPosition = headerRect.bottom + 5; // Add 5px padding
// Set the positions
if (searchOptionsPanel) {
searchOptionsPanel.style.top = `${topPosition}px`;
// Make sure the panel is visible when positioned
if (!searchOptionsPanel.classList.contains('hidden') &&
window.getComputedStyle(searchOptionsPanel).display === 'none') {
searchOptionsPanel.style.display = 'block';
}
}
if (filterPanel) {
filterPanel.style.top = `${topPosition}px`;
}
// Adjust panel horizontal position based on the search container
const searchContainer = document.querySelector('.header-search');
if (searchContainer) {
const searchRect = searchContainer.getBoundingClientRect();
// Position the search options panel aligned with the search container
if (searchOptionsPanel) {
searchOptionsPanel.style.right = `${window.innerWidth - searchRect.right}px`;
}
// Position the filter panel aligned with the filter button
if (filterPanel) {
const filterButton = document.getElementById('filterButton');
if (filterButton) {
const filterRect = filterButton.getBoundingClientRect();
filterPanel.style.right = `${window.innerWidth - filterRect.right}px`;
}
}
}
}
performSearch() {
const query = this.searchInput.value.trim();
const options = this.getActiveSearchOptions();

View File

@@ -59,6 +59,12 @@ class RecipeManager {
window.importRecipes = () => this.importRecipes();
window.importManager = this.importManager;
window.loadMoreRecipes = () => this.loadMoreRecipes();
// Add appendRecipeCards function for the search manager to use
window.appendRecipeCards = (recipes) => {
const data = { items: recipes, has_more: false };
this.updateRecipesGrid(data, false);
};
}
initEventListeners() {
@@ -70,28 +76,6 @@ class RecipeManager {
this.loadRecipes();
});
}
// Search input
const searchInput = document.getElementById('searchInput');
if (searchInput) {
let debounceTimeout;
searchInput.addEventListener('input', () => {
clearTimeout(debounceTimeout);
debounceTimeout = setTimeout(() => {
this.pageState.filters.search = searchInput.value;
this.loadRecipes();
}, 300);
});
}
// Import button
const importButton = document.querySelector('button[onclick="importRecipes()"]');
if (importButton) {
importButton.onclick = (e) => {
e.preventDefault();
this.importManager.showImportModal();
};
}
}
async loadRecipes(resetPage = true) {

View File

@@ -106,20 +106,45 @@ export function updatePanelPositions() {
const searchOptionsPanel = document.getElementById('searchOptionsPanel');
const filterPanel = document.getElementById('filterPanel');
if (!searchOptionsPanel || !filterPanel) return;
if (!searchOptionsPanel && !filterPanel) return;
// Get the controls container
const controls = document.querySelector('.controls');
if (!controls) return;
// Get the header element
const header = document.querySelector('.app-header');
if (!header) return;
// Calculate the position based on the bottom of the controls container
const controlsRect = controls.getBoundingClientRect();
const topPosition = controlsRect.bottom + 10; // Add 10px padding
// Calculate the position based on the bottom of the header
const headerRect = header.getBoundingClientRect();
const topPosition = headerRect.bottom + 5; // Add 5px padding
// Set the positions
searchOptionsPanel.style.top = `${topPosition}px`;
filterPanel.style.top = `${topPosition}px`;
}
if (searchOptionsPanel) {
searchOptionsPanel.style.top = `${topPosition}px`;
}
if (filterPanel) {
filterPanel.style.top = `${topPosition}px`;
}
// Adjust panel horizontal position based on the search container
const searchContainer = document.querySelector('.header-search');
if (searchContainer) {
const searchRect = searchContainer.getBoundingClientRect();
// Position the search options panel aligned with the search container
if (searchOptionsPanel) {
searchOptionsPanel.style.right = `${window.innerWidth - searchRect.right}px`;
}
// Position the filter panel aligned with the filter button
if (filterPanel) {
const filterButton = document.getElementById('filterButton');
if (filterButton) {
const filterRect = filterButton.getBoundingClientRect();
filterPanel.style.right = `${window.innerWidth - filterRect.right}px`;
}
}
}
}
// Update the toggleFolderTags function
export function toggleFolderTags() {
@@ -144,11 +169,7 @@ export function toggleFolderTags() {
// Update panel positions after toggling
// Use a small delay to ensure the DOM has updated
setTimeout(() => {
if (window.searchManager && typeof window.searchManager.updatePanelPositions === 'function') {
window.searchManager.updatePanelPositions();
} else if (typeof updatePanelPositions === 'function') {
updatePanelPositions();
}
updatePanelPositions();
}, 50);
}
}

View File

@@ -76,14 +76,4 @@
{% block main_script %}
<script type="module" src="/loras_static/js/recipes.js"></script>
{% endblock %}
{% block additional_scripts %}
<script>
// Refresh recipes
function refreshRecipes() {
// Will be implemented in recipes.js
window.location.reload();
}
</script>
{% endblock %}