feat(createCharacterComparison): add generic for font type and checks for the absence of the fonts

This commit is contained in:
Ilia Mashkov
2026-01-26 12:34:27 +03:00
parent 9b8ebed1c3
commit 2b820230bc

View File

@@ -15,16 +15,22 @@ export interface LineData {
* @param fontB - The second font definition
* @returns Object with reactive state (lines, containerWidth) and methods (breakIntoLines, getCharState)
*/
export function createCharacterComparison(
export function createCharacterComparison<
T extends { name: string; id: string } | undefined = undefined,
>(
text: () => string,
fontA: () => { name: string; id: string },
fontB: () => { name: string; id: string },
fontA: () => T,
fontB: () => T,
weight: () => number,
size: () => number,
) {
let lines = $state<LineData[]>([]);
let containerWidth = $state(0);
function fontDefined<T extends { name: string; id: string }>(font: T | undefined): font is T {
return font !== undefined;
}
/**
* Measures text width using a canvas context.
* @param ctx - Canvas rendering context
@@ -36,10 +42,11 @@ export function createCharacterComparison(
function measureText(
ctx: CanvasRenderingContext2D,
text: string,
fontFamily: string,
fontSize: number,
fontWeight: number,
fontFamily?: string,
): number {
if (!fontFamily) return 0;
ctx.font = `${fontWeight} ${fontSize}px ${fontFamily}`;
return ctx.measureText(text).width;
}
@@ -73,7 +80,7 @@ export function createCharacterComparison(
container: HTMLElement | undefined,
measureCanvas: HTMLCanvasElement | undefined,
) {
if (!container || !measureCanvas) return;
if (!container || !measureCanvas || !fontA() || !fontB()) return;
const rect = container.getBoundingClientRect();
containerWidth = rect.width;
@@ -92,22 +99,24 @@ export function createCharacterComparison(
let currentLineWords: string[] = [];
function pushLine(words: string[]) {
if (words.length === 0) return;
if (words.length === 0 || !fontDefined(fontA()) || !fontDefined(fontB())) {
return;
}
const lineText = words.join(' ');
// Measure both fonts at the CURRENT weight
const widthA = measureText(
ctx!,
lineText,
fontA().name,
Math.min(fontSize, controlledFontSize),
currentWeight,
fontA()?.name,
);
const widthB = measureText(
ctx!,
lineText,
fontB().name,
Math.min(fontSize, controlledFontSize),
currentWeight,
fontB()?.name,
);
const maxWidth = Math.max(widthA, widthB);
newLines.push({ text: lineText, width: maxWidth });
@@ -121,16 +130,16 @@ export function createCharacterComparison(
const widthA = measureText(
ctx,
testLine,
fontA().name,
Math.min(fontSize, controlledFontSize),
currentWeight,
fontA()?.name,
);
const widthB = measureText(
ctx,
testLine,
fontB().name,
Math.min(fontSize, controlledFontSize),
currentWeight,
fontB()?.name,
);
const maxWidth = Math.max(widthA, widthB);
const isContainerOverflown = maxWidth > availableWidth;
@@ -155,16 +164,16 @@ export function createCharacterComparison(
const wA = measureText(
ctx,
testFragment,
fontA().name,
fontSize,
currentWeight,
fontA()?.name,
);
const wB = measureText(
ctx,
testFragment,
fontB().name,
fontSize,
currentWeight,
fontB()?.name,
);
if (Math.max(wA, wB) <= availableWidth) {