@umituz/react-native-splash 1.0.5 → 1.4.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/README.md CHANGED
@@ -1,17 +1,13 @@
1
1
  # @umituz/react-native-splash
2
2
 
3
- Generic splash screen for React Native apps with animations, gradients, and customizable branding. Follows SOLID, DRY, KISS principles - fully reusable across hundreds of apps.
3
+ Simple splash screen for React Native apps with logo, app name, and loading indicator. Minimal dependencies, no over-engineering.
4
4
 
5
5
  ## Features
6
6
 
7
- - ✅ **Generic & Reusable**: Works with any app, fully customizable via props
8
- - ✅ **Animations**: Smooth fade, scale, and slide animations
9
- - ✅ **Gradient Backgrounds**: Support for gradient or solid colors
10
- - ✅ **Customizable Branding**: Logo, app name, tagline, footer
11
- - ✅ **Loading Indicators**: Progress bar and loading text
12
- - ✅ **SOLID Principles**: Clean architecture, easy to extend
13
- - ✅ **DRY**: No code duplication, generic components
14
- - ✅ **KISS**: Simple API, easy to understand and use
7
+ - ✅ **Simple & Lightweight**: No animations, no gradients, just the essentials
8
+ - ✅ **Minimal Dependencies**: Only requires AtomicIcon and localization
9
+ - ✅ **Customizable**: Logo, app name, tagline, background color
10
+ - ✅ **Loading Indicator**: Built-in ActivityIndicator
15
11
  - ✅ **Type-Safe**: Full TypeScript support
16
12
 
17
13
  ## Installation
@@ -37,7 +33,7 @@ import { SplashScreen } from '@umituz/react-native-splash';
37
33
  />
38
34
  ```
39
35
 
40
- ### Advanced Example
36
+ ### With Custom Background
41
37
 
42
38
  ```typescript
43
39
  <SplashScreen
@@ -45,13 +41,8 @@ import { SplashScreen } from '@umituz/react-native-splash';
45
41
  tagline="Your tagline here"
46
42
  logo="Sparkles"
47
43
  backgroundColor="#3B82F6"
48
- gradientColors={['#3B82F6', '#8B5CF6']}
49
44
  loadingText="Loading..."
50
45
  showLoading={true}
51
- showProgressBar={true}
52
- footerText="Powered by"
53
- versionText="v1.0.0"
54
- animationDuration={2000}
55
46
  minimumDisplayTime={1500}
56
47
  onReady={handleReady}
57
48
  />
@@ -79,13 +70,8 @@ interface SplashOptions {
79
70
  tagline?: string;
80
71
  logo?: ReactNode | string;
81
72
  backgroundColor?: string;
82
- gradientColors?: string[];
83
73
  loadingText?: string;
84
74
  showLoading?: boolean;
85
- showProgressBar?: boolean;
86
- footerText?: string;
87
- versionText?: string;
88
- animationDuration?: number;
89
75
  minimumDisplayTime?: number;
90
76
  onReady?: () => void | Promise<void>;
91
77
  renderLogo?: () => ReactNode;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@umituz/react-native-splash",
3
- "version": "1.0.5",
3
+ "version": "1.4.0",
4
4
  "description": "Generic splash screen for React Native apps with animations, gradients, and customizable branding. SOLID, DRY, KISS principles applied.",
5
5
  "main": "./src/index.ts",
6
6
  "types": "./src/index.ts",
@@ -31,9 +31,11 @@
31
31
  "url": "https://github.com/umituz/react-native-splash"
32
32
  },
33
33
  "peerDependencies": {
34
+ "@umituz/react-native-design-system-atoms": "*",
35
+ "@umituz/react-native-design-system-responsive": "*",
34
36
  "@umituz/react-native-design-system-theme": "*",
35
37
  "@umituz/react-native-localization": "latest",
36
- "@umituz/react-native-design-system": "latest",
38
+ "expo-linear-gradient": "~14.0.0",
37
39
  "react": ">=18.2.0",
38
40
  "react-native": ">=0.74.0",
39
41
  "react-native-safe-area-context": "^5.0.0"
@@ -41,10 +43,11 @@
41
43
  "devDependencies": {
42
44
  "@types/react": "^18.2.45",
43
45
  "@types/react-native": "^0.73.0",
46
+ "@umituz/react-native-design-system-atoms": "*",
47
+ "@umituz/react-native-design-system-responsive": "*",
44
48
  "@umituz/react-native-design-system-theme": "*",
45
49
  "@umituz/react-native-localization": "latest",
46
- "@umituz/react-native-design-system": "latest",
47
- "react": "^18.2.0",
50
+ "react": ">=18.2.0",
48
51
  "react-native": "^0.74.0",
49
52
  "react-native-safe-area-context": "^5.6.0",
50
53
  "typescript": "^5.3.3"
@@ -27,10 +27,9 @@ export interface SplashOptions {
27
27
  logo?: ReactNode | string;
28
28
 
29
29
  /**
30
- * Background color or gradient colors
30
+ * Background color
31
31
  */
32
32
  backgroundColor?: string;
33
- gradientColors?: string[];
34
33
 
35
34
  /**
36
35
  * Loading text
@@ -42,26 +41,6 @@ export interface SplashOptions {
42
41
  */
43
42
  showLoading?: boolean;
44
43
 
45
- /**
46
- * Show progress bar (default: true)
47
- */
48
- showProgressBar?: boolean;
49
-
50
- /**
51
- * Footer text (e.g., "Powered by...")
52
- */
53
- footerText?: string;
54
-
55
- /**
56
- * Version text
57
- */
58
- versionText?: string;
59
-
60
- /**
61
- * Animation duration in milliseconds (default: 2000)
62
- */
63
- animationDuration?: number;
64
-
65
44
  /**
66
45
  * Minimum display time in milliseconds (default: 1500)
67
46
  */
@@ -1,15 +1,19 @@
1
1
  /**
2
2
  * Splash Screen Component
3
3
  *
4
- * Generic splash screen with animations, gradients, and customizable branding
4
+ * Simple splash screen with logo, app name, and loading indicator
5
5
  */
6
6
 
7
- import React, { useEffect, useRef, useMemo } from "react";
8
- import { View, StyleSheet, Animated } from "react-native";
7
+ import React, { useEffect } from "react";
8
+ import { View, StyleSheet, Text, ActivityIndicator } from "react-native";
9
+ import { LinearGradient } from "expo-linear-gradient";
9
10
  import { useSafeAreaInsets } from "react-native-safe-area-context";
10
- import { useAppDesignTokens } from "@umituz/react-native-design-system-theme";
11
11
  import { useLocalization } from "@umituz/react-native-localization";
12
- import { useResponsive, AtomicIcon, AtomicText, STATIC_TOKENS, withAlpha } from "@umituz/react-native-design-system";
12
+ import {
13
+ useDesignSystemTheme,
14
+ useAppDesignTokens,
15
+ } from "@umituz/react-native-design-system-theme";
16
+ import { AtomicIcon } from "@umituz/react-native-design-system-atoms";
13
17
  import type { SplashOptions } from "../../domain/entities/SplashOptions";
14
18
 
15
19
  export interface SplashScreenProps extends SplashOptions {
@@ -27,13 +31,8 @@ export const SplashScreen: React.FC<SplashScreenProps> = ({
27
31
  tagline,
28
32
  logo,
29
33
  backgroundColor,
30
- gradientColors,
31
34
  loadingText,
32
35
  showLoading = true,
33
- showProgressBar = true,
34
- footerText,
35
- versionText,
36
- animationDuration = 2000,
37
36
  minimumDisplayTime = 1500,
38
37
  onReady,
39
38
  renderLogo,
@@ -41,309 +40,211 @@ export const SplashScreen: React.FC<SplashScreenProps> = ({
41
40
  renderFooter,
42
41
  visible = true,
43
42
  }) => {
44
- const tokens = useAppDesignTokens();
45
- const responsive = useResponsive();
46
43
  const insets = useSafeAreaInsets();
47
44
  const { t } = useLocalization();
45
+ const { themeMode } = useDesignSystemTheme();
46
+ const tokens = useAppDesignTokens();
48
47
 
49
- // Safety check: Don't render if tokens or responsive are not ready
50
- if (!tokens || !responsive || !tokens.spacing || !responsive.horizontalPadding) {
51
- return null;
52
- }
48
+ const isDark = themeMode === "dark";
49
+ const colors = tokens.colors;
50
+ const spacing = tokens.spacing;
53
51
 
54
- const styles = useMemo(
55
- () => getStyles(tokens, responsive, insets, backgroundColor, gradientColors),
56
- [tokens, responsive, insets, backgroundColor, gradientColors],
57
- );
58
-
59
- // Animation values
60
- const fadeAnim = useRef(new Animated.Value(0)).current;
61
- const scaleAnim = useRef(new Animated.Value(0.8)).current;
62
- const slideAnim = useRef(new Animated.Value(50)).current;
52
+ // Use provided backgroundColor or theme-based color
53
+ const finalBackgroundColor = backgroundColor || colors.primary;
54
+
55
+ // Generate gradient colors based on theme
56
+ const gradientColors = backgroundColor
57
+ ? [backgroundColor, adjustColorBrightness(backgroundColor, isDark ? -15 : 10)]
58
+ : isDark
59
+ ? [colors.primary, adjustColorBrightness(colors.primary, -20)]
60
+ : [colors.primary, adjustColorBrightness(colors.primary, 15)];
61
+
62
+ const styles = getStyles(insets, finalBackgroundColor, colors, spacing, isDark);
63
63
 
64
64
  /**
65
- * Initialize animations
65
+ * Call onReady after minimum display time
66
66
  */
67
67
  useEffect(() => {
68
68
  if (!visible) return;
69
69
 
70
- // Start entrance animations
71
- Animated.parallel([
72
- Animated.timing(fadeAnim, {
73
- toValue: 1,
74
- duration: 1000,
75
- useNativeDriver: true,
76
- }),
77
- Animated.spring(scaleAnim, {
78
- toValue: 1,
79
- tension: 100,
80
- friction: 8,
81
- useNativeDriver: true,
82
- }),
83
- Animated.timing(slideAnim, {
84
- toValue: 0,
85
- duration: 800,
86
- useNativeDriver: true,
87
- }),
88
- ]).start();
89
-
90
- // Call onReady after minimum display time
91
70
  const timer = setTimeout(() => {
92
71
  if (onReady) {
93
72
  onReady();
94
73
  }
95
- }, Math.max(minimumDisplayTime, animationDuration));
74
+ }, minimumDisplayTime);
96
75
 
97
76
  return () => clearTimeout(timer);
98
- }, [visible, fadeAnim, scaleAnim, slideAnim, minimumDisplayTime, animationDuration, onReady]);
77
+ }, [visible, minimumDisplayTime, onReady]);
99
78
 
100
79
  if (!visible) return null;
101
80
 
102
81
  const displayAppName = appName || t("branding.appName", "App Name");
103
82
  const displayTagline = tagline || t("branding.tagline", "Your tagline here");
104
83
  const displayLoadingText = loadingText || t("general.loading", "Loading...");
105
- const displayFooterText = footerText || t("branding.poweredBy", "Powered by");
106
- const displayVersionText = versionText;
107
84
 
108
85
  return (
109
86
  <View style={styles.container}>
110
- {/* Background Gradient Effect */}
111
- {gradientColors && gradientColors.length > 0 ? (
112
- <View style={[styles.backgroundGradient, { backgroundColor: gradientColors[0] }]} />
113
- ) : (
114
- <View style={[styles.backgroundGradient, { backgroundColor: backgroundColor || tokens.colors.primary }]} />
115
- )}
116
-
117
- {/* Main Content */}
118
- <Animated.View
119
- style={[
120
- styles.content,
121
- {
122
- opacity: fadeAnim,
123
- transform: [{ scale: scaleAnim }],
124
- },
125
- ]}
87
+ <LinearGradient
88
+ colors={gradientColors}
89
+ start={{ x: 0, y: 0 }}
90
+ end={{ x: 1, y: 1 }}
91
+ style={styles.gradient}
126
92
  >
127
- {renderLogo ? (
128
- renderLogo()
129
- ) : (
130
- <View style={styles.logoContainer}>
131
- <Animated.View
132
- style={[
133
- styles.logoBackground,
134
- {
135
- transform: [{ scale: scaleAnim }],
136
- },
137
- ]}
138
- >
139
- {typeof logo === "string" ? (
140
- <AtomicIcon name={logo || "Sparkles"} size="xl" color="primary" />
141
- ) : logo ? (
142
- logo
143
- ) : (
144
- <AtomicIcon name="Sparkles" size="xl" color="primary" />
145
- )}
146
- </Animated.View>
147
- </View>
148
- )}
149
-
150
- {renderContent ? (
151
- renderContent()
152
- ) : (
153
- <Animated.View
154
- style={[
155
- styles.textContainer,
156
- {
157
- opacity: fadeAnim,
158
- transform: [{ translateY: slideAnim }],
159
- },
160
- ]}
161
- >
162
- <AtomicText style={styles.appName}>{displayAppName}</AtomicText>
163
- <AtomicText style={styles.tagline}>{displayTagline}</AtomicText>
164
- </Animated.View>
165
- )}
166
- </Animated.View>
167
-
168
- {/* Loading Indicator */}
169
- {showLoading && (
170
- <Animated.View
171
- style={[
172
- styles.loadingContainer,
173
- {
174
- opacity: fadeAnim,
175
- },
176
- ]}
177
- >
178
- {showProgressBar && (
179
- <View style={styles.loadingBar}>
180
- <Animated.View
181
- style={[
182
- styles.loadingProgress,
183
- {
184
- transform: [{ scaleX: scaleAnim }],
185
- },
186
- ]}
187
- />
93
+ {/* Main Content */}
94
+ <View style={styles.content}>
95
+ {renderLogo ? (
96
+ renderLogo()
97
+ ) : (
98
+ <View style={styles.logoContainer}>
99
+ <View style={styles.logoBackground}>
100
+ {typeof logo === "string" ? (
101
+ <AtomicIcon
102
+ name={logo || "Sparkles"}
103
+ size="xl"
104
+ color={isDark ? "primary" : "white"}
105
+ />
106
+ ) : logo ? (
107
+ logo
108
+ ) : (
109
+ <AtomicIcon
110
+ name="Sparkles"
111
+ size="xl"
112
+ color={isDark ? "primary" : "white"}
113
+ />
114
+ )}
115
+ </View>
188
116
  </View>
189
117
  )}
190
- <AtomicText style={styles.loadingText}>{displayLoadingText}</AtomicText>
191
- </Animated.View>
192
- )}
193
118
 
194
- {/* Footer */}
195
- {renderFooter ? (
196
- renderFooter()
197
- ) : (
198
- <Animated.View
199
- style={[
200
- styles.footer,
201
- {
202
- opacity: fadeAnim,
203
- },
204
- ]}
205
- >
206
- {displayFooterText && (
207
- <AtomicText style={styles.footerText}>{displayFooterText}</AtomicText>
208
- )}
209
- {displayVersionText && (
210
- <AtomicText style={styles.versionText}>{displayVersionText}</AtomicText>
119
+ {renderContent ? (
120
+ renderContent()
121
+ ) : (
122
+ <View style={styles.textContainer}>
123
+ <Text style={styles.appName}>{displayAppName}</Text>
124
+ {displayTagline && (
125
+ <Text style={styles.tagline}>{displayTagline}</Text>
126
+ )}
127
+ </View>
211
128
  )}
212
- </Animated.View>
213
- )}
129
+ </View>
130
+
131
+ {/* Loading Indicator */}
132
+ {showLoading && (
133
+ <View style={styles.loadingContainer}>
134
+ <ActivityIndicator
135
+ size="large"
136
+ color={isDark ? colors.primary : "#FFFFFF"}
137
+ />
138
+ <Text style={styles.loadingText}>{displayLoadingText}</Text>
139
+ </View>
140
+ )}
141
+
142
+ {/* Footer */}
143
+ {renderFooter && renderFooter()}
144
+ </LinearGradient>
214
145
  </View>
215
146
  );
216
147
  };
217
148
 
149
+ /**
150
+ * Adjust color brightness
151
+ */
152
+ const adjustColorBrightness = (hex: string, percent: number): string => {
153
+ const num = parseInt(hex.replace("#", ""), 16);
154
+ const amt = Math.round(2.55 * percent);
155
+ const R = (num >> 16) + amt;
156
+ const G = ((num >> 8) & 0x00ff) + amt;
157
+ const B = (num & 0x0000ff) + amt;
158
+ return (
159
+ "#" +
160
+ (
161
+ 0x1000000 +
162
+ (R < 255 ? (R < 1 ? 0 : R) : 255) * 0x10000 +
163
+ (G < 255 ? (G < 1 ? 0 : G) : 255) * 0x100 +
164
+ (B < 255 ? (B < 1 ? 0 : B) : 255)
165
+ )
166
+ .toString(16)
167
+ .slice(1)
168
+ );
169
+ };
170
+
218
171
  const getStyles = (
219
- tokens: ReturnType<typeof useAppDesignTokens>,
220
- responsive: ReturnType<typeof useResponsive>,
221
172
  insets: { top: number; bottom: number },
222
- backgroundColor?: string,
223
- gradientColors?: string[],
173
+ backgroundColor: string,
174
+ colors: any,
175
+ spacing: any,
176
+ isDark: boolean,
224
177
  ) => {
225
- // Safety check: Return empty styles if tokens or responsive are not ready
226
- if (!tokens || !responsive || !tokens.spacing || !tokens.colors || !responsive.horizontalPadding) {
227
- return StyleSheet.create({
228
- container: { flex: 1 },
229
- backgroundGradient: {},
230
- content: {},
231
- logoContainer: {},
232
- logoBackground: {},
233
- textContainer: {},
234
- appName: {},
235
- tagline: {},
236
- loadingContainer: {},
237
- loadingBar: {},
238
- loadingProgress: {},
239
- loadingText: {},
240
- footer: {},
241
- footerText: {},
242
- versionText: {},
243
- });
244
- }
245
-
178
+ const textColor = isDark ? colors.textPrimary : "#FFFFFF";
179
+ const logoBgOpacity = isDark ? 0.15 : 0.2;
180
+ const textOpacity = isDark ? 1 : 0.95;
181
+
246
182
  return StyleSheet.create({
247
183
  container: {
248
184
  flex: 1,
249
- backgroundColor: backgroundColor || tokens.colors.primary,
250
- paddingHorizontal: responsive.horizontalPadding,
185
+ backgroundColor: backgroundColor,
186
+ },
187
+ gradient: {
188
+ flex: 1,
251
189
  paddingTop: insets.top,
252
190
  paddingBottom: insets.bottom,
253
191
  },
254
- backgroundGradient: {
255
- position: "absolute",
256
- top: 0,
257
- left: 0,
258
- right: 0,
259
- bottom: 0,
260
- opacity: 0.9,
261
- },
262
192
  content: {
263
- flex: 3,
193
+ flex: 1,
264
194
  alignItems: "center",
265
195
  justifyContent: "center",
266
- paddingHorizontal: tokens.spacing.lg,
196
+ paddingHorizontal: spacing.lg,
267
197
  },
268
198
  logoContainer: {
269
- marginBottom: tokens.spacing.xxl * 2,
199
+ marginBottom: spacing.xl,
270
200
  alignItems: "center",
271
201
  justifyContent: "center",
272
202
  },
273
203
  logoBackground: {
274
- width: responsive.logoSize,
275
- height: responsive.logoSize,
276
- borderRadius: STATIC_TOKENS.borders.radius.full,
277
- backgroundColor: tokens.colors.surface,
204
+ width: 120,
205
+ height: 120,
206
+ borderRadius: 60,
207
+ backgroundColor: isDark
208
+ ? `${colors.primary}${Math.round(logoBgOpacity * 255).toString(16).padStart(2, "0")}`
209
+ : `rgba(255, 255, 255, ${logoBgOpacity})`,
278
210
  alignItems: "center",
279
211
  justifyContent: "center",
280
- padding: STATIC_TOKENS.spacing.md,
212
+ padding: spacing.md,
213
+ borderWidth: isDark ? 2 : 0,
214
+ borderColor: isDark ? `${colors.primary}40` : "transparent",
281
215
  },
282
216
  textContainer: {
283
217
  alignItems: "center",
284
- marginBottom: tokens.spacing.xxl * 2,
218
+ marginBottom: spacing.xxxl,
285
219
  },
286
220
  appName: {
287
- ...STATIC_TOKENS.typography.headingLarge,
288
- color: tokens.colors.textInverse,
221
+ fontSize: 32,
222
+ fontWeight: "700",
223
+ color: textColor,
289
224
  textAlign: "center",
290
- marginBottom: tokens.spacing.md,
291
- fontSize: responsive.getFontSize(STATIC_TOKENS.typography.headingLarge.fontSize ?? 32),
292
- fontWeight: STATIC_TOKENS.typography.bold,
225
+ marginBottom: spacing.sm,
226
+ letterSpacing: -0.5,
293
227
  },
294
228
  tagline: {
295
- ...STATIC_TOKENS.typography.bodyLarge,
296
- color: tokens.colors.textInverse,
229
+ fontSize: 16,
230
+ color: textColor,
297
231
  textAlign: "center",
298
- opacity: 0.9,
299
- maxWidth: responsive.maxContentWidth,
300
- fontSize: responsive.getFontSize(STATIC_TOKENS.typography.bodyLarge.fontSize ?? 18),
232
+ opacity: textOpacity * 0.85,
233
+ maxWidth: 300,
234
+ lineHeight: 22,
235
+ fontWeight: "400",
301
236
  },
302
237
  loadingContainer: {
303
- flex: 1,
304
238
  alignItems: "center",
305
239
  justifyContent: "center",
306
- width: responsive.maxContentWidth,
307
- alignSelf: "center",
308
- },
309
- loadingBar: {
310
- width: "100%",
311
- height: 4,
312
- backgroundColor: withAlpha(tokens.colors.textInverse, 0.3),
313
- borderRadius: STATIC_TOKENS.borders.radius.xs,
314
- marginBottom: tokens.spacing.md,
315
- overflow: "hidden",
316
- },
317
- loadingProgress: {
318
- width: "100%",
319
- height: "100%",
320
- backgroundColor: tokens.colors.textInverse,
321
- borderRadius: STATIC_TOKENS.borders.radius.xs,
240
+ paddingBottom: spacing.xxxl,
322
241
  },
323
242
  loadingText: {
324
- ...STATIC_TOKENS.typography.bodyMedium,
325
- color: tokens.colors.textInverse,
326
- opacity: 0.8,
327
- fontSize: responsive.getFontSize(STATIC_TOKENS.typography.bodyMedium.fontSize ?? 16),
328
- },
329
- footer: {
330
- flex: 0.5,
331
- alignItems: "center",
332
- justifyContent: "flex-start",
333
- paddingBottom: tokens.spacing.md,
334
- },
335
- footerText: {
336
- ...STATIC_TOKENS.typography.caption,
337
- color: tokens.colors.textInverse,
338
- opacity: 0.7,
339
- marginBottom: tokens.spacing.xs,
340
- fontSize: responsive.getFontSize(STATIC_TOKENS.typography.caption.fontSize ?? 12),
341
- },
342
- versionText: {
343
- ...STATIC_TOKENS.typography.caption,
344
- color: tokens.colors.textInverse,
345
- opacity: 0.5,
346
- fontSize: responsive.getFontSize(STATIC_TOKENS.typography.caption.fontSize ?? 12),
243
+ fontSize: 14,
244
+ color: textColor,
245
+ opacity: textOpacity * 0.7,
246
+ marginTop: spacing.md,
247
+ fontWeight: "500",
347
248
  },
348
249
  });
349
250
  };