feat(web): highlight active person thumbnail in detail panel and edit faces panel (#27401)

- Dim non-hovered person thumbnails to 40% opacity when any face is active
- Add ring highlight on the active person's thumbnail
- Add focus-visible outline styling for keyboard navigation
- Apply same treatment to both detail panel people section and edit faces side panel

Change-Id: I4ac10fe4568b95f3e0e8d9104133180f6a6a6964

Co-authored-by: Alex <alex.tran1502@gmail.com>
This commit is contained in:
Min Idzelis
2026-04-01 11:49:09 -04:00
committed by GitHub
parent da4b88fc14
commit c9e251c78c
3 changed files with 14 additions and 1 deletions

View File

@@ -227,8 +227,9 @@
<div class="mt-2 flex flex-wrap gap-2">
{#each people as person, index (person.id)}
{#if showingHiddenPeople || !person.isHidden}
{@const isHighlighted = people[index].faces.some((f) => $boundingBoxesArray.some((b) => b.id === f.id))}
<a
class="w-22"
class="group w-22 outline-none"
href={Route.viewPerson(person, { previousRoute })}
onfocus={() => ($boundingBoxesArray = people[index].faces)}
onblur={() => ($boundingBoxesArray = [])}
@@ -245,6 +246,8 @@
widthStyle="90px"
heightStyle="90px"
hidden={person.isHidden}
highlighted={isHighlighted}
class="group-focus-visible:outline-2 group-focus-visible:outline-offset-2 group-focus-visible:outline-immich-primary dark:group-focus-visible:outline-immich-dark-primary"
/>
</div>
<p class="mt-1 truncate font-medium" title={person.name}>{person.name}</p>

View File

@@ -16,6 +16,7 @@
circle?: boolean;
hidden?: boolean;
border?: boolean;
highlighted?: boolean;
hiddenIconClass?: string;
class?: ClassValue;
brokenAssetClass?: ClassValue;
@@ -34,6 +35,7 @@
circle = false,
hidden = false,
border = false,
highlighted = false,
hiddenIconClass = 'text-white',
onComplete = undefined,
class: imageClass = '',
@@ -60,6 +62,8 @@
shadow && 'shadow-lg',
(circle || !heightStyle) && 'aspect-square',
border && 'border-3 border-immich-dark-primary/80 hover:border-immich-primary',
'transition-shadow duration-150',
highlighted && 'ring-4 ring-immich-primary dark:ring-immich-dark-primary',
]);
let style = $derived(

View File

@@ -225,6 +225,7 @@
{:else}
{#each peopleWithFaces as face, index (face.id)}
{@const personName = face.person ? face.person?.name : $t('face_unassigned')}
{@const isHighlighted = $boundingBoxesArray.some((b) => b.id === face.id)}
<div class="relative h-29 w-24">
<div
role="button"
@@ -239,6 +240,7 @@
<ImageThumbnail
curve
shadow
highlighted={isHighlighted}
url={selectedPersonToCreate[face.id]}
altText={$t('new_person')}
title={$t('new_person')}
@@ -249,6 +251,7 @@
<ImageThumbnail
curve
shadow
highlighted={isHighlighted}
url={getPeopleThumbnailUrl(selectedPersonToReassign[face.id])}
altText={selectedPersonToReassign[face.id].name}
title={$getPersonNameWithHiddenValue(
@@ -263,6 +266,7 @@
<ImageThumbnail
curve
shadow
highlighted={isHighlighted}
url={getPeopleThumbnailUrl(face.person)}
altText={face.person.name}
title={$getPersonNameWithHiddenValue(face.person.name, face.person.isHidden)}
@@ -275,6 +279,7 @@
<ImageThumbnail
curve
shadow
highlighted={isHighlighted}
url="/src/lib/assets/no-thumbnail.png"
altText={$t('face_unassigned')}
title={$t('face_unassigned')}
@@ -285,6 +290,7 @@
<ImageThumbnail
curve
shadow
highlighted={isHighlighted}
url={data === null ? '/src/lib/assets/no-thumbnail.png' : data}
altText={$t('face_unassigned')}
title={$t('face_unassigned')}