@umituz/react-native-splash 1.4.1 → 1.5.1

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-splash",
3
- "version": "1.4.1",
3
+ "version": "1.5.1",
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",
@@ -0,0 +1,48 @@
1
+ /**
2
+ * Splash Decorations Component
3
+ * Single Responsibility: Render decorative background elements
4
+ */
5
+
6
+ import React from "react";
7
+ import { View, StyleSheet } from "react-native";
8
+
9
+ export const SplashDecorations: React.FC = () => {
10
+ return (
11
+ <>
12
+ <View style={styles.circle1} />
13
+ <View style={styles.circle2} />
14
+ <View style={styles.circle3} />
15
+ </>
16
+ );
17
+ };
18
+
19
+ const styles = StyleSheet.create({
20
+ circle1: {
21
+ position: "absolute",
22
+ width: 300,
23
+ height: 300,
24
+ borderRadius: 150,
25
+ backgroundColor: "rgba(255, 255, 255, 0.05)",
26
+ top: -100,
27
+ right: -100,
28
+ },
29
+ circle2: {
30
+ position: "absolute",
31
+ width: 200,
32
+ height: 200,
33
+ borderRadius: 100,
34
+ backgroundColor: "rgba(255, 255, 255, 0.03)",
35
+ bottom: -50,
36
+ left: -50,
37
+ },
38
+ circle3: {
39
+ position: "absolute",
40
+ width: 150,
41
+ height: 150,
42
+ borderRadius: 75,
43
+ backgroundColor: "rgba(255, 255, 255, 0.04)",
44
+ top: "30%",
45
+ right: "10%",
46
+ },
47
+ });
48
+
@@ -0,0 +1,72 @@
1
+ /**
2
+ * Splash Loading Component
3
+ * Single Responsibility: Render splash screen loading indicator
4
+ */
5
+
6
+ import React from "react";
7
+ import { View, Text, StyleSheet } from "react-native";
8
+ import type { DesignTokens } from "@umituz/react-native-design-system-theme";
9
+
10
+ export interface SplashLoadingProps {
11
+ loadingText: string;
12
+ tokens: DesignTokens;
13
+ bottomInset: number;
14
+ }
15
+
16
+ export const SplashLoading: React.FC<SplashLoadingProps> = ({
17
+ loadingText,
18
+ tokens,
19
+ bottomInset,
20
+ }) => {
21
+ const styles = getStyles(tokens, bottomInset);
22
+
23
+ return (
24
+ <View style={styles.container}>
25
+ <View style={styles.barContainer}>
26
+ <View style={styles.bar} />
27
+ </View>
28
+ <Text style={styles.text}>{loadingText}</Text>
29
+ </View>
30
+ );
31
+ };
32
+
33
+ const getStyles = (tokens: DesignTokens, bottomInset: number) => {
34
+ return StyleSheet.create({
35
+ container: {
36
+ alignItems: "center",
37
+ justifyContent: "center",
38
+ paddingBottom: tokens.spacing.xxxl + bottomInset,
39
+ zIndex: 1,
40
+ },
41
+ barContainer: {
42
+ width: 120,
43
+ height: 4,
44
+ backgroundColor: "rgba(255, 255, 255, 0.2)",
45
+ borderRadius: 2,
46
+ overflow: "hidden",
47
+ marginBottom: tokens.spacing.md,
48
+ },
49
+ bar: {
50
+ width: "60%",
51
+ height: "100%",
52
+ backgroundColor: "#FFFFFF",
53
+ borderRadius: 2,
54
+ shadowColor: "#FFFFFF",
55
+ shadowOffset: { width: 0, height: 0 },
56
+ shadowOpacity: 0.8,
57
+ shadowRadius: 4,
58
+ elevation: 4,
59
+ },
60
+ text: {
61
+ fontSize: 13,
62
+ color: "#FFFFFF",
63
+ opacity: 0.9,
64
+ fontWeight: "500" as const,
65
+ letterSpacing: 0.8,
66
+ textShadowColor: "rgba(0, 0, 0, 0.15)",
67
+ textShadowOffset: { width: 0, height: 1 },
68
+ textShadowRadius: 2,
69
+ },
70
+ });
71
+ };
72
+
@@ -0,0 +1,78 @@
1
+ /**
2
+ * Splash Logo Component
3
+ * Single Responsibility: Render splash screen logo
4
+ */
5
+
6
+ import React from "react";
7
+ import { View, StyleSheet } from "react-native";
8
+ import { AtomicIcon } from "@umituz/react-native-design-system-atoms";
9
+
10
+ export interface SplashLogoProps {
11
+ logo?: string | React.ReactNode;
12
+ logoSize?: number;
13
+ glowSize?: number;
14
+ }
15
+
16
+ export const SplashLogo: React.FC<SplashLogoProps> = ({
17
+ logo,
18
+ logoSize = 140,
19
+ glowSize = 160,
20
+ }) => {
21
+ const styles = getStyles(logoSize, glowSize);
22
+
23
+ return (
24
+ <View style={styles.container}>
25
+ <View style={styles.glow} />
26
+ <View style={styles.background}>
27
+ {typeof logo === "string" ? (
28
+ <AtomicIcon name={logo || "Sparkles"} size="xxl" customColor="#FFFFFF" />
29
+ ) : logo ? (
30
+ logo
31
+ ) : (
32
+ <AtomicIcon name="Sparkles" size="xxl" customColor="#FFFFFF" />
33
+ )}
34
+ </View>
35
+ </View>
36
+ );
37
+ };
38
+
39
+ const getStyles = (logoSize: number, glowSize: number) => {
40
+ const radius = logoSize / 2;
41
+ const glowRadius = glowSize / 2;
42
+
43
+ return StyleSheet.create({
44
+ container: {
45
+ alignItems: "center",
46
+ justifyContent: "center",
47
+ position: "relative",
48
+ },
49
+ glow: {
50
+ position: "absolute",
51
+ width: glowSize,
52
+ height: glowSize,
53
+ borderRadius: glowRadius,
54
+ backgroundColor: "rgba(255, 255, 255, 0.15)",
55
+ shadowColor: "#FFFFFF",
56
+ shadowOffset: { width: 0, height: 0 },
57
+ shadowOpacity: 0.5,
58
+ shadowRadius: 30,
59
+ elevation: 10,
60
+ },
61
+ background: {
62
+ width: logoSize,
63
+ height: logoSize,
64
+ borderRadius: radius,
65
+ backgroundColor: "rgba(255, 255, 255, 0.2)",
66
+ alignItems: "center",
67
+ justifyContent: "center",
68
+ borderWidth: 2,
69
+ borderColor: "rgba(255, 255, 255, 0.3)",
70
+ shadowColor: "#000",
71
+ shadowOffset: { width: 0, height: 4 },
72
+ shadowOpacity: 0.3,
73
+ shadowRadius: 8,
74
+ elevation: 8,
75
+ },
76
+ });
77
+ };
78
+
@@ -1,11 +1,10 @@
1
1
  /**
2
2
  * Splash Screen Component
3
- *
4
- * Simple splash screen with logo, app name, and loading indicator
3
+ * Single Responsibility: Orchestrate splash screen UI
5
4
  */
6
5
 
7
6
  import React, { useEffect } from "react";
8
- import { View, StyleSheet, Text, ActivityIndicator } from "react-native";
7
+ import { View, StyleSheet } from "react-native";
9
8
  import { LinearGradient } from "expo-linear-gradient";
10
9
  import { useSafeAreaInsets } from "react-native-safe-area-context";
11
10
  import { useLocalization } from "@umituz/react-native-localization";
@@ -13,13 +12,17 @@ import {
13
12
  useDesignSystemTheme,
14
13
  useAppDesignTokens,
15
14
  } from "@umituz/react-native-design-system-theme";
16
- import { AtomicIcon } from "@umituz/react-native-design-system-atoms";
15
+ import { SplashLogo } from "./SplashLogo";
16
+ import { SplashTypography } from "./SplashTypography";
17
+ import { SplashLoading } from "./SplashLoading";
18
+ import { SplashDecorations } from "./SplashDecorations";
19
+ import {
20
+ getDefaultGradient,
21
+ generateGradientFromColor,
22
+ } from "../utils/splashGradient.utils";
17
23
  import type { SplashOptions } from "../../domain/entities/SplashOptions";
18
24
 
19
25
  export interface SplashScreenProps extends SplashOptions {
20
- /**
21
- * Whether splash is visible
22
- */
23
26
  visible?: boolean;
24
27
  }
25
28
 
@@ -44,26 +47,15 @@ export const SplashScreen: React.FC<SplashScreenProps> = ({
44
47
  const { t } = useLocalization();
45
48
  const { themeMode } = useDesignSystemTheme();
46
49
  const tokens = useAppDesignTokens();
47
-
50
+
48
51
  const isDark = themeMode === "dark";
49
- const colors = tokens.colors;
50
- const spacing = tokens.spacing;
51
-
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: [string, string] = 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
-
64
- /**
65
- * Call onReady after minimum display time
66
- */
52
+
53
+ const gradientColors = backgroundColor
54
+ ? generateGradientFromColor(backgroundColor, isDark)
55
+ : getDefaultGradient(isDark);
56
+
57
+ const styles = getStyles(insets, tokens.spacing);
58
+
67
59
  useEffect(() => {
68
60
  if (!visible) return;
69
61
 
@@ -90,162 +82,61 @@ export const SplashScreen: React.FC<SplashScreenProps> = ({
90
82
  end={{ x: 1, y: 1 }}
91
83
  style={styles.gradient}
92
84
  >
93
- {/* Main Content */}
85
+ <SplashDecorations />
86
+
94
87
  <View style={styles.content}>
95
88
  {renderLogo ? (
96
89
  renderLogo()
97
90
  ) : (
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" : "textInverse"}
105
- />
106
- ) : logo ? (
107
- logo
108
- ) : (
109
- <AtomicIcon
110
- name="Sparkles"
111
- size="xl"
112
- color={isDark ? "primary" : "textInverse"}
113
- />
114
- )}
115
- </View>
116
- </View>
91
+ <SplashLogo logo={logo} />
117
92
  )}
118
93
 
119
94
  {renderContent ? (
120
95
  renderContent()
121
96
  ) : (
122
- <View style={styles.textContainer}>
123
- <Text style={styles.appName}>{displayAppName}</Text>
124
- {displayTagline && (
125
- <Text style={styles.tagline}>{displayTagline}</Text>
126
- )}
127
- </View>
97
+ <SplashTypography
98
+ appName={displayAppName}
99
+ tagline={displayTagline}
100
+ tokens={tokens}
101
+ />
128
102
  )}
129
103
  </View>
130
104
 
131
- {/* Loading Indicator */}
132
105
  {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>
106
+ <SplashLoading
107
+ loadingText={displayLoadingText}
108
+ tokens={tokens}
109
+ bottomInset={insets.bottom}
110
+ />
140
111
  )}
141
112
 
142
- {/* Footer */}
143
113
  {renderFooter && renderFooter()}
144
114
  </LinearGradient>
145
115
  </View>
146
116
  );
147
117
  };
148
118
 
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
-
171
119
  const getStyles = (
172
120
  insets: { top: number; bottom: number },
173
- backgroundColor: string,
174
- colors: any,
175
121
  spacing: any,
176
- isDark: boolean,
177
122
  ) => {
178
- const textColor = isDark ? colors.textPrimary : "#FFFFFF";
179
- const logoBgOpacity = isDark ? 0.15 : 0.2;
180
- const textOpacity = isDark ? 1 : 0.95;
181
-
182
123
  return StyleSheet.create({
183
124
  container: {
184
125
  flex: 1,
185
- backgroundColor: backgroundColor,
186
126
  },
187
127
  gradient: {
188
128
  flex: 1,
189
129
  paddingTop: insets.top,
190
130
  paddingBottom: insets.bottom,
131
+ position: "relative",
132
+ overflow: "hidden",
191
133
  },
192
134
  content: {
193
135
  flex: 1,
194
136
  alignItems: "center",
195
137
  justifyContent: "center",
196
- paddingHorizontal: spacing.lg,
197
- },
198
- logoContainer: {
199
- marginBottom: spacing.xl,
200
- alignItems: "center",
201
- justifyContent: "center",
202
- },
203
- logoBackground: {
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})`,
210
- alignItems: "center",
211
- justifyContent: "center",
212
- padding: spacing.md,
213
- borderWidth: isDark ? 2 : 0,
214
- borderColor: isDark ? `${colors.primary}40` : "transparent",
215
- },
216
- textContainer: {
217
- alignItems: "center",
218
- marginBottom: spacing.xxxl,
219
- },
220
- appName: {
221
- fontSize: 32,
222
- fontWeight: "700",
223
- color: textColor,
224
- textAlign: "center",
225
- marginBottom: spacing.sm,
226
- letterSpacing: -0.5,
227
- },
228
- tagline: {
229
- fontSize: 16,
230
- color: textColor,
231
- textAlign: "center",
232
- opacity: textOpacity * 0.85,
233
- maxWidth: 300,
234
- lineHeight: 22,
235
- fontWeight: "400",
236
- },
237
- loadingContainer: {
238
- alignItems: "center",
239
- justifyContent: "center",
240
- paddingBottom: spacing.xxxl,
241
- },
242
- loadingText: {
243
- fontSize: 14,
244
- color: textColor,
245
- opacity: textOpacity * 0.7,
246
- marginTop: spacing.md,
247
- fontWeight: "500",
138
+ paddingHorizontal: spacing.xl,
139
+ zIndex: 1,
248
140
  },
249
141
  });
250
142
  };
251
-
@@ -0,0 +1,70 @@
1
+ /**
2
+ * Splash Typography Component
3
+ * Single Responsibility: Render splash screen typography
4
+ */
5
+
6
+ import React from "react";
7
+ import { View, Text, StyleSheet } from "react-native";
8
+ import type { DesignTokens } from "@umituz/react-native-design-system-theme";
9
+
10
+ export interface SplashTypographyProps {
11
+ appName: string;
12
+ tagline?: string;
13
+ tokens: DesignTokens;
14
+ }
15
+
16
+ export const SplashTypography: React.FC<SplashTypographyProps> = ({
17
+ appName,
18
+ tagline,
19
+ tokens,
20
+ }) => {
21
+ const styles = getStyles(tokens);
22
+
23
+ return (
24
+ <View style={styles.container}>
25
+ <Text style={styles.appName} numberOfLines={1}>
26
+ {appName}
27
+ </Text>
28
+ {tagline && (
29
+ <Text style={styles.tagline} numberOfLines={2}>
30
+ {tagline}
31
+ </Text>
32
+ )}
33
+ </View>
34
+ );
35
+ };
36
+
37
+ const getStyles = (tokens: DesignTokens) => {
38
+ return StyleSheet.create({
39
+ container: {
40
+ alignItems: "center",
41
+ marginTop: tokens.spacing.xl,
42
+ },
43
+ appName: {
44
+ fontSize: 48,
45
+ fontWeight: "800" as const,
46
+ color: "#FFFFFF",
47
+ textAlign: "center" as const,
48
+ marginBottom: tokens.spacing.md,
49
+ letterSpacing: -1.2,
50
+ lineHeight: 56,
51
+ textShadowColor: "rgba(0, 0, 0, 0.25)",
52
+ textShadowOffset: { width: 0, height: 2 },
53
+ textShadowRadius: 6,
54
+ },
55
+ tagline: {
56
+ fontSize: 17,
57
+ color: "#FFFFFF",
58
+ textAlign: "center" as const,
59
+ opacity: 0.95,
60
+ maxWidth: 320,
61
+ lineHeight: 24,
62
+ fontWeight: "500" as const,
63
+ letterSpacing: 0.2,
64
+ textShadowColor: "rgba(0, 0, 0, 0.2)",
65
+ textShadowOffset: { width: 0, height: 1 },
66
+ textShadowRadius: 3,
67
+ },
68
+ });
69
+ };
70
+
@@ -0,0 +1,56 @@
1
+ /**
2
+ * Splash Gradient Utilities
3
+ * Single Responsibility: Gradient color generation
4
+ */
5
+
6
+ /**
7
+ * Adjust color brightness
8
+ */
9
+ export const adjustColorBrightness = (hex: string, percent: number): string => {
10
+ const num = parseInt(hex.replace("#", ""), 16);
11
+ const amt = Math.round(2.55 * percent);
12
+ const R = (num >> 16) + amt;
13
+ const G = ((num >> 8) & 0x00ff) + amt;
14
+ const B = (num & 0x0000ff) + amt;
15
+ return (
16
+ "#" +
17
+ (
18
+ 0x1000000 +
19
+ (R < 255 ? (R < 1 ? 0 : R) : 255) * 0x10000 +
20
+ (G < 255 ? (G < 1 ? 0 : G) : 255) * 0x100 +
21
+ (B < 255 ? (B < 1 ? 0 : B) : 255)
22
+ )
23
+ .toString(16)
24
+ .slice(1)
25
+ );
26
+ };
27
+
28
+ /**
29
+ * Get default gradient colors based on theme
30
+ */
31
+ export const getDefaultGradient = (isDark: boolean): string[] => {
32
+ return isDark
33
+ ? ["#1E1B4B", "#312E81", "#4C1D95", "#6B21A8"] // Dark indigo to purple
34
+ : ["#6366F1", "#8B5CF6", "#A855F7", "#EC4899"]; // Indigo to pink
35
+ };
36
+
37
+ /**
38
+ * Generate gradient colors from backgroundColor
39
+ */
40
+ export const generateGradientFromColor = (
41
+ backgroundColor: string,
42
+ isDark: boolean,
43
+ ): string[] => {
44
+ return isDark
45
+ ? [
46
+ backgroundColor,
47
+ adjustColorBrightness(backgroundColor, -20),
48
+ adjustColorBrightness(backgroundColor, -30),
49
+ ]
50
+ : [
51
+ backgroundColor,
52
+ adjustColorBrightness(backgroundColor, 10),
53
+ adjustColorBrightness(backgroundColor, 20),
54
+ ];
55
+ };
56
+