Add entry create button

This commit is contained in:
Evan Fiordeliso 2025-06-29 18:41:54 -04:00
parent 6ffad2b5d1
commit bd21890885
8 changed files with 192 additions and 34 deletions

View File

@ -30,6 +30,7 @@
"sass": "^1.89.2",
"svelte": "^5.34.8",
"svelte-check": "^4.2.2",
"svelte-popperjs": "^1.3.2",
"svelte-preprocess": "^6.0.3",
"tslib": "2.4.0",
"typescript": "5.0.4"

View File

@ -59,6 +59,9 @@ importers:
svelte-check:
specifier: ^4.2.2
version: 4.2.2(picomatch@4.0.2)(svelte@5.34.8)(typescript@5.0.4)
svelte-popperjs:
specifier: ^1.3.2
version: 1.3.2(@popperjs/core@2.11.8)(svelte@5.34.8)
svelte-preprocess:
specifier: ^6.0.3
version: 6.0.3(postcss@8.5.6)(sass@1.89.2)(svelte@5.34.8)(typescript@5.0.4)
@ -1444,6 +1447,12 @@ packages:
svelte: ^4.0.0 || ^5.0.0-next.0
typescript: '>=5.0.0'
svelte-popperjs@1.3.2:
resolution: {integrity: sha512-fwrErlkvngL876WXRnL3OLlfk/n9YkZwwLxuKRpZOYCJLt1zrwhoKTXS+/sRgDveD/zd6GQ35hV89EOip+NBGA==}
peerDependencies:
'@popperjs/core': '>=2'
svelte: '>=3'
svelte-preprocess@6.0.3:
resolution: {integrity: sha512-PLG2k05qHdhmRG7zR/dyo5qKvakhm8IJ+hD2eFRQmMLHp7X3eJnjeupUtvuRpbNiF31RjVw45W+abDwHEmP5OA==}
engines: {node: '>= 18.0.0'}
@ -3050,6 +3059,11 @@ snapshots:
transitivePeerDependencies:
- picomatch
svelte-popperjs@1.3.2(@popperjs/core@2.11.8)(svelte@5.34.8):
dependencies:
'@popperjs/core': 2.11.8
svelte: 5.34.8
svelte-preprocess@6.0.3(postcss@8.5.6)(sass@1.89.2)(svelte@5.34.8)(typescript@5.0.4):
dependencies:
svelte: 5.34.8

View File

@ -1,15 +1,21 @@
<script lang="ts">
import type { ReadingLogEntry } from "@src/types";
import type { App } from "obsidian";
import { BookSuggest } from "@settings/suggesters/book";
interface Props {
entry: ReadingLogEntry;
onSubmit: (entry: ReadingLogEntry) => void;
app: App;
entry?: ReadingLogEntry;
onSubmit?: (entry: ReadingLogEntry) => void;
}
let { entry, onSubmit }: Props = $props();
let { app, entry, onSubmit }: Props = $props();
let pagesRead = $state(entry.pagesRead);
let pagesReadTotal = $state(entry.pagesReadTotal);
let pagesRemaining = $state(entry.pagesRemaining);
let editMode = $derived(entry !== undefined);
let book = $state(entry?.book ?? "");
let pagesRead = $state(entry?.pagesRead ?? 0);
let pagesReadTotal = $state(entry?.pagesReadTotal ?? 0);
let pagesRemaining = $state(entry?.pagesRemaining ?? 0);
let createdAt = $state(entry?.createdAt ?? new Date());
// Source: https://github.com/sveltejs/svelte/discussions/14220#discussioncomment-11188219
function watch<T>(
@ -40,19 +46,33 @@
function onsubmit(ev: SubmitEvent) {
ev.preventDefault();
onSubmit({
...entry,
onSubmit?.({
book,
pagesRead,
pagesReadTotal,
pagesRemaining,
createdAt: new Date(createdAt),
});
}
function bookSuggest(el: HTMLInputElement) {
new BookSuggest(app, el);
}
</script>
<div class="obt-reading-log-entry-editor">
<h2>Edit Reading Log Entry</h2>
<h2>{editMode ? "Edit" : "Create"} Reading Log Entry</h2>
<form {onsubmit}>
<div class="fields">
<label for="book">Book</label>
<input
type="text"
name="book"
id="book"
bind:value={book}
disabled={editMode}
use:bookSuggest
/>
<label for="pagesRead">Pages Read</label>
<input
type="number"
@ -74,6 +94,14 @@
id="pagesRemaining"
bind:value={pagesRemaining}
/>
<label for="createdAt">Created At</label>
<input
type="datetime-local"
name="createdAt"
id="createdAt"
bind:value={createdAt}
disabled={editMode}
/>
</div>
<button type="submit">Submit</button>
</form>
@ -95,7 +123,13 @@
.fields {
display: grid;
grid-template-columns: max-content 1fr;
gap: var(--size-4-2);
gap: var(--size-4-4);
align-items: center;
input:disabled {
color: var(--text-muted);
cursor: not-allowed;
}
}
}
}

View File

@ -2,8 +2,9 @@
import type { ReadingLog } from "@utils/storage";
import type { ReadingLogEntry } from "@src/types";
import type { App } from "obsidian";
import { Edit, Trash } from "lucide-svelte";
import { Edit, Trash, Plus } from "lucide-svelte";
import { ReadingLogEntryEditModal } from "@views/reading-log-entry-edit-modal";
import { ReadingLogNewEntryModal } from "@views/reading-log-new-entry-modal";
const ALL_TIME = "ALL_TIME";
@ -68,6 +69,16 @@
),
);
function createEntry() {
const modal = new ReadingLogNewEntryModal(app);
modal.once("submit", async (event) => {
modal.close();
await readingLog.addRawEntry(event.entry);
reload();
});
modal.open();
}
function editEntry(i: number, entry: ReadingLogEntry) {
const modal = new ReadingLogEntryEditModal(app, entry);
modal.once("submit", async (event) => {
@ -86,28 +97,36 @@
</script>
<div class="obt-reading-log-viewer">
<div class="filters">
<select class="year-filter" bind:value={selectedYear}>
{#each years as year}
<option value={year.toString()}>{year}</option>
{/each}
<option class="all-time" value={ALL_TIME}>All Time</option>
</select>
<select class="month-filter" bind:value={selectedMonth}>
<option value="">Select Month</option>
<option value="01">January</option>
<option value="02">February</option>
<option value="03">March</option>
<option value="04">April</option>
<option value="05">May</option>
<option value="06">June</option>
<option value="07">July</option>
<option value="08">August</option>
<option value="09">September</option>
<option value="10">October</option>
<option value="11">November</option>
<option value="12">December</option>
</select>
<div class="controls">
<div class="left">
<select class="year-filter" bind:value={selectedYear}>
{#each years as year}
<option value={year.toString()}>{year}</option>
{/each}
<option class="all-time" value={ALL_TIME}>All Time</option>
</select>
<select class="month-filter" bind:value={selectedMonth}>
<option value="">Select Month</option>
<option value="01">January</option>
<option value="02">February</option>
<option value="03">March</option>
<option value="04">April</option>
<option value="05">May</option>
<option value="06">June</option>
<option value="07">July</option>
<option value="08">August</option>
<option value="09">September</option>
<option value="10">October</option>
<option value="11">November</option>
<option value="12">December</option>
</select>
</div>
<div class="right">
<button onclick={createEntry} class="create-entry" type="button">
<Plus />
Create
</button>
</div>
</div>
<table>
@ -155,6 +174,27 @@
@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);
}
}
}
}
.year-filter:has(> option.all-time:checked) + .month-filter {
display: none;
}

View File

@ -0,0 +1,34 @@
// Credits go to Liam's Periodic Notes Plugin: https://github.com/liamcain/obsidian-periodic-notes
import { TAbstractFile, TFile } from "obsidian";
import { TextInputSuggest } from "./core";
export class BookSuggest extends TextInputSuggest<TFile> {
getSuggestions(inputStr: string): TFile[] {
const abstractFiles = this.app.vault.getAllLoadedFiles();
const files: TFile[] = [];
const lowerCaseInputStr = inputStr.toLowerCase();
abstractFiles.forEach((file: TAbstractFile) => {
if (
file instanceof TFile &&
file.extension === "md" &&
file.basename.toLowerCase().contains(lowerCaseInputStr)
) {
files.push(file);
}
});
return files;
}
renderSuggestion(file: TFile, el: HTMLElement): void {
el.setText(file.basename);
}
selectSuggestion(file: TFile): void {
this.inputEl.value = file.basename;
this.inputEl.trigger("input");
this.close();
}
}

View File

@ -93,7 +93,11 @@ export class ReadingLog {
createdAt: new Date(),
};
this.entries.push(newEntry);
await this.addRawEntry(newEntry);
}
public async addRawEntry(entry: ReadingLogEntry) {
this.entries.push(entry);
await this.storeEntries();
}

View File

@ -21,6 +21,7 @@ export class ReadingLogEntryEditModal extends EventEmitter<
constructor(app: App, entry: ReadingLogEntry) {
super(app, ReadingLogEntryEditor, {
props: {
app,
entry,
onSubmit: (entry: ReadingLogEntry) =>
this.emit("submit", new SubmitEvent(entry)),

View File

@ -0,0 +1,30 @@
import ReadingLogEntryEditor from "@components/ReadingLogEntryEditor.svelte";
import type { ReadingLogEntry } from "@src/types";
import { Event, EventEmitter } from "@utils/event";
import { App } from "obsidian";
import { SvelteModal } from "./svelte-modal";
export class SubmitEvent extends Event {
constructor(public readonly entry: ReadingLogEntry) {
super();
}
}
interface ReadingLogNewEntryModalEventMap {
submit: SubmitEvent;
}
export class ReadingLogNewEntryModal extends EventEmitter<
ReadingLogNewEntryModalEventMap,
typeof SvelteModal<typeof ReadingLogEntryEditor>
>(SvelteModal) {
constructor(app: App) {
super(app, ReadingLogEntryEditor, {
props: {
app,
onSubmit: (entry: ReadingLogEntry) =>
this.emit("submit", new SubmitEvent(entry)),
},
});
}
}