1
0
Fork 0
mirror of https://github.com/immich-app/immich.git synced 2024-12-29 15:11:58 +00:00

feat(web): navigate with keyboard on person page (#5486)

* feat: navigate with keyboard on person page

* pr feedback

* pr feedback

* pr feedback

* fix: remove unused import
This commit is contained in:
martin 2024-02-13 16:57:15 +01:00 committed by GitHub
parent b4579e788b
commit dcfd1f9ea6
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
3 changed files with 81 additions and 22 deletions

View file

@ -27,6 +27,7 @@
export let fullwidth = false;
export let border = false;
export let title: string | undefined = '';
let className = '';
export { className as class };

View file

@ -8,12 +8,6 @@
import { mdiArrowLeft, mdiClose, mdiMerge } from '@mdi/js';
import Icon from '$lib/components/elements/icon.svelte';
const dispatch = createEventDispatcher<{
reject: void;
confirm: [PersonResponseDto, PersonResponseDto];
close: void;
}>();
export let personMerge1: PersonResponseDto;
export let personMerge2: PersonResponseDto;
export let potentialMergePeople: PersonResponseDto[];
@ -22,6 +16,21 @@
const title = personMerge2.name;
const dispatch = createEventDispatcher<{
reject: void;
confirm: [PersonResponseDto, PersonResponseDto];
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]];
@ -29,6 +38,8 @@
};
</script>
<svelte:document on:keypress={handleKeyboardPress} />
<FullScreenModal on:clickOutside={() => dispatch('close')}>
<div class="flex h-full w-full place-content-center place-items-center overflow-hidden">
<div
@ -114,7 +125,7 @@
<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 px-4 pb-4">
<Button color="gray" fullwidth on:click={() => dispatch('reject')}>No</Button>
<Button fullwidth color="gray" on:click={() => dispatch('reject')}>No</Button>
<Button fullwidth on:click={() => dispatch('confirm', [personMerge1, personMerge2])}>Yes</Button>
</div>
</div>

View file

@ -88,6 +88,8 @@
**/
let searchWord: string;
let isSearchingPeople = false;
let focusedElements: (HTMLButtonElement | null)[] = Array.from({ length: maximumLengthSearchPeople }, () => null);
let indexFocus: number | null = null;
const searchPeople = async () => {
if ((people.length < maximumLengthSearchPeople && name.startsWith(searchWord)) || name === '') {
@ -116,6 +118,7 @@
$: {
if (people) {
suggestedPeople = name ? searchNameLocal(name, people, 5, data.person.id) : [];
indexFocus = null;
}
}
@ -129,8 +132,51 @@
viewMode = ViewMode.MERGE_PEOPLE;
}
});
const handleKeyboardPress = (event: KeyboardEvent) => {
if (suggestedPeople.length === 0) {
return;
}
if (!$showAssetViewer) {
event.stopPropagation();
switch (event.key) {
case 'ArrowDown': {
event.preventDefault();
if (indexFocus === null) {
indexFocus = 0;
} else if (indexFocus === suggestedPeople.length - 1) {
indexFocus = 0;
} else {
indexFocus++;
}
focusedElements[indexFocus]?.focus();
return;
}
case 'ArrowUp': {
if (indexFocus === null) {
indexFocus = 0;
return;
}
if (indexFocus === 0) {
indexFocus = suggestedPeople.length - 1;
} else {
indexFocus--;
}
focusedElements[indexFocus]?.focus();
return;
}
case 'Enter': {
if (indexFocus !== null) {
handleSuggestPeople(suggestedPeople[indexFocus]);
}
}
}
}
};
const handleEscape = () => {
if ($showAssetViewer) {
if ($showAssetViewer || viewMode === ViewMode.SUGGEST_MERGE) {
return;
}
if ($isMultiSelectState) {
@ -350,6 +396,7 @@
};
</script>
<svelte:document on:keydown={handleKeyboardPress} />
{#if viewMode === ViewMode.UNASSIGN_ASSETS}
<UnMergeFaceSelector
assetIds={[...$selectedAssets].map((a) => a.id)}
@ -499,24 +546,24 @@
</div>
{:else}
{#each suggestedPeople as person, index (person.id)}
<div
class="flex border-t border-x border-gray-400 dark:border-immich-dark-gray h-14 place-items-center bg-gray-200 p-2 dark:bg-gray-700 hover:bg-gray-300 hover:dark:bg-[#232932] {index ===
<button
bind:this={focusedElements[index]}
class="flex w-full border-t border-gray-400 dark:border-immich-dark-gray h-14 place-items-center bg-gray-200 p-2 dark:bg-gray-700 hover:bg-gray-300 hover:dark:bg-[#232932] focus:bg-gray-300 focus:dark:bg-[#232932] {index ===
suggestedPeople.length - 1
? 'rounded-b-lg border-b'
: ''}"
on:click={() => handleSuggestPeople(person)}
>
<button class="flex w-full place-items-center" on:click={() => handleSuggestPeople(person)}>
<ImageThumbnail
circle
shadow
url={api.getPeopleThumbnailUrl(person.id)}
altText={person.name}
widthStyle="2rem"
heightStyle="2rem"
/>
<p class="ml-4 text-gray-700 dark:text-gray-100">{person.name}</p>
</button>
</div>
<ImageThumbnail
circle
shadow
url={api.getPeopleThumbnailUrl(person.id)}
altText={person.name}
widthStyle="2rem"
heightStyle="2rem"
/>
<p class="ml-4 text-gray-700 dark:text-gray-100">{person.name}</p>
</button>
{/each}
{/if}
</div>