summaryrefslogtreecommitdiffstatshomepage
path: root/frontend/src/lib/dialogs
diff options
context:
space:
mode:
Diffstat (limited to 'frontend/src/lib/dialogs')
-rw-r--r--frontend/src/lib/dialogs/AddArtist.svelte26
-rw-r--r--frontend/src/lib/dialogs/AddCharacter.svelte26
-rw-r--r--frontend/src/lib/dialogs/AddCircle.svelte26
-rw-r--r--frontend/src/lib/dialogs/AddNamespace.svelte26
-rw-r--r--frontend/src/lib/dialogs/AddTag.svelte26
-rw-r--r--frontend/src/lib/dialogs/AddWorld.svelte26
-rw-r--r--frontend/src/lib/dialogs/ConfirmDeletion.svelte31
-rw-r--r--frontend/src/lib/dialogs/EditArtist.svelte39
-rw-r--r--frontend/src/lib/dialogs/EditCharacter.svelte39
-rw-r--r--frontend/src/lib/dialogs/EditCircle.svelte39
-rw-r--r--frontend/src/lib/dialogs/EditNamespace.svelte39
-rw-r--r--frontend/src/lib/dialogs/EditTag.svelte37
-rw-r--r--frontend/src/lib/dialogs/EditWorld.svelte43
-rw-r--r--frontend/src/lib/dialogs/UpdateComics.svelte139
-rw-r--r--frontend/src/lib/dialogs/UpdateTags.svelte45
-rw-r--r--frontend/src/lib/dialogs/components/UpdateModeSelector.svelte4
16 files changed, 266 insertions, 345 deletions
diff --git a/frontend/src/lib/dialogs/AddArtist.svelte b/frontend/src/lib/dialogs/AddArtist.svelte
index 6ec93c5..9fc2ca1 100644
--- a/frontend/src/lib/dialogs/AddArtist.svelte
+++ b/frontend/src/lib/dialogs/AddArtist.svelte
@@ -1,30 +1,22 @@
<script lang="ts">
- import { addArtist, type ArtistInput } from '$gql/Mutations';
+ import type { AddArtistInput } from '$gql/graphql';
+ import { addArtist } from '$gql/Mutations';
import Dialog from '$lib/components/Dialog.svelte';
- import SubmitButton from '$lib/components/SubmitButton.svelte';
import ArtistForm from '$lib/forms/ArtistForm.svelte';
import { toastFinally } from '$lib/Toasts';
import { getContextClient } from '@urql/svelte';
- import { closeModal } from 'svelte-modals';
+ import type { ModalProps } from 'svelte-modals';
const client = getContextClient();
- export let isOpen: boolean;
+ let modal: ModalProps = $props();
+ const initial = { name: '' };
- let artist = { name: '' };
-
- function add(event: CustomEvent<ArtistInput>) {
- addArtist(client, { input: event.detail }).then(closeModal).catch(toastFinally);
+ function submit(input: AddArtistInput) {
+ addArtist(client, { input }).then(modal.close).catch(toastFinally);
}
</script>
-<Dialog {isOpen}>
- <svelte:fragment slot="header">
- <h2>Add Artist</h2>
- </svelte:fragment>
- <ArtistForm bind:artist on:submit={add}>
- <div class="flex justify-end gap-4">
- <SubmitButton active={artist.name.length > 0} />
- </div>
- </ArtistForm>
+<Dialog title="Add Artist" {...modal}>
+ <ArtistForm {initial} {submit} />
</Dialog>
diff --git a/frontend/src/lib/dialogs/AddCharacter.svelte b/frontend/src/lib/dialogs/AddCharacter.svelte
index 23fea08..1585e34 100644
--- a/frontend/src/lib/dialogs/AddCharacter.svelte
+++ b/frontend/src/lib/dialogs/AddCharacter.svelte
@@ -1,30 +1,22 @@
<script lang="ts">
- import { addCharacter, type CharacterInput } from '$gql/Mutations';
+ import type { AddCharacterInput } from '$gql/graphql';
+ import { addCharacter } from '$gql/Mutations';
import Dialog from '$lib/components/Dialog.svelte';
- import SubmitButton from '$lib/components/SubmitButton.svelte';
import CharacterForm from '$lib/forms/CharacterForm.svelte';
import { toastFinally } from '$lib/Toasts';
import { getContextClient } from '@urql/svelte';
- import { closeModal } from 'svelte-modals';
+ import type { ModalProps } from 'svelte-modals';
const client = getContextClient();
- export let isOpen: boolean;
+ let modal: ModalProps = $props();
+ const initial = { name: '' };
- let character = { name: '' };
-
- function add(event: CustomEvent<CharacterInput>) {
- addCharacter(client, { input: event.detail }).then(closeModal).catch(toastFinally);
+ function submit(input: AddCharacterInput) {
+ addCharacter(client, { input }).then(modal.close).catch(toastFinally);
}
</script>
-<Dialog {isOpen}>
- <svelte:fragment slot="header">
- <h2>Add Character</h2>
- </svelte:fragment>
- <CharacterForm bind:character on:submit={add}>
- <div class="flex justify-end gap-4">
- <SubmitButton active={character.name.length > 0} />
- </div>
- </CharacterForm>
+<Dialog title="Add Character" {...modal}>
+ <CharacterForm {initial} {submit} />
</Dialog>
diff --git a/frontend/src/lib/dialogs/AddCircle.svelte b/frontend/src/lib/dialogs/AddCircle.svelte
index f0ef014..faffc63 100644
--- a/frontend/src/lib/dialogs/AddCircle.svelte
+++ b/frontend/src/lib/dialogs/AddCircle.svelte
@@ -1,30 +1,22 @@
<script lang="ts">
- import { addCircle, type CircleInput } from '$gql/Mutations';
+ import type { AddCircleInput } from '$gql/graphql';
+ import { addCircle } from '$gql/Mutations';
import Dialog from '$lib/components/Dialog.svelte';
- import SubmitButton from '$lib/components/SubmitButton.svelte';
import CircleForm from '$lib/forms/CircleForm.svelte';
import { toastFinally } from '$lib/Toasts';
import { getContextClient } from '@urql/svelte';
- import { closeModal } from 'svelte-modals';
+ import type { ModalProps } from 'svelte-modals';
const client = getContextClient();
- export let isOpen: boolean;
+ let modal: ModalProps = $props();
+ const initial = { name: '' };
- let circle = { name: '' };
-
- function add(event: CustomEvent<CircleInput>) {
- addCircle(client, { input: event.detail }).then(closeModal).catch(toastFinally);
+ function submit(input: AddCircleInput) {
+ addCircle(client, { input }).then(modal.close).catch(toastFinally);
}
</script>
-<Dialog {isOpen}>
- <svelte:fragment slot="header">
- <h2>Add Circle</h2>
- </svelte:fragment>
- <CircleForm bind:circle on:submit={add}>
- <div class="flex justify-end gap-4">
- <SubmitButton active={circle.name.length > 0} />
- </div>
- </CircleForm>
+<Dialog title="Add Circle" {...modal}>
+ <CircleForm {initial} {submit} />
</Dialog>
diff --git a/frontend/src/lib/dialogs/AddNamespace.svelte b/frontend/src/lib/dialogs/AddNamespace.svelte
index e81b22a..45183f4 100644
--- a/frontend/src/lib/dialogs/AddNamespace.svelte
+++ b/frontend/src/lib/dialogs/AddNamespace.svelte
@@ -1,30 +1,22 @@
<script lang="ts">
- import { addNamespace, type NamespaceInput } from '$gql/Mutations';
+ import type { AddNamespaceInput } from '$gql/graphql';
+ import { addNamespace } from '$gql/Mutations';
import Dialog from '$lib/components/Dialog.svelte';
- import SubmitButton from '$lib/components/SubmitButton.svelte';
import NamespaceForm from '$lib/forms/NamespaceForm.svelte';
import { toastFinally } from '$lib/Toasts';
import { getContextClient } from '@urql/svelte';
- import { closeModal } from 'svelte-modals';
+ import type { ModalProps } from 'svelte-modals';
const client = getContextClient();
- export let isOpen: boolean;
+ let modal: ModalProps = $props();
+ const initial = { name: '' };
- let namespace = { name: '' };
-
- function add(event: CustomEvent<NamespaceInput>) {
- addNamespace(client, { input: event.detail }).then(closeModal).catch(toastFinally);
+ function submit(input: AddNamespaceInput) {
+ addNamespace(client, { input }).then(modal.close).catch(toastFinally);
}
</script>
-<Dialog {isOpen}>
- <svelte:fragment slot="header">
- <h2>Add Namespace</h2>
- </svelte:fragment>
- <NamespaceForm bind:namespace on:submit={add}>
- <div class="flex justify-end gap-4">
- <SubmitButton active={namespace.name.length > 0} />
- </div>
- </NamespaceForm>
+<Dialog title="Add Namespace" {...modal}>
+ <NamespaceForm {initial} {submit} />
</Dialog>
diff --git a/frontend/src/lib/dialogs/AddTag.svelte b/frontend/src/lib/dialogs/AddTag.svelte
index 00d3a03..da78bce 100644
--- a/frontend/src/lib/dialogs/AddTag.svelte
+++ b/frontend/src/lib/dialogs/AddTag.svelte
@@ -1,30 +1,22 @@
<script lang="ts">
- import { addTag, type TagInput } from '$gql/Mutations';
+ import type { AddTagInput } from '$gql/graphql';
+ import { addTag } from '$gql/Mutations';
import Dialog from '$lib/components/Dialog.svelte';
- import SubmitButton from '$lib/components/SubmitButton.svelte';
import TagForm from '$lib/forms/TagForm.svelte';
import { toastFinally } from '$lib/Toasts';
import { getContextClient } from '@urql/svelte';
- import { closeModal } from 'svelte-modals';
+ import { type ModalProps } from 'svelte-modals';
const client = getContextClient();
- export let isOpen: boolean;
+ let modal: ModalProps = $props();
+ const initial = { name: '', namespaces: [] };
- let tag = { name: '', namespaces: [] };
-
- function add(event: CustomEvent<TagInput>) {
- addTag(client, { input: event.detail }).then(closeModal).catch(toastFinally);
+ function submit(input: AddTagInput) {
+ addTag(client, { input }).then(modal.close).catch(toastFinally);
}
</script>
-<Dialog {isOpen}>
- <svelte:fragment slot="header">
- <h2>Add Tag</h2>
- </svelte:fragment>
- <TagForm bind:tag on:submit={add}>
- <div class="flex justify-end gap-4">
- <SubmitButton active={tag.name.length > 0} />
- </div>
- </TagForm>
+<Dialog title="Add Tag" {...modal}>
+ <TagForm {initial} {submit} />
</Dialog>
diff --git a/frontend/src/lib/dialogs/AddWorld.svelte b/frontend/src/lib/dialogs/AddWorld.svelte
index ceb946e..075d872 100644
--- a/frontend/src/lib/dialogs/AddWorld.svelte
+++ b/frontend/src/lib/dialogs/AddWorld.svelte
@@ -1,30 +1,22 @@
<script lang="ts">
- import { addWorld, type WorldInput } from '$gql/Mutations';
+ import type { AddWorldInput } from '$gql/graphql';
+ import { addWorld } from '$gql/Mutations';
import Dialog from '$lib/components/Dialog.svelte';
- import SubmitButton from '$lib/components/SubmitButton.svelte';
import WorldForm from '$lib/forms/WorldForm.svelte';
import { toastFinally } from '$lib/Toasts';
import { getContextClient } from '@urql/svelte';
- import { closeModal } from 'svelte-modals';
+ import type { ModalProps } from 'svelte-modals';
const client = getContextClient();
- export let isOpen: boolean;
+ let modal: ModalProps = $props();
+ const initial = { name: '' };
- let world = { name: '' };
-
- function add(event: CustomEvent<WorldInput>) {
- addWorld(client, { input: event.detail }).then(closeModal).catch(toastFinally);
+ function submit(input: AddWorldInput) {
+ addWorld(client, { input }).then(modal.close).catch(toastFinally);
}
</script>
-<Dialog {isOpen}>
- <svelte:fragment slot="header">
- <h2>Add World</h2>
- </svelte:fragment>
- <WorldForm bind:world on:submit={add}>
- <div class="flex justify-end gap-4">
- <SubmitButton active={world.name.length > 0} />
- </div>
- </WorldForm>
+<Dialog title="Add World" {...modal}>
+ <WorldForm {initial} {submit} />
</Dialog>
diff --git a/frontend/src/lib/dialogs/ConfirmDeletion.svelte b/frontend/src/lib/dialogs/ConfirmDeletion.svelte
index 6b0cbf8..571fd05 100644
--- a/frontend/src/lib/dialogs/ConfirmDeletion.svelte
+++ b/frontend/src/lib/dialogs/ConfirmDeletion.svelte
@@ -1,29 +1,30 @@
<script lang="ts">
import { accelerator } from '$lib/Shortcuts';
import Dialog from '$lib/components/Dialog.svelte';
- import { closeModal } from 'svelte-modals';
+ import type { ModalProps } from 'svelte-modals';
- export let isOpen: boolean;
- export let callback: () => void;
+ interface Props extends ModalProps {
+ callback: () => void;
+ names: string[];
+ typename: string;
+ warning?: string;
+ }
+
+ let { callback, names, typename, warning = undefined, ...modal }: Props = $props();
- export let names: string[];
- export let typename: string;
- export let warning: string | undefined = undefined;
const multiple = names.length > 1;
const formattedTypename = multiple ? `${typename}s` : typename;
const formattedNames = multiple ? `${names.length} ${formattedTypename}` : names[0];
- function confirm() {
+ function confirm(event: SubmitEvent) {
+ event.preventDefault();
callback();
- closeModal();
+ modal.close();
}
</script>
-<Dialog {isOpen}>
- <svelte:fragment slot="header">
- <h2>Delete {formattedTypename}</h2>
- </svelte:fragment>
- <form on:submit|preventDefault={confirm}>
+<Dialog title="Delete {formattedTypename}" {...modal}>
+ <form onsubmit={confirm}>
<div class="flex flex-col">
<p class="mb-3">
Are you sure you want to delete <span class="font-semibold">{formattedNames}</span>?
@@ -39,13 +40,13 @@
{/if}
{/if}
{#if warning}
- <p class="font-medium text-red-600">Warning: {warning}</p>
+ <p class="font-semibold text-rose-600">Warning: {warning}</p>
{/if}
</div>
<div class="flex justify-end gap-4">
<button type="submit" class="btn-rose" use:accelerator={'Enter'}>Delete</button>
- <button type="button" on:click={closeModal} class="btn-slate">Cancel</button>
+ <button type="button" onclick={() => modal.close()} class="btn-slate">Cancel</button>
</div>
</form>
</Dialog>
diff --git a/frontend/src/lib/dialogs/EditArtist.svelte b/frontend/src/lib/dialogs/EditArtist.svelte
index dd08bc6..fa5c143 100644
--- a/frontend/src/lib/dialogs/EditArtist.svelte
+++ b/frontend/src/lib/dialogs/EditArtist.svelte
@@ -1,46 +1,37 @@
<script lang="ts">
- import { deleteArtists, updateArtists, type ArtistInput } from '$gql/Mutations';
- import { itemEquals } from '$gql/Utils';
- import { type Artist } from '$gql/graphql';
+ import { deleteArtists, updateArtists } from '$gql/Mutations';
+ import { omitIdentifiers } from '$gql/Utils';
+ import type { Artist, UpdateArtistInput } from '$gql/graphql';
import { toastFinally } from '$lib/Toasts';
import { confirmDeletion } from '$lib/Utils';
import DeleteButton from '$lib/components/DeleteButton.svelte';
import Dialog from '$lib/components/Dialog.svelte';
- import SubmitButton from '$lib/components/SubmitButton.svelte';
import ArtistForm from '$lib/forms/ArtistForm.svelte';
import { getContextClient } from '@urql/svelte';
- import { closeModal } from 'svelte-modals';
+ import type { ModalProps } from 'svelte-modals';
const client = getContextClient();
- export let isOpen: boolean;
+ interface Props extends ModalProps {
+ artist: Artist;
+ }
- export let artist: Artist;
- const original = structuredClone(artist);
- $: pending = !itemEquals(artist, original);
+ let { artist, ...modal }: Props = $props();
+ const initial = omitIdentifiers(artist);
- function save(event: CustomEvent<ArtistInput>) {
- updateArtists(client, { ids: artist.id, input: event.detail })
- .then(closeModal)
- .catch(toastFinally);
+ function submit(input: UpdateArtistInput) {
+ updateArtists(client, { ids: artist.id, input }).then(modal.close).catch(toastFinally);
}
function deleteArtist() {
confirmDeletion('Artist', artist.name, () => {
- deleteArtists(client, { ids: artist.id }).then(closeModal).catch(toastFinally);
+ deleteArtists(client, { ids: artist.id }).then(modal.close).catch(toastFinally);
});
}
</script>
-<Dialog {isOpen}>
- <svelte:fragment slot="header">
- <h2>Edit Artist</h2>
- </svelte:fragment>
- <ArtistForm bind:artist on:submit={save}>
- <div class="flex gap-4">
- <DeleteButton on:click={deleteArtist} />
- <div class="grow" />
- <SubmitButton active={pending} />
- </div>
+<Dialog title="Edit Artist" {...modal}>
+ <ArtistForm {initial} {submit}>
+ <DeleteButton onclick={deleteArtist} />
</ArtistForm>
</Dialog>
diff --git a/frontend/src/lib/dialogs/EditCharacter.svelte b/frontend/src/lib/dialogs/EditCharacter.svelte
index 3b45e78..71125db 100644
--- a/frontend/src/lib/dialogs/EditCharacter.svelte
+++ b/frontend/src/lib/dialogs/EditCharacter.svelte
@@ -1,46 +1,37 @@
<script lang="ts">
- import { deleteCharacters, updateCharacters, type CharacterInput } from '$gql/Mutations';
- import { itemEquals } from '$gql/Utils';
- import { type Character } from '$gql/graphql';
+ import { deleteCharacters, updateCharacters } from '$gql/Mutations';
+ import { omitIdentifiers } from '$gql/Utils';
+ import type { Character, UpdateCharacterInput } from '$gql/graphql';
import { toastFinally } from '$lib/Toasts';
import { confirmDeletion } from '$lib/Utils';
import DeleteButton from '$lib/components/DeleteButton.svelte';
import Dialog from '$lib/components/Dialog.svelte';
- import SubmitButton from '$lib/components/SubmitButton.svelte';
import CharacterForm from '$lib/forms/CharacterForm.svelte';
import { getContextClient } from '@urql/svelte';
- import { closeModal } from 'svelte-modals';
+ import type { ModalProps } from 'svelte-modals';
const client = getContextClient();
- export let isOpen: boolean;
+ interface Props extends ModalProps {
+ character: Character;
+ }
- export let character: Character;
- const original = structuredClone(character);
- $: pending = !itemEquals(original, character);
+ let { character, ...modal }: Props = $props();
+ const initial = omitIdentifiers(character);
- function save(event: CustomEvent<CharacterInput>) {
- updateCharacters(client, { ids: character.id, input: event.detail })
- .then(closeModal)
- .catch(toastFinally);
+ function submit(input: UpdateCharacterInput) {
+ updateCharacters(client, { ids: character.id, input }).then(modal.close).catch(toastFinally);
}
function deleteCharacter() {
confirmDeletion('Character', character.name, () => {
- deleteCharacters(client, { ids: character.id }).then(closeModal).catch(toastFinally);
+ deleteCharacters(client, { ids: character.id }).then(modal.close).catch(toastFinally);
});
}
</script>
-<Dialog {isOpen}>
- <svelte:fragment slot="header">
- <h2>Edit Character</h2>
- </svelte:fragment>
- <CharacterForm bind:character on:submit={save}>
- <div class="flex gap-4">
- <DeleteButton on:click={deleteCharacter} />
- <div class="grow" />
- <SubmitButton active={pending} />
- </div>
+<Dialog title="Edit Character" {...modal}>
+ <CharacterForm {initial} {submit}>
+ <DeleteButton onclick={deleteCharacter} />
</CharacterForm>
</Dialog>
diff --git a/frontend/src/lib/dialogs/EditCircle.svelte b/frontend/src/lib/dialogs/EditCircle.svelte
index bdc1217..7cb0f14 100644
--- a/frontend/src/lib/dialogs/EditCircle.svelte
+++ b/frontend/src/lib/dialogs/EditCircle.svelte
@@ -1,46 +1,37 @@
<script lang="ts">
- import { deleteCircles, updateCircles, type CircleInput } from '$gql/Mutations';
- import { itemEquals } from '$gql/Utils';
- import { type Circle } from '$gql/graphql';
+ import { deleteCircles, updateCircles } from '$gql/Mutations';
+ import { omitIdentifiers } from '$gql/Utils';
+ import type { Circle, UpdateCircleInput } from '$gql/graphql';
import { toastFinally } from '$lib/Toasts';
import { confirmDeletion } from '$lib/Utils';
import DeleteButton from '$lib/components/DeleteButton.svelte';
import Dialog from '$lib/components/Dialog.svelte';
- import SubmitButton from '$lib/components/SubmitButton.svelte';
import CircleForm from '$lib/forms/CircleForm.svelte';
import { getContextClient } from '@urql/svelte';
- import { closeModal } from 'svelte-modals';
+ import type { ModalProps } from 'svelte-modals';
const client = getContextClient();
- export let isOpen: boolean;
+ interface Props extends ModalProps {
+ circle: Circle;
+ }
- export let circle: Circle;
- const original = structuredClone(circle);
- $: pending = !itemEquals(original, circle);
+ let { circle, ...modal }: Props = $props();
+ const initial = omitIdentifiers(circle);
- function save(event: CustomEvent<CircleInput>) {
- updateCircles(client, { ids: circle.id, input: event.detail })
- .then(closeModal)
- .catch(toastFinally);
+ function submit(input: UpdateCircleInput) {
+ updateCircles(client, { ids: circle.id, input }).then(modal.close).catch(toastFinally);
}
function deleteCircle() {
confirmDeletion('Circle', circle.name, () => {
- deleteCircles(client, { ids: circle.id }).then(closeModal).catch(toastFinally);
+ deleteCircles(client, { ids: circle.id }).then(modal.close).catch(toastFinally);
});
}
</script>
-<Dialog {isOpen}>
- <svelte:fragment slot="header">
- <h2>Edit Circle</h2>
- </svelte:fragment>
- <CircleForm bind:circle on:submit={save}>
- <div class="flex gap-4">
- <DeleteButton on:click={deleteCircle} />
- <div class="grow" />
- <SubmitButton active={pending} />
- </div>
+<Dialog title="Edit Circle" {...modal}>
+ <CircleForm {initial} {submit}>
+ <DeleteButton onclick={deleteCircle} />
</CircleForm>
</Dialog>
diff --git a/frontend/src/lib/dialogs/EditNamespace.svelte b/frontend/src/lib/dialogs/EditNamespace.svelte
index f398b21..b104f83 100644
--- a/frontend/src/lib/dialogs/EditNamespace.svelte
+++ b/frontend/src/lib/dialogs/EditNamespace.svelte
@@ -1,46 +1,37 @@
<script lang="ts">
- import { deleteNamespaces, updateNamespaces, type NamespaceInput } from '$gql/Mutations';
- import { itemEquals } from '$gql/Utils';
- import { type Namespace } from '$gql/graphql';
+ import { deleteNamespaces, updateNamespaces } from '$gql/Mutations';
+ import { omitIdentifiers } from '$gql/Utils';
+ import type { Namespace, UpdateNamespaceInput } from '$gql/graphql';
import { toastFinally } from '$lib/Toasts';
import { confirmDeletion } from '$lib/Utils';
import DeleteButton from '$lib/components/DeleteButton.svelte';
import Dialog from '$lib/components/Dialog.svelte';
- import SubmitButton from '$lib/components/SubmitButton.svelte';
import NamespaceForm from '$lib/forms/NamespaceForm.svelte';
import { getContextClient } from '@urql/svelte';
- import { closeModal } from 'svelte-modals';
+ import type { ModalProps } from 'svelte-modals';
const client = getContextClient();
- export let isOpen: boolean;
+ interface Props extends ModalProps {
+ namespace: Namespace;
+ }
- export let namespace: Namespace;
- const original = structuredClone(namespace);
- $: pending = !itemEquals(original, namespace);
+ let { namespace, ...modal }: Props = $props();
+ const initial = omitIdentifiers(namespace);
- function save(event: CustomEvent<NamespaceInput>) {
- updateNamespaces(client, { ids: namespace.id, input: event.detail })
- .then(closeModal)
- .catch(toastFinally);
+ function submit(input: UpdateNamespaceInput) {
+ updateNamespaces(client, { ids: namespace.id, input }).then(modal.close).catch(toastFinally);
}
function deleteNamespace() {
confirmDeletion('Namespace', namespace.name, () => {
- deleteNamespaces(client, { ids: namespace.id }).then(closeModal).catch(toastFinally);
+ deleteNamespaces(client, { ids: namespace.id }).then(modal.close).catch(toastFinally);
});
}
</script>
-<Dialog {isOpen}>
- <svelte:fragment slot="header">
- <h2>Edit Namespace</h2>
- </svelte:fragment>
- <NamespaceForm bind:namespace on:submit={save}>
- <div class="flex gap-4">
- <DeleteButton on:click={deleteNamespace} />
- <div class="grow" />
- <SubmitButton active={pending} />
- </div>
+<Dialog title="Edit Namespace" {...modal}>
+ <NamespaceForm {initial} {submit}>
+ <DeleteButton onclick={deleteNamespace} />
</NamespaceForm>
</Dialog>
diff --git a/frontend/src/lib/dialogs/EditTag.svelte b/frontend/src/lib/dialogs/EditTag.svelte
index d2d0013..555d6d1 100644
--- a/frontend/src/lib/dialogs/EditTag.svelte
+++ b/frontend/src/lib/dialogs/EditTag.svelte
@@ -1,44 +1,37 @@
<script lang="ts">
- import { deleteTags, updateTags, type TagInput } from '$gql/Mutations';
- import { tagEquals } from '$gql/Utils';
- import { type FullTag } from '$gql/graphql';
+ import { deleteTags, updateTags } from '$gql/Mutations';
+ import { omitIdentifiers } from '$gql/Utils';
+ import { type FullTag, type UpdateTagInput } from '$gql/graphql';
import { toastFinally } from '$lib/Toasts';
import { confirmDeletion } from '$lib/Utils';
import DeleteButton from '$lib/components/DeleteButton.svelte';
import Dialog from '$lib/components/Dialog.svelte';
- import SubmitButton from '$lib/components/SubmitButton.svelte';
import TagForm from '$lib/forms/TagForm.svelte';
import { getContextClient } from '@urql/svelte';
- import { closeModal } from 'svelte-modals';
+ import { type ModalProps } from 'svelte-modals';
const client = getContextClient();
- export let isOpen: boolean;
+ interface Props extends ModalProps {
+ tag: FullTag;
+ }
- export let tag: FullTag;
- const original = structuredClone(tag);
- $: pending = !tagEquals(original, tag);
+ let { tag, ...modal }: Props = $props();
+ const initial = omitIdentifiers(tag);
- function save(event: CustomEvent<TagInput>) {
- updateTags(client, { ids: tag.id, input: event.detail }).then(closeModal).catch(toastFinally);
+ function submit(input: UpdateTagInput) {
+ updateTags(client, { ids: tag.id, input }).then(modal.close).catch(toastFinally);
}
function deleteTag() {
confirmDeletion('Tag', tag.name, () => {
- deleteTags(client, { ids: tag.id }).then(closeModal).catch(toastFinally);
+ deleteTags(client, { ids: tag.id }).then(modal.close).catch(toastFinally);
});
}
</script>
-<Dialog {isOpen}>
- <svelte:fragment slot="header">
- <h2>Edit Tag</h2>
- </svelte:fragment>
- <TagForm bind:tag on:submit={save}>
- <div class="flex gap-4">
- <DeleteButton on:click={deleteTag} />
- <div class="grow" />
- <SubmitButton active={pending} />
- </div>
+<Dialog title="Edit Tag" {...modal}>
+ <TagForm {initial} {submit}>
+ <DeleteButton onclick={deleteTag} />
</TagForm>
</Dialog>
diff --git a/frontend/src/lib/dialogs/EditWorld.svelte b/frontend/src/lib/dialogs/EditWorld.svelte
index 82afe6a..869dc21 100644
--- a/frontend/src/lib/dialogs/EditWorld.svelte
+++ b/frontend/src/lib/dialogs/EditWorld.svelte
@@ -1,46 +1,37 @@
<script lang="ts">
- import { type World } from '$gql/graphql';
- import { deleteWorlds, updateWorlds, type WorldInput } from '$gql/Mutations';
- import { itemEquals } from '$gql/Utils';
+ import { deleteWorlds, updateWorlds } from '$gql/Mutations';
+ import { omitIdentifiers } from '$gql/Utils';
+ import type { UpdateWorldInput, World } from '$gql/graphql';
+ import { toastFinally } from '$lib/Toasts';
+ import { confirmDeletion } from '$lib/Utils';
import DeleteButton from '$lib/components/DeleteButton.svelte';
import Dialog from '$lib/components/Dialog.svelte';
- import SubmitButton from '$lib/components/SubmitButton.svelte';
import WorldForm from '$lib/forms/WorldForm.svelte';
- import { toastFinally } from '$lib/Toasts';
- import { confirmDeletion } from '$lib/Utils';
import { getContextClient } from '@urql/svelte';
- import { closeModal } from 'svelte-modals';
+ import type { ModalProps } from 'svelte-modals';
const client = getContextClient();
- export let isOpen: boolean;
+ interface Props extends ModalProps {
+ world: World;
+ }
- export let world: World;
- const original = structuredClone(world);
- $: pending = !itemEquals(original, world);
+ let { world, ...modal }: Props = $props();
+ const initial = omitIdentifiers(world);
- function save(event: CustomEvent<WorldInput>) {
- updateWorlds(client, { ids: world.id, input: event.detail })
- .then(closeModal)
- .catch(toastFinally);
+ function submit(input: UpdateWorldInput) {
+ updateWorlds(client, { ids: world.id, input }).then(modal.close).catch(toastFinally);
}
function deleteWorld() {
confirmDeletion('World', world.name, () => {
- deleteWorlds(client, { ids: world.id }).then(closeModal).catch(toastFinally);
+ deleteWorlds(client, { ids: world.id }).then(modal.close).catch(toastFinally);
});
}
</script>
-<Dialog {isOpen}>
- <svelte:fragment slot="header">
- <h2>Edit World</h2>
- </svelte:fragment>
- <WorldForm bind:world on:submit={save}>
- <div class="flex gap-4">
- <DeleteButton on:click={deleteWorld} />
- <div class="grow" />
- <SubmitButton active={pending} />
- </div>
+<Dialog title="Edit World" {...modal}>
+ <WorldForm {initial} {submit}>
+ <DeleteButton onclick={deleteWorld} />
</WorldForm>
</Dialog>
diff --git a/frontend/src/lib/dialogs/UpdateComics.svelte b/frontend/src/lib/dialogs/UpdateComics.svelte
index 8de9622..483e379 100644
--- a/frontend/src/lib/dialogs/UpdateComics.svelte
+++ b/frontend/src/lib/dialogs/UpdateComics.svelte
@@ -3,94 +3,109 @@
import { artistList, characterList, circleList, comicTagList, worldList } from '$gql/Queries';
import { categories, censorships, directions, languages, layouts, ratings } from '$lib/Enums';
import { toastFinally } from '$lib/Toasts';
- import { UpdateComicsControls } from '$lib/Update';
+ import { UpdateComicsControls } from '$lib/Update.svelte';
import Dialog from '$lib/components/Dialog.svelte';
- import Labelled from '$lib/components/Labelled.svelte';
import LabelledBlock from '$lib/components/LabelledBlock.svelte';
import Select from '$lib/components/Select.svelte';
import SubmitButton from '$lib/components/SubmitButton.svelte';
import { getContextClient } from '@urql/svelte';
- import { closeModal } from 'svelte-modals';
+ import type { ModalProps } from 'svelte-modals';
import UpdateModeSelector from './components/UpdateModeSelector.svelte';
const client = getContextClient();
- export let isOpen: boolean;
- export let ids: number[];
+ interface Props extends ModalProps {
+ ids: number[];
+ }
- $: tagsQuery = comicTagList(client);
- $: artistsQuery = artistList(client);
- $: charactersQuery = characterList(client);
- $: circlesQuery = circleList(client);
- $: worldsQuery = worldList(client);
+ let { ids, ...modal }: Props = $props();
- $: tags = $tagsQuery.data?.comicTags.edges;
- $: artists = $artistsQuery.data?.artists.edges;
- $: characters = $charactersQuery.data?.characters.edges;
- $: circles = $circlesQuery.data?.circles.edges;
- $: worlds = $worldsQuery.data?.worlds.edges;
+ let tagsQuery = $derived(comicTagList(client));
+ let artistsQuery = $derived(artistList(client));
+ let charactersQuery = $derived(characterList(client));
+ let circlesQuery = $derived(circleList(client));
+ let worldsQuery = $derived(worldList(client));
+
+ let tags = $derived($tagsQuery.data?.comicTags.edges);
+ let artists = $derived($artistsQuery.data?.artists.edges);
+ let characters = $derived($charactersQuery.data?.characters.edges);
+ let circles = $derived($circlesQuery.data?.circles.edges);
+ let worlds = $derived($worldsQuery.data?.worlds.edges);
const controls = new UpdateComicsControls();
- const update = () => {
- updateComics(client, {
- ids: ids,
- input: controls.toInput()
- })
- .then(closeModal)
- .catch(toastFinally);
- };
+ function update(event: SubmitEvent) {
+ event.preventDefault();
+
+ updateComics(client, { ids, input: controls.input() }).then(modal.close).catch(toastFinally);
+ }
</script>
-<Dialog {isOpen}>
- <svelte:fragment slot="header">
- <h2>Edit Comics</h2>
- </svelte:fragment>
- <form on:submit|preventDefault={update}>
+<Dialog title="Edit Comics" {...modal}>
+ <form onsubmit={update}>
<div class="grid-labels">
- <Labelled label="Category" let:id>
- <Select clearable {id} options={categories} bind:value={controls.category.value} />
- </Labelled>
- <Labelled label="Rating" let:id>
- <Select clearable {id} options={ratings} bind:value={controls.rating.value} />
- </Labelled>
- <Labelled label="Censorship" let:id>
- <Select clearable {id} options={censorships} bind:value={controls.censorship.value} />
- </Labelled>
- <Labelled label="Language" let:id>
- <Select clearable {id} options={languages} bind:value={controls.language.value} />
- </Labelled>
- <Labelled label="Direction" let:id>
- <Select clearable {id} options={directions} bind:value={controls.direction.value} />
- </Labelled>
- <Labelled label="Layout" let:id>
- <Select clearable {id} options={layouts} bind:value={controls.layout.value} />
- </Labelled>
+ <label class="self-center" for="category">Category</label>
+ <Select clearable id="category" options={categories} bind:value={controls.category.value} />
+
+ <label class="self-center" for="rating">Rating</label>
+ <Select clearable id="rating" options={ratings} bind:value={controls.rating.value} />
+
+ <label class="self-center" for="censor">Censorship</label>
+ <Select clearable id="censor" options={censorships} bind:value={controls.censorship.value} />
+
+ <label class="self-center" for="language">Language</label>
+ <Select clearable id="language" options={languages} bind:value={controls.language.value} />
+
+ <label class="self-center" for="direction">Direction</label>
+ <Select clearable id="direction" options={directions} bind:value={controls.direction.value} />
+
+ <label class="self-center" for="layout">Layout</label>
+ <Select clearable id="layout" options={layouts} bind:value={controls.layout.value} />
</div>
- <LabelledBlock label="Artists" let:id>
- <Select multi {id} options={artists} bind:value={controls.artists.ids} />
- <UpdateModeSelector bind:mode={controls.artists.options.mode} slot="controls" />
+ <LabelledBlock label="Artists">
+ {#snippet children({ id })}
+ <Select multi {id} options={artists} bind:value={controls.artists.ids} />
+ {/snippet}
+ {#snippet side()}
+ <UpdateModeSelector bind:mode={controls.artists.options.mode} />
+ {/snippet}
</LabelledBlock>
- <LabelledBlock label="Circles" let:id>
- <Select multi {id} options={circles} bind:value={controls.circles.ids} />
- <UpdateModeSelector bind:mode={controls.circles.options.mode} slot="controls" />
+ <LabelledBlock label="Circles">
+ {#snippet children({ id })}
+ <Select multi {id} options={circles} bind:value={controls.circles.ids} />
+ {/snippet}
+ {#snippet side()}
+ <UpdateModeSelector bind:mode={controls.circles.options.mode} />
+ {/snippet}
</LabelledBlock>
- <LabelledBlock label="Characters" let:id>
- <Select multi {id} options={characters} bind:value={controls.characters.ids} />
- <UpdateModeSelector bind:mode={controls.characters.options.mode} slot="controls" />
+ <LabelledBlock label="Characters">
+ {#snippet children({ id })}
+ <Select multi {id} options={characters} bind:value={controls.characters.ids} />
+ {/snippet}
+ {#snippet side()}
+ <UpdateModeSelector bind:mode={controls.characters.options.mode} />
+ {/snippet}
</LabelledBlock>
- <LabelledBlock label="Worlds" let:id>
- <Select multi {id} options={worlds} bind:value={controls.worlds.ids} />
- <UpdateModeSelector bind:mode={controls.worlds.options.mode} slot="controls" />
+ <LabelledBlock label="Worlds">
+ {#snippet children({ id })}
+ <Select multi {id} options={worlds} bind:value={controls.worlds.ids} />
+ {/snippet}
+ {#snippet side()}
+ <UpdateModeSelector bind:mode={controls.worlds.options.mode} />
+ {/snippet}
</LabelledBlock>
- <LabelledBlock label="Tags" let:id>
- <Select multi {id} options={tags} bind:value={controls.tags.ids} />
- <UpdateModeSelector bind:mode={controls.tags.options.mode} slot="controls" />
+ <LabelledBlock label="Tags">
+ {#snippet children({ id })}
+ <Select multi {id} options={tags} bind:value={controls.tags.ids} />
+ {/snippet}
+ {#snippet side()}
+ <UpdateModeSelector bind:mode={controls.tags.options.mode} />
+ {/snippet}
</LabelledBlock>
<div class="flex justify-end gap-4">
- <SubmitButton active={controls.hasInput()} />
+ <SubmitButton pending={controls.pending()} />
</div>
</form>
</Dialog>
diff --git a/frontend/src/lib/dialogs/UpdateTags.svelte b/frontend/src/lib/dialogs/UpdateTags.svelte
index f753c7f..840e92e 100644
--- a/frontend/src/lib/dialogs/UpdateTags.svelte
+++ b/frontend/src/lib/dialogs/UpdateTags.svelte
@@ -2,44 +2,49 @@
import { updateTags } from '$gql/Mutations';
import { namespaceList } from '$gql/Queries';
import { toastFinally } from '$lib/Toasts';
- import { UpdateTagsControls } from '$lib/Update';
+ import { UpdateTagsControls } from '$lib/Update.svelte';
import Dialog from '$lib/components/Dialog.svelte';
import LabelledBlock from '$lib/components/LabelledBlock.svelte';
import Select from '$lib/components/Select.svelte';
import SubmitButton from '$lib/components/SubmitButton.svelte';
import { getContextClient } from '@urql/svelte';
- import { closeModal } from 'svelte-modals';
+ import { modals, type ModalProps } from 'svelte-modals';
import UpdateModeSelector from './components/UpdateModeSelector.svelte';
const client = getContextClient();
- $: namespaceQuery = namespaceList(client);
- $: namespaces = $namespaceQuery.data?.namespaces.edges;
+ let namespaceQuery = $derived(namespaceList(client));
+ let namespaces = $derived($namespaceQuery.data?.namespaces.edges);
- export let isOpen: boolean;
- export let ids: number[];
+ interface Props extends ModalProps {
+ ids: number[];
+ }
- const controls = new UpdateTagsControls();
+ let { ids, ...modal }: Props = $props();
+ let controls = new UpdateTagsControls();
- const update = () => {
- updateTags(client, { ids: ids, input: controls.toInput() })
- .then(closeModal)
+ function update(event: SubmitEvent) {
+ event.preventDefault();
+
+ updateTags(client, { ids, input: controls.input() })
+ .then(() => modals.close())
.catch(toastFinally);
- };
+ }
</script>
-<Dialog {isOpen}>
- <svelte:fragment slot="header">
- <h2>Edit Tags</h2>
- </svelte:fragment>
- <form on:submit|preventDefault={update}>
- <LabelledBlock label="Namespaces" let:id>
- <Select multi {id} options={namespaces} bind:value={controls.namespaces.ids} />
- <UpdateModeSelector bind:mode={controls.namespaces.options.mode} slot="controls" />
+<Dialog title="Edit Tags" {...modal}>
+ <form onsubmit={update}>
+ <LabelledBlock label="Namespaces">
+ {#snippet children({ id })}
+ <Select multi {id} options={namespaces} bind:value={controls.namespaces.ids} />
+ {/snippet}
+ {#snippet side()}
+ <UpdateModeSelector bind:mode={controls.namespaces.options.mode} />
+ {/snippet}
</LabelledBlock>
<div class="flex justify-end gap-4">
- <SubmitButton active={controls.hasInput()} />
+ <SubmitButton pending={controls.pending()} />
</div>
</form>
</Dialog>
diff --git a/frontend/src/lib/dialogs/components/UpdateModeSelector.svelte b/frontend/src/lib/dialogs/components/UpdateModeSelector.svelte
index e4b4479..6548fb5 100644
--- a/frontend/src/lib/dialogs/components/UpdateModeSelector.svelte
+++ b/frontend/src/lib/dialogs/components/UpdateModeSelector.svelte
@@ -2,7 +2,7 @@
import { UpdateMode } from '$gql/graphql';
import { UpdateModeLabel } from '$lib/Enums';
- export let mode: UpdateMode;
+ let { mode = $bindable() }: { mode: UpdateMode } = $props();
function select(e: string) {
mode = e as UpdateMode;
@@ -16,7 +16,7 @@
class:active={mode === e}
class:dangerous={mode !== UpdateMode.Add}
class="btn btn-xs hover:bg-slate-700 [&.active.dangerous]:bg-rose-800 [&.active]:bg-indigo-700"
- on:click={() => select(e)}
+ onclick={() => select(e)}
>
{label}
</button>