Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
36 commits
Select commit Hold shift + click to select a range
4149974
Extract chartWebFont helper for chart font loading
roryabraham Jun 3, 2026
b82f739
Add ChartDefaultTypefaceContext and platform providers
roryabraham Jun 3, 2026
a3b4687
Wire Victory charts through ChartDefaultTypefaceProvider
roryabraham Jun 3, 2026
5af76f4
Fix lint in chart typeface context files
roryabraham Jun 3, 2026
a13a7a5
Remove unused chart typeface exports for knip
roryabraham Jun 3, 2026
9096176
Remove unnecessary displayName
roryabraham Jun 3, 2026
4318681
Wrap Skia chart overlays with ChartDefaultTypefaceProvider
roryabraham Jun 3, 2026
1e5309f
Load Expensify New Kansas for Victory chart labels
roryabraham Jun 3, 2026
b78dfa1
Load all app Skia typefaces in ChartDefaultTypefaceProvider
roryabraham Jun 3, 2026
84b809b
Fix VictoryChartPieLabel Skia context usage
roryabraham Jun 3, 2026
bf9370a
Remove unused chart typeface type exports for knip
roryabraham Jun 3, 2026
363c4bd
Reorganize Charts module into utils, types, and hooks folders.
roryabraham Jun 3, 2026
db3b35b
Fix Charts import aliases and default export for lint
roryabraham Jun 3, 2026
0ec3e15
chore(charts): add chart font registry and loader cache
roryabraham Jun 3, 2026
43d3f01
feat(charts): add ChartFontsProvider and context hooks
roryabraham Jun 3, 2026
a99499f
refactor(charts): migrate Victory HTML to ChartFontsProvider
roryabraham Jun 3, 2026
7e162d8
feat(charts): unify Bar/Line fonts via ChartFontsProvider
roryabraham Jun 3, 2026
e38912c
refactor(charts): remove legacy chart font loading
roryabraham Jun 3, 2026
bdeaf13
chore: remove victory-native cartesian skia context bridge doc
roryabraham Jun 3, 2026
7ce823b
refactor(charts): improve font load errors and drop useMemo
roryabraham Jun 3, 2026
ab7ab31
chore(charts): remove redundant component displayNames
roryabraham Jun 3, 2026
f8a0200
fix(charts): keep VictoryChartContext out of VictoryChartPie
roryabraham Jun 3, 2026
e919a42
chore(charts): drop redundant ChartFontsProvider in cartesian overlays
roryabraham Jun 3, 2026
164b5fd
fix(charts): restore ChartFontsProvider in cartesian renderOutside
roryabraham Jun 3, 2026
e18dd95
fix(charts): resolve bold string weight in getChartSkiaTypeface
roryabraham Jun 3, 2026
0949e9a
refactor(charts): dedupe font loads via shared constants
roryabraham Jun 3, 2026
7a0a7f9
refactor(charts): centralize chart font weight normalization
roryabraham Jun 3, 2026
fad7fc0
refactor(charts): add ChartFontsBoundary for Skia overlays
roryabraham Jun 3, 2026
a79a81c
refactor(charts): hoist pie label parse and trim font types
roryabraham Jun 3, 2026
4b51eee
chore(charts): fix ESLint violations in chart font modules
roryabraham Jun 3, 2026
739fbca
refactor(charts): inline ChartFontsProvider in Skia overlays
roryabraham Jun 3, 2026
73c102a
chore(charts): drop useMemo from VictoryChartPie
roryabraham Jun 3, 2026
39940a1
Fix prettier
roryabraham Jun 3, 2026
0be9474
chore(charts): remove unused ChartFontFamilyName export
roryabraham Jun 3, 2026
3840c53
fix(charts): use mutable fontFamilies array for Skia API
roryabraham Jun 3, 2026
91c7bde
Merge remote-tracking branch 'origin/main' into rory/chart-default-ty…
roryabraham Jun 3, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 10 additions & 1 deletion src/components/Charts/BarChart/BarChartContent.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import ChartXAxisLabels from '@components/Charts/components/ChartXAxisLabels';
import ChartYAxisLabels from '@components/Charts/components/ChartYAxisLabels';
import type {HitTestArgs} from '@components/Charts/hooks';
import {
ChartFontsProvider,
useChartFontManager,
useChartInteractions,
useChartLabelFormats,
Expand Down Expand Up @@ -44,7 +45,7 @@ type BarChartProps = CartesianChartProps & {
useSingleColor?: boolean;
};

function BarChartContent({data, isLoading, yAxisUnit, yAxisUnitPosition = 'left', useSingleColor = false, onBarPress}: BarChartProps) {
function BarChartContentBody({data, isLoading, yAxisUnit, yAxisUnitPosition = 'left', useSingleColor = false, onBarPress}: BarChartProps) {
const theme = useTheme();
const styles = useThemeStyles();
const fontMgr = useChartFontManager();
Expand Down Expand Up @@ -301,6 +302,14 @@ function BarChartContent({data, isLoading, yAxisUnit, yAxisUnitPosition = 'left'
);
}

function BarChartContent(props: BarChartProps) {
return (
<ChartFontsProvider>
<BarChartContentBody {...props} />
</ChartFontsProvider>
);
}

export default BarChartContent;
export type {BarChartProps};
export {BAR_INNER_PADDING};
11 changes: 10 additions & 1 deletion src/components/Charts/LineChart/LineChartContent.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import LeftFrameLine from '@components/Charts/components/LeftFrameLine';
import ScatterPoints from '@components/Charts/components/ScatterPoints';
import type {HitTestArgs} from '@components/Charts/hooks';
import {
ChartFontsProvider,
useChartFontManager,
useChartInteractions,
useChartLabelFormats,
Expand Down Expand Up @@ -47,7 +48,7 @@ type LineChartProps = CartesianChartProps & {
onPointPress?: (dataPoint: ChartDataPoint, index: number) => void;
};

function LineChartContent({data, isLoading, yAxisUnit, yAxisUnitPosition = 'left', onPointPress}: LineChartProps) {
function LineChartContentBody({data, isLoading, yAxisUnit, yAxisUnitPosition = 'left', onPointPress}: LineChartProps) {
const theme = useTheme();
const styles = useThemeStyles();
const fontMgr = useChartFontManager();
Expand Down Expand Up @@ -304,5 +305,13 @@ function LineChartContent({data, isLoading, yAxisUnit, yAxisUnitPosition = 'left
);
}

function LineChartContent(props: LineChartProps) {
return (
<ChartFontsProvider>
<LineChartContentBody {...props} />
</ChartFontsProvider>
);
}

export default LineChartContent;
export type {LineChartProps};
6 changes: 2 additions & 4 deletions src/components/Charts/VictoryTheme.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,7 @@
* Centralized styles and layout constants for the chart components.
*/
import colors from '@styles/theme/colors';

/** Font families used by all chart label components (Paragraph API multi-font fallback). */
const CHART_FONT_FAMILIES = ['ExpensifyNeue', 'NotoSansSymbols', 'NotoSansSCMonths'];
import {CHART_FONT_FAMILY_NAMES} from './utils/chartFontConstants';

/**
* Expensify Chart Color Palette.
Expand Down Expand Up @@ -54,7 +52,7 @@ const VictoryTheme = {
default: getChartColor(DEFAULT_CHART_COLOR_INDEX),
getColor: getChartColor,
},
fontFamilies: CHART_FONT_FAMILIES,
fontFamilies: Array.from(CHART_FONT_FAMILY_NAMES),
axis: {
/** Number of Y-axis ticks (including zero) */
tickCount: 5,
Expand Down
26 changes: 26 additions & 0 deletions src/components/Charts/context/ChartFontsContext.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
import type {SkTypefaceFontProvider} from '@shopify/react-native-skia';
import {createContext, useContext} from 'react';
import type ChartFontsValue from '@components/Charts/types/chartFontsTypes';
import type {ChartDefaultTypeface} from '@components/Charts/types/chartSkiaTypefaceTypes';

const ChartFontsContext = createContext<ChartFontsValue | null>(null);

function useChartFontsContext(): ChartFontsValue {
const context = useContext(ChartFontsContext);

if (!context) {
throw new Error('Chart fonts hooks must be used within ChartFontsProvider');
}

return context;
}

function useChartTypefaces(): ChartDefaultTypeface {
return useChartFontsContext().typefaces;
}

function useChartFontManager(): SkTypefaceFontProvider | null {
return useChartFontsContext().fontMgr;
}

export {ChartFontsContext, useChartFontManager, useChartTypefaces};
25 changes: 25 additions & 0 deletions src/components/Charts/context/ChartFontsProvider.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
import React from 'react';
import useChartFonts from '@components/Charts/hooks/useChartFonts';
import type ChartFontsValue from '@components/Charts/types/chartFontsTypes';
import {ChartFontsContext} from './ChartFontsContext';

type ChartFontsProviderProps = {
children: React.ReactNode;
value?: ChartFontsValue;
};

function ChartFontsLoaderProvider({children}: {children: React.ReactNode}) {
const fonts = useChartFonts();

return <ChartFontsContext.Provider value={fonts}>{children}</ChartFontsContext.Provider>;
}

function ChartFontsProvider({children, value}: ChartFontsProviderProps) {
if (value) {
return <ChartFontsContext.Provider value={value}>{children}</ChartFontsContext.Provider>;
}

return <ChartFontsLoaderProvider>{children}</ChartFontsLoaderProvider>;
}

export default ChartFontsProvider;
4 changes: 3 additions & 1 deletion src/components/Charts/hooks/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,9 @@ export {default as useChartLabelLayout} from './useChartLabelLayout';
export {default as useChartLabelMeasurements} from './useChartLabelMeasurements';
export {default as useChartParagraphs} from './useChartParagraphs';
export {default as useYAxisLabelWidth} from './useYAxisLabelWidth';
export {default as useChartFontManager, useChartDefaultTypeface} from './useChartFontManager/useChartFontManager';
export {useChartFontManager} from '@components/Charts/context/ChartFontsContext';
export {default as ChartFontsProvider} from '@components/Charts/context/ChartFontsProvider';
export {useChartTypefaces} from '@components/Charts/context/ChartFontsContext';
export {useChartInteractions, TOOLTIP_BAR_GAP} from './useChartInteractions';
export type {HitTestArgs} from './useChartInteractions';
export {default as useChartLabelFormats} from './useChartLabelFormats';
Expand Down

This file was deleted.

This file was deleted.

15 changes: 15 additions & 0 deletions src/components/Charts/hooks/useChartFonts.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import {useEffect, useSyncExternalStore} from 'react';
import type ChartFontsValue from '@components/Charts/types/chartFontsTypes';
import {getChartFontsSnapshot, loadChartFontsOnce, subscribeToChartFonts} from '@components/Charts/utils/chartFontsCache';

function useChartFonts(): ChartFontsValue {
const fonts = useSyncExternalStore(subscribeToChartFonts, getChartFontsSnapshot, getChartFontsSnapshot);

useEffect(() => {
loadChartFontsOnce();
}, []);

return fonts;
}

export default useChartFonts;
9 changes: 9 additions & 0 deletions src/components/Charts/types/chartFontsTypes.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import type {SkTypefaceFontProvider} from '@shopify/react-native-skia';
import type {ChartDefaultTypeface} from './chartSkiaTypefaceTypes';

type ChartFontsValue = {
typefaces: ChartDefaultTypeface;
fontMgr: SkTypefaceFontProvider | null;
};

export default ChartFontsValue;
9 changes: 9 additions & 0 deletions src/components/Charts/types/chartSkiaTypefaceTypes.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import type {SkTypeface} from '@shopify/react-native-skia';
// eslint-disable-next-line no-restricted-imports
import type FontFamilyStyles from '@styles/utils/FontUtils/fontFamily/types';

type ChartSkiaTypefaceKey = Exclude<keyof FontFamilyStyles, 'SYSTEM'>;

type ChartDefaultTypeface = Record<ChartSkiaTypefaceKey, SkTypeface | null>;

export type {ChartDefaultTypeface, ChartSkiaTypefaceKey};
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import type {SkParagraph} from '@shopify/react-native-skia';
import type {ValueOf} from 'type-fest';
import type {LABEL_ROTATIONS} from './VictoryTheme';
import type {LABEL_ROTATIONS} from '@components/Charts/VictoryTheme';

type ChartDataPoint = {
/** Label displayed under the data point (e.g., "Amazon", "Nov 2025") */
Expand Down
37 changes: 37 additions & 0 deletions src/components/Charts/utils/chartFontAssets.native.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
import type {DataModule} from '@shopify/react-native-skia';
import type {ChartSkiaTypefaceKey} from '@components/Charts/types/chartSkiaTypefaceTypes';

const EXPENSIFY_MONO_REGULAR = require('@assets/fonts/native/ExpensifyMono-Regular.otf') as DataModule;
const EXPENSIFY_MONO_BOLD = require('@assets/fonts/native/ExpensifyMono-Bold.otf') as DataModule;
const EXPENSIFY_MONO_ITALIC = require('@assets/fonts/native/ExpensifyMono-Italic.otf') as DataModule;
const EXPENSIFY_MONO_BOLD_ITALIC = require('@assets/fonts/native/ExpensifyMono-BoldItalic.otf') as DataModule;
const EXPENSIFY_NEUE_REGULAR = require('@assets/fonts/native/ExpensifyNeue-Regular.otf') as DataModule;
const EXPENSIFY_NEUE_BOLD = require('@assets/fonts/native/ExpensifyNeue-Bold.otf') as DataModule;
const EXPENSIFY_NEUE_ITALIC = require('@assets/fonts/native/ExpensifyNeue-Italic.otf') as DataModule;
const EXPENSIFY_NEUE_BOLD_ITALIC = require('@assets/fonts/native/ExpensifyNeue-BoldItalic.otf') as DataModule;
const EXPENSIFY_NEW_KANSAS_MEDIUM = require('@assets/fonts/native/ExpensifyNewKansas-Medium.otf') as DataModule;
const EXPENSIFY_NEW_KANSAS_MEDIUM_ITALIC = require('@assets/fonts/native/ExpensifyNewKansas-MediumItalic.otf') as DataModule;
const CUSTOM_EMOJI_FONT = require('@assets/fonts/native/CustomEmojiNativeFont.ttf') as DataModule;
const SANS_SYMBOLS_FONT = require('@assets/fonts/NotoSans-Symbols.ttf') as DataModule;
const SANS_SC_MONTHS_FONT = require('@assets/fonts/NotoSansSC-Months.ttf') as DataModule;

const CHART_SKIA_TYPEFACE_ASSETS: Record<ChartSkiaTypefaceKey, DataModule> = {
MONOSPACE: EXPENSIFY_MONO_REGULAR,
MONOSPACE_BOLD: EXPENSIFY_MONO_BOLD,
MONOSPACE_ITALIC: EXPENSIFY_MONO_ITALIC,
MONOSPACE_BOLD_ITALIC: EXPENSIFY_MONO_BOLD_ITALIC,
EXP_NEUE: EXPENSIFY_NEUE_REGULAR,
EXP_NEUE_BOLD: EXPENSIFY_NEUE_BOLD,
EXP_NEUE_ITALIC: EXPENSIFY_NEUE_ITALIC,
EXP_NEUE_BOLD_ITALIC: EXPENSIFY_NEUE_BOLD_ITALIC,
EXP_NEW_KANSAS_MEDIUM: EXPENSIFY_NEW_KANSAS_MEDIUM,
EXP_NEW_KANSAS_MEDIUM_ITALIC: EXPENSIFY_NEW_KANSAS_MEDIUM_ITALIC,
CUSTOM_EMOJI_FONT,
};

const CHART_FONT_MGR_SUPPLEMENTAL_ASSETS = {
NotoSansSymbols: SANS_SYMBOLS_FONT,
NotoSansSCMonths: SANS_SC_MONTHS_FONT,
} as const;

export {CHART_FONT_MGR_SUPPLEMENTAL_ASSETS, CHART_SKIA_TYPEFACE_ASSETS};
38 changes: 38 additions & 0 deletions src/components/Charts/utils/chartFontAssets.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
import type {DataModule} from '@shopify/react-native-skia';
import type {ChartSkiaTypefaceKey} from '@components/Charts/types/chartSkiaTypefaceTypes';
import chartWebFont from './chartWebFont';

const EXPENSIFY_MONO_REGULAR = chartWebFont(require('@assets/fonts/web/ExpensifyMono-Regular.woff2') as string);
const EXPENSIFY_MONO_BOLD = chartWebFont(require('@assets/fonts/web/ExpensifyMono-Bold.woff2') as string);
const EXPENSIFY_MONO_ITALIC = chartWebFont(require('@assets/fonts/web/ExpensifyMono-Italic.woff2') as string);
const EXPENSIFY_MONO_BOLD_ITALIC = chartWebFont(require('@assets/fonts/web/ExpensifyMono-BoldItalic.woff2') as string);
const EXPENSIFY_NEUE_REGULAR = chartWebFont(require('@assets/fonts/web/ExpensifyNeue-Regular.woff2') as string);
const EXPENSIFY_NEUE_BOLD = chartWebFont(require('@assets/fonts/web/ExpensifyNeue-Bold.woff2') as string);
const EXPENSIFY_NEUE_ITALIC = chartWebFont(require('@assets/fonts/web/ExpensifyNeue-Italic.woff2') as string);
const EXPENSIFY_NEUE_BOLD_ITALIC = chartWebFont(require('@assets/fonts/web/ExpensifyNeue-BoldItalic.woff2') as string);
const EXPENSIFY_NEW_KANSAS_MEDIUM = chartWebFont(require('@assets/fonts/web/ExpensifyNewKansas-Medium.woff2') as string);
const EXPENSIFY_NEW_KANSAS_MEDIUM_ITALIC = chartWebFont(require('@assets/fonts/web/ExpensifyNewKansas-MediumItalic.woff2') as string);
const CUSTOM_EMOJI_FONT = chartWebFont(require('@assets/fonts/web/CustomEmojiWebFont.ttf') as string);
const SANS_SYMBOLS_FONT = chartWebFont(require('@assets/fonts/NotoSans-Symbols.ttf') as string);
const SANS_SC_MONTHS_FONT = chartWebFont(require('@assets/fonts/NotoSansSC-Months.ttf') as string);

const CHART_SKIA_TYPEFACE_ASSETS: Record<ChartSkiaTypefaceKey, DataModule> = {
MONOSPACE: EXPENSIFY_MONO_REGULAR,
MONOSPACE_BOLD: EXPENSIFY_MONO_BOLD,
MONOSPACE_ITALIC: EXPENSIFY_MONO_ITALIC,
MONOSPACE_BOLD_ITALIC: EXPENSIFY_MONO_BOLD_ITALIC,
EXP_NEUE: EXPENSIFY_NEUE_REGULAR,
EXP_NEUE_BOLD: EXPENSIFY_NEUE_BOLD,
EXP_NEUE_ITALIC: EXPENSIFY_NEUE_ITALIC,
EXP_NEUE_BOLD_ITALIC: EXPENSIFY_NEUE_BOLD_ITALIC,
EXP_NEW_KANSAS_MEDIUM: EXPENSIFY_NEW_KANSAS_MEDIUM,
EXP_NEW_KANSAS_MEDIUM_ITALIC: EXPENSIFY_NEW_KANSAS_MEDIUM_ITALIC,
CUSTOM_EMOJI_FONT,
};

const CHART_FONT_MGR_SUPPLEMENTAL_ASSETS = {
NotoSansSymbols: SANS_SYMBOLS_FONT,
NotoSansSCMonths: SANS_SC_MONTHS_FONT,
} as const;

export {CHART_FONT_MGR_SUPPLEMENTAL_ASSETS, CHART_SKIA_TYPEFACE_ASSETS};
16 changes: 16 additions & 0 deletions src/components/Charts/utils/chartFontConstants.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import type {TupleToUnion} from 'type-fest';
import type {ChartSkiaTypefaceKey} from '@components/Charts/types/chartSkiaTypefaceTypes';

/** Font families registered on Skia fontMgr and used by Paragraph API multi-font fallback. */
const CHART_FONT_FAMILY_NAMES = ['ExpensifyNeue', 'ExpensifyMono', 'ExpensifyNewKansas', 'NotoSansSymbols', 'NotoSansSCMonths'] as const;

type ChartFontFamilyName = TupleToUnion<typeof CHART_FONT_FAMILY_NAMES>;

/** Maps fontMgr family names to already-loaded chart typefaces (Noto families load separately). */
const CHART_FONT_MGR_FROM_TYPEFACES: Record<Exclude<ChartFontFamilyName, 'NotoSansSymbols' | 'NotoSansSCMonths'>, ChartSkiaTypefaceKey[]> = {
ExpensifyNeue: ['EXP_NEUE', 'EXP_NEUE_BOLD', 'EXP_NEUE_ITALIC', 'EXP_NEUE_BOLD_ITALIC'],
ExpensifyMono: ['MONOSPACE', 'MONOSPACE_BOLD', 'MONOSPACE_ITALIC', 'MONOSPACE_BOLD_ITALIC'],
ExpensifyNewKansas: ['EXP_NEW_KANSAS_MEDIUM', 'EXP_NEW_KANSAS_MEDIUM_ITALIC'],
};

export {CHART_FONT_FAMILY_NAMES, CHART_FONT_MGR_FROM_TYPEFACES};
Loading
Loading