@umituz/react-native-design-system 2.10.0 → 2.11.0
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
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@umituz/react-native-design-system",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.11.0",
|
|
4
4
|
"description": "Universal design system for React Native apps - Consolidated package with atoms, molecules, organisms, theme, typography, responsive, safe area, exception, infinite scroll, UUID, image, timezone, offline, onboarding, and loading utilities",
|
|
5
5
|
"main": "./src/index.ts",
|
|
6
6
|
"types": "./src/index.ts",
|
|
@@ -158,5 +158,8 @@
|
|
|
158
158
|
"src",
|
|
159
159
|
"README.md",
|
|
160
160
|
"LICENSE"
|
|
161
|
-
]
|
|
161
|
+
],
|
|
162
|
+
"dependencies": {
|
|
163
|
+
"@umituz/react-native-design-system": "^2.10.1"
|
|
164
|
+
}
|
|
162
165
|
}
|
package/src/exports/atoms.ts
CHANGED
|
@@ -70,4 +70,10 @@ export {
|
|
|
70
70
|
type GlassViewProps,
|
|
71
71
|
AtomicImage,
|
|
72
72
|
type AtomicImageProps,
|
|
73
|
+
// Icon Registry (for custom icon renderers)
|
|
74
|
+
IconProvider,
|
|
75
|
+
useIconRenderer,
|
|
76
|
+
useHasCustomIconRenderer,
|
|
77
|
+
type IconRenderProps,
|
|
78
|
+
type IconRenderer,
|
|
73
79
|
} from '../atoms';
|
|
@@ -1,38 +1,126 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Custom Colors Types
|
|
3
|
-
*
|
|
3
|
+
*
|
|
4
4
|
* Types for custom theme color overrides
|
|
5
|
+
*
|
|
6
|
+
* ARCHITECTURE:
|
|
7
|
+
* - Apps provide CustomThemeColors to DesignSystemProvider
|
|
8
|
+
* - These colors override the default palette
|
|
9
|
+
* - Supports both light and dark mode overrides
|
|
10
|
+
* - Apps control their own brand colors completely
|
|
11
|
+
*
|
|
12
|
+
* BEST PRACTICES (based on Shopify Restyle, React Native Paper, Tamagui):
|
|
13
|
+
* - All color keys are optional - only override what you need
|
|
14
|
+
* - Provide separate light/dark palettes for proper theme switching
|
|
15
|
+
* - Use semantic color names (primary, surface, etc.) not literal colors
|
|
5
16
|
*/
|
|
6
17
|
|
|
7
18
|
import type { ColorPalette } from './ColorPalette';
|
|
8
19
|
import { isValidHexColor } from './colors/ColorUtils';
|
|
9
20
|
|
|
10
21
|
/**
|
|
11
|
-
*
|
|
22
|
+
* Complete custom theme colors - can override ANY color in the palette
|
|
23
|
+
* All properties are optional - apps only provide what they want to customize
|
|
12
24
|
*/
|
|
13
25
|
export interface CustomThemeColors {
|
|
26
|
+
// PRIMARY BRAND COLORS
|
|
14
27
|
primary?: string;
|
|
15
28
|
primaryLight?: string;
|
|
16
29
|
primaryDark?: string;
|
|
30
|
+
|
|
31
|
+
// SECONDARY COLORS
|
|
17
32
|
secondary?: string;
|
|
18
33
|
secondaryLight?: string;
|
|
19
34
|
secondaryDark?: string;
|
|
35
|
+
|
|
36
|
+
// ACCENT COLORS
|
|
20
37
|
accent?: string;
|
|
21
38
|
accentLight?: string;
|
|
22
39
|
accentDark?: string;
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
40
|
+
|
|
41
|
+
// ON-COLORS (text on colored backgrounds - CRITICAL for contrast)
|
|
42
|
+
onPrimary?: string;
|
|
43
|
+
onSecondary?: string;
|
|
44
|
+
onSuccess?: string;
|
|
45
|
+
onError?: string;
|
|
46
|
+
onWarning?: string;
|
|
47
|
+
onInfo?: string;
|
|
48
|
+
onSurface?: string;
|
|
49
|
+
onBackground?: string;
|
|
50
|
+
onSurfaceDisabled?: string;
|
|
51
|
+
onSurfaceVariant?: string;
|
|
52
|
+
|
|
53
|
+
// CONTAINER COLORS
|
|
54
|
+
primaryContainer?: string;
|
|
55
|
+
onPrimaryContainer?: string;
|
|
56
|
+
secondaryContainer?: string;
|
|
57
|
+
onSecondaryContainer?: string;
|
|
58
|
+
errorContainer?: string;
|
|
59
|
+
onErrorContainer?: string;
|
|
60
|
+
|
|
61
|
+
// OUTLINE COLORS
|
|
62
|
+
outline?: string;
|
|
63
|
+
outlineVariant?: string;
|
|
64
|
+
outlineDisabled?: string;
|
|
65
|
+
|
|
66
|
+
// SEMANTIC UI COLORS
|
|
67
|
+
success?: string;
|
|
68
|
+
successLight?: string;
|
|
69
|
+
successDark?: string;
|
|
70
|
+
error?: string;
|
|
71
|
+
errorLight?: string;
|
|
72
|
+
errorDark?: string;
|
|
73
|
+
warning?: string;
|
|
74
|
+
warningLight?: string;
|
|
75
|
+
warningDark?: string;
|
|
76
|
+
info?: string;
|
|
77
|
+
infoLight?: string;
|
|
78
|
+
infoDark?: string;
|
|
79
|
+
|
|
80
|
+
// SEMANTIC CONTAINER COLORS
|
|
81
|
+
successContainer?: string;
|
|
82
|
+
onSuccessContainer?: string;
|
|
83
|
+
warningContainer?: string;
|
|
84
|
+
onWarningContainer?: string;
|
|
85
|
+
infoContainer?: string;
|
|
86
|
+
onInfoContainer?: string;
|
|
87
|
+
|
|
88
|
+
// BACKGROUND COLORS
|
|
27
89
|
backgroundPrimary?: string;
|
|
28
90
|
backgroundSecondary?: string;
|
|
91
|
+
|
|
92
|
+
// SURFACE COLORS
|
|
29
93
|
surface?: string;
|
|
30
94
|
surfaceVariant?: string;
|
|
31
|
-
|
|
32
|
-
|
|
95
|
+
surfaceSecondary?: string;
|
|
96
|
+
surfaceDisabled?: string;
|
|
97
|
+
|
|
98
|
+
// TEXT COLORS
|
|
33
99
|
textPrimary?: string;
|
|
34
100
|
textSecondary?: string;
|
|
35
101
|
textTertiary?: string;
|
|
102
|
+
textDisabled?: string;
|
|
103
|
+
textInverse?: string;
|
|
104
|
+
|
|
105
|
+
// BORDER COLORS
|
|
106
|
+
border?: string;
|
|
107
|
+
borderLight?: string;
|
|
108
|
+
borderMedium?: string;
|
|
109
|
+
borderFocus?: string;
|
|
110
|
+
borderDisabled?: string;
|
|
111
|
+
|
|
112
|
+
// COMPONENT-SPECIFIC COLORS
|
|
113
|
+
buttonPrimary?: string;
|
|
114
|
+
buttonSecondary?: string;
|
|
115
|
+
inputBackground?: string;
|
|
116
|
+
inputBorder?: string;
|
|
117
|
+
cardBackground?: string;
|
|
118
|
+
|
|
119
|
+
// SPECIAL & UTILITY COLORS
|
|
120
|
+
transparent?: string;
|
|
121
|
+
black?: string;
|
|
122
|
+
white?: string;
|
|
123
|
+
modalOverlay?: string;
|
|
36
124
|
}
|
|
37
125
|
|
|
38
126
|
/**
|
|
@@ -54,8 +142,13 @@ export const validateCustomColors = (customColors: CustomThemeColors): boolean =
|
|
|
54
142
|
|
|
55
143
|
/**
|
|
56
144
|
* Apply custom colors to color palette
|
|
57
|
-
*
|
|
58
|
-
*
|
|
145
|
+
*
|
|
146
|
+
* This function takes a base palette and merges custom colors into it.
|
|
147
|
+
* Apps can override ANY color key - the system simply spreads custom values
|
|
148
|
+
* over the base palette.
|
|
149
|
+
*
|
|
150
|
+
* @param palette - Base color palette (light or dark)
|
|
151
|
+
* @param customColors - Custom colors to apply (partial override)
|
|
59
152
|
* @returns Color palette with custom colors applied
|
|
60
153
|
*/
|
|
61
154
|
export const applyCustomColors = (
|
|
@@ -68,90 +161,65 @@ export const applyCustomColors = (
|
|
|
68
161
|
|
|
69
162
|
// Validate custom colors
|
|
70
163
|
if (!validateCustomColors(customColors)) {
|
|
164
|
+
if (__DEV__) {
|
|
165
|
+
console.warn('[DesignSystem] Invalid custom colors provided - using default palette');
|
|
166
|
+
}
|
|
71
167
|
return palette;
|
|
72
168
|
}
|
|
73
169
|
|
|
170
|
+
// Start with base palette
|
|
74
171
|
const result: Partial<ColorPalette> = {
|
|
75
172
|
...palette,
|
|
76
173
|
};
|
|
77
174
|
|
|
78
|
-
// Apply custom
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
if (!customColors.buttonPrimary) {
|
|
82
|
-
result.buttonPrimary = customColors.primary;
|
|
83
|
-
}
|
|
84
|
-
}
|
|
85
|
-
if (customColors.primaryLight) {
|
|
86
|
-
result.primaryLight = customColors.primaryLight;
|
|
87
|
-
}
|
|
88
|
-
if (customColors.primaryDark) {
|
|
89
|
-
result.primaryDark = customColors.primaryDark;
|
|
90
|
-
}
|
|
175
|
+
// Apply ALL custom colors dynamically
|
|
176
|
+
// This approach allows any color key to be overridden
|
|
177
|
+
const colorKeys = Object.keys(customColors) as (keyof CustomThemeColors)[];
|
|
91
178
|
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
result
|
|
179
|
+
for (const key of colorKeys) {
|
|
180
|
+
const value = customColors[key];
|
|
181
|
+
if (value !== undefined) {
|
|
182
|
+
// Direct assignment for matching keys
|
|
183
|
+
(result as Record<string, string>)[key] = value;
|
|
97
184
|
}
|
|
98
185
|
}
|
|
99
|
-
if (customColors.secondaryLight) {
|
|
100
|
-
result.secondaryLight = customColors.secondaryLight;
|
|
101
|
-
}
|
|
102
|
-
if (customColors.secondaryDark) {
|
|
103
|
-
result.secondaryDark = customColors.secondaryDark;
|
|
104
|
-
}
|
|
105
186
|
|
|
106
|
-
// Apply
|
|
107
|
-
if
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
if (customColors.accentLight) {
|
|
111
|
-
result.accentLight = customColors.accentLight;
|
|
112
|
-
}
|
|
113
|
-
if (customColors.accentDark) {
|
|
114
|
-
result.accentDark = customColors.accentDark;
|
|
187
|
+
// Apply smart defaults and aliases
|
|
188
|
+
// Primary -> buttonPrimary (if buttonPrimary not explicitly set)
|
|
189
|
+
if (customColors.primary && !customColors.buttonPrimary) {
|
|
190
|
+
result.buttonPrimary = customColors.primary;
|
|
115
191
|
}
|
|
116
192
|
|
|
117
|
-
//
|
|
118
|
-
if (customColors.
|
|
119
|
-
result.
|
|
120
|
-
}
|
|
121
|
-
if (customColors.buttonSecondary) {
|
|
122
|
-
result.buttonSecondary = customColors.buttonSecondary;
|
|
193
|
+
// Secondary -> buttonSecondary (if buttonSecondary not explicitly set)
|
|
194
|
+
if (customColors.secondary && !customColors.buttonSecondary) {
|
|
195
|
+
result.buttonSecondary = customColors.secondary;
|
|
123
196
|
}
|
|
124
197
|
|
|
125
|
-
//
|
|
198
|
+
// Background aliases
|
|
126
199
|
if (customColors.backgroundPrimary) {
|
|
127
|
-
result.
|
|
128
|
-
result.background = customColors.backgroundPrimary; // Alias
|
|
129
|
-
}
|
|
130
|
-
if (customColors.backgroundSecondary) {
|
|
131
|
-
result.backgroundSecondary = customColors.backgroundSecondary;
|
|
200
|
+
result.background = customColors.backgroundPrimary;
|
|
132
201
|
}
|
|
133
|
-
|
|
134
|
-
//
|
|
202
|
+
|
|
203
|
+
// Surface aliases
|
|
135
204
|
if (customColors.surface) {
|
|
136
|
-
result.
|
|
137
|
-
result.
|
|
138
|
-
result.cardBackground = customColors.surface; // Alias
|
|
205
|
+
result.card = customColors.surface;
|
|
206
|
+
result.cardBackground = customColors.surface;
|
|
139
207
|
}
|
|
140
|
-
if (customColors.surfaceVariant) {
|
|
141
|
-
result.
|
|
142
|
-
result.surfaceSecondary = customColors.surfaceVariant; // Alias
|
|
208
|
+
if (customColors.surfaceVariant && !customColors.surfaceSecondary) {
|
|
209
|
+
result.surfaceSecondary = customColors.surfaceVariant;
|
|
143
210
|
}
|
|
144
|
-
|
|
145
|
-
//
|
|
211
|
+
|
|
212
|
+
// Text aliases
|
|
146
213
|
if (customColors.textPrimary) {
|
|
147
|
-
result.
|
|
148
|
-
result.text = customColors.textPrimary; // Alias
|
|
214
|
+
result.text = customColors.textPrimary;
|
|
149
215
|
}
|
|
150
|
-
|
|
151
|
-
|
|
216
|
+
|
|
217
|
+
// Input aliases
|
|
218
|
+
if (customColors.inputBorder && !result.inputBorder) {
|
|
219
|
+
result.inputBorder = customColors.inputBorder;
|
|
152
220
|
}
|
|
153
|
-
if (customColors.
|
|
154
|
-
result.
|
|
221
|
+
if (customColors.border && !customColors.inputBorder) {
|
|
222
|
+
result.inputBorder = customColors.border;
|
|
155
223
|
}
|
|
156
224
|
|
|
157
225
|
return result as ColorPalette;
|
|
@@ -4,7 +4,7 @@ import { GestureHandlerRootView } from 'react-native-gesture-handler';
|
|
|
4
4
|
import { useFonts } from 'expo-font';
|
|
5
5
|
import { SafeAreaProvider, initialWindowMetrics } from '../../../safe-area';
|
|
6
6
|
import { useThemeStore } from '../stores/themeStore';
|
|
7
|
-
import { useDesignSystemTheme } from '../globalThemeStore';
|
|
7
|
+
import { useDesignSystemTheme, type ThemeMode } from '../globalThemeStore';
|
|
8
8
|
import type { CustomThemeColors } from '../../core/CustomColors';
|
|
9
9
|
import { SplashScreen } from '../../../molecules/splash';
|
|
10
10
|
import type { SplashScreenProps } from '../../../molecules/splash/types';
|
|
@@ -14,8 +14,24 @@ import { IconProvider, type IconRenderer } from '../../../atoms/IconRegistry';
|
|
|
14
14
|
interface DesignSystemProviderProps {
|
|
15
15
|
/** App content */
|
|
16
16
|
children: ReactNode;
|
|
17
|
-
/**
|
|
17
|
+
/**
|
|
18
|
+
* Custom theme colors to override defaults
|
|
19
|
+
* Apps can override ANY color key from the palette
|
|
20
|
+
* @example
|
|
21
|
+
* customColors={{
|
|
22
|
+
* primary: '#FF6B6B',
|
|
23
|
+
* onPrimary: '#FFFFFF',
|
|
24
|
+
* backgroundPrimary: '#FFFFFF',
|
|
25
|
+
* textPrimary: '#1A1A1A',
|
|
26
|
+
* }}
|
|
27
|
+
*/
|
|
18
28
|
customColors?: CustomThemeColors;
|
|
29
|
+
/**
|
|
30
|
+
* Initial theme mode for the app
|
|
31
|
+
* Apps control whether they start in light or dark mode
|
|
32
|
+
* @default 'light'
|
|
33
|
+
*/
|
|
34
|
+
initialThemeMode?: ThemeMode;
|
|
19
35
|
/** Custom fonts to load (name -> source map) */
|
|
20
36
|
fonts?: Record<string, any>;
|
|
21
37
|
/** Show loading indicator while initializing (default: true) */
|
|
@@ -30,10 +46,10 @@ interface DesignSystemProviderProps {
|
|
|
30
46
|
onError?: (error: unknown) => void;
|
|
31
47
|
/**
|
|
32
48
|
* Custom icon renderer function
|
|
33
|
-
* Allows apps to use their own icon library
|
|
49
|
+
* Allows apps to use their own icon library
|
|
34
50
|
* @example
|
|
35
51
|
* iconRenderer={({ name, size, color }) => (
|
|
36
|
-
* <
|
|
52
|
+
* <LucideIcons name={name} size={size} color={color} />
|
|
37
53
|
* )}
|
|
38
54
|
*/
|
|
39
55
|
iconRenderer?: IconRenderer;
|
|
@@ -41,11 +57,22 @@ interface DesignSystemProviderProps {
|
|
|
41
57
|
|
|
42
58
|
/**
|
|
43
59
|
* DesignSystemProvider
|
|
44
|
-
*
|
|
60
|
+
*
|
|
61
|
+
* Main provider for the design system. Wraps your app and provides:
|
|
62
|
+
* - Theme (colors, spacing, typography)
|
|
63
|
+
* - Custom icon rendering
|
|
64
|
+
* - Safe area handling
|
|
65
|
+
* - Gesture handling
|
|
66
|
+
*
|
|
67
|
+
* ARCHITECTURE (based on Shopify Restyle, React Native Paper, Tamagui):
|
|
68
|
+
* - Apps provide customColors to override default palette
|
|
69
|
+
* - Apps set initialThemeMode to control light/dark mode
|
|
70
|
+
* - All design system components use these values via useAppDesignTokens
|
|
45
71
|
*/
|
|
46
72
|
export const DesignSystemProvider: React.FC<DesignSystemProviderProps> = ({
|
|
47
73
|
children,
|
|
48
74
|
customColors,
|
|
75
|
+
initialThemeMode = 'light', // Default to light mode (most common)
|
|
49
76
|
fonts,
|
|
50
77
|
showLoadingIndicator = true,
|
|
51
78
|
splashConfig,
|
|
@@ -55,12 +82,14 @@ export const DesignSystemProvider: React.FC<DesignSystemProviderProps> = ({
|
|
|
55
82
|
iconRenderer,
|
|
56
83
|
}: DesignSystemProviderProps) => {
|
|
57
84
|
const [isInitialized, setIsInitialized] = useState(false);
|
|
58
|
-
|
|
85
|
+
|
|
59
86
|
// Load fonts if provided
|
|
60
87
|
const [fontsLoaded, fontError] = fonts ? useFonts(fonts) : [true, null];
|
|
61
|
-
|
|
88
|
+
|
|
62
89
|
const initialize = useThemeStore((state) => state.initialize);
|
|
90
|
+
const setThemeMode = useThemeStore((state) => state.setThemeMode);
|
|
63
91
|
const setCustomColors = useDesignSystemTheme((state) => state.setCustomColors);
|
|
92
|
+
const setGlobalThemeMode = useDesignSystemTheme((state) => state.setThemeMode);
|
|
64
93
|
|
|
65
94
|
useEffect(() => {
|
|
66
95
|
// Apply custom colors if provided
|
|
@@ -68,16 +97,22 @@ export const DesignSystemProvider: React.FC<DesignSystemProviderProps> = ({
|
|
|
68
97
|
setCustomColors(customColors);
|
|
69
98
|
}
|
|
70
99
|
|
|
71
|
-
//
|
|
100
|
+
// Set initial theme mode from app
|
|
101
|
+
setGlobalThemeMode(initialThemeMode);
|
|
102
|
+
|
|
103
|
+
// Initialize theme store with the initial theme mode
|
|
72
104
|
initialize()
|
|
73
|
-
.then(() => {
|
|
105
|
+
.then(async () => {
|
|
106
|
+
// After initialization, set the theme mode from app config
|
|
107
|
+
// This overrides any persisted theme mode if app explicitly sets one
|
|
108
|
+
await setThemeMode(initialThemeMode);
|
|
74
109
|
setIsInitialized(true);
|
|
75
110
|
})
|
|
76
111
|
.catch((error) => {
|
|
77
112
|
setIsInitialized(true); // Still render app even on error
|
|
78
113
|
onError?.(error);
|
|
79
114
|
});
|
|
80
|
-
}, [initialize, customColors, setCustomColors, onError]);
|
|
115
|
+
}, [initialize, customColors, initialThemeMode, setCustomColors, setGlobalThemeMode, setThemeMode, onError]);
|
|
81
116
|
|
|
82
117
|
// Handle initialization completion when both theme and fonts are ready
|
|
83
118
|
useEffect(() => {
|