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 { RatingModal } from "@ui/modals";
|
||||
import type { ReadingLog } from "@utils/ReadingLog";
|
||||
import { READ_STATE } from "@src/const";
|
||||
import { STATUS_READ } from "@src/const";
|
||||
import { mkdirRecursive, dirname } from "@utils/fs";
|
||||
|
||||
export class LogReadingFinishedCommand extends EditorCheckCommand {
|
||||
|
@ -70,7 +70,7 @@ export class LogReadingFinishedCommand extends EditorCheckCommand {
|
|||
const endDate = moment().format("YYYY-MM-DD");
|
||||
|
||||
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.ratingProperty] = ratings.rating;
|
||||
if (this.settings.spiceProperty !== "") {
|
||||
|
|
|
@ -7,7 +7,7 @@ import {
|
|||
} from "obsidian";
|
||||
import { EditorCheckCommand } from "./Command";
|
||||
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 {
|
||||
constructor(
|
||||
|
@ -38,7 +38,7 @@ export class LogReadingStartedCommand extends EditorCheckCommand {
|
|||
const startDate = moment().format("YYYY-MM-DD");
|
||||
|
||||
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;
|
||||
});
|
||||
|
||||
|
|
|
@ -8,7 +8,7 @@ import {
|
|||
import { EditorCheckCommand } from "./Command";
|
||||
import type { BookTrackerPluginSettings } from "@ui/settings";
|
||||
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 {
|
||||
constructor(
|
||||
|
@ -38,7 +38,7 @@ export class ResetReadingStatusCommand extends EditorCheckCommand {
|
|||
const file = ctx.file!;
|
||||
|
||||
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.endDateProperty] = "";
|
||||
});
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
export const TO_BE_READ_STATE = "To Be Read";
|
||||
export const IN_PROGRESS_STATE = "Currently Reading";
|
||||
export const READ_STATE = "Read";
|
||||
export const STATUS_TO_BE_READ = "To Be Read";
|
||||
export const STATUS_IN_PROGRESS = "Currently Reading";
|
||||
export const STATUS_READ = "Read";
|
||||
|
||||
export const CONTENT_TYPE_EXTENSIONS: Record<string, string> = {
|
||||
"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 {
|
||||
name: string;
|
||||
|
@ -24,8 +28,8 @@ export interface Book {
|
|||
isbn13: string;
|
||||
}
|
||||
|
||||
export type ToBeReadState = typeof TO_BE_READ_STATE;
|
||||
export type InProgressState = typeof IN_PROGRESS_STATE;
|
||||
export type ReadState = typeof READ_STATE;
|
||||
export type ToBeReadState = typeof STATUS_TO_BE_READ;
|
||||
export type InProgressState = typeof STATUS_IN_PROGRESS;
|
||||
export type ReadState = typeof STATUS_READ;
|
||||
|
||||
export type ReadingState = ToBeReadState | InProgressState | ReadState;
|
||||
|
|
|
@ -2,6 +2,7 @@ import { registerCodeBlockRenderer } from ".";
|
|||
import { SvelteCodeBlockRenderer } from "./SvelteCodeBlockRenderer";
|
||||
import BookshelfCodeBlockView from "./BookshelfCodeBlockView.svelte";
|
||||
import type BookTrackerPlugin from "@src/main";
|
||||
import z from "zod/v4";
|
||||
|
||||
export function registerBookshelfCodeBlockProcessor(
|
||||
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<
|
||||
typeof BookshelfCodeBlockView
|
||||
> {
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
<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 { ReadingState } from "@src/types";
|
||||
import Book from "@ui/components/bookshelf/Book.svelte";
|
||||
|
@ -9,26 +9,42 @@
|
|||
import {
|
||||
createMetadata,
|
||||
setMetadataContext,
|
||||
type FileMetadata,
|
||||
} from "@ui/stores/metadata.svelte";
|
||||
import {
|
||||
createSettings,
|
||||
setSettingsContext,
|
||||
} from "@ui/stores/settings.svelte";
|
||||
import { COLOR_NAMES } from "@utils/color";
|
||||
import { randomElement } from "@utils/rand";
|
||||
import { COLOR_NAMES, type ColorName } from "@utils/color";
|
||||
import { randomElement, randomFloat, randomInt } from "@utils/rand";
|
||||
import { onDestroy } from "svelte";
|
||||
import { BookshelfSettingsSchema } from "./BookshelfCodeBlock";
|
||||
import { parseYaml, TFile } from "obsidian";
|
||||
|
||||
interface Props {
|
||||
plugin: BookTrackerPlugin;
|
||||
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);
|
||||
setSettingsContext(settingsStore);
|
||||
|
||||
let stateFilter: ReadingState = $state(READ_STATE);
|
||||
let stateFilter: ReadingState = $state(STATUS_TO_BE_READ);
|
||||
|
||||
const metadataStore = createMetadata(plugin, stateFilter);
|
||||
setMetadataContext(metadataStore);
|
||||
|
@ -40,155 +56,113 @@
|
|||
"split-bands",
|
||||
] as const;
|
||||
|
||||
function randomDesign() {
|
||||
return randomElement(designs);
|
||||
const randomDesign = () => 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() {
|
||||
return randomElement(COLOR_NAMES);
|
||||
}
|
||||
const books = $derived.by(() => {
|
||||
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());
|
||||
</script>
|
||||
|
||||
<Bookshelf>
|
||||
<Book
|
||||
title="Hello World"
|
||||
width={1120}
|
||||
color={randomColor()}
|
||||
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
|
||||
title="Coding for Dummies"
|
||||
subtitle="JS tutorial"
|
||||
color={randomColor()}
|
||||
design={randomDesign()}
|
||||
/>
|
||||
<BookStackElement
|
||||
title="Coding for Dummies"
|
||||
subtitle="C# tutorial"
|
||||
color={randomColor()}
|
||||
design={randomDesign()}
|
||||
/>
|
||||
</BookStack>
|
||||
<Book
|
||||
title="CoffeeScript"
|
||||
subtitle="The JS Alternative"
|
||||
author="The Dev Guy"
|
||||
color={randomColor()}
|
||||
design={randomDesign()}
|
||||
orientation="on-display"
|
||||
/>
|
||||
<Book
|
||||
title="Cheat Sheet"
|
||||
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>
|
||||
{#each books as book}
|
||||
{#if Array.isArray(book)}
|
||||
<BookStack totalChildren={book.length}>
|
||||
{#each book as bookData}
|
||||
<BookStackElement
|
||||
title={bookData.title}
|
||||
subtitle={bookData.subtitle}
|
||||
color={bookData.color}
|
||||
design={bookData.design}
|
||||
onClick={() =>
|
||||
plugin.app.workspace.openLinkText(
|
||||
bookData.file.path,
|
||||
"",
|
||||
true,
|
||||
)}
|
||||
/>
|
||||
{/each}
|
||||
</BookStack>
|
||||
{:else}
|
||||
<Book
|
||||
title={book.title}
|
||||
subtitle={book.subtitle}
|
||||
author={book.author}
|
||||
width={book.width}
|
||||
color={book.color}
|
||||
design={book.design}
|
||||
orientation={book.orientation}
|
||||
onClick={() =>
|
||||
plugin.app.workspace.openLinkText(book.file.path, "", true)}
|
||||
/>
|
||||
{/if}
|
||||
{/each}
|
||||
</Bookshelf>
|
||||
|
|
|
@ -6,6 +6,7 @@
|
|||
import { createReadingLog } from "@ui/stores/reading-log.svelte";
|
||||
import { ALL_TIME } from "@ui/stores/date-filter.svelte";
|
||||
import { onDestroy } from "svelte";
|
||||
import { getLinkpath } from "obsidian";
|
||||
|
||||
interface Props {
|
||||
plugin: BookTrackerPlugin;
|
||||
|
@ -14,9 +15,7 @@
|
|||
const { plugin }: Props = $props();
|
||||
|
||||
function bookUri(book: string) {
|
||||
const v = encodeURIComponent(plugin.app.vault.getName());
|
||||
const f = encodeURIComponent(book + ".md");
|
||||
return `obsidian://open?vault=${v}&file=${f}`;
|
||||
return getLinkpath(book + ".md");
|
||||
}
|
||||
|
||||
const store = createReadingLog(plugin.readingLog);
|
||||
|
|
|
@ -26,6 +26,7 @@
|
|||
orientation?: "tilted" | "on-display";
|
||||
height?: number;
|
||||
width?: number;
|
||||
onClick?: () => void;
|
||||
}
|
||||
|
||||
let {
|
||||
|
@ -37,6 +38,7 @@
|
|||
orientation,
|
||||
height,
|
||||
width,
|
||||
onClick,
|
||||
}: BookProps = $props();
|
||||
|
||||
function widthCheck(input: number | undefined) {
|
||||
|
@ -69,10 +71,11 @@
|
|||
{design}
|
||||
{height}
|
||||
{width}
|
||||
{onClick}
|
||||
/>
|
||||
</BookTiltedDefault>
|
||||
{:else if orientation === "on-display"}
|
||||
<BookOnDisplay color={color.hex}>
|
||||
<BookOnDisplay color={color.hex} {onClick}>
|
||||
<div
|
||||
class="book-display-crease"
|
||||
style:--book-color={color.hex}
|
||||
|
@ -84,19 +87,19 @@
|
|||
</BookOnDisplay>
|
||||
{/if}
|
||||
{:else if design === "split-bands"}
|
||||
<BookSplitBands color={color.hex} width={verifiedWidth}>
|
||||
<BookSplitBands color={color.hex} width={verifiedWidth} {onClick}>
|
||||
<BookText {title} {subtitle} />
|
||||
</BookSplitBands>
|
||||
{:else if design === "dual-top-bands"}
|
||||
<BookDualTopBands color={color.hex} width={verifiedWidth}>
|
||||
<BookDualTopBands color={color.hex} width={verifiedWidth} {onClick}>
|
||||
<BookText {title} {subtitle} />
|
||||
</BookDualTopBands>
|
||||
{:else if design === "colored-spine"}
|
||||
<BookColoredSpine color={color.hex} width={verifiedWidth}>
|
||||
<BookColoredSpine color={color.hex} width={verifiedWidth} {onClick}>
|
||||
<BookText {title} {subtitle} />
|
||||
</BookColoredSpine>
|
||||
{:else}
|
||||
<BookDefault color={color.hex} width={verifiedWidth}>
|
||||
<BookDefault color={color.hex} width={verifiedWidth} {onClick}>
|
||||
<BookText {title} {subtitle} />
|
||||
</BookDefault>
|
||||
{/if}
|
||||
|
|
|
@ -11,6 +11,7 @@
|
|||
subtitle?: string;
|
||||
color?: ColorName | string;
|
||||
design?: "default" | "split-bands" | "dual-top-bands" | "colored-spine";
|
||||
onClick?: () => void;
|
||||
}
|
||||
|
||||
let {
|
||||
|
@ -18,6 +19,7 @@
|
|||
subtitle,
|
||||
color: colorRaw = "green",
|
||||
design,
|
||||
onClick,
|
||||
}: Props = $props();
|
||||
|
||||
const color = $derived(
|
||||
|
@ -29,19 +31,19 @@
|
|||
|
||||
<li class="bookshelf__bookstack-elem">
|
||||
{#if design === "split-bands"}
|
||||
<BookStackSplitBands color={color.hex}>
|
||||
<BookStackSplitBands color={color.hex} {onClick}>
|
||||
<BookText {title} {subtitle} />
|
||||
</BookStackSplitBands>
|
||||
{:else if design === "dual-top-bands"}
|
||||
<BookStackDualTopBands color={color.hex}>
|
||||
<BookStackDualTopBands color={color.hex} {onClick}>
|
||||
<BookText {title} {subtitle} />
|
||||
</BookStackDualTopBands>
|
||||
{:else if design === "colored-spine"}
|
||||
<BookStackColoredSpine color={color.hex}>
|
||||
<BookStackColoredSpine color={color.hex} {onClick}>
|
||||
<BookText {title} {subtitle} />
|
||||
</BookStackColoredSpine>
|
||||
{:else}
|
||||
<BookStackDefault color={color.hex}>
|
||||
<BookStackDefault color={color.hex} {onClick}>
|
||||
<BookText {title} {subtitle} />
|
||||
</BookStackDefault>
|
||||
{/if}
|
||||
|
|
|
@ -19,6 +19,7 @@ $bookEdge: 2px;
|
|||
display: inline-flex;
|
||||
flex-flow: column nowrap;
|
||||
list-style: none;
|
||||
float: left;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
|
||||
|
|
|
@ -7,9 +7,10 @@
|
|||
children?: Snippet;
|
||||
color?: string;
|
||||
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 borderRightColor = $derived(chroma(color).mix("black", 0.04));
|
||||
|
@ -26,6 +27,10 @@
|
|||
style:--book-width={width + "px"}
|
||||
style:width={width + "px"}
|
||||
style:color={textColor}
|
||||
onclick={onClick}
|
||||
onkeydown={(ev) => ev.key === "Enter" && onClick?.()}
|
||||
role="link"
|
||||
tabindex="0"
|
||||
>
|
||||
{@render children?.()}
|
||||
</div>
|
||||
|
|
|
@ -7,9 +7,10 @@
|
|||
children?: Snippet;
|
||||
color?: string;
|
||||
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 borderLeftColor = $derived(chroma(color).mix("white", 0.04));
|
||||
|
@ -25,6 +26,10 @@
|
|||
style:--book-width={width + "px"}
|
||||
style:width={width + "px"}
|
||||
style:color={textColor}
|
||||
onclick={onClick}
|
||||
onkeydown={(ev) => ev.key === "Enter" && onClick?.()}
|
||||
role="link"
|
||||
tabindex="0"
|
||||
>
|
||||
{@render children?.()}
|
||||
</div>
|
||||
|
|
|
@ -7,9 +7,10 @@
|
|||
children?: Snippet;
|
||||
color?: string;
|
||||
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 borderLeftColor = $derived(chroma(color).mix("white", 0.04));
|
||||
|
@ -27,6 +28,10 @@
|
|||
style:--book-width={width + "px"}
|
||||
style:width={width + "px"}
|
||||
style:color={textColor}
|
||||
onclick={onClick}
|
||||
onkeydown={(ev) => ev.key === "Enter" && onClick?.()}
|
||||
role="link"
|
||||
tabindex="0"
|
||||
>
|
||||
{@render children?.()}
|
||||
</div>
|
||||
|
|
|
@ -4,14 +4,19 @@
|
|||
interface Props {
|
||||
children?: Snippet;
|
||||
color?: string;
|
||||
onClick?: () => void;
|
||||
}
|
||||
|
||||
let { children, color = "green" }: Props = $props();
|
||||
let { children, color = "green", onClick }: Props = $props();
|
||||
</script>
|
||||
|
||||
<div
|
||||
class="bookshelf__book-wrapper bookshelf__book-onDisplay"
|
||||
style:--book-color={color}
|
||||
onclick={onClick}
|
||||
onkeydown={(ev) => ev.key === "Enter" && onClick?.()}
|
||||
role="link"
|
||||
tabindex="0"
|
||||
>
|
||||
{@render children?.()}
|
||||
</div>
|
||||
|
|
|
@ -7,9 +7,10 @@
|
|||
children?: Snippet;
|
||||
color?: string;
|
||||
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 borderLeftColor = $derived(chroma(color).mix("white", 0.04));
|
||||
|
@ -27,6 +28,10 @@
|
|||
style:--book-width={width + "px"}
|
||||
style:width={width + "px"}
|
||||
style:color={textColor}
|
||||
onclick={onClick}
|
||||
onkeydown={(ev) => ev.key === "Enter" && onClick?.()}
|
||||
role="link"
|
||||
tabindex="0"
|
||||
>
|
||||
{@render children?.()}
|
||||
</div>
|
||||
|
|
|
@ -6,9 +6,10 @@
|
|||
interface Props {
|
||||
children?: Snippet;
|
||||
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 borderRightColor = $derived(chroma(color).mix("black", 0.04));
|
||||
|
@ -22,6 +23,10 @@
|
|||
style:--book-border-right-color={borderRightColor.css()}
|
||||
style:--book-background-color={backgroundColor.css()}
|
||||
style:color={textColor}
|
||||
onclick={onClick}
|
||||
onkeydown={(ev) => ev.key === "Enter" && onClick?.()}
|
||||
role="link"
|
||||
tabindex="0"
|
||||
>
|
||||
{@render children?.()}
|
||||
</div>
|
||||
|
|
|
@ -6,9 +6,10 @@
|
|||
interface Props {
|
||||
children?: Snippet;
|
||||
color?: string;
|
||||
onClick?: () => void;
|
||||
}
|
||||
|
||||
let { children, color = "green" }: Props = $props();
|
||||
let { children, color = "green", onClick }: Props = $props();
|
||||
|
||||
const backgroundColor = $derived(chroma(color));
|
||||
const borderLeftColor = $derived(chroma(color).mix("white", 0.04));
|
||||
|
@ -22,6 +23,10 @@
|
|||
style:--book-border-left-color={borderLeftColor.css()}
|
||||
style:--book-border-right-color={borderRightColor.css()}
|
||||
style:color={textColor}
|
||||
onclick={onClick}
|
||||
onkeydown={(ev) => ev.key === "Enter" && onClick?.()}
|
||||
role="link"
|
||||
tabindex="0"
|
||||
>
|
||||
{@render children?.()}
|
||||
</div>
|
||||
|
|
|
@ -6,9 +6,10 @@
|
|||
interface Props {
|
||||
children?: Snippet;
|
||||
color?: string;
|
||||
onClick?: () => void;
|
||||
}
|
||||
|
||||
let { children, color = "green" }: Props = $props();
|
||||
let { children, color = "green", onClick }: Props = $props();
|
||||
|
||||
const backgroundColor = $derived(chroma(color));
|
||||
const borderLeftColor = $derived(chroma(color).mix("white", 0.04));
|
||||
|
@ -24,6 +25,10 @@
|
|||
style:--book-border-right-color={borderRightColor.css()}
|
||||
style:--book-band-color={bandColor.css()}
|
||||
style:color={textColor}
|
||||
onclick={onClick}
|
||||
onkeydown={(ev) => ev.key === "Enter" && onClick?.()}
|
||||
role="link"
|
||||
tabindex="0"
|
||||
>
|
||||
{@render children?.()}
|
||||
</div>
|
||||
|
|
|
@ -6,9 +6,10 @@
|
|||
interface Props {
|
||||
children?: Snippet;
|
||||
color?: string;
|
||||
onClick?: () => void;
|
||||
}
|
||||
|
||||
let { children, color = "green" }: Props = $props();
|
||||
let { children, color = "green", onClick }: Props = $props();
|
||||
|
||||
const backgroundColor = $derived(chroma(color));
|
||||
const borderLeftColor = $derived(chroma(color).mix("white", 0.04));
|
||||
|
@ -24,6 +25,10 @@
|
|||
style:--book-border-right-color={borderRightColor.css()}
|
||||
style:--book-band-color={bandColor.css()}
|
||||
style:color={textColor}
|
||||
onclick={onClick}
|
||||
onkeydown={(ev) => ev.key === "Enter" && onClick?.()}
|
||||
role="link"
|
||||
tabindex="0"
|
||||
>
|
||||
{@render children?.()}
|
||||
</div>
|
||||
|
|
|
@ -1,10 +1,11 @@
|
|||
import { READ_STATE } from "@src/const";
|
||||
import { STATUS_READ } from "@src/const";
|
||||
import type { CachedMetadata, TFile } from "obsidian";
|
||||
import { getContext, setContext } from "svelte";
|
||||
import { getSettingsContext } from "./settings.svelte";
|
||||
import type BookTrackerPlugin from "@src/main";
|
||||
import { createDateFilter, type DateFilterStore } from "./date-filter.svelte";
|
||||
import type { ReadingState } from "@src/types";
|
||||
import type { BookTrackerPluginSettings } from "@ui/settings";
|
||||
|
||||
export type FileMetadata = {
|
||||
file: TFile;
|
||||
|
@ -16,34 +17,45 @@ export type FileProperty = {
|
|||
value: any;
|
||||
};
|
||||
|
||||
interface MetadataStore extends DateFilterStore {
|
||||
export interface MetadataStore extends DateFilterStore {
|
||||
get metadata(): FileMetadata[];
|
||||
|
||||
destroy(): void;
|
||||
}
|
||||
export function createMetadata(
|
||||
|
||||
function getMetadata(
|
||||
plugin: BookTrackerPlugin,
|
||||
state: ReadingState = READ_STATE
|
||||
): MetadataStore {
|
||||
const settingsStore = getSettingsContext();
|
||||
settings: BookTrackerPluginSettings,
|
||||
state: ReadingState
|
||||
): FileMetadata[] {
|
||||
const metadata: FileMetadata[] = [];
|
||||
for (const file of plugin.app.vault.getMarkdownFiles()) {
|
||||
const frontmatter =
|
||||
plugin.app.metadataCache.getFileCache(file)?.frontmatter ?? {};
|
||||
|
||||
let metadata: FileMetadata[] = $state([]);
|
||||
|
||||
$effect(() => {
|
||||
const newMetadata: FileMetadata[] = [];
|
||||
|
||||
for (const file of plugin.app.vault.getMarkdownFiles()) {
|
||||
const frontmatter =
|
||||
plugin.app.metadataCache.getFileCache(file)?.frontmatter ?? {};
|
||||
|
||||
if (frontmatter[settingsStore.settings.statusProperty] !== state) {
|
||||
continue;
|
||||
}
|
||||
|
||||
newMetadata.push({ file, frontmatter });
|
||||
if (frontmatter[settings.statusProperty] !== state) {
|
||||
continue;
|
||||
}
|
||||
|
||||
metadata = newMetadata;
|
||||
metadata.push({ file, frontmatter });
|
||||
}
|
||||
return metadata;
|
||||
}
|
||||
|
||||
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) {
|
||||
|
@ -57,7 +69,6 @@ export function createMetadata(
|
|||
return f;
|
||||
});
|
||||
}
|
||||
|
||||
plugin.registerEvent(plugin.app.metadataCache.on("changed", onChanged));
|
||||
|
||||
function onDeleted(file: TFile) {
|
||||
|
@ -69,13 +80,17 @@ export function createMetadata(
|
|||
() => metadata,
|
||||
(f) => {
|
||||
// @ts-expect-error Moment is provided by Obsidian
|
||||
return moment(f.frontmatter[settings.endDateProperty]);
|
||||
return moment(
|
||||
f.frontmatter[settingsStore.settings.endDateProperty]
|
||||
);
|
||||
}
|
||||
);
|
||||
|
||||
return {
|
||||
get metadata() {
|
||||
return dateFilter.filteredData;
|
||||
return statusFilter === STATUS_READ
|
||||
? dateFilter.filteredData
|
||||
: metadata;
|
||||
},
|
||||
get filterYear() {
|
||||
return dateFilter.filterYear;
|
||||
|
|
|
@ -2,7 +2,7 @@ import type BookTrackerPlugin from "@src/main";
|
|||
import { type BookTrackerSettings, DEFAULT_SETTINGS } from "../settings/types";
|
||||
import { getContext, setContext } from "svelte";
|
||||
|
||||
interface SettingsStore {
|
||||
export interface SettingsStore {
|
||||
settings: BookTrackerSettings;
|
||||
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) {
|
||||
return Math.floor(Math.random() * (max - min + 1)) + min;
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue