mirror of
https://github.com/olehomelchenko/astrolabe.git
synced 2026-08-08 02:02:33 +00:00
Chart fonts: self-hosted roster + document.fonts.load render gate
This commit is contained in:
@@ -84,18 +84,62 @@ export interface ThemeFontOption {
|
||||
}
|
||||
|
||||
/**
|
||||
* Fonts the builder can apply today: the two self-hosted Plex faces the app
|
||||
* already loads, plus web-safe/system stacks that need no loading at all. Every
|
||||
* entry is render-safe without a `document.fonts.load` gate — Plex is loaded by
|
||||
* the UI before any chart renders, the rest resolve to locally installed faces.
|
||||
* The self-hosted roster (scope doc §4.5) extends this list and brings the
|
||||
* pre-render loading gate with it.
|
||||
* Fonts the builder can apply: the self-hosted roster (scope doc §3 — chart
|
||||
* fonts, registered in styles/chart-fonts.css) followed by web-safe/system
|
||||
* stacks that need no loading. The roster faces require their woff2 to be loaded
|
||||
* before a chart measures text, which the render path's font gate handles
|
||||
* (`collectFontFamilies` + `document.fonts.load`); the system stacks resolve to
|
||||
* locally installed faces. Order is by role so the in-face dropdown reads as a
|
||||
* specimen. Every primary family pairs with a category-appropriate fallback.
|
||||
*/
|
||||
export const THEME_FONT_OPTIONS: ReadonlyArray<ThemeFontOption> = [
|
||||
// Sans
|
||||
{ value: '"IBM Plex Sans", system-ui, -apple-system, sans-serif', label: 'IBM Plex Sans' },
|
||||
{ value: '"Inter", system-ui, sans-serif', label: 'Inter' },
|
||||
{ value: '"Libre Franklin", system-ui, sans-serif', label: 'Libre Franklin' },
|
||||
{ value: '"Roboto Condensed", system-ui, sans-serif', label: 'Roboto Condensed' },
|
||||
{ value: '"IBM Plex Sans Condensed", system-ui, sans-serif', label: 'IBM Plex Sans Condensed' },
|
||||
// Serif
|
||||
{ value: '"IBM Plex Serif", Georgia, serif', label: 'IBM Plex Serif' },
|
||||
{ value: '"Source Serif 4", Georgia, serif', label: 'Source Serif 4' },
|
||||
{ value: '"Spectral", Georgia, serif', label: 'Spectral' },
|
||||
// Mono
|
||||
{ value: '"IBM Plex Mono", ui-monospace, monospace', label: 'IBM Plex Mono' },
|
||||
{ value: '"Space Mono", ui-monospace, monospace', label: 'Space Mono' },
|
||||
// Display
|
||||
{ value: '"Space Grotesk", system-ui, sans-serif', label: 'Space Grotesk' },
|
||||
{ value: '"Playfair Display", Georgia, serif', label: 'Playfair Display' },
|
||||
// Handwriting
|
||||
{ value: '"Caveat", cursive', label: 'Caveat' },
|
||||
// System / web-safe (no load)
|
||||
{ value: 'system-ui, -apple-system, sans-serif', label: 'System UI' },
|
||||
{ value: 'Helvetica, Arial, sans-serif', label: 'Helvetica / Arial' },
|
||||
{ value: 'Georgia, "Times New Roman", serif', label: 'Georgia' },
|
||||
{ value: '"Courier New", Courier, monospace', label: 'Courier' },
|
||||
];
|
||||
|
||||
/**
|
||||
* Collect every font family stack referenced under a `font` or `*Font` key
|
||||
* anywhere in `value` (a Vega-Lite spec or config) — the read-counterpart of
|
||||
* `applyFontToConfig`'s write. The render path uses it to know which faces to
|
||||
* `document.fonts.load` before measuring text. `data`/`datasets` are skipped:
|
||||
* they hold dataset rows (potentially huge, never fonts), so walking them is
|
||||
* wasted work. Returns the distinct stacks, in no particular order.
|
||||
*/
|
||||
export function collectFontFamilies(value: unknown): Set<string> {
|
||||
const out = new Set<string>();
|
||||
const walk = (node: unknown): void => {
|
||||
if (Array.isArray(node)) {
|
||||
for (const item of node) walk(item);
|
||||
return;
|
||||
}
|
||||
if (!isJsonObject(node)) return;
|
||||
for (const [key, v] of Object.entries(node)) {
|
||||
if (key === 'data' || key === 'datasets') continue;
|
||||
if ((key === 'font' || key.endsWith('Font')) && typeof v === 'string') out.add(v);
|
||||
else walk(v);
|
||||
}
|
||||
};
|
||||
walk(value);
|
||||
return out;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user