Skip to content

Commit 01e3b8e

Browse files
authored
refactor: form modals (#24790)
1 parent 5a7c9a2 commit 01e3b8e

9 files changed

+161
-218
lines changed

web/src/lib/modals/AlbumEditModal.svelte

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@
2424
};
2525
</script>
2626

27-
<FormModal icon={mdiRenameOutline} title={$t('edit_album')} size="medium" {onClose} {onSubmit} submitText={$t('save')}>
27+
<FormModal icon={mdiRenameOutline} title={$t('edit_album')} size="medium" {onClose} {onSubmit}>
2828
<div class="flex items-center gap-8 m-4">
2929
<AlbumCover {album} class="h-50 w-50 shadow-lg hidden sm:flex" />
3030

web/src/lib/modals/ApiKeyUpdateModal.svelte

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@
3434
};
3535
</script>
3636

37-
<FormModal title={$t('api_key')} icon={mdiKeyVariant} {onClose} {onSubmit} size="giant" submitText={$t('save')}>
37+
<FormModal title={$t('api_key')} icon={mdiKeyVariant} {onClose} {onSubmit} size="giant">
3838
<div class="mb-4 flex flex-col gap-2">
3939
<Field label={$t('name')}>
4040
<Input bind:value={name} />
Lines changed: 51 additions & 55 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
<script lang="ts">
22
import { tagAssets } from '$lib/utils/asset-utils';
33
import { getAllTags, upsertTags, type TagResponseDto } from '@immich/sdk';
4-
import { Button, HStack, Icon, Modal, ModalBody, ModalFooter } from '@immich/ui';
4+
import { FormModal, Icon } from '@immich/ui';
55
import { mdiClose, mdiTag } from '@mdi/js';
66
import { onMount } from 'svelte';
77
import { t } from 'svelte-i18n';
@@ -25,7 +25,11 @@
2525
allTags = await getAllTags();
2626
});
2727
28-
const handleSubmit = async () => {
28+
const onSubmit = async () => {
29+
if (selectedIds.size === 0) {
30+
return;
31+
}
32+
2933
await tagAssets({ tagIds: [...selectedIds], assetIds, showNotification: false });
3034
onClose(true);
3135
};
@@ -47,60 +51,52 @@
4751
const handleRemove = (tag: string) => {
4852
selectedIds.delete(tag);
4953
};
50-
51-
const onsubmit = async (event: Event) => {
52-
event.preventDefault();
53-
await handleSubmit();
54-
};
5554
</script>
5655

57-
<Modal size="small" title={$t('tag_assets')} icon={mdiTag} {onClose}>
58-
<ModalBody>
59-
<form {onsubmit} autocomplete="off" id="create-tag-form">
60-
<div class="my-4 flex flex-col gap-2">
61-
<Combobox
62-
onSelect={handleSelect}
63-
label={$t('tag')}
64-
{allowCreate}
65-
defaultFirstOption
66-
options={allTags.map((tag) => ({ id: tag.id, label: tag.value, value: tag.id }))}
67-
placeholder={$t('search_tags')}
68-
forceFocus
69-
/>
70-
</div>
71-
</form>
72-
73-
<section class="flex flex-wrap pt-2 gap-1">
74-
{#each selectedIds as tagId (tagId)}
75-
{@const tag = tagMap[tagId]}
76-
{#if tag}
77-
<div class="flex group transition-all">
78-
<span
79-
class="inline-block h-min whitespace-nowrap ps-3 pe-1 group-hover:ps-3 py-1 text-center align-baseline leading-none text-gray-100 dark:text-immich-dark-gray bg-primary roudned-s-full hover:bg-immich-primary/80 dark:hover:bg-immich-dark-primary/80 transition-all"
80-
>
81-
<p class="text-sm">
82-
{tag.value}
83-
</p>
84-
</span>
56+
<FormModal
57+
size="small"
58+
title={$t('tag_assets')}
59+
icon={mdiTag}
60+
{onClose}
61+
{onSubmit}
62+
submitText={$t('tag_assets')}
63+
{disabled}
64+
>
65+
<div class="my-4 flex flex-col gap-2">
66+
<Combobox
67+
onSelect={handleSelect}
68+
label={$t('tag')}
69+
{allowCreate}
70+
defaultFirstOption
71+
options={allTags.map((tag) => ({ id: tag.id, label: tag.value, value: tag.id }))}
72+
placeholder={$t('search_tags')}
73+
forceFocus
74+
/>
75+
</div>
8576

86-
<button
87-
type="button"
88-
class="text-gray-100 dark:text-immich-dark-gray bg-immich-primary/95 dark:bg-immich-dark-primary/95 rounded-e-full place-items-center place-content-center pe-2 ps-1 py-1 hover:bg-immich-primary/80 dark:hover:bg-immich-dark-primary/80 transition-all"
89-
title={$t('remove_tag')}
90-
onclick={() => handleRemove(tagId)}
91-
>
92-
<Icon icon={mdiClose} />
93-
</button>
94-
</div>
95-
{/if}
96-
{/each}
97-
</section>
98-
</ModalBody>
77+
<section class="flex flex-wrap pt-2 gap-1">
78+
{#each selectedIds as tagId (tagId)}
79+
{@const tag = tagMap[tagId]}
80+
{#if tag}
81+
<div class="flex group transition-all">
82+
<span
83+
class="inline-block h-min whitespace-nowrap ps-3 pe-1 group-hover:ps-3 py-1 text-center align-baseline leading-none text-gray-100 dark:text-immich-dark-gray bg-primary roudned-s-full hover:bg-immich-primary/80 dark:hover:bg-immich-dark-primary/80 transition-all"
84+
>
85+
<p class="text-sm">
86+
{tag.value}
87+
</p>
88+
</span>
9989

100-
<ModalFooter>
101-
<HStack fullWidth>
102-
<Button shape="round" fullWidth color="secondary" onclick={() => onClose()}>{$t('cancel')}</Button>
103-
<Button type="submit" shape="round" fullWidth form="create-tag-form" {disabled}>{$t('tag_assets')}</Button>
104-
</HStack>
105-
</ModalFooter>
106-
</Modal>
90+
<button
91+
type="button"
92+
class="text-gray-100 dark:text-immich-dark-gray bg-immich-primary/95 dark:bg-immich-dark-primary/95 rounded-e-full place-items-center place-content-center pe-2 ps-1 py-1 hover:bg-immich-primary/80 dark:hover:bg-immich-dark-primary/80 transition-all"
93+
title={$t('remove_tag')}
94+
onclick={() => handleRemove(tagId)}
95+
>
96+
<Icon icon={mdiClose} />
97+
</button>
98+
</div>
99+
{/if}
100+
{/each}
101+
</section>
102+
</FormModal>
Lines changed: 17 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
<script lang="ts">
22
import { handleAddLibraryExclusionPattern } from '$lib/services/library.service';
33
import type { LibraryResponseDto } from '@immich/sdk';
4-
import { Button, Field, HStack, Input, Modal, ModalBody, ModalFooter, Text } from '@immich/ui';
4+
import { Field, FormModal, Input, Text } from '@immich/ui';
55
import { mdiFolderSync } from '@mdi/js';
66
import { t } from 'svelte-i18n';
77
@@ -11,33 +11,26 @@
1111
};
1212
1313
const { library, onClose }: Props = $props();
14-
let exclusionPattern = $state('');
14+
let value = $state('');
1515
16-
const onsubmit = async () => {
17-
const success = await handleAddLibraryExclusionPattern(library, exclusionPattern);
16+
const onSubmit = async () => {
17+
const success = await handleAddLibraryExclusionPattern(library, value);
1818
if (success) {
1919
onClose();
2020
}
2121
};
2222
</script>
2323

24-
<Modal title={$t('add_exclusion_pattern')} icon={mdiFolderSync} {onClose} size="small">
25-
<ModalBody>
26-
<form {onsubmit} autocomplete="off" id="library-exclusion-pattern-form">
27-
<Text size="small" class="mb-4">{$t('admin.exclusion_pattern_description')}</Text>
28-
29-
<Field label={$t('pattern')}>
30-
<Input bind:value={exclusionPattern} />
31-
</Field>
32-
</form>
33-
</ModalBody>
34-
35-
<ModalFooter>
36-
<HStack fullWidth>
37-
<Button shape="round" color="secondary" fullWidth onclick={() => onClose()}>{$t('cancel')}</Button>
38-
<Button shape="round" type="submit" fullWidth form="library-exclusion-pattern-form">
39-
{$t('add')}
40-
</Button>
41-
</HStack>
42-
</ModalFooter>
43-
</Modal>
24+
<FormModal
25+
title={$t('add_exclusion_pattern')}
26+
icon={mdiFolderSync}
27+
{onClose}
28+
{onSubmit}
29+
submitText={$t('add')}
30+
size="small"
31+
>
32+
<Text size="small" class="mb-4">{$t('admin.exclusion_pattern_description')}</Text>
33+
<Field label={$t('pattern')}>
34+
<Input bind:value />
35+
</Field>
36+
</FormModal>
Lines changed: 11 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
<script lang="ts">
22
import { handleEditExclusionPattern } from '$lib/services/library.service';
33
import type { LibraryResponseDto } from '@immich/sdk';
4-
import { Button, Field, HStack, Input, Modal, ModalBody, ModalFooter, Text } from '@immich/ui';
4+
import { Field, FormModal, Input, Text } from '@immich/ui';
55
import { mdiFolderSync } from '@mdi/js';
66
import { t } from 'svelte-i18n';
77
@@ -11,35 +11,20 @@
1111
onClose: () => void;
1212
};
1313
14-
const { library, exclusionPattern, onClose }: Props = $props();
14+
const { library, exclusionPattern: oldValue, onClose }: Props = $props();
15+
let newValue = $state(oldValue);
1516
16-
let newExclusionPattern = $state(exclusionPattern);
17-
18-
const onsubmit = async () => {
19-
const success = await handleEditExclusionPattern(library, exclusionPattern, newExclusionPattern);
17+
const onSubmit = async () => {
18+
const success = await handleEditExclusionPattern(library, oldValue, newValue);
2019
if (success) {
2120
onClose();
2221
}
2322
};
2423
</script>
2524

26-
<Modal title={$t('edit_exclusion_pattern')} icon={mdiFolderSync} {onClose} size="small">
27-
<ModalBody>
28-
<form {onsubmit} autocomplete="off" id="library-exclusion-pattern-form">
29-
<Text size="small" class="mb-4">{$t('admin.exclusion_pattern_description')}</Text>
30-
31-
<Field label={$t('pattern')}>
32-
<Input bind:value={newExclusionPattern} />
33-
</Field>
34-
</form>
35-
</ModalBody>
36-
37-
<ModalFooter>
38-
<HStack fullWidth>
39-
<Button shape="round" color="secondary" fullWidth onclick={() => onClose()}>{$t('cancel')}</Button>
40-
<Button shape="round" type="submit" fullWidth form="library-exclusion-pattern-form">
41-
{$t('save')}
42-
</Button>
43-
</HStack>
44-
</ModalFooter>
45-
</Modal>
25+
<FormModal title={$t('edit_exclusion_pattern')} icon={mdiFolderSync} {onClose} {onSubmit} size="small">
26+
<Text size="small" class="mb-4">{$t('admin.exclusion_pattern_description')}</Text>
27+
<Field label={$t('pattern')}>
28+
<Input bind:value={newValue} />
29+
</Field>
30+
</FormModal>
Lines changed: 17 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
<script lang="ts">
22
import { handleAddLibraryFolder } from '$lib/services/library.service';
33
import type { LibraryResponseDto } from '@immich/sdk';
4-
import { Button, Field, HStack, Input, Modal, ModalBody, ModalFooter, Text } from '@immich/ui';
4+
import { Field, FormModal, Input, Text } from '@immich/ui';
55
import { mdiFolderSync } from '@mdi/js';
66
import { t } from 'svelte-i18n';
77
@@ -11,34 +11,26 @@
1111
};
1212
1313
const { library, onClose }: Props = $props();
14-
let folder = $state('');
15-
16-
const onsubmit = async () => {
17-
const success = await handleAddLibraryFolder(library, folder);
14+
let value = $state('');
1815
16+
const onSubmit = async () => {
17+
const success = await handleAddLibraryFolder(library, value);
1918
if (success) {
2019
onClose();
2120
}
2221
};
2322
</script>
2423

25-
<Modal title={$t('library_add_folder')} icon={mdiFolderSync} {onClose} size="small">
26-
<ModalBody>
27-
<form {onsubmit} autocomplete="off" id="library-import-path-form">
28-
<Text size="small" class="mb-4">{$t('admin.library_folder_description')}</Text>
29-
30-
<Field label={$t('path')}>
31-
<Input bind:value={folder} />
32-
</Field>
33-
</form>
34-
</ModalBody>
35-
36-
<ModalFooter>
37-
<HStack fullWidth>
38-
<Button shape="round" color="secondary" fullWidth onclick={() => onClose()}>{$t('cancel')}</Button>
39-
<Button shape="round" type="submit" fullWidth form="library-import-path-form">
40-
{$t('add')}
41-
</Button>
42-
</HStack>
43-
</ModalFooter>
44-
</Modal>
24+
<FormModal
25+
title={$t('library_add_folder')}
26+
icon={mdiFolderSync}
27+
{onClose}
28+
{onSubmit}
29+
size="small"
30+
submitText={$t('add')}
31+
>
32+
<Text size="small" class="mb-4">{$t('admin.library_folder_description')}</Text>
33+
<Field label={$t('path')}>
34+
<Input bind:value />
35+
</Field>
36+
</FormModal>
Lines changed: 11 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
<script lang="ts">
22
import { handleEditLibraryFolder } from '$lib/services/library.service';
33
import type { LibraryResponseDto } from '@immich/sdk';
4-
import { Button, Field, HStack, Input, Modal, ModalBody, ModalFooter, Text } from '@immich/ui';
4+
import { Field, FormModal, Input, Text } from '@immich/ui';
55
import { mdiFolderSync } from '@mdi/js';
66
import { t } from 'svelte-i18n';
77
@@ -11,35 +11,21 @@
1111
onClose: () => void;
1212
};
1313
14-
const { library, folder, onClose }: Props = $props();
14+
const { library, folder: oldValue, onClose }: Props = $props();
1515
16-
let newFolder = $state(folder);
16+
let newValue = $state(oldValue);
1717
18-
const onsubmit = async () => {
19-
const success = await handleEditLibraryFolder(library, folder, newFolder);
18+
const onSubmit = async () => {
19+
const success = await handleEditLibraryFolder(library, oldValue, newValue);
2020
if (success) {
2121
onClose();
2222
}
2323
};
2424
</script>
2525

26-
<Modal title={$t('library_edit_folder')} icon={mdiFolderSync} {onClose} size="small">
27-
<ModalBody>
28-
<form {onsubmit} autocomplete="off" id="library-import-path-form">
29-
<Text size="small" class="mb-4">{$t('admin.library_folder_description')}</Text>
30-
31-
<Field label={$t('path')}>
32-
<Input bind:value={newFolder} />
33-
</Field>
34-
</form>
35-
</ModalBody>
36-
37-
<ModalFooter>
38-
<HStack fullWidth>
39-
<Button shape="round" color="secondary" fullWidth onclick={() => onClose()}>{$t('cancel')}</Button>
40-
<Button shape="round" type="submit" fullWidth form="library-import-path-form">
41-
{$t('save')}
42-
</Button>
43-
</HStack>
44-
</ModalFooter>
45-
</Modal>
26+
<FormModal title={$t('library_edit_folder')} icon={mdiFolderSync} {onClose} {onSubmit} size="small">
27+
<Text size="small" class="mb-4">{$t('admin.library_folder_description')}</Text>
28+
<Field label={$t('path')}>
29+
<Input bind:value={newValue} />
30+
</Field>
31+
</FormModal>

0 commit comments

Comments
 (0)