feat: card highlight system for cross-entity navigation
When clicking a CrossLink, the target entity ID is passed as ?highlight=<id> in the URL. The destination page: 1. Shows a semi-transparent dim overlay (z-index: 10) 2. Finds the card with data-entity-id matching the ID 3. Scrolls to it smoothly (block: center) 4. Applies a pulsing primary-color box-shadow animation (z-index: 11) 5. Cleans up overlay + animation after 2 seconds If the card isn't in DOM yet (data still loading), a MutationObserver waits up to 5 seconds for it to appear. Changes: - New highlight.ts utility with highlightFromUrl(), MutationObserver, dim overlay management - Card component accepts entityId prop → data-entity-id attribute - CrossLink accepts entityId prop → appends ?highlight=<id> to href - All 9 entity pages: Card elements have entityId, highlightFromUrl() called after data loads - CSS: cardHighlight keyframe animation + nav-dim-overlay styles
This commit is contained in:
@@ -14,6 +14,7 @@
|
||||
import IconButton from '$lib/components/IconButton.svelte';
|
||||
import CrossLink from '$lib/components/CrossLink.svelte';
|
||||
import { snackSuccess, snackError } from '$lib/stores/snackbar.svelte';
|
||||
import { highlightFromUrl } from '$lib/highlight';
|
||||
import type { ServiceProvider, TelegramBot } from '$lib/types';
|
||||
|
||||
let trackers = $state<any[]>([]);
|
||||
@@ -61,7 +62,7 @@
|
||||
telegramBotsCache.fetch(),
|
||||
]);
|
||||
} catch (err: any) { error = err.message || t('common.loadError'); snackError(error); }
|
||||
finally { loaded = true; }
|
||||
finally { loaded = true; highlightFromUrl(); }
|
||||
}
|
||||
|
||||
function openNew() { form = defaultForm(); editing = null; showForm = true; }
|
||||
@@ -226,14 +227,14 @@
|
||||
{:else}
|
||||
<div class="space-y-3 stagger-children">
|
||||
{#each trackers as trk}
|
||||
<Card hover>
|
||||
<Card hover entityId={trk.id}>
|
||||
<div class="flex items-center justify-between">
|
||||
<div>
|
||||
<div class="flex items-center gap-2">
|
||||
<span style="color: var(--color-primary);"><MdiIcon name={trk.icon || 'mdiConsoleLine'} size={20} /></span>
|
||||
<p class="font-medium">{trk.name}</p>
|
||||
<CrossLink href="/providers" icon="mdiServer" label={providerName(trk.provider_id)} />
|
||||
<CrossLink href="/command-configs" icon="mdiCog" label={configName(trk.command_config_id)} />
|
||||
<CrossLink href="/providers" icon="mdiServer" label={providerName(trk.provider_id)} entityId={trk.provider_id} />
|
||||
<CrossLink href="/command-configs" icon="mdiCog" label={configName(trk.command_config_id)} entityId={trk.command_config_id} />
|
||||
<span class="text-xs px-1.5 py-0.5 rounded font-mono {trk.enabled
|
||||
? 'bg-emerald-500/10 text-emerald-500'
|
||||
: 'bg-red-500/10 text-red-500'}">
|
||||
@@ -270,7 +271,7 @@
|
||||
<div class="flex items-center justify-between text-sm px-2 py-1 rounded hover:bg-[var(--color-muted)]">
|
||||
<div class="flex items-center gap-2">
|
||||
<MdiIcon name="mdiRobot" size={14} />
|
||||
<CrossLink href="/telegram-bots" icon="mdiRobot" label={listener.name || listener.listener_type} />
|
||||
<CrossLink href="/telegram-bots" icon="mdiRobot" label={listener.name || listener.listener_type} entityId={listener.listener_id} />
|
||||
<span class="text-xs px-1.5 py-0.5 rounded bg-blue-500/10 text-blue-500 font-mono">{listener.listener_type}</span>
|
||||
</div>
|
||||
<IconButton icon="mdiClose" title={t('commandTracker.removeListener')} size={14}
|
||||
|
||||
Reference in New Issue
Block a user