1
0
mirror of https://github.com/immich-app/immich.git synced 2024-11-24 08:52:28 +02:00

fix(web): consistent modal escape behavior (#7677)

* fix(web): consistent modal escape behavior

* make onClose optional
This commit is contained in:
Michel Heusschen 2024-03-07 04:18:53 +01:00 committed by GitHub
parent 3da2b05428
commit 5dd11ca17a
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
39 changed files with 111 additions and 123 deletions

View File

@ -10,6 +10,7 @@
const dispatch = createEventDispatcher<{
success: void;
fail: void;
cancel: void;
}>();
const handleDeleteUser = async () => {
@ -27,7 +28,12 @@
};
</script>
<ConfirmDialogue title="Delete User" confirmText="Delete" on:confirm={handleDeleteUser} on:cancel>
<ConfirmDialogue
title="Delete User"
confirmText="Delete"
onConfirm={handleDeleteUser}
onClose={() => dispatch('cancel')}
>
<svelte:fragment slot="prompt">
<div class="flex flex-col gap-4">
<p>

View File

@ -148,8 +148,8 @@
{#if confirmJob}
<ConfirmDialogue
prompt="Are you sure you want to reprocess all faces? This will also clear named people."
on:confirm={onConfirm}
on:cancel={() => (confirmJob = null)}
{onConfirm}
onClose={() => (confirmJob = null)}
/>
{/if}

View File

@ -8,6 +8,7 @@
const dispatch = createEventDispatcher<{
success: void;
fail: void;
cancel: void;
}>();
const handleRestoreUser = async () => {
@ -24,8 +25,8 @@
title="Restore User"
confirmText="Continue"
confirmColor="green"
on:confirm={handleRestoreUser}
on:cancel
onConfirm={handleRestoreUser}
onClose={() => dispatch('cancel')}
>
<svelte:fragment slot="prompt">
<p><b>{user.name}</b>'s account will be restored.</p>

View File

@ -1,8 +1,11 @@
<script lang="ts">
import ConfirmDialogue from '$lib/components/shared-components/confirm-dialogue.svelte';
export let onCancel: () => void;
export let onConfirm: () => void;
</script>
<ConfirmDialogue title="Disable Login" on:cancel on:confirm>
<ConfirmDialogue title="Disable Login" onClose={onCancel} {onConfirm}>
<svelte:fragment slot="prompt">
<div class="flex flex-col gap-4">
<p>Are you sure you want to disable all login methods? Login will be completely disabled.</p>

View File

@ -56,7 +56,7 @@
</script>
{#if isConfirmOpen}
<ConfirmDisableLogin on:cancel={() => handleConfirm(false)} on:confirm={() => handleConfirm(true)} />
<ConfirmDisableLogin onCancel={() => handleConfirm(false)} onConfirm={() => handleConfirm(true)} />
{/if}
<div class="mt-2">

View File

@ -41,7 +41,7 @@
</script>
{#if isConfirmOpen}
<ConfirmDisableLogin on:cancel={() => handleConfirm(false)} on:confirm={() => handleConfirm(true)} />
<ConfirmDisableLogin onCancel={() => handleConfirm(false)} onConfirm={() => handleConfirm(true)} />
{/if}
<div>

View File

@ -19,7 +19,7 @@
}>();
</script>
<FullScreenModal on:clickOutside={() => dispatch('close')}>
<FullScreenModal onClose={() => dispatch('close')}>
<div class="flex h-full w-full place-content-center place-items-center overflow-hidden p-2 md:p-0">
<div
class="w-[550px] rounded-3xl border bg-immich-bg shadow-sm dark:border-immich-dark-gray dark:bg-immich-dark-gray dark:text-immich-dark-fg"

View File

@ -129,8 +129,8 @@
title="Leave Album?"
prompt="Are you sure you want to leave {album.albumName}?"
confirmText="Leave"
on:confirm={handleRemoveUser}
on:cancel={() => (selectedRemoveUser = null)}
onConfirm={handleRemoveUser}
onClose={() => (selectedRemoveUser = null)}
/>
{/if}
@ -139,7 +139,7 @@
title="Remove User?"
prompt="Are you sure you want to remove {selectedRemoveUser.name}"
confirmText="Remove"
on:confirm={handleRemoveUser}
on:cancel={() => (selectedRemoveUser = null)}
onConfirm={handleRemoveUser}
onClose={() => (selectedRemoveUser = null)}
/>
{/if}

View File

@ -158,8 +158,8 @@
<ConfirmDialogue
title="Merge people"
confirmText="Merge"
on:confirm={handleMerge}
on:cancel={() => (isShowConfirmation = false)}
onConfirm={handleMerge}
onClose={() => (isShowConfirmation = false)}
>
<svelte:fragment slot="prompt">
<p>Are you sure you want merge these people ?</p></svelte:fragment

View File

@ -23,15 +23,6 @@
close: void;
}>();
const handleKeyboardPress = (event: KeyboardEvent) => {
switch (event.key) {
case 'Escape': {
dispatch('close');
return;
}
}
};
const changePersonToMerge = (newperson: PersonResponseDto) => {
const index = potentialMergePeople.indexOf(newperson);
[potentialMergePeople[index], personMerge2] = [personMerge2, potentialMergePeople[index]];
@ -39,9 +30,7 @@
};
</script>
<svelte:document on:keypress={handleKeyboardPress} />
<FullScreenModal on:clickOutside={() => dispatch('close')}>
<FullScreenModal onClose={() => dispatch('close')}>
<div class="flex h-full w-full place-content-center place-items-center overflow-hidden">
<div
class="w-[250px] max-w-[125vw] rounded-3xl border bg-immich-bg shadow-sm dark:border-immich-dark-gray dark:bg-immich-dark-gray dark:text-immich-dark-fg md:w-[375px]"

View File

@ -21,7 +21,7 @@
};
</script>
<FullScreenModal on:clickOutside={() => handleCancel()}>
<FullScreenModal onClose={handleCancel}>
<div
class="w-[500px] max-w-[95vw] rounded-3xl border bg-immich-bg p-4 py-8 shadow-sm dark:border-immich-dark-gray dark:bg-immich-dark-gray dark:text-immich-dark-fg"
>

View File

@ -29,7 +29,7 @@
};
</script>
<FullScreenModal on:clickOutside={handleCancel}>
<FullScreenModal onClose={handleCancel}>
<div
class="w-[500px] max-w-[95vw] rounded-3xl border bg-immich-bg p-4 py-8 shadow-sm dark:border-immich-dark-gray dark:bg-immich-dark-gray dark:text-immich-dark-fg"
>

View File

@ -147,8 +147,8 @@
<ConfirmDialogue
title="Reset Password"
confirmText="Reset"
on:confirm={resetPassword}
on:cancel={() => (isShowResetPasswordConfirmation = false)}
onConfirm={resetPassword}
onClose={() => (isShowResetPasswordConfirmation = false)}
>
<svelte:fragment slot="prompt">
<p>

View File

@ -29,7 +29,7 @@
const handleSubmit = () => dispatch('submit', { excludePattern: exclusionPattern });
</script>
<FullScreenModal on:clickOutside={() => handleCancel()}>
<FullScreenModal onClose={handleCancel}>
<div
class="w-[500px] max-w-[95vw] rounded-3xl border bg-immich-bg p-4 py-8 shadow-sm dark:border-immich-dark-gray dark:bg-immich-dark-gray dark:text-immich-dark-fg"
>

View File

@ -31,7 +31,7 @@
const handleSubmit = () => dispatch('submit', { importPath });
</script>
<FullScreenModal on:clickOutside={() => handleCancel()}>
<FullScreenModal onClose={handleCancel}>
<div
class="w-[500px] max-w-[95vw] rounded-3xl border bg-immich-bg p-4 py-8 shadow-sm dark:border-immich-dark-gray dark:bg-immich-dark-gray dark:text-immich-dark-fg"
>

View File

@ -28,7 +28,7 @@
const handleSubmit = () => dispatch('submit', { ownerId });
</script>
<FullScreenModal on:clickOutside={() => handleCancel()}>
<FullScreenModal onClose={handleCancel}>
<div
class="w-[500px] max-w-[95vw] rounded-3xl border bg-immich-bg p-4 py-8 shadow-sm dark:border-immich-dark-gray dark:bg-immich-dark-gray dark:text-immich-dark-fg"
>

View File

@ -21,7 +21,7 @@
const handleClose = () => dispatch('close');
</script>
<FullScreenModal on:clickOutside={handleClose} on:escape={handleClose}>
<FullScreenModal onClose={handleClose}>
<div
class="flex w-96 max-w-lg flex-col gap-8 rounded-3xl border bg-white p-8 shadow-sm dark:border-immich-dark-gray dark:bg-immich-dark-gray"
>

View File

@ -59,8 +59,8 @@
<ConfirmDialogue
title="Remove from {album.albumName}"
confirmText="Remove"
on:confirm={removeFromAlbum}
on:cancel={() => (isShowConfirmation = false)}
onConfirm={removeFromAlbum}
onClose={() => (isShowConfirmation = false)}
>
<svelte:fragment slot="prompt">
<p>

View File

@ -53,7 +53,7 @@
title="Remove Assets?"
prompt="Are you sure you want to remove {getAssets().size} asset(s) from this shared link?"
confirmText="Remove"
on:confirm={() => handleRemove()}
on:cancel={() => (removing = false)}
onConfirm={() => handleRemove()}
onClose={() => (removing = false)}
/>
{/if}

View File

@ -27,9 +27,8 @@
<ConfirmDialogue
title="Permanently Delete Asset{size > 1 ? 's' : ''}"
confirmText="Delete"
on:confirm={handleConfirm}
on:cancel={() => dispatch('cancel')}
on:escape={() => dispatch('cancel')}
onConfirm={handleConfirm}
onClose={() => dispatch('cancel')}
>
<svelte:fragment slot="prompt">
<p>

View File

@ -68,8 +68,8 @@
title="Edit date & time"
prompt="Please select a new date:"
disabled={!date.isValid}
on:confirm={handleConfirm}
on:cancel={handleCancel}
onConfirm={handleConfirm}
onClose={handleCancel}
>
<div class="flex flex-col text-md px-4 text-center gap-2" slot="prompt">
<div class="mt-2" />

View File

@ -144,8 +144,8 @@
cancelColor="secondary"
title="Change Location"
width={800}
on:confirm={handleConfirm}
on:cancel={handleCancel}
onConfirm={handleConfirm}
onClose={handleCancel}
>
<div slot="prompt" class="flex flex-col w-full h-full gap-2">
<div class="relative w-64 sm:w-96" use:clickOutside on:outclick={() => (hideSuggestion = true)}>

View File

@ -1,5 +1,4 @@
<script lang="ts">
import { createEventDispatcher } from 'svelte';
import FullScreenModal from './full-screen-modal.svelte';
import Button from '../elements/buttons/button.svelte';
import type { Color } from '$lib/components/elements/buttons/button.svelte';
@ -13,29 +12,18 @@
export let hideCancelButton = false;
export let disabled = false;
export let width = 500;
const dispatch = createEventDispatcher<{ cancel: void; confirm: void; 'click-outside': void }>();
export let onClose: () => void;
export let onConfirm: () => void;
let isConfirmButtonDisabled = false;
const handleCancel = () => dispatch('cancel');
const handleEscape = () => {
if (!isConfirmButtonDisabled) {
dispatch('cancel');
}
};
const handleConfirm = () => {
isConfirmButtonDisabled = true;
dispatch('confirm');
};
const handleClickOutside = () => {
dispatch('click-outside');
onConfirm();
};
</script>
<FullScreenModal on:clickOutside={handleClickOutside} on:escape={() => handleEscape()}>
<FullScreenModal {onClose}>
<div
class="max-w-[95vw] rounded-3xl border bg-immich-bg p-4 py-8 shadow-sm dark:border-immich-dark-gray dark:bg-immich-dark-gray dark:text-immich-dark-fg"
style="width: {width}px"
@ -56,7 +44,7 @@
<div class="mt-4 flex w-full gap-4 px-4">
{#if !hideCancelButton}
<Button color={cancelColor} fullwidth on:click={handleCancel}>
<Button color={cancelColor} fullwidth on:click={onClose}>
{cancelText}
</Button>
{/if}

View File

@ -1,12 +1,8 @@
<script lang="ts">
import { clickOutside } from '../../utils/click-outside';
import { createEventDispatcher } from 'svelte';
import { fade } from 'svelte/transition';
const dispatch = createEventDispatcher<{
clickOutside: void;
escape: void;
}>();
export let onClose: (() => void) | undefined = undefined;
</script>
<section
@ -14,12 +10,7 @@
out:fade={{ duration: 100 }}
class="fixed left-0 top-0 z-[9990] flex h-screen w-screen place-content-center place-items-center bg-black/40"
>
<div
class="z-[9999]"
use:clickOutside
on:outclick={() => dispatch('clickOutside')}
on:escape={() => dispatch('escape')}
>
<div class="z-[9999]" use:clickOutside={{ onOutclick: onClose, onEscape: onClose }}>
<slot />
</div>
</section>

View File

@ -15,7 +15,7 @@
const colors: UserAvatarColor[] = Object.values(UserAvatarColor);
</script>
<FullScreenModal on:clickOutside={() => dispatch('close')} on:escape={() => dispatch('close')}>
<FullScreenModal onClose={() => dispatch('close')}>
<div class="flex h-full w-full place-content-center place-items-center overflow-hidden">
<div
class=" rounded-3xl border bg-immich-bg shadow-sm dark:border-immich-dark-gray dark:bg-immich-dark-gray dark:text-immich-dark-fg p-4"

View File

@ -36,7 +36,7 @@
}>();
</script>
<FullScreenModal on:clickOutside={() => dispatch('close')} on:escape={() => dispatch('close')}>
<FullScreenModal onClose={() => dispatch('close')}>
<div class="flex h-full w-full place-content-center place-items-center overflow-hidden">
<div
class="w-[400px] max-w-[125vw] rounded-3xl border bg-immich-bg shadow-sm dark:border-immich-dark-gray dark:bg-immich-dark-gray dark:text-immich-dark-fg md:w-[650px]"

View File

@ -33,7 +33,7 @@
</script>
{#if showModal}
<FullScreenModal on:clickOutside={() => (showModal = false)}>
<FullScreenModal onClose={() => (showModal = false)}>
<div
class="max-w-lg rounded-3xl border bg-immich-bg px-8 py-10 shadow-sm dark:border-immich-dark-gray dark:bg-immich-dark-gray dark:text-immich-dark-fg"
>

View File

@ -30,7 +30,7 @@
};
</script>
<FullScreenModal on:clickOutside={onClose} on:escape={onClose}>
<FullScreenModal {onClose}>
<div
class="flex w-full md:w-96 max-w-lg flex-col gap-8 rounded-3xl border bg-white p-8 shadow-sm dark:border-immich-dark-gray dark:bg-immich-dark-gray"
>

View File

@ -50,16 +50,16 @@
{#if deleteDevice}
<ConfirmDialogue
prompt="Are you sure you want to log out this device?"
on:confirm={() => handleDelete()}
on:cancel={() => (deleteDevice = null)}
onConfirm={() => handleDelete()}
onClose={() => (deleteDevice = null)}
/>
{/if}
{#if deleteAll}
<ConfirmDialogue
prompt="Are you sure you want to log out all devices?"
on:confirm={() => handleDeleteAll()}
on:cancel={() => (deleteAll = false)}
onConfirm={() => handleDeleteAll()}
onClose={() => (deleteAll = false)}
/>
{/if}

View File

@ -190,7 +190,7 @@
<ConfirmDialogue
title="Stop sharing your photos?"
prompt="{removePartnerDto.name} will no longer be able to access your photos."
on:cancel={() => (removePartnerDto = null)}
on:confirm={() => handleRemovePartner()}
onClose={() => (removePartnerDto = null)}
onConfirm={() => handleRemovePartner()}
/>
{/if}

View File

@ -105,8 +105,8 @@
{#if deleteKey}
<ConfirmDialogue
prompt="Are you sure you want to delete this API Key?"
on:confirm={() => handleDelete()}
on:cancel={() => (deleteKey = null)}
onConfirm={() => handleDelete()}
onClose={() => (deleteKey = null)}
/>
{/if}

View File

@ -1,20 +1,42 @@
import type { ActionReturn } from 'svelte/action';
interface Attributes {
/** @deprecated */
'on:outclick'?: (e: CustomEvent) => void;
/** @deprecated **/
'on:escape'?: (e: CustomEvent) => void;
}
export function clickOutside(node: HTMLElement): ActionReturn<void, Attributes> {
interface Options {
onOutclick?: () => void;
onEscape?: () => void;
}
export function clickOutside(node: HTMLElement, options: Options = {}): ActionReturn<void, Attributes> {
const { onOutclick, onEscape } = options;
const handleClick = (event: MouseEvent) => {
const targetNode = event.target as Node | null;
if (!node.contains(targetNode)) {
if (node.contains(targetNode)) {
return;
}
if (onOutclick) {
onOutclick();
} else {
node.dispatchEvent(new CustomEvent('outclick'));
}
};
const handleKey = (event: KeyboardEvent) => {
if (event.key === 'Escape') {
if (event.key !== 'Escape') {
return;
}
if (onEscape) {
event.stopPropagation();
onEscape();
} else {
node.dispatchEvent(new CustomEvent('escape'));
}
};

View File

@ -236,7 +236,7 @@
</script>
{#if shouldShowEditUserForm}
<FullScreenModal on:clickOutside={() => (shouldShowEditUserForm = false)}>
<FullScreenModal onClose={() => (shouldShowEditUserForm = false)}>
<EditAlbumForm
album={selectedAlbum}
on:editSuccess={() => successModifyAlbum()}
@ -399,8 +399,8 @@
<ConfirmDialogue
title="Delete Album"
confirmText="Delete"
on:confirm={deleteSelectedAlbum}
on:cancel={() => (albumToDelete = null)}
onConfirm={deleteSelectedAlbum}
onClose={() => (albumToDelete = null)}
>
<svelte:fragment slot="prompt">
<p>Are you sure you want to delete the album <b>{albumToDelete.albumName}</b>?</p>

View File

@ -692,8 +692,8 @@
<ConfirmDialogue
title="Delete album"
confirmText="Delete"
on:confirm={handleRemoveAlbum}
on:cancel={() => (viewMode = ViewMode.VIEW)}
onConfirm={handleRemoveAlbum}
onClose={() => (viewMode = ViewMode.VIEW)}
>
<svelte:fragment slot="prompt">
<p>Are you sure you want to delete the album <b>{album.albumName}</b>?</p>

View File

@ -420,16 +420,14 @@
<svelte:window bind:innerHeight />
{#if showMergeModal}
<FullScreenModal on:clickOutside={() => (showMergeModal = false)}>
<MergeSuggestionModal
{personMerge1}
{personMerge2}
{potentialMergePeople}
on:close={() => (showMergeModal = false)}
on:reject={() => changeName()}
on:confirm={(event) => handleMergeSamePerson(event.detail)}
/>
</FullScreenModal>
<MergeSuggestionModal
{personMerge1}
{personMerge2}
{potentialMergePeople}
on:close={() => (showMergeModal = false)}
on:reject={() => changeName()}
on:confirm={(event) => handleMergeSamePerson(event.detail)}
/>
{/if}
<UserPageLayout
@ -487,7 +485,7 @@
{/if}
{#if showChangeNameModal}
<FullScreenModal on:clickOutside={() => (showChangeNameModal = false)}>
<FullScreenModal onClose={() => (showChangeNameModal = false)}>
<div
class="w-[500px] max-w-[95vw] rounded-3xl border bg-immich-bg p-4 py-8 shadow-sm dark:border-immich-dark-gray dark:bg-immich-dark-gray dark:text-immich-dark-fg"
>

View File

@ -91,7 +91,7 @@
title="Delete Shared Link"
prompt="Are you sure you want to delete this shared link?"
confirmText="Delete"
on:confirm={() => handleDeleteLink()}
on:cancel={() => (deleteLinkId = null)}
onConfirm={() => handleDeleteLink()}
onClose={() => (deleteLinkId = null)}
/>
{/if}

View File

@ -105,8 +105,8 @@
<ConfirmDialogue
title="Empty Trash"
confirmText="Empty"
on:confirm={handleEmptyTrash}
on:cancel={() => (isShowEmptyConfirmation = false)}
onConfirm={handleEmptyTrash}
onClose={() => (isShowEmptyConfirmation = false)}
>
<svelte:fragment slot="prompt">
<p>Are you sure you want to empty the trash? This will remove all the assets in trash permanently from Immich.</p>

View File

@ -307,8 +307,8 @@
<ConfirmDialogue
title="Warning!"
prompt="Are you sure you want to delete this library? This will delete all {deleteAssetCount} contained assets from Immich and cannot be undone. Files will remain on disk."
on:confirm={handleDelete}
on:cancel={() => (confirmDeleteLibrary = null)}
onConfirm={handleDelete}
onClose={() => (confirmDeleteLibrary = null)}
/>
{/if}

View File

@ -103,19 +103,13 @@
<section id="setting-content" class="flex place-content-center sm:mx-4">
<section class="w-full pb-28 lg:w-[850px]">
{#if shouldShowCreateUserForm}
<FullScreenModal
on:clickOutside={() => (shouldShowCreateUserForm = false)}
on:escape={() => (shouldShowCreateUserForm = false)}
>
<FullScreenModal onClose={() => (shouldShowCreateUserForm = false)}>
<CreateUserForm on:submit={onUserCreated} on:cancel={() => (shouldShowCreateUserForm = false)} />
</FullScreenModal>
{/if}
{#if shouldShowEditUserForm}
<FullScreenModal
on:clickOutside={() => (shouldShowEditUserForm = false)}
on:escape={() => (shouldShowEditUserForm = false)}
>
<FullScreenModal onClose={() => (shouldShowEditUserForm = false)}>
<EditUserForm
user={selectedUser}
canResetPassword={selectedUser?.id !== $user.id}
@ -145,10 +139,7 @@
{/if}
{#if shouldShowInfoPanel}
<FullScreenModal
on:clickOutside={() => (shouldShowInfoPanel = false)}
on:escape={() => (shouldShowInfoPanel = false)}
>
<FullScreenModal onClose={() => (shouldShowInfoPanel = false)}>
<div class="w-[500px] max-w-[95vw] rounded-3xl border bg-white p-8 text-sm shadow-sm">
<h1 class="mb-4 text-lg font-medium text-immich-primary">Password reset success</h1>