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

View File

@@ -144,10 +144,9 @@
/* Filter Panel Styles */ /* Filter Panel Styles */
.filter-panel { .filter-panel {
position: absolute; position: fixed;
top: 140px; /* Adjust to be closer to the filter button */
right: 20px; right: 20px;
width: 300px; width: 320px;
background-color: var(--card-bg); background-color: var(--card-bg);
border: 1px solid var(--border-color); border: 1px solid var(--border-color);
border-radius: var(--border-radius-base); border-radius: var(--border-radius-base);
@@ -312,7 +311,7 @@
width: calc(100% - 40px); width: calc(100% - 40px);
left: 20px; left: 20px;
right: 20px; right: 20px;
top: 140px; top: 160px; /* Adjusted for mobile layout */
} }
} }
@@ -351,10 +350,9 @@
/* Search Options Panel */ /* Search Options Panel */
.search-options-panel { .search-options-panel {
position: absolute; position: fixed;
top: 140px; right: 20px;
right: 65px; /* Position it closer to the search options button */ width: 280px;
width: 280px; /* Slightly wider to accommodate tags better */
background-color: var(--card-bg); background-color: var(--card-bg);
border: 1px solid var(--border-color); border: 1px solid var(--border-color);
border-radius: var(--border-radius-base); border-radius: var(--border-radius-base);
@@ -508,3 +506,14 @@ input:checked + .slider:before {
.slider.round:before { .slider.round:before {
border-radius: 50%; 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 */ overflow-y: auto; /* Enable scrolling here */
width: 100%; width: 100%;
position: relative; position: relative;
overflow-y: scroll;
} }
.container { .container {

View File

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

View File

@@ -13,6 +13,7 @@ export class FilterManager {
this.filterPanel = document.getElementById('filterPanel'); this.filterPanel = document.getElementById('filterPanel');
this.filterButton = document.getElementById('filterButton'); this.filterButton = document.getElementById('filterButton');
this.activeFiltersCount = document.getElementById('activeFiltersCount'); this.activeFiltersCount = document.getElementById('activeFiltersCount');
this.tagsLoaded = false;
this.initialize(); this.initialize();
} }
@@ -141,19 +142,34 @@ export class FilterManager {
} }
toggleFilterPanel() { toggleFilterPanel() {
const wasHidden = this.filterPanel.classList.contains('hidden'); if (this.filterPanel) {
const isHidden = this.filterPanel.classList.contains('hidden');
this.filterPanel.classList.toggle('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();
}
// If the panel is being opened, load the top tags and update selections this.filterPanel.classList.remove('hidden');
if (wasHidden) { this.filterButton.classList.add('active');
this.loadTopTags();
this.updateTagSelections(); // Load tags if they haven't been loaded yet
if (!this.tagsLoaded) {
this.loadTopTags();
this.tagsLoaded = true;
}
} else {
this.closeFilterPanel();
}
} }
} }
closeFilterPanel() { closeFilterPanel() {
this.filterPanel.classList.add('hidden'); this.filterPanel.classList.add('hidden');
this.filterButton.classList.remove('active');
} }
updateTagSelections() { updateTagSelections() {

View File

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

View File

@@ -30,6 +30,34 @@ export class SearchManager {
// Initialize search options // Initialize search options
this.initSearchOptions(); 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() { initSearchOptions() {
@@ -103,16 +131,6 @@ export class SearchManager {
// Ensure at least one search option is selected // Ensure at least one search option is selected
this.validateSearchOptions(); 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 // Add method to validate search options
@@ -137,6 +155,8 @@ export class SearchManager {
if (this.searchOptionsPanel) { if (this.searchOptionsPanel) {
const isHidden = this.searchOptionsPanel.classList.contains('hidden'); const isHidden = this.searchOptionsPanel.classList.contains('hidden');
if (isHidden) { if (isHidden) {
// Update position before showing
this.updatePanelPositions();
this.searchOptionsPanel.classList.remove('hidden'); this.searchOptionsPanel.classList.remove('hidden');
this.searchOptionsToggle.classList.add('active'); this.searchOptionsToggle.classList.add('active');
} else { } else {
@@ -209,6 +229,9 @@ export class SearchManager {
this.isSearching = true; this.isSearching = true;
state.loadingManager.showSimpleLoading('Searching...'); state.loadingManager.showSimpleLoading('Searching...');
// Store current scroll position
const scrollPosition = window.pageYOffset || document.documentElement.scrollTop;
state.currentPage = 1; state.currentPage = 1;
state.hasMore = true; state.hasMore = true;
@@ -250,6 +273,14 @@ export class SearchManager {
state.hasMore = state.currentPage < data.total_pages; state.hasMore = state.currentPage < data.total_pages;
state.currentPage++; state.currentPage++;
} }
// Restore scroll position after content is loaded
setTimeout(() => {
window.scrollTo({
top: scrollPosition,
behavior: 'instant' // Use 'instant' to prevent animation
});
}, 10);
} }
} catch (error) { } catch (error) {
console.error('Search error:', error); console.error('Search error:', error);
@@ -259,4 +290,23 @@ export class SearchManager {
state.loadingManager.hide(); 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() { export function toggleFolderTags() {
const folderTags = document.querySelector('.folder-tags'); const folderTags = document.querySelector('.folder-tags');
const btn = document.querySelector('.toggle-folders-btn'); const toggleBtn = document.querySelector('.toggle-folders-btn i');
const isCollapsed = folderTags.classList.toggle('collapsed');
// 更新按钮提示文本 if (folderTags) {
btn.title = isCollapsed ? 'Expand folder tags' : 'Collapse folder tags'; folderTags.classList.toggle('collapsed');
// 保存状态到 localStorage if (folderTags.classList.contains('collapsed')) {
localStorage.setItem('folderTagsCollapsed', isCollapsed); 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 // Add this to your existing initialization code
@@ -128,10 +169,13 @@ export function initBackToTop() {
button.title = 'Back to top'; button.title = 'Back to top';
document.body.appendChild(button); document.body.appendChild(button);
// Get the scrollable container
const scrollContainer = document.querySelector('.page-content');
// Show/hide button based on scroll position // Show/hide button based on scroll position
const toggleBackToTop = () => { const toggleBackToTop = () => {
const scrollThreshold = window.innerHeight * 0.75; const scrollThreshold = window.innerHeight * 0.3;
if (window.scrollY > scrollThreshold) { if (scrollContainer.scrollTop > scrollThreshold) {
button.classList.add('visible'); button.classList.add('visible');
} else { } else {
button.classList.remove('visible'); button.classList.remove('visible');
@@ -140,14 +184,14 @@ export function initBackToTop() {
// Smooth scroll to top // Smooth scroll to top
button.addEventListener('click', () => { button.addEventListener('click', () => {
window.scrollTo({ scrollContainer.scrollTo({
top: 0, top: 0,
behavior: 'smooth' behavior: 'smooth'
}); });
}); });
// Listen for scroll events // Listen for scroll events on the scrollable container
window.addEventListener('scroll', toggleBackToTop); scrollContainer.addEventListener('scroll', toggleBackToTop);
// Initial check // Initial check
toggleBackToTop(); toggleBackToTop();