feat(font-list): show empty state when search yields no fonts

Adds an `empty` snippet prop to FontVirtualList and supplies it from the
sidebar FontList. Settled queries with zero results now render a centered
"No typefaces found" label instead of a blank list area.
This commit is contained in:
Ilia Mashkov
2026-05-28 21:36:23 +03:00
parent dec83c93d0
commit c90a258f6c
2 changed files with 17 additions and 0 deletions
@@ -40,6 +40,10 @@ interface Props extends
* Skeleton snippet * Skeleton snippet
*/ */
skeleton?: Snippet; skeleton?: Snippet;
/**
* Empty-state snippet rendered when the query settled with zero fonts
*/
empty?: Snippet;
} }
let { let {
@@ -47,6 +51,7 @@ let {
onVisibleItemsChange, onVisibleItemsChange,
weight, weight,
skeleton, skeleton,
empty,
...rest ...rest
}: Props = $props(); }: Props = $props();
@@ -59,6 +64,8 @@ let isCatchingUp = $state(false);
const showInitialSkeleton = $derived(!!skeleton && isLoading && fontCatalogStore.fonts.length === 0); const showInitialSkeleton = $derived(!!skeleton && isLoading && fontCatalogStore.fonts.length === 0);
const showCatchupSkeleton = $derived(!!skeleton && isCatchingUp); const showCatchupSkeleton = $derived(!!skeleton && isCatchingUp);
// Settled query with no matches — empty state replaces the (otherwise blank) list.
const showEmpty = $derived(!!empty && !isLoading && !isCatchingUp && fontCatalogStore.fonts.length === 0);
function handleInternalVisibleChange(items: UnifiedFont[]) { function handleInternalVisibleChange(items: UnifiedFont[]) {
visibleFonts = items; visibleFonts = items;
@@ -163,6 +170,10 @@ function handleNearBottom(_lastVisibleIndex: number) {
<div class="overflow-hidden h-full" transition:fade={{ duration: 300 }}> <div class="overflow-hidden h-full" transition:fade={{ duration: 300 }}>
{@render skeleton()} {@render skeleton()}
</div> </div>
{:else if showEmpty && empty}
<div class="h-full" transition:fade={{ duration: 200 }}>
{@render empty()}
</div>
{:else} {:else}
<!-- VirtualList persists during pagination - no destruction/recreation --> <!-- VirtualList persists during pagination - no destruction/recreation -->
<VirtualList <VirtualList
@@ -104,6 +104,12 @@ function isFontReady(font: UnifiedFont): boolean {
gap={2} gap={2}
class="bg-transparent min-h-0 h-full scroll-stable py-2 pl-6 pr-4" class="bg-transparent min-h-0 h-full scroll-stable py-2 pl-6 pr-4"
> >
{#snippet empty()}
<div class="px-6 py-12 flex items-center justify-center">
<Label variant="muted" size="sm">No typefaces found</Label>
</div>
{/snippet}
{#snippet skeleton()} {#snippet skeleton()}
<div class="py-2.5 md:py-3 px-7"> <div class="py-2.5 md:py-3 px-7">
{#each { length: 50 } as _, index (index)} {#each { length: 50 } as _, index (index)}