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:
Ben 2024-04-16 05:06:15 +00:00 committed by GitHub
parent 28f591d01b
commit bcdec25843
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
29 changed files with 251 additions and 337 deletions

View file

@ -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>

View file

@ -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>

View file

@ -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}

View file

@ -96,7 +96,6 @@
<ConfirmDialogue
id="change-location-modal"
confirmColor="primary"
cancelColor="secondary"
title="Change location"
width="wide"
onConfirm={handleConfirm}

View file

@ -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>

View file

@ -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>

View file

@ -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>

View file

@ -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>

View file

@ -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}