Fix layout

This commit is contained in:
Will Miao
2025-03-13 20:37:23 +08:00
parent e7233c147d
commit 0b0caa1142
11 changed files with 175 additions and 75 deletions

View File

@@ -1,11 +0,0 @@
<!DOCTYPE html>
<html lang="en">
<head>
<!-- ...existing code... -->
</head>
<body>
{% include 'components/header.html' %}
<!-- ...existing code... -->
</body>
</html>

View File

@@ -1,10 +0,0 @@
<!DOCTYPE html>
<html lang="en">
<head>
<!-- ...existing code... -->
</head>
<body>
{% include 'components/header.html' %}
<!-- ...existing code... -->
</body>
</html>

View File

@@ -1,10 +0,0 @@
<!DOCTYPE html>
<html lang="en">
<head>
<!-- ...existing code... -->
</head>
<body>
{% include 'components/header.html' %}
<!-- ...existing code... -->
</body>
</html>

View File

@@ -2,10 +2,10 @@
.modal {
display: none;
position: fixed;
top: 0;
top: 48px; /* Start below the header */
left: 0;
width: 100%;
height: 100%;
height: calc(100% - 48px); /* Adjust height to exclude header */
background: rgba(0, 0, 0, 0.2); /* 调整为更淡的半透明黑色 */
z-index: var(--z-modal);
overflow: hidden; /* 改为 hidden防止双滚动条 */
@@ -24,7 +24,7 @@ body.modal-open {
max-width: 800px;
height: auto;
max-height: 90vh;
margin: 2rem auto;
margin: 1rem auto; /* Reduce top margin from 5rem to 1rem */
background: var(--lora-surface);
border-radius: var(--border-radius-base);
padding: var(--space-3);

View File

@@ -144,10 +144,9 @@
/* Filter Panel Styles */
.filter-panel {
position: absolute;
top: 140px; /* Adjust to be closer to the filter button */
position: fixed;
right: 20px;
width: 300px;
width: 320px;
background-color: var(--card-bg);
border: 1px solid var(--border-color);
border-radius: var(--border-radius-base);
@@ -312,7 +311,7 @@
width: calc(100% - 40px);
left: 20px;
right: 20px;
top: 140px;
top: 160px; /* Adjusted for mobile layout */
}
}
@@ -351,10 +350,9 @@
/* Search Options Panel */
.search-options-panel {
position: absolute;
top: 140px;
right: 65px; /* Position it closer to the search options button */
width: 280px; /* Slightly wider to accommodate tags better */
position: fixed;
right: 20px;
width: 280px;
background-color: var(--card-bg);
border: 1px solid var(--border-color);
border-radius: var(--border-radius-base);
@@ -507,4 +505,15 @@ input:checked + .slider:before {
.slider.round:before {
border-radius: 50%;
}
/* Mobile adjustments */
@media (max-width: 768px) {
.search-options-panel,
.filter-panel {
width: calc(100% - 40px);
left: 20px;
right: 20px;
top: 160px; /* Adjusted for mobile layout */
}
}

View File

@@ -4,6 +4,7 @@
overflow-y: auto; /* Enable scrolling here */
width: 100%;
position: relative;
overflow-y: scroll;
}
.container {

View File

@@ -17,7 +17,8 @@ import {
openCivitai,
toggleFolderTags,
initFolderTagsVisibility,
initBackToTop
initBackToTop,
updatePanelPositions
} from './utils/uiHelpers.js';
import { initializeInfiniteScroll } from './utils/infiniteScroll.js';
import { showDeleteModal, confirmDelete, closeDeleteModal } from './utils/modalUtils.js';
@@ -56,6 +57,7 @@ window.toggleApiKeyVisibility = toggleApiKeyVisibility;
window.moveManager = moveManager;
window.toggleShowcase = toggleShowcase;
window.scrollToTop = scrollToTop;
window.updatePanelPositions = updatePanelPositions;
// Export bulk manager methods to window
window.toggleBulkMode = () => bulkManager.toggleBulkMode();
@@ -96,6 +98,12 @@ document.addEventListener('DOMContentLoaded', async () => {
// Initialize the bulk manager
bulkManager.initialize();
// Initial positioning
updatePanelPositions();
// Update positions on window resize
window.addEventListener('resize', updatePanelPositions);
});
// Initialize event listeners

View File

@@ -13,6 +13,7 @@ export class FilterManager {
this.filterPanel = document.getElementById('filterPanel');
this.filterButton = document.getElementById('filterButton');
this.activeFiltersCount = document.getElementById('activeFiltersCount');
this.tagsLoaded = false;
this.initialize();
}
@@ -140,20 +141,35 @@ export class FilterManager {
});
}
toggleFilterPanel() {
const wasHidden = this.filterPanel.classList.contains('hidden');
this.filterPanel.classList.toggle('hidden');
// If the panel is being opened, load the top tags and update selections
if (wasHidden) {
this.loadTopTags();
this.updateTagSelections();
toggleFilterPanel() {
if (this.filterPanel) {
const isHidden = this.filterPanel.classList.contains('hidden');
if (isHidden) {
// Update panel positions before showing
if (window.searchManager && typeof window.searchManager.updatePanelPositions === 'function') {
window.searchManager.updatePanelPositions();
} else if (typeof updatePanelPositions === 'function') {
updatePanelPositions();
}
this.filterPanel.classList.remove('hidden');
this.filterButton.classList.add('active');
// Load tags if they haven't been loaded yet
if (!this.tagsLoaded) {
this.loadTopTags();
this.tagsLoaded = true;
}
} else {
this.closeFilterPanel();
}
}
}
closeFilterPanel() {
this.filterPanel.classList.add('hidden');
this.filterButton.classList.remove('active');
}
updateTagSelections() {

View File

@@ -1,16 +1,19 @@
import { state } from '../state/index.js';
import { loadMoreLoras } from '../api/loraApi.js';
import { debounce } from './debounce.js';
export function initializeInfiniteScroll() {
if (state.observer) {
state.observer.disconnect();
}
const debouncedLoadMore = debounce(loadMoreLoras, 200);
state.observer = new IntersectionObserver(
(entries) => {
const target = entries[0];
if (target.isIntersecting && !state.isLoading && state.hasMore) {
loadMoreLoras();
debouncedLoadMore();
}
},
{ threshold: 0.1 }

View File

@@ -30,6 +30,34 @@ export class SearchManager {
// Initialize search options
this.initSearchOptions();
// Add global click handler to close panels when clicking outside
document.addEventListener('click', (e) => {
// Close search options panel when clicking outside
if (this.searchOptionsPanel &&
!this.searchOptionsPanel.contains(e.target) &&
e.target !== this.searchOptionsToggle &&
!this.searchOptionsToggle.contains(e.target)) {
this.closeSearchOptionsPanel();
}
// Close filter panel when clicking outside (if filterManager exists)
const filterPanel = document.getElementById('filterPanel');
const filterButton = document.getElementById('filterButton');
if (filterPanel &&
!filterPanel.contains(e.target) &&
e.target !== filterButton &&
!filterButton.contains(e.target) &&
window.filterManager) {
window.filterManager.closeFilterPanel();
}
});
// Initialize panel positions
this.updatePanelPositions();
// Add resize listener
window.addEventListener('resize', this.updatePanelPositions.bind(this));
}
initSearchOptions() {
@@ -103,16 +131,6 @@ export class SearchManager {
// Ensure at least one search option is selected
this.validateSearchOptions();
// Close panel when clicking outside
document.addEventListener('click', (e) => {
if (this.searchOptionsPanel &&
!this.searchOptionsPanel.contains(e.target) &&
e.target !== this.searchOptionsToggle &&
!this.searchOptionsToggle.contains(e.target)) {
this.closeSearchOptionsPanel();
}
});
}
// Add method to validate search options
@@ -137,6 +155,8 @@ export class SearchManager {
if (this.searchOptionsPanel) {
const isHidden = this.searchOptionsPanel.classList.contains('hidden');
if (isHidden) {
// Update position before showing
this.updatePanelPositions();
this.searchOptionsPanel.classList.remove('hidden');
this.searchOptionsToggle.classList.add('active');
} else {
@@ -209,6 +229,9 @@ export class SearchManager {
this.isSearching = true;
state.loadingManager.showSimpleLoading('Searching...');
// Store current scroll position
const scrollPosition = window.pageYOffset || document.documentElement.scrollTop;
state.currentPage = 1;
state.hasMore = true;
@@ -250,6 +273,14 @@ export class SearchManager {
state.hasMore = state.currentPage < data.total_pages;
state.currentPage++;
}
// Restore scroll position after content is loaded
setTimeout(() => {
window.scrollTo({
top: scrollPosition,
behavior: 'instant' // Use 'instant' to prevent animation
});
}, 10);
}
} catch (error) {
console.error('Search error:', error);
@@ -259,4 +290,23 @@ export class SearchManager {
state.loadingManager.hide();
}
}
updatePanelPositions() {
const searchOptionsPanel = document.getElementById('searchOptionsPanel');
const filterPanel = document.getElementById('filterPanel');
if (!searchOptionsPanel || !filterPanel) return;
// Get the controls container
const controls = document.querySelector('.controls');
if (!controls) return;
// Calculate the position based on the bottom of the controls container
const controlsRect = controls.getBoundingClientRect();
const topPosition = controlsRect.bottom + 10; // Add 10px padding
// Set the positions
searchOptionsPanel.style.top = `${topPosition}px`;
filterPanel.style.top = `${topPosition}px`;
}
}

View File

@@ -98,16 +98,57 @@ export function openCivitai(modelName) {
}
}
/**
* Dynamically positions the search options panel and filter panel
* based on the current layout and folder tags container height
*/
export function updatePanelPositions() {
const searchOptionsPanel = document.getElementById('searchOptionsPanel');
const filterPanel = document.getElementById('filterPanel');
if (!searchOptionsPanel || !filterPanel) return;
// Get the controls container
const controls = document.querySelector('.controls');
if (!controls) return;
// Calculate the position based on the bottom of the controls container
const controlsRect = controls.getBoundingClientRect();
const topPosition = controlsRect.bottom + 10; // Add 10px padding
// Set the positions
searchOptionsPanel.style.top = `${topPosition}px`;
filterPanel.style.top = `${topPosition}px`;
}
// Update the toggleFolderTags function
export function toggleFolderTags() {
const folderTags = document.querySelector('.folder-tags');
const btn = document.querySelector('.toggle-folders-btn');
const isCollapsed = folderTags.classList.toggle('collapsed');
const toggleBtn = document.querySelector('.toggle-folders-btn i');
// 更新按钮提示文本
btn.title = isCollapsed ? 'Expand folder tags' : 'Collapse folder tags';
// 保存状态到 localStorage
localStorage.setItem('folderTagsCollapsed', isCollapsed);
if (folderTags) {
folderTags.classList.toggle('collapsed');
if (folderTags.classList.contains('collapsed')) {
toggleBtn.className = 'fas fa-chevron-down';
toggleBtn.parentElement.title = 'Expand folder tags';
localStorage.setItem('folderTagsCollapsed', 'true');
} else {
toggleBtn.className = 'fas fa-chevron-up';
toggleBtn.parentElement.title = 'Collapse folder tags';
localStorage.setItem('folderTagsCollapsed', 'false');
}
// 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();
}
}, 50);
}
}
// Add this to your existing initialization code
@@ -128,10 +169,13 @@ export function initBackToTop() {
button.title = 'Back to top';
document.body.appendChild(button);
// Get the scrollable container
const scrollContainer = document.querySelector('.page-content');
// Show/hide button based on scroll position
const toggleBackToTop = () => {
const scrollThreshold = window.innerHeight * 0.75;
if (window.scrollY > scrollThreshold) {
const scrollThreshold = window.innerHeight * 0.3;
if (scrollContainer.scrollTop > scrollThreshold) {
button.classList.add('visible');
} else {
button.classList.remove('visible');
@@ -140,14 +184,14 @@ export function initBackToTop() {
// Smooth scroll to top
button.addEventListener('click', () => {
window.scrollTo({
scrollContainer.scrollTo({
top: 0,
behavior: 'smooth'
});
});
// Listen for scroll events
window.addEventListener('scroll', toggleBackToTop);
// Listen for scroll events on the scrollable container
scrollContainer.addEventListener('scroll', toggleBackToTop);
// Initial check
toggleBackToTop();