Refactor/reacrhitecture to fsd+ #49

Merged
ilia merged 70 commits from refactor/reacrhitecture-to-fsd+ into main 2026-06-03 09:55:47 +00:00
7 changed files with 61 additions and 25 deletions
Showing only changes of commit 09869aed00 - Show all commits
+1
View File
@@ -19,6 +19,7 @@ export type { FontRowSizeResolverOptions } from './lib';
export { export {
FontApplicator, FontApplicator,
FontSampler,
FontVirtualList, FontVirtualList,
} from './ui'; } from './ui';
@@ -4,7 +4,7 @@ import { defineMeta } from '@storybook/addon-svelte-csf';
import FontSampler from './FontSampler.svelte'; import FontSampler from './FontSampler.svelte';
const { Story } = defineMeta({ const { Story } = defineMeta({
title: 'Features/FontSampler', title: 'Entities/Font/FontSampler',
component: FontSampler, component: FontSampler,
tags: ['autodocs'], tags: ['autodocs'],
parameters: { parameters: {
@@ -39,8 +39,8 @@ const { Story } = defineMeta({
</script> </script>
<script lang="ts"> <script lang="ts">
import type { UnifiedFont } from '$entities/Font';
import type { ComponentProps } from 'svelte'; import type { ComponentProps } from 'svelte';
import type { UnifiedFont } from '../../model/types';
// Mock fonts for testing // Mock fonts for testing
const mockArial: UnifiedFont = { const mockArial: UnifiedFont = {
@@ -84,6 +84,14 @@ const mockGeorgia: UnifiedFont = {
isVariable: false, isVariable: false,
}, },
}; };
// Stand-in for the AdjustTypography store the composing widget injects.
const mockTypography = {
renderedSize: 48,
weight: 400,
height: 1.5,
spacing: 0,
};
</script> </script>
<Story <Story
@@ -93,6 +101,7 @@ const mockGeorgia: UnifiedFont = {
status: 'loaded', status: 'loaded',
text: 'The quick brown fox jumps over the lazy dog', text: 'The quick brown fox jumps over the lazy dog',
index: 0, index: 0,
typography: mockTypography,
}} }}
> >
{#snippet template(args: ComponentProps<typeof FontSampler>)} {#snippet template(args: ComponentProps<typeof FontSampler>)}
@@ -111,6 +120,7 @@ const mockGeorgia: UnifiedFont = {
text: text:
'Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris.', 'Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris.',
index: 1, index: 1,
typography: mockTypography,
}} }}
> >
{#snippet template(args: ComponentProps<typeof FontSampler>)} {#snippet template(args: ComponentProps<typeof FontSampler>)}
@@ -4,12 +4,6 @@
Visual design matches FontCard: sharp corners, red hover accent, header stats. Visual design matches FontCard: sharp corners, red hover accent, header stats.
--> -->
<script lang="ts"> <script lang="ts">
import {
FontApplicator,
type FontLoadStatus,
type UnifiedFont,
} from '$entities/Font';
import { getTypographySettingsStore } from '$features/AdjustTypography/model';
import { import {
Badge, Badge,
ContentEditable, ContentEditable,
@@ -18,6 +12,35 @@ import {
Stat, Stat,
} from '$shared/ui'; } from '$shared/ui';
import { fly } from 'svelte/transition'; import { fly } from 'svelte/transition';
import type {
FontLoadStatus,
UnifiedFont,
} from '../../model/types';
import FontApplicator from '../FontApplicator/FontApplicator.svelte';
/**
* Minimal typography contract this view renders with. The AdjustTypography
* store satisfies it structurally; defining it here keeps the entity decoupled
* from that feature (no entity -> feature import).
*/
interface FontSampleTypography {
/**
* Rendered font size in px
*/
renderedSize: number;
/**
* Numeric font weight
*/
weight: number;
/**
* Line-height multiplier
*/
height: number;
/**
* Letter spacing
*/
spacing: number;
}
interface Props { interface Props {
/** /**
@@ -39,11 +62,15 @@ interface Props {
* @default 0 * @default 0
*/ */
index?: number; index?: number;
/**
* Typography settings to render the sample with. Injected by the composing
* widget (which owns the AdjustTypography store) so this entity view stays
* decoupled from that feature — the same inversion as `status`.
*/
typography: FontSampleTypography;
} }
let { font, status, text = $bindable(), index = 0 }: Props = $props(); let { font, status, text = $bindable(), index = 0, typography }: Props = $props();
const typographySettingsStore = getTypographySettingsStore();
// Extract provider badge with fallback // Extract provider badge with fallback
const providerBadge = $derived( const providerBadge = $derived(
@@ -52,10 +79,10 @@ const providerBadge = $derived(
); );
const stats = $derived([ const stats = $derived([
{ label: 'SZ', value: `${typographySettingsStore.renderedSize}PX` }, { label: 'SZ', value: `${typography.renderedSize}PX` },
{ label: 'WGT', value: `${typographySettingsStore.weight}` }, { label: 'WGT', value: `${typography.weight}` },
{ label: 'LH', value: typographySettingsStore.height?.toFixed(2) }, { label: 'LH', value: typography.height.toFixed(2) },
{ label: 'LTR', value: `${typographySettingsStore.spacing}` }, { label: 'LTR', value: `${typography.spacing}` },
]); ]);
</script> </script>
@@ -73,7 +100,7 @@ const stats = $derived([
min-h-60 min-h-60
rounded-none rounded-none
" "
style:font-weight={typographySettingsStore.weight} style:font-weight={typography.weight}
> >
<!-- ── Header bar ─────────────────────────────────────────────────── --> <!-- ── Header bar ─────────────────────────────────────────────────── -->
<div <div
@@ -141,9 +168,9 @@ const stats = $derived([
<FontApplicator {font} {status}> <FontApplicator {font} {status}>
<ContentEditable <ContentEditable
bind:text bind:text
fontSize={typographySettingsStore.renderedSize} fontSize={typography.renderedSize}
lineHeight={typographySettingsStore.height} lineHeight={typography.height}
letterSpacing={typographySettingsStore.spacing} letterSpacing={typography.spacing}
/> />
</FontApplicator> </FontApplicator>
</div> </div>
+2
View File
@@ -1,7 +1,9 @@
import FontApplicator from './FontApplicator/FontApplicator.svelte'; import FontApplicator from './FontApplicator/FontApplicator.svelte';
import FontSampler from './FontSampler/FontSampler.svelte';
import FontVirtualList from './FontVirtualList/FontVirtualList.svelte'; import FontVirtualList from './FontVirtualList/FontVirtualList.svelte';
export { export {
FontApplicator, FontApplicator,
FontSampler,
FontVirtualList, FontVirtualList,
}; };
-1
View File
@@ -1 +0,0 @@
export { FontSampler } from './ui';
-3
View File
@@ -1,3 +0,0 @@
import FontSampler from './FontSampler/FontSampler.svelte';
export { FontSampler };
@@ -6,6 +6,7 @@
--> -->
<script lang="ts"> <script lang="ts">
import { import {
FontSampler,
FontVirtualList, FontVirtualList,
createFontRowSizeResolver, createFontRowSizeResolver,
getFontCatalog, getFontCatalog,
@@ -15,7 +16,6 @@ import {
TypographyMenu, TypographyMenu,
getTypographySettingsStore, getTypographySettingsStore,
} from '$features/AdjustTypography'; } from '$features/AdjustTypography';
import { FontSampler } from '$features/DisplayFont';
import { throttle } from '$shared/lib/utils'; import { throttle } from '$shared/lib/utils';
import { Skeleton } from '$shared/ui'; import { Skeleton } from '$shared/ui';
import { getLayoutManager } from '../../model'; import { getLayoutManager } from '../../model';
@@ -127,7 +127,7 @@ const fontRowHeight = $derived.by(() =>
getFontStatus reads a $state SvelteMap, so the row stays reactive. getFontStatus reads a $state SvelteMap, so the row stays reactive.
--> -->
{@const status = fontLifecycleManager.getFontStatus(font.id, typographySettingsStore.weight, font.features?.isVariable)} {@const status = fontLifecycleManager.getFontStatus(font.id, typographySettingsStore.weight, font.features?.isVariable)}
<FontSampler bind:text {font} {index} {status} /> <FontSampler bind:text {font} {index} {status} typography={typographySettingsStore} />
{/snippet} {/snippet}
</FontVirtualList> </FontVirtualList>