Add search and filter functionality to header; adjust styles for responsiveness

This commit is contained in:
Will Miao
2025-03-13 21:02:54 +08:00
parent 444e8004c7
commit 3620376c3c
5 changed files with 149 additions and 85 deletions

View File

@@ -23,6 +23,7 @@
.header-branding {
display: flex;
align-items: center;
flex-shrink: 0;
}
.logo-link {
@@ -48,6 +49,8 @@
.main-nav {
display: flex;
gap: 0.5rem;
flex-shrink: 0;
margin-right: 1rem;
}
.nav-item {
@@ -62,11 +65,28 @@
font-size: 0.9rem;
}
.nav-item:hover {
background-color: var(--lora-surface-hover, oklch(95% 0.02 256));
}
.nav-item.active {
background-color: var(--lora-accent);
color: white;
}
/* Header search */
.header-search {
flex: 1;
max-width: 400px;
margin: 0 1rem;
}
/* Header controls (formerly corner controls) */
.header-controls {
display: flex;
align-items: center;
gap: 8px;
flex-shrink: 0;
}
.header-controls > div {
@@ -120,4 +140,28 @@
width: 28px;
height: 28px;
}
.header-search {
max-width: none;
margin: 0 0.5rem;
}
.main-nav {
margin-right: 0.5rem;
}
}
/* For very small screens */
@media (max-width: 600px) {
.header-container {
padding: 0 8px;
}
.main-nav {
display: none; /* Hide navigation on very small screens */
}
.header-search {
flex: 1;
}
}

View File

@@ -1,9 +1,7 @@
/* Search Container Styles */
.search-container {
position: relative;
width: 250px;
margin-left: auto;
flex-shrink: 0; /* 防止搜索框被压缩 */
width: 100%;
display: flex;
align-items: center;
gap: 4px;
@@ -12,14 +10,14 @@
/* 调整搜索框样式以匹配其他控件 */
.search-container input {
width: 100%;
padding: 6px 75px 6px 12px; /* Increased right padding to accommodate both buttons */
border: 1px solid oklch(65% 0.02 256); /* 更深的边框颜色,提高对比度 */
padding: 6px 35px 6px 12px; /* Reduced right padding */
border: 1px solid oklch(65% 0.02 256);
border-radius: var(--border-radius-sm);
background: var(--lora-surface);
color: var(--text-color);
font-size: 0.9em;
height: 32px;
box-sizing: border-box; /* 确保padding不会增加总宽度 */
box-sizing: border-box;
}
.search-container input:focus {
@@ -34,7 +32,7 @@
transform: translateY(-50%);
color: oklch(var(--text-color) / 0.5);
pointer-events: none;
line-height: 1; /* 防止图标影响容器高度 */
line-height: 1;
}
/* 修改清空按钮样式 */
@@ -47,8 +45,8 @@
cursor: pointer;
border: none;
background: none;
padding: 4px 8px; /* 增加点击区域 */
display: none; /* 默认隐藏 */
padding: 4px 8px;
display: none;
line-height: 1;
transition: color 0.2s ease;
}
@@ -146,16 +144,17 @@
.filter-panel {
position: fixed;
right: 20px;
top: 50px; /* Position below header */
width: 320px;
background-color: var(--card-bg);
border: 1px solid var(--border-color);
border-radius: var(--border-radius-base);
box-shadow: 0 4px 10px rgba(0, 0, 0, 0.1);
z-index: var(--z-overlay); /* Increase z-index to be above cards */
z-index: var(--z-overlay);
padding: 16px;
transition: transform 0.3s ease, opacity 0.3s ease;
transform-origin: top right;
max-height: calc(100vh - 160px);
max-height: calc(100vh - 70px); /* Adjusted for header height */
overflow-y: auto;
}
@@ -352,6 +351,7 @@
.search-options-panel {
position: fixed;
right: 20px;
top: 50px; /* Position below header */
width: 280px;
background-color: var(--card-bg);
border: 1px solid var(--border-color);

View File

@@ -297,16 +297,32 @@ export class SearchManager {
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`;
// 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
searchOptionsPanel.style.right = `${window.innerWidth - searchRect.right}px`;
// Position the filter panel aligned with the filter button
const filterButton = document.getElementById('filterButton');
if (filterButton) {
const filterRect = filterButton.getBoundingClientRect();
filterPanel.style.right = `${window.innerWidth - filterRect.right}px`;
}
}
}
}

View File

@@ -33,73 +33,6 @@
<i class="fas fa-th-large"></i> Bulk
</button>
</div>
<div class="search-container">
<input type="text" id="searchInput" placeholder="Search models..." />
<!-- 清空按钮将由JavaScript动态添加到这里 -->
<i class="fas fa-search search-icon"></i>
<button class="search-options-toggle" id="searchOptionsToggle" title="Search Options">
<i class="fas fa-sliders-h"></i>
</button>
<button class="search-filter-toggle" id="filterButton" onclick="filterManager.toggleFilterPanel()" title="Filter models">
<i class="fas fa-filter"></i>
<span class="filter-badge" id="activeFiltersCount" style="display: none">0</span>
</button>
</div>
</div>
</div>
<!-- Add search options panel -->
<div id="searchOptionsPanel" class="search-options-panel hidden">
<div class="options-header">
<h3>Search Options</h3>
<button class="close-options-btn" id="closeSearchOptions">
<i class="fas fa-times"></i>
</button>
</div>
<div class="options-section">
<h4>Search In:</h4>
<div class="search-option-tags">
<div class="search-option-tag active" data-option="filename">Filename</div>
<div class="search-option-tag active" data-option="tags">Tags</div>
<div class="search-option-tag active" data-option="modelname">Model Name</div>
</div>
</div>
<div class="options-section">
<div class="search-option-switch">
<span>Include Subfolders</span>
<label class="switch">
<input type="checkbox" id="recursiveSearchToggle">
<span class="slider round"></span>
</label>
</div>
</div>
</div>
<!-- Add filter panel -->
<div id="filterPanel" class="filter-panel hidden">
<div class="filter-header">
<h3>Filter Models</h3>
<button class="close-filter-btn" onclick="filterManager.closeFilterPanel()">
<i class="fas fa-times"></i>
</button>
</div>
<div class="filter-section">
<h4>Base Model</h4>
<div class="filter-tags" id="baseModelTags">
<!-- Tags will be dynamically inserted here -->
</div>
</div>
<div class="filter-section">
<h4>Tags (Top 20)</h4>
<div class="filter-tags" id="modelTagsFilter">
<!-- Top tags will be dynamically inserted here -->
<div class="tags-loading">Loading tags...</div>
</div>
</div>
<div class="filter-actions">
<button class="clear-filters-btn" onclick="filterManager.clearFilters()">
Clear All Filters
</button>
</div>
</div>

View File

@@ -17,6 +17,22 @@
<i class="fas fa-check-circle"></i> Checkpoints
</a>
</nav>
<!-- Add search container to header -->
<div class="header-search">
<div class="search-container">
<input type="text" id="searchInput" placeholder="Search models..." />
<i class="fas fa-search search-icon"></i>
<button class="search-options-toggle" id="searchOptionsToggle" title="Search Options">
<i class="fas fa-sliders-h"></i>
</button>
<button class="search-filter-toggle" id="filterButton" onclick="filterManager.toggleFilterPanel()" title="Filter models">
<i class="fas fa-filter"></i>
<span class="filter-badge" id="activeFiltersCount" style="display: none">0</span>
</button>
</div>
</div>
<div class="header-actions">
<!-- Integrated corner controls -->
<div class="header-controls">
@@ -37,4 +53,59 @@
</div>
</div>
</div>
</header>
</header>
<!-- Add search options panel -->
<div id="searchOptionsPanel" class="search-options-panel hidden">
<div class="options-header">
<h3>Search Options</h3>
<button class="close-options-btn" id="closeSearchOptions">
<i class="fas fa-times"></i>
</button>
</div>
<div class="options-section">
<h4>Search In:</h4>
<div class="search-option-tags">
<div class="search-option-tag active" data-option="filename">Filename</div>
<div class="search-option-tag active" data-option="tags">Tags</div>
<div class="search-option-tag active" data-option="modelname">Model Name</div>
</div>
</div>
<div class="options-section">
<div class="search-option-switch">
<span>Include Subfolders</span>
<label class="switch">
<input type="checkbox" id="recursiveSearchToggle">
<span class="slider round"></span>
</label>
</div>
</div>
</div>
<!-- Add filter panel -->
<div id="filterPanel" class="filter-panel hidden">
<div class="filter-header">
<h3>Filter Models</h3>
<button class="close-filter-btn" onclick="filterManager.closeFilterPanel()">
<i class="fas fa-times"></i>
</button>
</div>
<div class="filter-section">
<h4>Base Model</h4>
<div class="filter-tags" id="baseModelTags">
<!-- Tags will be dynamically inserted here -->
</div>
</div>
<div class="filter-section">
<h4>Tags (Top 20)</h4>
<div class="filter-tags" id="modelTagsFilter">
<!-- Top tags will be dynamically inserted here -->
<div class="tags-loading">Loading tags...</div>
</div>
</div>
<div class="filter-actions">
<button class="clear-filters-btn" onclick="filterManager.clearFilters()">
Clear All Filters
</button>
</div>
</div>