generated from tpl/obsidian-sample-plugin
186 lines
4.1 KiB
Svelte
186 lines
4.1 KiB
Svelte
<script lang="ts">
|
|
import type { ReadingLogEntry } from "@utils/ReadingLog";
|
|
import { Edit, Trash, Plus } from "lucide-svelte";
|
|
import { ReadingLogEntryEditModal } from "@ui/modals";
|
|
import type BookTrackerPlugin from "@src/main";
|
|
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;
|
|
}
|
|
|
|
const { plugin }: Props = $props();
|
|
|
|
function bookUri(book: string) {
|
|
return getLinkpath(book + ".md");
|
|
}
|
|
|
|
const store = createReadingLog(plugin.readingLog);
|
|
onDestroy(() => store.destroy());
|
|
|
|
function createEntry() {
|
|
const modal = new ReadingLogEntryEditModal(plugin, async (entry) => {
|
|
modal.close();
|
|
await store.addEntry(entry);
|
|
});
|
|
modal.open();
|
|
}
|
|
|
|
function editEntry(entry: ReadingLogEntry) {
|
|
const modal = new ReadingLogEntryEditModal(
|
|
plugin,
|
|
async (entry) => {
|
|
modal.close();
|
|
await store.updateEntry(entry);
|
|
},
|
|
entry,
|
|
);
|
|
modal.open();
|
|
}
|
|
|
|
async function removeEntry(entry: ReadingLogEntry) {
|
|
await store.removeEntry(entry);
|
|
}
|
|
</script>
|
|
|
|
<div class="obt-reading-log-viewer">
|
|
<div class="controls">
|
|
<div class="left">
|
|
<select class="year-filter" bind:value={store.filterYear}>
|
|
{#each store.filterYears as year}
|
|
<option value={year}>{year}</option>
|
|
{/each}
|
|
<option value={ALL_TIME}>All Time</option>
|
|
</select>
|
|
{#if store.filterYear !== ALL_TIME}
|
|
<select class="month-filter" bind:value={store.filterMonth}>
|
|
<option value={ALL_TIME}>Select Month</option>
|
|
<option value={1}>January</option>
|
|
<option value={2}>February</option>
|
|
<option value={3}>March</option>
|
|
<option value={4}>April</option>
|
|
<option value={5}>May</option>
|
|
<option value={6}>June</option>
|
|
<option value={7}>July</option>
|
|
<option value={8}>August</option>
|
|
<option value={9}>September</option>
|
|
<option value={10}>October</option>
|
|
<option value={11}>November</option>
|
|
<option value={12}>December</option>
|
|
</select>
|
|
{/if}
|
|
</div>
|
|
<div class="right">
|
|
<button onclick={createEntry} class="create-entry" type="button">
|
|
<Plus />
|
|
Create
|
|
</button>
|
|
</div>
|
|
</div>
|
|
|
|
<table>
|
|
<thead>
|
|
<tr>
|
|
<th class="date">Date</th>
|
|
<th class="book">Book</th>
|
|
<th class="pages-read">Pages Read</th>
|
|
<th class="percent-complete">Percent Complete</th>
|
|
<th class="actions">Actions</th>
|
|
</tr>
|
|
</thead>
|
|
<tbody>
|
|
{#each store.entries as entry}
|
|
<tr>
|
|
<td class="date">{entry.createdAt.format("YYYY-MM-DD")}</td>
|
|
<td class="book"
|
|
><a href={bookUri(entry.book)}>{entry.book}</a></td
|
|
>
|
|
<td class="pages-read">{entry.pagesRead}</td>
|
|
<td class="percent-complete">
|
|
{Math.round(
|
|
(entry.pagesReadTotal /
|
|
(entry.pagesReadTotal + entry.pagesRemaining)) *
|
|
100,
|
|
)}%
|
|
</td>
|
|
<td class="actions">
|
|
<button onclick={() => editEntry(entry)}>
|
|
<Edit />
|
|
<span>Edit</span>
|
|
</button>
|
|
<button onclick={() => removeEntry(entry)}>
|
|
<Trash />
|
|
<span>Delete</span>
|
|
</button>
|
|
</td>
|
|
</tr>
|
|
{:else}
|
|
<tr>
|
|
<td colspan="5" class="no-entries">No entries found</td>
|
|
</tr>
|
|
{/each}
|
|
</tbody>
|
|
</table>
|
|
</div>
|
|
|
|
<!-- svelte-ignore css_unused_selector -->
|
|
<style lang="scss">
|
|
@use "../styles/utils";
|
|
|
|
.obt-reading-log-viewer {
|
|
.controls {
|
|
display: grid;
|
|
grid-template-columns: 1fr 1fr;
|
|
align-items: center;
|
|
|
|
.right {
|
|
text-align: right;
|
|
|
|
button.create-entry {
|
|
display: inline-flex;
|
|
gap: var(--size-2-2);
|
|
align-items: center;
|
|
|
|
:global(svg) {
|
|
width: var(--icon-s);
|
|
height: var(--icon-s);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
table {
|
|
width: 100%;
|
|
|
|
td.book {
|
|
width: 100%;
|
|
}
|
|
|
|
th,
|
|
td:not(.book) {
|
|
white-space: nowrap;
|
|
}
|
|
|
|
td.pages-read,
|
|
td.percent-complete {
|
|
text-align: center;
|
|
}
|
|
|
|
td.actions span {
|
|
@include utils.visually-hidden;
|
|
}
|
|
|
|
td.no-entries {
|
|
text-align: center;
|
|
font-size: 1.5rem;
|
|
font-weight: var(--bold-weight);
|
|
font-style: italic;
|
|
padding: var(--size-4-6);
|
|
}
|
|
}
|
|
}
|
|
</style>
|