fix: search palette triggers highlight, restore CSS keyframe blink
- SearchPalette now calls requestHighlight(id) before goto() - Restore smooth CSS @keyframes cardHighlight (0%→none, 25%→glow, 75%→glow, 100%→none) instead of JS interval pulse - Inline style.animation overrides class-based stagger animation; cleanup sets animation:'none' (inline beats class, no stagger replay)
This commit is contained in:
@@ -184,7 +184,13 @@ a:focus-visible {
|
|||||||
font-family: var(--font-mono);
|
font-family: var(--font-mono);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Card highlight dim overlay for cross-entity navigation */
|
/* Card highlight for cross-entity navigation */
|
||||||
|
@keyframes cardHighlight {
|
||||||
|
0%, 100% { box-shadow: none; }
|
||||||
|
25%, 75% { box-shadow: 0 0 0 3px var(--color-primary), 0 0 20px color-mix(in srgb, var(--color-primary) 30%, transparent); }
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Dim overlay behind highlighted card */
|
||||||
.nav-dim-overlay {
|
.nav-dim-overlay {
|
||||||
position: fixed;
|
position: fixed;
|
||||||
inset: 0;
|
inset: 0;
|
||||||
|
|||||||
@@ -2,6 +2,7 @@
|
|||||||
import { goto } from '$app/navigation';
|
import { goto } from '$app/navigation';
|
||||||
import { t } from '$lib/i18n';
|
import { t } from '$lib/i18n';
|
||||||
import MdiIcon from './MdiIcon.svelte';
|
import MdiIcon from './MdiIcon.svelte';
|
||||||
|
import { requestHighlight } from '$lib/highlight';
|
||||||
import {
|
import {
|
||||||
fetchAllCaches,
|
fetchAllCaches,
|
||||||
providersCache,
|
providersCache,
|
||||||
@@ -140,6 +141,7 @@
|
|||||||
|
|
||||||
function navigateTo(result: SearchResult) {
|
function navigateTo(result: SearchResult) {
|
||||||
closePalette();
|
closePalette();
|
||||||
|
requestHighlight(result.id);
|
||||||
goto(result.href);
|
goto(result.href);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,19 +1,19 @@
|
|||||||
/**
|
/**
|
||||||
* Card highlight system for cross-entity navigation.
|
* Card highlight system for cross-entity navigation.
|
||||||
*
|
*
|
||||||
* CrossLink calls requestHighlight(id) before goto(), storing the ID globally.
|
* CrossLink/SearchPalette call requestHighlight(id) before goto().
|
||||||
* The destination page calls highlightFromUrl() after data loads, which
|
* The destination page calls highlightFromUrl() after data loads, which
|
||||||
* picks up the pending ID and highlights the matching card.
|
* picks up the pending ID and highlights the matching card with a
|
||||||
|
* smooth CSS keyframe glow + dim overlay.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
const HIGHLIGHT_DURATION = 2500;
|
const HIGHLIGHT_DURATION = 2000;
|
||||||
const PULSE_INTERVAL = 400;
|
|
||||||
const WAIT_TIMEOUT = 5000;
|
const WAIT_TIMEOUT = 5000;
|
||||||
|
|
||||||
/** Pending highlight ID — set by CrossLink before navigation. */
|
/** Pending highlight ID — set before navigation. */
|
||||||
let _pendingHighlight: string | null = null;
|
let _pendingHighlight: string | null = null;
|
||||||
|
|
||||||
/** Request a card highlight. Called by CrossLink before goto(). */
|
/** Request a card highlight. Called before goto(). */
|
||||||
export function requestHighlight(id: string | number): void {
|
export function requestHighlight(id: string | number): void {
|
||||||
_pendingHighlight = String(id);
|
_pendingHighlight = String(id);
|
||||||
}
|
}
|
||||||
@@ -39,7 +39,7 @@ export function highlightFromUrl(): void {
|
|||||||
|
|
||||||
if (!id) return;
|
if (!id) return;
|
||||||
|
|
||||||
// Wait a tick for DOM to render after loaded=true
|
// Wait for DOM to render after loaded=true
|
||||||
requestAnimationFrame(() => {
|
requestAnimationFrame(() => {
|
||||||
requestAnimationFrame(() => {
|
requestAnimationFrame(() => {
|
||||||
const card = document.querySelector(`[data-entity-id="${id}"]`);
|
const card = document.querySelector(`[data-entity-id="${id}"]`);
|
||||||
@@ -55,49 +55,21 @@ export function highlightFromUrl(): void {
|
|||||||
function _highlightCard(card: HTMLElement): void {
|
function _highlightCard(card: HTMLElement): void {
|
||||||
const overlay = _showDimOverlay();
|
const overlay = _showDimOverlay();
|
||||||
|
|
||||||
|
// Scroll to card
|
||||||
card.scrollIntoView({ behavior: 'smooth', block: 'center' });
|
card.scrollIntoView({ behavior: 'smooth', block: 'center' });
|
||||||
|
|
||||||
// Save originals
|
// Apply highlight via inline style (overrides stagger CSS class animation)
|
||||||
const origBoxShadow = card.style.boxShadow;
|
card.style.animation = 'cardHighlight 2s ease-in-out';
|
||||||
const origZIndex = card.style.zIndex;
|
|
||||||
const origPosition = card.style.position;
|
|
||||||
const origBorderColor = card.style.borderColor;
|
|
||||||
|
|
||||||
// Elevate above overlay
|
|
||||||
card.style.position = 'relative';
|
card.style.position = 'relative';
|
||||||
card.style.zIndex = '11';
|
card.style.zIndex = '11';
|
||||||
|
|
||||||
// Get primary color
|
// Cleanup: set animation to 'none' (inline beats class, prevents stagger replay)
|
||||||
const primary = getComputedStyle(document.documentElement)
|
|
||||||
.getPropertyValue('--color-primary').trim();
|
|
||||||
|
|
||||||
// Pulsing glow
|
|
||||||
let on = true;
|
|
||||||
const glowOn = `0 0 0 3px ${primary}, 0 0 24px ${primary}60`;
|
|
||||||
const glowOff = `0 0 0 2px ${primary}80`;
|
|
||||||
|
|
||||||
card.style.boxShadow = glowOn;
|
|
||||||
card.style.borderColor = primary;
|
|
||||||
|
|
||||||
const pulseTimer = setInterval(() => {
|
|
||||||
on = !on;
|
|
||||||
card.style.boxShadow = on ? glowOn : glowOff;
|
|
||||||
}, PULSE_INTERVAL);
|
|
||||||
|
|
||||||
// Cleanup
|
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
clearInterval(pulseTimer);
|
card.style.animation = 'none';
|
||||||
card.style.transition = 'box-shadow 0.3s ease, border-color 0.3s ease';
|
card.style.removeProperty('position');
|
||||||
card.style.boxShadow = origBoxShadow;
|
card.style.removeProperty('z-index');
|
||||||
card.style.borderColor = origBorderColor;
|
overlay.classList.remove('active');
|
||||||
|
setTimeout(() => overlay.remove(), 300);
|
||||||
setTimeout(() => {
|
|
||||||
card.style.position = origPosition;
|
|
||||||
card.style.zIndex = origZIndex;
|
|
||||||
card.style.removeProperty('transition');
|
|
||||||
overlay.classList.remove('active');
|
|
||||||
setTimeout(() => overlay.remove(), 300);
|
|
||||||
}, 300);
|
|
||||||
}, HIGHLIGHT_DURATION);
|
}, HIGHLIGHT_DURATION);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user