feat: wire createFontRowSizeResolver into SampleList for pretext-backed row heights
Some checks failed
Workflow / build (pull_request) Failing after 49s
Workflow / publish (pull_request) Has been skipped

This commit is contained in:
Ilia Mashkov
2026-04-13 13:23:03 +03:00
parent 20accb9c93
commit a74abbb0b3

View File

@@ -5,7 +5,12 @@
- Provides a typography menu for font setup. - Provides a typography menu for font setup.
--> -->
<script lang="ts"> <script lang="ts">
import { FontVirtualList } from '$entities/Font'; import {
FontVirtualList,
appliedFontsManager,
createFontRowSizeResolver,
fontStore,
} from '$entities/Font';
import { FontSampler } from '$features/DisplayFont'; import { FontSampler } from '$features/DisplayFont';
import { import {
TypographyMenu, TypographyMenu,
@@ -15,12 +20,30 @@ import { throttle } from '$shared/lib/utils';
import { Skeleton } from '$shared/ui'; import { Skeleton } from '$shared/ui';
import { layoutManager } from '../../model'; import { layoutManager } from '../../model';
// 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
// on desktop, where container widths are wide and estimates matter most, it is invisible.
// Over-estimating chrome is safe (row is slightly taller than text needs, never cut off).
const SAMPLER_CHROME_HEIGHT = 56;
// p-4 = 16px per side = 32px total horizontal padding in FontSampler's content area.
// Using the smallest breakpoint (mobile) ensures contentWidth is never over-estimated:
// wider actual padding → more text wrapping → pretext height ≥ rendered height → safe.
const SAMPLER_CONTENT_PADDING_X = 32;
// Fallback row height used when the font has not loaded yet.
// Matches the previous hardcoded itemHeight={220} value to avoid regressions.
const SAMPLER_FALLBACK_HEIGHT = 220;
let text = $state('The quick brown fox jumps over the lazy dog...'); let text = $state('The quick brown fox jumps over the lazy dog...');
let wrapper = $state<HTMLDivElement | null>(null); let wrapper = $state<HTMLDivElement | null>(null);
// Binds to the actual window height // Binds to the actual window height
let innerHeight = $state(0); let innerHeight = $state(0);
// Is the component above the middle of the viewport? // Is the component above the middle of the viewport?
let isAboveMiddle = $state(false); let isAboveMiddle = $state(false);
// Inner width of the wrapper div — updated by bind:clientWidth on mount and resize.
let containerWidth = $state(0);
const checkPosition = throttle(() => { const checkPosition = throttle(() => {
if (!wrapper) return; if (!wrapper) return;
@@ -30,6 +53,24 @@ const checkPosition = throttle(() => {
isAboveMiddle = rect.top < viewportMiddle; isAboveMiddle = rect.top < viewportMiddle;
}, 100); }, 100);
// Resolver recreated when typography values change. The returned closure reads
// appliedFontsManager.statuses (a SvelteMap) on every call, so any font status
// change triggers a full offsets recompute in createVirtualizer — no DOM snap.
const fontRowHeight = $derived.by(() =>
createFontRowSizeResolver({
getFonts: () => fontStore.fonts,
getWeight: () => controlManager.weight,
getPreviewText: () => text,
getContainerWidth: () => containerWidth,
getFontSizePx: () => controlManager.renderedSize,
getLineHeightPx: () => controlManager.height * controlManager.renderedSize,
getStatus: key => appliedFontsManager.statuses.get(key),
contentHorizontalPadding: SAMPLER_CONTENT_PADDING_X,
chromeHeight: SAMPLER_CHROME_HEIGHT,
fallbackHeight: SAMPLER_FALLBACK_HEIGHT,
})
);
</script> </script>
{#snippet skeleton()} {#snippet skeleton()}
@@ -52,9 +93,9 @@ const checkPosition = throttle(() => {
onresize={checkPosition} onresize={checkPosition}
/> />
<div bind:this={wrapper}> <div bind:this={wrapper} bind:clientWidth={containerWidth}>
<FontVirtualList <FontVirtualList
itemHeight={220} itemHeight={fontRowHeight}
useWindowScroll={true} useWindowScroll={true}
weight={controlManager.weight} weight={controlManager.weight}
columns={layoutManager.columns} columns={layoutManager.columns}