refactor(font): replace fontCatalogStore singleton with lazy getFontCatalog

Swap the eagerly-constructed fontCatalogStore singleton for a lazy
getFontCatalog() accessor (plus __resetFontCatalog for tests), so the
InfiniteQueryObserver is created on first use rather than at module load.
Update the model barrels and all consumers (FontVirtualList, SampleList,
SampleListSection) to the accessor.

Also extract createFontLoadRequestConfig from FontVirtualList: it resolves a
font's URL for a weight and returns a 0-or-1 element array, letting callers
flatMap over a list to build load requests and drop unresolvable fonts in one
pass.
This commit is contained in:
Ilia Mashkov
2026-06-01 17:24:55 +03:00
parent 39d1ce4c37
commit 10603d18bf
8 changed files with 204 additions and 49 deletions
@@ -10,8 +10,8 @@ import {
createFontRowSizeResolver,
} from '$entities/Font';
import {
fontCatalogStore,
fontLifecycleManager,
getFontCatalog,
} from '$entities/Font/model';
import {
TypographyMenu,
@@ -22,6 +22,8 @@ import { throttle } from '$shared/lib/utils';
import { Skeleton } from '$shared/ui';
import { layoutManager } from '../../model';
const fontCatalog = getFontCatalog();
// FontSampler chrome heights — derived from Tailwind classes in FontSampler.svelte.
// Header: py-3 (12+12px padding) + ~32px content row ≈ 56px.
// Only the header is counted; the mobile footer (md:hidden) is excluded because
@@ -44,14 +46,16 @@ const SAMPLER_FALLBACK_HEIGHT = 220;
*/
const CHECK_POSITION_THROTTLE_MS = 100;
let text = $state('The quick brown fox jumps over the lazy dog...');
const fonts = $derived(fontCatalog?.fonts);
let text = $state<string>('The quick brown fox jumps over the lazy dog...');
let wrapper = $state<HTMLDivElement | null>(null);
// Binds to the actual window height
let innerHeight = $state(0);
let innerHeight = $state<number>(0);
// Is the component above the middle of the viewport?
let isAboveMiddle = $state(false);
let isAboveMiddle = $state<boolean>(false);
// Inner width of the wrapper div — updated by bind:clientWidth on mount and resize.
let containerWidth = $state(0);
let containerWidth = $state<number>(0);
const checkPosition = throttle(() => {
if (!wrapper) {
@@ -69,7 +73,7 @@ const checkPosition = throttle(() => {
// change triggers a full offsets recompute in createVirtualizer — no DOM snap.
const fontRowHeight = $derived.by(() =>
createFontRowSizeResolver({
getFonts: () => fontCatalogStore.fonts,
getFonts: () => fonts,
getWeight: () => typographySettingsStore.weight,
getPreviewText: () => text,
getContainerWidth: () => containerWidth,
@@ -3,7 +3,7 @@
Wraps SampleList with a Section component
-->
<script lang="ts">
import { fontCatalogStore } from '$entities/Font/model';
import { getFontCatalog } from '$entities/Font/model';
import { NavigationWrapper } from '$features/Breadcrumb';
import type { ResponsiveManager } from '$shared/lib';
import { cn } from '$shared/lib';
@@ -26,6 +26,9 @@ interface Props {
const { index }: Props = $props();
const responsive = getContext<ResponsiveManager>('responsive');
const fontCatalog = getFontCatalog();
const total = $derived<number>(fontCatalog?.pagination?.total);
</script>
<NavigationWrapper index={2} title="Samples">
@@ -36,7 +39,7 @@ const responsive = getContext<ResponsiveManager>('responsive');
id="sample_set"
title="Sample Set"
headerTitle="visual_output"
headerSubtitle="items_total: {fontCatalogStore.pagination.total ?? 0}"
headerSubtitle="items_total: {total ?? 0}"
headerAction={registerAction}
>
{#snippet headerContent()}