generated from tpl/obsidian-sample-plugin
Add entry create button
This commit is contained in:
parent
6ffad2b5d1
commit
bd21890885
|
@ -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"
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
}
|
|
@ -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();
|
||||
}
|
||||
|
||||
|
|
|
@ -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)),
|
||||
|
|
|
@ -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)),
|
||||
},
|
||||
});
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue