mirror of
https://github.com/immich-app/immich.git
synced 2025-05-31 04:05:39 -04:00
feat(web,a11y): standardize base modal (#8388)
* consistent headings * remove escape key handler * add aria attributes
This commit is contained in:
parent
28e8e539f6
commit
f7afc0334e
@ -66,13 +66,7 @@
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
{#if !selectedRemoveUser}
|
{#if !selectedRemoveUser}
|
||||||
<BaseModal on:close={() => dispatch('close')}>
|
<BaseModal id="share-info-modal" title="Options" on:close>
|
||||||
<svelte:fragment slot="title">
|
|
||||||
<span class="flex place-items-center gap-2">
|
|
||||||
<p class="font-medium text-immich-fg dark:text-immich-dark-fg">Options</p>
|
|
||||||
</span>
|
|
||||||
</svelte:fragment>
|
|
||||||
|
|
||||||
<section class="immich-scrollbar max-h-[400px] overflow-y-auto pb-4">
|
<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 w-full place-items-center justify-between gap-4 p-5">
|
||||||
<div class="flex place-items-center gap-4">
|
<div class="flex place-items-center gap-4">
|
||||||
|
@ -13,7 +13,6 @@
|
|||||||
import { createEventDispatcher, onMount } from 'svelte';
|
import { createEventDispatcher, onMount } from 'svelte';
|
||||||
import Button from '../elements/buttons/button.svelte';
|
import Button from '../elements/buttons/button.svelte';
|
||||||
import BaseModal from '../shared-components/base-modal.svelte';
|
import BaseModal from '../shared-components/base-modal.svelte';
|
||||||
import ImmichLogo from '../shared-components/immich-logo.svelte';
|
|
||||||
import UserAvatar from '../shared-components/user-avatar.svelte';
|
import UserAvatar from '../shared-components/user-avatar.svelte';
|
||||||
|
|
||||||
export let album: AlbumResponseDto;
|
export let album: AlbumResponseDto;
|
||||||
@ -55,14 +54,7 @@
|
|||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<BaseModal on:close={() => dispatch('close')}>
|
<BaseModal id="user-selection-modal" title="Invite to album" showLogo on:close>
|
||||||
<svelte:fragment slot="title">
|
|
||||||
<span class="flex place-items-center gap-2">
|
|
||||||
<ImmichLogo noText={true} width={36} />
|
|
||||||
<p class="font-medium">Invite to album</p>
|
|
||||||
</span>
|
|
||||||
</svelte:fragment>
|
|
||||||
|
|
||||||
{#if selectedUsers.length > 0}
|
{#if selectedUsers.length > 0}
|
||||||
<div class="mb-2 flex flex-wrap place-items-center gap-4 overflow-x-auto px-5 py-2 sticky">
|
<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>
|
<p class="font-medium">To</p>
|
||||||
|
@ -737,7 +737,6 @@
|
|||||||
on:newAlbum={({ detail }) => handleAddToNewAlbum(detail)}
|
on:newAlbum={({ detail }) => handleAddToNewAlbum(detail)}
|
||||||
on:album={({ detail }) => handleAddToAlbum(detail)}
|
on:album={({ detail }) => handleAddToAlbum(detail)}
|
||||||
on:close={() => (isShowAlbumPicker = false)}
|
on:close={() => (isShowAlbumPicker = false)}
|
||||||
on:escape={() => (isShowAlbumPicker = false)}
|
|
||||||
/>
|
/>
|
||||||
{/if}
|
{/if}
|
||||||
|
|
||||||
@ -751,19 +750,11 @@
|
|||||||
{/if}
|
{/if}
|
||||||
|
|
||||||
{#if isShowProfileImageCrop}
|
{#if isShowProfileImageCrop}
|
||||||
<ProfileImageCropper
|
<ProfileImageCropper {asset} on:close={() => (isShowProfileImageCrop = false)} />
|
||||||
{asset}
|
|
||||||
on:close={() => (isShowProfileImageCrop = false)}
|
|
||||||
on:escape={() => (isShowProfileImageCrop = false)}
|
|
||||||
/>
|
|
||||||
{/if}
|
{/if}
|
||||||
|
|
||||||
{#if isShowShareModal}
|
{#if isShowShareModal}
|
||||||
<CreateSharedLinkModal
|
<CreateSharedLinkModal assetIds={[asset.id]} on:close={() => (isShowShareModal = false)} />
|
||||||
assetIds={[asset.id]}
|
|
||||||
on:close={() => (isShowShareModal = false)}
|
|
||||||
on:escape={() => (isShowShareModal = false)}
|
|
||||||
/>
|
|
||||||
{/if}
|
{/if}
|
||||||
</section>
|
</section>
|
||||||
</FocusTrap>
|
</FocusTrap>
|
||||||
|
@ -2,26 +2,14 @@
|
|||||||
import CircleIconButton from '$lib/components/elements/buttons/circle-icon-button.svelte';
|
import CircleIconButton from '$lib/components/elements/buttons/circle-icon-button.svelte';
|
||||||
import CreateSharedLinkModal from '$lib/components/shared-components/create-share-link-modal/create-shared-link-modal.svelte';
|
import CreateSharedLinkModal from '$lib/components/shared-components/create-share-link-modal/create-shared-link-modal.svelte';
|
||||||
import { mdiShareVariantOutline } from '@mdi/js';
|
import { mdiShareVariantOutline } from '@mdi/js';
|
||||||
import { createEventDispatcher } from 'svelte';
|
|
||||||
import { getAssetControlContext } from '../asset-select-control-bar.svelte';
|
import { getAssetControlContext } from '../asset-select-control-bar.svelte';
|
||||||
|
|
||||||
let showModal = false;
|
let showModal = false;
|
||||||
const dispatch = createEventDispatcher<{
|
|
||||||
escape: void;
|
|
||||||
}>();
|
|
||||||
const { getAssets } = getAssetControlContext();
|
const { getAssets } = getAssetControlContext();
|
||||||
const escape = () => {
|
|
||||||
dispatch('escape');
|
|
||||||
showModal = false;
|
|
||||||
};
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<CircleIconButton title="Share" icon={mdiShareVariantOutline} on:click={() => (showModal = true)} />
|
<CircleIconButton title="Share" icon={mdiShareVariantOutline} on:click={() => (showModal = true)} />
|
||||||
|
|
||||||
{#if showModal}
|
{#if showModal}
|
||||||
<CreateSharedLinkModal
|
<CreateSharedLinkModal assetIds={[...getAssets()].map(({ id }) => id)} on:close={() => (showModal = false)} />
|
||||||
assetIds={[...getAssets()].map(({ id }) => id)}
|
|
||||||
on:close={() => (showModal = false)}
|
|
||||||
on:escape={escape}
|
|
||||||
/>
|
|
||||||
{/if}
|
{/if}
|
||||||
|
@ -43,17 +43,16 @@
|
|||||||
const handleNew = () => {
|
const handleNew = () => {
|
||||||
dispatch('newAlbum', search.length > 0 ? search : '');
|
dispatch('newAlbum', search.length > 0 ? search : '');
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const getTitle = () => {
|
||||||
|
if (shared) {
|
||||||
|
return 'Add to shared album';
|
||||||
|
}
|
||||||
|
return 'Add to album';
|
||||||
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<BaseModal on:close on:escape>
|
<BaseModal id="album-selection-modal" title={getTitle()} on:close>
|
||||||
<svelte:fragment slot="title">
|
|
||||||
<span class="flex place-items-center gap-2">
|
|
||||||
<p class="font-medium">
|
|
||||||
Add to {shared ? 'Shared ' : ''}Album
|
|
||||||
</p>
|
|
||||||
</span>
|
|
||||||
</svelte:fragment>
|
|
||||||
|
|
||||||
<div class="mb-2 flex max-h-[400px] flex-col">
|
<div class="mb-2 flex max-h-[400px] flex-col">
|
||||||
{#if loading}
|
{#if loading}
|
||||||
{#each { length: 3 } as _}
|
{#each { length: 3 } as _}
|
||||||
@ -69,11 +68,9 @@
|
|||||||
</div>
|
</div>
|
||||||
{/each}
|
{/each}
|
||||||
{:else}
|
{:else}
|
||||||
<!-- svelte-ignore a11y-autofocus -->
|
|
||||||
<input
|
<input
|
||||||
class="border-b-4 border-immich-bg bg-immich-bg px-6 py-2 text-2xl focus:border-immich-primary dark:border-immich-dark-gray dark:bg-immich-dark-gray dark:focus:border-immich-dark-primary"
|
class="border-b-4 border-immich-bg bg-immich-bg px-6 py-2 text-2xl focus:border-immich-primary dark:border-immich-dark-gray dark:bg-immich-dark-gray dark:focus:border-immich-dark-primary"
|
||||||
placeholder="Search"
|
placeholder="Search"
|
||||||
autofocus
|
|
||||||
bind:value={search}
|
bind:value={search}
|
||||||
/>
|
/>
|
||||||
<div class="immich-scrollbar overflow-y-auto">
|
<div class="immich-scrollbar overflow-y-auto">
|
||||||
|
@ -7,12 +7,26 @@
|
|||||||
import { clickOutside } from '$lib/utils/click-outside';
|
import { clickOutside } from '$lib/utils/click-outside';
|
||||||
import { mdiClose } from '@mdi/js';
|
import { mdiClose } from '@mdi/js';
|
||||||
import FocusTrap from '$lib/components/shared-components/focus-trap.svelte';
|
import FocusTrap from '$lib/components/shared-components/focus-trap.svelte';
|
||||||
|
import ImmichLogo from '$lib/components/shared-components/immich-logo.svelte';
|
||||||
|
import Icon from '$lib/components/elements/icon.svelte';
|
||||||
|
|
||||||
const dispatch = createEventDispatcher<{
|
const dispatch = createEventDispatcher<{
|
||||||
escape: void;
|
|
||||||
close: void;
|
close: void;
|
||||||
}>();
|
}>();
|
||||||
|
/**
|
||||||
|
* Unique identifier for the modal.
|
||||||
|
*/
|
||||||
|
export let id: string;
|
||||||
|
export let title: string;
|
||||||
export let zIndex = 9999;
|
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;
|
||||||
|
|
||||||
onMount(() => {
|
onMount(() => {
|
||||||
if (browser) {
|
if (browser) {
|
||||||
@ -36,7 +50,8 @@
|
|||||||
|
|
||||||
<FocusTrap>
|
<FocusTrap>
|
||||||
<div
|
<div
|
||||||
id="immich-modal"
|
aria-modal="true"
|
||||||
|
aria-labelledby={`${id}-title`}
|
||||||
style:z-index={zIndex}
|
style:z-index={zIndex}
|
||||||
transition:fade={{ duration: 100, easing: quintOut }}
|
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"
|
class="fixed left-0 top-0 flex h-full w-full place-content-center place-items-center overflow-hidden bg-black/50"
|
||||||
@ -44,16 +59,23 @@
|
|||||||
<div
|
<div
|
||||||
use:clickOutside={{
|
use:clickOutside={{
|
||||||
onOutclick: () => dispatch('close'),
|
onOutclick: () => dispatch('close'),
|
||||||
onEscape: () => dispatch('escape'),
|
onEscape: () => dispatch('close'),
|
||||||
}}
|
}}
|
||||||
class="max-h-[800px] min-h-[200px] w-[450px] overflow-y-auto rounded-lg bg-immich-bg shadow-md dark:bg-immich-dark-gray dark:text-immich-dark-fg immich-scrollbar"
|
class="max-h-[800px] min-h-[200px] w-[450px] overflow-y-auto rounded-lg bg-immich-bg shadow-md dark:bg-immich-dark-gray dark:text-immich-dark-fg immich-scrollbar"
|
||||||
tabindex="-1"
|
tabindex="-1"
|
||||||
>
|
>
|
||||||
<div class="flex place-items-center justify-between px-5 py-3">
|
<div class="flex place-items-center justify-between px-5 py-3">
|
||||||
<div>
|
<div class="flex items-center">
|
||||||
<slot name="title">
|
{#if showLogo}
|
||||||
<p>Modal Title</p>
|
<ImmichLogo noText={true} width={24} />
|
||||||
</slot>
|
<div class="w-2" />
|
||||||
|
{:else if icon}
|
||||||
|
<Icon path={icon} size={24} ariaHidden={true} class="text-immich-primary dark:text-immich-dark-primary" />
|
||||||
|
<div class="w-2" />
|
||||||
|
{/if}
|
||||||
|
<h1 id={`${id}-title`} class="text-xl font-medium text-immich-primary dark:text-immich-dark-primary mt-1">
|
||||||
|
{title}
|
||||||
|
</h1>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<CircleIconButton on:click={() => dispatch('close')} icon={mdiClose} size={'20'} title="Close" />
|
<CircleIconButton on:click={() => dispatch('close')} icon={mdiClose} size={'20'} title="Close" />
|
||||||
|
@ -143,20 +143,16 @@
|
|||||||
handleError(error, 'Failed to edit shared link');
|
handleError(error, 'Failed to edit shared link');
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const getTitle = () => {
|
||||||
|
if (editingLink) {
|
||||||
|
return 'Edit link';
|
||||||
|
}
|
||||||
|
return 'Create link to share';
|
||||||
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<BaseModal on:close={() => dispatch('close')} on:escape={() => dispatch('escape')}>
|
<BaseModal id="create-shared-link-modal" title={getTitle()} icon={mdiLink} on:close>
|
||||||
<svelte:fragment slot="title">
|
|
||||||
<span class="flex place-items-center gap-2">
|
|
||||||
<Icon path={mdiLink} size={24} />
|
|
||||||
{#if editingLink}
|
|
||||||
<p class="font-medium text-immich-fg dark:text-immich-dark-fg">Edit link</p>
|
|
||||||
{:else}
|
|
||||||
<p class="font-medium text-immich-fg dark:text-immich-dark-fg">Create link to share</p>
|
|
||||||
{/if}
|
|
||||||
</span>
|
|
||||||
</svelte:fragment>
|
|
||||||
|
|
||||||
<section class="mx-6 mb-6">
|
<section class="mx-6 mb-6">
|
||||||
{#if shareType === SharedLinkType.Album}
|
{#if shareType === SharedLinkType.Album}
|
||||||
{#if !editingLink}
|
{#if !editingLink}
|
||||||
|
@ -71,12 +71,7 @@
|
|||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<BaseModal on:close on:escape>
|
<BaseModal id="profile-image-cropper" title="Set profile picture" on:close>
|
||||||
<svelte:fragment slot="title">
|
|
||||||
<span class="flex place-items-center gap-2">
|
|
||||||
<p class="font-medium">Set profile picture</p>
|
|
||||||
</span>
|
|
||||||
</svelte:fragment>
|
|
||||||
<div class="flex place-items-center items-center justify-center">
|
<div class="flex place-items-center items-center justify-center">
|
||||||
<div
|
<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-1/2 overflow-hidden rounded-full border-4 border-immich-primary bg-immich-dark-primary dark:border-immich-dark-primary dark:bg-immich-primary"
|
||||||
|
@ -3,7 +3,6 @@
|
|||||||
import { createEventDispatcher, onMount } from 'svelte';
|
import { createEventDispatcher, onMount } from 'svelte';
|
||||||
import Button from '../elements/buttons/button.svelte';
|
import Button from '../elements/buttons/button.svelte';
|
||||||
import BaseModal from '../shared-components/base-modal.svelte';
|
import BaseModal from '../shared-components/base-modal.svelte';
|
||||||
import ImmichLogo from '../shared-components/immich-logo.svelte';
|
|
||||||
import UserAvatar from '../shared-components/user-avatar.svelte';
|
import UserAvatar from '../shared-components/user-avatar.svelte';
|
||||||
|
|
||||||
export let user: UserResponseDto;
|
export let user: UserResponseDto;
|
||||||
@ -33,14 +32,7 @@
|
|||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<BaseModal on:close={() => dispatch('close')}>
|
<BaseModal id="partner-selection-modal" title="Add partner" showLogo on:close>
|
||||||
<svelte:fragment slot="title">
|
|
||||||
<span class="flex place-items-center gap-2">
|
|
||||||
<ImmichLogo noText width={36} />
|
|
||||||
<p class="font-medium">Add partner</p>
|
|
||||||
</span>
|
|
||||||
</svelte:fragment>
|
|
||||||
|
|
||||||
<div class="immich-scrollbar max-h-[300px] overflow-y-auto">
|
<div class="immich-scrollbar max-h-[300px] overflow-y-auto">
|
||||||
{#if availableUsers.length > 0}
|
{#if availableUsers.length > 0}
|
||||||
{#each availableUsers as user}
|
{#each availableUsers as user}
|
||||||
|
@ -53,7 +53,7 @@
|
|||||||
assets={$selectedAssets}
|
assets={$selectedAssets}
|
||||||
clearSelect={() => assetInteractionStore.clearMultiselect()}
|
clearSelect={() => assetInteractionStore.clearMultiselect()}
|
||||||
>
|
>
|
||||||
<CreateSharedLink on:escape={() => (handleEscapeKey = true)} />
|
<CreateSharedLink />
|
||||||
<SelectAllAssets {assetStore} {assetInteractionStore} />
|
<SelectAllAssets {assetStore} {assetInteractionStore} />
|
||||||
<AssetSelectContextMenu icon={mdiPlus} title="Add to...">
|
<AssetSelectContextMenu icon={mdiPlus} title="Add to...">
|
||||||
<AddToAlbum />
|
<AddToAlbum />
|
||||||
|
Loading…
x
Reference in New Issue
Block a user