From 6ffad2b5d10bc0ff27e14c05eb1af7279433d7d7 Mon Sep 17 00:00:00 2001 From: Evan Fiordeliso Date: Sun, 29 Jun 2025 08:49:06 -0400 Subject: [PATCH] Add svelte modal helper --- src/views/goodreads-search-modal.ts | 22 +++++---------- src/views/rating-modal.ts | 22 +++++---------- src/views/reading-log-entry-edit-modal.ts | 30 ++++++--------------- src/views/reading-progress-modal.ts | 30 ++++++--------------- src/views/svelte-modal.ts | 33 +++++++++++++++++++++++ 5 files changed, 61 insertions(+), 76 deletions(-) create mode 100644 src/views/svelte-modal.ts diff --git a/src/views/goodreads-search-modal.ts b/src/views/goodreads-search-modal.ts index c7d9704..7ace9f0 100644 --- a/src/views/goodreads-search-modal.ts +++ b/src/views/goodreads-search-modal.ts @@ -1,8 +1,8 @@ import GoodreadsSearch from "@components/GoodreadsSearch.svelte"; import { type SearchResult } from "@data-sources/goodreads"; import { Event, EventEmitter } from "@utils/event"; -import { App, Modal, Notice } from "obsidian"; -import { mount, unmount } from "svelte"; +import { App } from "obsidian"; +import { SvelteModal } from "./svelte-modal"; export class SearchEvent extends Event { constructor( @@ -26,13 +26,10 @@ interface GoodreadsSearchModalEventMap { export class GoodreadsSearchModal extends EventEmitter< GoodreadsSearchModalEventMap, - typeof Modal ->(Modal) { - private component: ReturnType | undefined; - - onOpen() { - this.component = mount(GoodreadsSearch, { - target: this.contentEl, + typeof SvelteModal +>(SvelteModal) { + constructor(app: App) { + super(app, GoodreadsSearch, { props: { onError: (error: Error) => { this.emit("error", new ErrorEvent(error)); @@ -44,13 +41,6 @@ export class GoodreadsSearchModal extends EventEmitter< }); } - onClose() { - if (this.component) { - unmount(this.component); - this.component = undefined; - } - } - static createAndOpen(app: App): Promise { return new Promise((resolve, reject) => { const modal = new GoodreadsSearchModal(app); diff --git a/src/views/rating-modal.ts b/src/views/rating-modal.ts index 15f8682..5d01ab9 100644 --- a/src/views/rating-modal.ts +++ b/src/views/rating-modal.ts @@ -1,7 +1,7 @@ import Rating from "@components/Rating.svelte"; import { Event, EventEmitter } from "@utils/event"; -import { App, Modal } from "obsidian"; -import { mount, unmount } from "svelte"; +import { App } from "obsidian"; +import { SvelteModal } from "./svelte-modal"; class SubmitEvent extends Event { constructor(public readonly rating: number) { @@ -15,13 +15,10 @@ interface RatingModalEventMap { export class RatingModal extends EventEmitter< RatingModalEventMap, - typeof Modal ->(Modal) { - private component: ReturnType | undefined; - - onOpen(): void { - this.component = mount(Rating, { - target: this.contentEl, + typeof SvelteModal +>(SvelteModal) { + constructor(app: App) { + super(app, Rating, { props: { onSubmit: (rating: number) => this.emit("submit", new SubmitEvent(rating)), @@ -29,13 +26,6 @@ export class RatingModal extends EventEmitter< }); } - onClose(): void { - if (this.component) { - unmount(this.component); - this.component = undefined; - } - } - static createAndOpen(app: App): Promise { return new Promise((resolve) => { const modal = new RatingModal(app); diff --git a/src/views/reading-log-entry-edit-modal.ts b/src/views/reading-log-entry-edit-modal.ts index 4958035..932b846 100644 --- a/src/views/reading-log-entry-edit-modal.ts +++ b/src/views/reading-log-entry-edit-modal.ts @@ -1,10 +1,10 @@ import ReadingLogEntryEditor from "@components/ReadingLogEntryEditor.svelte"; import type { ReadingLogEntry } from "@src/types"; import { Event, EventEmitter } from "@utils/event"; -import { App, Modal } from "obsidian"; -import { mount, unmount } from "svelte"; +import { App } from "obsidian"; +import { SvelteModal } from "./svelte-modal"; -class SubmitEvent extends Event { +export class SubmitEvent extends Event { constructor(public readonly entry: ReadingLogEntry) { super(); } @@ -16,29 +16,15 @@ interface ReadingLogEntryEditModalEventMap { export class ReadingLogEntryEditModal extends EventEmitter< ReadingLogEntryEditModalEventMap, - typeof Modal ->(Modal) { - private component: ReturnType | undefined; - - constructor(app: App, private readonly entry: ReadingLogEntry) { - super(app); - } - - onOpen(): void { - this.component = mount(ReadingLogEntryEditor, { - target: this.contentEl, + typeof SvelteModal +>(SvelteModal) { + constructor(app: App, entry: ReadingLogEntry) { + super(app, ReadingLogEntryEditor, { props: { - entry: this.entry, + entry, onSubmit: (entry: ReadingLogEntry) => this.emit("submit", new SubmitEvent(entry)), }, }); } - - onClose(): void { - if (this.component) { - unmount(this.component); - this.component = undefined; - } - } } diff --git a/src/views/reading-progress-modal.ts b/src/views/reading-progress-modal.ts index 7e1860b..c6b32fa 100644 --- a/src/views/reading-progress-modal.ts +++ b/src/views/reading-progress-modal.ts @@ -1,9 +1,9 @@ import ReadingProgress from "@components/ReadingProgress.svelte"; import { Event, EventEmitter } from "@utils/event"; -import { App, Modal } from "obsidian"; -import { mount, unmount } from "svelte"; +import { App } from "obsidian"; +import { SvelteModal } from "./svelte-modal"; -class SubmitEvent extends Event { +export class SubmitEvent extends Event { constructor(public readonly pageNumber: number) { super(); } @@ -15,19 +15,12 @@ interface ReadingProgressModalEventMap { export class ReadingProgressModal extends EventEmitter< ReadingProgressModalEventMap, - typeof Modal ->(Modal) { - private component: ReturnType | undefined; - - constructor(app: App, private readonly pageLength: number) { - super(app); - } - - onOpen(): void { - this.component = mount(ReadingProgress, { - target: this.contentEl, + typeof SvelteModal +>(SvelteModal) { + constructor(app: App, pageLength: number) { + super(app, ReadingProgress, { props: { - pageLength: this.pageLength, + pageLength, onSubmit: (pageNumber: number) => { this.emit("submit", new SubmitEvent(pageNumber)); }, @@ -35,13 +28,6 @@ export class ReadingProgressModal extends EventEmitter< }); } - onClose(): void { - if (this.component) { - unmount(this.component); - this.component = undefined; - } - } - static createAndOpen(app: App, pageLength: number): Promise { return new Promise((resolve) => { const modal = new ReadingProgressModal(app, pageLength); diff --git a/src/views/svelte-modal.ts b/src/views/svelte-modal.ts new file mode 100644 index 0000000..e345430 --- /dev/null +++ b/src/views/svelte-modal.ts @@ -0,0 +1,33 @@ +import { App, Modal } from "obsidian"; +import { mount, unmount, type Component, type MountOptions } from "svelte"; + +export class SvelteModal< + TComponent extends Component, + TProps extends Record = {}, + TExports extends Record = {}, + TBindings extends keyof TProps | "" = string +> extends Modal { + protected component: TExports | undefined; + + constructor( + app: App, + private readonly componentCtor: TComponent, + private readonly mountOpts: Omit, "target"> + ) { + super(app); + } + + onOpen(): void { + this.component = mount(this.componentCtor, { + ...this.mountOpts, + target: this.contentEl, + }); + } + + onClose(): void { + if (this.component) { + unmount(this.component); + this.component = undefined; + } + } +}