refactor(Font): rename fontStore and appliedFontsManager
Both names were vague or overloaded: - fontStore / FontStore -> fontCatalogStore / FontCatalogStore Three font-related stores live in this slice; the new name names the paginated catalog specifically. - appliedFontsManager / AppliedFontsManager -> fontLifecycleManager / FontLifecycleManager "Applied" collided with the filter-side appliedFilterStore (different meaning). The class actually orchestrates a load-use-evict lifecycle with FontBufferCache + FontEvictionPolicy + FontLoadQueue collaborators, so "Manager" is justified. Companion types file moved alongside (appliedFonts.ts -> fontLifecycle.ts). Directories, file basenames, factory (createFontStore -> createFontCatalogStore), and the AppliedFontsManagerDeps interface all renamed. All consumers (ComparisonView, SampleList, FontList, FontApplicator, FontVirtualList, FilterAndSortFonts bindings, createFontRowSizeResolver, mocks) updated.
This commit is contained in:
@@ -10,7 +10,7 @@ const { Story } = defineMeta({
|
||||
docs: {
|
||||
description: {
|
||||
component:
|
||||
'Virtualized font list backed by the `fontStore` singleton. Handles font loading registration (pin/touch) for visible items and triggers infinite scroll pagination via `fontStore.nextPage()`. Because the component reads directly from the `fontStore` singleton, stories render against a live (but empty/loading) store — no font data will appear unless the API is reachable from the Storybook host.',
|
||||
'Virtualized font list backed by the `fontCatalogStore` singleton. Handles font loading registration (pin/touch) for visible items and triggers infinite scroll pagination via `fontCatalogStore.nextPage()`. Because the component reads directly from the `fontCatalogStore` singleton, stories render against a live (but empty/loading) store — no font data will appear unless the API is reachable from the Storybook host.',
|
||||
},
|
||||
story: { inline: false },
|
||||
},
|
||||
@@ -33,7 +33,7 @@ import type { ComponentProps } from 'svelte';
|
||||
docs: {
|
||||
description: {
|
||||
story:
|
||||
'Skeleton state shown while `fontStore.fonts` is empty and `fontStore.isLoading` is true. In a real session the skeleton fades out once the first page loads.',
|
||||
'Skeleton state shown while `fontCatalogStore.fonts` is empty and `fontCatalogStore.isLoading` is true. In a real session the skeleton fades out once the first page loads.',
|
||||
},
|
||||
},
|
||||
}}
|
||||
@@ -63,7 +63,7 @@ import type { ComponentProps } from 'svelte';
|
||||
docs: {
|
||||
description: {
|
||||
story:
|
||||
'No `skeleton` snippet provided. When `fontStore.fonts` is empty the underlying VirtualList renders its empty state directly.',
|
||||
'No `skeleton` snippet provided. When `fontCatalogStore.fonts` is empty the underlying VirtualList renders its empty state directly.',
|
||||
},
|
||||
},
|
||||
}}
|
||||
@@ -86,7 +86,7 @@ import type { ComponentProps } from 'svelte';
|
||||
docs: {
|
||||
description: {
|
||||
story:
|
||||
'Demonstrates how to configure a `children` snippet for item rendering. The list will be empty because `fontStore` is not populated in Storybook, but the template shows the expected slot shape: `{ item: UnifiedFont }`.',
|
||||
'Demonstrates how to configure a `children` snippet for item rendering. The list will be empty because `fontCatalogStore` is not populated in Storybook, but the template shows the expected slot shape: `{ item: UnifiedFont }`.',
|
||||
},
|
||||
},
|
||||
}}
|
||||
|
||||
@@ -18,8 +18,8 @@ import { getFontUrl } from '../../lib';
|
||||
import {
|
||||
type FontLoadRequestConfig,
|
||||
type UnifiedFont,
|
||||
appliedFontsManager,
|
||||
fontStore,
|
||||
fontCatalogStore,
|
||||
fontLifecycleManager,
|
||||
} from '../../model';
|
||||
|
||||
interface Props extends
|
||||
@@ -51,13 +51,13 @@ let {
|
||||
}: Props = $props();
|
||||
|
||||
const isLoading = $derived(
|
||||
fontStore.isFetching || fontStore.isLoading,
|
||||
fontCatalogStore.isFetching || fontCatalogStore.isLoading,
|
||||
);
|
||||
|
||||
let visibleFonts = $state<UnifiedFont[]>([]);
|
||||
let isCatchingUp = $state(false);
|
||||
|
||||
const showInitialSkeleton = $derived(!!skeleton && isLoading && fontStore.fonts.length === 0);
|
||||
const showInitialSkeleton = $derived(!!skeleton && isLoading && fontCatalogStore.fonts.length === 0);
|
||||
const showCatchupSkeleton = $derived(!!skeleton && isCatchingUp);
|
||||
|
||||
function handleInternalVisibleChange(items: UnifiedFont[]) {
|
||||
@@ -68,23 +68,23 @@ function handleInternalVisibleChange(items: UnifiedFont[]) {
|
||||
|
||||
/**
|
||||
* Handle jump scroll — batch-load all missing pages then re-enable font loading.
|
||||
* Suppresses appliedFontsManager.touch() during catch-up to avoid loading
|
||||
* Suppresses fontLifecycleManager.touch() during catch-up to avoid loading
|
||||
* font files for thousands of intermediate fonts.
|
||||
*/
|
||||
async function handleJump(targetIndex: number) {
|
||||
if (isCatchingUp || !fontStore.pagination.hasMore) {
|
||||
if (isCatchingUp || !fontCatalogStore.pagination.hasMore) {
|
||||
return;
|
||||
}
|
||||
isCatchingUp = true;
|
||||
try {
|
||||
await fontStore.fetchAllPagesTo(targetIndex);
|
||||
await fontCatalogStore.fetchAllPagesTo(targetIndex);
|
||||
} finally {
|
||||
isCatchingUp = false;
|
||||
}
|
||||
}
|
||||
|
||||
const debouncedTouch = debounce((configs: FontLoadRequestConfig[]) => {
|
||||
appliedFontsManager.touch(configs);
|
||||
fontLifecycleManager.touch(configs);
|
||||
}, 150);
|
||||
|
||||
// Re-touch whenever visible set or weight changes — fixes weight-change gap
|
||||
@@ -111,11 +111,11 @@ $effect(() => {
|
||||
const w = weight;
|
||||
const fonts = visibleFonts;
|
||||
for (const f of fonts) {
|
||||
appliedFontsManager.pin(f.id, w, f.features?.isVariable);
|
||||
fontLifecycleManager.pin(f.id, w, f.features?.isVariable);
|
||||
}
|
||||
return () => {
|
||||
for (const f of fonts) {
|
||||
appliedFontsManager.unpin(f.id, w, f.features?.isVariable);
|
||||
fontLifecycleManager.unpin(f.id, w, f.features?.isVariable);
|
||||
}
|
||||
};
|
||||
});
|
||||
@@ -125,12 +125,12 @@ $effect(() => {
|
||||
*/
|
||||
function loadMore() {
|
||||
if (
|
||||
!fontStore.pagination.hasMore
|
||||
|| fontStore.isFetching
|
||||
!fontCatalogStore.pagination.hasMore
|
||||
|| fontCatalogStore.isFetching
|
||||
) {
|
||||
return;
|
||||
}
|
||||
fontStore.nextPage();
|
||||
fontCatalogStore.nextPage();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -140,12 +140,12 @@ function loadMore() {
|
||||
* of the loaded items. Only fetches if there are more pages available.
|
||||
*/
|
||||
function handleNearBottom(_lastVisibleIndex: number) {
|
||||
const { hasMore } = fontStore.pagination;
|
||||
const { hasMore } = fontCatalogStore.pagination;
|
||||
|
||||
// VirtualList already checks if we're near the bottom of loaded items.
|
||||
// Guard isCatchingUp: fetchAllPagesTo bypasses TQ so isFetching stays false
|
||||
// during batch catch-up, which would otherwise let nextPage() race with it.
|
||||
if (hasMore && !fontStore.isFetching && !isCatchingUp) {
|
||||
if (hasMore && !fontCatalogStore.isFetching && !isCatchingUp) {
|
||||
loadMore();
|
||||
}
|
||||
}
|
||||
@@ -160,8 +160,8 @@ function handleNearBottom(_lastVisibleIndex: number) {
|
||||
{:else}
|
||||
<!-- VirtualList persists during pagination - no destruction/recreation -->
|
||||
<VirtualList
|
||||
items={fontStore.fonts}
|
||||
total={fontStore.pagination.total}
|
||||
items={fontCatalogStore.fonts}
|
||||
total={fontCatalogStore.pagination.total}
|
||||
isLoading={isLoading || isCatchingUp}
|
||||
onVisibleItemsChange={handleInternalVisibleChange}
|
||||
onNearBottom={handleNearBottom}
|
||||
|
||||
Reference in New Issue
Block a user