Compare commits
9 Commits
main
...
feature/un
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
dde187e0b2 | ||
|
|
5a7c61ade7 | ||
|
|
d2bce85f9c | ||
|
|
e509463911 | ||
|
|
db08f523f6 | ||
|
|
c5fa159c14 | ||
|
|
8645c7dcc8 | ||
|
|
fbeb84270b | ||
|
|
c1ac9b5bc4 |
@@ -85,19 +85,11 @@ onDestroy(() => themeManager.destroy());
|
||||
theme === 'dark' ? 'dark' : '',
|
||||
)}
|
||||
>
|
||||
<header>
|
||||
<BreadcrumbHeader />
|
||||
</header>
|
||||
|
||||
<!-- <ScrollArea class="h-screen w-screen"> -->
|
||||
<!-- <main class="flex-1 w-full mx-auto relative"> -->
|
||||
<TooltipProvider>
|
||||
{#if fontsReady}
|
||||
{@render children?.()}
|
||||
{/if}
|
||||
</TooltipProvider>
|
||||
<!-- </main> -->
|
||||
<!-- </ScrollArea> -->
|
||||
<footer></footer>
|
||||
</div>
|
||||
</ResponsiveProvider>
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import type { ControlModel } from '$shared/lib';
|
||||
import type { ControlId } from '..';
|
||||
import type { ControlId } from '../types/typography';
|
||||
|
||||
/**
|
||||
* Font size constants
|
||||
@@ -1,2 +1,3 @@
|
||||
export * from './const/const';
|
||||
export * from './store';
|
||||
export * from './types';
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
// Applied fonts manager
|
||||
export { appliedFontsManager } from './appliedFontsStore/appliedFontsStore.svelte';
|
||||
export * from './appliedFontsStore/appliedFontsStore.svelte';
|
||||
|
||||
// Batch font store
|
||||
export { BatchFontStore } from './batchFontStore.svelte';
|
||||
|
||||
@@ -33,3 +33,4 @@ export type {
|
||||
} from './store';
|
||||
|
||||
export * from './store/appliedFonts';
|
||||
export * from './typography';
|
||||
|
||||
1
src/entities/Font/model/types/typography.ts
Normal file
1
src/entities/Font/model/types/typography.ts
Normal file
@@ -0,0 +1 @@
|
||||
export type ControlId = 'font_size' | 'font_weight' | 'line_height' | 'letter_spacing';
|
||||
@@ -10,6 +10,7 @@ import { cn } from '$shared/shadcn/utils/shadcn-utils';
|
||||
import type { Snippet } from 'svelte';
|
||||
import { prefersReducedMotion } from 'svelte/motion';
|
||||
import {
|
||||
DEFAULT_FONT_WEIGHT,
|
||||
type UnifiedFont,
|
||||
appliedFontsManager,
|
||||
} from '../../model';
|
||||
@@ -36,7 +37,7 @@ interface Props {
|
||||
|
||||
let {
|
||||
font,
|
||||
weight = 400,
|
||||
weight = DEFAULT_FONT_WEIGHT,
|
||||
className,
|
||||
children,
|
||||
}: Props = $props();
|
||||
|
||||
@@ -18,8 +18,8 @@ import {
|
||||
type FontLoadRequestConfig,
|
||||
type UnifiedFont,
|
||||
appliedFontsManager,
|
||||
fontStore,
|
||||
} from '../../model';
|
||||
import { fontStore } from '../../model/store';
|
||||
|
||||
interface Props extends
|
||||
Omit<
|
||||
@@ -53,30 +53,42 @@ const isLoading = $derived(
|
||||
fontStore.isFetching || fontStore.isLoading,
|
||||
);
|
||||
|
||||
function handleInternalVisibleChange(visibleItems: UnifiedFont[]) {
|
||||
const configs: FontLoadRequestConfig[] = [];
|
||||
|
||||
visibleItems.forEach(item => {
|
||||
const url = getFontUrl(item, weight);
|
||||
|
||||
if (url) {
|
||||
configs.push({
|
||||
id: item.id,
|
||||
name: item.name,
|
||||
weight,
|
||||
url,
|
||||
isVariable: item.features?.isVariable,
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
// Auto-register fonts with the manager
|
||||
appliedFontsManager.touch(configs);
|
||||
let visibleFonts = $state<UnifiedFont[]>([]);
|
||||
|
||||
function handleInternalVisibleChange(items: UnifiedFont[]) {
|
||||
visibleFonts = items;
|
||||
// Forward the call to any external listener
|
||||
// onVisibleItemsChange?.(visibleItems);
|
||||
onVisibleItemsChange?.(items);
|
||||
}
|
||||
|
||||
// Re-touch whenever visible set or weight changes — fixes weight-change gap
|
||||
$effect(() => {
|
||||
const configs: FontLoadRequestConfig[] = visibleFonts.flatMap(item => {
|
||||
const url = getFontUrl(item, weight);
|
||||
if (!url) return [];
|
||||
return [{ id: item.id, name: item.name, weight, url, isVariable: item.features?.isVariable }];
|
||||
});
|
||||
if (configs.length > 0) {
|
||||
appliedFontsManager.touch(configs);
|
||||
}
|
||||
});
|
||||
|
||||
// Pin visible fonts so the eviction policy never removes on-screen entries.
|
||||
// Cleanup captures the snapshot values, so a weight change unpins the old
|
||||
// weight before pinning the new one.
|
||||
$effect(() => {
|
||||
const w = weight;
|
||||
const fonts = visibleFonts;
|
||||
for (const f of fonts) {
|
||||
appliedFontsManager.pin(f.id, w, f.features?.isVariable);
|
||||
}
|
||||
return () => {
|
||||
for (const f of fonts) {
|
||||
appliedFontsManager.unpin(f.id, w, f.features?.isVariable);
|
||||
}
|
||||
};
|
||||
});
|
||||
|
||||
/**
|
||||
* Load more fonts by moving to the next page
|
||||
*/
|
||||
|
||||
@@ -35,7 +35,6 @@ const { Story } = defineMeta({
|
||||
|
||||
<script lang="ts">
|
||||
import type { UnifiedFont } from '$entities/Font';
|
||||
import { controlManager } from '$features/SetupFont';
|
||||
|
||||
// Mock fonts for testing
|
||||
const mockArial: UnifiedFont = {
|
||||
|
||||
@@ -8,14 +8,13 @@ import {
|
||||
FontApplicator,
|
||||
type UnifiedFont,
|
||||
} from '$entities/Font';
|
||||
import { controlManager } from '$features/SetupFont';
|
||||
import { typographySettingsStore } from '$features/SetupFont/model';
|
||||
import {
|
||||
Badge,
|
||||
ContentEditable,
|
||||
Divider,
|
||||
Footnote,
|
||||
Stat,
|
||||
StatGroup,
|
||||
} from '$shared/ui';
|
||||
import { fly } from 'svelte/transition';
|
||||
|
||||
@@ -37,11 +36,6 @@ interface Props {
|
||||
|
||||
let { font, text = $bindable(), index = 0 }: Props = $props();
|
||||
|
||||
const fontWeight = $derived(controlManager.weight);
|
||||
const fontSize = $derived(controlManager.renderedSize);
|
||||
const lineHeight = $derived(controlManager.height);
|
||||
const letterSpacing = $derived(controlManager.spacing);
|
||||
|
||||
// Adjust the property name to match your UnifiedFont type
|
||||
const fontType = $derived((font as any).type ?? (font as any).category ?? '');
|
||||
|
||||
@@ -52,10 +46,10 @@ const providerBadge = $derived(
|
||||
);
|
||||
|
||||
const stats = $derived([
|
||||
{ label: 'SZ', value: `${fontSize}PX` },
|
||||
{ label: 'WGT', value: `${fontWeight}` },
|
||||
{ label: 'LH', value: lineHeight?.toFixed(2) },
|
||||
{ label: 'LTR', value: `${letterSpacing}` },
|
||||
{ label: 'SZ', value: `${typographySettingsStore.renderedSize}PX` },
|
||||
{ label: 'WGT', value: `${typographySettingsStore.weight}` },
|
||||
{ label: 'LH', value: typographySettingsStore.height?.toFixed(2) },
|
||||
{ label: 'LTR', value: `${typographySettingsStore.spacing}` },
|
||||
]);
|
||||
</script>
|
||||
|
||||
@@ -75,7 +69,7 @@ const stats = $derived([
|
||||
min-h-60
|
||||
rounded-none
|
||||
"
|
||||
style:font-weight={fontWeight}
|
||||
style:font-weight={typographySettingsStore.weight}
|
||||
>
|
||||
<!-- ── Header bar ─────────────────────────────────────────────────── -->
|
||||
<div
|
||||
@@ -140,12 +134,12 @@ const stats = $derived([
|
||||
|
||||
<!-- ── Main content area ──────────────────────────────────────────── -->
|
||||
<div class="flex-1 p-4 sm:p-5 md:p-8 flex items-center overflow-hidden bg-paper dark:bg-dark-card relative z-10">
|
||||
<FontApplicator {font} weight={fontWeight}>
|
||||
<FontApplicator {font} weight={typographySettingsStore.weight}>
|
||||
<ContentEditable
|
||||
bind:text
|
||||
{fontSize}
|
||||
{lineHeight}
|
||||
{letterSpacing}
|
||||
fontSize={typographySettingsStore.renderedSize}
|
||||
lineHeight={typographySettingsStore.height}
|
||||
letterSpacing={typographySettingsStore.spacing}
|
||||
/>
|
||||
</FontApplicator>
|
||||
</div>
|
||||
|
||||
@@ -1,28 +1,6 @@
|
||||
export { TypographyMenu } from './ui';
|
||||
|
||||
export {
|
||||
type ControlId,
|
||||
controlManager,
|
||||
DEFAULT_FONT_SIZE,
|
||||
DEFAULT_FONT_WEIGHT,
|
||||
DEFAULT_LETTER_SPACING,
|
||||
DEFAULT_LINE_HEIGHT,
|
||||
DEFAULT_TYPOGRAPHY_CONTROLS_DATA,
|
||||
FONT_SIZE_STEP,
|
||||
FONT_WEIGHT_STEP,
|
||||
LINE_HEIGHT_STEP,
|
||||
MAX_FONT_SIZE,
|
||||
MAX_FONT_WEIGHT,
|
||||
MAX_LINE_HEIGHT,
|
||||
MIN_FONT_SIZE,
|
||||
MIN_FONT_WEIGHT,
|
||||
MIN_LINE_HEIGHT,
|
||||
MULTIPLIER_L,
|
||||
MULTIPLIER_M,
|
||||
MULTIPLIER_S,
|
||||
} from './model';
|
||||
|
||||
export {
|
||||
createTypographyControlManager,
|
||||
type TypographyControlManager,
|
||||
createTypographySettingsManager,
|
||||
type TypographySettingsManager,
|
||||
} from './lib';
|
||||
export { typographySettingsStore } from './model';
|
||||
export { TypographyMenu } from './ui';
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
export {
|
||||
createTypographyControlManager,
|
||||
type TypographyControlManager,
|
||||
} from './controlManager/controlManager.svelte';
|
||||
createTypographySettingsManager,
|
||||
type TypographySettingsManager,
|
||||
} from './settingsManager/settingsManager.svelte';
|
||||
|
||||
@@ -10,6 +10,13 @@
|
||||
* when displaying/editing, but the base size is what's stored.
|
||||
*/
|
||||
|
||||
import {
|
||||
type ControlId,
|
||||
DEFAULT_FONT_SIZE,
|
||||
DEFAULT_FONT_WEIGHT,
|
||||
DEFAULT_LETTER_SPACING,
|
||||
DEFAULT_LINE_HEIGHT,
|
||||
} from '$entities/Font';
|
||||
import {
|
||||
type ControlDataModel,
|
||||
type ControlModel,
|
||||
@@ -19,13 +26,6 @@ import {
|
||||
createTypographyControl,
|
||||
} from '$shared/lib';
|
||||
import { SvelteMap } from 'svelte/reactivity';
|
||||
import {
|
||||
type ControlId,
|
||||
DEFAULT_FONT_SIZE,
|
||||
DEFAULT_FONT_WEIGHT,
|
||||
DEFAULT_LETTER_SPACING,
|
||||
DEFAULT_LINE_HEIGHT,
|
||||
} from '../../model';
|
||||
|
||||
type ControlOnlyFields<T extends string = string> = Omit<ControlModel<T>, keyof ControlDataModel>;
|
||||
|
||||
@@ -52,7 +52,7 @@ export interface TypographySettings {
|
||||
* Manages multiple typography controls with persistent storage and
|
||||
* responsive scaling support for font size.
|
||||
*/
|
||||
export class TypographyControlManager {
|
||||
export class TypographySettingsManager {
|
||||
/** Map of controls keyed by ID */
|
||||
#controls = new SvelteMap<string, Control>();
|
||||
/** Responsive multiplier for font size display */
|
||||
@@ -242,7 +242,7 @@ export class TypographyControlManager {
|
||||
* @param storageId - Persistent storage identifier
|
||||
* @returns Typography control manager instance
|
||||
*/
|
||||
export function createTypographyControlManager(
|
||||
export function createTypographySettingsManager(
|
||||
configs: ControlModel<ControlId>[],
|
||||
storageId: string = 'glyphdiff:typography',
|
||||
) {
|
||||
@@ -252,5 +252,5 @@ export function createTypographyControlManager(
|
||||
lineHeight: DEFAULT_LINE_HEIGHT,
|
||||
letterSpacing: DEFAULT_LETTER_SPACING,
|
||||
});
|
||||
return new TypographyControlManager(configs, storage);
|
||||
return new TypographySettingsManager(configs, storage);
|
||||
}
|
||||
@@ -1,4 +1,11 @@
|
||||
/** @vitest-environment jsdom */
|
||||
import {
|
||||
DEFAULT_FONT_SIZE,
|
||||
DEFAULT_FONT_WEIGHT,
|
||||
DEFAULT_LETTER_SPACING,
|
||||
DEFAULT_LINE_HEIGHT,
|
||||
DEFAULT_TYPOGRAPHY_CONTROLS_DATA,
|
||||
} from '$entities/Font';
|
||||
import {
|
||||
afterEach,
|
||||
beforeEach,
|
||||
@@ -8,21 +15,14 @@ import {
|
||||
vi,
|
||||
} from 'vitest';
|
||||
import {
|
||||
DEFAULT_FONT_SIZE,
|
||||
DEFAULT_FONT_WEIGHT,
|
||||
DEFAULT_LETTER_SPACING,
|
||||
DEFAULT_LINE_HEIGHT,
|
||||
DEFAULT_TYPOGRAPHY_CONTROLS_DATA,
|
||||
} from '../../model';
|
||||
import {
|
||||
TypographyControlManager,
|
||||
type TypographySettings,
|
||||
} from './controlManager.svelte';
|
||||
TypographySettingsManager,
|
||||
} from './settingsManager.svelte';
|
||||
|
||||
/**
|
||||
* Test Strategy for TypographyControlManager
|
||||
* Test Strategy for TypographySettingsManager
|
||||
*
|
||||
* This test suite validates the TypographyControlManager state management logic.
|
||||
* This test suite validates the TypographySettingsManager state management logic.
|
||||
* These are unit tests for the manager logic, separate from component rendering.
|
||||
*
|
||||
* NOTE: Svelte 5's $effect runs in microtasks, so we need to flush effects
|
||||
@@ -45,7 +45,7 @@ async function flushEffects() {
|
||||
await Promise.resolve();
|
||||
}
|
||||
|
||||
describe('TypographyControlManager - Unit Tests', () => {
|
||||
describe('TypographySettingsManager - Unit Tests', () => {
|
||||
let mockStorage: TypographySettings;
|
||||
let mockPersistentStore: {
|
||||
value: TypographySettings;
|
||||
@@ -85,7 +85,7 @@ describe('TypographyControlManager - Unit Tests', () => {
|
||||
|
||||
describe('Initialization', () => {
|
||||
it('creates manager with default values from storage', () => {
|
||||
const manager = new TypographyControlManager(
|
||||
const manager = new TypographySettingsManager(
|
||||
DEFAULT_TYPOGRAPHY_CONTROLS_DATA,
|
||||
mockPersistentStore,
|
||||
);
|
||||
@@ -105,7 +105,7 @@ describe('TypographyControlManager - Unit Tests', () => {
|
||||
};
|
||||
mockPersistentStore = createMockPersistentStore(mockStorage);
|
||||
|
||||
const manager = new TypographyControlManager(
|
||||
const manager = new TypographySettingsManager(
|
||||
DEFAULT_TYPOGRAPHY_CONTROLS_DATA,
|
||||
mockPersistentStore,
|
||||
);
|
||||
@@ -117,7 +117,7 @@ describe('TypographyControlManager - Unit Tests', () => {
|
||||
});
|
||||
|
||||
it('initializes font size control with base size multiplied by current multiplier (1)', () => {
|
||||
const manager = new TypographyControlManager(
|
||||
const manager = new TypographySettingsManager(
|
||||
DEFAULT_TYPOGRAPHY_CONTROLS_DATA,
|
||||
mockPersistentStore,
|
||||
);
|
||||
@@ -126,7 +126,7 @@ describe('TypographyControlManager - Unit Tests', () => {
|
||||
});
|
||||
|
||||
it('returns all controls via controls getter', () => {
|
||||
const manager = new TypographyControlManager(
|
||||
const manager = new TypographySettingsManager(
|
||||
DEFAULT_TYPOGRAPHY_CONTROLS_DATA,
|
||||
mockPersistentStore,
|
||||
);
|
||||
@@ -142,7 +142,7 @@ describe('TypographyControlManager - Unit Tests', () => {
|
||||
});
|
||||
|
||||
it('returns individual controls via specific getters', () => {
|
||||
const manager = new TypographyControlManager(
|
||||
const manager = new TypographySettingsManager(
|
||||
DEFAULT_TYPOGRAPHY_CONTROLS_DATA,
|
||||
mockPersistentStore,
|
||||
);
|
||||
@@ -160,7 +160,7 @@ describe('TypographyControlManager - Unit Tests', () => {
|
||||
});
|
||||
|
||||
it('control instances have expected interface', () => {
|
||||
const manager = new TypographyControlManager(
|
||||
const manager = new TypographySettingsManager(
|
||||
DEFAULT_TYPOGRAPHY_CONTROLS_DATA,
|
||||
mockPersistentStore,
|
||||
);
|
||||
@@ -179,7 +179,7 @@ describe('TypographyControlManager - Unit Tests', () => {
|
||||
|
||||
describe('Multiplier System', () => {
|
||||
it('has default multiplier of 1', () => {
|
||||
const manager = new TypographyControlManager(
|
||||
const manager = new TypographySettingsManager(
|
||||
DEFAULT_TYPOGRAPHY_CONTROLS_DATA,
|
||||
mockPersistentStore,
|
||||
);
|
||||
@@ -188,7 +188,7 @@ describe('TypographyControlManager - Unit Tests', () => {
|
||||
});
|
||||
|
||||
it('updates multiplier when set', () => {
|
||||
const manager = new TypographyControlManager(
|
||||
const manager = new TypographySettingsManager(
|
||||
DEFAULT_TYPOGRAPHY_CONTROLS_DATA,
|
||||
mockPersistentStore,
|
||||
);
|
||||
@@ -201,7 +201,7 @@ describe('TypographyControlManager - Unit Tests', () => {
|
||||
});
|
||||
|
||||
it('does not update multiplier if set to same value', () => {
|
||||
const manager = new TypographyControlManager(
|
||||
const manager = new TypographySettingsManager(
|
||||
DEFAULT_TYPOGRAPHY_CONTROLS_DATA,
|
||||
mockPersistentStore,
|
||||
);
|
||||
@@ -217,7 +217,7 @@ describe('TypographyControlManager - Unit Tests', () => {
|
||||
mockStorage = { fontSize: 48, fontWeight: 400, lineHeight: 1.5, letterSpacing: 0 };
|
||||
mockPersistentStore = createMockPersistentStore(mockStorage);
|
||||
|
||||
const manager = new TypographyControlManager(
|
||||
const manager = new TypographySettingsManager(
|
||||
DEFAULT_TYPOGRAPHY_CONTROLS_DATA,
|
||||
mockPersistentStore,
|
||||
);
|
||||
@@ -241,7 +241,7 @@ describe('TypographyControlManager - Unit Tests', () => {
|
||||
});
|
||||
|
||||
it('updates font size control display value when multiplier increases', () => {
|
||||
const manager = new TypographyControlManager(
|
||||
const manager = new TypographySettingsManager(
|
||||
DEFAULT_TYPOGRAPHY_CONTROLS_DATA,
|
||||
mockPersistentStore,
|
||||
);
|
||||
@@ -262,7 +262,7 @@ describe('TypographyControlManager - Unit Tests', () => {
|
||||
|
||||
describe('Base Size Setter', () => {
|
||||
it('updates baseSize when set directly', () => {
|
||||
const manager = new TypographyControlManager(
|
||||
const manager = new TypographySettingsManager(
|
||||
DEFAULT_TYPOGRAPHY_CONTROLS_DATA,
|
||||
mockPersistentStore,
|
||||
);
|
||||
@@ -273,7 +273,7 @@ describe('TypographyControlManager - Unit Tests', () => {
|
||||
});
|
||||
|
||||
it('updates size control value when baseSize is set', () => {
|
||||
const manager = new TypographyControlManager(
|
||||
const manager = new TypographySettingsManager(
|
||||
DEFAULT_TYPOGRAPHY_CONTROLS_DATA,
|
||||
mockPersistentStore,
|
||||
);
|
||||
@@ -284,7 +284,7 @@ describe('TypographyControlManager - Unit Tests', () => {
|
||||
});
|
||||
|
||||
it('applies multiplier to size control when baseSize is set', () => {
|
||||
const manager = new TypographyControlManager(
|
||||
const manager = new TypographySettingsManager(
|
||||
DEFAULT_TYPOGRAPHY_CONTROLS_DATA,
|
||||
mockPersistentStore,
|
||||
);
|
||||
@@ -298,7 +298,7 @@ describe('TypographyControlManager - Unit Tests', () => {
|
||||
|
||||
describe('Rendered Size Calculation', () => {
|
||||
it('calculates renderedSize as baseSize * multiplier', () => {
|
||||
const manager = new TypographyControlManager(
|
||||
const manager = new TypographySettingsManager(
|
||||
DEFAULT_TYPOGRAPHY_CONTROLS_DATA,
|
||||
mockPersistentStore,
|
||||
);
|
||||
@@ -307,7 +307,7 @@ describe('TypographyControlManager - Unit Tests', () => {
|
||||
});
|
||||
|
||||
it('updates renderedSize when multiplier changes', () => {
|
||||
const manager = new TypographyControlManager(
|
||||
const manager = new TypographySettingsManager(
|
||||
DEFAULT_TYPOGRAPHY_CONTROLS_DATA,
|
||||
mockPersistentStore,
|
||||
);
|
||||
@@ -320,7 +320,7 @@ describe('TypographyControlManager - Unit Tests', () => {
|
||||
});
|
||||
|
||||
it('updates renderedSize when baseSize changes', () => {
|
||||
const manager = new TypographyControlManager(
|
||||
const manager = new TypographySettingsManager(
|
||||
DEFAULT_TYPOGRAPHY_CONTROLS_DATA,
|
||||
mockPersistentStore,
|
||||
);
|
||||
@@ -340,7 +340,7 @@ describe('TypographyControlManager - Unit Tests', () => {
|
||||
// proxy effect behavior should be tested in E2E tests.
|
||||
|
||||
it('does NOT immediately update baseSize from control change (effect is async)', () => {
|
||||
const manager = new TypographyControlManager(
|
||||
const manager = new TypographySettingsManager(
|
||||
DEFAULT_TYPOGRAPHY_CONTROLS_DATA,
|
||||
mockPersistentStore,
|
||||
);
|
||||
@@ -355,7 +355,7 @@ describe('TypographyControlManager - Unit Tests', () => {
|
||||
});
|
||||
|
||||
it('updates baseSize via direct setter (synchronous)', () => {
|
||||
const manager = new TypographyControlManager(
|
||||
const manager = new TypographySettingsManager(
|
||||
DEFAULT_TYPOGRAPHY_CONTROLS_DATA,
|
||||
mockPersistentStore,
|
||||
);
|
||||
@@ -380,7 +380,7 @@ describe('TypographyControlManager - Unit Tests', () => {
|
||||
};
|
||||
mockPersistentStore = createMockPersistentStore(mockStorage);
|
||||
|
||||
const manager = new TypographyControlManager(
|
||||
const manager = new TypographySettingsManager(
|
||||
DEFAULT_TYPOGRAPHY_CONTROLS_DATA,
|
||||
mockPersistentStore,
|
||||
);
|
||||
@@ -393,7 +393,7 @@ describe('TypographyControlManager - Unit Tests', () => {
|
||||
});
|
||||
|
||||
it('syncs to storage after effect flush (async)', async () => {
|
||||
const manager = new TypographyControlManager(
|
||||
const manager = new TypographySettingsManager(
|
||||
DEFAULT_TYPOGRAPHY_CONTROLS_DATA,
|
||||
mockPersistentStore,
|
||||
);
|
||||
@@ -409,7 +409,7 @@ describe('TypographyControlManager - Unit Tests', () => {
|
||||
});
|
||||
|
||||
it('syncs control changes to storage after effect flush (async)', async () => {
|
||||
const manager = new TypographyControlManager(
|
||||
const manager = new TypographySettingsManager(
|
||||
DEFAULT_TYPOGRAPHY_CONTROLS_DATA,
|
||||
mockPersistentStore,
|
||||
);
|
||||
@@ -422,7 +422,7 @@ describe('TypographyControlManager - Unit Tests', () => {
|
||||
});
|
||||
|
||||
it('syncs height control changes to storage after effect flush (async)', async () => {
|
||||
const manager = new TypographyControlManager(
|
||||
const manager = new TypographySettingsManager(
|
||||
DEFAULT_TYPOGRAPHY_CONTROLS_DATA,
|
||||
mockPersistentStore,
|
||||
);
|
||||
@@ -434,7 +434,7 @@ describe('TypographyControlManager - Unit Tests', () => {
|
||||
});
|
||||
|
||||
it('syncs spacing control changes to storage after effect flush (async)', async () => {
|
||||
const manager = new TypographyControlManager(
|
||||
const manager = new TypographySettingsManager(
|
||||
DEFAULT_TYPOGRAPHY_CONTROLS_DATA,
|
||||
mockPersistentStore,
|
||||
);
|
||||
@@ -448,7 +448,7 @@ describe('TypographyControlManager - Unit Tests', () => {
|
||||
|
||||
describe('Control Value Getters', () => {
|
||||
it('returns current weight value', () => {
|
||||
const manager = new TypographyControlManager(
|
||||
const manager = new TypographySettingsManager(
|
||||
DEFAULT_TYPOGRAPHY_CONTROLS_DATA,
|
||||
mockPersistentStore,
|
||||
);
|
||||
@@ -460,7 +460,7 @@ describe('TypographyControlManager - Unit Tests', () => {
|
||||
});
|
||||
|
||||
it('returns current height value', () => {
|
||||
const manager = new TypographyControlManager(
|
||||
const manager = new TypographySettingsManager(
|
||||
DEFAULT_TYPOGRAPHY_CONTROLS_DATA,
|
||||
mockPersistentStore,
|
||||
);
|
||||
@@ -472,7 +472,7 @@ describe('TypographyControlManager - Unit Tests', () => {
|
||||
});
|
||||
|
||||
it('returns current spacing value', () => {
|
||||
const manager = new TypographyControlManager(
|
||||
const manager = new TypographySettingsManager(
|
||||
DEFAULT_TYPOGRAPHY_CONTROLS_DATA,
|
||||
mockPersistentStore,
|
||||
);
|
||||
@@ -485,7 +485,7 @@ describe('TypographyControlManager - Unit Tests', () => {
|
||||
|
||||
it('returns default value when control is not found', () => {
|
||||
// Create a manager with empty configs (no controls)
|
||||
const manager = new TypographyControlManager([], mockPersistentStore);
|
||||
const manager = new TypographySettingsManager([], mockPersistentStore);
|
||||
|
||||
expect(manager.weight).toBe(DEFAULT_FONT_WEIGHT);
|
||||
expect(manager.height).toBe(DEFAULT_LINE_HEIGHT);
|
||||
@@ -503,7 +503,7 @@ describe('TypographyControlManager - Unit Tests', () => {
|
||||
};
|
||||
mockPersistentStore = createMockPersistentStore(mockStorage);
|
||||
|
||||
const manager = new TypographyControlManager(
|
||||
const manager = new TypographySettingsManager(
|
||||
DEFAULT_TYPOGRAPHY_CONTROLS_DATA,
|
||||
mockPersistentStore,
|
||||
);
|
||||
@@ -536,7 +536,7 @@ describe('TypographyControlManager - Unit Tests', () => {
|
||||
clear: clearSpy,
|
||||
};
|
||||
|
||||
const manager = new TypographyControlManager(
|
||||
const manager = new TypographySettingsManager(
|
||||
DEFAULT_TYPOGRAPHY_CONTROLS_DATA,
|
||||
mockPersistentStore,
|
||||
);
|
||||
@@ -547,7 +547,7 @@ describe('TypographyControlManager - Unit Tests', () => {
|
||||
});
|
||||
|
||||
it('respects multiplier when resetting font size control', () => {
|
||||
const manager = new TypographyControlManager(
|
||||
const manager = new TypographySettingsManager(
|
||||
DEFAULT_TYPOGRAPHY_CONTROLS_DATA,
|
||||
mockPersistentStore,
|
||||
);
|
||||
@@ -565,7 +565,7 @@ describe('TypographyControlManager - Unit Tests', () => {
|
||||
|
||||
describe('Complex Scenarios', () => {
|
||||
it('handles changing multiplier then modifying baseSize', () => {
|
||||
const manager = new TypographyControlManager(
|
||||
const manager = new TypographySettingsManager(
|
||||
DEFAULT_TYPOGRAPHY_CONTROLS_DATA,
|
||||
mockPersistentStore,
|
||||
);
|
||||
@@ -586,7 +586,7 @@ describe('TypographyControlManager - Unit Tests', () => {
|
||||
});
|
||||
|
||||
it('maintains correct renderedSize throughout changes', () => {
|
||||
const manager = new TypographyControlManager(
|
||||
const manager = new TypographySettingsManager(
|
||||
DEFAULT_TYPOGRAPHY_CONTROLS_DATA,
|
||||
mockPersistentStore,
|
||||
);
|
||||
@@ -608,7 +608,7 @@ describe('TypographyControlManager - Unit Tests', () => {
|
||||
});
|
||||
|
||||
it('handles multiple control changes in sequence', async () => {
|
||||
const manager = new TypographyControlManager(
|
||||
const manager = new TypographySettingsManager(
|
||||
DEFAULT_TYPOGRAPHY_CONTROLS_DATA,
|
||||
mockPersistentStore,
|
||||
);
|
||||
@@ -633,7 +633,7 @@ describe('TypographyControlManager - Unit Tests', () => {
|
||||
mockStorage = { fontSize: 48, fontWeight: 400, lineHeight: 1.5, letterSpacing: 0 };
|
||||
mockPersistentStore = createMockPersistentStore(mockStorage);
|
||||
|
||||
const manager = new TypographyControlManager(
|
||||
const manager = new TypographySettingsManager(
|
||||
DEFAULT_TYPOGRAPHY_CONTROLS_DATA,
|
||||
mockPersistentStore,
|
||||
);
|
||||
@@ -645,7 +645,7 @@ describe('TypographyControlManager - Unit Tests', () => {
|
||||
});
|
||||
|
||||
it('handles very small multiplier', () => {
|
||||
const manager = new TypographyControlManager(
|
||||
const manager = new TypographySettingsManager(
|
||||
DEFAULT_TYPOGRAPHY_CONTROLS_DATA,
|
||||
mockPersistentStore,
|
||||
);
|
||||
@@ -658,7 +658,7 @@ describe('TypographyControlManager - Unit Tests', () => {
|
||||
});
|
||||
|
||||
it('handles large base size with multiplier', () => {
|
||||
const manager = new TypographyControlManager(
|
||||
const manager = new TypographySettingsManager(
|
||||
DEFAULT_TYPOGRAPHY_CONTROLS_DATA,
|
||||
mockPersistentStore,
|
||||
);
|
||||
@@ -671,7 +671,7 @@ describe('TypographyControlManager - Unit Tests', () => {
|
||||
});
|
||||
|
||||
it('handles floating point precision in multiplier', () => {
|
||||
const manager = new TypographyControlManager(
|
||||
const manager = new TypographySettingsManager(
|
||||
DEFAULT_TYPOGRAPHY_CONTROLS_DATA,
|
||||
mockPersistentStore,
|
||||
);
|
||||
@@ -690,7 +690,7 @@ describe('TypographyControlManager - Unit Tests', () => {
|
||||
});
|
||||
|
||||
it('handles control methods (increase/decrease)', () => {
|
||||
const manager = new TypographyControlManager(
|
||||
const manager = new TypographySettingsManager(
|
||||
DEFAULT_TYPOGRAPHY_CONTROLS_DATA,
|
||||
mockPersistentStore,
|
||||
);
|
||||
@@ -704,7 +704,7 @@ describe('TypographyControlManager - Unit Tests', () => {
|
||||
});
|
||||
|
||||
it('handles control boundary conditions', () => {
|
||||
const manager = new TypographyControlManager(
|
||||
const manager = new TypographySettingsManager(
|
||||
DEFAULT_TYPOGRAPHY_CONTROLS_DATA,
|
||||
mockPersistentStore,
|
||||
);
|
||||
@@ -1,24 +1 @@
|
||||
export {
|
||||
DEFAULT_FONT_SIZE,
|
||||
DEFAULT_FONT_WEIGHT,
|
||||
DEFAULT_LETTER_SPACING,
|
||||
DEFAULT_LINE_HEIGHT,
|
||||
DEFAULT_TYPOGRAPHY_CONTROLS_DATA,
|
||||
FONT_SIZE_STEP,
|
||||
FONT_WEIGHT_STEP,
|
||||
LINE_HEIGHT_STEP,
|
||||
MAX_FONT_SIZE,
|
||||
MAX_FONT_WEIGHT,
|
||||
MAX_LINE_HEIGHT,
|
||||
MIN_FONT_SIZE,
|
||||
MIN_FONT_WEIGHT,
|
||||
MIN_LINE_HEIGHT,
|
||||
MULTIPLIER_L,
|
||||
MULTIPLIER_M,
|
||||
MULTIPLIER_S,
|
||||
} from './const/const';
|
||||
|
||||
export {
|
||||
type ControlId,
|
||||
controlManager,
|
||||
} from './state/manager.svelte';
|
||||
export { typographySettingsStore } from './state/typographySettingsStore';
|
||||
|
||||
@@ -1,6 +0,0 @@
|
||||
import { createTypographyControlManager } from '../../lib';
|
||||
import { DEFAULT_TYPOGRAPHY_CONTROLS_DATA } from '../const/const';
|
||||
|
||||
export type ControlId = 'font_size' | 'font_weight' | 'line_height' | 'letter_spacing';
|
||||
|
||||
export const controlManager = createTypographyControlManager(DEFAULT_TYPOGRAPHY_CONTROLS_DATA);
|
||||
@@ -0,0 +1,7 @@
|
||||
import { DEFAULT_TYPOGRAPHY_CONTROLS_DATA } from '$entities/Font';
|
||||
import { createTypographySettingsManager } from '../../lib';
|
||||
|
||||
export const typographySettingsStore = createTypographySettingsManager(
|
||||
DEFAULT_TYPOGRAPHY_CONTROLS_DATA,
|
||||
'glyphdiff:comparison:typography',
|
||||
);
|
||||
@@ -6,10 +6,14 @@
|
||||
Desktop: inline bar with combo controls.
|
||||
-->
|
||||
<script lang="ts">
|
||||
import {
|
||||
MULTIPLIER_L,
|
||||
MULTIPLIER_M,
|
||||
MULTIPLIER_S,
|
||||
} from '$entities/Font';
|
||||
import type { ResponsiveManager } from '$shared/lib';
|
||||
import { cn } from '$shared/shadcn/utils/shadcn-utils';
|
||||
import {
|
||||
Button,
|
||||
ComboControl,
|
||||
ControlGroup,
|
||||
Slider,
|
||||
@@ -20,12 +24,7 @@ import { Popover } from 'bits-ui';
|
||||
import { getContext } from 'svelte';
|
||||
import { cubicOut } from 'svelte/easing';
|
||||
import { fly } from 'svelte/transition';
|
||||
import {
|
||||
MULTIPLIER_L,
|
||||
MULTIPLIER_M,
|
||||
MULTIPLIER_S,
|
||||
controlManager,
|
||||
} from '../../model';
|
||||
import { typographySettingsStore } from '../../model';
|
||||
|
||||
interface Props {
|
||||
/**
|
||||
@@ -52,16 +51,16 @@ $effect(() => {
|
||||
if (!responsive) return;
|
||||
switch (true) {
|
||||
case responsive.isMobile:
|
||||
controlManager.multiplier = MULTIPLIER_S;
|
||||
typographySettingsStore.multiplier = MULTIPLIER_S;
|
||||
break;
|
||||
case responsive.isTablet:
|
||||
controlManager.multiplier = MULTIPLIER_M;
|
||||
typographySettingsStore.multiplier = MULTIPLIER_M;
|
||||
break;
|
||||
case responsive.isDesktop:
|
||||
controlManager.multiplier = MULTIPLIER_L;
|
||||
typographySettingsStore.multiplier = MULTIPLIER_L;
|
||||
break;
|
||||
default:
|
||||
controlManager.multiplier = MULTIPLIER_L;
|
||||
typographySettingsStore.multiplier = MULTIPLIER_L;
|
||||
}
|
||||
});
|
||||
</script>
|
||||
@@ -133,7 +132,7 @@ $effect(() => {
|
||||
</div>
|
||||
|
||||
<!-- Controls -->
|
||||
{#each controlManager.controls as control (control.id)}
|
||||
{#each typographySettingsStore.controls as control (control.id)}
|
||||
<ControlGroup label={control.controlLabel ?? ''}>
|
||||
<Slider
|
||||
bind:value={control.instance.value}
|
||||
@@ -174,7 +173,7 @@ $effect(() => {
|
||||
</div>
|
||||
|
||||
<!-- Controls with dividers between each -->
|
||||
{#each controlManager.controls as control, i (control.id)}
|
||||
{#each typographySettingsStore.controls as control, i (control.id)}
|
||||
{#if i > 0}
|
||||
<div class="w-px h-6 md:h-8 bg-black/5 dark:bg-white/10 mx-0.5 md:mx-1 shrink-0"></div>
|
||||
{/if}
|
||||
|
||||
@@ -21,10 +21,7 @@ import {
|
||||
fontStore,
|
||||
getFontUrl,
|
||||
} from '$entities/Font';
|
||||
import {
|
||||
DEFAULT_TYPOGRAPHY_CONTROLS_DATA,
|
||||
createTypographyControlManager,
|
||||
} from '$features/SetupFont';
|
||||
import { typographySettingsStore } from '$features/SetupFont/model';
|
||||
import { createPersistentStore } from '$shared/lib';
|
||||
import { untrack } from 'svelte';
|
||||
|
||||
@@ -68,8 +65,8 @@ export class ComparisonStore {
|
||||
#side = $state<Side>('A');
|
||||
/** Slider position for character morphing (0-100) */
|
||||
#sliderPosition = $state(50);
|
||||
/** Typography controls for this comparison */
|
||||
#typography = createTypographyControlManager(DEFAULT_TYPOGRAPHY_CONTROLS_DATA, 'glyphdiff:comparison:typography');
|
||||
// /** Typography controls for this comparison */
|
||||
// #typography = createTypographyControlManager(DEFAULT_TYPOGRAPHY_CONTROLS_DATA, 'glyphdiff:comparison:typography');
|
||||
/** TanStack Query-backed batch font fetcher */
|
||||
#batchStore: BatchFontStore;
|
||||
|
||||
@@ -99,7 +96,7 @@ export class ComparisonStore {
|
||||
$effect(() => {
|
||||
const fa = this.#fontA;
|
||||
const fb = this.#fontB;
|
||||
const weight = this.#typography.weight;
|
||||
const weight = typographySettingsStore.weight;
|
||||
|
||||
if (!fa || !fb) return;
|
||||
|
||||
@@ -137,6 +134,19 @@ export class ComparisonStore {
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
// Effect 4: Pin fontA/fontB so eviction never removes on-screen fonts
|
||||
$effect(() => {
|
||||
const fa = this.#fontA;
|
||||
const fb = this.#fontB;
|
||||
const w = typographySettingsStore.weight;
|
||||
if (fa) appliedFontsManager.pin(fa.id, w, fa.features?.isVariable);
|
||||
if (fb) appliedFontsManager.pin(fb.id, w, fb.features?.isVariable);
|
||||
return () => {
|
||||
if (fa) appliedFontsManager.unpin(fa.id, w, fa.features?.isVariable);
|
||||
if (fb) appliedFontsManager.unpin(fb.id, w, fb.features?.isVariable);
|
||||
};
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
@@ -152,8 +162,8 @@ export class ComparisonStore {
|
||||
return;
|
||||
}
|
||||
|
||||
const weight = this.#typography.weight;
|
||||
const size = this.#typography.renderedSize;
|
||||
const weight = typographySettingsStore.weight;
|
||||
const size = typographySettingsStore.renderedSize;
|
||||
const fontAName = this.#fontA?.name;
|
||||
const fontBName = this.#fontB?.name;
|
||||
|
||||
@@ -201,12 +211,12 @@ export class ComparisonStore {
|
||||
};
|
||||
}
|
||||
|
||||
// ── Getters / Setters ─────────────────────────────────────────────────────
|
||||
// // ── Getters / Setters ─────────────────────────────────────────────────────
|
||||
|
||||
/** Typography control manager */
|
||||
get typography() {
|
||||
return this.#typography;
|
||||
}
|
||||
// /** Typography control manager */
|
||||
// get typography() {
|
||||
// return typographySettingsStore;
|
||||
// }
|
||||
|
||||
/** Font for side A */
|
||||
get fontA() {
|
||||
@@ -273,7 +283,7 @@ export class ComparisonStore {
|
||||
this.#fontB = undefined;
|
||||
this.#batchStore.setIds([]);
|
||||
storage.clear();
|
||||
this.#typography.reset();
|
||||
typographySettingsStore.reset();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -53,15 +53,19 @@ vi.mock('$shared/lib/helpers/createPersistentStore/createPersistentStore.svelte'
|
||||
|
||||
// ── $entities/Font mock — keep real BatchFontStore, stub singletons ───────────
|
||||
|
||||
vi.mock('$entities/Font', async () => {
|
||||
vi.mock('$entities/Font', async importOriginal => {
|
||||
const actual = await importOriginal<typeof import('$entities/Font')>();
|
||||
const { BatchFontStore } = await import(
|
||||
'$entities/Font/model/store/batchFontStore.svelte'
|
||||
);
|
||||
return {
|
||||
...actual,
|
||||
BatchFontStore,
|
||||
fontStore: { fonts: [] },
|
||||
appliedFontsManager: {
|
||||
touch: vi.fn(),
|
||||
pin: vi.fn(),
|
||||
unpin: vi.fn(),
|
||||
getFontStatus: vi.fn(),
|
||||
ready: vi.fn(() => Promise.resolve()),
|
||||
},
|
||||
@@ -80,9 +84,20 @@ vi.mock('$features/SetupFont', () => ({
|
||||
})),
|
||||
}));
|
||||
|
||||
vi.mock('$features/SetupFont/model', () => ({
|
||||
typographySettingsStore: {
|
||||
weight: 400,
|
||||
renderedSize: 48,
|
||||
reset: vi.fn(),
|
||||
},
|
||||
}));
|
||||
|
||||
// ── Imports (after mocks) ─────────────────────────────────────────────────────
|
||||
|
||||
import { fontStore } from '$entities/Font';
|
||||
import {
|
||||
appliedFontsManager,
|
||||
fontStore,
|
||||
} from '$entities/Font';
|
||||
import * as proxyFonts from '$entities/Font/api/proxy/proxyFonts';
|
||||
import { ComparisonStore } from './comparisonStore.svelte';
|
||||
|
||||
@@ -209,4 +224,55 @@ describe('ComparisonStore', () => {
|
||||
expect(store.fontB).toBeUndefined();
|
||||
});
|
||||
});
|
||||
|
||||
// ── Pin / Unpin ───────────────────────────────────────────────────────────
|
||||
|
||||
describe('Pin / Unpin (eviction guard)', () => {
|
||||
it('pins fontA and fontB when they are loaded', async () => {
|
||||
mockStorage._value.fontAId = mockFontA.id;
|
||||
mockStorage._value.fontBId = mockFontB.id;
|
||||
vi.spyOn(proxyFonts, 'fetchFontsByIds').mockResolvedValue([mockFontA, mockFontB]);
|
||||
|
||||
new ComparisonStore();
|
||||
|
||||
await vi.waitFor(() => {
|
||||
expect(appliedFontsManager.pin).toHaveBeenCalledWith(
|
||||
mockFontA.id,
|
||||
400,
|
||||
mockFontA.features?.isVariable,
|
||||
);
|
||||
expect(appliedFontsManager.pin).toHaveBeenCalledWith(
|
||||
mockFontB.id,
|
||||
400,
|
||||
mockFontB.features?.isVariable,
|
||||
);
|
||||
}, { timeout: 2000 });
|
||||
});
|
||||
|
||||
it('unpins the old font when fontA is replaced', async () => {
|
||||
mockStorage._value.fontAId = mockFontA.id;
|
||||
mockStorage._value.fontBId = mockFontB.id;
|
||||
vi.spyOn(proxyFonts, 'fetchFontsByIds').mockResolvedValue([mockFontA, mockFontB]);
|
||||
|
||||
const store = new ComparisonStore();
|
||||
await vi.waitFor(() => expect(store.fontA?.id).toBe(mockFontA.id), { timeout: 2000 });
|
||||
|
||||
const mockFontC: typeof mockFontA = { ...mockFontA, id: 'playfair', name: 'Playfair Display' };
|
||||
vi.spyOn(proxyFonts, 'fetchFontsByIds').mockResolvedValue([mockFontC, mockFontB]);
|
||||
store.fontA = mockFontC;
|
||||
|
||||
await vi.waitFor(() => {
|
||||
expect(appliedFontsManager.unpin).toHaveBeenCalledWith(
|
||||
mockFontA.id,
|
||||
400,
|
||||
mockFontA.features?.isVariable,
|
||||
);
|
||||
expect(appliedFontsManager.pin).toHaveBeenCalledWith(
|
||||
mockFontC.id,
|
||||
400,
|
||||
mockFontC.features?.isVariable,
|
||||
);
|
||||
}, { timeout: 2000 });
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
Renders a single character with morphing animation
|
||||
-->
|
||||
<script lang="ts">
|
||||
import { typographySettingsStore } from '$features/SetupFont';
|
||||
import { cn } from '$shared/shadcn/utils/shadcn-utils';
|
||||
import { comparisonStore } from '../../model';
|
||||
|
||||
@@ -25,7 +26,7 @@ let { char, proximity, isPast }: Props = $props();
|
||||
|
||||
const fontA = $derived(comparisonStore.fontA);
|
||||
const fontB = $derived(comparisonStore.fontB);
|
||||
const typography = $derived(comparisonStore.typography);
|
||||
const typography = $derived(typographySettingsStore);
|
||||
|
||||
let slot = $state<0 | 1>(0);
|
||||
let slotFonts = $state<[string, string]>(['', '']);
|
||||
|
||||
@@ -15,14 +15,13 @@ import {
|
||||
getContext,
|
||||
untrack,
|
||||
} from 'svelte';
|
||||
import { comparisonStore } from '../../model';
|
||||
import FontList from '../FontList/FontList.svelte';
|
||||
import Header from '../Header/Header.svelte';
|
||||
import Sidebar from '../Sidebar/Sidebar.svelte';
|
||||
import SliderArea from '../SliderArea/SliderArea.svelte';
|
||||
|
||||
const responsive = getContext<ResponsiveManager>('responsive');
|
||||
const typography = $derived(comparisonStore.typography);
|
||||
// const typography = $derived(comparisonStore.typography);
|
||||
const isMobileOrTabletPortrait = $derived(responsive.isMobile || responsive.isTabletPortrait);
|
||||
let isSidebarOpen = $state(!isMobileOrTabletPortrait);
|
||||
|
||||
@@ -45,7 +44,7 @@ $effect(() => {
|
||||
{#snippet main()}
|
||||
<FontList />
|
||||
{/snippet}
|
||||
|
||||
<!--
|
||||
{#snippet controls()}
|
||||
{#if typography.sizeControl && typography.weightControl && typography.heightControl && typography.spacingControl}
|
||||
<ControlGroup label="Size">
|
||||
@@ -89,6 +88,7 @@ $effect(() => {
|
||||
</div>
|
||||
{/if}
|
||||
{/snippet}
|
||||
-->
|
||||
</Sidebar>
|
||||
{/snippet}
|
||||
</SidebarContainer>
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
-->
|
||||
<script lang="ts">
|
||||
import {
|
||||
DEFAULT_FONT_WEIGHT,
|
||||
FontApplicator,
|
||||
FontVirtualList,
|
||||
type UnifiedFont,
|
||||
@@ -18,8 +19,6 @@ import { crossfade } from 'svelte/transition';
|
||||
import { comparisonStore } from '../../model';
|
||||
|
||||
const side = $derived(comparisonStore.side);
|
||||
const typography = $derived(comparisonStore.typography);
|
||||
|
||||
let prevIndexA: number | null = null;
|
||||
let prevIndexB: number | null = null;
|
||||
let selectedIndexA: number | null = null;
|
||||
@@ -79,7 +78,7 @@ $effect(() => {
|
||||
</div>
|
||||
<FontVirtualList
|
||||
data-font-list
|
||||
weight={typography.weight}
|
||||
weight={DEFAULT_FONT_WEIGHT}
|
||||
itemHeight={45}
|
||||
class="bg-transparent min-h-0 h-full scroll-stable pr-4"
|
||||
>
|
||||
@@ -95,7 +94,7 @@ $effect(() => {
|
||||
class="w-full px-3 md:px-4 py-2.5 md:py-3 justify-between text-left text-sm flex"
|
||||
iconPosition="right"
|
||||
>
|
||||
<FontApplicator {font} weight={typography.weight}>{font.name}</FontApplicator>
|
||||
<FontApplicator {font}>{font.name}</FontApplicator>
|
||||
|
||||
{#snippet icon()}
|
||||
{#if active}
|
||||
|
||||
@@ -3,8 +3,8 @@
|
||||
Renders a line of text in the SliderArea
|
||||
-->
|
||||
<script lang="ts">
|
||||
import { typographySettingsStore } from '$features/SetupFont';
|
||||
import type { Snippet } from 'svelte';
|
||||
import { comparisonStore } from '../../model';
|
||||
|
||||
interface LineChar {
|
||||
char: string;
|
||||
@@ -26,7 +26,7 @@ interface Props {
|
||||
*/
|
||||
character: Snippet<[{ char: string; index: number }]>;
|
||||
}
|
||||
const typography = $derived(comparisonStore.typography);
|
||||
const typography = $derived(typographySettingsStore);
|
||||
|
||||
let { chars, character }: Props = $props();
|
||||
</script>
|
||||
|
||||
@@ -8,6 +8,8 @@
|
||||
- Performance optimized using offscreen canvas for measurements and transform-based animations.
|
||||
-->
|
||||
<script lang="ts">
|
||||
import { TypographyMenu } from '$features/SetupFont';
|
||||
import { typographySettingsStore } from '$features/SetupFont/model';
|
||||
import {
|
||||
type ResponsiveManager,
|
||||
debounce,
|
||||
@@ -42,7 +44,7 @@ let { isSidebarOpen = false, class: className }: Props = $props();
|
||||
const fontA = $derived(comparisonStore.fontA);
|
||||
const fontB = $derived(comparisonStore.fontB);
|
||||
const isLoading = $derived(comparisonStore.isLoading || !comparisonStore.isReady);
|
||||
const typography = $derived(comparisonStore.typography);
|
||||
const typography = $derived(typographySettingsStore);
|
||||
|
||||
let container = $state<HTMLElement>();
|
||||
|
||||
@@ -179,12 +181,7 @@ const scaleClass = $derived(
|
||||
The paper div inside scales down when the sidebar opens on desktop.
|
||||
-->
|
||||
<div class={cn('flex-1 relative flex items-center justify-center p-0 overflow-hidden bg-surface dark:bg-dark-bg', className)}>
|
||||
<!--
|
||||
Paper surface.
|
||||
Replaces the old glassmorphism card with a clean white/dark sheet.
|
||||
Scale transition replaces motion.div spring — CSS transition-transform
|
||||
is smooth enough here; a JS spring would add ~4kb for minimal gain.
|
||||
-->
|
||||
<!-- Paper surface -->
|
||||
<div
|
||||
class={cn(
|
||||
'w-full h-full flex flex-col items-center justify-center relative',
|
||||
@@ -248,4 +245,10 @@ const scaleClass = $derived(
|
||||
{/if}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<TypographyMenu
|
||||
class={cn(
|
||||
'absolute bottom-4 sm:bottom-5 right-4 sm:left-1/2 sm:right-[unset] sm:-translate-x-1/2 z-50',
|
||||
)}
|
||||
/>
|
||||
</div>
|
||||
|
||||
@@ -14,7 +14,7 @@ import {
|
||||
import { FontSampler } from '$features/DisplayFont';
|
||||
import {
|
||||
TypographyMenu,
|
||||
controlManager,
|
||||
typographySettingsStore,
|
||||
} from '$features/SetupFont';
|
||||
import { throttle } from '$shared/lib/utils';
|
||||
import { Skeleton } from '$shared/ui';
|
||||
@@ -60,11 +60,11 @@ const checkPosition = throttle(() => {
|
||||
const fontRowHeight = $derived.by(() =>
|
||||
createFontRowSizeResolver({
|
||||
getFonts: () => fontStore.fonts,
|
||||
getWeight: () => controlManager.weight,
|
||||
getWeight: () => typographySettingsStore.weight,
|
||||
getPreviewText: () => text,
|
||||
getContainerWidth: () => containerWidth,
|
||||
getFontSizePx: () => controlManager.renderedSize,
|
||||
getLineHeightPx: () => controlManager.height * controlManager.renderedSize,
|
||||
getFontSizePx: () => typographySettingsStore.renderedSize,
|
||||
getLineHeightPx: () => typographySettingsStore.height * typographySettingsStore.renderedSize,
|
||||
getStatus: key => appliedFontsManager.statuses.get(key),
|
||||
contentHorizontalPadding: SAMPLER_CONTENT_PADDING_X,
|
||||
chromeHeight: SAMPLER_CHROME_HEIGHT,
|
||||
@@ -97,7 +97,7 @@ const fontRowHeight = $derived.by(() =>
|
||||
<FontVirtualList
|
||||
itemHeight={fontRowHeight}
|
||||
useWindowScroll={true}
|
||||
weight={controlManager.weight}
|
||||
weight={typographySettingsStore.weight}
|
||||
columns={layoutManager.columns}
|
||||
gap={layoutManager.gap}
|
||||
{skeleton}
|
||||
|
||||
Reference in New Issue
Block a user