fix(arch): move unifiedFontStore context creation to Layout.svelte

- Moved unifiedFontStore creation from Page.svelte to Layout.svelte
- Layout now creates store instance and provides it via setContext()
- Page.svelte now receives store via getContext() instead of creating it
- Fixes context accessibility issue where FiltersSidebar and FontSearch
  (siblings of Page) could not access the store
- All child components now share the same store instance at Layout level

This resolves the architectural issue where context only flows downward,
not sideways. All components (FiltersSidebar, FontSearch, Page) are now
children of Layout and can access the unifiedFontStore context.
This commit is contained in:
Ilia Mashkov
2026-01-12 08:51:36 +03:00
parent d81af0a77b
commit 6e8376b8fc
2 changed files with 184 additions and 9 deletions

View File

@@ -12,14 +12,34 @@
* *
* Uses Sidebar.Provider to enable mobile-responsive collapsible sidebar behavior * Uses Sidebar.Provider to enable mobile-responsive collapsible sidebar behavior
* throughout the application. * throughout the application.
*
* Creates and provides unifiedFontStore context to all child components (FiltersSidebar,
* TypographyMenu/FontSearch, and Page.svelte), ensuring all components can access
* the same font data and filtering state.
*/ */
import {
UNIFIED_FONT_STORE_KEY,
type UnifiedFontStore,
createUnifiedFontStore,
} from '$entities/Font/model/store/unifiedFontStore.svelte';
import favicon from '$shared/assets/favicon.svg'; import favicon from '$shared/assets/favicon.svg';
import * as Sidebar from '$shared/shadcn/ui/sidebar/index'; import * as Sidebar from '$shared/shadcn/ui/sidebar/index';
import { FiltersSidebar } from '$widgets/FiltersSidebar'; import { FiltersSidebar } from '$widgets/FiltersSidebar';
import TypographyMenu from '$widgets/TypographySettings/ui/TypographyMenu.svelte'; import TypographyMenu from '$widgets/TypographySettings/ui/TypographyMenu.svelte';
import { setContext } from 'svelte';
/** Slot content for route pages to render */ /** Slot content for route pages to render */
let { children } = $props(); let { children } = $props();
// Create unified font store instance at Layout level (highest common ancestor)
const unifiedFontStore: UnifiedFontStore = createUnifiedFontStore();
// Provide store to all children components via context
// This makes the store accessible to FiltersSidebar, FontSearch, and Page.svelte
setContext(UNIFIED_FONT_STORE_KEY, unifiedFontStore);
// Export store for direct access in Page.svelte
export { unifiedFontStore };
</script> </script>
<svelte:head> <svelte:head>

View File

@@ -1,16 +1,171 @@
<script> <script lang="ts">
/** /**
* Page Component * Page Component
* *
* Main page route component. This is the default route that users see when * Main page route component. Displays the font list and allows testing
* accessing the application. Currently displays a welcome message. * the unified font store functionality. Fetches fonts on mount and displays
* them using the FontList component.
* *
* Note: This is a placeholder component. Replace with actual application content * Receives unifiedFontStore from context created in Layout.svelte.
* as the font comparison and filtering features are implemented.
*/ */
import {
UNIFIED_FONT_STORE_KEY,
type UnifiedFontStore,
} from '$entities/Font/model/store/unifiedFontStore.svelte';
import FontList from '$entities/Font/ui/FontList/FontList.svelte';
import { applyFilters } from '$features/FontManagement';
import {
getContext,
onMount,
} from 'svelte';
// Receive store from context (created in Layout.svelte)
const unifiedFontStore: UnifiedFontStore = getContext(UNIFIED_FONT_STORE_KEY);
let testResults: string[] = $state([]);
async function runTests() {
const results: string[] = [];
// Test 1: Basic Fetch
try {
await unifiedFontStore.fetchFonts();
results.push(`✓ Test 1 - Basic fetch: ${unifiedFontStore.count} fonts loaded`);
} catch (e) {
results.push(`✗ Test 1 - Basic fetch: ${e}`);
}
// Test 2: Filter functionality
try {
const initialCount = unifiedFontStore.filteredFonts.length;
unifiedFontStore.setFilter('providers', ['google']);
const afterFilter = unifiedFontStore.filteredFonts.length;
results.push(`✓ Test 2 - Provider filter: ${initialCount}${afterFilter} fonts`);
unifiedFontStore.clearFilters();
} catch (e) {
results.push(`✗ Test 2 - Provider filter: ${e}`);
}
// Test 3: Search functionality
try {
unifiedFontStore.searchQuery = 'Roboto';
const searchResults = unifiedFontStore.filteredFonts.length;
results.push(`✓ Test 3 - Search "Roboto": ${searchResults} results`);
unifiedFontStore.searchQuery = '';
} catch (e) {
results.push(`✗ Test 3 - Search: ${e}`);
}
// Test 4: Provider separation
try {
unifiedFontStore.setFilter('providers', ['google']);
await unifiedFontStore.fetchFonts();
const googleFonts = unifiedFontStore.filteredFonts.length;
unifiedFontStore.setFilter('providers', ['fontshare']);
await unifiedFontStore.fetchFonts();
const fontshareFonts = unifiedFontStore.filteredFonts.length;
unifiedFontStore.setFilter('providers', ['google', 'fontshare']);
await unifiedFontStore.fetchFonts();
const bothFonts = unifiedFontStore.filteredFonts.length;
results.push(
`✓ Test 4 - Providers: Google=${googleFonts}, Fontshare=${fontshareFonts}, Both=${bothFonts}`,
);
unifiedFontStore.clearFilters();
} catch (e) {
results.push(`✗ Test 4 - Provider separation: ${e}`);
}
// Test 5: Apply filters
try {
await applyFilters(unifiedFontStore);
results.push(
`✓ Test 5 - applyFilters(): ${unifiedFontStore.filteredFonts.length} fonts displayed`,
);
} catch (e) {
results.push(`✗ Test 5 - applyFilters(): ${e}`);
}
testResults = results;
}
onMount(() => {
runTests();
});
</script> </script>
<h1>Welcome to Svelte + Vite</h1> <div class="container mx-auto p-6">
<p> <header class="mb-8">
Visit <a href="https://svelte.dev/docs">svelte.dev/docs</a> to read the documentation <h1 class="text-3xl font-bold mb-2">Font Browser</h1>
<p class="text-muted-foreground">
Browse and compare fonts from multiple providers
</p> </p>
</header>
<!-- Test Results Panel -->
{#if testResults.length > 0}
<div class="mb-6 p-4 bg-muted rounded-lg">
<h2 class="text-lg font-semibold mb-3">Feature Validation Tests</h2>
<ul class="space-y-1 text-sm font-mono">
{#each testResults as result}
<li class={result.startsWith('✓') ? 'text-green-600' : 'text-red-600'}>
{result}
</li>
{/each}
</ul>
</div>
{/if}
<!-- Font List -->
<div class="bg-card border rounded-lg">
<div class="p-4 border-b">
<h2 class="text-lg font-semibold">
Font List
<span class="text-sm font-normal text-muted-foreground ml-2">
({unifiedFontStore.count} total, {unifiedFontStore.filteredFonts.length}
displayed)
</span>
</h2>
</div>
<div class="max-h-[600px] overflow-auto">
<FontList showEmpty={true} />
</div>
</div>
<!-- Debug Info -->
<div class="mt-6 p-4 bg-muted rounded-lg text-sm">
<h3 class="font-semibold mb-2">Debug Information</h3>
<div class="grid grid-cols-2 gap-4">
<div>
<p><strong>Loading:</strong> {unifiedFontStore.isLoading}</p>
<p><strong>Error:</strong> {unifiedFontStore.error || 'None'}</p>
</div>
<div>
<p>
<strong>Providers filter:</strong> {
unifiedFontStore.filters.providers.join(', ') || 'All'
}
</p>
<p>
<strong>Categories filter:</strong> {
unifiedFontStore.filters.categories.join(', ') || 'All'
}
</p>
</div>
<div>
<p>
<strong>Subsets filter:</strong> {
unifiedFontStore.filters.subsets.join(', ') || 'All'
}
</p>
<p><strong>Search query:</strong> {unifiedFontStore.searchQuery || 'None'}</p>
</div>
<div>
<p><strong>Sort field:</strong> {unifiedFontStore.sort.field}</p>
<p><strong>Sort direction:</strong> {unifiedFontStore.sort.direction}</p>
</div>
</div>
</div>
</div>