@react-native-styled-system/core 1.4.4 → 2.1.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/lib/commonjs/@types/Responsive.js +8 -0
- package/lib/commonjs/@types/Responsive.js.map +1 -0
- package/lib/commonjs/@types/SxProps.js +9 -0
- package/lib/commonjs/@types/SxProps.js.map +1 -1
- package/lib/commonjs/@types/ThemedDict.js +396 -2
- package/lib/commonjs/@types/ThemedDict.js.map +1 -1
- package/lib/commonjs/__testUtils__/testTheme.js +51 -0
- package/lib/commonjs/__testUtils__/testTheme.js.map +1 -0
- package/lib/commonjs/hook/useSx.js +13 -4
- package/lib/commonjs/hook/useSx.js.map +1 -1
- package/lib/commonjs/hook/useSxStyle.js +10 -3
- package/lib/commonjs/hook/useSxStyle.js.map +1 -1
- package/lib/commonjs/index.js +22 -0
- package/lib/commonjs/index.js.map +1 -1
- package/lib/commonjs/internal/util/resolveResponsiveValue.js +52 -0
- package/lib/commonjs/internal/util/resolveResponsiveValue.js.map +1 -0
- package/lib/commonjs/provider/StyledSystemProvider.js +17 -11
- package/lib/commonjs/provider/StyledSystemProvider.js.map +1 -1
- package/lib/commonjs/util/createSxComponent.js +5 -7
- package/lib/commonjs/util/createSxComponent.js.map +1 -1
- package/lib/commonjs/util/createTheme.js +43 -0
- package/lib/commonjs/util/createTheme.js.map +1 -0
- package/lib/commonjs/util/propsToThemedStyle.js +11 -3
- package/lib/commonjs/util/propsToThemedStyle.js.map +1 -1
- package/lib/module/@types/AnyStyle.js +0 -2
- package/lib/module/@types/Responsive.js +2 -0
- package/lib/module/@types/Responsive.js.map +1 -0
- package/lib/module/@types/SxProps.js +9 -2
- package/lib/module/@types/SxProps.js.map +1 -1
- package/lib/module/@types/ThemedDict.js +394 -3
- package/lib/module/@types/ThemedDict.js.map +1 -1
- package/lib/module/@types/ThemedTypings.js +0 -2
- package/lib/module/@types/Token.js +0 -2
- package/lib/module/__testUtils__/testTheme.js +45 -0
- package/lib/module/__testUtils__/testTheme.js.map +1 -0
- package/lib/module/hook/useSx.js +13 -6
- package/lib/module/hook/useSx.js.map +1 -1
- package/lib/module/hook/useSxStyle.js +10 -5
- package/lib/module/hook/useSxStyle.js.map +1 -1
- package/lib/module/hook/useSxTokens.js +0 -2
- package/lib/module/hook/useSxTokens.js.map +1 -1
- package/lib/module/index.js +2 -2
- package/lib/module/index.js.map +1 -1
- package/lib/module/internal/TokenParser/ColorsParser.js +0 -2
- package/lib/module/internal/TokenParser/ColorsParser.js.map +1 -1
- package/lib/module/internal/TokenParser/RadiiParser.js +0 -2
- package/lib/module/internal/TokenParser/RadiiParser.js.map +1 -1
- package/lib/module/internal/TokenParser/SizesParser.js +0 -2
- package/lib/module/internal/TokenParser/SizesParser.js.map +1 -1
- package/lib/module/internal/TokenParser/SpaceParser.js +0 -2
- package/lib/module/internal/TokenParser/SpaceParser.js.map +1 -1
- package/lib/module/internal/TokenParser/TokenParser.js +0 -2
- package/lib/module/internal/TokenParser/TokenParser.js.map +1 -1
- package/lib/module/internal/TokenParser/TypographyParser.js +0 -2
- package/lib/module/internal/TokenParser/TypographyParser.js.map +1 -1
- package/lib/module/internal/useStableCallback.js +0 -2
- package/lib/module/internal/useStableCallback.js.map +1 -1
- package/lib/module/internal/util/StyleHash.js +0 -2
- package/lib/module/internal/util/StyleHash.js.map +1 -1
- package/lib/module/internal/util/fillStyleIfNotNullish.js +0 -2
- package/lib/module/internal/util/fillStyleIfNotNullish.js.map +1 -1
- package/lib/module/internal/util/mutateShortcutPropToOriginalKeys.js +0 -2
- package/lib/module/internal/util/mutateShortcutPropToOriginalKeys.js.map +1 -1
- package/lib/module/internal/util/parsePxSuffixNumber.js +0 -2
- package/lib/module/internal/util/parsePxSuffixNumber.js.map +1 -1
- package/lib/module/internal/util/printWarning.js +0 -2
- package/lib/module/internal/util/printWarning.js.map +1 -1
- package/lib/module/internal/util/resolveResponsiveValue.js +44 -0
- package/lib/module/internal/util/resolveResponsiveValue.js.map +1 -0
- package/lib/module/provider/StyledSystemProvider.js +17 -12
- package/lib/module/provider/StyledSystemProvider.js.map +1 -1
- package/lib/module/util/createSxComponent.js +5 -9
- package/lib/module/util/createSxComponent.js.map +1 -1
- package/lib/module/util/createTheme.js +37 -0
- package/lib/module/util/createTheme.js.map +1 -0
- package/lib/module/util/propsToThemedStyle.js +11 -5
- package/lib/module/util/propsToThemedStyle.js.map +1 -1
- package/lib/typescript/src/@types/Responsive.d.ts +4 -0
- package/lib/typescript/src/@types/Responsive.d.ts.map +1 -0
- package/lib/typescript/src/@types/SxProps.d.ts +15 -3
- package/lib/typescript/src/@types/SxProps.d.ts.map +1 -1
- package/lib/typescript/src/@types/ThemedDict.d.ts +26 -0
- package/lib/typescript/src/@types/ThemedDict.d.ts.map +1 -1
- package/lib/typescript/src/__testUtils__/testTheme.d.ts +12 -0
- package/lib/typescript/src/__testUtils__/testTheme.d.ts.map +1 -0
- package/lib/typescript/src/hook/useSx.d.ts +2 -1
- package/lib/typescript/src/hook/useSx.d.ts.map +1 -1
- package/lib/typescript/src/hook/useSxStyle.d.ts +2 -1
- package/lib/typescript/src/hook/useSxStyle.d.ts.map +1 -1
- package/lib/typescript/src/index.d.ts +2 -0
- package/lib/typescript/src/index.d.ts.map +1 -1
- package/lib/typescript/src/internal/util/resolveResponsiveValue.d.ts +12 -0
- package/lib/typescript/src/internal/util/resolveResponsiveValue.d.ts.map +1 -0
- package/lib/typescript/src/provider/StyledSystemProvider.d.ts +3 -1
- package/lib/typescript/src/provider/StyledSystemProvider.d.ts.map +1 -1
- package/lib/typescript/src/util/createSxComponent.d.ts +4 -4
- package/lib/typescript/src/util/createSxComponent.d.ts.map +1 -1
- package/lib/typescript/src/util/createTheme.d.ts +4 -0
- package/lib/typescript/src/util/createTheme.d.ts.map +1 -0
- package/lib/typescript/src/util/propsToThemedStyle.d.ts +3 -1
- package/lib/typescript/src/util/propsToThemedStyle.d.ts.map +1 -1
- package/package.json +9 -8
- package/src/@types/Responsive.ts +7 -0
- package/src/@types/SxProps.ts +36 -7
- package/src/@types/ThemedDict.ts +412 -0
- package/src/__testUtils__/testTheme.ts +43 -0
- package/src/hook/useSx.test.ts +117 -38
- package/src/hook/useSx.ts +11 -2
- package/src/hook/useSxStyle.test.ts +102 -33
- package/src/hook/useSxStyle.ts +12 -1
- package/src/hook/useSxTokens.test.ts +65 -32
- package/src/index.ts +2 -0
- package/src/internal/util/resolveResponsiveValue.test.ts +171 -0
- package/src/internal/util/resolveResponsiveValue.ts +58 -0
- package/src/provider/StyledSystemProvider.tsx +14 -4
- package/src/util/createSxComponent.tsx +8 -4
- package/src/util/createTheme.test.ts +126 -0
- package/src/util/createTheme.ts +29 -0
- package/src/util/propsToThemedStyle.ts +10 -3
- package/lib/commonjs/internal/util/fillNullishThemeKey.js +0 -18
- package/lib/commonjs/internal/util/fillNullishThemeKey.js.map +0 -1
- package/lib/commonjs/package.json +0 -1
- package/lib/module/internal/util/fillNullishThemeKey.js +0 -13
- package/lib/module/internal/util/fillNullishThemeKey.js.map +0 -1
- package/lib/typescript/src/internal/util/fillNullishThemeKey.d.ts +0 -3
- package/lib/typescript/src/internal/util/fillNullishThemeKey.d.ts.map +0 -1
- package/src/internal/util/fillNullishThemeKey.ts +0 -12
|
@@ -1,57 +1,126 @@
|
|
|
1
1
|
import { StyleSheet } from 'react-native';
|
|
2
2
|
import { renderHook } from '@testing-library/react-native';
|
|
3
3
|
|
|
4
|
+
import { baseTheme, emptyTheme, responsiveTheme } from '../__testUtils__/testTheme';
|
|
4
5
|
import type { TextSxProps } from '../@types/SxProps';
|
|
5
6
|
import type { ThemedDict } from '../@types/ThemedDict';
|
|
6
7
|
|
|
8
|
+
import type { UseSxStyleOptions } from './useSxStyle';
|
|
7
9
|
import { useSxStyle } from './useSxStyle';
|
|
8
10
|
|
|
9
|
-
|
|
11
|
+
const expectResult = (
|
|
10
12
|
theme: ThemedDict,
|
|
11
13
|
sx: TextSxProps,
|
|
12
14
|
{
|
|
13
15
|
expectation,
|
|
16
|
+
cache,
|
|
17
|
+
screenWidth,
|
|
14
18
|
}: {
|
|
15
19
|
expectation: object;
|
|
20
|
+
cache?: boolean;
|
|
21
|
+
screenWidth?: number;
|
|
16
22
|
},
|
|
17
|
-
) {
|
|
23
|
+
) => {
|
|
24
|
+
const options: UseSxStyleOptions = { theme, cache, screenWidth };
|
|
18
25
|
const {
|
|
19
26
|
result: { current },
|
|
20
|
-
} = renderHook(() => useSxStyle(
|
|
27
|
+
} = renderHook(() => useSxStyle(options));
|
|
21
28
|
|
|
22
29
|
expect(StyleSheet.flatten(current(sx))).toEqual(expectation);
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
const baseTheme: ThemedDict = {
|
|
26
|
-
colors: {
|
|
27
|
-
red: 'red',
|
|
28
|
-
blue: 'blue',
|
|
29
|
-
green: 'green',
|
|
30
|
-
},
|
|
31
|
-
sizes: {
|
|
32
|
-
1: 4,
|
|
33
|
-
2: 8,
|
|
34
|
-
pagePadding: 20,
|
|
35
|
-
full: '100%',
|
|
36
|
-
},
|
|
37
|
-
space: { 1: 4, 2: 8, pagePadding: 20, full: '100%' },
|
|
38
|
-
radii: {
|
|
39
|
-
sm: 8,
|
|
40
|
-
md: 12,
|
|
41
|
-
lg: 20,
|
|
42
|
-
},
|
|
43
|
-
typography: {
|
|
44
|
-
title: {
|
|
45
|
-
fontFamily: 'Noto Sans',
|
|
46
|
-
fontSize: 14,
|
|
47
|
-
fontStyle: 'normal',
|
|
48
|
-
fontWeight: '400',
|
|
49
|
-
},
|
|
50
|
-
},
|
|
51
30
|
};
|
|
52
31
|
|
|
53
|
-
describe('
|
|
54
|
-
it('', () => {
|
|
32
|
+
describe('space parsing', () => {
|
|
33
|
+
it('resolves space token', () => {
|
|
55
34
|
expectResult(baseTheme, { mt: 2 }, { expectation: { marginTop: 8 } });
|
|
56
35
|
});
|
|
36
|
+
|
|
37
|
+
it('resolves multiple space tokens', () => {
|
|
38
|
+
expectResult(baseTheme, { mt: 1, mb: 2 }, { expectation: { marginTop: 4, marginBottom: 8 } });
|
|
39
|
+
});
|
|
40
|
+
|
|
41
|
+
it('resolves px suffix', () => {
|
|
42
|
+
expectResult(baseTheme, { m: '15px' }, { expectation: { margin: 15 } });
|
|
43
|
+
});
|
|
44
|
+
|
|
45
|
+
it('resolves percentage', () => {
|
|
46
|
+
expectResult(baseTheme, { m: '100%' }, { expectation: { margin: '100%' } });
|
|
47
|
+
});
|
|
48
|
+
});
|
|
49
|
+
|
|
50
|
+
describe('color parsing', () => {
|
|
51
|
+
it('resolves color token', () => {
|
|
52
|
+
expectResult(baseTheme, { bg: 'red' }, { expectation: { backgroundColor: 'red' } });
|
|
53
|
+
});
|
|
54
|
+
|
|
55
|
+
it('resolves raw hex color', () => {
|
|
56
|
+
expectResult(baseTheme, { bg: '#ffffff' }, { expectation: { backgroundColor: '#ffffff' } });
|
|
57
|
+
});
|
|
58
|
+
});
|
|
59
|
+
|
|
60
|
+
describe('sizes parsing', () => {
|
|
61
|
+
it('resolves size token', () => {
|
|
62
|
+
expectResult(baseTheme, { w: 1 }, { expectation: { width: 4 } });
|
|
63
|
+
});
|
|
64
|
+
|
|
65
|
+
it('resolves percentage size', () => {
|
|
66
|
+
expectResult(baseTheme, { w: 'full' as any }, { expectation: { width: '100%' } });
|
|
67
|
+
});
|
|
68
|
+
});
|
|
69
|
+
|
|
70
|
+
describe('radii parsing', () => {
|
|
71
|
+
it('resolves radii token', () => {
|
|
72
|
+
expectResult(baseTheme, { radius: 'sm' as any }, { expectation: { borderRadius: 8 } });
|
|
73
|
+
});
|
|
74
|
+
});
|
|
75
|
+
|
|
76
|
+
describe('shortcut props', () => {
|
|
77
|
+
it('resolves bg shortcut', () => {
|
|
78
|
+
expectResult(baseTheme, { bg: 'red' }, { expectation: { backgroundColor: 'red' } });
|
|
79
|
+
});
|
|
80
|
+
|
|
81
|
+
it('resolves w/h shortcuts', () => {
|
|
82
|
+
expectResult(baseTheme, { w: 1, h: 2 }, { expectation: { width: 4, height: 8 } });
|
|
83
|
+
});
|
|
84
|
+
});
|
|
85
|
+
|
|
86
|
+
describe('responsive', () => {
|
|
87
|
+
it('resolves responsive array', () => {
|
|
88
|
+
expectResult(
|
|
89
|
+
responsiveTheme,
|
|
90
|
+
{ w: [100, 200] as any },
|
|
91
|
+
{ expectation: { width: 200 }, screenWidth: 500 },
|
|
92
|
+
);
|
|
93
|
+
});
|
|
94
|
+
|
|
95
|
+
it('uses base value when screenWidth below breakpoint', () => {
|
|
96
|
+
expectResult(
|
|
97
|
+
responsiveTheme,
|
|
98
|
+
{ w: [100, 200] as any },
|
|
99
|
+
{ expectation: { width: 100 }, screenWidth: 320 },
|
|
100
|
+
);
|
|
101
|
+
});
|
|
102
|
+
});
|
|
103
|
+
|
|
104
|
+
describe('cache', () => {
|
|
105
|
+
it('cached results have same reference', () => {
|
|
106
|
+
const options: UseSxStyleOptions = { theme: emptyTheme, cache: true };
|
|
107
|
+
const {
|
|
108
|
+
result: { current },
|
|
109
|
+
} = renderHook(() => useSxStyle(options));
|
|
110
|
+
|
|
111
|
+
const style1 = current({ bg: 'red' });
|
|
112
|
+
const style2 = current({ bg: 'red' });
|
|
113
|
+
|
|
114
|
+
expect(style1 === style2).toBe(true);
|
|
115
|
+
});
|
|
116
|
+
});
|
|
117
|
+
|
|
118
|
+
describe('edge case', () => {
|
|
119
|
+
it('returns empty style for empty sx', () => {
|
|
120
|
+
expectResult(baseTheme, {}, { expectation: {} });
|
|
121
|
+
});
|
|
122
|
+
|
|
123
|
+
it('falls back to context default theme when option theme is undefined', () => {
|
|
124
|
+
expectResult(undefined as any, { w: 1 }, { expectation: { width: 1 } });
|
|
125
|
+
});
|
|
57
126
|
});
|
package/src/hook/useSxStyle.ts
CHANGED
|
@@ -11,14 +11,21 @@ import { propsToThemedStyle } from '../util/propsToThemedStyle';
|
|
|
11
11
|
export type UseSxStyleOptions = {
|
|
12
12
|
theme?: ThemedDict;
|
|
13
13
|
cache?: boolean;
|
|
14
|
+
screenWidth?: number;
|
|
14
15
|
};
|
|
15
16
|
const defaultOptions: UseSxStyleOptions = {};
|
|
16
17
|
|
|
17
|
-
export const useSxStyle = ({
|
|
18
|
+
export const useSxStyle = ({
|
|
19
|
+
theme: optionTheme,
|
|
20
|
+
cache,
|
|
21
|
+
screenWidth: optionScreenWidth,
|
|
22
|
+
}: UseSxStyleOptions = defaultOptions) => {
|
|
18
23
|
const styledSystemContext = useContext(StyledSystemContext);
|
|
19
24
|
|
|
20
25
|
return (sx: TextSxProps): StyleProp<TextStyle> => {
|
|
21
26
|
const theme = optionTheme ?? styledSystemContext?.theme;
|
|
27
|
+
const breakpoints = theme?.breakpoints ?? [];
|
|
28
|
+
const screenWidth = optionScreenWidth ?? styledSystemContext?.screenWidth ?? 0;
|
|
22
29
|
|
|
23
30
|
if (!theme) {
|
|
24
31
|
printWarning('theme not found from useSxStyle, empty style will be returned.');
|
|
@@ -31,12 +38,16 @@ export const useSxStyle = ({ theme: optionTheme, cache }: UseSxStyleOptions = de
|
|
|
31
38
|
propsToThemedStyle({
|
|
32
39
|
theme,
|
|
33
40
|
sx,
|
|
41
|
+
breakpoints,
|
|
42
|
+
screenWidth,
|
|
34
43
|
}),
|
|
35
44
|
);
|
|
36
45
|
} else {
|
|
37
46
|
return propsToThemedStyle({
|
|
38
47
|
theme,
|
|
39
48
|
sx,
|
|
49
|
+
breakpoints,
|
|
50
|
+
screenWidth,
|
|
40
51
|
});
|
|
41
52
|
}
|
|
42
53
|
};
|
|
@@ -1,59 +1,92 @@
|
|
|
1
1
|
import { renderHook } from '@testing-library/react-native';
|
|
2
2
|
|
|
3
|
+
import { baseTheme } from '../__testUtils__/testTheme';
|
|
3
4
|
import type { ThemedDict } from '../@types/ThemedDict';
|
|
4
5
|
import type { ThemedTypings } from '../@types/ThemedTypings';
|
|
5
6
|
|
|
6
7
|
import { useSxTokens } from './useSxTokens';
|
|
7
8
|
|
|
8
|
-
const
|
|
9
|
-
colors: {
|
|
10
|
-
red: 'red',
|
|
11
|
-
blue: 'blue',
|
|
12
|
-
green: 'green',
|
|
13
|
-
},
|
|
14
|
-
sizes: {
|
|
15
|
-
1: 4,
|
|
16
|
-
2: 8,
|
|
17
|
-
pagePadding: 20,
|
|
18
|
-
},
|
|
19
|
-
space: { 1: 4, 2: 8, pagePadding: 20 },
|
|
20
|
-
radii: {
|
|
21
|
-
sm: 8,
|
|
22
|
-
md: 12,
|
|
23
|
-
lg: 20,
|
|
24
|
-
},
|
|
25
|
-
typography: {},
|
|
26
|
-
};
|
|
27
|
-
|
|
28
|
-
function expectResult<T extends keyof ThemedTypings, V extends ThemedTypings[T]>(
|
|
9
|
+
const expectResult = <T extends keyof ThemedTypings, V extends ThemedTypings[T]>(
|
|
29
10
|
theme: ThemedDict,
|
|
30
11
|
tokenGroup: T,
|
|
31
12
|
tokenValues: Array<Exclude<V, null | undefined>>,
|
|
32
|
-
expectation:
|
|
33
|
-
) {
|
|
13
|
+
expectation: unknown[],
|
|
14
|
+
) => {
|
|
34
15
|
const {
|
|
35
16
|
result: { current },
|
|
36
17
|
} = renderHook(() => useSxTokens(tokenGroup, tokenValues, { theme }));
|
|
37
18
|
|
|
38
19
|
return expect(current).toEqual(expectation);
|
|
39
|
-
}
|
|
20
|
+
};
|
|
21
|
+
|
|
22
|
+
describe('colors', () => {
|
|
23
|
+
it('resolves single color', () => {
|
|
24
|
+
expectResult(baseTheme, 'colors', ['red'], ['red']);
|
|
25
|
+
});
|
|
26
|
+
|
|
27
|
+
it('resolves multiple colors', () => {
|
|
28
|
+
expectResult(baseTheme, 'colors', ['red', 'blue', 'green'], ['red', 'blue', 'green']);
|
|
29
|
+
});
|
|
30
|
+
});
|
|
40
31
|
|
|
41
|
-
describe('
|
|
42
|
-
it('
|
|
43
|
-
expectResult(
|
|
32
|
+
describe('radii', () => {
|
|
33
|
+
it('resolves single radius', () => {
|
|
34
|
+
expectResult(baseTheme, 'radii', ['sm' as any], [8]);
|
|
44
35
|
});
|
|
45
36
|
|
|
46
|
-
it('radii', () => {
|
|
47
|
-
expectResult(
|
|
37
|
+
it('resolves multiple radii', () => {
|
|
38
|
+
expectResult(baseTheme, 'radii', ['sm' as any, 'md' as any, 'lg' as any], [8, 12, 20]);
|
|
39
|
+
});
|
|
40
|
+
});
|
|
41
|
+
|
|
42
|
+
describe('space', () => {
|
|
43
|
+
it('resolves space tokens', () => {
|
|
44
|
+
expectResult(baseTheme, 'space', [1 as any, 2 as any], [4, 8]);
|
|
45
|
+
});
|
|
46
|
+
|
|
47
|
+
it('resolves string key space tokens', () => {
|
|
48
|
+
expectResult(baseTheme, 'space', ['pagePadding' as any], [20]);
|
|
49
|
+
});
|
|
50
|
+
});
|
|
51
|
+
|
|
52
|
+
describe('sizes', () => {
|
|
53
|
+
it('resolves size tokens', () => {
|
|
54
|
+
expectResult(baseTheme, 'sizes', [1 as any, 2 as any], [4, 8]);
|
|
55
|
+
});
|
|
56
|
+
|
|
57
|
+
it('resolves percentage size token', () => {
|
|
58
|
+
expectResult(baseTheme, 'sizes', ['full' as any], ['100%']);
|
|
59
|
+
});
|
|
60
|
+
});
|
|
61
|
+
|
|
62
|
+
describe('typography', () => {
|
|
63
|
+
it('resolves typography token', () => {
|
|
64
|
+
expectResult(
|
|
65
|
+
baseTheme,
|
|
66
|
+
'typography',
|
|
67
|
+
['title' as any],
|
|
68
|
+
[
|
|
69
|
+
{
|
|
70
|
+
fontFamily: 'Noto Sans',
|
|
71
|
+
fontSize: 14,
|
|
72
|
+
fontStyle: 'normal',
|
|
73
|
+
fontWeight: '400',
|
|
74
|
+
},
|
|
75
|
+
],
|
|
76
|
+
);
|
|
48
77
|
});
|
|
49
78
|
});
|
|
50
79
|
|
|
51
80
|
describe('edge case', () => {
|
|
52
|
-
it('
|
|
53
|
-
expectResult(
|
|
81
|
+
it('returns undefined for non-existent token value', () => {
|
|
82
|
+
expectResult(baseTheme, 'radii', ['' as any], [undefined]);
|
|
54
83
|
});
|
|
55
84
|
|
|
56
|
-
it('
|
|
85
|
+
it('returns undefined when theme is undefined', () => {
|
|
57
86
|
expectResult(undefined as any, 'radii', ['' as any], [undefined]);
|
|
58
87
|
});
|
|
88
|
+
|
|
89
|
+
it('returns empty array for empty token values', () => {
|
|
90
|
+
expectResult(baseTheme, 'colors', [], []);
|
|
91
|
+
});
|
|
59
92
|
});
|
package/src/index.ts
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
export * from './@types/Responsive';
|
|
1
2
|
export * from './@types/SxProps';
|
|
2
3
|
export * from './@types/ThemedDict';
|
|
3
4
|
export * from './@types/ThemedTypings';
|
|
@@ -7,3 +8,4 @@ export * from './hook/useSxTokens';
|
|
|
7
8
|
export * from './provider/StyledSystemProvider';
|
|
8
9
|
export * from './util/propsToThemedStyle';
|
|
9
10
|
export * from './util/createSxComponent';
|
|
11
|
+
export * from './util/createTheme';
|
|
@@ -0,0 +1,171 @@
|
|
|
1
|
+
import { resolveResponsiveSx, resolveResponsiveValue } from './resolveResponsiveValue';
|
|
2
|
+
|
|
3
|
+
describe('resolveResponsiveValue', () => {
|
|
4
|
+
it('returns single value as-is', () => {
|
|
5
|
+
expect(resolveResponsiveValue({ value: 100, breakpoints: [480, 768], screenWidth: 320 })).toBe(
|
|
6
|
+
100,
|
|
7
|
+
);
|
|
8
|
+
|
|
9
|
+
expect(
|
|
10
|
+
resolveResponsiveValue({ value: 'red', breakpoints: [480, 768], screenWidth: 320 }),
|
|
11
|
+
).toBe('red');
|
|
12
|
+
});
|
|
13
|
+
|
|
14
|
+
it('returns undefined for empty array', () => {
|
|
15
|
+
expect(
|
|
16
|
+
resolveResponsiveValue({ value: [], breakpoints: [480, 768], screenWidth: 320 }),
|
|
17
|
+
).toBeUndefined();
|
|
18
|
+
});
|
|
19
|
+
|
|
20
|
+
it('returns base value (index 0) when screenWidth < first breakpoint', () => {
|
|
21
|
+
expect(
|
|
22
|
+
resolveResponsiveValue({ value: [100, 200, 300], breakpoints: [480, 768], screenWidth: 320 }),
|
|
23
|
+
).toBe(100);
|
|
24
|
+
});
|
|
25
|
+
|
|
26
|
+
it('returns index 1 when screenWidth >= first breakpoint', () => {
|
|
27
|
+
expect(
|
|
28
|
+
resolveResponsiveValue({ value: [100, 200, 300], breakpoints: [480, 768], screenWidth: 480 }),
|
|
29
|
+
).toBe(200);
|
|
30
|
+
|
|
31
|
+
expect(
|
|
32
|
+
resolveResponsiveValue({ value: [100, 200, 300], breakpoints: [480, 768], screenWidth: 600 }),
|
|
33
|
+
).toBe(200);
|
|
34
|
+
});
|
|
35
|
+
|
|
36
|
+
it('returns index 2 when screenWidth >= second breakpoint', () => {
|
|
37
|
+
expect(
|
|
38
|
+
resolveResponsiveValue({ value: [100, 200, 300], breakpoints: [480, 768], screenWidth: 768 }),
|
|
39
|
+
).toBe(300);
|
|
40
|
+
|
|
41
|
+
expect(
|
|
42
|
+
resolveResponsiveValue({
|
|
43
|
+
value: [100, 200, 300],
|
|
44
|
+
breakpoints: [480, 768],
|
|
45
|
+
screenWidth: 1024,
|
|
46
|
+
}),
|
|
47
|
+
).toBe(300);
|
|
48
|
+
});
|
|
49
|
+
|
|
50
|
+
it('uses last matching value when array is shorter than breakpoints+1', () => {
|
|
51
|
+
expect(
|
|
52
|
+
resolveResponsiveValue({
|
|
53
|
+
value: [100, 200],
|
|
54
|
+
breakpoints: [480, 768, 1024],
|
|
55
|
+
screenWidth: 1024,
|
|
56
|
+
}),
|
|
57
|
+
).toBe(200);
|
|
58
|
+
});
|
|
59
|
+
|
|
60
|
+
it('always returns base value when breakpoints is empty', () => {
|
|
61
|
+
expect(
|
|
62
|
+
resolveResponsiveValue({ value: [100, 200, 300], breakpoints: [], screenWidth: 0 }),
|
|
63
|
+
).toBe(100);
|
|
64
|
+
|
|
65
|
+
expect(
|
|
66
|
+
resolveResponsiveValue({ value: [100, 200, 300], breakpoints: [], screenWidth: 9999 }),
|
|
67
|
+
).toBe(100);
|
|
68
|
+
});
|
|
69
|
+
|
|
70
|
+
it('works with string token arrays', () => {
|
|
71
|
+
expect(
|
|
72
|
+
resolveResponsiveValue({
|
|
73
|
+
value: ['red', 'blue', 'green'],
|
|
74
|
+
breakpoints: [480, 768],
|
|
75
|
+
screenWidth: 500,
|
|
76
|
+
}),
|
|
77
|
+
).toBe('blue');
|
|
78
|
+
});
|
|
79
|
+
|
|
80
|
+
it('returns undefined for undefined value', () => {
|
|
81
|
+
expect(
|
|
82
|
+
resolveResponsiveValue({ value: undefined, breakpoints: [480], screenWidth: 320 }),
|
|
83
|
+
).toBeUndefined();
|
|
84
|
+
});
|
|
85
|
+
|
|
86
|
+
it('returns null for null value', () => {
|
|
87
|
+
expect(
|
|
88
|
+
resolveResponsiveValue({ value: null, breakpoints: [480], screenWidth: 320 }),
|
|
89
|
+
).toBeNull();
|
|
90
|
+
});
|
|
91
|
+
|
|
92
|
+
it('returns base value for single-element array', () => {
|
|
93
|
+
expect(
|
|
94
|
+
resolveResponsiveValue({ value: [100], breakpoints: [480, 768], screenWidth: 1024 }),
|
|
95
|
+
).toBe(100);
|
|
96
|
+
});
|
|
97
|
+
|
|
98
|
+
it('resolves undefined entry in array', () => {
|
|
99
|
+
expect(
|
|
100
|
+
resolveResponsiveValue({
|
|
101
|
+
value: [100, undefined, 300],
|
|
102
|
+
breakpoints: [480, 768],
|
|
103
|
+
screenWidth: 500,
|
|
104
|
+
}),
|
|
105
|
+
).toBeUndefined();
|
|
106
|
+
|
|
107
|
+
expect(
|
|
108
|
+
resolveResponsiveValue({
|
|
109
|
+
value: [100, undefined, 300],
|
|
110
|
+
breakpoints: [480, 768],
|
|
111
|
+
screenWidth: 768,
|
|
112
|
+
}),
|
|
113
|
+
).toBe(300);
|
|
114
|
+
});
|
|
115
|
+
});
|
|
116
|
+
|
|
117
|
+
describe('resolveResponsiveSx', () => {
|
|
118
|
+
it('resolves all array props to single values', () => {
|
|
119
|
+
const result = resolveResponsiveSx({
|
|
120
|
+
sx: { backgroundColor: ['red', 'blue'] as any, width: [100, 200] as any },
|
|
121
|
+
breakpoints: [480],
|
|
122
|
+
screenWidth: 500,
|
|
123
|
+
});
|
|
124
|
+
|
|
125
|
+
expect(result.backgroundColor).toBe('blue');
|
|
126
|
+
expect(result.width).toBe(200);
|
|
127
|
+
});
|
|
128
|
+
|
|
129
|
+
it('keeps single values unchanged', () => {
|
|
130
|
+
const result = resolveResponsiveSx({
|
|
131
|
+
sx: { backgroundColor: 'red' as any, width: 100 as any },
|
|
132
|
+
breakpoints: [480],
|
|
133
|
+
screenWidth: 500,
|
|
134
|
+
});
|
|
135
|
+
|
|
136
|
+
expect(result.backgroundColor).toBe('red');
|
|
137
|
+
expect(result.width).toBe(100);
|
|
138
|
+
});
|
|
139
|
+
|
|
140
|
+
it('handles mixed array and single values', () => {
|
|
141
|
+
const result = resolveResponsiveSx({
|
|
142
|
+
sx: { backgroundColor: ['red', 'blue'] as any, width: 100 as any },
|
|
143
|
+
breakpoints: [480],
|
|
144
|
+
screenWidth: 500,
|
|
145
|
+
});
|
|
146
|
+
|
|
147
|
+
expect(result.backgroundColor).toBe('blue');
|
|
148
|
+
expect(result.width).toBe(100);
|
|
149
|
+
});
|
|
150
|
+
|
|
151
|
+
it('does not resolve transform arrays', () => {
|
|
152
|
+
const transformValue = [{ translateX: 10 }];
|
|
153
|
+
const result = resolveResponsiveSx({
|
|
154
|
+
sx: { transform: transformValue as any },
|
|
155
|
+
breakpoints: [480],
|
|
156
|
+
screenWidth: 500,
|
|
157
|
+
});
|
|
158
|
+
|
|
159
|
+
expect(result.transform).toBe(transformValue);
|
|
160
|
+
});
|
|
161
|
+
|
|
162
|
+
it('returns empty object for undefined sx', () => {
|
|
163
|
+
const result = resolveResponsiveSx({
|
|
164
|
+
sx: undefined,
|
|
165
|
+
breakpoints: [480],
|
|
166
|
+
screenWidth: 500,
|
|
167
|
+
});
|
|
168
|
+
|
|
169
|
+
expect(result).toEqual({});
|
|
170
|
+
});
|
|
171
|
+
});
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import { RESPONSIVE_EXCLUDED_KEYS } from '../../@types/Responsive';
|
|
2
|
+
import type { ResolvedTextSxProps, TextSxProps } from '../../@types/SxProps';
|
|
3
|
+
|
|
4
|
+
export const resolveResponsiveValue = <T>({
|
|
5
|
+
value,
|
|
6
|
+
breakpoints,
|
|
7
|
+
screenWidth,
|
|
8
|
+
}: {
|
|
9
|
+
value: T | T[] | undefined | null;
|
|
10
|
+
breakpoints: number[];
|
|
11
|
+
screenWidth: number;
|
|
12
|
+
}): T | undefined | null => {
|
|
13
|
+
if (!Array.isArray(value)) {
|
|
14
|
+
return value;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
if (value.length === 0) {
|
|
18
|
+
return undefined;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
let resolved = value[0] as T;
|
|
22
|
+
|
|
23
|
+
for (let i = 0; i < breakpoints.length && i + 1 < value.length; i++) {
|
|
24
|
+
if (screenWidth >= breakpoints[i]!) {
|
|
25
|
+
resolved = value[i + 1] as T;
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
return resolved;
|
|
30
|
+
};
|
|
31
|
+
|
|
32
|
+
export const resolveResponsiveSx = ({
|
|
33
|
+
sx,
|
|
34
|
+
breakpoints,
|
|
35
|
+
screenWidth,
|
|
36
|
+
}: {
|
|
37
|
+
sx: TextSxProps | undefined;
|
|
38
|
+
breakpoints: number[];
|
|
39
|
+
screenWidth: number;
|
|
40
|
+
}): ResolvedTextSxProps => {
|
|
41
|
+
if (!sx) {
|
|
42
|
+
return {} as ResolvedTextSxProps;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
const resolved: Record<string, unknown> = {};
|
|
46
|
+
|
|
47
|
+
for (const key of Object.keys(sx)) {
|
|
48
|
+
const val = (sx as Record<string, unknown>)[key];
|
|
49
|
+
|
|
50
|
+
if (RESPONSIVE_EXCLUDED_KEYS.has(key) || !Array.isArray(val)) {
|
|
51
|
+
resolved[key] = val;
|
|
52
|
+
} else {
|
|
53
|
+
resolved[key] = resolveResponsiveValue({ value: val, breakpoints, screenWidth });
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
return resolved as ResolvedTextSxProps;
|
|
58
|
+
};
|
|
@@ -1,23 +1,33 @@
|
|
|
1
1
|
import type { PropsWithChildren } from 'react';
|
|
2
2
|
import React from 'react';
|
|
3
|
+
import { useWindowDimensions } from 'react-native';
|
|
3
4
|
|
|
4
5
|
import type { ThemedDict } from '../@types/ThemedDict';
|
|
5
6
|
import { emptyThemedDict } from '../@types/ThemedDict';
|
|
6
|
-
import {
|
|
7
|
+
import { createTheme } from '../util/createTheme';
|
|
7
8
|
|
|
8
9
|
export type StyledSystemContextValue = {
|
|
9
10
|
theme: ThemedDict;
|
|
11
|
+
screenWidth: number;
|
|
10
12
|
};
|
|
11
13
|
|
|
12
14
|
export const StyledSystemContext = React.createContext<StyledSystemContextValue>({
|
|
13
15
|
theme: emptyThemedDict,
|
|
16
|
+
screenWidth: 0,
|
|
14
17
|
});
|
|
15
|
-
type Props = PropsWithChildren<{ theme: Partial<ThemedDict
|
|
18
|
+
type Props = PropsWithChildren<{ theme: Partial<ThemedDict>; screenWidth?: number }>;
|
|
19
|
+
|
|
20
|
+
const StyledSystemProviderInner = ({ children, theme, screenWidth }: Props) => {
|
|
21
|
+
const { width } = useWindowDimensions();
|
|
22
|
+
const resolvedScreenWidth = screenWidth ?? width;
|
|
16
23
|
|
|
17
|
-
export const StyledSystemProvider = ({ children, theme }: Props) => {
|
|
18
24
|
return (
|
|
19
|
-
<StyledSystemContext.Provider
|
|
25
|
+
<StyledSystemContext.Provider
|
|
26
|
+
value={{ theme: createTheme(theme), screenWidth: resolvedScreenWidth }}
|
|
27
|
+
>
|
|
20
28
|
{children}
|
|
21
29
|
</StyledSystemContext.Provider>
|
|
22
30
|
);
|
|
23
31
|
};
|
|
32
|
+
|
|
33
|
+
export const StyledSystemProvider = StyledSystemProviderInner;
|
|
@@ -6,11 +6,13 @@ import type { UseSxOptions } from '../hook/useSx';
|
|
|
6
6
|
import { useSx } from '../hook/useSx';
|
|
7
7
|
|
|
8
8
|
export function createSxComponent<Props extends object, Ref>(Base: ComponentType<Props>) {
|
|
9
|
+
type MergedProps = Omit<Props, keyof SxProps> & SxProps;
|
|
10
|
+
|
|
9
11
|
return ({
|
|
10
12
|
defaultProps,
|
|
11
13
|
sxOptions,
|
|
12
|
-
}: { defaultProps?:
|
|
13
|
-
const Transformed = forwardRef<Ref,
|
|
14
|
+
}: { defaultProps?: MergedProps; sxOptions?: UseSxOptions } = {}) => {
|
|
15
|
+
const Transformed = forwardRef<Ref, MergedProps>(function (props, ref) {
|
|
14
16
|
const { filteredProps, getStyle } = useSx({ ...defaultProps, ...props }, sxOptions);
|
|
15
17
|
|
|
16
18
|
return <Base {...(filteredProps as any)} style={getStyle()} ref={ref as any} />;
|
|
@@ -23,11 +25,13 @@ export function createSxComponent<Props extends object, Ref>(Base: ComponentType
|
|
|
23
25
|
}
|
|
24
26
|
|
|
25
27
|
export function createSxTextComponent<Props extends object, Ref>(Base: ComponentType<Props>) {
|
|
28
|
+
type MergedProps = Omit<Props, keyof TextSxProps> & TextSxProps;
|
|
29
|
+
|
|
26
30
|
return ({
|
|
27
31
|
defaultProps,
|
|
28
32
|
sxOptions,
|
|
29
|
-
}: { defaultProps?:
|
|
30
|
-
const Transformed = forwardRef<Ref,
|
|
33
|
+
}: { defaultProps?: MergedProps; sxOptions?: UseSxOptions } = {}) => {
|
|
34
|
+
const Transformed = forwardRef<Ref, MergedProps>(function (props, ref) {
|
|
31
35
|
const { filteredProps, getStyle } = useSx(
|
|
32
36
|
{ ...defaultProps, ...props },
|
|
33
37
|
{ styleType: 'TextStyle', ...sxOptions },
|