generated from tpl/obsidian-sample-plugin
152 lines
3.6 KiB
Svelte
152 lines
3.6 KiB
Svelte
<script lang="ts">
|
|
import type BookTrackerPlugin from "@src/main";
|
|
import Book from "@ui/components/bookshelf/Book.svelte";
|
|
import Bookshelf from "@ui/components/bookshelf/Bookshelf.svelte";
|
|
import BookStack from "@ui/components/bookshelf/BookStack.svelte";
|
|
import {
|
|
getMetadataContext,
|
|
type FileMetadata,
|
|
} from "@ui/stores/metadata.svelte";
|
|
import { COLOR_NAMES, type ColorName } from "@utils/color";
|
|
import { randomElement, randomFloat } from "@utils/rand";
|
|
import { v4 as uuidv4 } from "uuid";
|
|
import memoize from "just-memoize";
|
|
import type { TFile } from "obsidian";
|
|
|
|
interface Props {
|
|
plugin: BookTrackerPlugin;
|
|
}
|
|
|
|
interface BookData {
|
|
id: string;
|
|
title: string;
|
|
subtitle?: string;
|
|
authors: string[];
|
|
width: number;
|
|
color: ColorName;
|
|
design: (typeof designs)[number];
|
|
orientation: "default" | "tilted" | "front";
|
|
file: TFile;
|
|
}
|
|
|
|
interface BookStackData {
|
|
id: string;
|
|
books: BookData[];
|
|
}
|
|
|
|
const { plugin }: Props = $props();
|
|
|
|
const metadataStore = getMetadataContext();
|
|
|
|
const designs = ["default", "dual-top-bands", "split-bands"] as const;
|
|
|
|
const randomDesign = () => randomElement(designs);
|
|
const randomColor = () => randomElement(COLOR_NAMES);
|
|
function randomOrientation() {
|
|
const n = randomFloat();
|
|
|
|
if (n < 0.55) {
|
|
return "default";
|
|
} else if (n < 0.8) {
|
|
return "tilted";
|
|
} else {
|
|
return "front";
|
|
}
|
|
}
|
|
const randomStackChance = () => randomFloat() > 0.9;
|
|
function randomStackSize() {
|
|
const n = randomFloat();
|
|
if (n < 0.15) {
|
|
return 5;
|
|
} else if (n < 0.3) {
|
|
return 4;
|
|
} else if (n < 0.5) {
|
|
return 3;
|
|
} else if (n < 0.8) {
|
|
return 2;
|
|
} else {
|
|
return 1;
|
|
}
|
|
}
|
|
|
|
const getBookData = memoize(
|
|
(meta: FileMetadata): BookData => {
|
|
const orientation = randomOrientation();
|
|
|
|
return {
|
|
id: meta.file.path,
|
|
title: meta.book.title,
|
|
subtitle:
|
|
meta.book.subtitle === "" ? undefined : meta.book.subtitle,
|
|
authors: meta.book.authors,
|
|
width: Math.min(Math.max(20, meta.book.pageCount / 10), 100),
|
|
color: randomColor(),
|
|
design: orientation === "front" ? "default" : randomDesign(),
|
|
orientation: randomOrientation(),
|
|
file: meta.file,
|
|
};
|
|
},
|
|
(metadata: FileMetadata) => metadata.file.path,
|
|
);
|
|
|
|
let books: (BookData | BookStackData)[] = $state([]);
|
|
$effect(() => {
|
|
let newBooks: (BookData | BookStackData)[] = [];
|
|
|
|
for (let i = 0; i < metadataStore.metadata.length; i++) {
|
|
if (randomStackChance()) {
|
|
const booksRemaining = metadataStore.metadata.length - i;
|
|
const stackSize = Math.min(booksRemaining, randomStackSize());
|
|
|
|
newBooks.push({
|
|
id: uuidv4(),
|
|
books: metadataStore.metadata
|
|
.slice(i, i + stackSize)
|
|
.map(getBookData),
|
|
});
|
|
i += stackSize - 1;
|
|
} else {
|
|
newBooks.push(getBookData(metadataStore.metadata[i]));
|
|
}
|
|
}
|
|
|
|
books = newBooks;
|
|
});
|
|
</script>
|
|
|
|
<Bookshelf>
|
|
{#each books as book (book.id)}
|
|
{#if "books" in book}
|
|
<BookStack>
|
|
{#each book.books as bookData (bookData.id)}
|
|
<Book
|
|
title={bookData.title}
|
|
subtitle={bookData.subtitle}
|
|
authors={bookData.authors}
|
|
width={bookData.width}
|
|
color={bookData.color}
|
|
design={bookData.design}
|
|
orientation="flat"
|
|
onClick={() =>
|
|
plugin.app.workspace
|
|
.getLeaf(false)
|
|
.openFile(bookData.file)}
|
|
/>
|
|
{/each}
|
|
</BookStack>
|
|
{:else}
|
|
<Book
|
|
title={book.title}
|
|
subtitle={book.subtitle}
|
|
authors={book.authors}
|
|
width={book.width}
|
|
color={book.color}
|
|
design={book.design}
|
|
orientation={book.orientation}
|
|
onClick={() =>
|
|
plugin.app.workspace.getLeaf(false).openFile(book.file)}
|
|
/>
|
|
{/if}
|
|
{/each}
|
|
</Bookshelf>
|