@umituz/react-native-splash 1.12.0 → 2.0.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 +3 -11
- package/src/domain/entities/SplashOptions.ts +8 -19
- package/src/index.ts +20 -19
- package/src/presentation/components/SplashScreen.tsx +70 -89
- package/src/presentation/hooks/useSplash.ts +0 -70
- package/src/presentation/utils/splashGradient.utils.ts +0 -47
- package/src/types/expo-linear-gradient.d.ts +0 -12
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@umituz/react-native-splash",
|
|
3
|
-
"version": "
|
|
4
|
-
"description": "
|
|
3
|
+
"version": "2.0.0",
|
|
4
|
+
"description": "Ultra minimal splash screen for React Native apps. Just icon, name, and tagline.",
|
|
5
5
|
"main": "./src/index.ts",
|
|
6
6
|
"types": "./src/index.ts",
|
|
7
7
|
"scripts": {
|
|
@@ -13,13 +13,7 @@
|
|
|
13
13
|
"splash",
|
|
14
14
|
"splash-screen",
|
|
15
15
|
"loading",
|
|
16
|
-
"
|
|
17
|
-
"gradient",
|
|
18
|
-
"ddd",
|
|
19
|
-
"domain-driven-design",
|
|
20
|
-
"solid",
|
|
21
|
-
"dry",
|
|
22
|
-
"kiss"
|
|
16
|
+
"minimal"
|
|
23
17
|
],
|
|
24
18
|
"author": "Ümit UZ <umit@umituz.com>",
|
|
25
19
|
"license": "MIT",
|
|
@@ -28,7 +22,6 @@
|
|
|
28
22
|
"url": "https://github.com/umituz/react-native-splash"
|
|
29
23
|
},
|
|
30
24
|
"peerDependencies": {
|
|
31
|
-
"expo-linear-gradient": ">=14.0.0",
|
|
32
25
|
"react": ">=18.2.0",
|
|
33
26
|
"react-native": ">=0.74.0",
|
|
34
27
|
"react-native-safe-area-context": ">=4.0.0"
|
|
@@ -37,7 +30,6 @@
|
|
|
37
30
|
"@types/node": "^20.0.0",
|
|
38
31
|
"@types/react": "~19.1.0",
|
|
39
32
|
"@types/react-native": "^0.73.0",
|
|
40
|
-
"expo-linear-gradient": "^14.0.0",
|
|
41
33
|
"react": "^19.0.0",
|
|
42
34
|
"react-native": "^0.76.0",
|
|
43
35
|
"react-native-safe-area-context": "^5.0.0",
|
|
@@ -1,36 +1,25 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Splash Options - Minimal Configuration
|
|
2
|
+
* Splash Options - Ultra Minimal Configuration
|
|
3
3
|
*/
|
|
4
4
|
|
|
5
|
-
import {
|
|
5
|
+
import type { ImageSourcePropType } from "react-native";
|
|
6
6
|
|
|
7
7
|
export interface SplashOptions {
|
|
8
|
+
/** App icon/logo image source */
|
|
9
|
+
icon?: ImageSourcePropType;
|
|
10
|
+
|
|
8
11
|
/** App name to display */
|
|
9
12
|
appName?: string;
|
|
10
13
|
|
|
11
14
|
/** Tagline or subtitle */
|
|
12
15
|
tagline?: string;
|
|
13
16
|
|
|
14
|
-
/** Background color */
|
|
17
|
+
/** Background color (default: #6366F1) */
|
|
15
18
|
backgroundColor?: string;
|
|
16
19
|
|
|
17
|
-
/**
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
/** Loading text */
|
|
21
|
-
loadingText?: string;
|
|
20
|
+
/** Text color (default: #FFFFFF) */
|
|
21
|
+
textColor?: string;
|
|
22
22
|
|
|
23
23
|
/** Show loading indicator (default: true) */
|
|
24
24
|
showLoading?: boolean;
|
|
25
|
-
|
|
26
|
-
/** Minimum display time in ms (default: 1500) */
|
|
27
|
-
minimumDisplayTime?: number;
|
|
28
|
-
|
|
29
|
-
/** Callback when splash is ready */
|
|
30
|
-
onReady?: () => void | Promise<void>;
|
|
31
|
-
|
|
32
|
-
/** Custom render functions */
|
|
33
|
-
renderLogo?: () => ReactNode;
|
|
34
|
-
renderContent?: () => ReactNode;
|
|
35
|
-
renderFooter?: () => ReactNode;
|
|
36
25
|
}
|
package/src/index.ts
CHANGED
|
@@ -1,30 +1,31 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* React Native Splash - Public API
|
|
3
3
|
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
4
|
+
* Ultra minimal splash screen for React Native apps.
|
|
5
|
+
* Just displays icon, app name, and tagline.
|
|
6
|
+
* Parent component controls visibility and timing.
|
|
6
7
|
*
|
|
7
8
|
* Usage:
|
|
8
9
|
* import { SplashScreen } from '@umituz/react-native-splash';
|
|
9
10
|
*
|
|
10
|
-
*
|
|
11
|
-
*
|
|
12
|
-
*
|
|
13
|
-
*
|
|
14
|
-
*
|
|
15
|
-
*
|
|
11
|
+
* // In your app:
|
|
12
|
+
* const [showSplash, setShowSplash] = useState(true);
|
|
13
|
+
*
|
|
14
|
+
* useEffect(() => {
|
|
15
|
+
* // Your initialization logic
|
|
16
|
+
* initializeApp().then(() => setShowSplash(false));
|
|
17
|
+
* }, []);
|
|
18
|
+
*
|
|
19
|
+
* if (showSplash) {
|
|
20
|
+
* return (
|
|
21
|
+
* <SplashScreen
|
|
22
|
+
* icon={require('./assets/icon.png')}
|
|
23
|
+
* appName="My App"
|
|
24
|
+
* tagline="Your tagline here"
|
|
25
|
+
* />
|
|
26
|
+
* );
|
|
27
|
+
* }
|
|
16
28
|
*/
|
|
17
29
|
|
|
18
|
-
// =============================================================================
|
|
19
|
-
// DOMAIN LAYER - Entities
|
|
20
|
-
// =============================================================================
|
|
21
|
-
|
|
22
30
|
export type { SplashOptions } from "./domain/entities/SplashOptions";
|
|
23
|
-
|
|
24
|
-
// =============================================================================
|
|
25
|
-
// PRESENTATION LAYER - Components
|
|
26
|
-
// =============================================================================
|
|
27
|
-
|
|
28
31
|
export { SplashScreen, type SplashScreenProps } from "./presentation/components/SplashScreen";
|
|
29
|
-
export { useSplash } from "./presentation/hooks/useSplash";
|
|
30
|
-
|
|
@@ -1,123 +1,104 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Splash Screen Component
|
|
3
3
|
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
4
|
+
* Ultra minimal splash screen - just displays icon, name, and tagline.
|
|
5
|
+
* Parent component controls visibility and timing.
|
|
6
6
|
*/
|
|
7
7
|
|
|
8
|
-
import React
|
|
9
|
-
import { View, Text, StyleSheet, ActivityIndicator } from "react-native";
|
|
10
|
-
import { LinearGradient } from "expo-linear-gradient";
|
|
8
|
+
import React from "react";
|
|
9
|
+
import { View, Text, Image, StyleSheet, ActivityIndicator } from "react-native";
|
|
11
10
|
import { useSafeAreaInsets } from "react-native-safe-area-context";
|
|
12
11
|
import type { SplashOptions } from "../../domain/entities/SplashOptions";
|
|
13
12
|
|
|
14
13
|
export interface SplashScreenProps extends SplashOptions {
|
|
14
|
+
/** Control visibility from parent */
|
|
15
15
|
visible?: boolean;
|
|
16
|
-
textColor?: string;
|
|
17
16
|
}
|
|
18
17
|
|
|
19
18
|
const DEFAULT_BG = "#6366F1";
|
|
20
19
|
const DEFAULT_TEXT = "#FFFFFF";
|
|
21
20
|
|
|
22
21
|
export const SplashScreen: React.FC<SplashScreenProps> = ({
|
|
22
|
+
icon,
|
|
23
23
|
appName = "",
|
|
24
24
|
tagline = "",
|
|
25
25
|
backgroundColor = DEFAULT_BG,
|
|
26
|
-
|
|
27
|
-
loadingText = "",
|
|
26
|
+
textColor = DEFAULT_TEXT,
|
|
28
27
|
showLoading = true,
|
|
29
|
-
minimumDisplayTime = 1500,
|
|
30
|
-
onReady,
|
|
31
|
-
renderLogo,
|
|
32
|
-
renderContent,
|
|
33
|
-
renderFooter,
|
|
34
28
|
visible = true,
|
|
35
|
-
textColor = DEFAULT_TEXT,
|
|
36
29
|
}) => {
|
|
37
30
|
const insets = useSafeAreaInsets();
|
|
38
|
-
const timerRef = useRef<NodeJS.Timeout | null>(null);
|
|
39
|
-
const onReadyRef = useRef(onReady);
|
|
40
|
-
const hasCalledOnReady = useRef(false);
|
|
41
|
-
|
|
42
|
-
// Keep onReady ref updated but don't trigger re-render
|
|
43
|
-
useEffect(() => {
|
|
44
|
-
onReadyRef.current = onReady;
|
|
45
|
-
}, [onReady]);
|
|
46
|
-
|
|
47
|
-
// Stable callback that uses ref
|
|
48
|
-
const handleReady = useCallback(() => {
|
|
49
|
-
if (hasCalledOnReady.current) return;
|
|
50
|
-
hasCalledOnReady.current = true;
|
|
51
|
-
onReadyRef.current?.();
|
|
52
|
-
}, []);
|
|
53
|
-
|
|
54
|
-
useEffect(() => {
|
|
55
|
-
if (!visible) return;
|
|
56
|
-
|
|
57
|
-
// Reset when becoming visible again
|
|
58
|
-
hasCalledOnReady.current = false;
|
|
59
|
-
|
|
60
|
-
timerRef.current = setTimeout(handleReady, minimumDisplayTime);
|
|
61
|
-
|
|
62
|
-
return () => {
|
|
63
|
-
if (timerRef.current) clearTimeout(timerRef.current);
|
|
64
|
-
};
|
|
65
|
-
}, [visible, minimumDisplayTime, handleReady]);
|
|
66
31
|
|
|
67
32
|
if (!visible) return null;
|
|
68
33
|
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
)}
|
|
87
|
-
</View>
|
|
88
|
-
|
|
89
|
-
{showLoading && (
|
|
90
|
-
<View style={styles.loading}>
|
|
91
|
-
<ActivityIndicator color={textColor} />
|
|
92
|
-
{loadingText ? <Text style={[styles.loadingText, { color: textColor }]}>{loadingText}</Text> : null}
|
|
34
|
+
return (
|
|
35
|
+
<View style={[styles.container, { backgroundColor }]}>
|
|
36
|
+
<View style={[styles.content, { paddingTop: insets.top, paddingBottom: insets.bottom }]}>
|
|
37
|
+
<View style={styles.center}>
|
|
38
|
+
{icon ? (
|
|
39
|
+
<Image source={icon} style={styles.icon} resizeMode="contain" />
|
|
40
|
+
) : (
|
|
41
|
+
<View style={styles.iconPlaceholder} />
|
|
42
|
+
)}
|
|
43
|
+
|
|
44
|
+
{appName ? (
|
|
45
|
+
<Text style={[styles.title, { color: textColor }]}>{appName}</Text>
|
|
46
|
+
) : null}
|
|
47
|
+
|
|
48
|
+
{tagline ? (
|
|
49
|
+
<Text style={[styles.subtitle, { color: textColor }]}>{tagline}</Text>
|
|
50
|
+
) : null}
|
|
93
51
|
</View>
|
|
94
|
-
)}
|
|
95
52
|
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
{hasGradient ? (
|
|
103
|
-
<LinearGradient colors={colors} start={{ x: 0, y: 0 }} end={{ x: 1, y: 1 }} style={styles.fill}>
|
|
104
|
-
{content}
|
|
105
|
-
</LinearGradient>
|
|
106
|
-
) : (
|
|
107
|
-
<View style={[styles.fill, { backgroundColor }]}>{content}</View>
|
|
108
|
-
)}
|
|
53
|
+
{showLoading ? (
|
|
54
|
+
<View style={styles.loading}>
|
|
55
|
+
<ActivityIndicator color={textColor} size="small" />
|
|
56
|
+
</View>
|
|
57
|
+
) : null}
|
|
58
|
+
</View>
|
|
109
59
|
</View>
|
|
110
60
|
);
|
|
111
61
|
};
|
|
112
62
|
|
|
113
63
|
const styles = StyleSheet.create({
|
|
114
|
-
container: {
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
64
|
+
container: {
|
|
65
|
+
flex: 1,
|
|
66
|
+
},
|
|
67
|
+
content: {
|
|
68
|
+
flex: 1,
|
|
69
|
+
justifyContent: "space-between",
|
|
70
|
+
},
|
|
71
|
+
center: {
|
|
72
|
+
flex: 1,
|
|
73
|
+
alignItems: "center",
|
|
74
|
+
justifyContent: "center",
|
|
75
|
+
paddingHorizontal: 24,
|
|
76
|
+
},
|
|
77
|
+
icon: {
|
|
78
|
+
width: 120,
|
|
79
|
+
height: 120,
|
|
80
|
+
marginBottom: 24,
|
|
81
|
+
},
|
|
82
|
+
iconPlaceholder: {
|
|
83
|
+
width: 100,
|
|
84
|
+
height: 100,
|
|
85
|
+
borderRadius: 50,
|
|
86
|
+
backgroundColor: "rgba(255,255,255,0.2)",
|
|
87
|
+
marginBottom: 24,
|
|
88
|
+
},
|
|
89
|
+
title: {
|
|
90
|
+
fontSize: 32,
|
|
91
|
+
fontWeight: "700",
|
|
92
|
+
textAlign: "center",
|
|
93
|
+
marginBottom: 8,
|
|
94
|
+
},
|
|
95
|
+
subtitle: {
|
|
96
|
+
fontSize: 16,
|
|
97
|
+
textAlign: "center",
|
|
98
|
+
opacity: 0.9,
|
|
99
|
+
},
|
|
100
|
+
loading: {
|
|
101
|
+
alignItems: "center",
|
|
102
|
+
paddingBottom: 48,
|
|
103
|
+
},
|
|
123
104
|
});
|
|
@@ -1,70 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Use Splash Hook
|
|
3
|
-
* Single Responsibility: Manage splash screen state and timing
|
|
4
|
-
*/
|
|
5
|
-
|
|
6
|
-
import { useEffect, useState, useRef } from "react";
|
|
7
|
-
|
|
8
|
-
interface UseSplashOptions {
|
|
9
|
-
minimumDisplayTime?: number;
|
|
10
|
-
onReady?: () => void | Promise<void>;
|
|
11
|
-
autoHide?: boolean;
|
|
12
|
-
}
|
|
13
|
-
|
|
14
|
-
export const useSplash = ({
|
|
15
|
-
minimumDisplayTime = 1500,
|
|
16
|
-
onReady,
|
|
17
|
-
autoHide = true,
|
|
18
|
-
}: UseSplashOptions = {}) => {
|
|
19
|
-
const [isVisible, setIsVisible] = useState(true);
|
|
20
|
-
const [isReady, setIsReady] = useState(false);
|
|
21
|
-
const timerRef = useRef<NodeJS.Timeout | null>(null);
|
|
22
|
-
const isReadyRef = useRef(false);
|
|
23
|
-
|
|
24
|
-
const hide = () => {
|
|
25
|
-
setIsVisible(false);
|
|
26
|
-
};
|
|
27
|
-
|
|
28
|
-
const show = () => {
|
|
29
|
-
setIsVisible(true);
|
|
30
|
-
};
|
|
31
|
-
|
|
32
|
-
const markReady = async () => {
|
|
33
|
-
if (isReadyRef.current) return;
|
|
34
|
-
|
|
35
|
-
isReadyRef.current = true;
|
|
36
|
-
setIsReady(true);
|
|
37
|
-
|
|
38
|
-
if (onReady) {
|
|
39
|
-
try {
|
|
40
|
-
await onReady();
|
|
41
|
-
} catch (error) {
|
|
42
|
-
if (__DEV__) {
|
|
43
|
-
console.error("Splash onReady error:", error);
|
|
44
|
-
}
|
|
45
|
-
}
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
if (autoHide) {
|
|
49
|
-
timerRef.current = setTimeout(() => {
|
|
50
|
-
hide();
|
|
51
|
-
}, minimumDisplayTime);
|
|
52
|
-
}
|
|
53
|
-
};
|
|
54
|
-
|
|
55
|
-
useEffect(() => {
|
|
56
|
-
return () => {
|
|
57
|
-
if (timerRef.current) {
|
|
58
|
-
clearTimeout(timerRef.current);
|
|
59
|
-
}
|
|
60
|
-
};
|
|
61
|
-
}, []);
|
|
62
|
-
|
|
63
|
-
return {
|
|
64
|
-
isVisible,
|
|
65
|
-
isReady,
|
|
66
|
-
hide,
|
|
67
|
-
show,
|
|
68
|
-
markReady,
|
|
69
|
-
};
|
|
70
|
-
};
|
|
@@ -1,47 +0,0 @@
|
|
|
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
|
-
* Generate gradient colors from backgroundColor
|
|
30
|
-
*/
|
|
31
|
-
export const generateGradientFromColor = (
|
|
32
|
-
backgroundColor: string,
|
|
33
|
-
isDark: boolean,
|
|
34
|
-
): readonly string[] => {
|
|
35
|
-
return isDark
|
|
36
|
-
? ([
|
|
37
|
-
backgroundColor,
|
|
38
|
-
adjustColorBrightness(backgroundColor, -20),
|
|
39
|
-
adjustColorBrightness(backgroundColor, -30),
|
|
40
|
-
] as const)
|
|
41
|
-
: ([
|
|
42
|
-
backgroundColor,
|
|
43
|
-
adjustColorBrightness(backgroundColor, 10),
|
|
44
|
-
adjustColorBrightness(backgroundColor, 20),
|
|
45
|
-
] as const);
|
|
46
|
-
};
|
|
47
|
-
|
|
@@ -1,12 +0,0 @@
|
|
|
1
|
-
declare module 'expo-linear-gradient' {
|
|
2
|
-
import { View, ViewProps } from 'react-native';
|
|
3
|
-
|
|
4
|
-
export interface LinearGradientProps extends ViewProps {
|
|
5
|
-
colors: readonly string[];
|
|
6
|
-
start?: { x: number; y: number };
|
|
7
|
-
end?: { x: number; y: number };
|
|
8
|
-
locations?: readonly number[];
|
|
9
|
-
}
|
|
10
|
-
|
|
11
|
-
export const LinearGradient: React.FC<LinearGradientProps>;
|
|
12
|
-
}
|