@umituz/react-native-design-system 2.6.82 → 2.6.83
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 +5 -1
- package/src/atoms/GlassView/GlassView.tsx +52 -0
- package/src/atoms/GlassView/index.ts +1 -0
- package/src/atoms/index.ts +3 -0
- package/src/molecules/GlowingCard/GlowingCard.tsx +74 -0
- package/src/molecules/GlowingCard/index.ts +1 -0
- package/src/molecules/index.ts +3 -0
- package/src/theme/core/CustomColors.ts +43 -1
- package/src/theme/infrastructure/providers/DesignSystemProvider.tsx +34 -12
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@umituz/react-native-design-system",
|
|
3
|
-
"version": "2.6.
|
|
3
|
+
"version": "2.6.83",
|
|
4
4
|
"description": "Universal design system for React Native apps - Consolidated package with atoms, molecules, organisms, theme, typography, responsive and safe area utilities",
|
|
5
5
|
"main": "./src/index.ts",
|
|
6
6
|
"types": "./src/index.ts",
|
|
@@ -60,9 +60,11 @@
|
|
|
60
60
|
"@umituz/react-native-storage": "*",
|
|
61
61
|
"@umituz/react-native-uuid": "*",
|
|
62
62
|
"expo-application": ">=5.0.0",
|
|
63
|
+
"expo-blur": ">=13.0.0",
|
|
63
64
|
"expo-clipboard": ">=8.0.0",
|
|
64
65
|
"expo-crypto": ">=13.0.0",
|
|
65
66
|
"expo-device": ">=5.0.0",
|
|
67
|
+
"expo-font": ">=12.0.0",
|
|
66
68
|
"expo-linear-gradient": ">=15.0.0",
|
|
67
69
|
"expo-sharing": ">=12.0.0",
|
|
68
70
|
"react": ">=19.0.0",
|
|
@@ -110,10 +112,12 @@
|
|
|
110
112
|
"eslint-plugin-react-hooks": "^7.0.1",
|
|
111
113
|
"eslint-plugin-react-native": "^5.0.0",
|
|
112
114
|
"expo-application": "~5.9.1",
|
|
115
|
+
"expo-blur": "~14.0.0",
|
|
113
116
|
"expo-clipboard": "~8.0.7",
|
|
114
117
|
"expo-crypto": "~14.0.0",
|
|
115
118
|
"expo-device": "~7.0.2",
|
|
116
119
|
"expo-file-system": "^19.0.21",
|
|
120
|
+
"expo-font": "~13.0.0",
|
|
117
121
|
"expo-haptics": "~14.0.0",
|
|
118
122
|
"expo-linear-gradient": "~15.0.7",
|
|
119
123
|
"expo-localization": "~16.0.1",
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import { StyleSheet, ViewStyle, StyleProp } from 'react-native';
|
|
3
|
+
import { BlurView, BlurTint } from 'expo-blur';
|
|
4
|
+
import { useDesignSystemTheme } from '../../theme';
|
|
5
|
+
|
|
6
|
+
export interface GlassViewProps {
|
|
7
|
+
children?: React.ReactNode;
|
|
8
|
+
style?: StyleProp<ViewStyle>;
|
|
9
|
+
intensity?: number;
|
|
10
|
+
tint?: BlurTint;
|
|
11
|
+
experimentalBlurMethod?: 'dimezisBlurView' | 'none';
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* GlassView
|
|
16
|
+
*
|
|
17
|
+
* A wrapper around Expo BlurView to create glassmorphism effects.
|
|
18
|
+
* Automatically adapts to current theme unless overridden.
|
|
19
|
+
*/
|
|
20
|
+
export const GlassView: React.FC<GlassViewProps> = ({
|
|
21
|
+
children,
|
|
22
|
+
style,
|
|
23
|
+
intensity = 50,
|
|
24
|
+
tint,
|
|
25
|
+
experimentalBlurMethod,
|
|
26
|
+
|
|
27
|
+
}) => {
|
|
28
|
+
const { themeMode } = useDesignSystemTheme();
|
|
29
|
+
const isDark = themeMode === 'dark';
|
|
30
|
+
|
|
31
|
+
// Default tint based on theme if not provided
|
|
32
|
+
// We explicitly cast the default strings to BlurTint to satisfy TS
|
|
33
|
+
const defaultTint = (isDark ? 'dark' : 'light') as BlurTint;
|
|
34
|
+
const resolvedTint = tint || defaultTint;
|
|
35
|
+
|
|
36
|
+
return (
|
|
37
|
+
<BlurView
|
|
38
|
+
intensity={intensity}
|
|
39
|
+
tint={resolvedTint}
|
|
40
|
+
style={[styles.container, style]}
|
|
41
|
+
experimentalBlurMethod={experimentalBlurMethod}
|
|
42
|
+
>
|
|
43
|
+
{children}
|
|
44
|
+
</BlurView>
|
|
45
|
+
);
|
|
46
|
+
};
|
|
47
|
+
|
|
48
|
+
const styles = StyleSheet.create({
|
|
49
|
+
container: {
|
|
50
|
+
overflow: 'hidden',
|
|
51
|
+
},
|
|
52
|
+
});
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from './GlassView';
|
package/src/atoms/index.ts
CHANGED
|
@@ -116,3 +116,6 @@ export { AtomicStatusBar, type AtomicStatusBarProps } from './status-bar';
|
|
|
116
116
|
|
|
117
117
|
// Keyboard Avoiding
|
|
118
118
|
export { AtomicKeyboardAvoidingView, type AtomicKeyboardAvoidingViewProps } from './AtomicKeyboardAvoidingView';
|
|
119
|
+
|
|
120
|
+
// GlassView
|
|
121
|
+
export { GlassView, type GlassViewProps } from './GlassView';
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import { View, StyleSheet, StyleProp, ViewStyle, Pressable, GestureResponderEvent } from 'react-native';
|
|
3
|
+
import { useAppDesignTokens } from '../../theme';
|
|
4
|
+
|
|
5
|
+
export interface GlowingCardProps {
|
|
6
|
+
children: React.ReactNode;
|
|
7
|
+
glowColor?: string;
|
|
8
|
+
intensity?: number; // 0 to 1, default 1
|
|
9
|
+
style?: StyleProp<ViewStyle>;
|
|
10
|
+
onPress?: (event: GestureResponderEvent) => void;
|
|
11
|
+
testID?: string;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* GlowingCard
|
|
16
|
+
*
|
|
17
|
+
* A card component with a neon-like glow effect using shadows.
|
|
18
|
+
*/
|
|
19
|
+
export const GlowingCard: React.FC<GlowingCardProps> = ({
|
|
20
|
+
children,
|
|
21
|
+
glowColor,
|
|
22
|
+
intensity = 1,
|
|
23
|
+
style,
|
|
24
|
+
onPress,
|
|
25
|
+
testID,
|
|
26
|
+
}) => {
|
|
27
|
+
const tokens = useAppDesignTokens();
|
|
28
|
+
const resolvedColor = glowColor || tokens.colors.primary;
|
|
29
|
+
|
|
30
|
+
const shadowStyle: ViewStyle = {
|
|
31
|
+
shadowColor: resolvedColor,
|
|
32
|
+
shadowOffset: { width: 0, height: 0 },
|
|
33
|
+
shadowOpacity: 0.6 * intensity,
|
|
34
|
+
shadowRadius: 10 * intensity,
|
|
35
|
+
elevation: 8 * intensity, // Android elevation
|
|
36
|
+
backgroundColor: tokens.colors.surface, // Ensure bg is solid or it looks weird
|
|
37
|
+
borderRadius: tokens.borders.radius.md,
|
|
38
|
+
borderColor: resolvedColor,
|
|
39
|
+
borderWidth: 1,
|
|
40
|
+
};
|
|
41
|
+
|
|
42
|
+
const containerStyle = [
|
|
43
|
+
styles.container,
|
|
44
|
+
shadowStyle,
|
|
45
|
+
style,
|
|
46
|
+
];
|
|
47
|
+
|
|
48
|
+
if (onPress) {
|
|
49
|
+
return (
|
|
50
|
+
<Pressable
|
|
51
|
+
style={({ pressed }) => [
|
|
52
|
+
containerStyle,
|
|
53
|
+
pressed && { opacity: 0.9, transform: [{ scale: 0.98 }] }
|
|
54
|
+
]}
|
|
55
|
+
onPress={onPress}
|
|
56
|
+
testID={testID}
|
|
57
|
+
>
|
|
58
|
+
{children}
|
|
59
|
+
</Pressable>
|
|
60
|
+
);
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
return (
|
|
64
|
+
<View style={containerStyle} testID={testID}>
|
|
65
|
+
{children}
|
|
66
|
+
</View>
|
|
67
|
+
);
|
|
68
|
+
};
|
|
69
|
+
|
|
70
|
+
const styles = StyleSheet.create({
|
|
71
|
+
container: {
|
|
72
|
+
overflow: 'visible', // Needed for shadow
|
|
73
|
+
},
|
|
74
|
+
});
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from './GlowingCard';
|
package/src/molecules/index.ts
CHANGED
|
@@ -22,6 +22,17 @@ export interface CustomThemeColors {
|
|
|
22
22
|
accentDark?: string;
|
|
23
23
|
buttonPrimary?: string;
|
|
24
24
|
buttonSecondary?: string;
|
|
25
|
+
|
|
26
|
+
// Background overrides
|
|
27
|
+
backgroundPrimary?: string;
|
|
28
|
+
backgroundSecondary?: string;
|
|
29
|
+
surface?: string;
|
|
30
|
+
surfaceVariant?: string;
|
|
31
|
+
|
|
32
|
+
// Text overrides
|
|
33
|
+
textPrimary?: string;
|
|
34
|
+
textSecondary?: string;
|
|
35
|
+
textTertiary?: string;
|
|
25
36
|
}
|
|
26
37
|
|
|
27
38
|
/**
|
|
@@ -111,6 +122,37 @@ export const applyCustomColors = (
|
|
|
111
122
|
result.buttonSecondary = customColors.buttonSecondary;
|
|
112
123
|
}
|
|
113
124
|
|
|
125
|
+
// Apply background override
|
|
126
|
+
if (customColors.backgroundPrimary) {
|
|
127
|
+
result.backgroundPrimary = customColors.backgroundPrimary;
|
|
128
|
+
result.background = customColors.backgroundPrimary; // Alias
|
|
129
|
+
}
|
|
130
|
+
if (customColors.backgroundSecondary) {
|
|
131
|
+
result.backgroundSecondary = customColors.backgroundSecondary;
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
// Apply surface overrides
|
|
135
|
+
if (customColors.surface) {
|
|
136
|
+
result.surface = customColors.surface;
|
|
137
|
+
result.card = customColors.surface; // Alias
|
|
138
|
+
result.cardBackground = customColors.surface; // Alias
|
|
139
|
+
}
|
|
140
|
+
if (customColors.surfaceVariant) {
|
|
141
|
+
result.surfaceVariant = customColors.surfaceVariant;
|
|
142
|
+
result.surfaceSecondary = customColors.surfaceVariant; // Alias
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
// Apply text overrides
|
|
146
|
+
if (customColors.textPrimary) {
|
|
147
|
+
result.textPrimary = customColors.textPrimary;
|
|
148
|
+
result.text = customColors.textPrimary; // Alias
|
|
149
|
+
}
|
|
150
|
+
if (customColors.textSecondary) {
|
|
151
|
+
result.textSecondary = customColors.textSecondary;
|
|
152
|
+
}
|
|
153
|
+
if (customColors.textTertiary) {
|
|
154
|
+
result.textTertiary = customColors.textTertiary;
|
|
155
|
+
}
|
|
156
|
+
|
|
114
157
|
return result as ColorPalette;
|
|
115
158
|
};
|
|
116
|
-
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import React, { useEffect, useState, ReactNode } from 'react';
|
|
2
2
|
import { ActivityIndicator, View, StyleSheet } from 'react-native';
|
|
3
3
|
import { GestureHandlerRootView } from 'react-native-gesture-handler';
|
|
4
|
+
import { useFonts } from 'expo-font';
|
|
4
5
|
import { SafeAreaProvider, initialWindowMetrics } from '../../../safe-area';
|
|
5
6
|
import { useThemeStore } from '../stores/themeStore';
|
|
6
7
|
import { useDesignSystemTheme } from '../globalThemeStore';
|
|
@@ -16,6 +17,8 @@ interface DesignSystemProviderProps {
|
|
|
16
17
|
children: ReactNode;
|
|
17
18
|
/** Custom theme colors to override defaults */
|
|
18
19
|
customColors?: CustomThemeColors;
|
|
20
|
+
/** Custom fonts to load (name -> source map) */
|
|
21
|
+
fonts?: Record<string, any>;
|
|
19
22
|
/** Show loading indicator while initializing (default: true) */
|
|
20
23
|
showLoadingIndicator?: boolean;
|
|
21
24
|
/** Splash screen configuration (used when showLoadingIndicator is true) */
|
|
@@ -35,6 +38,7 @@ interface DesignSystemProviderProps {
|
|
|
35
38
|
export const DesignSystemProvider: React.FC<DesignSystemProviderProps> = ({
|
|
36
39
|
children,
|
|
37
40
|
customColors,
|
|
41
|
+
fonts,
|
|
38
42
|
showLoadingIndicator = true,
|
|
39
43
|
splashConfig,
|
|
40
44
|
loadingComponent,
|
|
@@ -44,6 +48,10 @@ export const DesignSystemProvider: React.FC<DesignSystemProviderProps> = ({
|
|
|
44
48
|
// ALL HOOKS MUST BE AT THE TOP (Rules of Hooks)
|
|
45
49
|
const [isInitialized, setIsInitialized] = useState(false);
|
|
46
50
|
const [minDisplayTimeMet, setMinDisplayTimeMet] = useState(false);
|
|
51
|
+
|
|
52
|
+
// Load fonts if provided
|
|
53
|
+
const [fontsLoaded, fontError] = fonts ? useFonts(fonts) : [true, null];
|
|
54
|
+
|
|
47
55
|
const initialize = useThemeStore((state) => state.initialize);
|
|
48
56
|
const setCustomColors = useDesignSystemTheme((state) => state.setCustomColors);
|
|
49
57
|
|
|
@@ -66,44 +74,58 @@ export const DesignSystemProvider: React.FC<DesignSystemProviderProps> = ({
|
|
|
66
74
|
// Initialize theme store
|
|
67
75
|
initialize()
|
|
68
76
|
.then(() => {
|
|
69
|
-
if (__DEV__) console.log('[DesignSystemProvider]
|
|
77
|
+
if (__DEV__) console.log('[DesignSystemProvider] Theme initialized successfully');
|
|
70
78
|
setIsInitialized(true);
|
|
71
|
-
if (__DEV__) console.log('[DesignSystemProvider] State updated - calling onInitialized callback');
|
|
72
|
-
onInitialized?.();
|
|
73
79
|
})
|
|
74
80
|
.catch((error) => {
|
|
75
81
|
if (__DEV__) console.error('[DesignSystemProvider] Initialization failed:', error);
|
|
76
|
-
if (__DEV__) console.log('[DesignSystemProvider] Error occurred - setting isInitialized to true anyway');
|
|
77
82
|
setIsInitialized(true); // Still render app even on error
|
|
78
83
|
onError?.(error);
|
|
79
84
|
});
|
|
80
85
|
|
|
81
86
|
return () => clearTimeout(displayTimer);
|
|
82
|
-
}, [initialize, customColors, setCustomColors,
|
|
87
|
+
}, [initialize, customColors, setCustomColors, onError]);
|
|
88
|
+
|
|
89
|
+
// Handle initialization completion when both theme and fonts are ready
|
|
90
|
+
useEffect(() => {
|
|
91
|
+
if (isInitialized && fontsLoaded && minDisplayTimeMet) {
|
|
92
|
+
if (__DEV__) console.log('[DesignSystemProvider] All systems ready - calling onInitialized');
|
|
93
|
+
onInitialized?.();
|
|
94
|
+
}
|
|
95
|
+
}, [isInitialized, fontsLoaded, minDisplayTimeMet, onInitialized]);
|
|
96
|
+
|
|
97
|
+
// Handle font errors
|
|
98
|
+
useEffect(() => {
|
|
99
|
+
if (fontError) {
|
|
100
|
+
if (__DEV__) console.error('[DesignSystemProvider] Font loading failed:', fontError);
|
|
101
|
+
onError?.(fontError);
|
|
102
|
+
}
|
|
103
|
+
}, [fontError, onError]);
|
|
83
104
|
|
|
84
105
|
// ALL HOOKS ABOVE - NOW SAFE TO USE OTHER LOGIC
|
|
85
106
|
|
|
86
107
|
if (__DEV__) {
|
|
87
108
|
console.log('[DesignSystemProvider] Component render:', {
|
|
88
109
|
isInitialized,
|
|
110
|
+
fontsLoaded,
|
|
89
111
|
minDisplayTimeMet,
|
|
90
112
|
showLoadingIndicator,
|
|
91
113
|
hasSplashConfig: !!splashConfig,
|
|
92
|
-
splashConfigKeys: splashConfig ? Object.keys(splashConfig) : [],
|
|
93
114
|
});
|
|
94
115
|
}
|
|
95
116
|
|
|
96
117
|
const renderContent = () => {
|
|
97
|
-
|
|
118
|
+
// Show splash if:
|
|
119
|
+
// 1. Loading indicator is enabled AND
|
|
120
|
+
// 2. Either theme not init OR fonts not loaded OR min time not met
|
|
121
|
+
const shouldShowSplash = showLoadingIndicator && (!isInitialized || !fontsLoaded || !minDisplayTimeMet);
|
|
98
122
|
|
|
99
123
|
if (__DEV__) {
|
|
100
124
|
console.log('[DesignSystemProvider] renderContent:', {
|
|
101
|
-
showLoadingIndicator,
|
|
102
|
-
isInitialized,
|
|
103
|
-
minDisplayTimeMet,
|
|
104
125
|
shouldShowSplash,
|
|
105
|
-
|
|
106
|
-
|
|
126
|
+
isInitialized,
|
|
127
|
+
fontsLoaded,
|
|
128
|
+
minDisplayTimeMet
|
|
107
129
|
});
|
|
108
130
|
}
|
|
109
131
|
|