From c0480fe028e254a19f88091bdfc0462d42aad82d Mon Sep 17 00:00:00 2001 From: Estrella Pan Date: Mon, 26 Jan 2026 20:59:28 +0100 Subject: [PATCH] feat(search): redesign search panel with chip cloud filters MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Add filter section with group, resolution, subtitle, season categories - Implement multi-select filters with smart disabling for incompatible options - Display result variant tags as non-clickable colored pills - Unify tag styling (pill shape, 12px font) across filters and results - Add expand/collapse for filter categories and variant overflow - Normalize tag values (resolution: FHD/HD/4K, subtitle: 简/繁/双语) - Calculate poster height to match 4 rows of variant chips (168px) - Add click-outside-to-close for modal Co-Authored-By: Claude Opus 4.5 --- .../src/components/search/ab-search-modal.vue | 1030 +++++++++++++---- webui/src/i18n/en.json | 3 +- webui/src/i18n/zh-CN.json | 3 +- 3 files changed, 836 insertions(+), 200 deletions(-) diff --git a/webui/src/components/search/ab-search-modal.vue b/webui/src/components/search/ab-search-modal.vue index c1685518..09227703 100644 --- a/webui/src/components/search/ab-search-modal.vue +++ b/webui/src/components/search/ab-search-modal.vue @@ -1,5 +1,5 @@ @@ -187,7 +536,13 @@ function clearFilters() { - - +
- {{ variant.group_name }} - {{ getResolution(variant) }} - {{ getSubtitle(variant) }} - {{ getSeason(variant) }} + {{ variant.group_name || 'Unknown' }} + + {{ getResolution(variant) }} + + + {{ getSubtitle(variant) }} + + + {{ getSeason(variant) }} +
+
@@ -370,7 +844,7 @@ function clearFilters() { .modal-content { width: 100%; max-width: 1100px; - max-height: calc(100dvh - 100px); // Use dynamic viewport height for iOS Safari keyboard support + max-height: calc(100dvh - 100px); display: flex; flex-direction: column; background: var(--color-surface); @@ -379,7 +853,6 @@ function clearFilters() { overflow: hidden; transition: background-color var(--transition-normal); - // Fallback for browsers that don't support dvh @supports not (max-height: 1dvh) { max-height: calc(100vh - 100px); } @@ -411,7 +884,7 @@ function clearFilters() { .search-input-wrapper { flex: 1; - min-width: 0; // Allow shrinking + min-width: 0; display: flex; align-items: center; gap: 8px; @@ -539,30 +1012,245 @@ function clearFilters() { } } -.close-btn { +// Filter Section - Chip Cloud +.filter-section { + padding: 14px 16px; + border-bottom: 1px solid var(--color-border); + background: var(--color-surface-hover); + flex-shrink: 0; + transition: background-color var(--transition-normal), border-color var(--transition-normal); +} + +.filter-category { + display: flex; + align-items: flex-start; + gap: 10px; + margin-bottom: 10px; + + &:last-of-type { + margin-bottom: 0; + } +} + +.category-icon { + width: 24px; + height: 28px; display: flex; align-items: center; justify-content: center; - width: 36px; - height: 36px; - background: var(--color-surface-hover); - border: 1px solid var(--color-border); - border-radius: var(--radius-md); - cursor: pointer; color: var(--color-text-muted); flex-shrink: 0; +} + +.filter-chips { + display: flex; + flex-wrap: wrap; + gap: 8px; + align-items: center; +} + +.filter-chip { + height: 28px; + padding: 0 12px; + font-size: 12px; + font-weight: 500; + font-family: inherit; + border-radius: var(--radius-full); + border: 1px solid transparent; + cursor: pointer; + user-select: none; transition: all var(--transition-fast); - @include forTablet { - width: 44px; - height: 44px; + &:focus-visible { + outline: 2px solid var(--color-primary); + outline-offset: 2px; } +} - &:hover { - background: var(--color-danger); - border-color: var(--color-danger); +// Group chips - Blue/Primary +.chip-group { + background: var(--color-primary-light); + color: var(--color-primary); + + &:hover:not(.disabled), + &.active { + background: var(--color-primary); color: #fff; } + + &.disabled { + background: var(--color-surface-hover); + color: var(--color-text-muted); + opacity: 0.5; + cursor: not-allowed; + } +} + +// Resolution chips - Green +.chip-resolution { + background: rgba(34, 197, 94, 0.15); + color: var(--color-success); + + &:hover:not(.disabled), + &.active { + background: var(--color-success); + color: #fff; + } + + &.disabled { + background: var(--color-surface-hover); + color: var(--color-text-muted); + opacity: 0.5; + cursor: not-allowed; + } +} + +// Subtitle chips - Orange +.chip-subtitle { + background: rgba(249, 115, 22, 0.15); + color: var(--color-accent); + + &:hover:not(.disabled), + &.active { + background: var(--color-accent); + color: #fff; + } + + &.disabled { + background: var(--color-surface-hover); + color: var(--color-text-muted); + opacity: 0.5; + cursor: not-allowed; + } +} + +// Season chips - Purple +.chip-season { + background: rgba(139, 92, 246, 0.15); + color: rgb(139, 92, 246); + + &:hover:not(.disabled), + &.active { + background: rgb(139, 92, 246); + color: #fff; + } + + &.disabled { + background: var(--color-surface-hover); + color: var(--color-text-muted); + opacity: 0.5; + cursor: not-allowed; + } +} + +.expand-btn { + height: 28px; + padding: 0 10px; + font-size: 12px; + font-weight: 500; + font-family: inherit; + color: var(--color-text-secondary); + background: transparent; + border: 1px dashed var(--color-border); + border-radius: var(--radius-full); + cursor: pointer; + transition: all var(--transition-fast); + + &:hover { + color: var(--color-primary); + border-color: var(--color-primary); + background: var(--color-primary-light); + } +} + +// Filter Summary +.filter-summary { + display: flex; + align-items: center; + justify-content: space-between; + margin-top: 12px; + padding-top: 12px; + border-top: 1px solid var(--color-border); + flex-wrap: wrap; + gap: 8px; +} + +.selected-filters { + display: flex; + align-items: center; + gap: 8px; + flex-wrap: wrap; + flex: 1; + min-width: 0; +} + +.selected-label { + font-size: 12px; + color: var(--color-text-muted); + flex-shrink: 0; +} + +.selected-chips { + display: flex; + flex-wrap: wrap; + gap: 6px; +} + +.selected-chip { + height: 24px; + padding: 0 8px; + font-size: 11px; + font-weight: 500; + border-radius: var(--radius-sm); + display: inline-flex; + align-items: center; + cursor: pointer; + color: #fff; + transition: opacity var(--transition-fast); + + &:hover { + opacity: 0.8; + } + + &.chip-group { + background: var(--color-primary); + } + + &.chip-resolution { + background: var(--color-success); + } + + &.chip-subtitle { + background: var(--color-accent); + } + + &.chip-season { + background: rgb(139, 92, 246); + } +} + +.clear-all-btn { + padding: 4px 10px; + font-size: 12px; + font-family: inherit; + color: var(--color-text-muted); + background: transparent; + border: 1px solid var(--color-border); + border-radius: var(--radius-sm); + cursor: pointer; + transition: all var(--transition-fast); + flex-shrink: 0; + + &:hover { + border-color: var(--color-danger); + color: var(--color-danger); + } +} + +.results-count { + font-size: 12px; + color: var(--color-text-muted); + flex-shrink: 0; } // Results @@ -600,64 +1288,65 @@ function clearFilters() { transform: scale(0.95) translateY(-10px); } -// Bangumi list +// Bangumi list - Compact .bangumi-list { display: flex; flex-direction: column; - gap: 16px; + gap: 12px; } .bangumi-row { display: flex; - gap: 16px; - padding: 16px; + gap: 12px; + padding: 12px; border: 1px solid var(--color-border); - border-radius: var(--radius-lg); + border-radius: var(--radius-md); background: var(--color-surface); + transition: border-color var(--transition-fast); } .bangumi-poster { - width: 100px; + // Height = 4 rows: 4 * 36px + 3 * 8px = 168px + // Width = 168px * 5/7 = 120px + width: 120px; + height: 168px; flex-shrink: 0; - @include forDesktop { - width: 120px; - } - img { width: 100%; - aspect-ratio: 5 / 7; + height: 100%; object-fit: cover; - border-radius: var(--radius-md); + border-radius: var(--radius-sm); background: var(--color-surface-hover); } } .bangumi-poster-placeholder { width: 100%; - aspect-ratio: 5 / 7; + height: 100%; display: flex; align-items: center; justify-content: center; - padding: 8px; - border-radius: var(--radius-md); + padding: 6px; + border-radius: var(--radius-sm); background: var(--color-surface-hover); border: 1px solid var(--color-border); } .placeholder-title { - font-size: 11px; + font-size: 10px; font-weight: 500; color: var(--color-text-muted); text-align: center; overflow: hidden; text-overflow: ellipsis; display: -webkit-box; - -webkit-line-clamp: 4; + -webkit-line-clamp: 3; -webkit-box-orient: vertical; line-height: 1.3; } +// Variant chips - flex wrap flow layout .bangumi-variants { flex: 1; min-width: 0; @@ -670,12 +1359,12 @@ function clearFilters() { .variant-chip { display: flex; align-items: center; - gap: 6px; - height: 32px; - padding: 0 10px; + gap: 8px; + height: 36px; + padding: 0 6px; background: var(--color-surface-hover); border: 1px solid var(--color-border); - border-radius: var(--radius-md); + border-radius: var(--radius-full); cursor: pointer; transition: all var(--transition-fast); @@ -683,118 +1372,63 @@ function clearFilters() { border-color: var(--color-primary); background: var(--color-primary); - .chip-tag { + .tag { background: rgba(255, 255, 255, 0.2); color: #fff; } } } -.chip-tag { +// Display-only tags (non-clickable) - unified with filter chips +.tag { display: inline-flex; align-items: center; - height: 20px; - padding: 0 6px; - font-size: 11px; + height: 24px; + padding: 0 10px; + font-size: 12px; font-weight: 500; - border-radius: var(--radius-sm); - white-space: nowrap; + border-radius: var(--radius-full); + pointer-events: none; transition: all var(--transition-fast); } -.chip-tag-group { +.tag-group { background: var(--color-primary-light); color: var(--color-primary); } -.chip-tag-res { +.tag-res { background: rgba(34, 197, 94, 0.15); color: var(--color-success); } -.chip-tag-sub { +.tag-sub { background: rgba(249, 115, 22, 0.15); color: var(--color-accent); } -.chip-tag-season { +.tag-season { background: rgba(139, 92, 246, 0.15); color: rgb(139, 92, 246); } -// Active filters bar -.active-filters { - display: flex; - align-items: center; - gap: 8px; - padding: 10px 16px; - background: var(--color-surface-hover); - border-bottom: 1px solid var(--color-border); - flex-wrap: wrap; -} - -.filter-label { +.variant-expand-btn { + height: 36px; + padding: 0 14px; font-size: 12px; font-weight: 500; - color: var(--color-text-secondary); -} - -.filter-tag { - display: inline-flex; - align-items: center; - height: 24px; - padding: 0 8px; - font-size: 12px; - font-weight: 500; - border-radius: var(--radius-sm); - cursor: pointer; - transition: opacity var(--transition-fast); - - &:hover { - opacity: 0.8; - } -} - -.filter-tag-group { - background: var(--color-primary); - color: #fff; -} - -.filter-tag-res { - background: var(--color-success); - color: #fff; -} - -.filter-tag-sub { - background: var(--color-accent); - color: #fff; -} - -.filter-tag-season { - background: rgb(139, 92, 246); - color: #fff; -} - -.clear-filters-btn { - margin-left: auto; - padding: 4px 10px; - font-size: 12px; font-family: inherit; - color: var(--color-text-muted); + color: var(--color-text-secondary); background: transparent; - border: 1px solid var(--color-border); - border-radius: var(--radius-sm); + border: 1px dashed var(--color-border); + border-radius: var(--radius-full); cursor: pointer; transition: all var(--transition-fast); &:hover { - border-color: var(--color-danger); - color: var(--color-danger); + color: var(--color-primary); + border-color: var(--color-primary); + background: var(--color-primary-light); } } - -// Active tag highlight -.chip-tag.active { - box-shadow: 0 0 0 2px var(--color-primary); -} diff --git a/webui/src/i18n/en.json b/webui/src/i18n/en.json index efc5af09..8c9c1897 100644 --- a/webui/src/i18n/en.json +++ b/webui/src/i18n/en.json @@ -395,7 +395,8 @@ "season": "Season", "clear": "Clear", "results": "results", - "active": "Filtering" + "active": "Filtering", + "collapse": "Less" }, "confirm": { "title": "Add Subscription", diff --git a/webui/src/i18n/zh-CN.json b/webui/src/i18n/zh-CN.json index f2cec9e5..3937adff 100644 --- a/webui/src/i18n/zh-CN.json +++ b/webui/src/i18n/zh-CN.json @@ -395,7 +395,8 @@ "season": "季度", "clear": "清除筛选", "results": "个结果", - "active": "筛选中" + "active": "筛选中", + "collapse": "收起" }, "confirm": { "title": "添加订阅",