@umituz/react-native-design-system 1.8.3 → 1.9.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/package.json +39 -35
- package/src/index.ts +1 -35
- package/src/presentation/utils/variants/__tests__/compound.test.ts +156 -0
- package/src/presentation/utils/variants/__tests__/core.test.ts +103 -0
- package/src/presentation/utils/variants/__tests__/helpers.test.ts +95 -0
- package/src/presentation/utils/variants/compound.ts +18 -9
- package/src/presentation/utils/variants/core.ts +25 -15
- package/src/presentation/utils/variants/helpers.ts +25 -4
- package/src/presentation/utils/variants.ts +0 -3
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@umituz/react-native-design-system",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.9.2",
|
|
4
4
|
"description": "Universal design system for React Native apps - Domain-Driven Design architecture with Material Design 3 components",
|
|
5
5
|
"main": "./src/index.ts",
|
|
6
6
|
"types": "./src/index.ts",
|
|
@@ -15,12 +15,11 @@
|
|
|
15
15
|
"./package.json": "./package.json"
|
|
16
16
|
},
|
|
17
17
|
"scripts": {
|
|
18
|
-
"typecheck": "npx
|
|
19
|
-
"lint": "eslint . --ext .ts,.tsx
|
|
20
|
-
"lint:fix": "eslint . --ext .ts,.tsx
|
|
21
|
-
"
|
|
22
|
-
"
|
|
23
|
-
"version:major": "npm version major -m 'chore: release v%s'"
|
|
18
|
+
"typecheck": "npx tsc --noEmit",
|
|
19
|
+
"lint": "eslint . --ext .ts,.tsx",
|
|
20
|
+
"lint:fix": "eslint . --ext .ts,.tsx --fix",
|
|
21
|
+
"test": "jest",
|
|
22
|
+
"test:watch": "jest --watch"
|
|
24
23
|
},
|
|
25
24
|
"keywords": [
|
|
26
25
|
"react-native",
|
|
@@ -40,8 +39,8 @@
|
|
|
40
39
|
"peerDependencies": {
|
|
41
40
|
"@expo/vector-icons": ">=14.0.0",
|
|
42
41
|
"@gorhom/bottom-sheet": ">=5.0.0",
|
|
43
|
-
"@react-native-community/datetimepicker": "
|
|
44
|
-
"@react-navigation/native": "
|
|
42
|
+
"@react-native-community/datetimepicker": ">=8.5.0",
|
|
43
|
+
"@react-navigation/native": ">=6.0.0",
|
|
45
44
|
"@umituz/react-native-bottom-sheet": "*",
|
|
46
45
|
"@umituz/react-native-design-system-atoms": "*",
|
|
47
46
|
"@umituz/react-native-design-system-molecules": "*",
|
|
@@ -49,14 +48,14 @@
|
|
|
49
48
|
"@umituz/react-native-design-system-responsive": "*",
|
|
50
49
|
"@umituz/react-native-design-system-theme": "*",
|
|
51
50
|
"@umituz/react-native-design-system-typography": "*",
|
|
52
|
-
"expo-linear-gradient": "
|
|
51
|
+
"expo-linear-gradient": ">=15.0.0",
|
|
53
52
|
"lucide-react-native": ">=0.468.0",
|
|
54
|
-
"react": ">=18.
|
|
55
|
-
"react-native": ">=0.
|
|
56
|
-
"react-native-gesture-handler": ">=2.
|
|
57
|
-
"react-native-reanimated": "
|
|
58
|
-
"react-native-svg": ">=
|
|
59
|
-
"zustand": "
|
|
53
|
+
"react": ">=18.3.0",
|
|
54
|
+
"react-native": ">=0.76.0",
|
|
55
|
+
"react-native-gesture-handler": ">=2.20.0",
|
|
56
|
+
"react-native-reanimated": ">=3.16.0",
|
|
57
|
+
"react-native-svg": ">=15.8.0",
|
|
58
|
+
"zustand": ">=5.0.0"
|
|
60
59
|
},
|
|
61
60
|
"peerDependenciesMeta": {
|
|
62
61
|
"expo-linear-gradient": {
|
|
@@ -72,13 +71,13 @@
|
|
|
72
71
|
"devDependencies": {
|
|
73
72
|
"@eslint/js": "^9.0.0",
|
|
74
73
|
"@expo/vector-icons": ">=14.0.0",
|
|
75
|
-
"@gorhom/bottom-sheet": "
|
|
76
|
-
"@react-native-community/datetimepicker": "
|
|
77
|
-
"@react-navigation/native": "
|
|
78
|
-
"@types/react": "
|
|
79
|
-
"@types/react-native": "
|
|
80
|
-
"@typescript-eslint/eslint-plugin": "^
|
|
81
|
-
"@typescript-eslint/parser": "^
|
|
74
|
+
"@gorhom/bottom-sheet": ">=5.0.0",
|
|
75
|
+
"@react-native-community/datetimepicker": ">=8.5.0",
|
|
76
|
+
"@react-navigation/native": ">=6.0.0",
|
|
77
|
+
"@types/react": ">=18.3.0",
|
|
78
|
+
"@types/react-native": ">=0.73.0",
|
|
79
|
+
"@typescript-eslint/eslint-plugin": "^8.0.0",
|
|
80
|
+
"@typescript-eslint/parser": "^8.0.0",
|
|
82
81
|
"@umituz/react-native-bottom-sheet": "*",
|
|
83
82
|
"@umituz/react-native-design-system-atoms": "*",
|
|
84
83
|
"@umituz/react-native-design-system-molecules": "*",
|
|
@@ -87,18 +86,23 @@
|
|
|
87
86
|
"@umituz/react-native-design-system-theme": "*",
|
|
88
87
|
"@umituz/react-native-design-system-typography": "*",
|
|
89
88
|
"eslint": "^9.0.0",
|
|
90
|
-
"eslint-plugin-react": "^7.
|
|
91
|
-
"eslint-plugin-react-hooks": "^
|
|
92
|
-
"
|
|
89
|
+
"eslint-plugin-react": "^7.37.0",
|
|
90
|
+
"eslint-plugin-react-hooks": "^5.0.0",
|
|
91
|
+
"eslint-plugin-react-refresh": "^0.4.0",
|
|
92
|
+
"expo-linear-gradient": ">=15.0.0",
|
|
93
93
|
"lucide-react-native": ">=0.468.0",
|
|
94
|
-
"react": ">=18.
|
|
95
|
-
"react-native": ">=0.
|
|
96
|
-
"react-native-gesture-handler": "
|
|
97
|
-
"react-native-reanimated": "
|
|
98
|
-
"react-native-safe-area-context": "
|
|
99
|
-
"react-native-svg": "
|
|
100
|
-
"typescript": "
|
|
101
|
-
"zustand": "
|
|
94
|
+
"react": ">=18.3.0",
|
|
95
|
+
"react-native": ">=0.76.0",
|
|
96
|
+
"react-native-gesture-handler": ">=2.20.0",
|
|
97
|
+
"react-native-reanimated": ">=3.16.0",
|
|
98
|
+
"react-native-safe-area-context": ">=5.6.0",
|
|
99
|
+
"react-native-svg": ">=15.8.0",
|
|
100
|
+
"typescript": ">=5.6.0",
|
|
101
|
+
"zustand": ">=5.0.0",
|
|
102
|
+
"jest": "^29.7.0",
|
|
103
|
+
"@types/jest": "^29.5.0",
|
|
104
|
+
"ts-jest": "^29.1.0",
|
|
105
|
+
"jest-environment-jsdom": "^29.7.0"
|
|
102
106
|
},
|
|
103
107
|
"publishConfig": {
|
|
104
108
|
"access": "public"
|
|
@@ -111,4 +115,4 @@
|
|
|
111
115
|
"dependencies": {
|
|
112
116
|
"@react-native-async-storage/async-storage": "^2.2.0"
|
|
113
117
|
}
|
|
114
|
-
}
|
|
118
|
+
}
|
package/src/index.ts
CHANGED
|
@@ -1,20 +1,12 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* @umituz/react-native-design-system
|
|
3
|
-
*
|
|
4
3
|
* Universal design system for React Native apps
|
|
5
|
-
* Re-exports from individual packages for convenience
|
|
6
4
|
*/
|
|
7
5
|
|
|
8
|
-
// =============================================================================
|
|
9
|
-
// COMMON STYLES - Re-export from @umituz/react-native-design-system-theme
|
|
10
|
-
// =============================================================================
|
|
11
6
|
export {
|
|
12
7
|
useCommonStyles,
|
|
13
8
|
} from '@umituz/react-native-design-system-theme';
|
|
14
9
|
|
|
15
|
-
// =============================================================================
|
|
16
|
-
// VARIANTS UTILITIES - Own utilities
|
|
17
|
-
// =============================================================================
|
|
18
10
|
export {
|
|
19
11
|
createVariants,
|
|
20
12
|
type VariantConfig,
|
|
@@ -33,9 +25,6 @@ export {
|
|
|
33
25
|
combineStyles,
|
|
34
26
|
} from './presentation/utils/variants/helpers';
|
|
35
27
|
|
|
36
|
-
// =============================================================================
|
|
37
|
-
// ATOMS - Re-export from @umituz/react-native-design-system-atoms
|
|
38
|
-
// =============================================================================
|
|
39
28
|
export {
|
|
40
29
|
AtomicText,
|
|
41
30
|
AtomicIcon,
|
|
@@ -47,17 +36,10 @@ export {
|
|
|
47
36
|
type IconName,
|
|
48
37
|
} from '@umituz/react-native-design-system-atoms';
|
|
49
38
|
|
|
50
|
-
|
|
51
|
-
// =============================================================================
|
|
52
|
-
// ORGANISMS - Re-export from @umituz/react-native-design-system-organisms
|
|
53
|
-
// =============================================================================
|
|
54
39
|
export {
|
|
55
40
|
ScreenLayout,
|
|
56
41
|
} from '@umituz/react-native-design-system-organisms';
|
|
57
42
|
|
|
58
|
-
// =============================================================================
|
|
59
|
-
// MOLECULES - Re-export from @umituz/react-native-design-system-molecules
|
|
60
|
-
// =============================================================================
|
|
61
43
|
export {
|
|
62
44
|
FormField,
|
|
63
45
|
ListItem,
|
|
@@ -72,13 +54,8 @@ export {
|
|
|
72
54
|
useConfirmationModal,
|
|
73
55
|
} from '@umituz/react-native-design-system-molecules';
|
|
74
56
|
|
|
75
|
-
// =============================================================================
|
|
76
|
-
// RESPONSIVE - Re-export from @umituz/react-native-design-system-responsive
|
|
77
|
-
// =============================================================================
|
|
78
57
|
export {
|
|
79
58
|
useResponsive,
|
|
80
|
-
useResponsiveSizes,
|
|
81
|
-
useDeviceType,
|
|
82
59
|
getScreenDimensions,
|
|
83
60
|
isSmallPhone,
|
|
84
61
|
isTablet,
|
|
@@ -94,26 +71,15 @@ export {
|
|
|
94
71
|
getResponsiveMaxWidth,
|
|
95
72
|
getResponsiveFontSize,
|
|
96
73
|
isLandscape,
|
|
97
|
-
getKeyboardBehavior,
|
|
98
74
|
getDeviceType,
|
|
99
|
-
|
|
75
|
+
getMinTouchTarget,
|
|
100
76
|
getSpacingMultiplier,
|
|
101
|
-
getOnboardingIconMarginTop,
|
|
102
|
-
getOnboardingIconMarginBottom,
|
|
103
|
-
getOnboardingTitleMarginBottom,
|
|
104
|
-
getOnboardingTextPadding,
|
|
105
|
-
getOnboardingDescriptionMarginTop,
|
|
106
77
|
IOS_HIG,
|
|
107
|
-
ANDROID_MATERIAL,
|
|
108
78
|
PLATFORM_CONSTANTS,
|
|
109
79
|
isValidTouchTarget,
|
|
110
|
-
getMinTouchTarget,
|
|
111
80
|
DeviceType,
|
|
112
81
|
} from '@umituz/react-native-design-system-responsive';
|
|
113
82
|
|
|
114
|
-
// =============================================================================
|
|
115
|
-
// THEME - Re-export from @umituz/react-native-design-system-theme
|
|
116
|
-
// =============================================================================
|
|
117
83
|
export {
|
|
118
84
|
useAppDesignTokens,
|
|
119
85
|
STATIC_TOKENS,
|
|
@@ -0,0 +1,156 @@
|
|
|
1
|
+
import { describe, it, expect } from '@jest/globals';
|
|
2
|
+
import { createAdvancedVariants, type AdvancedVariantConfig } from '../compound';
|
|
3
|
+
|
|
4
|
+
describe('createAdvancedVariants', () => {
|
|
5
|
+
it('should behave like createVariants when no compound variants provided', () => {
|
|
6
|
+
const config: AdvancedVariantConfig<{
|
|
7
|
+
size: Record<string, any>;
|
|
8
|
+
color: Record<string, any>;
|
|
9
|
+
}> = {
|
|
10
|
+
base: { borderRadius: 4 },
|
|
11
|
+
variants: {
|
|
12
|
+
size: {
|
|
13
|
+
small: { padding: 8 },
|
|
14
|
+
large: { padding: 16 },
|
|
15
|
+
},
|
|
16
|
+
color: {
|
|
17
|
+
primary: { backgroundColor: 'blue' },
|
|
18
|
+
secondary: { backgroundColor: 'gray' },
|
|
19
|
+
},
|
|
20
|
+
},
|
|
21
|
+
};
|
|
22
|
+
|
|
23
|
+
const getStyles = createAdvancedVariants(config);
|
|
24
|
+
const result = getStyles({ size: 'large', color: 'primary' });
|
|
25
|
+
|
|
26
|
+
expect(result).toEqual({
|
|
27
|
+
borderRadius: 4,
|
|
28
|
+
padding: 16,
|
|
29
|
+
backgroundColor: 'blue',
|
|
30
|
+
});
|
|
31
|
+
});
|
|
32
|
+
|
|
33
|
+
it('should apply compound variants when conditions are met', () => {
|
|
34
|
+
const config: AdvancedVariantConfig<{
|
|
35
|
+
size: Record<string, any>;
|
|
36
|
+
color: Record<string, any>;
|
|
37
|
+
}> = {
|
|
38
|
+
base: { borderRadius: 4 },
|
|
39
|
+
variants: {
|
|
40
|
+
size: {
|
|
41
|
+
small: { padding: 8 },
|
|
42
|
+
large: { padding: 16 },
|
|
43
|
+
},
|
|
44
|
+
color: {
|
|
45
|
+
primary: { backgroundColor: 'blue' },
|
|
46
|
+
secondary: { backgroundColor: 'gray' },
|
|
47
|
+
},
|
|
48
|
+
},
|
|
49
|
+
compoundVariants: [
|
|
50
|
+
{
|
|
51
|
+
conditions: { size: 'large', color: 'primary' },
|
|
52
|
+
style: { borderWidth: 2, borderColor: 'darkblue' },
|
|
53
|
+
},
|
|
54
|
+
],
|
|
55
|
+
};
|
|
56
|
+
|
|
57
|
+
const getStyles = createAdvancedVariants(config);
|
|
58
|
+
const result = getStyles({ size: 'large', color: 'primary' });
|
|
59
|
+
|
|
60
|
+
expect(result).toEqual({
|
|
61
|
+
borderRadius: 4,
|
|
62
|
+
padding: 16,
|
|
63
|
+
backgroundColor: 'blue',
|
|
64
|
+
borderWidth: 2,
|
|
65
|
+
borderColor: 'darkblue',
|
|
66
|
+
});
|
|
67
|
+
});
|
|
68
|
+
|
|
69
|
+
it('should not apply compound variants when conditions are not met', () => {
|
|
70
|
+
const config: AdvancedVariantConfig<{
|
|
71
|
+
size: Record<string, any>;
|
|
72
|
+
color: Record<string, any>;
|
|
73
|
+
}> = {
|
|
74
|
+
base: { borderRadius: 4 },
|
|
75
|
+
variants: {
|
|
76
|
+
size: {
|
|
77
|
+
small: { padding: 8 },
|
|
78
|
+
large: { padding: 16 },
|
|
79
|
+
},
|
|
80
|
+
color: {
|
|
81
|
+
primary: { backgroundColor: 'blue' },
|
|
82
|
+
secondary: { backgroundColor: 'gray' },
|
|
83
|
+
},
|
|
84
|
+
},
|
|
85
|
+
compoundVariants: [
|
|
86
|
+
{
|
|
87
|
+
conditions: { size: 'large', color: 'primary' },
|
|
88
|
+
style: { borderWidth: 2, borderColor: 'darkblue' },
|
|
89
|
+
},
|
|
90
|
+
],
|
|
91
|
+
};
|
|
92
|
+
|
|
93
|
+
const getStyles = createAdvancedVariants(config);
|
|
94
|
+
const result = getStyles({ size: 'small', color: 'primary' });
|
|
95
|
+
|
|
96
|
+
expect(result).toEqual({
|
|
97
|
+
borderRadius: 4,
|
|
98
|
+
padding: 8,
|
|
99
|
+
backgroundColor: 'blue',
|
|
100
|
+
});
|
|
101
|
+
});
|
|
102
|
+
|
|
103
|
+
it('should apply multiple compound variants when conditions are met', () => {
|
|
104
|
+
const config: AdvancedVariantConfig<{
|
|
105
|
+
size: Record<string, any>;
|
|
106
|
+
color: Record<string, any>;
|
|
107
|
+
variant: Record<string, any>;
|
|
108
|
+
}> = {
|
|
109
|
+
base: { borderRadius: 4 },
|
|
110
|
+
variants: {
|
|
111
|
+
size: {
|
|
112
|
+
small: { padding: 8 },
|
|
113
|
+
large: { padding: 16 },
|
|
114
|
+
},
|
|
115
|
+
color: {
|
|
116
|
+
primary: { backgroundColor: 'blue' },
|
|
117
|
+
secondary: { backgroundColor: 'gray' },
|
|
118
|
+
},
|
|
119
|
+
variant: {
|
|
120
|
+
outlined: { borderWidth: 1 },
|
|
121
|
+
filled: { borderWidth: 0 },
|
|
122
|
+
},
|
|
123
|
+
},
|
|
124
|
+
compoundVariants: [
|
|
125
|
+
{
|
|
126
|
+
conditions: { size: 'large', color: 'primary' },
|
|
127
|
+
style: { borderWidth: 2, borderColor: 'darkblue' },
|
|
128
|
+
},
|
|
129
|
+
{
|
|
130
|
+
conditions: { color: 'secondary', variant: 'outlined' },
|
|
131
|
+
style: { borderStyle: 'dashed' },
|
|
132
|
+
},
|
|
133
|
+
],
|
|
134
|
+
};
|
|
135
|
+
|
|
136
|
+
const getStyles = createAdvancedVariants(config);
|
|
137
|
+
const result1 = getStyles({ size: 'large', color: 'primary', variant: 'filled' });
|
|
138
|
+
const result2 = getStyles({ size: 'small', color: 'secondary', variant: 'outlined' });
|
|
139
|
+
|
|
140
|
+
expect(result1).toEqual({
|
|
141
|
+
borderRadius: 4,
|
|
142
|
+
padding: 16,
|
|
143
|
+
backgroundColor: 'blue',
|
|
144
|
+
borderWidth: 2,
|
|
145
|
+
borderColor: 'darkblue',
|
|
146
|
+
});
|
|
147
|
+
|
|
148
|
+
expect(result2).toEqual({
|
|
149
|
+
borderRadius: 4,
|
|
150
|
+
padding: 8,
|
|
151
|
+
backgroundColor: 'gray',
|
|
152
|
+
borderWidth: 1,
|
|
153
|
+
borderStyle: 'dashed',
|
|
154
|
+
});
|
|
155
|
+
});
|
|
156
|
+
});
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
import { describe, it, expect } from '@jest/globals';
|
|
2
|
+
import { View, Text } from 'react-native';
|
|
3
|
+
import { createVariants, type VariantConfig } from '../core';
|
|
4
|
+
|
|
5
|
+
describe('createVariants', () => {
|
|
6
|
+
it('should return base style when no variants provided', () => {
|
|
7
|
+
const config: VariantConfig<{ size: Record<string, any> }> = {
|
|
8
|
+
base: { backgroundColor: 'red' },
|
|
9
|
+
};
|
|
10
|
+
|
|
11
|
+
const getStyles = createVariants(config);
|
|
12
|
+
const result = getStyles();
|
|
13
|
+
|
|
14
|
+
expect(result).toEqual({ backgroundColor: 'red' });
|
|
15
|
+
});
|
|
16
|
+
|
|
17
|
+
it('should apply variant styles based on props', () => {
|
|
18
|
+
const config: VariantConfig<{ size: Record<string, any> }> = {
|
|
19
|
+
base: { backgroundColor: 'red' },
|
|
20
|
+
variants: {
|
|
21
|
+
size: {
|
|
22
|
+
small: { padding: 8 },
|
|
23
|
+
large: { padding: 16 },
|
|
24
|
+
},
|
|
25
|
+
},
|
|
26
|
+
};
|
|
27
|
+
|
|
28
|
+
const getStyles = createVariants(config);
|
|
29
|
+
const smallResult = getStyles({ size: 'small' });
|
|
30
|
+
const largeResult = getStyles({ size: 'large' });
|
|
31
|
+
|
|
32
|
+
expect(smallResult).toEqual({ backgroundColor: 'red', padding: 8 });
|
|
33
|
+
expect(largeResult).toEqual({ backgroundColor: 'red', padding: 16 });
|
|
34
|
+
});
|
|
35
|
+
|
|
36
|
+
it('should apply default variants when no props provided', () => {
|
|
37
|
+
const config: VariantConfig<{ size: Record<string, any> }> = {
|
|
38
|
+
base: { backgroundColor: 'red' },
|
|
39
|
+
variants: {
|
|
40
|
+
size: {
|
|
41
|
+
small: { padding: 8 },
|
|
42
|
+
large: { padding: 16 },
|
|
43
|
+
},
|
|
44
|
+
},
|
|
45
|
+
defaultVariants: {
|
|
46
|
+
size: 'large',
|
|
47
|
+
},
|
|
48
|
+
};
|
|
49
|
+
|
|
50
|
+
const getStyles = createVariants(config);
|
|
51
|
+
const result = getStyles();
|
|
52
|
+
|
|
53
|
+
expect(result).toEqual({ backgroundColor: 'red', padding: 16 });
|
|
54
|
+
});
|
|
55
|
+
|
|
56
|
+
it('should override default variants with props', () => {
|
|
57
|
+
const config: VariantConfig<{ size: Record<string, any> }> = {
|
|
58
|
+
base: { backgroundColor: 'red' },
|
|
59
|
+
variants: {
|
|
60
|
+
size: {
|
|
61
|
+
small: { padding: 8 },
|
|
62
|
+
large: { padding: 16 },
|
|
63
|
+
},
|
|
64
|
+
},
|
|
65
|
+
defaultVariants: {
|
|
66
|
+
size: 'large',
|
|
67
|
+
},
|
|
68
|
+
};
|
|
69
|
+
|
|
70
|
+
const getStyles = createVariants(config);
|
|
71
|
+
const result = getStyles({ size: 'small' });
|
|
72
|
+
|
|
73
|
+
expect(result).toEqual({ backgroundColor: 'red', padding: 8 });
|
|
74
|
+
});
|
|
75
|
+
|
|
76
|
+
it('should handle multiple variants', () => {
|
|
77
|
+
const config: VariantConfig<{
|
|
78
|
+
size: Record<string, any>;
|
|
79
|
+
color: Record<string, any>;
|
|
80
|
+
}> = {
|
|
81
|
+
base: { borderRadius: 4 },
|
|
82
|
+
variants: {
|
|
83
|
+
size: {
|
|
84
|
+
small: { padding: 8 },
|
|
85
|
+
large: { padding: 16 },
|
|
86
|
+
},
|
|
87
|
+
color: {
|
|
88
|
+
primary: { backgroundColor: 'blue' },
|
|
89
|
+
secondary: { backgroundColor: 'gray' },
|
|
90
|
+
},
|
|
91
|
+
},
|
|
92
|
+
};
|
|
93
|
+
|
|
94
|
+
const getStyles = createVariants(config);
|
|
95
|
+
const result = getStyles({ size: 'large', color: 'primary' });
|
|
96
|
+
|
|
97
|
+
expect(result).toEqual({
|
|
98
|
+
borderRadius: 4,
|
|
99
|
+
padding: 16,
|
|
100
|
+
backgroundColor: 'blue',
|
|
101
|
+
});
|
|
102
|
+
});
|
|
103
|
+
});
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
import { describe, it, expect } from '@jest/globals';
|
|
2
|
+
import { combineStyles, conditionalStyle, responsiveStyle } from '../helpers';
|
|
3
|
+
|
|
4
|
+
describe('helpers', () => {
|
|
5
|
+
describe('combineStyles', () => {
|
|
6
|
+
it('should combine multiple styles', () => {
|
|
7
|
+
const style1 = { backgroundColor: 'red' };
|
|
8
|
+
const style2 = { padding: 10 };
|
|
9
|
+
const style3 = { margin: 5 };
|
|
10
|
+
|
|
11
|
+
const result = combineStyles(style1, style2, style3);
|
|
12
|
+
|
|
13
|
+
expect(result).toEqual({
|
|
14
|
+
backgroundColor: 'red',
|
|
15
|
+
padding: 10,
|
|
16
|
+
margin: 5,
|
|
17
|
+
});
|
|
18
|
+
});
|
|
19
|
+
|
|
20
|
+
it('should ignore undefined, null, and false styles', () => {
|
|
21
|
+
const style1 = { backgroundColor: 'red' };
|
|
22
|
+
const style2 = undefined;
|
|
23
|
+
const style3 = null;
|
|
24
|
+
const style4 = false;
|
|
25
|
+
const style5 = { padding: 10 };
|
|
26
|
+
|
|
27
|
+
const result = combineStyles(style1, style2, style3, style4, style5);
|
|
28
|
+
|
|
29
|
+
expect(result).toEqual({
|
|
30
|
+
backgroundColor: 'red',
|
|
31
|
+
padding: 10,
|
|
32
|
+
});
|
|
33
|
+
});
|
|
34
|
+
|
|
35
|
+
it('should return empty object when no styles provided', () => {
|
|
36
|
+
const result = combineStyles();
|
|
37
|
+
|
|
38
|
+
expect(result).toEqual({});
|
|
39
|
+
});
|
|
40
|
+
|
|
41
|
+
it('should handle empty array', () => {
|
|
42
|
+
const result = combineStyles();
|
|
43
|
+
|
|
44
|
+
expect(result).toEqual({});
|
|
45
|
+
});
|
|
46
|
+
});
|
|
47
|
+
|
|
48
|
+
describe('conditionalStyle', () => {
|
|
49
|
+
it('should return trueStyle when condition is true', () => {
|
|
50
|
+
const trueStyle = { backgroundColor: 'red' };
|
|
51
|
+
const falseStyle = { backgroundColor: 'blue' };
|
|
52
|
+
|
|
53
|
+
const result = conditionalStyle(true, trueStyle, falseStyle);
|
|
54
|
+
|
|
55
|
+
expect(result).toEqual(trueStyle);
|
|
56
|
+
});
|
|
57
|
+
|
|
58
|
+
it('should return falseStyle when condition is false', () => {
|
|
59
|
+
const trueStyle = { backgroundColor: 'red' };
|
|
60
|
+
const falseStyle = { backgroundColor: 'blue' };
|
|
61
|
+
|
|
62
|
+
const result = conditionalStyle(false, trueStyle, falseStyle);
|
|
63
|
+
|
|
64
|
+
expect(result).toEqual(falseStyle);
|
|
65
|
+
});
|
|
66
|
+
|
|
67
|
+
it('should return undefined when condition is false and no falseStyle provided', () => {
|
|
68
|
+
const trueStyle = { backgroundColor: 'red' };
|
|
69
|
+
|
|
70
|
+
const result = conditionalStyle(false, trueStyle);
|
|
71
|
+
|
|
72
|
+
expect(result).toBeUndefined();
|
|
73
|
+
});
|
|
74
|
+
});
|
|
75
|
+
|
|
76
|
+
describe('responsiveStyle', () => {
|
|
77
|
+
it('should return small style (current implementation)', () => {
|
|
78
|
+
const small = { padding: 8 };
|
|
79
|
+
const medium = { padding: 12 };
|
|
80
|
+
const large = { padding: 16 };
|
|
81
|
+
|
|
82
|
+
const result = responsiveStyle(small, medium, large);
|
|
83
|
+
|
|
84
|
+
expect(result).toEqual(small);
|
|
85
|
+
});
|
|
86
|
+
|
|
87
|
+
it('should work with only small style', () => {
|
|
88
|
+
const small = { padding: 8 };
|
|
89
|
+
|
|
90
|
+
const result = responsiveStyle(small);
|
|
91
|
+
|
|
92
|
+
expect(result).toEqual(small);
|
|
93
|
+
});
|
|
94
|
+
});
|
|
95
|
+
});
|
|
@@ -1,27 +1,36 @@
|
|
|
1
1
|
import { Style, VariantConfig, VariantProps, createVariants } from './core';
|
|
2
2
|
|
|
3
|
-
export
|
|
3
|
+
export interface CompoundVariant<T extends Record<string, Record<string, Style>>> {
|
|
4
4
|
conditions: Partial<VariantProps<T>>;
|
|
5
5
|
style: Style;
|
|
6
|
-
}
|
|
6
|
+
}
|
|
7
7
|
|
|
8
|
-
export
|
|
8
|
+
export interface AdvancedVariantConfig<T extends Record<string, Record<string, Style>>>
|
|
9
|
+
extends VariantConfig<T> {
|
|
9
10
|
compoundVariants?: CompoundVariant<T>[];
|
|
10
|
-
}
|
|
11
|
+
}
|
|
11
12
|
|
|
12
|
-
export function createAdvancedVariants<T extends Record<string,
|
|
13
|
+
export function createAdvancedVariants<T extends Record<string, Record<string, Style>>>(
|
|
14
|
+
config: AdvancedVariantConfig<T>
|
|
15
|
+
) {
|
|
13
16
|
const baseVariantFn = createVariants(config);
|
|
14
17
|
|
|
15
|
-
return
|
|
18
|
+
return (props: VariantProps<T> = {}): Style => {
|
|
16
19
|
let result = baseVariantFn(props);
|
|
17
20
|
|
|
21
|
+
if (__DEV__) {
|
|
22
|
+
console.log('[Design System] Creating advanced variant with compound conditions');
|
|
23
|
+
}
|
|
24
|
+
|
|
18
25
|
if (config.compoundVariants) {
|
|
19
|
-
|
|
26
|
+
config.compoundVariants.forEach(compound => {
|
|
20
27
|
const conditionsMet = Object.entries(compound.conditions).every(
|
|
21
28
|
([key, value]) => props[key as keyof T] === value
|
|
22
29
|
);
|
|
23
|
-
if (conditionsMet)
|
|
24
|
-
|
|
30
|
+
if (conditionsMet) {
|
|
31
|
+
result = { ...result, ...compound.style };
|
|
32
|
+
}
|
|
33
|
+
});
|
|
25
34
|
}
|
|
26
35
|
|
|
27
36
|
return result;
|
|
@@ -1,39 +1,49 @@
|
|
|
1
1
|
import { ViewStyle, TextStyle, ImageStyle } from 'react-native';
|
|
2
2
|
|
|
3
|
-
type Style = ViewStyle | TextStyle | ImageStyle;
|
|
3
|
+
export type Style = ViewStyle | TextStyle | ImageStyle;
|
|
4
4
|
|
|
5
|
-
export
|
|
5
|
+
export interface VariantConfig<T extends Record<string, Record<string, Style>>> {
|
|
6
6
|
base?: Style;
|
|
7
7
|
variants?: T;
|
|
8
|
-
defaultVariants?: { [K in keyof T]
|
|
9
|
-
}
|
|
8
|
+
defaultVariants?: Partial<{ [K in keyof T]: keyof T[K] }>;
|
|
9
|
+
}
|
|
10
10
|
|
|
11
|
-
export type VariantProps<T extends Record<string,
|
|
11
|
+
export type VariantProps<T extends Record<string, Record<string, Style>>> = {
|
|
12
12
|
[K in keyof T]?: keyof T[K];
|
|
13
13
|
};
|
|
14
14
|
|
|
15
|
-
export function createVariants<T extends Record<string,
|
|
16
|
-
|
|
15
|
+
export function createVariants<T extends Record<string, Record<string, Style>>>(
|
|
16
|
+
config: VariantConfig<T>
|
|
17
|
+
) {
|
|
18
|
+
return (props: VariantProps<T> = {}): Style => {
|
|
17
19
|
let result: Style = { ...config.base };
|
|
18
20
|
|
|
21
|
+
if (__DEV__) {
|
|
22
|
+
console.log('[Design System] Creating variant with props:', props);
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
// Apply default variants
|
|
19
26
|
if (config.defaultVariants) {
|
|
20
|
-
|
|
27
|
+
Object.entries(config.defaultVariants).forEach(([variantKey, defaultValue]) => {
|
|
21
28
|
if (config.variants?.[variantKey] && defaultValue) {
|
|
22
29
|
const variantStyle = config.variants[variantKey][defaultValue as string];
|
|
23
|
-
if (variantStyle)
|
|
30
|
+
if (variantStyle) {
|
|
31
|
+
result = { ...result, ...variantStyle };
|
|
32
|
+
}
|
|
24
33
|
}
|
|
25
|
-
}
|
|
34
|
+
});
|
|
26
35
|
}
|
|
27
36
|
|
|
28
|
-
|
|
37
|
+
// Apply props variants
|
|
38
|
+
Object.entries(props).forEach(([variantKey, variantValue]) => {
|
|
29
39
|
if (config.variants?.[variantKey] && variantValue) {
|
|
30
40
|
const variantStyle = config.variants[variantKey][variantValue as string];
|
|
31
|
-
if (variantStyle)
|
|
41
|
+
if (variantStyle) {
|
|
42
|
+
result = { ...result, ...variantStyle };
|
|
43
|
+
}
|
|
32
44
|
}
|
|
33
|
-
}
|
|
45
|
+
});
|
|
34
46
|
|
|
35
47
|
return result;
|
|
36
48
|
};
|
|
37
49
|
}
|
|
38
|
-
|
|
39
|
-
export type { Style };
|
|
@@ -1,13 +1,34 @@
|
|
|
1
1
|
import { Style } from './core';
|
|
2
2
|
|
|
3
|
-
export function combineStyles<T extends Style>(
|
|
4
|
-
|
|
3
|
+
export function combineStyles<T extends Style>(
|
|
4
|
+
...styles: (T | undefined | null | false)[]
|
|
5
|
+
): T {
|
|
6
|
+
return styles.reduce<T>(
|
|
7
|
+
(acc, style) => (style ? { ...acc, ...style } : acc),
|
|
8
|
+
{} as T
|
|
9
|
+
);
|
|
5
10
|
}
|
|
6
11
|
|
|
7
|
-
export function conditionalStyle<T extends Style>(
|
|
12
|
+
export function conditionalStyle<T extends Style>(
|
|
13
|
+
condition: boolean,
|
|
14
|
+
trueStyle: T,
|
|
15
|
+
falseStyle?: T
|
|
16
|
+
): T | undefined {
|
|
17
|
+
if (__DEV__) {
|
|
18
|
+
console.log('[Design System] Conditional style applied:', condition);
|
|
19
|
+
}
|
|
8
20
|
return condition ? trueStyle : falseStyle;
|
|
9
21
|
}
|
|
10
22
|
|
|
11
|
-
export function responsiveStyle<T extends Style>(
|
|
23
|
+
export function responsiveStyle<T extends Style>(
|
|
24
|
+
small: T,
|
|
25
|
+
medium?: T,
|
|
26
|
+
large?: T
|
|
27
|
+
): T {
|
|
28
|
+
if (__DEV__) {
|
|
29
|
+
console.log('[Design System] Responsive style - using small breakpoint');
|
|
30
|
+
}
|
|
31
|
+
void medium;
|
|
32
|
+
void large;
|
|
12
33
|
return small;
|
|
13
34
|
}
|