2026-01-18 14:59:00 +03:00
|
|
|
<!--
|
|
|
|
|
Component: FontListItem
|
2026-02-01 11:58:22 +03:00
|
|
|
Displays a font item and manages its animations
|
2026-01-18 14:59:00 +03:00
|
|
|
-->
|
2026-01-18 12:59:12 +03:00
|
|
|
<script lang="ts">
|
2026-01-22 15:41:55 +03:00
|
|
|
import { cn } from '$shared/shadcn/utils/shadcn-utils';
|
2026-02-01 11:52:09 +03:00
|
|
|
import type { Snippet } from 'svelte';
|
2026-01-22 15:41:55 +03:00
|
|
|
import { Spring } from 'svelte/motion';
|
2026-02-09 17:33:09 +03:00
|
|
|
import { type UnifiedFont } from '../../model';
|
2026-01-18 12:59:12 +03:00
|
|
|
|
|
|
|
|
interface Props {
|
2026-01-18 15:55:07 +03:00
|
|
|
/**
|
|
|
|
|
* Object with information about font
|
|
|
|
|
*/
|
2026-01-18 12:59:12 +03:00
|
|
|
font: UnifiedFont;
|
2026-01-22 15:41:55 +03:00
|
|
|
/**
|
|
|
|
|
* Is element fully visible
|
|
|
|
|
*/
|
2026-02-02 12:13:58 +03:00
|
|
|
isFullyVisible: boolean;
|
|
|
|
|
/**
|
|
|
|
|
* Is element partially visible
|
|
|
|
|
*/
|
|
|
|
|
isPartiallyVisible: boolean;
|
2026-01-22 15:41:55 +03:00
|
|
|
/**
|
|
|
|
|
* From 0 to 1
|
|
|
|
|
*/
|
|
|
|
|
proximity: number;
|
2026-02-01 11:52:09 +03:00
|
|
|
/**
|
|
|
|
|
* Children snippet
|
|
|
|
|
*/
|
|
|
|
|
children: Snippet<[font: UnifiedFont]>;
|
2026-01-18 12:59:12 +03:00
|
|
|
}
|
|
|
|
|
|
2026-02-02 12:13:58 +03:00
|
|
|
const { font, isFullyVisible, isPartiallyVisible, proximity, children }: Props = $props();
|
2026-01-22 15:41:55 +03:00
|
|
|
|
|
|
|
|
let timeoutId = $state<NodeJS.Timeout | null>(null);
|
|
|
|
|
|
|
|
|
|
// Create a spring for smooth scale animation
|
|
|
|
|
const scale = new Spring(1, {
|
|
|
|
|
stiffness: 0.3,
|
|
|
|
|
damping: 0.7,
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
// Springs react to the virtualizer's computed state
|
|
|
|
|
const bloom = new Spring(0, {
|
|
|
|
|
stiffness: 0.15,
|
|
|
|
|
damping: 0.6,
|
|
|
|
|
});
|
2026-01-18 12:59:12 +03:00
|
|
|
|
2026-01-22 15:41:55 +03:00
|
|
|
// Sync spring to proximity for a "Lens" effect
|
|
|
|
|
$effect(() => {
|
2026-02-02 12:13:58 +03:00
|
|
|
bloom.target = isPartiallyVisible ? 1 : 0;
|
2026-01-22 15:41:55 +03:00
|
|
|
});
|
2026-01-18 14:59:00 +03:00
|
|
|
|
2026-01-22 15:41:55 +03:00
|
|
|
$effect(() => {
|
|
|
|
|
return () => {
|
|
|
|
|
if (timeoutId) {
|
|
|
|
|
clearTimeout(timeoutId);
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
function animateSelection() {
|
|
|
|
|
scale.target = 0.98;
|
|
|
|
|
|
|
|
|
|
timeoutId = setTimeout(() => {
|
|
|
|
|
scale.target = 1;
|
|
|
|
|
}, 150);
|
|
|
|
|
}
|
2026-01-18 12:59:12 +03:00
|
|
|
</script>
|
|
|
|
|
|
2026-01-22 15:41:55 +03:00
|
|
|
<div
|
|
|
|
|
class={cn('pb-1 will-change-transform')}
|
|
|
|
|
style:opacity={bloom.current}
|
|
|
|
|
style:transform="
|
|
|
|
|
scale({0.92 + (bloom.current * 0.08)})
|
|
|
|
|
translateY({(1 - bloom.current) * 10}px)
|
|
|
|
|
"
|
|
|
|
|
>
|
2026-02-01 11:52:09 +03:00
|
|
|
{@render children?.(font)}
|
2026-01-18 12:59:12 +03:00
|
|
|
</div>
|