import { VIRTUAL_INDEX_NOT_LOADED } from '$entities/Font'; import { cubicOut } from 'svelte/easing'; import { type CrossfadeParams, type TransitionConfig, crossfade, } from 'svelte/transition'; /** * Custom parameters for dot transitions in virtualized lists. */ export interface DotTransitionParams extends CrossfadeParams { /** * Unique key for crossfade pairing */ key: any; /** * Current index of the item in the list */ index: number; /** * Target index to move towards (e.g. counterpart side index) */ otherIndex: number; } /** * Type-safe helper to create dot transition parameters. */ export function getDotTransitionParams( key: 'active-dot' | 'inactive-dot', index: number, otherIndex: number, ): DotTransitionParams { return { key, index, otherIndex }; } /** * Type guard for DotTransitionParams. */ function isDotTransitionParams(p: CrossfadeParams): p is DotTransitionParams { return ( p !== null && typeof p === 'object' && 'index' in p && 'otherIndex' in p ); } /** * Creates a crossfade transition pair optimized for virtualized font lists. * * It uses the 'index' and 'otherIndex' params to calculate the direction * of the slide animation when a matching pair cannot be found in the DOM * (e.g. because it was virtualized out). */ export function createDotCrossfade() { return crossfade({ duration: 300, easing: cubicOut, fallback(node: Element, params: CrossfadeParams, _intro: boolean): TransitionConfig { if (!isDotTransitionParams(params)) { return { duration: 300, easing: cubicOut, css: t => `opacity: ${t};`, }; } const { index, otherIndex } = params; // If the other target is unknown, just fade in place if (otherIndex === undefined || otherIndex === -1) { return { duration: 300, easing: cubicOut, css: t => `opacity: ${t};`, }; } const diff = otherIndex - index; const sign = diff > 0 ? 1 : (diff < 0 ? -1 : 0); // Use container height for a full-height slide const listEl = node.closest('[data-font-list]'); const h = listEl?.clientHeight ?? 300; const fromY = sign * h; return { duration: 300, easing: cubicOut, css: (t, u) => ` transform: translateY(${fromY * u}px); opacity: ${t}; `, }; }, }); }