mirror of
https://github.com/immich-app/immich.git
synced 2024-12-28 06:31:58 +00:00
feat(web,a11y): consolidate BaseModal into FullScreenModal (#8787)
* feat(web,a11y): FullScreenModal sticky buttons * chore(web): combine BaseModal into FullScreenModal --------- Co-authored-by: Alex <alex.tran1502@gmail.com>
This commit is contained in:
parent
28f591d01b
commit
bcdec25843
29 changed files with 251 additions and 337 deletions
|
@ -7,7 +7,6 @@
|
|||
import EditAlbumForm from '$lib/components/forms/edit-album-form.svelte';
|
||||
import ConfirmDialogue from '$lib/components/shared-components/confirm-dialogue.svelte';
|
||||
import CreateSharedLinkModal from '$lib/components/shared-components/create-share-link-modal/create-shared-link-modal.svelte';
|
||||
import FullScreenModal from '$lib/components/shared-components/full-screen-modal.svelte';
|
||||
import {
|
||||
NotificationType,
|
||||
notificationController,
|
||||
|
@ -432,9 +431,12 @@
|
|||
{#if allowEdit}
|
||||
<!-- Edit Modal -->
|
||||
{#if albumToEdit}
|
||||
<FullScreenModal id="edit-album-modal" title="Edit album" width="wide" onClose={() => (albumToEdit = null)}>
|
||||
<EditAlbumForm album={albumToEdit} onEditSuccess={successEditAlbumInfo} onCancel={() => (albumToEdit = null)} />
|
||||
</FullScreenModal>
|
||||
<EditAlbumForm
|
||||
album={albumToEdit}
|
||||
onEditSuccess={successEditAlbumInfo}
|
||||
onCancel={() => (albumToEdit = null)}
|
||||
onClose={() => (albumToEdit = null)}
|
||||
/>
|
||||
{/if}
|
||||
|
||||
<!-- Share Modal -->
|
||||
|
|
|
@ -5,12 +5,12 @@
|
|||
import { getContextMenuPosition } from '../../utils/context-menu';
|
||||
import { handleError } from '../../utils/handle-error';
|
||||
import CircleIconButton from '../elements/buttons/circle-icon-button.svelte';
|
||||
import BaseModal from '../shared-components/base-modal.svelte';
|
||||
import ConfirmDialogue from '../shared-components/confirm-dialogue.svelte';
|
||||
import ContextMenu from '../shared-components/context-menu/context-menu.svelte';
|
||||
import MenuOption from '../shared-components/context-menu/menu-option.svelte';
|
||||
import { NotificationType, notificationController } from '../shared-components/notification/notification';
|
||||
import UserAvatar from '../shared-components/user-avatar.svelte';
|
||||
import FullScreenModal from '$lib/components/shared-components/full-screen-modal.svelte';
|
||||
|
||||
export let album: AlbumResponseDto;
|
||||
export let onClose: () => void;
|
||||
|
@ -66,7 +66,7 @@
|
|||
</script>
|
||||
|
||||
{#if !selectedRemoveUser}
|
||||
<BaseModal id="share-info-modal" title="Options" {onClose}>
|
||||
<FullScreenModal id="share-info-modal" title="Options" {onClose}>
|
||||
<section class="immich-scrollbar max-h-[400px] overflow-y-auto pb-4">
|
||||
<div class="flex w-full place-items-center justify-between gap-4 p-5">
|
||||
<div class="flex place-items-center gap-4">
|
||||
|
@ -80,7 +80,7 @@
|
|||
</div>
|
||||
{#each album.sharedUsers as user}
|
||||
<div
|
||||
class="flex w-full place-items-center justify-between gap-4 p-5 transition-colors hover:bg-gray-50 dark:hover:bg-gray-700"
|
||||
class="flex w-full place-items-center justify-between gap-4 p-5 rounded-xl transition-colors hover:bg-gray-50 dark:hover:bg-gray-700"
|
||||
>
|
||||
<div class="flex place-items-center gap-4">
|
||||
<UserAvatar {user} size="md" />
|
||||
|
@ -116,7 +116,7 @@
|
|||
</div>
|
||||
{/each}
|
||||
</section>
|
||||
</BaseModal>
|
||||
</FullScreenModal>
|
||||
{/if}
|
||||
|
||||
{#if selectedRemoveUser && selectedRemoveUser?.id === currentUser?.id}
|
||||
|
|
|
@ -12,8 +12,8 @@
|
|||
import { mdiCheck, mdiLink, mdiShareCircle } from '@mdi/js';
|
||||
import { createEventDispatcher, onMount } from 'svelte';
|
||||
import Button from '../elements/buttons/button.svelte';
|
||||
import BaseModal from '../shared-components/base-modal.svelte';
|
||||
import UserAvatar from '../shared-components/user-avatar.svelte';
|
||||
import FullScreenModal from '$lib/components/shared-components/full-screen-modal.svelte';
|
||||
|
||||
export let album: AlbumResponseDto;
|
||||
export let onClose: () => void;
|
||||
|
@ -54,7 +54,7 @@
|
|||
};
|
||||
</script>
|
||||
|
||||
<BaseModal id="user-selection-modal" title="Invite to album" showLogo {onClose}>
|
||||
<FullScreenModal id="user-selection-modal" title="Invite to album" showLogo {onClose}>
|
||||
{#if selectedUsers.length > 0}
|
||||
<div class="mb-2 flex flex-wrap place-items-center gap-4 overflow-x-auto px-5 py-2 sticky">
|
||||
<p class="font-medium">To</p>
|
||||
|
@ -75,13 +75,13 @@
|
|||
|
||||
<div class="immich-scrollbar max-h-[500px] overflow-y-auto">
|
||||
{#if users.length > 0}
|
||||
<p class="px-5 text-xs font-medium">SUGGESTIONS</p>
|
||||
<p class="text-xs font-medium">SUGGESTIONS</p>
|
||||
|
||||
<div class="my-4">
|
||||
{#each users as user}
|
||||
<button
|
||||
on:click={() => handleSelect(user)}
|
||||
class="flex w-full place-items-center gap-4 px-5 py-4 transition-all hover:bg-gray-200 dark:hover:bg-gray-700"
|
||||
class="flex w-full place-items-center gap-4 px-5 py-4 transition-all hover:bg-gray-200 dark:hover:bg-gray-700 rounded-xl"
|
||||
>
|
||||
{#if selectedUsers.includes(user)}
|
||||
<div
|
||||
|
@ -112,7 +112,7 @@
|
|||
</div>
|
||||
|
||||
{#if users.length > 0}
|
||||
<div class="p-3">
|
||||
<div class="py-3">
|
||||
<Button
|
||||
size="sm"
|
||||
fullwidth
|
||||
|
@ -125,7 +125,7 @@
|
|||
|
||||
<hr />
|
||||
|
||||
<div id="shared-buttons" class="my-4 flex place-content-center place-items-center justify-around">
|
||||
<div id="shared-buttons" class="mt-4 flex place-content-center place-items-center justify-around">
|
||||
<button
|
||||
class="flex flex-col place-content-center place-items-center gap-2 hover:cursor-pointer"
|
||||
on:click={() => dispatch('share')}
|
||||
|
@ -144,4 +144,4 @@
|
|||
</button>
|
||||
{/if}
|
||||
</div>
|
||||
</BaseModal>
|
||||
</FullScreenModal>
|
||||
|
|
|
@ -29,7 +29,7 @@
|
|||
|
||||
<button
|
||||
on:click={() => dispatch('album')}
|
||||
class="flex w-full gap-4 px-6 py-2 text-left transition-colors hover:bg-gray-200 dark:hover:bg-gray-700"
|
||||
class="flex w-full gap-4 px-6 py-2 text-left transition-colors hover:bg-gray-200 dark:hover:bg-gray-700 rounded-xl"
|
||||
>
|
||||
<div class="h-12 w-12 shrink-0 rounded-xl bg-slate-300">
|
||||
{#if album.albumThumbnailAssetId}
|
||||
|
|
|
@ -27,6 +27,7 @@
|
|||
export let fullwidth = false;
|
||||
export let border = false;
|
||||
export let title: string | undefined = '';
|
||||
export let form: string | undefined = undefined;
|
||||
|
||||
let className = '';
|
||||
export { className as class };
|
||||
|
@ -65,6 +66,7 @@
|
|||
{type}
|
||||
{disabled}
|
||||
{title}
|
||||
{form}
|
||||
on:click
|
||||
on:focus
|
||||
on:blur
|
||||
|
|
|
@ -102,8 +102,8 @@
|
|||
<div class="flex px-4 pt-2">
|
||||
<p class="text-sm text-gray-500 dark:text-gray-300">They will be merged together</p>
|
||||
</div>
|
||||
<div class="mt-8 flex w-full gap-4 pb-4">
|
||||
<svelte:fragment slot="sticky-bottom">
|
||||
<Button fullwidth color="gray" on:click={() => dispatch('reject')}>No</Button>
|
||||
<Button fullwidth on:click={() => dispatch('confirm', [personMerge1, personMerge2])}>Yes</Button>
|
||||
</div>
|
||||
</svelte:fragment>
|
||||
</FullScreenModal>
|
||||
|
|
|
@ -20,14 +20,14 @@
|
|||
};
|
||||
</script>
|
||||
|
||||
<FullScreenModal id="set-birthday-modal" title="Set date of birth" icon={mdiCake} onClose={handleCancel}>
|
||||
<FullScreenModal id="set-birth-date-modal" title="Set date of birth" icon={mdiCake} onClose={handleCancel}>
|
||||
<div class="text-immich-primary dark:text-immich-dark-primary">
|
||||
<p class="text-sm dark:text-immich-dark-fg">
|
||||
Date of birth is used to calculate the age of this person at the time of a photo.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<form on:submit|preventDefault={() => handleSubmit()} autocomplete="off">
|
||||
<form on:submit|preventDefault={() => handleSubmit()} autocomplete="off" id="set-birth-date-form">
|
||||
<div class="my-4 flex flex-col gap-2">
|
||||
<DateInput
|
||||
class="immich-form-input"
|
||||
|
@ -38,9 +38,9 @@
|
|||
max={todayFormatted}
|
||||
/>
|
||||
</div>
|
||||
<div class="mt-8 flex w-full gap-4">
|
||||
<Button color="gray" fullwidth on:click={() => handleCancel()}>Cancel</Button>
|
||||
<Button type="submit" fullwidth>Set</Button>
|
||||
</div>
|
||||
</form>
|
||||
<svelte:fragment slot="sticky-bottom">
|
||||
<Button color="gray" fullwidth on:click={() => handleCancel()}>Cancel</Button>
|
||||
<Button type="submit" fullwidth form="set-birth-date-form">Set</Button>
|
||||
</svelte:fragment>
|
||||
</FullScreenModal>
|
||||
|
|
|
@ -29,15 +29,14 @@
|
|||
</script>
|
||||
|
||||
<FullScreenModal id="api-key-modal" {title} icon={mdiKeyVariant} onClose={handleCancel}>
|
||||
<form on:submit|preventDefault={handleSubmit} autocomplete="off">
|
||||
<form on:submit|preventDefault={handleSubmit} autocomplete="off" id="api-key-form">
|
||||
<div class="mb-4 flex flex-col gap-2">
|
||||
<label class="immich-form-label" for="name">Name</label>
|
||||
<input class="immich-form-input" id="name" name="name" type="text" bind:value={apiKey.name} />
|
||||
</div>
|
||||
|
||||
<div class="mt-8 flex w-full gap-4">
|
||||
<Button color="gray" fullwidth on:click={handleCancel}>{cancelText}</Button>
|
||||
<Button type="submit" fullwidth>{submitText}</Button>
|
||||
</div>
|
||||
</form>
|
||||
<svelte:fragment slot="sticky-bottom">
|
||||
<Button color="gray" fullwidth on:click={handleCancel}>{cancelText}</Button>
|
||||
<Button type="submit" fullwidth form="api-key-form">{submitText}</Button>
|
||||
</svelte:fragment>
|
||||
</FullScreenModal>
|
||||
|
|
|
@ -31,10 +31,10 @@
|
|||
<textarea class="immich-form-input" id="secret" name="secret" readonly={true} value={secret} />
|
||||
</div>
|
||||
|
||||
<div class="mt-8 flex w-full gap-4">
|
||||
<svelte:fragment slot="sticky-bottom">
|
||||
{#if canCopyImagesToClipboard}
|
||||
<Button on:click={() => copyToClipboard(secret)} fullwidth>Copy to Clipboard</Button>
|
||||
{/if}
|
||||
<Button on:click={() => handleDone()} fullwidth>Done</Button>
|
||||
</div>
|
||||
</svelte:fragment>
|
||||
</FullScreenModal>
|
||||
|
|
|
@ -7,6 +7,9 @@
|
|||
import Button from '../elements/buttons/button.svelte';
|
||||
import PasswordField from '../shared-components/password-field.svelte';
|
||||
import Slider from '../elements/slider.svelte';
|
||||
import FullScreenModal from '$lib/components/shared-components/full-screen-modal.svelte';
|
||||
|
||||
export let onClose: () => void;
|
||||
|
||||
let error: string;
|
||||
let success: string;
|
||||
|
@ -68,53 +71,55 @@
|
|||
}
|
||||
</script>
|
||||
|
||||
<form on:submit|preventDefault={registerUser} autocomplete="off">
|
||||
<div class="my-4 flex flex-col gap-2">
|
||||
<label class="immich-form-label" for="email">Email</label>
|
||||
<input class="immich-form-input" id="email" bind:value={email} type="email" required />
|
||||
</div>
|
||||
<FullScreenModal id="create-new-user-modal" title="Create new user" showLogo {onClose}>
|
||||
<form on:submit|preventDefault={registerUser} autocomplete="off" id="create-new-user-form">
|
||||
<div class="my-4 flex flex-col gap-2">
|
||||
<label class="immich-form-label" for="email">Email</label>
|
||||
<input class="immich-form-input" id="email" bind:value={email} type="email" required />
|
||||
</div>
|
||||
|
||||
<div class="my-4 flex flex-col gap-2">
|
||||
<label class="immich-form-label" for="password">Password</label>
|
||||
<PasswordField id="password" bind:password autocomplete="new-password" />
|
||||
</div>
|
||||
<div class="my-4 flex flex-col gap-2">
|
||||
<label class="immich-form-label" for="password">Password</label>
|
||||
<PasswordField id="password" bind:password autocomplete="new-password" />
|
||||
</div>
|
||||
|
||||
<div class="my-4 flex flex-col gap-2">
|
||||
<label class="immich-form-label" for="confirmPassword">Confirm Password</label>
|
||||
<PasswordField id="confirmPassword" bind:password={confirmPassword} autocomplete="new-password" />
|
||||
</div>
|
||||
<div class="my-4 flex flex-col gap-2">
|
||||
<label class="immich-form-label" for="confirmPassword">Confirm Password</label>
|
||||
<PasswordField id="confirmPassword" bind:password={confirmPassword} autocomplete="new-password" />
|
||||
</div>
|
||||
|
||||
<div class="my-4 flex place-items-center justify-between gap-2">
|
||||
<label class="text-sm dark:text-immich-dark-fg" for="require-password-change">
|
||||
Require user to change password on first login
|
||||
</label>
|
||||
<Slider id="require-password-change" bind:checked={shouldChangePassword} />
|
||||
</div>
|
||||
<div class="my-4 flex place-items-center justify-between gap-2">
|
||||
<label class="text-sm dark:text-immich-dark-fg" for="require-password-change">
|
||||
Require user to change password on first login
|
||||
</label>
|
||||
<Slider id="require-password-change" bind:checked={shouldChangePassword} />
|
||||
</div>
|
||||
|
||||
<div class="my-4 flex flex-col gap-2">
|
||||
<label class="immich-form-label" for="name">Name</label>
|
||||
<input class="immich-form-input" id="name" bind:value={name} type="text" required />
|
||||
</div>
|
||||
<div class="my-4 flex flex-col gap-2">
|
||||
<label class="immich-form-label" for="name">Name</label>
|
||||
<input class="immich-form-input" id="name" bind:value={name} type="text" required />
|
||||
</div>
|
||||
|
||||
<div class="my-4 flex flex-col gap-2">
|
||||
<label class="flex items-center gap-2 immich-form-label" for="quotaSize">
|
||||
Quota Size (GiB)
|
||||
{#if quotaSizeWarning}
|
||||
<p class="text-red-400 text-sm">You set a quota higher than the disk size</p>
|
||||
{/if}
|
||||
</label>
|
||||
<input class="immich-form-input" id="quotaSize" type="number" min="0" bind:value={quotaSize} />
|
||||
</div>
|
||||
<div class="my-4 flex flex-col gap-2">
|
||||
<label class="flex items-center gap-2 immich-form-label" for="quotaSize">
|
||||
Quota Size (GiB)
|
||||
{#if quotaSizeWarning}
|
||||
<p class="text-red-400 text-sm">You set a quota higher than the disk size</p>
|
||||
{/if}
|
||||
</label>
|
||||
<input class="immich-form-input" id="quotaSize" type="number" min="0" bind:value={quotaSize} />
|
||||
</div>
|
||||
|
||||
{#if error}
|
||||
<p class="text-sm text-red-400">{error}</p>
|
||||
{/if}
|
||||
{#if error}
|
||||
<p class="text-sm text-red-400">{error}</p>
|
||||
{/if}
|
||||
|
||||
{#if success}
|
||||
<p class="text-sm text-immich-primary">{success}</p>
|
||||
{/if}
|
||||
<div class="flex w-full gap-4 pt-4">
|
||||
{#if success}
|
||||
<p class="text-sm text-immich-primary">{success}</p>
|
||||
{/if}
|
||||
</form>
|
||||
<svelte:fragment slot="sticky-bottom">
|
||||
<Button color="gray" fullwidth on:click={() => dispatch('cancel')}>Cancel</Button>
|
||||
<Button type="submit" disabled={isCreatingUser} fullwidth>Create</Button>
|
||||
</div>
|
||||
</form>
|
||||
<Button type="submit" disabled={isCreatingUser} fullwidth form="create-new-user-form">Create</Button>
|
||||
</svelte:fragment>
|
||||
</FullScreenModal>
|
||||
|
|
|
@ -3,10 +3,12 @@
|
|||
import { handleError } from '$lib/utils/handle-error';
|
||||
import Button from '$lib/components/elements/buttons/button.svelte';
|
||||
import AlbumCover from '$lib/components/album-page/album-cover.svelte';
|
||||
import FullScreenModal from '$lib/components/shared-components/full-screen-modal.svelte';
|
||||
|
||||
export let album: AlbumResponseDto;
|
||||
export let onEditSuccess: ((album: AlbumResponseDto) => unknown) | undefined = undefined;
|
||||
export let onCancel: (() => unknown) | undefined = undefined;
|
||||
export let onClose: () => void;
|
||||
|
||||
let albumName = album.albumName;
|
||||
let description = album.description;
|
||||
|
@ -34,29 +36,28 @@
|
|||
};
|
||||
</script>
|
||||
|
||||
<form on:submit|preventDefault={handleUpdateAlbumInfo} autocomplete="off">
|
||||
<div class="flex items-center">
|
||||
<div class="hidden sm:flex">
|
||||
<AlbumCover {album} css="h-[200px] w-[200px] m-4 shadow-lg" />
|
||||
</div>
|
||||
|
||||
<div class="flex-grow">
|
||||
<div class="m-4 flex flex-col gap-2">
|
||||
<label class="immich-form-label" for="name">Name</label>
|
||||
<input class="immich-form-input" id="name" type="text" bind:value={albumName} />
|
||||
<FullScreenModal id="edit-album-modal" title="Edit album" width="wide" {onClose}>
|
||||
<form on:submit|preventDefault={handleUpdateAlbumInfo} autocomplete="off" id="edit-album-form">
|
||||
<div class="flex items-center">
|
||||
<div class="hidden sm:flex">
|
||||
<AlbumCover {album} css="h-[200px] w-[200px] m-4 shadow-lg" />
|
||||
</div>
|
||||
|
||||
<div class="m-4 flex flex-col gap-2">
|
||||
<label class="immich-form-label" for="description">Description</label>
|
||||
<textarea class="immich-form-input" id="description" bind:value={description} />
|
||||
<div class="flex-grow">
|
||||
<div class="m-4 flex flex-col gap-2">
|
||||
<label class="immich-form-label" for="name">Name</label>
|
||||
<input class="immich-form-input" id="name" type="text" bind:value={albumName} />
|
||||
</div>
|
||||
|
||||
<div class="m-4 flex flex-col gap-2">
|
||||
<label class="immich-form-label" for="description">Description</label>
|
||||
<textarea class="immich-form-input" id="description" bind:value={description} />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="flex justify-center">
|
||||
<div class="mt-8 flex w-full sm:w-2/3 gap-4">
|
||||
<Button color="gray" fullwidth on:click={() => onCancel?.()}>Cancel</Button>
|
||||
<Button type="submit" fullwidth disabled={isSubmitting}>OK</Button>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</form>
|
||||
<svelte:fragment slot="sticky-bottom">
|
||||
<Button color="gray" fullwidth on:click={() => onCancel?.()}>Cancel</Button>
|
||||
<Button type="submit" fullwidth disabled={isSubmitting} form="edit-album-form">OK</Button>
|
||||
</svelte:fragment>
|
||||
</FullScreenModal>
|
||||
|
|
|
@ -7,10 +7,13 @@
|
|||
import { updateUser, type UserResponseDto } from '@immich/sdk';
|
||||
import { createEventDispatcher } from 'svelte';
|
||||
import Button from '../elements/buttons/button.svelte';
|
||||
import FullScreenModal from '$lib/components/shared-components/full-screen-modal.svelte';
|
||||
import { mdiAccountEditOutline } from '@mdi/js';
|
||||
|
||||
export let user: UserResponseDto;
|
||||
export let canResetPassword = true;
|
||||
export let newPassword: string;
|
||||
export let onClose: () => void;
|
||||
|
||||
let error: string;
|
||||
let success: string;
|
||||
|
@ -87,59 +90,63 @@
|
|||
}
|
||||
</script>
|
||||
|
||||
<form on:submit|preventDefault={editUser} autocomplete="off">
|
||||
<div class="my-4 flex flex-col gap-2">
|
||||
<label class="immich-form-label" for="email">Email</label>
|
||||
<input class="immich-form-input" id="email" name="email" type="email" bind:value={user.email} />
|
||||
</div>
|
||||
<FullScreenModal id="edit-user-modal" title="Edit user" icon={mdiAccountEditOutline} {onClose}>
|
||||
<form on:submit|preventDefault={editUser} autocomplete="off" id="edit-user-form">
|
||||
<div class="my-4 flex flex-col gap-2">
|
||||
<label class="immich-form-label" for="email">Email</label>
|
||||
<input class="immich-form-input" id="email" name="email" type="email" bind:value={user.email} />
|
||||
</div>
|
||||
|
||||
<div class="my-4 flex flex-col gap-2">
|
||||
<label class="immich-form-label" for="name">Name</label>
|
||||
<input class="immich-form-input" id="name" name="name" type="text" required bind:value={user.name} />
|
||||
</div>
|
||||
<div class="my-4 flex flex-col gap-2">
|
||||
<label class="immich-form-label" for="name">Name</label>
|
||||
<input class="immich-form-input" id="name" name="name" type="text" required bind:value={user.name} />
|
||||
</div>
|
||||
|
||||
<div class="my-4 flex flex-col gap-2">
|
||||
<label class="flex items-center gap-2 immich-form-label" for="quotaSize"
|
||||
>Quota Size (GiB) {#if quotaSizeWarning}
|
||||
<p class="text-red-400 text-sm">You set a quota higher than the disk size</p>
|
||||
{/if}</label
|
||||
>
|
||||
<input class="immich-form-input" id="quotaSize" name="quotaSize" type="number" min="0" bind:value={quotaSize} />
|
||||
<p>Note: Enter 0 for unlimited quota</p>
|
||||
</div>
|
||||
<div class="my-4 flex flex-col gap-2">
|
||||
<label class="flex items-center gap-2 immich-form-label" for="quotaSize"
|
||||
>Quota Size (GiB) {#if quotaSizeWarning}
|
||||
<p class="text-red-400 text-sm">You set a quota higher than the disk size</p>
|
||||
{/if}</label
|
||||
>
|
||||
<input class="immich-form-input" id="quotaSize" name="quotaSize" type="number" min="0" bind:value={quotaSize} />
|
||||
<p>Note: Enter 0 for unlimited quota</p>
|
||||
</div>
|
||||
|
||||
<div class="my-4 flex flex-col gap-2">
|
||||
<label class="immich-form-label" for="storage-label">Storage Label</label>
|
||||
<input
|
||||
class="immich-form-input"
|
||||
id="storage-label"
|
||||
name="storage-label"
|
||||
type="text"
|
||||
bind:value={user.storageLabel}
|
||||
/>
|
||||
<div class="my-4 flex flex-col gap-2">
|
||||
<label class="immich-form-label" for="storage-label">Storage Label</label>
|
||||
<input
|
||||
class="immich-form-input"
|
||||
id="storage-label"
|
||||
name="storage-label"
|
||||
type="text"
|
||||
bind:value={user.storageLabel}
|
||||
/>
|
||||
|
||||
<p>
|
||||
Note: To apply the Storage Label to previously uploaded assets, run the
|
||||
<a href={AppRoute.ADMIN_JOBS} class="text-immich-primary dark:text-immich-dark-primary"> Storage Migration Job</a>
|
||||
</p>
|
||||
</div>
|
||||
<p>
|
||||
Note: To apply the Storage Label to previously uploaded assets, run the
|
||||
<a href={AppRoute.ADMIN_JOBS} class="text-immich-primary dark:text-immich-dark-primary">
|
||||
Storage Migration Job</a
|
||||
>
|
||||
</p>
|
||||
</div>
|
||||
|
||||
{#if error}
|
||||
<p class="ml-4 text-sm text-red-400">{error}</p>
|
||||
{/if}
|
||||
{#if error}
|
||||
<p class="ml-4 text-sm text-red-400">{error}</p>
|
||||
{/if}
|
||||
|
||||
{#if success}
|
||||
<p class="ml-4 text-sm text-immich-primary">{success}</p>
|
||||
{/if}
|
||||
<div class="mt-8 flex w-full gap-4">
|
||||
{#if success}
|
||||
<p class="ml-4 text-sm text-immich-primary">{success}</p>
|
||||
{/if}
|
||||
</form>
|
||||
<svelte:fragment slot="sticky-bottom">
|
||||
{#if canResetPassword}
|
||||
<Button color="light-red" fullwidth on:click={() => (isShowResetPasswordConfirmation = true)}
|
||||
>Reset password</Button
|
||||
>
|
||||
{/if}
|
||||
<Button type="submit" fullwidth>Confirm</Button>
|
||||
</div>
|
||||
</form>
|
||||
<Button type="submit" fullwidth form="edit-user-form">Confirm</Button>
|
||||
</svelte:fragment>
|
||||
</FullScreenModal>
|
||||
|
||||
{#if isShowResetPasswordConfirmation}
|
||||
<ConfirmDialogue
|
||||
|
|
|
@ -34,7 +34,7 @@
|
|||
icon={mdiFolderRemove}
|
||||
onClose={handleCancel}
|
||||
>
|
||||
<form on:submit|preventDefault={() => handleSubmit()} autocomplete="off">
|
||||
<form on:submit|preventDefault={() => handleSubmit()} autocomplete="off" id="add-exclusion-pattern-form">
|
||||
<p class="py-5 text-sm">
|
||||
Exclusion patterns lets you ignore files and folders when scanning your library. This is useful if you have
|
||||
folders that contain files you don't want to import, such as RAW files.
|
||||
|
@ -52,18 +52,17 @@
|
|||
bind:value={exclusionPattern}
|
||||
/>
|
||||
</div>
|
||||
<div class="mt-8 flex w-full gap-4">
|
||||
<Button color="gray" fullwidth on:click={() => handleCancel()}>Cancel</Button>
|
||||
{#if isEditing}
|
||||
<Button color="red" fullwidth on:click={() => dispatch('delete')}>Delete</Button>
|
||||
{/if}
|
||||
|
||||
<Button type="submit" disabled={!canSubmit} fullwidth>{submitText}</Button>
|
||||
</div>
|
||||
<div class="mt-8 flex w-full gap-4">
|
||||
{#if isDuplicate}
|
||||
<p class="text-red-500 text-sm">This exclusion pattern already exists.</p>
|
||||
{/if}
|
||||
</div>
|
||||
</form>
|
||||
<svelte:fragment slot="sticky-bottom">
|
||||
<Button color="gray" fullwidth on:click={() => handleCancel()}>Cancel</Button>
|
||||
{#if isEditing}
|
||||
<Button color="red" fullwidth on:click={() => dispatch('delete')}>Delete</Button>
|
||||
{/if}
|
||||
<Button type="submit" disabled={!canSubmit} fullwidth form="add-exclusion-pattern-form">{submitText}</Button>
|
||||
</svelte:fragment>
|
||||
</FullScreenModal>
|
||||
|
|
|
@ -31,7 +31,7 @@
|
|||
</script>
|
||||
|
||||
<FullScreenModal id="library-import-path-modal" {title} icon={mdiFolderSync} onClose={handleCancel}>
|
||||
<form on:submit|preventDefault={() => handleSubmit()} autocomplete="off">
|
||||
<form on:submit|preventDefault={() => handleSubmit()} autocomplete="off" id="library-import-path-form">
|
||||
<p class="py-5 text-sm">
|
||||
Specify a folder to import. This folder, including subfolders, will be scanned for images and videos.
|
||||
</p>
|
||||
|
@ -41,19 +41,17 @@
|
|||
<input class="immich-form-input" id="path" name="path" type="text" bind:value={importPath} />
|
||||
</div>
|
||||
|
||||
<div class="mt-8 flex w-full gap-4">
|
||||
<Button color="gray" fullwidth on:click={() => handleCancel()}>{cancelText}</Button>
|
||||
{#if isEditing}
|
||||
<Button color="red" fullwidth on:click={() => dispatch('delete')}>Delete</Button>
|
||||
{/if}
|
||||
|
||||
<Button type="submit" disabled={!canSubmit} fullwidth>{submitText}</Button>
|
||||
</div>
|
||||
|
||||
<div class="mt-8 flex w-full gap-4">
|
||||
{#if isDuplicate}
|
||||
<p class="text-red-500 text-sm">This import path already exists.</p>
|
||||
{/if}
|
||||
</div>
|
||||
</form>
|
||||
<svelte:fragment slot="sticky-bottom">
|
||||
<Button color="gray" fullwidth on:click={() => handleCancel()}>{cancelText}</Button>
|
||||
{#if isEditing}
|
||||
<Button color="red" fullwidth on:click={() => dispatch('delete')}>Delete</Button>
|
||||
{/if}
|
||||
<Button type="submit" disabled={!canSubmit} fullwidth form="library-import-path-form">{submitText}</Button>
|
||||
</svelte:fragment>
|
||||
</FullScreenModal>
|
||||
|
|
|
@ -33,15 +33,13 @@
|
|||
icon={mdiFolderSync}
|
||||
onClose={handleCancel}
|
||||
>
|
||||
<form on:submit|preventDefault={() => handleSubmit()} autocomplete="off">
|
||||
<form on:submit|preventDefault={() => handleSubmit()} autocomplete="off" id="select-library-owner-form">
|
||||
<p class="p-5 text-sm">NOTE: This cannot be changed later!</p>
|
||||
|
||||
<SettingSelect bind:value={ownerId} options={userOptions} name="user" />
|
||||
|
||||
<div class="mt-8 flex w-full gap-4">
|
||||
<Button color="gray" fullwidth on:click={() => handleCancel()}>Cancel</Button>
|
||||
|
||||
<Button type="submit" fullwidth>Create</Button>
|
||||
</div>
|
||||
</form>
|
||||
<svelte:fragment slot="sticky-bottom">
|
||||
<Button color="gray" fullwidth on:click={() => handleCancel()}>Cancel</Button>
|
||||
<Button type="submit" fullwidth form="select-library-owner-form">Create</Button>
|
||||
</svelte:fragment>
|
||||
</FullScreenModal>
|
||||
|
|
|
@ -25,6 +25,7 @@
|
|||
<form
|
||||
on:submit|preventDefault={() => dispatch('save', settings)}
|
||||
class="flex flex-col gap-4 text-immich-primary dark:text-immich-dark-primary"
|
||||
id="map-settings-form"
|
||||
>
|
||||
<SettingSwitch id="allow-dark-mode" title="Allow dark mode" bind:checked={settings.allowDarkMode} />
|
||||
<SettingSwitch id="only-favorites" title="Only favorites" bind:checked={settings.onlyFavorites} />
|
||||
|
@ -103,10 +104,9 @@
|
|||
</div>
|
||||
</div>
|
||||
{/if}
|
||||
|
||||
<div class="mt-4 flex w-full gap-4">
|
||||
<Button color="gray" size="sm" fullwidth on:click={handleClose}>Cancel</Button>
|
||||
<Button type="submit" size="sm" fullwidth>Save</Button>
|
||||
</div>
|
||||
</form>
|
||||
<svelte:fragment slot="sticky-bottom">
|
||||
<Button color="gray" size="sm" fullwidth on:click={handleClose}>Cancel</Button>
|
||||
<Button type="submit" size="sm" fullwidth form="map-settings-form">Save</Button>
|
||||
</svelte:fragment>
|
||||
</FullScreenModal>
|
||||
|
|
|
@ -4,8 +4,8 @@
|
|||
import { mdiPlus } from '@mdi/js';
|
||||
import { createEventDispatcher, onMount } from 'svelte';
|
||||
import AlbumListItem from '../asset-viewer/album-list-item.svelte';
|
||||
import BaseModal from './base-modal.svelte';
|
||||
import { normalizeSearchString } from '$lib/utils/string-utils';
|
||||
import FullScreenModal from '$lib/components/shared-components/full-screen-modal.svelte';
|
||||
|
||||
let albums: AlbumResponseDto[] = [];
|
||||
let recentAlbums: AlbumResponseDto[] = [];
|
||||
|
@ -52,7 +52,7 @@
|
|||
};
|
||||
</script>
|
||||
|
||||
<BaseModal id="album-selection-modal" title={getTitle()} {onClose}>
|
||||
<FullScreenModal id="album-selection-modal" title={getTitle()} {onClose}>
|
||||
<div class="mb-2 flex max-h-[400px] flex-col">
|
||||
{#if loading}
|
||||
{#each { length: 3 } as _}
|
||||
|
@ -76,7 +76,7 @@
|
|||
<div class="immich-scrollbar overflow-y-auto">
|
||||
<button
|
||||
on:click={handleNew}
|
||||
class="flex w-full items-center gap-4 px-6 py-2 transition-colors hover:bg-gray-200 dark:hover:bg-gray-700"
|
||||
class="flex w-full items-center gap-4 px-6 py-2 transition-colors hover:bg-gray-200 dark:hover:bg-gray-700 rounded-xl"
|
||||
>
|
||||
<div class="flex h-12 w-12 items-center justify-center">
|
||||
<Icon path={mdiPlus} size="30" />
|
||||
|
@ -110,4 +110,4 @@
|
|||
</div>
|
||||
{/if}
|
||||
</div>
|
||||
</BaseModal>
|
||||
</FullScreenModal>
|
||||
|
|
|
@ -1,78 +0,0 @@
|
|||
<script lang="ts">
|
||||
import { fade } from 'svelte/transition';
|
||||
import { quintOut } from 'svelte/easing';
|
||||
import { onMount, onDestroy } from 'svelte';
|
||||
import { browser } from '$app/environment';
|
||||
import { clickOutside } from '$lib/utils/click-outside';
|
||||
import FocusTrap from '$lib/components/shared-components/focus-trap.svelte';
|
||||
import ModalHeader from '$lib/components/shared-components/modal-header.svelte';
|
||||
|
||||
/**
|
||||
* Unique identifier for the modal.
|
||||
*/
|
||||
export let id: string;
|
||||
export let title: string;
|
||||
export let onClose: () => void;
|
||||
export let zIndex = 9999;
|
||||
/**
|
||||
* If true, the logo will be displayed next to the modal title.
|
||||
*/
|
||||
export let showLogo = false;
|
||||
/**
|
||||
* Optional icon to display next to the modal title, if `showLogo` is false.
|
||||
*/
|
||||
export let icon: string | undefined = undefined;
|
||||
|
||||
$: titleId = `${id}-title`;
|
||||
|
||||
onMount(() => {
|
||||
if (browser) {
|
||||
const scrollTop = document.documentElement.scrollTop;
|
||||
const scrollLeft = document.documentElement.scrollLeft;
|
||||
|
||||
/* eslint-disable unicorn/prefer-add-event-listener */
|
||||
window.onscroll = function () {
|
||||
window.scrollTo(scrollLeft, scrollTop);
|
||||
};
|
||||
}
|
||||
});
|
||||
|
||||
onDestroy(() => {
|
||||
if (browser) {
|
||||
/* eslint-disable unicorn/prefer-add-event-listener */
|
||||
window.onscroll = null;
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
<FocusTrap>
|
||||
<div
|
||||
aria-modal="true"
|
||||
aria-labelledby={titleId}
|
||||
style:z-index={zIndex}
|
||||
transition:fade={{ duration: 100, easing: quintOut }}
|
||||
class="fixed left-0 top-0 flex h-full w-full place-content-center place-items-center overflow-hidden bg-black/50"
|
||||
>
|
||||
<div
|
||||
use:clickOutside={{
|
||||
onOutclick: onClose,
|
||||
onEscape: onClose,
|
||||
}}
|
||||
class="min-h-[200px] w-[450px] overflow-y-auto rounded-3xl bg-immich-bg shadow-md dark:bg-immich-dark-gray dark:text-immich-dark-fg immich-scrollbar scroll-pb-20"
|
||||
style="max-height: min(95vh, 800px);"
|
||||
tabindex="-1"
|
||||
>
|
||||
<ModalHeader id={titleId} {title} {showLogo} {icon} {onClose} />
|
||||
|
||||
<div>
|
||||
<slot />
|
||||
</div>
|
||||
|
||||
{#if $$slots['sticky-bottom']}
|
||||
<div class="sticky bottom-0 bg-immich-bg px-5 pb-5 pt-3 dark:bg-immich-dark-gray">
|
||||
<slot name="sticky-bottom" />
|
||||
</div>
|
||||
{/if}
|
||||
</div>
|
||||
</div>
|
||||
</FocusTrap>
|
|
@ -65,7 +65,6 @@
|
|||
<ConfirmDialogue
|
||||
id="edit-date-time-modal"
|
||||
confirmColor="primary"
|
||||
cancelColor="secondary"
|
||||
title="Edit date and time"
|
||||
prompt="Please select a new date:"
|
||||
disabled={!date.isValid}
|
||||
|
|
|
@ -96,7 +96,6 @@
|
|||
<ConfirmDialogue
|
||||
id="change-location-modal"
|
||||
confirmColor="primary"
|
||||
cancelColor="secondary"
|
||||
title="Change location"
|
||||
width="wide"
|
||||
onConfirm={handleConfirm}
|
||||
|
|
|
@ -9,7 +9,7 @@
|
|||
export let confirmText = 'Confirm';
|
||||
export let confirmColor: Color = 'red';
|
||||
export let cancelText = 'Cancel';
|
||||
export let cancelColor: Color = 'primary';
|
||||
export let cancelColor: Color = 'secondary';
|
||||
export let hideCancelButton = false;
|
||||
export let disabled = false;
|
||||
export let width: 'wide' | 'narrow' = 'narrow';
|
||||
|
@ -31,7 +31,7 @@
|
|||
</slot>
|
||||
</div>
|
||||
|
||||
<div class="mt-4 flex flex-col sm:flex-row w-full gap-4">
|
||||
<svelte:fragment slot="sticky-bottom">
|
||||
{#if !hideCancelButton}
|
||||
<Button color={cancelColor} fullwidth on:click={onClose}>
|
||||
{cancelText}
|
||||
|
@ -40,5 +40,5 @@
|
|||
<Button color={confirmColor} fullwidth on:click={handleConfirm} disabled={disabled || isConfirmButtonDisabled}>
|
||||
{confirmText}
|
||||
</Button>
|
||||
</div>
|
||||
</svelte:fragment>
|
||||
</FullScreenModal>
|
||||
|
|
|
@ -8,12 +8,12 @@
|
|||
import { SharedLinkType, createSharedLink, updateSharedLink, type SharedLinkResponseDto } from '@immich/sdk';
|
||||
import { mdiContentCopy, mdiLink } from '@mdi/js';
|
||||
import { createEventDispatcher } from 'svelte';
|
||||
import BaseModal from '../base-modal.svelte';
|
||||
import type { ImmichDropDownOption } from '../dropdown-button.svelte';
|
||||
import DropdownButton from '../dropdown-button.svelte';
|
||||
import { NotificationType, notificationController } from '../notification/notification';
|
||||
import SettingInputField, { SettingInputFieldType } from '../settings/setting-input-field.svelte';
|
||||
import SettingSwitch from '../settings/setting-switch.svelte';
|
||||
import FullScreenModal from '$lib/components/shared-components/full-screen-modal.svelte';
|
||||
|
||||
export let onClose: () => void;
|
||||
export let albumId: string | undefined = undefined;
|
||||
|
@ -159,8 +159,8 @@
|
|||
};
|
||||
</script>
|
||||
|
||||
<BaseModal id="create-shared-link-modal" title={getTitle()} icon={mdiLink} {onClose}>
|
||||
<section class="mx-6 mb-6">
|
||||
<FullScreenModal id="create-shared-link-modal" title={getTitle()} icon={mdiLink} {onClose}>
|
||||
<section>
|
||||
{#if shareType === SharedLinkType.Album}
|
||||
{#if !editingLink}
|
||||
<div>Let anyone with the link see photos and people in this album.</div>
|
||||
|
@ -246,29 +246,22 @@
|
|||
</div>
|
||||
</section>
|
||||
|
||||
<hr />
|
||||
|
||||
<section slot="sticky-bottom">
|
||||
<svelte:fragment slot="sticky-bottom">
|
||||
{#if !sharedLink}
|
||||
{#if editingLink}
|
||||
<div class="flex justify-end">
|
||||
<Button size="sm" on:click={handleEditLink}>Confirm</Button>
|
||||
</div>
|
||||
<Button size="sm" fullwidth on:click={handleEditLink}>Confirm</Button>
|
||||
{:else}
|
||||
<div class="flex justify-end">
|
||||
<Button size="sm" on:click={handleCreateSharedLink}>Create link</Button>
|
||||
</div>
|
||||
<Button size="sm" fullwidth on:click={handleCreateSharedLink}>Create link</Button>
|
||||
{/if}
|
||||
{:else}
|
||||
<div class="flex w-full gap-4">
|
||||
<div class="flex w-full gap-2">
|
||||
<input class="immich-form-input w-full" bind:value={sharedLink} disabled />
|
||||
|
||||
<LinkButton on:click={() => (sharedLink ? copyToClipboard(sharedLink) : '')}>
|
||||
<div class="flex place-items-center gap-2 text-sm">
|
||||
<Icon path={mdiContentCopy} size="18" />
|
||||
<Icon path={mdiContentCopy} ariaLabel="Copy link to clipboard" size="18" />
|
||||
</div>
|
||||
</LinkButton>
|
||||
</div>
|
||||
{/if}
|
||||
</section>
|
||||
</BaseModal>
|
||||
</svelte:fragment>
|
||||
</FullScreenModal>
|
||||
|
|
|
@ -29,6 +29,7 @@
|
|||
export let width: 'wide' | 'narrow' | 'auto' = 'narrow';
|
||||
|
||||
$: titleId = `${id}-title`;
|
||||
$: isStickyBottom = !!$$slots['sticky-bottom'];
|
||||
|
||||
let modalWidth: string;
|
||||
$: {
|
||||
|
@ -50,15 +51,25 @@
|
|||
>
|
||||
<div
|
||||
class="z-[9999] max-w-[95vw] max-h-[95vh] {modalWidth} overflow-y-auto rounded-3xl bg-immich-bg shadow-md dark:bg-immich-dark-gray dark:text-immich-dark-fg immich-scrollbar"
|
||||
style="max-height: min(95vh, 900px);"
|
||||
use:clickOutside={{ onOutclick: onClose, onEscape: onClose }}
|
||||
tabindex="-1"
|
||||
aria-modal="true"
|
||||
aria-labelledby={titleId}
|
||||
class:scroll-pb-40={isStickyBottom}
|
||||
class:sm:scroll-p-24={isStickyBottom}
|
||||
>
|
||||
<ModalHeader id={titleId} {title} {showLogo} {icon} {onClose} />
|
||||
<div class="p-5 pt-0">
|
||||
<slot />
|
||||
</div>
|
||||
{#if isStickyBottom}
|
||||
<div
|
||||
class="flex flex-col sm:flex-row justify-end w-full gap-2 sm:gap-4 sticky bottom-0 py-4 px-5 bg-immich-bg dark:bg-immich-dark-gray border-t border-gray-200 dark:border-gray-500 shadow"
|
||||
>
|
||||
<slot name="sticky-bottom" />
|
||||
</div>
|
||||
{/if}
|
||||
</div>
|
||||
</section>
|
||||
</FocusTrap>
|
||||
|
|
|
@ -6,8 +6,8 @@
|
|||
import { onMount } from 'svelte';
|
||||
import PhotoViewer from '../asset-viewer/photo-viewer.svelte';
|
||||
import Button from '../elements/buttons/button.svelte';
|
||||
import BaseModal from './base-modal.svelte';
|
||||
import { NotificationType, notificationController } from './notification/notification';
|
||||
import FullScreenModal from '$lib/components/shared-components/full-screen-modal.svelte';
|
||||
|
||||
export let asset: AssetResponseDto;
|
||||
export let onClose: () => void;
|
||||
|
@ -69,18 +69,15 @@
|
|||
};
|
||||
</script>
|
||||
|
||||
<BaseModal id="profile-image-cropper" title="Set profile picture" {onClose}>
|
||||
<FullScreenModal id="profile-image-cropper" title="Set profile picture" width="auto" {onClose}>
|
||||
<div class="flex place-items-center items-center justify-center">
|
||||
<div
|
||||
class="relative flex aspect-square w-1/2 overflow-hidden rounded-full border-4 border-immich-primary bg-immich-dark-primary dark:border-immich-dark-primary dark:bg-immich-primary"
|
||||
class="relative flex aspect-square w-[250px] overflow-hidden rounded-full border-4 border-immich-primary bg-immich-dark-primary dark:border-immich-dark-primary dark:bg-immich-primary"
|
||||
>
|
||||
<PhotoViewer bind:element={imgElement} {asset} />
|
||||
</div>
|
||||
</div>
|
||||
<span class="flex justify-end p-4">
|
||||
<Button on:click={handleSetProfilePicture}>
|
||||
<p>Set as profile picture</p>
|
||||
</Button>
|
||||
</span>
|
||||
<div class="mb-2 flex max-h-[400px] flex-col" />
|
||||
</BaseModal>
|
||||
<svelte:fragment slot="sticky-bottom">
|
||||
<Button fullwidth on:click={handleSetProfilePicture}>Set as profile picture</Button>
|
||||
</svelte:fragment>
|
||||
</FullScreenModal>
|
||||
|
|
|
@ -53,8 +53,8 @@
|
|||
<code>Latest Version: {releaseVersion}</code>
|
||||
</div>
|
||||
|
||||
<div class="mt-8 text-right">
|
||||
<svelte:fragment slot="sticky-bottom">
|
||||
<Button fullwidth on:click={onAcknowledge}>Acknowledge</Button>
|
||||
</div>
|
||||
</svelte:fragment>
|
||||
</FullScreenModal>
|
||||
{/if}
|
||||
|
|
|
@ -46,6 +46,8 @@
|
|||
min={1}
|
||||
bind:value={$slideshowDelay}
|
||||
/>
|
||||
<Button class="w-full" color="gray" on:click={onClose}>Done</Button>
|
||||
</div>
|
||||
<svelte:fragment slot="sticky-bottom">
|
||||
<Button fullwidth color="primary" on:click={onClose}>Done</Button>
|
||||
</svelte:fragment>
|
||||
</FullScreenModal>
|
||||
|
|
|
@ -2,8 +2,8 @@
|
|||
import { getAllUsers, getPartners, type UserResponseDto } from '@immich/sdk';
|
||||
import { createEventDispatcher, onMount } from 'svelte';
|
||||
import Button from '../elements/buttons/button.svelte';
|
||||
import BaseModal from '../shared-components/base-modal.svelte';
|
||||
import UserAvatar from '../shared-components/user-avatar.svelte';
|
||||
import FullScreenModal from '$lib/components/shared-components/full-screen-modal.svelte';
|
||||
|
||||
export let user: UserResponseDto;
|
||||
export let onClose: () => void;
|
||||
|
@ -33,13 +33,13 @@
|
|||
};
|
||||
</script>
|
||||
|
||||
<BaseModal id="partner-selection-modal" title="Add partner" showLogo {onClose}>
|
||||
<FullScreenModal id="partner-selection-modal" title="Add partner" showLogo {onClose}>
|
||||
<div class="immich-scrollbar max-h-[300px] overflow-y-auto">
|
||||
{#if availableUsers.length > 0}
|
||||
{#each availableUsers as user}
|
||||
<button
|
||||
on:click={() => selectUser(user)}
|
||||
class="flex w-full place-items-center gap-4 px-5 py-4 transition-all hover:bg-gray-200 dark:hover:bg-gray-700"
|
||||
class="flex w-full place-items-center gap-4 px-5 py-4 transition-all hover:bg-gray-200 dark:hover:bg-gray-700 rounded-xl"
|
||||
>
|
||||
{#if selectedUsers.includes(user)}
|
||||
<span
|
||||
|
@ -61,15 +61,15 @@
|
|||
</button>
|
||||
{/each}
|
||||
{:else}
|
||||
<p class="p-5 text-sm">
|
||||
<p class="py-5 text-sm">
|
||||
Looks like you shared your photos with all users or you don't have any user to share with.
|
||||
</p>
|
||||
{/if}
|
||||
|
||||
{#if selectedUsers.length > 0}
|
||||
<div class="flex place-content-end p-5">
|
||||
<Button size="sm" rounded="lg" on:click={() => dispatch('add-users', selectedUsers)}>Add</Button>
|
||||
<div class="pt-5">
|
||||
<Button size="sm" fullwidth on:click={() => dispatch('add-users', selectedUsers)}>Add</Button>
|
||||
</div>
|
||||
{/if}
|
||||
</div>
|
||||
</BaseModal>
|
||||
</FullScreenModal>
|
||||
|
|
|
@ -465,24 +465,22 @@
|
|||
|
||||
{#if showChangeNameModal}
|
||||
<FullScreenModal id="change-name-modal" title="Change name" onClose={() => (showChangeNameModal = false)}>
|
||||
<form on:submit|preventDefault={submitNameChange} autocomplete="off">
|
||||
<form on:submit|preventDefault={submitNameChange} autocomplete="off" id="change-name-form">
|
||||
<div class="flex flex-col gap-2">
|
||||
<label class="immich-form-label" for="name">Name</label>
|
||||
<!-- svelte-ignore a11y-autofocus -->
|
||||
<input class="immich-form-input" id="name" name="name" type="text" bind:value={personName} autofocus />
|
||||
</div>
|
||||
|
||||
<div class="mt-8 flex w-full gap-4">
|
||||
<Button
|
||||
color="gray"
|
||||
fullwidth
|
||||
on:click={() => {
|
||||
showChangeNameModal = false;
|
||||
}}>Cancel</Button
|
||||
>
|
||||
<Button type="submit" fullwidth>Ok</Button>
|
||||
<input class="immich-form-input" id="name" name="name" type="text" bind:value={personName} />
|
||||
</div>
|
||||
</form>
|
||||
<svelte:fragment slot="sticky-bottom">
|
||||
<Button
|
||||
color="gray"
|
||||
fullwidth
|
||||
on:click={() => {
|
||||
showChangeNameModal = false;
|
||||
}}>Cancel</Button
|
||||
>
|
||||
<Button type="submit" fullwidth form="change-name-form">Ok</Button>
|
||||
</svelte:fragment>
|
||||
</FullScreenModal>
|
||||
{/if}
|
||||
|
||||
|
|
|
@ -9,7 +9,6 @@
|
|||
import CreateUserForm from '$lib/components/forms/create-user-form.svelte';
|
||||
import EditUserForm from '$lib/components/forms/edit-user-form.svelte';
|
||||
import UserPageLayout from '$lib/components/layouts/user-page-layout.svelte';
|
||||
import FullScreenModal from '$lib/components/shared-components/full-screen-modal.svelte';
|
||||
import {
|
||||
NotificationType,
|
||||
notificationController,
|
||||
|
@ -21,14 +20,7 @@
|
|||
import { asByteUnitString } from '$lib/utils/byte-units';
|
||||
import { copyToClipboard } from '$lib/utils';
|
||||
import { UserStatus, getAllUsers, type UserResponseDto } from '@immich/sdk';
|
||||
import {
|
||||
mdiAccountEditOutline,
|
||||
mdiClose,
|
||||
mdiContentCopy,
|
||||
mdiDeleteRestore,
|
||||
mdiPencilOutline,
|
||||
mdiTrashCanOutline,
|
||||
} from '@mdi/js';
|
||||
import { mdiClose, mdiContentCopy, mdiDeleteRestore, mdiPencilOutline, mdiTrashCanOutline } from '@mdi/js';
|
||||
import { DateTime } from 'luxon';
|
||||
import { onMount } from 'svelte';
|
||||
import type { PageData } from './$types';
|
||||
|
@ -123,32 +115,22 @@
|
|||
<section id="setting-content" class="flex place-content-center sm:mx-4">
|
||||
<section class="w-full pb-28 lg:w-[850px]">
|
||||
{#if shouldShowCreateUserForm}
|
||||
<FullScreenModal
|
||||
id="create-new-user-modal"
|
||||
title="Create new user"
|
||||
showLogo
|
||||
<CreateUserForm
|
||||
on:submit={onUserCreated}
|
||||
on:cancel={() => (shouldShowCreateUserForm = false)}
|
||||
onClose={() => (shouldShowCreateUserForm = false)}
|
||||
>
|
||||
<CreateUserForm on:submit={onUserCreated} on:cancel={() => (shouldShowCreateUserForm = false)} />
|
||||
</FullScreenModal>
|
||||
/>
|
||||
{/if}
|
||||
|
||||
{#if shouldShowEditUserForm}
|
||||
<FullScreenModal
|
||||
id="edit-user-modal"
|
||||
title="Edit user"
|
||||
icon={mdiAccountEditOutline}
|
||||
<EditUserForm
|
||||
user={selectedUser}
|
||||
bind:newPassword
|
||||
canResetPassword={selectedUser?.id !== $user.id}
|
||||
on:editSuccess={onEditUserSuccess}
|
||||
on:resetPasswordSuccess={onEditPasswordSuccess}
|
||||
onClose={() => (shouldShowEditUserForm = false)}
|
||||
>
|
||||
<EditUserForm
|
||||
user={selectedUser}
|
||||
bind:newPassword
|
||||
canResetPassword={selectedUser?.id !== $user.id}
|
||||
on:editSuccess={onEditUserSuccess}
|
||||
on:resetPasswordSuccess={onEditPasswordSuccess}
|
||||
on:close={() => (shouldShowEditUserForm = false)}
|
||||
/>
|
||||
</FullScreenModal>
|
||||
/>
|
||||
{/if}
|
||||
|
||||
{#if shouldShowDeleteConfirmDialog}
|
||||
|
|
Loading…
Reference in a new issue