Skip to content

Commit 33b2f4b

Browse files
committed
feat: add dynamicColor prop and DynamicTheme refactor
1 parent e7dff9f commit 33b2f4b

10 files changed

Lines changed: 510 additions & 496 deletions

File tree

src/babel/__fixtures__/rewrite-imports/code.js

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,5 +10,4 @@ import {
1010
NonExistentSecond as Stuff,
1111
ThemeProvider,
1212
withTheme,
13-
DefaultTheme,
1413
} from 'react-native-paper';

src/babel/__fixtures__/rewrite-imports/output.js

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,4 +8,3 @@ import { MD3Colors } from "react-native-paper/lib/module/deprecated";
88
import { NonExistent, NonExistentSecond as Stuff } from "react-native-paper/lib/module/index.js";
99
import { ThemeProvider } from "react-native-paper/lib/module/core/theming";
1010
import { withTheme } from "react-native-paper/lib/module/core/theming";
11-
import { DefaultTheme } from "react-native-paper/lib/module/core/theming";

src/components/__tests__/TextInput.test.tsx

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,9 +5,10 @@ import { Platform, StyleSheet, Text, View } from 'react-native';
55
import { fireEvent } from '@testing-library/react-native';
66

77
import PaperProvider from '../../core/PaperProvider';
8-
import { DefaultTheme, getTheme, ThemeProvider } from '../../core/theming';
8+
import { getTheme, ThemeProvider } from '../../core/theming';
99
import { render } from '../../test-utils';
1010
import { red500 } from '../../theme/colors';
11+
import { LightTheme } from '../../theme/schemes';
1112
import { tokens } from '../../theme/tokens';
1213
import {
1314
getFlatInputColors,
@@ -369,9 +370,9 @@ it('calls onLayout on right-side affix adornment', () => {
369370
it("correctly applies theme background to label when input's background is transparent", () => {
370371
const backgroundColor = 'transparent';
371372
const theme = {
372-
...DefaultTheme,
373+
...LightTheme,
373374
colors: {
374-
...DefaultTheme.colors,
375+
...LightTheme.colors,
375376
background: 'pink',
376377
},
377378
};

src/core/PaperProvider.tsx

Lines changed: 20 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,11 @@ import { useSystemColorScheme } from './useSystemColorScheme';
1212
import MaterialCommunityIcon from '../components/MaterialCommunityIcon';
1313
import PortalHost from '../components/Portal/PortalHost';
1414
import { ReduceMotionContext } from '../theme/accessibility/ReduceMotionContext';
15+
import {
16+
isDynamicColorSupported,
17+
lightDynamicColors,
18+
darkDynamicColors,
19+
} from '../theme/schemes/DynamicTheme';
1520
import type { ThemeProp } from '../types';
1621

1722
export type Props = {
@@ -20,28 +25,33 @@ export type Props = {
2025
settings?: Settings;
2126
direction?: Direction;
2227
reduceMotion?: ReduceMotionPreference;
28+
dynamicColor?: boolean;
2329
};
2430

2531
const PaperProvider = (props: Props) => {
26-
const { reduceMotion = 'auto' } = props;
32+
const { reduceMotion = 'auto', dynamicColor = false } = props;
2733

2834
const colorScheme = useSystemColorScheme(!props.theme);
2935
const resolvedReduceMotion = useResolvedReduceMotion(reduceMotion);
3036

3137
const theme = React.useMemo(() => {
32-
const scheme = colorScheme === 'dark' ? 'dark' : 'light';
33-
const defaultThemeBase = defaultThemes[scheme];
34-
const userScale = props.theme?.animation?.scale ?? 1;
38+
const isDark = props.theme?.dark ?? colorScheme === 'dark';
39+
const base = defaultThemes[isDark ? 'dark' : 'light'];
40+
const dynamicColors =
41+
dynamicColor && isDynamicColorSupported
42+
? isDark
43+
? darkDynamicColors
44+
: lightDynamicColors
45+
: undefined;
46+
const scale = resolvedReduceMotion ? 0 : props.theme?.animation?.scale ?? 1;
3547

3648
return {
37-
...defaultThemeBase,
49+
...base,
3850
...props.theme,
39-
animation: {
40-
...props.theme?.animation,
41-
scale: resolvedReduceMotion ? 0 : userScale,
42-
},
51+
colors: { ...base.colors, ...props.theme?.colors, ...dynamicColors },
52+
animation: { ...props.theme?.animation, scale },
4353
};
44-
}, [colorScheme, props.theme, resolvedReduceMotion]);
54+
}, [colorScheme, props.theme, resolvedReduceMotion, dynamicColor]);
4555

4656
const { children, settings } = props;
4757

src/core/__tests__/PaperProvider.test.tsx

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -213,6 +213,19 @@ describe('PaperProvider', () => {
213213
).toStrictEqual(0);
214214
});
215215

216+
it('leaves theme.colors unchanged when dynamicColor is true on an unsupported platform', async () => {
217+
mockAppearance();
218+
const { getByTestId } = render(
219+
<PaperProvider dynamicColor>
220+
<FakeChild />
221+
</PaperProvider>
222+
);
223+
// `isDynamicColorSupported` is false on the test platform → no color override.
224+
expect(getByTestId('provider-child-view').props.theme.colors.primary).toBe(
225+
LightTheme.colors.primary
226+
);
227+
});
228+
216229
it('should set Appearance listeners, if there is no theme', async () => {
217230
mockAppearance();
218231
const { getByTestId } = render(createProvider());

src/index.tsx

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,6 @@ export {
44
useTheme,
55
withTheme,
66
ThemeProvider,
7-
DefaultTheme,
87
adaptNavigationTheme,
98
} from './core/theming';
109

src/theme/provider.tsx

Lines changed: 5 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,7 @@ import type { ComponentType } from 'react';
33
import { $DeepPartial, createTheming } from '@callstack/react-theme-provider';
44

55
import { DarkTheme, LightTheme } from './schemes';
6-
import type { InternalTheme, Theme, NavigationTheme } from './types';
7-
8-
export const DefaultTheme = LightTheme;
6+
import type { Theme, NavigationTheme } from './types';
97

108
export const {
119
ThemeProvider,
@@ -18,11 +16,11 @@ export function useTheme<T = Theme>(overrides?: $DeepPartial<T>) {
1816
}
1917

2018
export const useInternalTheme = (
21-
themeOverrides: $DeepPartial<InternalTheme> | undefined
22-
) => useAppTheme<InternalTheme>(themeOverrides);
19+
themeOverrides: $DeepPartial<Theme> | undefined
20+
) => useAppTheme<Theme>(themeOverrides);
2321

24-
export const withInternalTheme = <Props extends { theme: InternalTheme }, C>(
25-
WrappedComponent: ComponentType<Props & { theme: InternalTheme }> & C
22+
export const withInternalTheme = <Props extends { theme: Theme }, C>(
23+
WrappedComponent: ComponentType<Props & { theme: Theme }> & C
2624
) => withTheme<Props, C>(WrappedComponent);
2725

2826
export const defaultThemes = {

0 commit comments

Comments
 (0)