From 2f099325e4af87af500725ecf2914fdc6ab3b571 Mon Sep 17 00:00:00 2001
From: Will Miao <13051207myq@gmail.com>
Date: Wed, 19 Feb 2025 12:53:09 +0800
Subject: [PATCH] Refactor showcase checkpoint
---
static/css/components/modal.css | 43 ++++++++++++++++++++++
static/js/components/LoraCard.js | 61 +++++++++++++++++++++++++++++---
static/js/main.js | 4 +--
3 files changed, 102 insertions(+), 6 deletions(-)
diff --git a/static/css/components/modal.css b/static/css/components/modal.css
index 2420a73f..e4967f78 100644
--- a/static/css/components/modal.css
+++ b/static/css/components/modal.css
@@ -666,10 +666,17 @@ body.modal-open {
justify-content: center;
gap: 8px;
margin-bottom: var(--space-2);
+ transition: background-color 0.2s, transform 0.2s;
}
.scroll-indicator:hover {
background: oklch(var(--lora-accent) / 0.1);
+ transform: translateY(-1px);
+}
+
+.scroll-indicator span {
+ font-size: 0.9em;
+ color: var(--text-color);
}
.lazy {
@@ -793,4 +800,40 @@ body.modal-open {
.info-item.notes {
grid-column: 1 / -1;
}
+}
+
+/* 修改 back-to-top 按钮样式,使其固定在 modal 内部 */
+.modal-content .back-to-top {
+ position: sticky; /* 改用 sticky 定位 */
+ float: right; /* 使用 float 确保按钮在右侧 */
+ bottom: 20px; /* 距离底部的距离 */
+ margin-right: 20px; /* 右侧间距 */
+ margin-top: -56px; /* 负边距确保不占用额外空间 */
+ width: 36px;
+ height: 36px;
+ border-radius: 50%;
+ background: var(--card-bg);
+ border: 1px solid var(--border-color);
+ color: var(--text-color);
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ cursor: pointer;
+ opacity: 0;
+ visibility: hidden;
+ transform: translateY(10px);
+ transition: all 0.3s ease;
+ z-index: 10;
+}
+
+.modal-content .back-to-top.visible {
+ opacity: 1;
+ visibility: visible;
+ transform: translateY(0);
+}
+
+.modal-content .back-to-top:hover {
+ background: var(--lora-accent);
+ color: white;
+ transform: translateY(-2px);
}
\ No newline at end of file
diff --git a/static/js/components/LoraCard.js b/static/js/components/LoraCard.js
index 99a35660..8229e6b1 100644
--- a/static/js/components/LoraCard.js
+++ b/static/js/components/LoraCard.js
@@ -178,6 +178,7 @@ export function showLoraModal(lora) {
modalManager.showModal('loraModal', content);
setupEditableFields();
+ setupShowcaseScroll(); // Add this line
}
function setupEditableFields() {
@@ -271,7 +272,7 @@ function renderShowcaseImages(images) {
- Show ${images.length} examples
+ Scroll or click to show ${images.length} examples
@@ -315,6 +316,9 @@ function renderShowcaseImages(images) {
}).join('')}
+
`;
}
@@ -329,18 +333,19 @@ export function toggleShowcase(element) {
carousel.classList.toggle('collapsed');
if (isCollapsed) {
- indicator.textContent = 'Hide examples';
+ const count = carousel.querySelectorAll('.media-wrapper').length;
+ indicator.textContent = `Scroll or click to hide examples`;
icon.classList.replace('fa-chevron-down', 'fa-chevron-up');
initLazyLoading(carousel);
} else {
const count = carousel.querySelectorAll('.media-wrapper').length;
- indicator.textContent = `Show ${count} examples`;
+ indicator.textContent = `Scroll or click to show ${count} examples`;
icon.classList.replace('fa-chevron-up', 'fa-chevron-down');
}
};
// Add lazy loading initialization
-export function initLazyLoading(container) {
+function initLazyLoading(container) {
const lazyElements = container.querySelectorAll('.lazy');
const lazyLoad = (element) => {
@@ -364,4 +369,52 @@ export function initLazyLoading(container) {
});
lazyElements.forEach(element => observer.observe(element));
+}
+
+export function setupShowcaseScroll() {
+ // Change from modal-content to window/document level
+ document.addEventListener('wheel', (event) => {
+ const modalContent = document.querySelector('.modal-content');
+ if (!modalContent) return;
+
+ const showcase = modalContent.querySelector('.showcase-section');
+ if (!showcase) return;
+
+ const carousel = showcase.querySelector('.carousel');
+ const scrollIndicator = showcase.querySelector('.scroll-indicator');
+
+ if (carousel?.classList.contains('collapsed') && event.deltaY > 0) {
+ const isNearBottom = modalContent.scrollHeight - modalContent.scrollTop - modalContent.clientHeight < 100;
+
+ if (isNearBottom) {
+ toggleShowcase(scrollIndicator);
+ event.preventDefault();
+ }
+ }
+ });
+
+ // Keep the existing scroll tracking code
+ const modalContent = document.querySelector('.modal-content');
+ if (modalContent) {
+ modalContent.addEventListener('scroll', () => {
+ const backToTopBtn = modalContent.querySelector('.back-to-top');
+ if (backToTopBtn) {
+ if (modalContent.scrollTop > 300) {
+ backToTopBtn.classList.add('visible');
+ } else {
+ backToTopBtn.classList.remove('visible');
+ }
+ }
+ });
+ }
+}
+
+export function scrollToTop(button) {
+ const modalContent = button.closest('.modal-content');
+ if (modalContent) {
+ modalContent.scrollTo({
+ top: 0,
+ behavior: 'smooth'
+ });
+ }
}
\ No newline at end of file
diff --git a/static/js/main.js b/static/js/main.js
index bbc2ab9b..5475890b 100644
--- a/static/js/main.js
+++ b/static/js/main.js
@@ -2,7 +2,7 @@ import { debounce } from './utils/debounce.js';
import { LoadingManager } from './managers/LoadingManager.js';
import { modalManager } from './managers/ModalManager.js';
import { state } from './state/index.js';
-import { showLoraModal, toggleShowcase, initLazyLoading } from './components/LoraCard.js';
+import { showLoraModal, toggleShowcase, scrollToTop } from './components/LoraCard.js';
import { loadMoreLoras, fetchCivitai, deleteModel, replacePreview, resetAndReload, refreshLoras } from './api/loraApi.js';
import {
showToast,
@@ -46,6 +46,7 @@ window.settingsManager = new SettingsManager();
window.toggleApiKeyVisibility = toggleApiKeyVisibility;
window.moveManager = moveManager;
window.toggleShowcase = toggleShowcase;
+window.scrollToTop = scrollToTop;
// Initialize everything when DOM is ready
document.addEventListener('DOMContentLoaded', () => {
@@ -59,7 +60,6 @@ document.addEventListener('DOMContentLoaded', () => {
initTheme();
initFolderTagsVisibility();
initBackToTop();
- initLazyLoading();
window.searchManager = new SearchManager();
new LoraContextMenu();
});