@umituz/react-native-splash 1.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/LICENSE +22 -0
- package/README.md +107 -0
- package/package.json +61 -0
- package/src/domain/entities/SplashOptions.ts +82 -0
- package/src/index.ts +29 -0
- package/src/presentation/components/SplashScreen.tsx +322 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2025 Ümit UZ
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
|
22
|
+
|
package/README.md
ADDED
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
# @umituz/react-native-splash
|
|
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.
|
|
4
|
+
|
|
5
|
+
## Features
|
|
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
|
|
15
|
+
- ✅ **Type-Safe**: Full TypeScript support
|
|
16
|
+
|
|
17
|
+
## Installation
|
|
18
|
+
|
|
19
|
+
```bash
|
|
20
|
+
npm install @umituz/react-native-splash
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
## Usage
|
|
24
|
+
|
|
25
|
+
### Basic Example
|
|
26
|
+
|
|
27
|
+
```typescript
|
|
28
|
+
import { SplashScreen } from '@umituz/react-native-splash';
|
|
29
|
+
|
|
30
|
+
<SplashScreen
|
|
31
|
+
appName="My App"
|
|
32
|
+
tagline="Your tagline here"
|
|
33
|
+
logo="Sparkles"
|
|
34
|
+
onReady={() => {
|
|
35
|
+
// Navigate to main app
|
|
36
|
+
}}
|
|
37
|
+
/>
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
### Advanced Example
|
|
41
|
+
|
|
42
|
+
```typescript
|
|
43
|
+
<SplashScreen
|
|
44
|
+
appName="My App"
|
|
45
|
+
tagline="Your tagline here"
|
|
46
|
+
logo="Sparkles"
|
|
47
|
+
backgroundColor="#3B82F6"
|
|
48
|
+
gradientColors={['#3B82F6', '#8B5CF6']}
|
|
49
|
+
loadingText="Loading..."
|
|
50
|
+
showLoading={true}
|
|
51
|
+
showProgressBar={true}
|
|
52
|
+
footerText="Powered by"
|
|
53
|
+
versionText="v1.0.0"
|
|
54
|
+
animationDuration={2000}
|
|
55
|
+
minimumDisplayTime={1500}
|
|
56
|
+
onReady={handleReady}
|
|
57
|
+
/>
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
### Custom Render Functions
|
|
61
|
+
|
|
62
|
+
```typescript
|
|
63
|
+
<SplashScreen
|
|
64
|
+
appName="My App"
|
|
65
|
+
renderLogo={() => <CustomLogo />}
|
|
66
|
+
renderContent={() => <CustomContent />}
|
|
67
|
+
renderFooter={() => <CustomFooter />}
|
|
68
|
+
onReady={handleReady}
|
|
69
|
+
/>
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
## API Reference
|
|
73
|
+
|
|
74
|
+
### `SplashOptions`
|
|
75
|
+
|
|
76
|
+
```typescript
|
|
77
|
+
interface SplashOptions {
|
|
78
|
+
appName?: string;
|
|
79
|
+
tagline?: string;
|
|
80
|
+
logo?: ReactNode | string;
|
|
81
|
+
backgroundColor?: string;
|
|
82
|
+
gradientColors?: string[];
|
|
83
|
+
loadingText?: string;
|
|
84
|
+
showLoading?: boolean;
|
|
85
|
+
showProgressBar?: boolean;
|
|
86
|
+
footerText?: string;
|
|
87
|
+
versionText?: string;
|
|
88
|
+
animationDuration?: number;
|
|
89
|
+
minimumDisplayTime?: number;
|
|
90
|
+
onReady?: () => void | Promise<void>;
|
|
91
|
+
renderLogo?: () => ReactNode;
|
|
92
|
+
renderContent?: () => ReactNode;
|
|
93
|
+
renderFooter?: () => ReactNode;
|
|
94
|
+
}
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
## Architecture
|
|
98
|
+
|
|
99
|
+
- **Domain Layer**: Entities and interfaces (business logic)
|
|
100
|
+
- **Presentation Layer**: Components (UI)
|
|
101
|
+
|
|
102
|
+
No app-specific code, fully generic and reusable.
|
|
103
|
+
|
|
104
|
+
## License
|
|
105
|
+
|
|
106
|
+
MIT
|
|
107
|
+
|
package/package.json
ADDED
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@umituz/react-native-splash",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "Generic splash screen for React Native apps with animations, gradients, and customizable branding. SOLID, DRY, KISS principles applied.",
|
|
5
|
+
"main": "./src/index.ts",
|
|
6
|
+
"types": "./src/index.ts",
|
|
7
|
+
"scripts": {
|
|
8
|
+
"typecheck": "tsc --noEmit",
|
|
9
|
+
"lint": "tsc --noEmit",
|
|
10
|
+
"version:patch": "npm version patch -m 'chore: release v%s'",
|
|
11
|
+
"version:minor": "npm version minor -m 'chore: release v%s'",
|
|
12
|
+
"version:major": "npm version major -m 'chore: release v%s'"
|
|
13
|
+
},
|
|
14
|
+
"keywords": [
|
|
15
|
+
"react-native",
|
|
16
|
+
"splash",
|
|
17
|
+
"splash-screen",
|
|
18
|
+
"loading",
|
|
19
|
+
"animation",
|
|
20
|
+
"gradient",
|
|
21
|
+
"ddd",
|
|
22
|
+
"domain-driven-design",
|
|
23
|
+
"solid",
|
|
24
|
+
"dry",
|
|
25
|
+
"kiss"
|
|
26
|
+
],
|
|
27
|
+
"author": "Ümit UZ <umit@umituz.com>",
|
|
28
|
+
"license": "MIT",
|
|
29
|
+
"repository": {
|
|
30
|
+
"type": "git",
|
|
31
|
+
"url": "https://github.com/umituz/react-native-splash"
|
|
32
|
+
},
|
|
33
|
+
"peerDependencies": {
|
|
34
|
+
"@umituz/react-native-theme": "latest",
|
|
35
|
+
"@umituz/react-native-localization": "latest",
|
|
36
|
+
"@umituz/react-native-design-system": "latest",
|
|
37
|
+
"react": ">=18.2.0",
|
|
38
|
+
"react-native": ">=0.74.0",
|
|
39
|
+
"react-native-safe-area-context": "^5.0.0"
|
|
40
|
+
},
|
|
41
|
+
"devDependencies": {
|
|
42
|
+
"@types/react": "^18.2.45",
|
|
43
|
+
"@types/react-native": "^0.73.0",
|
|
44
|
+
"@umituz/react-native-theme": "latest",
|
|
45
|
+
"@umituz/react-native-localization": "latest",
|
|
46
|
+
"@umituz/react-native-design-system": "latest",
|
|
47
|
+
"react": "^18.2.0",
|
|
48
|
+
"react-native": "^0.74.0",
|
|
49
|
+
"react-native-safe-area-context": "^5.6.0",
|
|
50
|
+
"typescript": "^5.3.3"
|
|
51
|
+
},
|
|
52
|
+
"publishConfig": {
|
|
53
|
+
"access": "public"
|
|
54
|
+
},
|
|
55
|
+
"files": [
|
|
56
|
+
"src",
|
|
57
|
+
"README.md",
|
|
58
|
+
"LICENSE"
|
|
59
|
+
]
|
|
60
|
+
}
|
|
61
|
+
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Splash Options
|
|
3
|
+
*
|
|
4
|
+
* Configuration options for splash screen
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import { ReactNode } from "react";
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Splash Options
|
|
11
|
+
* Customize the splash screen experience
|
|
12
|
+
*/
|
|
13
|
+
export interface SplashOptions {
|
|
14
|
+
/**
|
|
15
|
+
* App name to display
|
|
16
|
+
*/
|
|
17
|
+
appName?: string;
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* Tagline or subtitle
|
|
21
|
+
*/
|
|
22
|
+
tagline?: string;
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* Logo/icon component or name
|
|
26
|
+
*/
|
|
27
|
+
logo?: ReactNode | string;
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* Background color or gradient colors
|
|
31
|
+
*/
|
|
32
|
+
backgroundColor?: string;
|
|
33
|
+
gradientColors?: string[];
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* Loading text
|
|
37
|
+
*/
|
|
38
|
+
loadingText?: string;
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* Show loading indicator (default: true)
|
|
42
|
+
*/
|
|
43
|
+
showLoading?: boolean;
|
|
44
|
+
|
|
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
|
+
/**
|
|
66
|
+
* Minimum display time in milliseconds (default: 1500)
|
|
67
|
+
*/
|
|
68
|
+
minimumDisplayTime?: number;
|
|
69
|
+
|
|
70
|
+
/**
|
|
71
|
+
* Callback when splash is ready to hide
|
|
72
|
+
*/
|
|
73
|
+
onReady?: () => void | Promise<void>;
|
|
74
|
+
|
|
75
|
+
/**
|
|
76
|
+
* Custom render functions
|
|
77
|
+
*/
|
|
78
|
+
renderLogo?: () => ReactNode;
|
|
79
|
+
renderContent?: () => ReactNode;
|
|
80
|
+
renderFooter?: () => ReactNode;
|
|
81
|
+
}
|
|
82
|
+
|
package/src/index.ts
ADDED
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* React Native Splash - Public API
|
|
3
|
+
*
|
|
4
|
+
* Generic splash screen for React Native apps with animations, gradients,
|
|
5
|
+
* and customizable branding. Follows SOLID, DRY, KISS principles.
|
|
6
|
+
*
|
|
7
|
+
* Usage:
|
|
8
|
+
* import { SplashScreen } from '@umituz/react-native-splash';
|
|
9
|
+
*
|
|
10
|
+
* <SplashScreen
|
|
11
|
+
* appName="My App"
|
|
12
|
+
* tagline="Your tagline here"
|
|
13
|
+
* logo="Sparkles"
|
|
14
|
+
* onReady={() => console.log('Ready')}
|
|
15
|
+
* />
|
|
16
|
+
*/
|
|
17
|
+
|
|
18
|
+
// =============================================================================
|
|
19
|
+
// DOMAIN LAYER - Entities
|
|
20
|
+
// =============================================================================
|
|
21
|
+
|
|
22
|
+
export type { SplashOptions } from "./domain/entities/SplashOptions";
|
|
23
|
+
|
|
24
|
+
// =============================================================================
|
|
25
|
+
// PRESENTATION LAYER - Components
|
|
26
|
+
// =============================================================================
|
|
27
|
+
|
|
28
|
+
export { SplashScreen, type SplashScreenProps } from "./presentation/components/SplashScreen";
|
|
29
|
+
|
|
@@ -0,0 +1,322 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Splash Screen Component
|
|
3
|
+
*
|
|
4
|
+
* Generic splash screen with animations, gradients, and customizable branding
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import React, { useEffect, useRef, useMemo } from "react";
|
|
8
|
+
import { View, StyleSheet, Animated } from "react-native";
|
|
9
|
+
import { useSafeAreaInsets } from "react-native-safe-area-context";
|
|
10
|
+
import { useAppDesignTokens } from "@umituz/react-native-theme";
|
|
11
|
+
import { useLocalization } from "@umituz/react-native-localization";
|
|
12
|
+
import { useResponsive, AtomicIcon, AtomicText, STATIC_TOKENS, withAlpha } from "@umituz/react-native-design-system";
|
|
13
|
+
import type { SplashOptions } from "../../domain/entities/SplashOptions";
|
|
14
|
+
|
|
15
|
+
export interface SplashScreenProps extends SplashOptions {
|
|
16
|
+
/**
|
|
17
|
+
* Whether splash is visible
|
|
18
|
+
*/
|
|
19
|
+
visible?: boolean;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* Splash Screen Component
|
|
24
|
+
*/
|
|
25
|
+
export const SplashScreen: React.FC<SplashScreenProps> = ({
|
|
26
|
+
appName,
|
|
27
|
+
tagline,
|
|
28
|
+
logo,
|
|
29
|
+
backgroundColor,
|
|
30
|
+
gradientColors,
|
|
31
|
+
loadingText,
|
|
32
|
+
showLoading = true,
|
|
33
|
+
showProgressBar = true,
|
|
34
|
+
footerText,
|
|
35
|
+
versionText,
|
|
36
|
+
animationDuration = 2000,
|
|
37
|
+
minimumDisplayTime = 1500,
|
|
38
|
+
onReady,
|
|
39
|
+
renderLogo,
|
|
40
|
+
renderContent,
|
|
41
|
+
renderFooter,
|
|
42
|
+
visible = true,
|
|
43
|
+
}) => {
|
|
44
|
+
const tokens = useAppDesignTokens();
|
|
45
|
+
const responsive = useResponsive();
|
|
46
|
+
const insets = useSafeAreaInsets();
|
|
47
|
+
const { t } = useLocalization();
|
|
48
|
+
const styles = useMemo(
|
|
49
|
+
() => getStyles(tokens, responsive, insets, backgroundColor, gradientColors),
|
|
50
|
+
[tokens, responsive, insets, backgroundColor, gradientColors],
|
|
51
|
+
);
|
|
52
|
+
|
|
53
|
+
// Animation values
|
|
54
|
+
const fadeAnim = useRef(new Animated.Value(0)).current;
|
|
55
|
+
const scaleAnim = useRef(new Animated.Value(0.8)).current;
|
|
56
|
+
const slideAnim = useRef(new Animated.Value(50)).current;
|
|
57
|
+
|
|
58
|
+
/**
|
|
59
|
+
* Initialize animations
|
|
60
|
+
*/
|
|
61
|
+
useEffect(() => {
|
|
62
|
+
if (!visible) return;
|
|
63
|
+
|
|
64
|
+
// Start entrance animations
|
|
65
|
+
Animated.parallel([
|
|
66
|
+
Animated.timing(fadeAnim, {
|
|
67
|
+
toValue: 1,
|
|
68
|
+
duration: 1000,
|
|
69
|
+
useNativeDriver: true,
|
|
70
|
+
}),
|
|
71
|
+
Animated.spring(scaleAnim, {
|
|
72
|
+
toValue: 1,
|
|
73
|
+
tension: 100,
|
|
74
|
+
friction: 8,
|
|
75
|
+
useNativeDriver: true,
|
|
76
|
+
}),
|
|
77
|
+
Animated.timing(slideAnim, {
|
|
78
|
+
toValue: 0,
|
|
79
|
+
duration: 800,
|
|
80
|
+
useNativeDriver: true,
|
|
81
|
+
}),
|
|
82
|
+
]).start();
|
|
83
|
+
|
|
84
|
+
// Call onReady after minimum display time
|
|
85
|
+
const timer = setTimeout(() => {
|
|
86
|
+
if (onReady) {
|
|
87
|
+
onReady();
|
|
88
|
+
}
|
|
89
|
+
}, Math.max(minimumDisplayTime, animationDuration));
|
|
90
|
+
|
|
91
|
+
return () => clearTimeout(timer);
|
|
92
|
+
}, [visible, fadeAnim, scaleAnim, slideAnim, minimumDisplayTime, animationDuration, onReady]);
|
|
93
|
+
|
|
94
|
+
if (!visible) return null;
|
|
95
|
+
|
|
96
|
+
const displayAppName = appName || t("branding.appName", "App Name");
|
|
97
|
+
const displayTagline = tagline || t("branding.tagline", "Your tagline here");
|
|
98
|
+
const displayLoadingText = loadingText || t("general.loading", "Loading...");
|
|
99
|
+
const displayFooterText = footerText || t("branding.poweredBy", "Powered by");
|
|
100
|
+
const displayVersionText = versionText;
|
|
101
|
+
|
|
102
|
+
return (
|
|
103
|
+
<View style={styles.container}>
|
|
104
|
+
{/* Background Gradient Effect */}
|
|
105
|
+
{gradientColors && gradientColors.length > 0 ? (
|
|
106
|
+
<View style={[styles.backgroundGradient, { backgroundColor: gradientColors[0] }]} />
|
|
107
|
+
) : (
|
|
108
|
+
<View style={[styles.backgroundGradient, { backgroundColor: backgroundColor || tokens.colors.primary }]} />
|
|
109
|
+
)}
|
|
110
|
+
|
|
111
|
+
{/* Main Content */}
|
|
112
|
+
<Animated.View
|
|
113
|
+
style={[
|
|
114
|
+
styles.content,
|
|
115
|
+
{
|
|
116
|
+
opacity: fadeAnim,
|
|
117
|
+
transform: [{ scale: scaleAnim }],
|
|
118
|
+
},
|
|
119
|
+
]}
|
|
120
|
+
>
|
|
121
|
+
{renderLogo ? (
|
|
122
|
+
renderLogo()
|
|
123
|
+
) : (
|
|
124
|
+
<View style={styles.logoContainer}>
|
|
125
|
+
<Animated.View
|
|
126
|
+
style={[
|
|
127
|
+
styles.logoBackground,
|
|
128
|
+
{
|
|
129
|
+
transform: [{ scale: scaleAnim }],
|
|
130
|
+
},
|
|
131
|
+
]}
|
|
132
|
+
>
|
|
133
|
+
{typeof logo === "string" ? (
|
|
134
|
+
<AtomicIcon name={logo || "Sparkles"} size="xl" color="primary" />
|
|
135
|
+
) : logo ? (
|
|
136
|
+
logo
|
|
137
|
+
) : (
|
|
138
|
+
<AtomicIcon name="Sparkles" size="xl" color="primary" />
|
|
139
|
+
)}
|
|
140
|
+
</Animated.View>
|
|
141
|
+
</View>
|
|
142
|
+
)}
|
|
143
|
+
|
|
144
|
+
{renderContent ? (
|
|
145
|
+
renderContent()
|
|
146
|
+
) : (
|
|
147
|
+
<Animated.View
|
|
148
|
+
style={[
|
|
149
|
+
styles.textContainer,
|
|
150
|
+
{
|
|
151
|
+
opacity: fadeAnim,
|
|
152
|
+
transform: [{ translateY: slideAnim }],
|
|
153
|
+
},
|
|
154
|
+
]}
|
|
155
|
+
>
|
|
156
|
+
<AtomicText style={styles.appName}>{displayAppName}</AtomicText>
|
|
157
|
+
<AtomicText style={styles.tagline}>{displayTagline}</AtomicText>
|
|
158
|
+
</Animated.View>
|
|
159
|
+
)}
|
|
160
|
+
</Animated.View>
|
|
161
|
+
|
|
162
|
+
{/* Loading Indicator */}
|
|
163
|
+
{showLoading && (
|
|
164
|
+
<Animated.View
|
|
165
|
+
style={[
|
|
166
|
+
styles.loadingContainer,
|
|
167
|
+
{
|
|
168
|
+
opacity: fadeAnim,
|
|
169
|
+
},
|
|
170
|
+
]}
|
|
171
|
+
>
|
|
172
|
+
{showProgressBar && (
|
|
173
|
+
<View style={styles.loadingBar}>
|
|
174
|
+
<Animated.View
|
|
175
|
+
style={[
|
|
176
|
+
styles.loadingProgress,
|
|
177
|
+
{
|
|
178
|
+
transform: [{ scaleX: scaleAnim }],
|
|
179
|
+
},
|
|
180
|
+
]}
|
|
181
|
+
/>
|
|
182
|
+
</View>
|
|
183
|
+
)}
|
|
184
|
+
<AtomicText style={styles.loadingText}>{displayLoadingText}</AtomicText>
|
|
185
|
+
</Animated.View>
|
|
186
|
+
)}
|
|
187
|
+
|
|
188
|
+
{/* Footer */}
|
|
189
|
+
{renderFooter ? (
|
|
190
|
+
renderFooter()
|
|
191
|
+
) : (
|
|
192
|
+
<Animated.View
|
|
193
|
+
style={[
|
|
194
|
+
styles.footer,
|
|
195
|
+
{
|
|
196
|
+
opacity: fadeAnim,
|
|
197
|
+
},
|
|
198
|
+
]}
|
|
199
|
+
>
|
|
200
|
+
{displayFooterText && (
|
|
201
|
+
<AtomicText style={styles.footerText}>{displayFooterText}</AtomicText>
|
|
202
|
+
)}
|
|
203
|
+
{displayVersionText && (
|
|
204
|
+
<AtomicText style={styles.versionText}>{displayVersionText}</AtomicText>
|
|
205
|
+
)}
|
|
206
|
+
</Animated.View>
|
|
207
|
+
)}
|
|
208
|
+
</View>
|
|
209
|
+
);
|
|
210
|
+
};
|
|
211
|
+
|
|
212
|
+
const getStyles = (
|
|
213
|
+
tokens: ReturnType<typeof useAppDesignTokens>,
|
|
214
|
+
responsive: ReturnType<typeof useResponsive>,
|
|
215
|
+
insets: { top: number; bottom: number },
|
|
216
|
+
backgroundColor?: string,
|
|
217
|
+
gradientColors?: string[],
|
|
218
|
+
) =>
|
|
219
|
+
StyleSheet.create({
|
|
220
|
+
container: {
|
|
221
|
+
flex: 1,
|
|
222
|
+
backgroundColor: backgroundColor || tokens.colors.primary,
|
|
223
|
+
paddingHorizontal: responsive.horizontalPadding,
|
|
224
|
+
paddingTop: insets.top,
|
|
225
|
+
paddingBottom: insets.bottom,
|
|
226
|
+
},
|
|
227
|
+
backgroundGradient: {
|
|
228
|
+
position: "absolute",
|
|
229
|
+
top: 0,
|
|
230
|
+
left: 0,
|
|
231
|
+
right: 0,
|
|
232
|
+
bottom: 0,
|
|
233
|
+
opacity: 0.9,
|
|
234
|
+
},
|
|
235
|
+
content: {
|
|
236
|
+
flex: 3,
|
|
237
|
+
alignItems: "center",
|
|
238
|
+
justifyContent: "center",
|
|
239
|
+
paddingHorizontal: tokens.spacing.lg,
|
|
240
|
+
},
|
|
241
|
+
logoContainer: {
|
|
242
|
+
marginBottom: tokens.spacing.xxl * 2,
|
|
243
|
+
alignItems: "center",
|
|
244
|
+
justifyContent: "center",
|
|
245
|
+
},
|
|
246
|
+
logoBackground: {
|
|
247
|
+
width: responsive.logoSize,
|
|
248
|
+
height: responsive.logoSize,
|
|
249
|
+
borderRadius: STATIC_TOKENS.borders.radius.full,
|
|
250
|
+
backgroundColor: tokens.colors.surface,
|
|
251
|
+
alignItems: "center",
|
|
252
|
+
justifyContent: "center",
|
|
253
|
+
padding: STATIC_TOKENS.spacing.md,
|
|
254
|
+
},
|
|
255
|
+
textContainer: {
|
|
256
|
+
alignItems: "center",
|
|
257
|
+
marginBottom: tokens.spacing.xxl * 2,
|
|
258
|
+
},
|
|
259
|
+
appName: {
|
|
260
|
+
...STATIC_TOKENS.typography.headingLarge,
|
|
261
|
+
color: tokens.colors.textInverse,
|
|
262
|
+
textAlign: "center",
|
|
263
|
+
marginBottom: tokens.spacing.md,
|
|
264
|
+
fontSize: responsive.getFontSize(STATIC_TOKENS.typography.headingLarge.fontSize ?? 32),
|
|
265
|
+
fontWeight: STATIC_TOKENS.typography.bold,
|
|
266
|
+
},
|
|
267
|
+
tagline: {
|
|
268
|
+
...STATIC_TOKENS.typography.bodyLarge,
|
|
269
|
+
color: tokens.colors.textInverse,
|
|
270
|
+
textAlign: "center",
|
|
271
|
+
opacity: 0.9,
|
|
272
|
+
maxWidth: responsive.maxContentWidth,
|
|
273
|
+
fontSize: responsive.getFontSize(STATIC_TOKENS.typography.bodyLarge.fontSize ?? 18),
|
|
274
|
+
},
|
|
275
|
+
loadingContainer: {
|
|
276
|
+
flex: 1,
|
|
277
|
+
alignItems: "center",
|
|
278
|
+
justifyContent: "center",
|
|
279
|
+
width: responsive.maxContentWidth,
|
|
280
|
+
alignSelf: "center",
|
|
281
|
+
},
|
|
282
|
+
loadingBar: {
|
|
283
|
+
width: "100%",
|
|
284
|
+
height: 4,
|
|
285
|
+
backgroundColor: withAlpha(tokens.colors.textInverse, 0.3),
|
|
286
|
+
borderRadius: STATIC_TOKENS.borders.radius.xs,
|
|
287
|
+
marginBottom: tokens.spacing.md,
|
|
288
|
+
overflow: "hidden",
|
|
289
|
+
},
|
|
290
|
+
loadingProgress: {
|
|
291
|
+
width: "100%",
|
|
292
|
+
height: "100%",
|
|
293
|
+
backgroundColor: tokens.colors.textInverse,
|
|
294
|
+
borderRadius: STATIC_TOKENS.borders.radius.xs,
|
|
295
|
+
},
|
|
296
|
+
loadingText: {
|
|
297
|
+
...STATIC_TOKENS.typography.bodyMedium,
|
|
298
|
+
color: tokens.colors.textInverse,
|
|
299
|
+
opacity: 0.8,
|
|
300
|
+
fontSize: responsive.getFontSize(STATIC_TOKENS.typography.bodyMedium.fontSize ?? 16),
|
|
301
|
+
},
|
|
302
|
+
footer: {
|
|
303
|
+
flex: 0.5,
|
|
304
|
+
alignItems: "center",
|
|
305
|
+
justifyContent: "flex-start",
|
|
306
|
+
paddingBottom: tokens.spacing.md,
|
|
307
|
+
},
|
|
308
|
+
footerText: {
|
|
309
|
+
...STATIC_TOKENS.typography.caption,
|
|
310
|
+
color: tokens.colors.textInverse,
|
|
311
|
+
opacity: 0.7,
|
|
312
|
+
marginBottom: tokens.spacing.xs,
|
|
313
|
+
fontSize: responsive.getFontSize(STATIC_TOKENS.typography.caption.fontSize ?? 12),
|
|
314
|
+
},
|
|
315
|
+
versionText: {
|
|
316
|
+
...STATIC_TOKENS.typography.caption,
|
|
317
|
+
color: tokens.colors.textInverse,
|
|
318
|
+
opacity: 0.5,
|
|
319
|
+
fontSize: responsive.getFontSize(STATIC_TOKENS.typography.caption.fontSize ?? 12),
|
|
320
|
+
},
|
|
321
|
+
});
|
|
322
|
+
|