obsidian-book-tracker/src/ui/components/charts/BookAndPages.svelte

129 lines
3.2 KiB
Svelte

<script lang="ts">
import { chart } from "@ui/directives/chart";
import { ALL_TIME } from "@ui/stores/date-filter.svelte";
import { getMetadataContext } from "@ui/stores/metadata.svelte";
import { getReadingLogContext } from "@ui/stores/reading-log.svelte";
import { getSettingsContext } from "@ui/stores/settings.svelte";
import { Color } from "@utils/color";
import type { ChartConfiguration } from "chart.js";
const settingsStore = getSettingsContext();
const store = getMetadataContext();
const readingLog = getReadingLogContext();
const isMonthly = $derived(
store.filterYear !== ALL_TIME && store.filterMonth !== ALL_TIME,
);
const items = $derived(
isMonthly
? readingLog.entries.map((entry) => ({
pageCount: entry.pagesRead,
date: entry.createdAt,
}))
: store.metadata.map((f) => ({
pageCount:
f.frontmatter[settingsStore.settings.pageCountProperty],
// @ts-expect-error Moment is provided by Obsidian
date: moment(
f.frontmatter[settingsStore.settings.endDateProperty],
),
})),
);
const config = $derived.by(() => {
const books = new Map<number, number>();
const pages = new Map<number, number>();
for (const item of items) {
let key: number;
if (store.filterYear === ALL_TIME) {
key = item.date.year();
} else if (store.filterMonth === ALL_TIME) {
key = item.date.month();
} else {
key = item.date.date();
}
const pageCount = pages.get(key) ?? 0;
pages.set(key, pageCount + item.pageCount);
const bookCount = books.get(key) ?? 0;
books.set(key, bookCount + 1);
}
if (isMonthly && typeof store.filterMonth === "number") {
// @ts-expect-error Moment is provided by Obsidian
const daysInMonth = moment()
.month(store.filterMonth - 1)
.daysInMonth();
for (let i = 1; i <= daysInMonth; i++) {
if (!pages.has(i)) {
books.set(i, 0);
pages.set(i, 0);
}
}
}
const labels = Array.from(books.keys())
.sort((a, b) => a - b)
.map((key) =>
store.filterYear === ALL_TIME || isMonthly
? key
: // @ts-expect-error Moment is provided by Obsidian
moment().month(key).format("MMM"),
);
const sortedBooks = Array.from(books.entries())
.sort((a, b) => a[0] - b[0])
.map((b) => b[1]);
const sortedPages = Array.from(pages.entries())
.sort((a, b) => a[0] - b[0])
.map((p) => p[1]);
let datasets = [
{
label: "Pages",
data: sortedPages,
borderColor: Color.fromName("blue").hex,
backgroundColor: Color.fromName("blue").alpha(0.5).rgba,
yAxisID: isMonthly ? "y" : "y1",
},
];
if (!isMonthly) {
datasets.push({
label: "Books",
data: sortedBooks,
borderColor: Color.fromName("red").hex,
backgroundColor: Color.fromName("red").alpha(0.5).rgba,
yAxisID: "y",
});
}
return {
type: "line",
data: {
labels,
datasets,
},
options: {
scales: {
y: {
type: "linear",
display: true,
position: "left",
ticks: { beginAtZero: true },
},
y1: {
type: "linear",
display: !isMonthly,
position: "right",
grid: { drawOnChartArea: false },
},
},
},
} as ChartConfiguration;
});
</script>
<canvas use:chart={config}></canvas>