feat(stammbaum): dim a node when outside the highlighted lineage (#703)
StammbaumNode gains an optional `dimmed` prop that sets group-level opacity (DIMMED_OPACITY) on the node's root <g>, so the box, accent bar, name, and dates fade together as one unit. A lineage-fade CSS transition eases the change and is neutralised under prefers-reduced-motion. The selected-node styling (active fill + mint accent bar) is untouched. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -1,5 +1,6 @@
|
|||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { NODE_W, NODE_H } from '$lib/person/genealogy/layout/buildLayout';
|
import { NODE_W, NODE_H } from '$lib/person/genealogy/layout/buildLayout';
|
||||||
|
import { DIMMED_OPACITY } from '$lib/person/genealogy/layout/highlightLineage';
|
||||||
import type { components } from '$lib/generated/api';
|
import type { components } from '$lib/generated/api';
|
||||||
|
|
||||||
type PersonNodeDTO = components['schemas']['PersonNodeDTO'];
|
type PersonNodeDTO = components['schemas']['PersonNodeDTO'];
|
||||||
@@ -8,10 +9,12 @@ interface Props {
|
|||||||
node: PersonNodeDTO;
|
node: PersonNodeDTO;
|
||||||
pos: { x: number; y: number };
|
pos: { x: number; y: number };
|
||||||
selected: boolean;
|
selected: boolean;
|
||||||
|
/** Dim the whole node when a lineage is highlighted and this person is outside it. */
|
||||||
|
dimmed?: boolean;
|
||||||
onSelect: (id: string) => void;
|
onSelect: (id: string) => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
let { node, pos, selected, onSelect }: Props = $props();
|
let { node, pos, selected, dimmed = false, onSelect }: Props = $props();
|
||||||
|
|
||||||
// Each node owns its own focus-ring state (the focus ring is decorative; the
|
// Each node owns its own focus-ring state (the focus ring is decorative; the
|
||||||
// `<g role="button">` is the real focus target).
|
// `<g role="button">` is the real focus target).
|
||||||
@@ -35,11 +38,12 @@ const datesLabel = $derived(
|
|||||||
aria-label="{node.displayName}{datesLabel}"
|
aria-label="{node.displayName}{datesLabel}"
|
||||||
aria-expanded={selected}
|
aria-expanded={selected}
|
||||||
transform="translate({pos.x}, {pos.y})"
|
transform="translate({pos.x}, {pos.y})"
|
||||||
|
opacity={dimmed ? DIMMED_OPACITY : undefined}
|
||||||
onclick={() => onSelect(node.id)}
|
onclick={() => onSelect(node.id)}
|
||||||
onkeydown={handleKey}
|
onkeydown={handleKey}
|
||||||
onfocus={() => (focused = true)}
|
onfocus={() => (focused = true)}
|
||||||
onblur={() => (focused = false)}
|
onblur={() => (focused = false)}
|
||||||
class="cursor-pointer focus:outline-none"
|
class="lineage-fade cursor-pointer focus:outline-none"
|
||||||
>
|
>
|
||||||
{#if focused}
|
{#if focused}
|
||||||
<rect
|
<rect
|
||||||
@@ -88,3 +92,15 @@ const datesLabel = $derived(
|
|||||||
</text>
|
</text>
|
||||||
{/if}
|
{/if}
|
||||||
</g>
|
</g>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
/* Ease the lineage focus+dim transition; instant for reduced-motion users. */
|
||||||
|
.lineage-fade {
|
||||||
|
transition: opacity 200ms ease;
|
||||||
|
}
|
||||||
|
@media (prefers-reduced-motion: reduce) {
|
||||||
|
.lineage-fade {
|
||||||
|
transition: none;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|||||||
Reference in New Issue
Block a user