generated from tpl/obsidian-sample-plugin
Put actual books on bookshelf view
This commit is contained in:
parent
658713fbec
commit
b9f146f922
|
@ -10,7 +10,7 @@ import { EditorCheckCommand } from "./Command";
|
||||||
import type { BookTrackerPluginSettings } from "@ui/settings";
|
import type { BookTrackerPluginSettings } from "@ui/settings";
|
||||||
import { RatingModal } from "@ui/modals";
|
import { RatingModal } from "@ui/modals";
|
||||||
import type { ReadingLog } from "@utils/ReadingLog";
|
import type { ReadingLog } from "@utils/ReadingLog";
|
||||||
import { READ_STATE } from "@src/const";
|
import { STATUS_READ } from "@src/const";
|
||||||
import { mkdirRecursive, dirname } from "@utils/fs";
|
import { mkdirRecursive, dirname } from "@utils/fs";
|
||||||
|
|
||||||
export class LogReadingFinishedCommand extends EditorCheckCommand {
|
export class LogReadingFinishedCommand extends EditorCheckCommand {
|
||||||
|
@ -70,7 +70,7 @@ export class LogReadingFinishedCommand extends EditorCheckCommand {
|
||||||
const endDate = moment().format("YYYY-MM-DD");
|
const endDate = moment().format("YYYY-MM-DD");
|
||||||
|
|
||||||
this.app.fileManager.processFrontMatter(file, (frontMatter) => {
|
this.app.fileManager.processFrontMatter(file, (frontMatter) => {
|
||||||
frontMatter[this.settings.statusProperty] = READ_STATE;
|
frontMatter[this.settings.statusProperty] = STATUS_READ;
|
||||||
frontMatter[this.settings.endDateProperty] = endDate;
|
frontMatter[this.settings.endDateProperty] = endDate;
|
||||||
frontMatter[this.settings.ratingProperty] = ratings.rating;
|
frontMatter[this.settings.ratingProperty] = ratings.rating;
|
||||||
if (this.settings.spiceProperty !== "") {
|
if (this.settings.spiceProperty !== "") {
|
||||||
|
|
|
@ -7,7 +7,7 @@ import {
|
||||||
} from "obsidian";
|
} from "obsidian";
|
||||||
import { EditorCheckCommand } from "./Command";
|
import { EditorCheckCommand } from "./Command";
|
||||||
import type { BookTrackerPluginSettings } from "@ui/settings";
|
import type { BookTrackerPluginSettings } from "@ui/settings";
|
||||||
import { IN_PROGRESS_STATE } from "@src/const";
|
import { STATUS_IN_PROGRESS } from "@src/const";
|
||||||
|
|
||||||
export class LogReadingStartedCommand extends EditorCheckCommand {
|
export class LogReadingStartedCommand extends EditorCheckCommand {
|
||||||
constructor(
|
constructor(
|
||||||
|
@ -38,7 +38,7 @@ export class LogReadingStartedCommand extends EditorCheckCommand {
|
||||||
const startDate = moment().format("YYYY-MM-DD");
|
const startDate = moment().format("YYYY-MM-DD");
|
||||||
|
|
||||||
this.app.fileManager.processFrontMatter(file, (frontMatter) => {
|
this.app.fileManager.processFrontMatter(file, (frontMatter) => {
|
||||||
frontMatter[this.settings.statusProperty] = IN_PROGRESS_STATE;
|
frontMatter[this.settings.statusProperty] = STATUS_IN_PROGRESS;
|
||||||
frontMatter[this.settings.startDateProperty] = startDate;
|
frontMatter[this.settings.startDateProperty] = startDate;
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
@ -8,7 +8,7 @@ import {
|
||||||
import { EditorCheckCommand } from "./Command";
|
import { EditorCheckCommand } from "./Command";
|
||||||
import type { BookTrackerPluginSettings } from "@ui/settings";
|
import type { BookTrackerPluginSettings } from "@ui/settings";
|
||||||
import type { ReadingLog } from "@utils/ReadingLog";
|
import type { ReadingLog } from "@utils/ReadingLog";
|
||||||
import { TO_BE_READ_STATE } from "@src/const";
|
import { STATUS_TO_BE_READ } from "@src/const";
|
||||||
|
|
||||||
export class ResetReadingStatusCommand extends EditorCheckCommand {
|
export class ResetReadingStatusCommand extends EditorCheckCommand {
|
||||||
constructor(
|
constructor(
|
||||||
|
@ -38,7 +38,7 @@ export class ResetReadingStatusCommand extends EditorCheckCommand {
|
||||||
const file = ctx.file!;
|
const file = ctx.file!;
|
||||||
|
|
||||||
this.app.fileManager.processFrontMatter(file, (frontMatter) => {
|
this.app.fileManager.processFrontMatter(file, (frontMatter) => {
|
||||||
frontMatter[this.settings.statusProperty] = TO_BE_READ_STATE;
|
frontMatter[this.settings.statusProperty] = STATUS_TO_BE_READ;
|
||||||
frontMatter[this.settings.startDateProperty] = "";
|
frontMatter[this.settings.startDateProperty] = "";
|
||||||
frontMatter[this.settings.endDateProperty] = "";
|
frontMatter[this.settings.endDateProperty] = "";
|
||||||
});
|
});
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
export const TO_BE_READ_STATE = "To Be Read";
|
export const STATUS_TO_BE_READ = "To Be Read";
|
||||||
export const IN_PROGRESS_STATE = "Currently Reading";
|
export const STATUS_IN_PROGRESS = "Currently Reading";
|
||||||
export const READ_STATE = "Read";
|
export const STATUS_READ = "Read";
|
||||||
|
|
||||||
export const CONTENT_TYPE_EXTENSIONS: Record<string, string> = {
|
export const CONTENT_TYPE_EXTENSIONS: Record<string, string> = {
|
||||||
"image/jpeg": "jpg",
|
"image/jpeg": "jpg",
|
||||||
|
|
12
src/types.ts
12
src/types.ts
|
@ -1,4 +1,8 @@
|
||||||
import type { IN_PROGRESS_STATE, READ_STATE, TO_BE_READ_STATE } from "./const";
|
import type {
|
||||||
|
STATUS_IN_PROGRESS,
|
||||||
|
STATUS_READ,
|
||||||
|
STATUS_TO_BE_READ,
|
||||||
|
} from "./const";
|
||||||
|
|
||||||
export interface Author {
|
export interface Author {
|
||||||
name: string;
|
name: string;
|
||||||
|
@ -24,8 +28,8 @@ export interface Book {
|
||||||
isbn13: string;
|
isbn13: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export type ToBeReadState = typeof TO_BE_READ_STATE;
|
export type ToBeReadState = typeof STATUS_TO_BE_READ;
|
||||||
export type InProgressState = typeof IN_PROGRESS_STATE;
|
export type InProgressState = typeof STATUS_IN_PROGRESS;
|
||||||
export type ReadState = typeof READ_STATE;
|
export type ReadState = typeof STATUS_READ;
|
||||||
|
|
||||||
export type ReadingState = ToBeReadState | InProgressState | ReadState;
|
export type ReadingState = ToBeReadState | InProgressState | ReadState;
|
||||||
|
|
|
@ -2,6 +2,7 @@ import { registerCodeBlockRenderer } from ".";
|
||||||
import { SvelteCodeBlockRenderer } from "./SvelteCodeBlockRenderer";
|
import { SvelteCodeBlockRenderer } from "./SvelteCodeBlockRenderer";
|
||||||
import BookshelfCodeBlockView from "./BookshelfCodeBlockView.svelte";
|
import BookshelfCodeBlockView from "./BookshelfCodeBlockView.svelte";
|
||||||
import type BookTrackerPlugin from "@src/main";
|
import type BookTrackerPlugin from "@src/main";
|
||||||
|
import z from "zod/v4";
|
||||||
|
|
||||||
export function registerBookshelfCodeBlockProcessor(
|
export function registerBookshelfCodeBlockProcessor(
|
||||||
plugin: BookTrackerPlugin
|
plugin: BookTrackerPlugin
|
||||||
|
@ -13,6 +14,12 @@ export function registerBookshelfCodeBlockProcessor(
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export const BookshelfSettingsSchema = z.object({
|
||||||
|
titleProperty: z.string(),
|
||||||
|
subtitleProperty: z.optional(z.string()),
|
||||||
|
authorsProperty: z.string(),
|
||||||
|
});
|
||||||
|
|
||||||
export class BookshelfCodeBlockRenderer extends SvelteCodeBlockRenderer<
|
export class BookshelfCodeBlockRenderer extends SvelteCodeBlockRenderer<
|
||||||
typeof BookshelfCodeBlockView
|
typeof BookshelfCodeBlockView
|
||||||
> {
|
> {
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { READ_STATE } from "@src/const";
|
import { STATUS_TO_BE_READ } from "@src/const";
|
||||||
import type BookTrackerPlugin from "@src/main";
|
import type BookTrackerPlugin from "@src/main";
|
||||||
import type { ReadingState } from "@src/types";
|
import type { ReadingState } from "@src/types";
|
||||||
import Book from "@ui/components/bookshelf/Book.svelte";
|
import Book from "@ui/components/bookshelf/Book.svelte";
|
||||||
|
@ -9,26 +9,42 @@
|
||||||
import {
|
import {
|
||||||
createMetadata,
|
createMetadata,
|
||||||
setMetadataContext,
|
setMetadataContext,
|
||||||
|
type FileMetadata,
|
||||||
} from "@ui/stores/metadata.svelte";
|
} from "@ui/stores/metadata.svelte";
|
||||||
import {
|
import {
|
||||||
createSettings,
|
createSettings,
|
||||||
setSettingsContext,
|
setSettingsContext,
|
||||||
} from "@ui/stores/settings.svelte";
|
} from "@ui/stores/settings.svelte";
|
||||||
import { COLOR_NAMES } from "@utils/color";
|
import { COLOR_NAMES, type ColorName } from "@utils/color";
|
||||||
import { randomElement } from "@utils/rand";
|
import { randomElement, randomFloat, randomInt } from "@utils/rand";
|
||||||
import { onDestroy } from "svelte";
|
import { onDestroy } from "svelte";
|
||||||
|
import { BookshelfSettingsSchema } from "./BookshelfCodeBlock";
|
||||||
|
import { parseYaml, TFile } from "obsidian";
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
plugin: BookTrackerPlugin;
|
plugin: BookTrackerPlugin;
|
||||||
source: string;
|
source: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
const { plugin }: Props = $props();
|
interface BookData {
|
||||||
|
title: string;
|
||||||
|
subtitle?: string;
|
||||||
|
author: string;
|
||||||
|
width: number;
|
||||||
|
color: ColorName;
|
||||||
|
design: (typeof designs)[number];
|
||||||
|
orientation: undefined | "tilted" | "on-display";
|
||||||
|
file: TFile;
|
||||||
|
}
|
||||||
|
|
||||||
|
const { plugin, source }: Props = $props();
|
||||||
|
|
||||||
|
const settings = $derived(BookshelfSettingsSchema.parse(parseYaml(source)));
|
||||||
|
|
||||||
const settingsStore = createSettings(plugin);
|
const settingsStore = createSettings(plugin);
|
||||||
setSettingsContext(settingsStore);
|
setSettingsContext(settingsStore);
|
||||||
|
|
||||||
let stateFilter: ReadingState = $state(READ_STATE);
|
let stateFilter: ReadingState = $state(STATUS_TO_BE_READ);
|
||||||
|
|
||||||
const metadataStore = createMetadata(plugin, stateFilter);
|
const metadataStore = createMetadata(plugin, stateFilter);
|
||||||
setMetadataContext(metadataStore);
|
setMetadataContext(metadataStore);
|
||||||
|
@ -40,155 +56,113 @@
|
||||||
"split-bands",
|
"split-bands",
|
||||||
] as const;
|
] as const;
|
||||||
|
|
||||||
function randomDesign() {
|
const randomDesign = () => randomElement(designs);
|
||||||
return randomElement(designs);
|
const randomColor = () => randomElement(COLOR_NAMES);
|
||||||
|
function randomOrientation() {
|
||||||
|
const n = randomFloat();
|
||||||
|
|
||||||
|
if (n < 0.55) {
|
||||||
|
return undefined;
|
||||||
|
} else if (n < 0.8) {
|
||||||
|
return "tilted";
|
||||||
|
} else {
|
||||||
|
return "on-display";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
const randomStackChance = () => randomFloat() > 0.9;
|
||||||
|
function randomStackSize() {
|
||||||
|
const n = randomFloat();
|
||||||
|
if (n < 0.2) {
|
||||||
|
return 5;
|
||||||
|
} else if (n < 0.5) {
|
||||||
|
return 4;
|
||||||
|
} else if (n < 0.8) {
|
||||||
|
return 3;
|
||||||
|
} else if (n < 0.98) {
|
||||||
|
return 2;
|
||||||
|
} else {
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
const calculateWidth = (pageCount: number) =>
|
||||||
|
Math.max(10, Math.min(1000, 200 + pageCount * 10));
|
||||||
|
|
||||||
|
function getBookData(metadata: FileMetadata): BookData {
|
||||||
|
return {
|
||||||
|
title: metadata.frontmatter[settings.titleProperty],
|
||||||
|
subtitle: settings.subtitleProperty
|
||||||
|
? metadata.frontmatter[settings.subtitleProperty]
|
||||||
|
: undefined,
|
||||||
|
author: metadata.frontmatter[settings.authorsProperty].join(", "),
|
||||||
|
width: calculateWidth(
|
||||||
|
metadata.frontmatter[settingsStore.settings.pageCountProperty],
|
||||||
|
),
|
||||||
|
color: randomColor(),
|
||||||
|
design: randomDesign(),
|
||||||
|
orientation: randomOrientation(),
|
||||||
|
file: metadata.file,
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
function randomColor() {
|
const books = $derived.by(() => {
|
||||||
return randomElement(COLOR_NAMES);
|
let books: (BookData | BookData[])[] = [];
|
||||||
|
|
||||||
|
for (let i = 0; i < metadataStore.metadata.length; i++) {
|
||||||
|
if (randomStackChance()) {
|
||||||
|
const booksRemaining = metadataStore.metadata.length - i;
|
||||||
|
const stackSize = randomInt(
|
||||||
|
1,
|
||||||
|
Math.min(booksRemaining, randomStackSize()),
|
||||||
|
);
|
||||||
|
|
||||||
|
books.push(
|
||||||
|
metadataStore.metadata
|
||||||
|
.slice(i, i + stackSize)
|
||||||
|
.map(getBookData),
|
||||||
|
);
|
||||||
|
i += stackSize - 1;
|
||||||
|
} else {
|
||||||
|
books.push(getBookData(metadataStore.metadata[i]));
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return books;
|
||||||
|
});
|
||||||
|
|
||||||
onDestroy(() => metadataStore.destroy());
|
onDestroy(() => metadataStore.destroy());
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<Bookshelf>
|
<Bookshelf>
|
||||||
<Book
|
{#each books as book}
|
||||||
title="Hello World"
|
{#if Array.isArray(book)}
|
||||||
width={1120}
|
<BookStack totalChildren={book.length}>
|
||||||
color={randomColor()}
|
{#each book as bookData}
|
||||||
design={randomDesign()}
|
|
||||||
/>
|
|
||||||
<Book
|
|
||||||
title="White Space"
|
|
||||||
width={700}
|
|
||||||
color={randomColor()}
|
|
||||||
design={randomDesign()}
|
|
||||||
/>
|
|
||||||
<Book
|
|
||||||
title="The Art of Computer Programming Vol 1"
|
|
||||||
width={1156}
|
|
||||||
color={randomColor()}
|
|
||||||
design={randomDesign()}
|
|
||||||
/>
|
|
||||||
<Book
|
|
||||||
title="Cascading Style Sheets"
|
|
||||||
subtitle="Guide to Design"
|
|
||||||
width={560}
|
|
||||||
color={randomColor()}
|
|
||||||
design={randomDesign()}
|
|
||||||
orientation="tilted"
|
|
||||||
/>
|
|
||||||
<Book
|
|
||||||
title="HTML5"
|
|
||||||
subtitle="Welcome to the Web"
|
|
||||||
width={1350}
|
|
||||||
color={randomColor()}
|
|
||||||
design={randomDesign()}
|
|
||||||
/>
|
|
||||||
<BookStack totalChildren={2}>
|
|
||||||
<BookStackElement
|
<BookStackElement
|
||||||
title="Coding for Dummies"
|
title={bookData.title}
|
||||||
subtitle="JS tutorial"
|
subtitle={bookData.subtitle}
|
||||||
color={randomColor()}
|
color={bookData.color}
|
||||||
design={randomDesign()}
|
design={bookData.design}
|
||||||
/>
|
onClick={() =>
|
||||||
<BookStackElement
|
plugin.app.workspace.openLinkText(
|
||||||
title="Coding for Dummies"
|
bookData.file.path,
|
||||||
subtitle="C# tutorial"
|
"",
|
||||||
color={randomColor()}
|
true,
|
||||||
design={randomDesign()}
|
)}
|
||||||
/>
|
/>
|
||||||
|
{/each}
|
||||||
</BookStack>
|
</BookStack>
|
||||||
|
{:else}
|
||||||
<Book
|
<Book
|
||||||
title="CoffeeScript"
|
title={book.title}
|
||||||
subtitle="The JS Alternative"
|
subtitle={book.subtitle}
|
||||||
author="The Dev Guy"
|
author={book.author}
|
||||||
color={randomColor()}
|
width={book.width}
|
||||||
design={randomDesign()}
|
color={book.color}
|
||||||
orientation="on-display"
|
design={book.design}
|
||||||
|
orientation={book.orientation}
|
||||||
|
onClick={() =>
|
||||||
|
plugin.app.workspace.openLinkText(book.file.path, "", true)}
|
||||||
/>
|
/>
|
||||||
<Book
|
{/if}
|
||||||
title="Cheat Sheet"
|
{/each}
|
||||||
subtitle="Guide to Design"
|
|
||||||
width={870}
|
|
||||||
color={randomColor()}
|
|
||||||
design={randomDesign()}
|
|
||||||
/>
|
|
||||||
<Book
|
|
||||||
title="Psychology of Colors"
|
|
||||||
width={540}
|
|
||||||
color={randomColor()}
|
|
||||||
design={randomDesign()}
|
|
||||||
/>
|
|
||||||
<Book
|
|
||||||
title="TypeScript"
|
|
||||||
subtitle="Intro JS to type checking"
|
|
||||||
width={1130}
|
|
||||||
color={randomColor()}
|
|
||||||
design={randomDesign()}
|
|
||||||
/>
|
|
||||||
<Book
|
|
||||||
title="Testing"
|
|
||||||
width={10}
|
|
||||||
color={randomColor()}
|
|
||||||
design={randomDesign()}
|
|
||||||
/>
|
|
||||||
<Book
|
|
||||||
title="JavaScript"
|
|
||||||
subtitle="The Definitive Guide"
|
|
||||||
author="David Flanagan"
|
|
||||||
color={randomColor()}
|
|
||||||
design={randomDesign()}
|
|
||||||
orientation="on-display"
|
|
||||||
/>
|
|
||||||
<Book
|
|
||||||
title="Pragmatic Programmer"
|
|
||||||
color={randomColor()}
|
|
||||||
design={randomDesign()}
|
|
||||||
/>
|
|
||||||
<Book title="White Space" color={randomColor()} design={randomDesign()} />
|
|
||||||
<Book
|
|
||||||
title="W3 Schools"
|
|
||||||
subtitle="The best around"
|
|
||||||
color={randomColor()}
|
|
||||||
design={randomDesign()}
|
|
||||||
orientation="tilted"
|
|
||||||
/>
|
|
||||||
<Book
|
|
||||||
title="UI/UX"
|
|
||||||
subtitle="Guide to Mobile Development"
|
|
||||||
author="John Doe"
|
|
||||||
color={randomColor()}
|
|
||||||
design={randomDesign()}
|
|
||||||
orientation="on-display"
|
|
||||||
/>
|
|
||||||
<Book
|
|
||||||
title="Clean Code"
|
|
||||||
color={randomColor()}
|
|
||||||
design={randomDesign()}
|
|
||||||
orientation="tilted"
|
|
||||||
/>
|
|
||||||
<Book title="Docs for Devs" color={randomColor()} design={randomDesign()} />
|
|
||||||
<BookStack totalChildren={4}>
|
|
||||||
<BookStackElement
|
|
||||||
title="The Art of Computer Programming Vol 1"
|
|
||||||
color={randomColor()}
|
|
||||||
design={randomDesign()}
|
|
||||||
/>
|
|
||||||
<BookStackElement
|
|
||||||
title="The Art of Computer Programming Vol 2"
|
|
||||||
color={randomColor()}
|
|
||||||
design={randomDesign()}
|
|
||||||
/>
|
|
||||||
<BookStackElement
|
|
||||||
title="The Art of Computer Programming Vol 3"
|
|
||||||
color={randomColor()}
|
|
||||||
design={randomDesign()}
|
|
||||||
/>
|
|
||||||
<BookStackElement
|
|
||||||
title="The Art of Computer Programming Vol 4a"
|
|
||||||
color={randomColor()}
|
|
||||||
design={randomDesign()}
|
|
||||||
/>
|
|
||||||
</BookStack>
|
|
||||||
</Bookshelf>
|
</Bookshelf>
|
||||||
|
|
|
@ -6,6 +6,7 @@
|
||||||
import { createReadingLog } from "@ui/stores/reading-log.svelte";
|
import { createReadingLog } from "@ui/stores/reading-log.svelte";
|
||||||
import { ALL_TIME } from "@ui/stores/date-filter.svelte";
|
import { ALL_TIME } from "@ui/stores/date-filter.svelte";
|
||||||
import { onDestroy } from "svelte";
|
import { onDestroy } from "svelte";
|
||||||
|
import { getLinkpath } from "obsidian";
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
plugin: BookTrackerPlugin;
|
plugin: BookTrackerPlugin;
|
||||||
|
@ -14,9 +15,7 @@
|
||||||
const { plugin }: Props = $props();
|
const { plugin }: Props = $props();
|
||||||
|
|
||||||
function bookUri(book: string) {
|
function bookUri(book: string) {
|
||||||
const v = encodeURIComponent(plugin.app.vault.getName());
|
return getLinkpath(book + ".md");
|
||||||
const f = encodeURIComponent(book + ".md");
|
|
||||||
return `obsidian://open?vault=${v}&file=${f}`;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const store = createReadingLog(plugin.readingLog);
|
const store = createReadingLog(plugin.readingLog);
|
||||||
|
|
|
@ -26,6 +26,7 @@
|
||||||
orientation?: "tilted" | "on-display";
|
orientation?: "tilted" | "on-display";
|
||||||
height?: number;
|
height?: number;
|
||||||
width?: number;
|
width?: number;
|
||||||
|
onClick?: () => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
let {
|
let {
|
||||||
|
@ -37,6 +38,7 @@
|
||||||
orientation,
|
orientation,
|
||||||
height,
|
height,
|
||||||
width,
|
width,
|
||||||
|
onClick,
|
||||||
}: BookProps = $props();
|
}: BookProps = $props();
|
||||||
|
|
||||||
function widthCheck(input: number | undefined) {
|
function widthCheck(input: number | undefined) {
|
||||||
|
@ -69,10 +71,11 @@
|
||||||
{design}
|
{design}
|
||||||
{height}
|
{height}
|
||||||
{width}
|
{width}
|
||||||
|
{onClick}
|
||||||
/>
|
/>
|
||||||
</BookTiltedDefault>
|
</BookTiltedDefault>
|
||||||
{:else if orientation === "on-display"}
|
{:else if orientation === "on-display"}
|
||||||
<BookOnDisplay color={color.hex}>
|
<BookOnDisplay color={color.hex} {onClick}>
|
||||||
<div
|
<div
|
||||||
class="book-display-crease"
|
class="book-display-crease"
|
||||||
style:--book-color={color.hex}
|
style:--book-color={color.hex}
|
||||||
|
@ -84,19 +87,19 @@
|
||||||
</BookOnDisplay>
|
</BookOnDisplay>
|
||||||
{/if}
|
{/if}
|
||||||
{:else if design === "split-bands"}
|
{:else if design === "split-bands"}
|
||||||
<BookSplitBands color={color.hex} width={verifiedWidth}>
|
<BookSplitBands color={color.hex} width={verifiedWidth} {onClick}>
|
||||||
<BookText {title} {subtitle} />
|
<BookText {title} {subtitle} />
|
||||||
</BookSplitBands>
|
</BookSplitBands>
|
||||||
{:else if design === "dual-top-bands"}
|
{:else if design === "dual-top-bands"}
|
||||||
<BookDualTopBands color={color.hex} width={verifiedWidth}>
|
<BookDualTopBands color={color.hex} width={verifiedWidth} {onClick}>
|
||||||
<BookText {title} {subtitle} />
|
<BookText {title} {subtitle} />
|
||||||
</BookDualTopBands>
|
</BookDualTopBands>
|
||||||
{:else if design === "colored-spine"}
|
{:else if design === "colored-spine"}
|
||||||
<BookColoredSpine color={color.hex} width={verifiedWidth}>
|
<BookColoredSpine color={color.hex} width={verifiedWidth} {onClick}>
|
||||||
<BookText {title} {subtitle} />
|
<BookText {title} {subtitle} />
|
||||||
</BookColoredSpine>
|
</BookColoredSpine>
|
||||||
{:else}
|
{:else}
|
||||||
<BookDefault color={color.hex} width={verifiedWidth}>
|
<BookDefault color={color.hex} width={verifiedWidth} {onClick}>
|
||||||
<BookText {title} {subtitle} />
|
<BookText {title} {subtitle} />
|
||||||
</BookDefault>
|
</BookDefault>
|
||||||
{/if}
|
{/if}
|
||||||
|
|
|
@ -11,6 +11,7 @@
|
||||||
subtitle?: string;
|
subtitle?: string;
|
||||||
color?: ColorName | string;
|
color?: ColorName | string;
|
||||||
design?: "default" | "split-bands" | "dual-top-bands" | "colored-spine";
|
design?: "default" | "split-bands" | "dual-top-bands" | "colored-spine";
|
||||||
|
onClick?: () => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
let {
|
let {
|
||||||
|
@ -18,6 +19,7 @@
|
||||||
subtitle,
|
subtitle,
|
||||||
color: colorRaw = "green",
|
color: colorRaw = "green",
|
||||||
design,
|
design,
|
||||||
|
onClick,
|
||||||
}: Props = $props();
|
}: Props = $props();
|
||||||
|
|
||||||
const color = $derived(
|
const color = $derived(
|
||||||
|
@ -29,19 +31,19 @@
|
||||||
|
|
||||||
<li class="bookshelf__bookstack-elem">
|
<li class="bookshelf__bookstack-elem">
|
||||||
{#if design === "split-bands"}
|
{#if design === "split-bands"}
|
||||||
<BookStackSplitBands color={color.hex}>
|
<BookStackSplitBands color={color.hex} {onClick}>
|
||||||
<BookText {title} {subtitle} />
|
<BookText {title} {subtitle} />
|
||||||
</BookStackSplitBands>
|
</BookStackSplitBands>
|
||||||
{:else if design === "dual-top-bands"}
|
{:else if design === "dual-top-bands"}
|
||||||
<BookStackDualTopBands color={color.hex}>
|
<BookStackDualTopBands color={color.hex} {onClick}>
|
||||||
<BookText {title} {subtitle} />
|
<BookText {title} {subtitle} />
|
||||||
</BookStackDualTopBands>
|
</BookStackDualTopBands>
|
||||||
{:else if design === "colored-spine"}
|
{:else if design === "colored-spine"}
|
||||||
<BookStackColoredSpine color={color.hex}>
|
<BookStackColoredSpine color={color.hex} {onClick}>
|
||||||
<BookText {title} {subtitle} />
|
<BookText {title} {subtitle} />
|
||||||
</BookStackColoredSpine>
|
</BookStackColoredSpine>
|
||||||
{:else}
|
{:else}
|
||||||
<BookStackDefault color={color.hex}>
|
<BookStackDefault color={color.hex} {onClick}>
|
||||||
<BookText {title} {subtitle} />
|
<BookText {title} {subtitle} />
|
||||||
</BookStackDefault>
|
</BookStackDefault>
|
||||||
{/if}
|
{/if}
|
||||||
|
|
|
@ -19,6 +19,7 @@ $bookEdge: 2px;
|
||||||
display: inline-flex;
|
display: inline-flex;
|
||||||
flex-flow: column nowrap;
|
flex-flow: column nowrap;
|
||||||
list-style: none;
|
list-style: none;
|
||||||
|
float: left;
|
||||||
margin: 0;
|
margin: 0;
|
||||||
padding: 0;
|
padding: 0;
|
||||||
|
|
||||||
|
|
|
@ -7,9 +7,10 @@
|
||||||
children?: Snippet;
|
children?: Snippet;
|
||||||
color?: string;
|
color?: string;
|
||||||
width?: number;
|
width?: number;
|
||||||
|
onClick?: () => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
let { children, color = "green", width = 40 }: Props = $props();
|
let { children, color = "green", width = 40, onClick }: Props = $props();
|
||||||
|
|
||||||
const borderLeftColor = $derived(chroma(color).mix("white", 0.04));
|
const borderLeftColor = $derived(chroma(color).mix("white", 0.04));
|
||||||
const borderRightColor = $derived(chroma(color).mix("black", 0.04));
|
const borderRightColor = $derived(chroma(color).mix("black", 0.04));
|
||||||
|
@ -26,6 +27,10 @@
|
||||||
style:--book-width={width + "px"}
|
style:--book-width={width + "px"}
|
||||||
style:width={width + "px"}
|
style:width={width + "px"}
|
||||||
style:color={textColor}
|
style:color={textColor}
|
||||||
|
onclick={onClick}
|
||||||
|
onkeydown={(ev) => ev.key === "Enter" && onClick?.()}
|
||||||
|
role="link"
|
||||||
|
tabindex="0"
|
||||||
>
|
>
|
||||||
{@render children?.()}
|
{@render children?.()}
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -7,9 +7,10 @@
|
||||||
children?: Snippet;
|
children?: Snippet;
|
||||||
color?: string;
|
color?: string;
|
||||||
width?: number;
|
width?: number;
|
||||||
|
onClick?: () => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
let { children, color = "green", width = 40 }: Props = $props();
|
let { children, color = "green", width = 40, onClick }: Props = $props();
|
||||||
|
|
||||||
const backgroundColor = $derived(chroma(color));
|
const backgroundColor = $derived(chroma(color));
|
||||||
const borderLeftColor = $derived(chroma(color).mix("white", 0.04));
|
const borderLeftColor = $derived(chroma(color).mix("white", 0.04));
|
||||||
|
@ -25,6 +26,10 @@
|
||||||
style:--book-width={width + "px"}
|
style:--book-width={width + "px"}
|
||||||
style:width={width + "px"}
|
style:width={width + "px"}
|
||||||
style:color={textColor}
|
style:color={textColor}
|
||||||
|
onclick={onClick}
|
||||||
|
onkeydown={(ev) => ev.key === "Enter" && onClick?.()}
|
||||||
|
role="link"
|
||||||
|
tabindex="0"
|
||||||
>
|
>
|
||||||
{@render children?.()}
|
{@render children?.()}
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -7,9 +7,10 @@
|
||||||
children?: Snippet;
|
children?: Snippet;
|
||||||
color?: string;
|
color?: string;
|
||||||
width?: number;
|
width?: number;
|
||||||
|
onClick?: () => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
let { children, color = "green", width = 40 }: Props = $props();
|
let { children, color = "green", width = 40, onClick }: Props = $props();
|
||||||
|
|
||||||
const backgroundColor = $derived(chroma(color));
|
const backgroundColor = $derived(chroma(color));
|
||||||
const borderLeftColor = $derived(chroma(color).mix("white", 0.04));
|
const borderLeftColor = $derived(chroma(color).mix("white", 0.04));
|
||||||
|
@ -27,6 +28,10 @@
|
||||||
style:--book-width={width + "px"}
|
style:--book-width={width + "px"}
|
||||||
style:width={width + "px"}
|
style:width={width + "px"}
|
||||||
style:color={textColor}
|
style:color={textColor}
|
||||||
|
onclick={onClick}
|
||||||
|
onkeydown={(ev) => ev.key === "Enter" && onClick?.()}
|
||||||
|
role="link"
|
||||||
|
tabindex="0"
|
||||||
>
|
>
|
||||||
{@render children?.()}
|
{@render children?.()}
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -4,14 +4,19 @@
|
||||||
interface Props {
|
interface Props {
|
||||||
children?: Snippet;
|
children?: Snippet;
|
||||||
color?: string;
|
color?: string;
|
||||||
|
onClick?: () => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
let { children, color = "green" }: Props = $props();
|
let { children, color = "green", onClick }: Props = $props();
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<div
|
<div
|
||||||
class="bookshelf__book-wrapper bookshelf__book-onDisplay"
|
class="bookshelf__book-wrapper bookshelf__book-onDisplay"
|
||||||
style:--book-color={color}
|
style:--book-color={color}
|
||||||
|
onclick={onClick}
|
||||||
|
onkeydown={(ev) => ev.key === "Enter" && onClick?.()}
|
||||||
|
role="link"
|
||||||
|
tabindex="0"
|
||||||
>
|
>
|
||||||
{@render children?.()}
|
{@render children?.()}
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -7,9 +7,10 @@
|
||||||
children?: Snippet;
|
children?: Snippet;
|
||||||
color?: string;
|
color?: string;
|
||||||
width?: number;
|
width?: number;
|
||||||
|
onClick?: () => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
let { children, color = "green", width = 40 }: Props = $props();
|
let { children, color = "green", width = 40, onClick }: Props = $props();
|
||||||
|
|
||||||
const backgroundColor = $derived(chroma(color));
|
const backgroundColor = $derived(chroma(color));
|
||||||
const borderLeftColor = $derived(chroma(color).mix("white", 0.04));
|
const borderLeftColor = $derived(chroma(color).mix("white", 0.04));
|
||||||
|
@ -27,6 +28,10 @@
|
||||||
style:--book-width={width + "px"}
|
style:--book-width={width + "px"}
|
||||||
style:width={width + "px"}
|
style:width={width + "px"}
|
||||||
style:color={textColor}
|
style:color={textColor}
|
||||||
|
onclick={onClick}
|
||||||
|
onkeydown={(ev) => ev.key === "Enter" && onClick?.()}
|
||||||
|
role="link"
|
||||||
|
tabindex="0"
|
||||||
>
|
>
|
||||||
{@render children?.()}
|
{@render children?.()}
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -6,9 +6,10 @@
|
||||||
interface Props {
|
interface Props {
|
||||||
children?: Snippet;
|
children?: Snippet;
|
||||||
color?: string;
|
color?: string;
|
||||||
|
onClick?: () => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
let { children, color = "green" }: Props = $props();
|
let { children, color = "green", onClick }: Props = $props();
|
||||||
|
|
||||||
const borderLeftColor = $derived(chroma(color).mix("white", 0.04));
|
const borderLeftColor = $derived(chroma(color).mix("white", 0.04));
|
||||||
const borderRightColor = $derived(chroma(color).mix("black", 0.04));
|
const borderRightColor = $derived(chroma(color).mix("black", 0.04));
|
||||||
|
@ -22,6 +23,10 @@
|
||||||
style:--book-border-right-color={borderRightColor.css()}
|
style:--book-border-right-color={borderRightColor.css()}
|
||||||
style:--book-background-color={backgroundColor.css()}
|
style:--book-background-color={backgroundColor.css()}
|
||||||
style:color={textColor}
|
style:color={textColor}
|
||||||
|
onclick={onClick}
|
||||||
|
onkeydown={(ev) => ev.key === "Enter" && onClick?.()}
|
||||||
|
role="link"
|
||||||
|
tabindex="0"
|
||||||
>
|
>
|
||||||
{@render children?.()}
|
{@render children?.()}
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -6,9 +6,10 @@
|
||||||
interface Props {
|
interface Props {
|
||||||
children?: Snippet;
|
children?: Snippet;
|
||||||
color?: string;
|
color?: string;
|
||||||
|
onClick?: () => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
let { children, color = "green" }: Props = $props();
|
let { children, color = "green", onClick }: Props = $props();
|
||||||
|
|
||||||
const backgroundColor = $derived(chroma(color));
|
const backgroundColor = $derived(chroma(color));
|
||||||
const borderLeftColor = $derived(chroma(color).mix("white", 0.04));
|
const borderLeftColor = $derived(chroma(color).mix("white", 0.04));
|
||||||
|
@ -22,6 +23,10 @@
|
||||||
style:--book-border-left-color={borderLeftColor.css()}
|
style:--book-border-left-color={borderLeftColor.css()}
|
||||||
style:--book-border-right-color={borderRightColor.css()}
|
style:--book-border-right-color={borderRightColor.css()}
|
||||||
style:color={textColor}
|
style:color={textColor}
|
||||||
|
onclick={onClick}
|
||||||
|
onkeydown={(ev) => ev.key === "Enter" && onClick?.()}
|
||||||
|
role="link"
|
||||||
|
tabindex="0"
|
||||||
>
|
>
|
||||||
{@render children?.()}
|
{@render children?.()}
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -6,9 +6,10 @@
|
||||||
interface Props {
|
interface Props {
|
||||||
children?: Snippet;
|
children?: Snippet;
|
||||||
color?: string;
|
color?: string;
|
||||||
|
onClick?: () => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
let { children, color = "green" }: Props = $props();
|
let { children, color = "green", onClick }: Props = $props();
|
||||||
|
|
||||||
const backgroundColor = $derived(chroma(color));
|
const backgroundColor = $derived(chroma(color));
|
||||||
const borderLeftColor = $derived(chroma(color).mix("white", 0.04));
|
const borderLeftColor = $derived(chroma(color).mix("white", 0.04));
|
||||||
|
@ -24,6 +25,10 @@
|
||||||
style:--book-border-right-color={borderRightColor.css()}
|
style:--book-border-right-color={borderRightColor.css()}
|
||||||
style:--book-band-color={bandColor.css()}
|
style:--book-band-color={bandColor.css()}
|
||||||
style:color={textColor}
|
style:color={textColor}
|
||||||
|
onclick={onClick}
|
||||||
|
onkeydown={(ev) => ev.key === "Enter" && onClick?.()}
|
||||||
|
role="link"
|
||||||
|
tabindex="0"
|
||||||
>
|
>
|
||||||
{@render children?.()}
|
{@render children?.()}
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -6,9 +6,10 @@
|
||||||
interface Props {
|
interface Props {
|
||||||
children?: Snippet;
|
children?: Snippet;
|
||||||
color?: string;
|
color?: string;
|
||||||
|
onClick?: () => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
let { children, color = "green" }: Props = $props();
|
let { children, color = "green", onClick }: Props = $props();
|
||||||
|
|
||||||
const backgroundColor = $derived(chroma(color));
|
const backgroundColor = $derived(chroma(color));
|
||||||
const borderLeftColor = $derived(chroma(color).mix("white", 0.04));
|
const borderLeftColor = $derived(chroma(color).mix("white", 0.04));
|
||||||
|
@ -24,6 +25,10 @@
|
||||||
style:--book-border-right-color={borderRightColor.css()}
|
style:--book-border-right-color={borderRightColor.css()}
|
||||||
style:--book-band-color={bandColor.css()}
|
style:--book-band-color={bandColor.css()}
|
||||||
style:color={textColor}
|
style:color={textColor}
|
||||||
|
onclick={onClick}
|
||||||
|
onkeydown={(ev) => ev.key === "Enter" && onClick?.()}
|
||||||
|
role="link"
|
||||||
|
tabindex="0"
|
||||||
>
|
>
|
||||||
{@render children?.()}
|
{@render children?.()}
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -1,10 +1,11 @@
|
||||||
import { READ_STATE } from "@src/const";
|
import { STATUS_READ } from "@src/const";
|
||||||
import type { CachedMetadata, TFile } from "obsidian";
|
import type { CachedMetadata, TFile } from "obsidian";
|
||||||
import { getContext, setContext } from "svelte";
|
import { getContext, setContext } from "svelte";
|
||||||
import { getSettingsContext } from "./settings.svelte";
|
import { getSettingsContext } from "./settings.svelte";
|
||||||
import type BookTrackerPlugin from "@src/main";
|
import type BookTrackerPlugin from "@src/main";
|
||||||
import { createDateFilter, type DateFilterStore } from "./date-filter.svelte";
|
import { createDateFilter, type DateFilterStore } from "./date-filter.svelte";
|
||||||
import type { ReadingState } from "@src/types";
|
import type { ReadingState } from "@src/types";
|
||||||
|
import type { BookTrackerPluginSettings } from "@ui/settings";
|
||||||
|
|
||||||
export type FileMetadata = {
|
export type FileMetadata = {
|
||||||
file: TFile;
|
file: TFile;
|
||||||
|
@ -16,34 +17,45 @@ export type FileProperty = {
|
||||||
value: any;
|
value: any;
|
||||||
};
|
};
|
||||||
|
|
||||||
interface MetadataStore extends DateFilterStore {
|
export interface MetadataStore extends DateFilterStore {
|
||||||
get metadata(): FileMetadata[];
|
get metadata(): FileMetadata[];
|
||||||
|
|
||||||
destroy(): void;
|
destroy(): void;
|
||||||
}
|
}
|
||||||
export function createMetadata(
|
|
||||||
|
function getMetadata(
|
||||||
plugin: BookTrackerPlugin,
|
plugin: BookTrackerPlugin,
|
||||||
state: ReadingState = READ_STATE
|
settings: BookTrackerPluginSettings,
|
||||||
): MetadataStore {
|
state: ReadingState
|
||||||
const settingsStore = getSettingsContext();
|
): FileMetadata[] {
|
||||||
|
const metadata: FileMetadata[] = [];
|
||||||
let metadata: FileMetadata[] = $state([]);
|
|
||||||
|
|
||||||
$effect(() => {
|
|
||||||
const newMetadata: FileMetadata[] = [];
|
|
||||||
|
|
||||||
for (const file of plugin.app.vault.getMarkdownFiles()) {
|
for (const file of plugin.app.vault.getMarkdownFiles()) {
|
||||||
const frontmatter =
|
const frontmatter =
|
||||||
plugin.app.metadataCache.getFileCache(file)?.frontmatter ?? {};
|
plugin.app.metadataCache.getFileCache(file)?.frontmatter ?? {};
|
||||||
|
|
||||||
if (frontmatter[settingsStore.settings.statusProperty] !== state) {
|
if (frontmatter[settings.statusProperty] !== state) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
newMetadata.push({ file, frontmatter });
|
metadata.push({ file, frontmatter });
|
||||||
|
}
|
||||||
|
return metadata;
|
||||||
}
|
}
|
||||||
|
|
||||||
metadata = newMetadata;
|
export function createMetadata(
|
||||||
|
plugin: BookTrackerPlugin,
|
||||||
|
statusFilter: ReadingState = STATUS_READ
|
||||||
|
): MetadataStore {
|
||||||
|
const settingsStore = getSettingsContext();
|
||||||
|
const initialMetadata = getMetadata(
|
||||||
|
plugin,
|
||||||
|
settingsStore.settings,
|
||||||
|
statusFilter
|
||||||
|
);
|
||||||
|
let metadata: FileMetadata[] = $state(initialMetadata);
|
||||||
|
|
||||||
|
$effect(() => {
|
||||||
|
metadata = getMetadata(plugin, settingsStore.settings, statusFilter);
|
||||||
});
|
});
|
||||||
|
|
||||||
function onChanged(file: TFile, _data: string, cache: CachedMetadata) {
|
function onChanged(file: TFile, _data: string, cache: CachedMetadata) {
|
||||||
|
@ -57,7 +69,6 @@ export function createMetadata(
|
||||||
return f;
|
return f;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
plugin.registerEvent(plugin.app.metadataCache.on("changed", onChanged));
|
plugin.registerEvent(plugin.app.metadataCache.on("changed", onChanged));
|
||||||
|
|
||||||
function onDeleted(file: TFile) {
|
function onDeleted(file: TFile) {
|
||||||
|
@ -69,13 +80,17 @@ export function createMetadata(
|
||||||
() => metadata,
|
() => metadata,
|
||||||
(f) => {
|
(f) => {
|
||||||
// @ts-expect-error Moment is provided by Obsidian
|
// @ts-expect-error Moment is provided by Obsidian
|
||||||
return moment(f.frontmatter[settings.endDateProperty]);
|
return moment(
|
||||||
|
f.frontmatter[settingsStore.settings.endDateProperty]
|
||||||
|
);
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
get metadata() {
|
get metadata() {
|
||||||
return dateFilter.filteredData;
|
return statusFilter === STATUS_READ
|
||||||
|
? dateFilter.filteredData
|
||||||
|
: metadata;
|
||||||
},
|
},
|
||||||
get filterYear() {
|
get filterYear() {
|
||||||
return dateFilter.filterYear;
|
return dateFilter.filterYear;
|
||||||
|
|
|
@ -2,7 +2,7 @@ import type BookTrackerPlugin from "@src/main";
|
||||||
import { type BookTrackerSettings, DEFAULT_SETTINGS } from "../settings/types";
|
import { type BookTrackerSettings, DEFAULT_SETTINGS } from "../settings/types";
|
||||||
import { getContext, setContext } from "svelte";
|
import { getContext, setContext } from "svelte";
|
||||||
|
|
||||||
interface SettingsStore {
|
export interface SettingsStore {
|
||||||
settings: BookTrackerSettings;
|
settings: BookTrackerSettings;
|
||||||
load(): Promise<void>;
|
load(): Promise<void>;
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,3 +1,10 @@
|
||||||
|
export function randomFloat(min?: number, max?: number) {
|
||||||
|
const minVal = min === undefined && max === undefined ? 0 : min ?? 0;
|
||||||
|
const maxVal = max === undefined ? (min === undefined ? 1 : min) : max;
|
||||||
|
|
||||||
|
return Math.random() * (maxVal - minVal) + minVal;
|
||||||
|
}
|
||||||
|
|
||||||
export function randomInt(min: number, max: number) {
|
export function randomInt(min: number, max: number) {
|
||||||
return Math.floor(Math.random() * (max - min + 1)) + min;
|
return Math.floor(Math.random() * (max - min + 1)) + min;
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue