mirror of
https://github.com/moku-project/Moku.git
synced 2026-06-13 09:19:56 -05:00
Feat: Always Display Library Stats & Library Stats Overhaul (#47)
This commit is contained in:
@@ -573,6 +573,7 @@
|
|||||||
{remainingCount}
|
{remainingCount}
|
||||||
renderLimit={store.settings.renderLimit ?? 48}
|
renderLimit={store.settings.renderLimit ?? 48}
|
||||||
cropCovers={store.settings.libraryCropCovers}
|
cropCovers={store.settings.libraryCropCovers}
|
||||||
|
statsAlways={store.settings.libraryStatsAlways ?? false}
|
||||||
libraryFilter={tab}
|
libraryFilter={tab}
|
||||||
onCardClick={onCardClick}
|
onCardClick={onCardClick}
|
||||||
onCardContextMenu={openCtx}
|
onCardContextMenu={openCtx}
|
||||||
|
|||||||
@@ -15,6 +15,7 @@
|
|||||||
remainingCount: number;
|
remainingCount: number;
|
||||||
renderLimit: number;
|
renderLimit: number;
|
||||||
cropCovers: boolean;
|
cropCovers: boolean;
|
||||||
|
statsAlways: boolean;
|
||||||
libraryFilter: string;
|
libraryFilter: string;
|
||||||
bulkWorking: boolean;
|
bulkWorking: boolean;
|
||||||
visibleCategories: Category[];
|
visibleCategories: Category[];
|
||||||
@@ -34,7 +35,7 @@
|
|||||||
|
|
||||||
let {
|
let {
|
||||||
visibleManga, filtered, loading, cols, anims, selectMode, selectedIds,
|
visibleManga, filtered, loading, cols, anims, selectMode, selectedIds,
|
||||||
hasMore, remainingCount, renderLimit, cropCovers, libraryFilter,
|
hasMore, remainingCount, renderLimit, cropCovers, statsAlways, libraryFilter,
|
||||||
bulkWorking, visibleCategories,
|
bulkWorking, visibleCategories,
|
||||||
onCardClick, onCardContextMenu, onCardPointerDown, onCardPointerUp, onCardPointerLeave,
|
onCardClick, onCardContextMenu, onCardPointerDown, onCardPointerUp, onCardPointerLeave,
|
||||||
onLoadMore, onRetry, onExitSelectMode, onSelectAll, onBulkMove, onBulkRemove, onBulkAutomate,
|
onLoadMore, onRetry, onExitSelectMode, onSelectAll, onBulkMove, onBulkRemove, onBulkAutomate,
|
||||||
@@ -128,23 +129,17 @@
|
|||||||
>
|
>
|
||||||
<div class="cover-wrap" class:completed={isCompleted}>
|
<div class="cover-wrap" class:completed={isCompleted}>
|
||||||
<Thumbnail src={m.thumbnailUrl} alt={m.title} class="cover" style="object-fit:{cropCovers ? 'cover' : 'contain'}" draggable="false" />
|
<Thumbnail src={m.thumbnailUrl} alt={m.title} class="cover" style="object-fit:{cropCovers ? 'cover' : 'contain'}" draggable="false" />
|
||||||
<div class="card-info-overlay" class:anim={anims} class:instant={!anims}>
|
<div class="card-info-overlay" class:anim={anims} class:instant={!anims} class:always={statsAlways}>
|
||||||
{#if isCompleted}
|
<div class="overlay-badges">
|
||||||
<span class="info-chip info-chip-done">✓ complete</span>
|
{#if isCompleted}
|
||||||
{:else if m.unreadCount}
|
<span class="badge badge-done">✓ Done</span>
|
||||||
<span class="info-chip info-chip-unread">
|
{:else if m.unreadCount}
|
||||||
<span class="info-chip-dot"></span>
|
<span class="badge badge-unread">{m.unreadCount} new</span>
|
||||||
{m.unreadCount} unread
|
{/if}
|
||||||
</span>
|
{#if m.downloadCount}
|
||||||
{:else}
|
<span class="badge badge-dl">↓ {m.downloadCount}</span>
|
||||||
<span></span>
|
{/if}
|
||||||
{/if}
|
</div>
|
||||||
{#if m.downloadCount}
|
|
||||||
<span class="info-chip info-chip-dl">
|
|
||||||
<span class="info-chip-dot"></span>
|
|
||||||
{m.downloadCount}
|
|
||||||
</span>
|
|
||||||
{/if}
|
|
||||||
</div>
|
</div>
|
||||||
{#if selectMode}
|
{#if selectMode}
|
||||||
<div class="select-overlay" aria-hidden="true">
|
<div class="select-overlay" aria-hidden="true">
|
||||||
@@ -200,15 +195,16 @@
|
|||||||
.card.anims .cover-wrap { transition: transform 0.18s cubic-bezier(0.16,1,0.3,1), border-color var(--t-base), box-shadow 0.18s cubic-bezier(0.16,1,0.3,1); }
|
.card.anims .cover-wrap { transition: transform 0.18s cubic-bezier(0.16,1,0.3,1), border-color var(--t-base), box-shadow 0.18s cubic-bezier(0.16,1,0.3,1); }
|
||||||
.cover-wrap.completed { box-shadow: inset 0 -2px 0 0 var(--accent); }
|
.cover-wrap.completed { box-shadow: inset 0 -2px 0 0 var(--accent); }
|
||||||
.card.anims .cover { transition: filter var(--t-base); }
|
.card.anims .cover { transition: filter var(--t-base); }
|
||||||
.card-info-overlay { position: absolute; bottom: 0; left: 0; right: 0; display: flex; align-items: flex-end; justify-content: space-between; padding: 20px 5px 5px; background: linear-gradient(to top, rgba(0,0,0,0.72) 0%, rgba(0,0,0,0.3) 55%, transparent 100%); opacity: 0; transform: translateY(3px); pointer-events: none; }
|
.card-info-overlay { position: absolute; bottom: -4px; left: 0; right: 0; padding: 32px 6px 10px; background: linear-gradient(to top, rgba(0,0,0,0.88) 0%, rgba(0,0,0,0.5) 50%, transparent 100%); opacity: 0; pointer-events: none; }
|
||||||
.card-info-overlay.anim { transition: opacity 0.18s ease, transform 0.18s cubic-bezier(0.16,1,0.3,1); }
|
.card-info-overlay.anim { transition: opacity 0.18s ease; }
|
||||||
.card-info-overlay.instant { transition: none; }
|
.card-info-overlay.instant { transition: none; }
|
||||||
.card:not(.select-mode):hover .card-info-overlay { opacity: 1; transform: translateY(0); }
|
.card-info-overlay.always { opacity: 1; }
|
||||||
.info-chip { display: flex; align-items: center; gap: 4px; font-size: 10px; font-weight: 700; letter-spacing: 0.03em; line-height: 1; padding: 3px 6px; border-radius: 4px; background: rgba(0,0,0,0.52); backdrop-filter: blur(6px); }
|
.card:not(.select-mode):hover .card-info-overlay { opacity: 1; }
|
||||||
.info-chip-unread { color: #fff; }
|
.overlay-badges { display: flex; align-items: flex-end; justify-content: space-between; gap: 4px; flex-wrap: wrap; }
|
||||||
.info-chip-done { color: var(--accent-fg); font-size: 9px; letter-spacing: 0.06em; text-transform: uppercase; }
|
.badge { font-family: var(--font-ui); font-size: 9.5px; font-weight: 700; letter-spacing: 0.04em; line-height: 1; padding: 3px 7px; border-radius: 20px; white-space: nowrap; }
|
||||||
.info-chip-dl { color: var(--accent-fg); }
|
.badge-unread { background: var(--accent); color: #fff; box-shadow: 0 1px 8px rgba(0,0,0,0.5); }
|
||||||
.info-chip-dot { width: 4px; height: 4px; border-radius: 50%; background: currentColor; flex-shrink: 0; }
|
.badge-done { background: rgba(255,255,255,0.18); color: rgba(255,255,255,0.9); border: 1px solid rgba(255,255,255,0.25); }
|
||||||
|
.badge-dl { background: rgba(0,0,0,0.55); color: rgba(255,255,255,0.8); border: 1px solid rgba(255,255,255,0.18); margin-left: auto; }
|
||||||
.select-overlay { position: absolute; inset: 0; background: rgba(0,0,0,0.18); display: flex; align-items: flex-start; justify-content: flex-end; padding: 6px; pointer-events: none; }
|
.select-overlay { position: absolute; inset: 0; background: rgba(0,0,0,0.18); display: flex; align-items: flex-start; justify-content: flex-end; padding: 6px; pointer-events: none; }
|
||||||
.select-check { color: var(--text-faint); opacity: 0.7; transition: color var(--t-base), opacity var(--t-base); }
|
.select-check { color: var(--text-faint); opacity: 0.7; transition: color var(--t-base), opacity var(--t-base); }
|
||||||
.select-check.checked { color: var(--accent-fg); opacity: 1; }
|
.select-check.checked { color: var(--accent-fg); opacity: 1; }
|
||||||
|
|||||||
@@ -19,6 +19,10 @@
|
|||||||
<div class="s-section">
|
<div class="s-section">
|
||||||
<p class="s-section-title">Display</p>
|
<p class="s-section-title">Display</p>
|
||||||
<div class="s-section-body">
|
<div class="s-section-body">
|
||||||
|
<label class="s-row">
|
||||||
|
<div class="s-row-info"><span class="s-label">Always show card stats</span><span class="s-desc">Show unread and download counts without needing to hover</span></div>
|
||||||
|
<button role="switch" aria-checked={store.settings.libraryStatsAlways ?? false} aria-label="Always show card stats" class="s-toggle" class:on={store.settings.libraryStatsAlways ?? false} onclick={() => updateSettings({ libraryStatsAlways: !(store.settings.libraryStatsAlways ?? false) })}><span class="s-toggle-thumb"></span></button>
|
||||||
|
</label>
|
||||||
<label class="s-row">
|
<label class="s-row">
|
||||||
<div class="s-row-info"><span class="s-label">Crop cover images</span><span class="s-desc">Fills the card with the cover art instead of letterboxing</span></div>
|
<div class="s-row-info"><span class="s-label">Crop cover images</span><span class="s-desc">Fills the card with the cover art instead of letterboxing</span></div>
|
||||||
<button role="switch" aria-checked={store.settings.libraryCropCovers} aria-label="Crop cover images" class="s-toggle" class:on={store.settings.libraryCropCovers} onclick={() => updateSettings({ libraryCropCovers: !store.settings.libraryCropCovers })}><span class="s-toggle-thumb"></span></button>
|
<button role="switch" aria-checked={store.settings.libraryCropCovers} aria-label="Crop cover images" class="s-toggle" class:on={store.settings.libraryCropCovers} onclick={() => updateSettings({ libraryCropCovers: !store.settings.libraryCropCovers })}><span class="s-toggle-thumb"></span></button>
|
||||||
|
|||||||
Reference in New Issue
Block a user