mirror of
https://github.com/moku-project/Moku.git
synced 2026-06-13 17:29:55 -05:00
Fix: Library Mappings
This commit is contained in:
@@ -393,7 +393,7 @@
|
|||||||
{:else}
|
{:else}
|
||||||
<LibraryToolbar
|
<LibraryToolbar
|
||||||
tab={libraryState.tab}
|
tab={libraryState.tab}
|
||||||
tabSortMode={libraryState.tabSort[libraryState.tab]?.mode ?? 'alphabetical'}
|
tabSortMode={libraryState.tabSort[libraryState.tab]?.mode ?? 'az'}
|
||||||
tabSortDir={libraryState.tabSort[libraryState.tab]?.dir ?? 'asc'}
|
tabSortDir={libraryState.tabSort[libraryState.tab]?.dir ?? 'asc'}
|
||||||
tabStatus={libraryState.tabStatus[libraryState.tab] ?? 'ALL'}
|
tabStatus={libraryState.tabStatus[libraryState.tab] ?? 'ALL'}
|
||||||
tabFilters={libraryState.tabFilters[libraryState.tab] ?? {}}
|
tabFilters={libraryState.tabFilters[libraryState.tab] ?? {}}
|
||||||
@@ -423,7 +423,6 @@
|
|||||||
onFilterPanelToggle={() => filterPanelOpen = !filterPanelOpen}
|
onFilterPanelToggle={() => filterPanelOpen = !filterPanelOpen}
|
||||||
onRefresh={startRefresh}
|
onRefresh={startRefresh}
|
||||||
onCancelRefresh={cancelRefresh}
|
onCancelRefresh={cancelRefresh}
|
||||||
onRefreshCategory={refreshCategory}
|
|
||||||
onOpenDownloadsFolder={openDownloadsFolder}
|
onOpenDownloadsFolder={openDownloadsFolder}
|
||||||
onTabDragStart={onTabDragStart}
|
onTabDragStart={onTabDragStart}
|
||||||
onTabDragOver={onTabDragOver}
|
onTabDragOver={onTabDragOver}
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { CheckSquare, Trash, Folder } from 'phosphor-svelte'
|
import { CheckSquare, Trash, Folder } from 'phosphor-svelte'
|
||||||
import Thumbnail from '$lib/components/shared/manga/Thumbnail.svelte'
|
import Thumbnail from '$lib/components/shared/manga/Thumbnail.svelte'
|
||||||
|
import { settingsState } from '$lib/state/settings.svelte'
|
||||||
import type { Manga, Category } from '$lib/types'
|
import type { Manga, Category } from '$lib/types'
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
@@ -27,6 +28,9 @@
|
|||||||
|
|
||||||
let movePanelOpen = $state(false)
|
let movePanelOpen = $state(false)
|
||||||
|
|
||||||
|
const statsAlways = $derived(settingsState.settings.libraryStatsAlways ?? false)
|
||||||
|
const cropCovers = $derived(settingsState.settings.libraryCropCovers ?? true)
|
||||||
|
|
||||||
function onDocDown(e: MouseEvent) {
|
function onDocDown(e: MouseEvent) {
|
||||||
if (movePanelOpen && !(e.target as HTMLElement).closest('.move-wrap')) movePanelOpen = false
|
if (movePanelOpen && !(e.target as HTMLElement).closest('.move-wrap')) movePanelOpen = false
|
||||||
}
|
}
|
||||||
@@ -114,10 +118,11 @@
|
|||||||
class="card"
|
class="card"
|
||||||
class:card-selected={isSelected}
|
class:card-selected={isSelected}
|
||||||
class:select-mode={selectMode}
|
class:select-mode={selectMode}
|
||||||
|
class:stats-always={statsAlways}
|
||||||
onclick={(e) => onCardClick(e, m)}
|
onclick={(e) => onCardClick(e, m)}
|
||||||
oncontextmenu={(e) => onCardContextMenu(e, m)}
|
oncontextmenu={(e) => onCardContextMenu(e, m)}
|
||||||
>
|
>
|
||||||
<div class="cover-wrap" class:completed={isCompleted}>
|
<div class="cover-wrap" class:completed={isCompleted} class:cover-contain={!cropCovers}>
|
||||||
<Thumbnail src={m.thumbnailUrl} alt={m.title} class="cover" id={m.id} />
|
<Thumbnail src={m.thumbnailUrl} alt={m.title} class="cover" id={m.id} />
|
||||||
<div class="overlay">
|
<div class="overlay">
|
||||||
<div class="badges">
|
<div class="badges">
|
||||||
@@ -236,6 +241,7 @@
|
|||||||
.cover-wrap.completed { box-shadow: inset 0 -2px 0 0 var(--accent); }
|
.cover-wrap.completed { box-shadow: inset 0 -2px 0 0 var(--accent); }
|
||||||
|
|
||||||
:global(.cover) { width: 100%; height: 100%; object-fit: cover; display: block; }
|
:global(.cover) { width: 100%; height: 100%; object-fit: cover; display: block; }
|
||||||
|
.cover-contain :global(.cover) { object-fit: contain; }
|
||||||
|
|
||||||
.overlay {
|
.overlay {
|
||||||
position: absolute; bottom: 0; left: 0; right: 0; z-index: 2;
|
position: absolute; bottom: 0; left: 0; right: 0; z-index: 2;
|
||||||
@@ -245,6 +251,7 @@
|
|||||||
transition: opacity 0.18s ease;
|
transition: opacity 0.18s ease;
|
||||||
}
|
}
|
||||||
.card:not(.select-mode):hover .overlay { opacity: 1; }
|
.card:not(.select-mode):hover .overlay { opacity: 1; }
|
||||||
|
.stats-always .overlay { opacity: 1; }
|
||||||
|
|
||||||
.badges { display: flex; align-items: flex-end; justify-content: space-between; gap: 4px; flex-wrap: wrap; }
|
.badges { display: flex; align-items: flex-end; justify-content: space-between; gap: 4px; flex-wrap: wrap; }
|
||||||
.badge {
|
.badge {
|
||||||
|
|||||||
@@ -4,21 +4,19 @@
|
|||||||
SortAscending, CaretUp, CaretDown, ArrowsClockwise, Star, X, CheckSquare,
|
SortAscending, CaretUp, CaretDown, ArrowsClockwise, Star, X, CheckSquare,
|
||||||
} from "phosphor-svelte";
|
} from "phosphor-svelte";
|
||||||
import LibraryFilters from "./LibraryFilters.svelte";
|
import LibraryFilters from "./LibraryFilters.svelte";
|
||||||
import type { Category } from "@types";
|
import type { Category } from "$lib/types";
|
||||||
import type { LibrarySortMode, LibrarySortDir, LibraryStatusFilter, LibraryContentFilter } from "@store/state.svelte";
|
import type { LibrarySortOption, LibrarySortDir, LibraryStatusFilter, LibraryContentFilter } from "$lib/state/library.svelte";
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
tab: string;
|
tab: string;
|
||||||
tabSortMode: LibrarySortMode;
|
tabSortMode: LibrarySortOption;
|
||||||
tabSortDir: LibrarySortDir;
|
tabSortDir: LibrarySortDir;
|
||||||
tabStatus: LibraryStatusFilter;
|
tabStatus: LibraryStatusFilter;
|
||||||
tabFilters: Partial<Record<LibraryContentFilter, boolean>>;
|
tabFilters: Partial<Record<LibraryContentFilter, boolean>>;
|
||||||
hasActiveFilters: boolean;
|
hasActiveFilters: boolean;
|
||||||
anims: boolean;
|
anims?: boolean;
|
||||||
visibleCategories: Category[];
|
visibleCategories: Category[];
|
||||||
visibleTabIds: string[];
|
visibleTabIds: string[];
|
||||||
virtualTabIds: string[];
|
|
||||||
folderTabIds: string[];
|
|
||||||
completedCatId: number | null;
|
completedCatId: number | null;
|
||||||
counts: Record<string, number>;
|
counts: Record<string, number>;
|
||||||
search: string;
|
search: string;
|
||||||
@@ -35,7 +33,7 @@
|
|||||||
tabsEl: HTMLDivElement;
|
tabsEl: HTMLDivElement;
|
||||||
onSearchChange: (v: string) => void;
|
onSearchChange: (v: string) => void;
|
||||||
onTabChange: (f: string) => void;
|
onTabChange: (f: string) => void;
|
||||||
onSortChange: (mode: LibrarySortMode) => void;
|
onSortChange: (mode: LibrarySortOption) => void;
|
||||||
onSortDirToggle: () => void;
|
onSortDirToggle: () => void;
|
||||||
onStatusChange: (s: LibraryStatusFilter) => void;
|
onStatusChange: (s: LibraryStatusFilter) => void;
|
||||||
onFilterToggle: (f: LibraryContentFilter) => void;
|
onFilterToggle: (f: LibraryContentFilter) => void;
|
||||||
@@ -44,7 +42,6 @@
|
|||||||
onFilterPanelToggle: () => void;
|
onFilterPanelToggle: () => void;
|
||||||
onRefresh: () => void;
|
onRefresh: () => void;
|
||||||
onCancelRefresh: () => void;
|
onCancelRefresh: () => void;
|
||||||
onRefreshCategory: (catId: number) => void;
|
|
||||||
onOpenDownloadsFolder: () => void;
|
onOpenDownloadsFolder: () => void;
|
||||||
onTabDragStart: (e: DragEvent, id: string) => void;
|
onTabDragStart: (e: DragEvent, id: string) => void;
|
||||||
onTabDragOver: (e: DragEvent, id: string, idx: number) => void;
|
onTabDragOver: (e: DragEvent, id: string, idx: number) => void;
|
||||||
@@ -55,13 +52,13 @@
|
|||||||
|
|
||||||
let {
|
let {
|
||||||
tab, tabSortMode, tabSortDir, tabStatus, tabFilters, hasActiveFilters,
|
tab, tabSortMode, tabSortDir, tabStatus, tabFilters, hasActiveFilters,
|
||||||
anims, visibleCategories, visibleTabIds, virtualTabIds, folderTabIds, completedCatId,
|
anims = false, visibleCategories, visibleTabIds, completedCatId,
|
||||||
counts, search, refreshing, refreshProgress, refreshDone, refreshingCatId,
|
counts, search, refreshing, refreshProgress, refreshDone, refreshingCatId,
|
||||||
activeDragKind, dragInsertIdx, dragTabId, dragOverTabId, sortPanelOpen, filterPanelOpen,
|
activeDragKind, dragInsertIdx, dragTabId, dragOverTabId, sortPanelOpen, filterPanelOpen,
|
||||||
tabsEl = $bindable(),
|
tabsEl = $bindable(),
|
||||||
onSearchChange, onTabChange, onSortChange, onSortDirToggle, onStatusChange,
|
onSearchChange, onTabChange, onSortChange, onSortDirToggle, onStatusChange,
|
||||||
onFilterToggle, onFiltersClear, onSortPanelToggle, onFilterPanelToggle,
|
onFilterToggle, onFiltersClear, onSortPanelToggle, onFilterPanelToggle,
|
||||||
onRefresh, onCancelRefresh, onRefreshCategory, onOpenDownloadsFolder,
|
onRefresh, onCancelRefresh, onOpenDownloadsFolder,
|
||||||
onTabDragStart, onTabDragOver, onTabDragLeave, onTabDrop, onTabDragEnd,
|
onTabDragStart, onTabDragOver, onTabDragLeave, onTabDrop, onTabDragEnd,
|
||||||
}: Props = $props();
|
}: Props = $props();
|
||||||
|
|
||||||
@@ -85,18 +82,18 @@
|
|||||||
else if (ol + ow > pl + cw) tabsEl.scrollTo({ left: ol + ow - cw, behavior: "smooth" });
|
else if (ol + ow > pl + cw) tabsEl.scrollTo({ left: ol + ow - cw, behavior: "smooth" });
|
||||||
});
|
});
|
||||||
|
|
||||||
const SORT_LABELS: Record<LibrarySortMode, string> = {
|
const SORT_LABELS: Record<LibrarySortOption, string> = {
|
||||||
az: "A–Z",
|
az: "A–Z",
|
||||||
unreadCount: "Unread chapters",
|
unreadCount: "Unread chapters",
|
||||||
totalChapters: "Total chapters",
|
totalChapters: "Total chapters",
|
||||||
recentlyAdded: "Recently added",
|
dateAdded: "Recently added",
|
||||||
recentlyRead: "Recently read",
|
lastRead: "Recently read",
|
||||||
latestFetched: "Latest fetched chapter",
|
latestFetched: "Latest fetched chapter",
|
||||||
latestUploaded: "Latest uploaded chapter",
|
latestUploaded: "Latest uploaded chapter",
|
||||||
};
|
};
|
||||||
|
|
||||||
const ALL_SORT_MODES: LibrarySortMode[] = [
|
const ALL_SORT_MODES: LibrarySortOption[] = [
|
||||||
"az", "unreadCount", "totalChapters", "recentlyAdded", "recentlyRead", "latestFetched", "latestUploaded",
|
"az", "unreadCount", "totalChapters", "dateAdded", "lastRead", "latestFetched", "latestUploaded",
|
||||||
];
|
];
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
|||||||
@@ -4,8 +4,8 @@ import type { Category } from "$lib/types";
|
|||||||
import { settingsState, updateSettings } from "$lib/state/settings.svelte";
|
import { settingsState, updateSettings } from "$lib/state/settings.svelte";
|
||||||
|
|
||||||
export type LibrarySortOption =
|
export type LibrarySortOption =
|
||||||
| "alphabetical"
|
| "az"
|
||||||
| "unread"
|
| "unreadCount"
|
||||||
| "lastRead"
|
| "lastRead"
|
||||||
| "dateAdded"
|
| "dateAdded"
|
||||||
| "totalChapters"
|
| "totalChapters"
|
||||||
@@ -153,11 +153,11 @@ class LibraryState {
|
|||||||
if (f.downloaded) items = items.filter(m => (m.downloadCount ?? 0) > 0);
|
if (f.downloaded) items = items.filter(m => (m.downloadCount ?? 0) > 0);
|
||||||
if (f.bookmarked) items = items.filter(m => (m.bookmarkCount ?? 0) > 0);
|
if (f.bookmarked) items = items.filter(m => (m.bookmarkCount ?? 0) > 0);
|
||||||
|
|
||||||
const { mode, dir } = this.tabSort[tab] ?? { mode: "alphabetical" as LibrarySortOption, dir: "asc" as LibrarySortDir };
|
const { mode, dir } = this.tabSort[tab] ?? { mode: "az" as LibrarySortOption, dir: "asc" as LibrarySortDir };
|
||||||
|
|
||||||
const sorted = [...items].sort((a, b) => {
|
const sorted = [...items].sort((a, b) => {
|
||||||
switch (mode) {
|
switch (mode) {
|
||||||
case "unread": return (b.unreadCount ?? 0) - (a.unreadCount ?? 0);
|
case "unreadCount": return (b.unreadCount ?? 0) - (a.unreadCount ?? 0);
|
||||||
case "lastRead": return (b.lastReadAt ?? 0) - (a.lastReadAt ?? 0);
|
case "lastRead": return (b.lastReadAt ?? 0) - (a.lastReadAt ?? 0);
|
||||||
case "dateAdded": return (b.addedAt ?? 0) - (a.addedAt ?? 0);
|
case "dateAdded": return (b.addedAt ?? 0) - (a.addedAt ?? 0);
|
||||||
case "totalChapters": return (b.chapters?.totalCount ?? 0) - (a.chapters?.totalCount ?? 0);
|
case "totalChapters": return (b.chapters?.totalCount ?? 0) - (a.chapters?.totalCount ?? 0);
|
||||||
@@ -185,7 +185,7 @@ class LibraryState {
|
|||||||
|
|
||||||
toggleTabSortDir(tab: string) {
|
toggleTabSortDir(tab: string) {
|
||||||
const prev = this.tabSort[tab];
|
const prev = this.tabSort[tab];
|
||||||
const mode = prev?.mode ?? "alphabetical";
|
const mode = prev?.mode ?? "az";
|
||||||
const dir = prev?.dir === "asc" ? "desc" : "asc";
|
const dir = prev?.dir === "asc" ? "desc" : "asc";
|
||||||
this.setTabSort(tab, mode, dir);
|
this.setTabSort(tab, mode, dir);
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user