@umituz/react-native-splash 2.1.6 → 2.1.7
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 +4 -6
- package/src/components/SplashScreen.tsx +171 -0
- package/src/index.ts +22 -20
- package/src/types/index.ts +55 -0
- package/src/domain/entities/SplashOptions.ts +0 -17
- package/src/presentation/components/SplashScreen.tsx +0 -184
- package/src/presentation/providers/SplashThemeProvider.tsx +0 -63
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@umituz/react-native-splash",
|
|
3
|
-
"version": "2.1.
|
|
4
|
-
"description": "
|
|
3
|
+
"version": "2.1.7",
|
|
4
|
+
"description": "Minimal prop-driven splash screen for React Native apps with design system integration.",
|
|
5
5
|
"main": "./src/index.ts",
|
|
6
6
|
"types": "./src/index.ts",
|
|
7
7
|
"scripts": {
|
|
@@ -18,7 +18,7 @@
|
|
|
18
18
|
"loading",
|
|
19
19
|
"minimal",
|
|
20
20
|
"design-system",
|
|
21
|
-
"
|
|
21
|
+
"prop-driven"
|
|
22
22
|
],
|
|
23
23
|
"author": "Ümit UZ <umit@umituz.com>",
|
|
24
24
|
"license": "MIT",
|
|
@@ -29,16 +29,14 @@
|
|
|
29
29
|
"peerDependencies": {
|
|
30
30
|
"@umituz/react-native-design-system": "latest",
|
|
31
31
|
"expo-linear-gradient": ">=13.0.0",
|
|
32
|
-
"expo-splash-screen": ">=0.23.0",
|
|
33
32
|
"react": ">=18.2.0",
|
|
34
33
|
"react-native": ">=0.74.0",
|
|
35
34
|
"react-native-safe-area-context": ">=4.0.0"
|
|
36
35
|
},
|
|
37
36
|
"devDependencies": {
|
|
38
37
|
"@umituz/react-native-design-system": "latest",
|
|
39
|
-
"expo-linear-gradient": "~15.0.7",
|
|
40
|
-
"expo-splash-screen": "~0.27.0",
|
|
41
38
|
"@types/react": "~19.1.10",
|
|
39
|
+
"expo-linear-gradient": "~15.0.7",
|
|
42
40
|
"react": "19.1.0",
|
|
43
41
|
"react-native": "0.81.5",
|
|
44
42
|
"react-native-safe-area-context": "^5.6.0",
|
|
@@ -0,0 +1,171 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Splash Screen Component
|
|
3
|
+
* Fully prop-driven splash screen - no provider required
|
|
4
|
+
* Main app controls all styling via props
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import React, { useEffect, useState, useCallback } from "react";
|
|
8
|
+
import { View, Image, StyleSheet } from "react-native";
|
|
9
|
+
import { LinearGradient } from "expo-linear-gradient";
|
|
10
|
+
import { useSafeAreaInsets } from "react-native-safe-area-context";
|
|
11
|
+
import { AtomicText } from "@umituz/react-native-design-system";
|
|
12
|
+
import type { SplashScreenProps } from "../types";
|
|
13
|
+
|
|
14
|
+
const ICON_SIZE = 120;
|
|
15
|
+
const ICON_PLACEHOLDER_SIZE = 100;
|
|
16
|
+
const CONTENT_PADDING = 24;
|
|
17
|
+
|
|
18
|
+
export const SplashScreen: React.FC<SplashScreenProps> = ({
|
|
19
|
+
icon,
|
|
20
|
+
appName,
|
|
21
|
+
tagline,
|
|
22
|
+
colors,
|
|
23
|
+
gradientColors,
|
|
24
|
+
visible = true,
|
|
25
|
+
maxDuration,
|
|
26
|
+
onTimeout,
|
|
27
|
+
onReady,
|
|
28
|
+
style,
|
|
29
|
+
}) => {
|
|
30
|
+
const insets = useSafeAreaInsets();
|
|
31
|
+
const [timedOut, setTimedOut] = useState(false);
|
|
32
|
+
|
|
33
|
+
const handleTimeout = useCallback(() => {
|
|
34
|
+
if (__DEV__) {
|
|
35
|
+
console.log(`[SplashScreen] Timeout reached: ${maxDuration}ms`);
|
|
36
|
+
}
|
|
37
|
+
setTimedOut(true);
|
|
38
|
+
onTimeout?.();
|
|
39
|
+
}, [maxDuration, onTimeout]);
|
|
40
|
+
|
|
41
|
+
useEffect(() => {
|
|
42
|
+
if (__DEV__) {
|
|
43
|
+
console.log("[SplashScreen] Mounted", { appName, visible });
|
|
44
|
+
}
|
|
45
|
+
onReady?.();
|
|
46
|
+
}, [appName, visible, onReady]);
|
|
47
|
+
|
|
48
|
+
useEffect(() => {
|
|
49
|
+
if (!maxDuration || !visible) return;
|
|
50
|
+
|
|
51
|
+
const timer = setTimeout(handleTimeout, maxDuration);
|
|
52
|
+
return () => clearTimeout(timer);
|
|
53
|
+
}, [maxDuration, visible, handleTimeout]);
|
|
54
|
+
|
|
55
|
+
if (!visible) {
|
|
56
|
+
if (__DEV__) {
|
|
57
|
+
console.log("[SplashScreen] Not visible, returning null");
|
|
58
|
+
}
|
|
59
|
+
return null;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
const iconPlaceholderColor = colors.iconPlaceholder ?? `${colors.text}30`;
|
|
63
|
+
|
|
64
|
+
const contentStyle = {
|
|
65
|
+
paddingTop: insets.top + CONTENT_PADDING,
|
|
66
|
+
paddingBottom: insets.bottom + CONTENT_PADDING,
|
|
67
|
+
};
|
|
68
|
+
|
|
69
|
+
const content = (
|
|
70
|
+
<View style={[styles.content, contentStyle]}>
|
|
71
|
+
<View style={styles.center}>
|
|
72
|
+
{icon ? (
|
|
73
|
+
<Image source={icon} style={styles.icon} resizeMode="contain" />
|
|
74
|
+
) : (
|
|
75
|
+
<View
|
|
76
|
+
style={[
|
|
77
|
+
styles.iconPlaceholder,
|
|
78
|
+
{ backgroundColor: iconPlaceholderColor },
|
|
79
|
+
]}
|
|
80
|
+
/>
|
|
81
|
+
)}
|
|
82
|
+
|
|
83
|
+
{appName ? (
|
|
84
|
+
<AtomicText
|
|
85
|
+
type="displaySmall"
|
|
86
|
+
style={[styles.title, { color: colors.text }]}
|
|
87
|
+
>
|
|
88
|
+
{appName}
|
|
89
|
+
</AtomicText>
|
|
90
|
+
) : null}
|
|
91
|
+
|
|
92
|
+
{tagline ? (
|
|
93
|
+
<AtomicText
|
|
94
|
+
type="bodyLarge"
|
|
95
|
+
style={[styles.tagline, { color: colors.text }]}
|
|
96
|
+
>
|
|
97
|
+
{tagline}
|
|
98
|
+
</AtomicText>
|
|
99
|
+
) : null}
|
|
100
|
+
|
|
101
|
+
{timedOut && __DEV__ ? (
|
|
102
|
+
<AtomicText
|
|
103
|
+
type="labelSmall"
|
|
104
|
+
style={[styles.timeoutText, { color: colors.text }]}
|
|
105
|
+
>
|
|
106
|
+
Initialization timeout
|
|
107
|
+
</AtomicText>
|
|
108
|
+
) : null}
|
|
109
|
+
</View>
|
|
110
|
+
</View>
|
|
111
|
+
);
|
|
112
|
+
|
|
113
|
+
if (gradientColors && gradientColors.length >= 2) {
|
|
114
|
+
return (
|
|
115
|
+
<LinearGradient
|
|
116
|
+
colors={gradientColors as [string, string, ...string[]]}
|
|
117
|
+
style={[styles.container, style]}
|
|
118
|
+
start={{ x: 0, y: 0 }}
|
|
119
|
+
end={{ x: 0, y: 1 }}
|
|
120
|
+
>
|
|
121
|
+
{content}
|
|
122
|
+
</LinearGradient>
|
|
123
|
+
);
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
return (
|
|
127
|
+
<View style={[styles.container, { backgroundColor: colors.background }, style]}>
|
|
128
|
+
{content}
|
|
129
|
+
</View>
|
|
130
|
+
);
|
|
131
|
+
};
|
|
132
|
+
|
|
133
|
+
const styles = StyleSheet.create({
|
|
134
|
+
container: {
|
|
135
|
+
flex: 1,
|
|
136
|
+
},
|
|
137
|
+
content: {
|
|
138
|
+
flex: 1,
|
|
139
|
+
justifyContent: "space-between",
|
|
140
|
+
},
|
|
141
|
+
center: {
|
|
142
|
+
flex: 1,
|
|
143
|
+
alignItems: "center",
|
|
144
|
+
justifyContent: "center",
|
|
145
|
+
paddingHorizontal: CONTENT_PADDING,
|
|
146
|
+
},
|
|
147
|
+
icon: {
|
|
148
|
+
width: ICON_SIZE,
|
|
149
|
+
height: ICON_SIZE,
|
|
150
|
+
marginBottom: CONTENT_PADDING,
|
|
151
|
+
},
|
|
152
|
+
iconPlaceholder: {
|
|
153
|
+
width: ICON_PLACEHOLDER_SIZE,
|
|
154
|
+
height: ICON_PLACEHOLDER_SIZE,
|
|
155
|
+
borderRadius: ICON_PLACEHOLDER_SIZE / 2,
|
|
156
|
+
marginBottom: CONTENT_PADDING,
|
|
157
|
+
},
|
|
158
|
+
title: {
|
|
159
|
+
textAlign: "center",
|
|
160
|
+
fontWeight: "800",
|
|
161
|
+
marginBottom: 8,
|
|
162
|
+
},
|
|
163
|
+
tagline: {
|
|
164
|
+
textAlign: "center",
|
|
165
|
+
opacity: 0.9,
|
|
166
|
+
},
|
|
167
|
+
timeoutText: {
|
|
168
|
+
textAlign: "center",
|
|
169
|
+
marginTop: 16,
|
|
170
|
+
},
|
|
171
|
+
});
|
package/src/index.ts
CHANGED
|
@@ -1,28 +1,30 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* React Native Splash - Public API
|
|
3
3
|
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
4
|
+
* Minimal splash screen for React Native apps.
|
|
5
|
+
* Fully prop-driven - no provider required.
|
|
6
6
|
*
|
|
7
7
|
* Usage:
|
|
8
|
-
* import {
|
|
8
|
+
* import { SplashScreen } from '@umituz/react-native-splash';
|
|
9
9
|
*
|
|
10
|
-
* <
|
|
11
|
-
*
|
|
12
|
-
*
|
|
13
|
-
*
|
|
14
|
-
*
|
|
15
|
-
*
|
|
16
|
-
*
|
|
17
|
-
*
|
|
18
|
-
*
|
|
19
|
-
*
|
|
10
|
+
* <SplashScreen
|
|
11
|
+
* appName="My App"
|
|
12
|
+
* tagline="Your tagline here"
|
|
13
|
+
* colors={{
|
|
14
|
+
* background: tokens.colors.primary,
|
|
15
|
+
* text: tokens.colors.onPrimary,
|
|
16
|
+
* }}
|
|
17
|
+
* icon={require('./assets/icon.png')}
|
|
18
|
+
* onReady={() => setReady(true)}
|
|
19
|
+
* />
|
|
20
|
+
*
|
|
21
|
+
* With gradient:
|
|
22
|
+
* <SplashScreen
|
|
23
|
+
* appName="My App"
|
|
24
|
+
* colors={{ background: '#000', text: '#fff' }}
|
|
25
|
+
* gradientColors={['#FF0080', '#7928CA']}
|
|
26
|
+
* />
|
|
20
27
|
*/
|
|
21
28
|
|
|
22
|
-
export type {
|
|
23
|
-
export { SplashScreen
|
|
24
|
-
export {
|
|
25
|
-
SplashThemeProvider,
|
|
26
|
-
type SplashThemeProviderProps,
|
|
27
|
-
useSplashTheme,
|
|
28
|
-
} from "./presentation/providers/SplashThemeProvider";
|
|
29
|
+
export type { SplashScreenProps, SplashColors } from "./types";
|
|
30
|
+
export { SplashScreen } from "./components/SplashScreen";
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Splash Screen Types
|
|
3
|
+
* All type definitions for the splash screen package
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import type { ImageSourcePropType, StyleProp, ViewStyle } from "react-native";
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Splash screen color configuration
|
|
10
|
+
* All colors should be provided by the main app via design tokens
|
|
11
|
+
*/
|
|
12
|
+
export interface SplashColors {
|
|
13
|
+
/** Background color when not using gradient */
|
|
14
|
+
background: string;
|
|
15
|
+
/** Text color for app name and tagline */
|
|
16
|
+
text: string;
|
|
17
|
+
/** Background color for icon placeholder circle */
|
|
18
|
+
iconPlaceholder?: string;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* Splash screen component props
|
|
23
|
+
* Fully prop-driven - no provider required
|
|
24
|
+
*/
|
|
25
|
+
export interface SplashScreenProps {
|
|
26
|
+
/** App name to display */
|
|
27
|
+
appName?: string;
|
|
28
|
+
|
|
29
|
+
/** Tagline or subtitle */
|
|
30
|
+
tagline?: string;
|
|
31
|
+
|
|
32
|
+
/** App icon/logo image source */
|
|
33
|
+
icon?: ImageSourcePropType;
|
|
34
|
+
|
|
35
|
+
/** Color configuration - required for proper theming */
|
|
36
|
+
colors: SplashColors;
|
|
37
|
+
|
|
38
|
+
/** Optional gradient colors (overrides background color) */
|
|
39
|
+
gradientColors?: readonly [string, string, ...string[]];
|
|
40
|
+
|
|
41
|
+
/** Control visibility */
|
|
42
|
+
visible?: boolean;
|
|
43
|
+
|
|
44
|
+
/** Maximum duration before timeout callback */
|
|
45
|
+
maxDuration?: number;
|
|
46
|
+
|
|
47
|
+
/** Callback when max duration is reached */
|
|
48
|
+
onTimeout?: () => void;
|
|
49
|
+
|
|
50
|
+
/** Callback when splash is ready/mounted */
|
|
51
|
+
onReady?: () => void;
|
|
52
|
+
|
|
53
|
+
/** Additional container style */
|
|
54
|
+
style?: StyleProp<ViewStyle>;
|
|
55
|
+
}
|
|
@@ -1,17 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Splash Options - Ultra Minimal Configuration
|
|
3
|
-
* Theme/colors managed by SplashThemeProvider
|
|
4
|
-
*/
|
|
5
|
-
|
|
6
|
-
import type { ImageSourcePropType } from "react-native";
|
|
7
|
-
|
|
8
|
-
export interface SplashOptions {
|
|
9
|
-
/** App icon/logo image source */
|
|
10
|
-
icon?: ImageSourcePropType;
|
|
11
|
-
|
|
12
|
-
/** App name to display */
|
|
13
|
-
appName?: string;
|
|
14
|
-
|
|
15
|
-
/** Tagline or subtitle */
|
|
16
|
-
tagline?: string;
|
|
17
|
-
}
|
|
@@ -1,184 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Splash Screen Component
|
|
3
|
-
* Ultra minimal splash screen with design system integration
|
|
4
|
-
* Parent component controls visibility and timing
|
|
5
|
-
*/
|
|
6
|
-
|
|
7
|
-
import React, { useEffect, useState } from "react";
|
|
8
|
-
import { View, Image, StyleSheet } from "react-native";
|
|
9
|
-
import { LinearGradient } from "expo-linear-gradient";
|
|
10
|
-
import { useSafeAreaInsets } from "react-native-safe-area-context";
|
|
11
|
-
import { AtomicText, useAppDesignTokens } from "@umituz/react-native-design-system";
|
|
12
|
-
import type { SplashOptions } from "../../domain/entities/SplashOptions";
|
|
13
|
-
import { useSplashTheme } from "../providers/SplashThemeProvider";
|
|
14
|
-
|
|
15
|
-
export interface SplashScreenProps extends SplashOptions {
|
|
16
|
-
visible?: boolean;
|
|
17
|
-
maxDuration?: number;
|
|
18
|
-
onTimeout?: () => void;
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
export const SplashScreen: React.FC<SplashScreenProps> = ({
|
|
22
|
-
icon,
|
|
23
|
-
appName = "",
|
|
24
|
-
tagline = "",
|
|
25
|
-
visible = true,
|
|
26
|
-
maxDuration,
|
|
27
|
-
onTimeout,
|
|
28
|
-
}) => {
|
|
29
|
-
const insets = useSafeAreaInsets();
|
|
30
|
-
const tokens = useAppDesignTokens();
|
|
31
|
-
const { colors, gradientColors } = useSplashTheme();
|
|
32
|
-
const [timedOut, setTimedOut] = useState(false);
|
|
33
|
-
|
|
34
|
-
useEffect(() => {
|
|
35
|
-
if (__DEV__) {
|
|
36
|
-
console.log(`[SplashScreen] Mounted - appName: ${appName}, visible: ${visible}`);
|
|
37
|
-
}
|
|
38
|
-
}, [appName, visible]);
|
|
39
|
-
|
|
40
|
-
useEffect(() => {
|
|
41
|
-
if (!maxDuration || !visible) return;
|
|
42
|
-
|
|
43
|
-
const timer = setTimeout(() => {
|
|
44
|
-
if (__DEV__) {
|
|
45
|
-
console.log(`[SplashScreen] Timeout reached: ${maxDuration}ms`);
|
|
46
|
-
}
|
|
47
|
-
setTimedOut(true);
|
|
48
|
-
onTimeout?.();
|
|
49
|
-
}, maxDuration);
|
|
50
|
-
|
|
51
|
-
return () => clearTimeout(timer);
|
|
52
|
-
}, [maxDuration, visible, onTimeout]);
|
|
53
|
-
|
|
54
|
-
if (!visible) {
|
|
55
|
-
if (__DEV__) console.log("[SplashScreen] Not visible, returning null");
|
|
56
|
-
return null;
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
const content = (
|
|
60
|
-
<View
|
|
61
|
-
style={[
|
|
62
|
-
styles.content,
|
|
63
|
-
{
|
|
64
|
-
paddingTop: insets.top + tokens.spacing.xl,
|
|
65
|
-
paddingBottom: insets.bottom + tokens.spacing.xl,
|
|
66
|
-
},
|
|
67
|
-
]}
|
|
68
|
-
>
|
|
69
|
-
<View style={styles.center}>
|
|
70
|
-
{icon ? (
|
|
71
|
-
<Image source={icon} style={styles.icon} resizeMode="contain" />
|
|
72
|
-
) : (
|
|
73
|
-
<View
|
|
74
|
-
style={[
|
|
75
|
-
styles.iconPlaceholder,
|
|
76
|
-
{ backgroundColor: colors.iconPlaceholderBg },
|
|
77
|
-
]}
|
|
78
|
-
/>
|
|
79
|
-
)}
|
|
80
|
-
|
|
81
|
-
{appName ? (
|
|
82
|
-
<AtomicText
|
|
83
|
-
type="displaySmall"
|
|
84
|
-
style={[
|
|
85
|
-
styles.title,
|
|
86
|
-
{
|
|
87
|
-
color: colors.textColor,
|
|
88
|
-
marginBottom: tokens.spacing.sm,
|
|
89
|
-
},
|
|
90
|
-
]}
|
|
91
|
-
>
|
|
92
|
-
{appName}
|
|
93
|
-
</AtomicText>
|
|
94
|
-
) : null}
|
|
95
|
-
|
|
96
|
-
{tagline ? (
|
|
97
|
-
<AtomicText
|
|
98
|
-
type="bodyLarge"
|
|
99
|
-
style={[
|
|
100
|
-
styles.subtitle,
|
|
101
|
-
{
|
|
102
|
-
color: colors.textColor,
|
|
103
|
-
opacity: 0.9,
|
|
104
|
-
},
|
|
105
|
-
]}
|
|
106
|
-
>
|
|
107
|
-
{tagline}
|
|
108
|
-
</AtomicText>
|
|
109
|
-
) : null}
|
|
110
|
-
|
|
111
|
-
{timedOut && __DEV__ ? (
|
|
112
|
-
<AtomicText
|
|
113
|
-
type="labelSmall"
|
|
114
|
-
style={[
|
|
115
|
-
styles.timeoutWarning,
|
|
116
|
-
{
|
|
117
|
-
color: colors.textColor,
|
|
118
|
-
marginTop: tokens.spacing.md,
|
|
119
|
-
},
|
|
120
|
-
]}
|
|
121
|
-
>
|
|
122
|
-
⚠️ Initialization timeout
|
|
123
|
-
</AtomicText>
|
|
124
|
-
) : null}
|
|
125
|
-
</View>
|
|
126
|
-
</View>
|
|
127
|
-
);
|
|
128
|
-
|
|
129
|
-
if (gradientColors && gradientColors.length >= 2) {
|
|
130
|
-
return (
|
|
131
|
-
<LinearGradient
|
|
132
|
-
colors={gradientColors as [string, string, ...string[]]}
|
|
133
|
-
style={styles.container}
|
|
134
|
-
start={{ x: 0, y: 0 }}
|
|
135
|
-
end={{ x: 0, y: 1 }}
|
|
136
|
-
>
|
|
137
|
-
{content}
|
|
138
|
-
</LinearGradient>
|
|
139
|
-
);
|
|
140
|
-
}
|
|
141
|
-
|
|
142
|
-
return (
|
|
143
|
-
<View style={[styles.container, { backgroundColor: colors.backgroundColor }]}>
|
|
144
|
-
{content}
|
|
145
|
-
</View>
|
|
146
|
-
);
|
|
147
|
-
};
|
|
148
|
-
|
|
149
|
-
const styles = StyleSheet.create({
|
|
150
|
-
container: {
|
|
151
|
-
flex: 1,
|
|
152
|
-
},
|
|
153
|
-
content: {
|
|
154
|
-
flex: 1,
|
|
155
|
-
justifyContent: "space-between",
|
|
156
|
-
},
|
|
157
|
-
center: {
|
|
158
|
-
flex: 1,
|
|
159
|
-
alignItems: "center",
|
|
160
|
-
justifyContent: "center",
|
|
161
|
-
paddingHorizontal: 24,
|
|
162
|
-
},
|
|
163
|
-
icon: {
|
|
164
|
-
width: 120,
|
|
165
|
-
height: 120,
|
|
166
|
-
marginBottom: 24,
|
|
167
|
-
},
|
|
168
|
-
iconPlaceholder: {
|
|
169
|
-
width: 100,
|
|
170
|
-
height: 100,
|
|
171
|
-
borderRadius: 50,
|
|
172
|
-
marginBottom: 24,
|
|
173
|
-
},
|
|
174
|
-
title: {
|
|
175
|
-
textAlign: "center",
|
|
176
|
-
fontWeight: "800",
|
|
177
|
-
},
|
|
178
|
-
subtitle: {
|
|
179
|
-
textAlign: "center",
|
|
180
|
-
},
|
|
181
|
-
timeoutWarning: {
|
|
182
|
-
textAlign: "center",
|
|
183
|
-
},
|
|
184
|
-
});
|
|
@@ -1,63 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Splash Theme Provider
|
|
3
|
-
* Centralized theme management for splash screen
|
|
4
|
-
* Follows provider pattern like onboarding package
|
|
5
|
-
*/
|
|
6
|
-
|
|
7
|
-
import React, { createContext, useContext, useMemo } from "react";
|
|
8
|
-
import { useAppDesignTokens } from "@umituz/react-native-design-system";
|
|
9
|
-
|
|
10
|
-
interface SplashColors {
|
|
11
|
-
backgroundColor: string;
|
|
12
|
-
textColor: string;
|
|
13
|
-
iconPlaceholderBg: string;
|
|
14
|
-
}
|
|
15
|
-
|
|
16
|
-
interface SplashThemeValue {
|
|
17
|
-
colors: SplashColors;
|
|
18
|
-
gradientColors?: readonly [string, string, ...string[]];
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
const SplashTheme = createContext<SplashThemeValue | null>(null);
|
|
22
|
-
|
|
23
|
-
export interface SplashThemeProviderProps {
|
|
24
|
-
children: React.ReactNode;
|
|
25
|
-
gradientColors?: readonly [string, string, ...string[]];
|
|
26
|
-
customColors?: Partial<SplashColors>;
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
export const SplashThemeProvider = ({
|
|
30
|
-
children,
|
|
31
|
-
gradientColors,
|
|
32
|
-
customColors,
|
|
33
|
-
}: SplashThemeProviderProps) => {
|
|
34
|
-
const tokens = useAppDesignTokens();
|
|
35
|
-
|
|
36
|
-
const colors = useMemo<SplashColors>(() => {
|
|
37
|
-
const defaults: SplashColors = {
|
|
38
|
-
backgroundColor: tokens.colors.primary,
|
|
39
|
-
textColor: tokens.colors.onPrimary,
|
|
40
|
-
iconPlaceholderBg: tokens.colors.onPrimary + "30",
|
|
41
|
-
};
|
|
42
|
-
|
|
43
|
-
return {
|
|
44
|
-
...defaults,
|
|
45
|
-
...customColors,
|
|
46
|
-
};
|
|
47
|
-
}, [tokens, customColors]);
|
|
48
|
-
|
|
49
|
-
const value = useMemo(
|
|
50
|
-
() => ({ colors, gradientColors }),
|
|
51
|
-
[colors, gradientColors]
|
|
52
|
-
);
|
|
53
|
-
|
|
54
|
-
return <SplashTheme.Provider value={value}>{children}</SplashTheme.Provider>;
|
|
55
|
-
};
|
|
56
|
-
|
|
57
|
-
export const useSplashTheme = (): SplashThemeValue => {
|
|
58
|
-
const theme = useContext(SplashTheme);
|
|
59
|
-
if (!theme) {
|
|
60
|
-
throw new Error("useSplashTheme must be used within SplashThemeProvider");
|
|
61
|
-
}
|
|
62
|
-
return theme;
|
|
63
|
-
};
|