mirror of
https://github.com/willmiao/ComfyUI-Lora-Manager.git
synced 2026-03-21 21:22:11 -03:00
Fix layout
This commit is contained in:
@@ -1,11 +0,0 @@
|
||||
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<!-- ...existing code... -->
|
||||
</head>
|
||||
<body>
|
||||
{% include 'components/header.html' %}
|
||||
<!-- ...existing code... -->
|
||||
</body>
|
||||
</html>
|
||||
@@ -1,10 +0,0 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<!-- ...existing code... -->
|
||||
</head>
|
||||
<body>
|
||||
{% include 'components/header.html' %}
|
||||
<!-- ...existing code... -->
|
||||
</body>
|
||||
</html>
|
||||
@@ -1,10 +0,0 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<!-- ...existing code... -->
|
||||
</head>
|
||||
<body>
|
||||
{% include 'components/header.html' %}
|
||||
<!-- ...existing code... -->
|
||||
</body>
|
||||
</html>
|
||||
@@ -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);
|
||||
|
||||
@@ -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 */
|
||||
}
|
||||
}
|
||||
@@ -4,6 +4,7 @@
|
||||
overflow-y: auto; /* Enable scrolling here */
|
||||
width: 100%;
|
||||
position: relative;
|
||||
overflow-y: scroll;
|
||||
}
|
||||
|
||||
.container {
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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() {
|
||||
|
||||
@@ -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 }
|
||||
|
||||
@@ -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`;
|
||||
}
|
||||
}
|
||||
@@ -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();
|
||||
|
||||
Reference in New Issue
Block a user