Files
chitai/frontend/src/lib/components/view/book-actions-menu.svelte
T
patrick 45b03764d2 chore: format the tree and clear ruff's findings
Applies ruff format, ruff check --fix and prettier, so CI can gate on all three.
The surviving unused imports were all re-exports in __init__.py, now covered by a
per-file ignore; the pdf.js viewer and the generated openapi types join the
vendored code that eslint and prettier already skip.
2026-08-17 15:17:14 -04:00

101 lines
2.9 KiB
Svelte

<script lang="ts">
import * as DropdownMenu from '$lib/components/ui/dropdown-menu/index';
import { buttonVariants } from '$lib/components/ui/button/index.js';
import { getBookOperationsState } from '$lib/state/bookOperations.svelte';
import { getLibraryState } from '$lib/state/library.svelte';
import { getBookshelfState } from '$lib/state/bookshelf.svelte';
import { getFileType } from '$lib/utils';
import {
BookOpenCheck,
BookOpenText,
Download,
EllipsisVertical,
Pencil,
Trash2
} from '@lucide/svelte';
import type { Book, BookFile } from '$lib/schema';
let { book, class: className = '' }: { book: Book; class?: string } = $props();
const bookOps = getBookOperationsState();
const libraryState = getLibraryState();
const bookshelfState = getBookshelfState();
function openInReader(file: BookFile) {
const type = getFileType(file.filename);
if (type === 'EPUB' || type === 'PDF')
window.open(`/book/${book.id}/read/${type.toLowerCase()}/${file.id}`, '_blank', 'noopener');
}
</script>
<!--
The per-book overflow menu, shared so the grid, list and table cannot drift
apart. data-row-control keeps a click here from toggling row selection.
-->
<DropdownMenu.Root>
<DropdownMenu.Trigger
data-row-control
aria-label="More actions for {book.title}"
class="{buttonVariants({ variant: 'ghost', size: 'icon' })} {className}"
>
<EllipsisVertical class="size-4" />
</DropdownMenu.Trigger>
<DropdownMenu.Content align="end" data-row-control>
<DropdownMenu.Group>
{#if book.files.length > 0}
<DropdownMenu.Item onclick={() => openInReader(book.files[0])}>
<BookOpenText class="size-4" />
Read
</DropdownMenu.Item>
<DropdownMenu.Item
onclick={() =>
bookOps.downloadBookFile(book.id, book.files[0].id, book.files[0].filename)}
>
<Download class="size-4" />
Download
</DropdownMenu.Item>
<DropdownMenu.Separator />
{/if}
<DropdownMenu.Item
onclick={() => {
bookOps.bookToEdit = book;
bookOps.editDialogOpen = true;
}}
>
<Pencil class="size-4" />
Edit
</DropdownMenu.Item>
<DropdownMenu.Item
onclick={async () => {
if (!book.progress?.completed) await bookOps.markBooksAsComplete([book.id]);
else await bookOps.markBooksAsIncomplete([book.id]);
}}
>
<BookOpenCheck class="size-4" />
{book.progress?.completed ? 'Mark as unfinished' : 'Mark as finished'}
</DropdownMenu.Item>
<DropdownMenu.Separator />
<DropdownMenu.Item
class="text-destructive"
onclick={() => {
bookOps.deleteDialogTitle = `Delete "${book.title}"?`;
bookOps.deleteFn = async (deleteFiles: boolean) => {
await bookOps.deleteBooks([book.id], deleteFiles);
libraryState.activeLibrary!.total!--;
bookshelfState.deletedBooks([book]);
};
bookOps.deleteDialogOpen = true;
}}
>
<Trash2 class="size-4" />
Delete
</DropdownMenu.Item>
</DropdownMenu.Group>
</DropdownMenu.Content>
</DropdownMenu.Root>