@umituz/react-native-onboarding 3.3.9 → 3.3.11
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 +1 -1
- package/src/domain/entities/OnboardingSlide.ts +5 -0
- package/src/presentation/components/OnboardingFooter.tsx +12 -13
- package/src/presentation/components/OnboardingHeader.tsx +7 -9
- package/src/presentation/components/OnboardingSlide.tsx +11 -17
- package/src/presentation/components/QuestionSlide.tsx +5 -6
- package/src/presentation/components/QuestionSlideHeader.tsx +10 -15
- package/src/presentation/providers/OnboardingThemeProvider.tsx +98 -0
- package/src/presentation/screens/OnboardingScreen.tsx +32 -29
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@umituz/react-native-onboarding",
|
|
3
|
-
"version": "3.3.
|
|
3
|
+
"version": "3.3.11",
|
|
4
4
|
"description": "Advanced onboarding flow for React Native apps with personalization questions, theme-aware colors, animations, and customizable slides. SOLID, DRY, KISS principles applied.",
|
|
5
5
|
"main": "./src/index.ts",
|
|
6
6
|
"types": "./src/index.ts",
|
|
@@ -41,6 +41,11 @@ export interface OnboardingSlide {
|
|
|
41
41
|
*/
|
|
42
42
|
icon: string;
|
|
43
43
|
|
|
44
|
+
/**
|
|
45
|
+
* Type of icon: 'emoji' or 'icon' (default: 'icon')
|
|
46
|
+
*/
|
|
47
|
+
iconType?: 'emoji' | 'icon';
|
|
48
|
+
|
|
44
49
|
/**
|
|
45
50
|
* Gradient colors for the slide background (optional)
|
|
46
51
|
* [startColor, endColor] or [color1, color2, color3] for multi-stop gradients
|
|
@@ -1,8 +1,9 @@
|
|
|
1
|
-
import React
|
|
1
|
+
import React from "react";
|
|
2
2
|
import { View, StyleSheet } from "react-native";
|
|
3
3
|
import { useSafeAreaInsets } from "react-native-safe-area-context";
|
|
4
4
|
import { useLocalization } from "@umituz/react-native-localization";
|
|
5
|
-
import {
|
|
5
|
+
import { AtomicButton, AtomicText } from "@umituz/react-native-design-system";
|
|
6
|
+
import { useOnboardingTheme } from "../providers/OnboardingThemeProvider";
|
|
6
7
|
|
|
7
8
|
export interface OnboardingFooterProps {
|
|
8
9
|
currentIndex: number;
|
|
@@ -15,7 +16,6 @@ export interface OnboardingFooterProps {
|
|
|
15
16
|
nextButtonText?: string;
|
|
16
17
|
getStartedButtonText?: string;
|
|
17
18
|
disabled?: boolean;
|
|
18
|
-
useGradient?: boolean;
|
|
19
19
|
}
|
|
20
20
|
|
|
21
21
|
export const OnboardingFooter = ({
|
|
@@ -29,11 +29,10 @@ export const OnboardingFooter = ({
|
|
|
29
29
|
nextButtonText,
|
|
30
30
|
getStartedButtonText,
|
|
31
31
|
disabled = false,
|
|
32
|
-
useGradient = false,
|
|
33
32
|
}: OnboardingFooterProps) => {
|
|
34
33
|
const insets = useSafeAreaInsets();
|
|
35
34
|
const { t } = useLocalization();
|
|
36
|
-
const
|
|
35
|
+
const { colors } = useOnboardingTheme();
|
|
37
36
|
|
|
38
37
|
const buttonText = isLastSlide
|
|
39
38
|
? getStartedButtonText || t("onboarding.getStarted") || "Get Started"
|
|
@@ -45,8 +44,8 @@ export const OnboardingFooter = ({
|
|
|
45
44
|
<View style={[styles.footer, { paddingBottom: insets.bottom + 24 }]}>
|
|
46
45
|
{showProgressBar && (
|
|
47
46
|
<View style={styles.progressContainer}>
|
|
48
|
-
<View style={[styles.progressBar, { backgroundColor:
|
|
49
|
-
<View style={[styles.progressFill, { width: `${progressPercent}%`, backgroundColor:
|
|
47
|
+
<View style={[styles.progressBar, { backgroundColor: colors.progressBarBg }]}>
|
|
48
|
+
<View style={[styles.progressFill, { width: `${progressPercent}%`, backgroundColor: colors.progressFillColor }]} />
|
|
50
49
|
</View>
|
|
51
50
|
</View>
|
|
52
51
|
)}
|
|
@@ -58,10 +57,10 @@ export const OnboardingFooter = ({
|
|
|
58
57
|
key={index}
|
|
59
58
|
style={[
|
|
60
59
|
styles.dot,
|
|
61
|
-
{ backgroundColor:
|
|
60
|
+
{ backgroundColor: colors.dotColor },
|
|
62
61
|
index === currentIndex && {
|
|
63
62
|
width: 12,
|
|
64
|
-
backgroundColor:
|
|
63
|
+
backgroundColor: colors.activeDotColor
|
|
65
64
|
}
|
|
66
65
|
]}
|
|
67
66
|
/>
|
|
@@ -73,9 +72,9 @@ export const OnboardingFooter = ({
|
|
|
73
72
|
onPress={onNext}
|
|
74
73
|
disabled={disabled}
|
|
75
74
|
fullWidth
|
|
76
|
-
variant=
|
|
77
|
-
style={
|
|
78
|
-
textStyle={
|
|
75
|
+
variant="primary"
|
|
76
|
+
style={{ backgroundColor: colors.buttonBg }}
|
|
77
|
+
textStyle={{ color: colors.buttonTextColor }}
|
|
79
78
|
>
|
|
80
79
|
{buttonText}
|
|
81
80
|
</AtomicButton>
|
|
@@ -83,7 +82,7 @@ export const OnboardingFooter = ({
|
|
|
83
82
|
{showProgressText && (
|
|
84
83
|
<AtomicText
|
|
85
84
|
type="labelSmall"
|
|
86
|
-
style={[styles.progressText, { color:
|
|
85
|
+
style={[styles.progressText, { color: colors.progressTextColor }]}
|
|
87
86
|
>
|
|
88
87
|
{currentIndex + 1} {t("general.of") || "of"} {totalSlides}
|
|
89
88
|
</AtomicText>
|
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
import React from "react";
|
|
2
2
|
import { View, TouchableOpacity, StyleSheet } from "react-native";
|
|
3
|
-
import { AtomicIcon, AtomicText
|
|
3
|
+
import { AtomicIcon, AtomicText } from "@umituz/react-native-design-system";
|
|
4
4
|
import { useLocalization } from "@umituz/react-native-localization";
|
|
5
|
+
import { useOnboardingTheme } from "../providers/OnboardingThemeProvider";
|
|
5
6
|
|
|
6
7
|
export interface OnboardingHeaderProps {
|
|
7
8
|
isFirstSlide: boolean;
|
|
@@ -10,7 +11,6 @@ export interface OnboardingHeaderProps {
|
|
|
10
11
|
showBackButton?: boolean;
|
|
11
12
|
showSkipButton?: boolean;
|
|
12
13
|
skipButtonText?: string;
|
|
13
|
-
useGradient?: boolean;
|
|
14
14
|
}
|
|
15
15
|
|
|
16
16
|
export const OnboardingHeader = ({
|
|
@@ -20,13 +20,11 @@ export const OnboardingHeader = ({
|
|
|
20
20
|
showBackButton = true,
|
|
21
21
|
showSkipButton = true,
|
|
22
22
|
skipButtonText,
|
|
23
|
-
useGradient = false,
|
|
24
23
|
}: OnboardingHeaderProps) => {
|
|
25
24
|
const { t } = useLocalization();
|
|
26
|
-
const
|
|
25
|
+
const { colors } = useOnboardingTheme();
|
|
27
26
|
|
|
28
27
|
const skipText = skipButtonText || t("onboarding.skip") || "Skip";
|
|
29
|
-
const iconColor = useGradient ? "#FFFFFF" : tokens.colors.textPrimary;
|
|
30
28
|
|
|
31
29
|
return (
|
|
32
30
|
<View style={styles.header}>
|
|
@@ -37,15 +35,15 @@ export const OnboardingHeader = ({
|
|
|
37
35
|
style={[
|
|
38
36
|
styles.headerButton,
|
|
39
37
|
{
|
|
40
|
-
backgroundColor:
|
|
41
|
-
borderColor:
|
|
38
|
+
backgroundColor: colors.headerButtonBg,
|
|
39
|
+
borderColor: colors.headerButtonBorder,
|
|
42
40
|
},
|
|
43
41
|
isFirstSlide && styles.headerButtonDisabled,
|
|
44
42
|
]}
|
|
45
43
|
activeOpacity={0.7}
|
|
46
44
|
hitSlop={{ top: 10, bottom: 10, left: 10, right: 10 }}
|
|
47
45
|
>
|
|
48
|
-
<AtomicIcon name="chevron-back" customSize={20} customColor={iconColor} />
|
|
46
|
+
<AtomicIcon name="chevron-back" customSize={20} customColor={colors.iconColor} />
|
|
49
47
|
</TouchableOpacity>
|
|
50
48
|
) : (
|
|
51
49
|
<View style={styles.headerButton} />
|
|
@@ -54,7 +52,7 @@ export const OnboardingHeader = ({
|
|
|
54
52
|
<TouchableOpacity onPress={onSkip} activeOpacity={0.7}>
|
|
55
53
|
<AtomicText
|
|
56
54
|
type="labelLarge"
|
|
57
|
-
style={[styles.skipText, { color:
|
|
55
|
+
style={[styles.skipText, { color: colors.textColor }]}
|
|
58
56
|
>
|
|
59
57
|
{skipText}
|
|
60
58
|
</AtomicText>
|
|
@@ -1,49 +1,43 @@
|
|
|
1
1
|
import React from "react";
|
|
2
2
|
import { View, ScrollView, StyleSheet } from "react-native";
|
|
3
|
-
import { AtomicIcon, AtomicText
|
|
3
|
+
import { AtomicIcon, AtomicText } from "@umituz/react-native-design-system";
|
|
4
4
|
import type { OnboardingSlide as OnboardingSlideType } from "../../domain/entities/OnboardingSlide";
|
|
5
5
|
import type { OnboardingThemeVariant } from "../../domain/entities/OnboardingTheme";
|
|
6
|
+
import { useOnboardingTheme } from "../providers/OnboardingThemeProvider";
|
|
6
7
|
|
|
7
8
|
export interface OnboardingSlideProps {
|
|
8
9
|
slide: OnboardingSlideType;
|
|
9
|
-
useGradient?: boolean;
|
|
10
10
|
variant?: OnboardingThemeVariant;
|
|
11
11
|
}
|
|
12
12
|
|
|
13
|
-
const EMOJI_REGEX = /[\u{1F300}-\u{1F9FF}]|[\u{2600}-\u{26FF}]|[\u{2700}-\u{27BF}]/u;
|
|
14
|
-
|
|
15
13
|
export const OnboardingSlide = ({
|
|
16
14
|
slide,
|
|
17
|
-
useGradient = false,
|
|
18
15
|
variant = "default"
|
|
19
16
|
}: OnboardingSlideProps) => {
|
|
20
|
-
const
|
|
17
|
+
const { colors } = useOnboardingTheme();
|
|
21
18
|
|
|
22
|
-
const isEmoji =
|
|
23
|
-
const
|
|
24
|
-
const iconColor = useGradient ? "#FFFFFF" : tokens.colors.primary;
|
|
25
|
-
const textColor = useGradient ? "#FFFFFF" : tokens.colors.textPrimary;
|
|
26
|
-
const subTextColor = useGradient ? "rgba(255, 255, 255, 0.8)" : tokens.colors.textSecondary;
|
|
19
|
+
const isEmoji = slide.iconType === 'emoji';
|
|
20
|
+
const hasIcon = slide.icon && slide.icon.length > 0;
|
|
27
21
|
const iconSize = variant === "minimal" ? 80 : 72;
|
|
28
22
|
|
|
29
23
|
return (
|
|
30
24
|
<ScrollView contentContainerStyle={styles.content} showsVerticalScrollIndicator={false} bounces={false}>
|
|
31
25
|
<View style={styles.slideContainer}>
|
|
32
|
-
{
|
|
26
|
+
{hasIcon && (
|
|
33
27
|
<View style={styles.iconBox}>
|
|
34
28
|
{isEmoji ? (
|
|
35
29
|
<AtomicText style={{ fontSize: iconSize }}>{slide.icon}</AtomicText>
|
|
36
30
|
) : (
|
|
37
|
-
<AtomicIcon name={slide.icon} customSize={iconSize} customColor={iconColor} />
|
|
31
|
+
<AtomicIcon name={slide.icon} customSize={iconSize} customColor={colors.iconColor} />
|
|
38
32
|
)}
|
|
39
33
|
</View>
|
|
40
34
|
)}
|
|
41
35
|
|
|
42
|
-
<AtomicText type="headlineMedium" style={[styles.title, { color: textColor }]}>
|
|
36
|
+
<AtomicText type="headlineMedium" style={[styles.title, { color: colors.textColor }]}>
|
|
43
37
|
{slide.title}
|
|
44
38
|
</AtomicText>
|
|
45
39
|
|
|
46
|
-
<AtomicText type="bodyLarge" style={[styles.description, { color: subTextColor }]}>
|
|
40
|
+
<AtomicText type="bodyLarge" style={[styles.description, { color: colors.subTextColor }]}>
|
|
47
41
|
{slide.description}
|
|
48
42
|
</AtomicText>
|
|
49
43
|
|
|
@@ -51,8 +45,8 @@ export const OnboardingSlide = ({
|
|
|
51
45
|
<View style={styles.featuresContainer}>
|
|
52
46
|
{slide.features.map((feature, index) => (
|
|
53
47
|
<View key={index} style={styles.featureItem}>
|
|
54
|
-
<AtomicIcon name="checkmark-circle" size="sm" customColor={iconColor} />
|
|
55
|
-
<AtomicText type="bodyMedium" style={[styles.featureText, { color: textColor }]}>
|
|
48
|
+
<AtomicIcon name="checkmark-circle" size="sm" customColor={colors.iconColor} />
|
|
49
|
+
<AtomicText type="bodyMedium" style={[styles.featureText, { color: colors.textColor }]}>
|
|
56
50
|
{feature}
|
|
57
51
|
</AtomicText>
|
|
58
52
|
</View>
|
|
@@ -1,16 +1,16 @@
|
|
|
1
1
|
import React from "react";
|
|
2
2
|
import { View, ScrollView, StyleSheet } from "react-native";
|
|
3
|
-
import { AtomicText
|
|
3
|
+
import { AtomicText } from "@umituz/react-native-design-system";
|
|
4
4
|
import type { OnboardingSlide } from "../../domain/entities/OnboardingSlide";
|
|
5
5
|
import type { OnboardingThemeVariant } from "../../domain/entities/OnboardingTheme";
|
|
6
6
|
import { QuestionSlideHeader } from "./QuestionSlideHeader";
|
|
7
7
|
import { QuestionRenderer } from "./QuestionRenderer";
|
|
8
|
+
import { useOnboardingTheme } from "../providers/OnboardingThemeProvider";
|
|
8
9
|
|
|
9
10
|
export interface QuestionSlideProps {
|
|
10
11
|
slide: OnboardingSlide;
|
|
11
12
|
value: any;
|
|
12
13
|
onChange: (value: any) => void;
|
|
13
|
-
useGradient?: boolean;
|
|
14
14
|
variant?: OnboardingThemeVariant;
|
|
15
15
|
}
|
|
16
16
|
|
|
@@ -18,9 +18,8 @@ export const QuestionSlide = ({
|
|
|
18
18
|
slide,
|
|
19
19
|
value,
|
|
20
20
|
onChange,
|
|
21
|
-
useGradient = false,
|
|
22
21
|
}: QuestionSlideProps) => {
|
|
23
|
-
const
|
|
22
|
+
const { colors } = useOnboardingTheme();
|
|
24
23
|
const { question } = slide;
|
|
25
24
|
|
|
26
25
|
if (!question) return null;
|
|
@@ -28,7 +27,7 @@ export const QuestionSlide = ({
|
|
|
28
27
|
return (
|
|
29
28
|
<ScrollView contentContainerStyle={styles.content} showsVerticalScrollIndicator={false} bounces={false}>
|
|
30
29
|
<View style={styles.slideContainer}>
|
|
31
|
-
<QuestionSlideHeader slide={slide}
|
|
30
|
+
<QuestionSlideHeader slide={slide} />
|
|
32
31
|
|
|
33
32
|
<View style={styles.questionContainer}>
|
|
34
33
|
<QuestionRenderer question={question} value={value} onChange={onChange} />
|
|
@@ -37,7 +36,7 @@ export const QuestionSlide = ({
|
|
|
37
36
|
{question.validation?.required && !value && (
|
|
38
37
|
<AtomicText
|
|
39
38
|
type="labelSmall"
|
|
40
|
-
style={[styles.requiredHint, { color:
|
|
39
|
+
style={[styles.requiredHint, { color: colors.errorColor }]}
|
|
41
40
|
>
|
|
42
41
|
* This field is required
|
|
43
42
|
</AtomicText>
|
|
@@ -1,46 +1,41 @@
|
|
|
1
1
|
import React from "react";
|
|
2
2
|
import { View, StyleSheet } from "react-native";
|
|
3
|
-
import { AtomicIcon, AtomicText
|
|
3
|
+
import { AtomicIcon, AtomicText } from "@umituz/react-native-design-system";
|
|
4
4
|
import type { OnboardingSlide } from "../../domain/entities/OnboardingSlide";
|
|
5
|
+
import { useOnboardingTheme } from "../providers/OnboardingThemeProvider";
|
|
5
6
|
|
|
6
7
|
export interface QuestionSlideHeaderProps {
|
|
7
8
|
slide: OnboardingSlide;
|
|
8
|
-
useGradient: boolean;
|
|
9
9
|
}
|
|
10
10
|
|
|
11
|
-
const isEmoji = (icon: string): boolean =>
|
|
12
|
-
/[\u{1F300}-\u{1F9FF}]|[\u{2600}-\u{26FF}]|[\u{2700}-\u{27BF}]/u.test(icon);
|
|
13
|
-
|
|
14
11
|
export const QuestionSlideHeader = ({
|
|
15
12
|
slide,
|
|
16
|
-
useGradient,
|
|
17
13
|
}: QuestionSlideHeaderProps) => {
|
|
18
|
-
const
|
|
19
|
-
const
|
|
20
|
-
const subTextColor = useGradient ? "rgba(255, 255, 255, 0.9)" : tokens.colors.textSecondary;
|
|
14
|
+
const { colors } = useOnboardingTheme();
|
|
15
|
+
const isEmoji = slide.iconType === 'emoji';
|
|
21
16
|
|
|
22
17
|
return (
|
|
23
18
|
<View style={styles.container}>
|
|
24
19
|
<View style={[
|
|
25
20
|
styles.iconContainer,
|
|
26
21
|
{
|
|
27
|
-
backgroundColor:
|
|
28
|
-
borderColor:
|
|
22
|
+
backgroundColor: colors.iconBg,
|
|
23
|
+
borderColor: colors.iconBorder,
|
|
29
24
|
}
|
|
30
25
|
]}>
|
|
31
|
-
{isEmoji
|
|
26
|
+
{isEmoji ? (
|
|
32
27
|
<AtomicText style={{ fontSize: 48 }}>{slide.icon}</AtomicText>
|
|
33
28
|
) : (
|
|
34
|
-
<AtomicIcon name={slide.icon as any} customSize={48} customColor={textColor} />
|
|
29
|
+
<AtomicIcon name={slide.icon as any} customSize={48} customColor={colors.textColor} />
|
|
35
30
|
)}
|
|
36
31
|
</View>
|
|
37
32
|
|
|
38
|
-
<AtomicText type="headlineMedium" style={[styles.title, { color: textColor }]}>
|
|
33
|
+
<AtomicText type="headlineMedium" style={[styles.title, { color: colors.textColor }]}>
|
|
39
34
|
{slide.title}
|
|
40
35
|
</AtomicText>
|
|
41
36
|
|
|
42
37
|
{slide.description && (
|
|
43
|
-
<AtomicText type="bodyMedium" style={[styles.description, { color: subTextColor }]}>
|
|
38
|
+
<AtomicText type="bodyMedium" style={[styles.description, { color: colors.subTextColor }]}>
|
|
44
39
|
{slide.description}
|
|
45
40
|
</AtomicText>
|
|
46
41
|
)}
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
import React, { createContext, useContext, useMemo } from 'react';
|
|
2
|
+
import { useAppDesignTokens } from '@umituz/react-native-design-system';
|
|
3
|
+
|
|
4
|
+
interface OnboardingColors {
|
|
5
|
+
iconColor: string;
|
|
6
|
+
textColor: string;
|
|
7
|
+
subTextColor: string;
|
|
8
|
+
buttonBg: string;
|
|
9
|
+
buttonTextColor: string;
|
|
10
|
+
progressBarBg: string;
|
|
11
|
+
progressFillColor: string;
|
|
12
|
+
dotColor: string;
|
|
13
|
+
activeDotColor: string;
|
|
14
|
+
progressTextColor: string;
|
|
15
|
+
headerButtonBg: string;
|
|
16
|
+
headerButtonBorder: string;
|
|
17
|
+
iconBg: string;
|
|
18
|
+
iconBorder: string;
|
|
19
|
+
errorColor: string;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
interface OnboardingThemeValue {
|
|
23
|
+
colors: OnboardingColors;
|
|
24
|
+
useGradient: boolean;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
const OnboardingTheme = createContext<OnboardingThemeValue | null>(null);
|
|
28
|
+
|
|
29
|
+
export interface OnboardingThemeProviderProps {
|
|
30
|
+
children: React.ReactNode;
|
|
31
|
+
useGradient?: boolean;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
export const OnboardingThemeProvider = ({
|
|
35
|
+
children,
|
|
36
|
+
useGradient = false,
|
|
37
|
+
}: OnboardingThemeProviderProps) => {
|
|
38
|
+
const tokens = useAppDesignTokens();
|
|
39
|
+
|
|
40
|
+
const colors = useMemo<OnboardingColors>(() => {
|
|
41
|
+
if (useGradient) {
|
|
42
|
+
return {
|
|
43
|
+
iconColor: tokens.colors.surface,
|
|
44
|
+
textColor: tokens.colors.surface,
|
|
45
|
+
subTextColor: tokens.colors.surface + 'CC',
|
|
46
|
+
buttonBg: tokens.colors.surface,
|
|
47
|
+
buttonTextColor: tokens.colors.primary,
|
|
48
|
+
progressBarBg: tokens.colors.surface + '30',
|
|
49
|
+
progressFillColor: tokens.colors.surface,
|
|
50
|
+
dotColor: tokens.colors.surface + '60',
|
|
51
|
+
activeDotColor: tokens.colors.surface,
|
|
52
|
+
progressTextColor: tokens.colors.surface + 'CC',
|
|
53
|
+
headerButtonBg: tokens.colors.surface + '30',
|
|
54
|
+
headerButtonBorder: tokens.colors.surface + '50',
|
|
55
|
+
iconBg: tokens.colors.surface + '30',
|
|
56
|
+
iconBorder: tokens.colors.surface + '60',
|
|
57
|
+
errorColor: tokens.colors.surface,
|
|
58
|
+
};
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
return {
|
|
62
|
+
iconColor: tokens.colors.primary,
|
|
63
|
+
textColor: tokens.colors.textPrimary,
|
|
64
|
+
subTextColor: tokens.colors.textSecondary,
|
|
65
|
+
buttonBg: tokens.colors.primary,
|
|
66
|
+
buttonTextColor: tokens.colors.onPrimary,
|
|
67
|
+
progressBarBg: tokens.colors.borderLight,
|
|
68
|
+
progressFillColor: tokens.colors.primary,
|
|
69
|
+
dotColor: tokens.colors.borderLight,
|
|
70
|
+
activeDotColor: tokens.colors.primary,
|
|
71
|
+
progressTextColor: tokens.colors.textSecondary,
|
|
72
|
+
headerButtonBg: tokens.colors.surface,
|
|
73
|
+
headerButtonBorder: tokens.colors.borderLight,
|
|
74
|
+
iconBg: tokens.colors.primary + '15',
|
|
75
|
+
iconBorder: tokens.colors.primary + '30',
|
|
76
|
+
errorColor: tokens.colors.error,
|
|
77
|
+
};
|
|
78
|
+
}, [tokens, useGradient]);
|
|
79
|
+
|
|
80
|
+
const value = useMemo(
|
|
81
|
+
() => ({ colors, useGradient }),
|
|
82
|
+
[colors, useGradient]
|
|
83
|
+
);
|
|
84
|
+
|
|
85
|
+
return (
|
|
86
|
+
<OnboardingTheme.Provider value={value}>
|
|
87
|
+
{children}
|
|
88
|
+
</OnboardingTheme.Provider>
|
|
89
|
+
);
|
|
90
|
+
};
|
|
91
|
+
|
|
92
|
+
export const useOnboardingTheme = (): OnboardingThemeValue => {
|
|
93
|
+
const theme = useContext(OnboardingTheme);
|
|
94
|
+
if (!theme) {
|
|
95
|
+
throw new Error('useOnboardingTheme must be used within OnboardingThemeProvider');
|
|
96
|
+
}
|
|
97
|
+
return theme;
|
|
98
|
+
};
|
|
@@ -12,6 +12,7 @@ import { StyleSheet } from "react-native";
|
|
|
12
12
|
import type { OnboardingOptions } from "../../domain/entities/OnboardingOptions";
|
|
13
13
|
import { useOnboardingScreenState } from "../hooks/useOnboardingScreenState";
|
|
14
14
|
import { OnboardingScreenContent } from "../components/OnboardingScreenContent";
|
|
15
|
+
import { OnboardingThemeProvider } from "../providers/OnboardingThemeProvider";
|
|
15
16
|
|
|
16
17
|
export interface OnboardingScreenProps extends OnboardingOptions {
|
|
17
18
|
/**
|
|
@@ -114,35 +115,37 @@ export const OnboardingScreen = ({
|
|
|
114
115
|
}
|
|
115
116
|
|
|
116
117
|
return (
|
|
117
|
-
<
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
118
|
+
<OnboardingThemeProvider useGradient={useGradient}>
|
|
119
|
+
<OnboardingScreenContent
|
|
120
|
+
containerStyle={[styles.container, containerStyle]}
|
|
121
|
+
useGradient={useGradient}
|
|
122
|
+
currentSlide={currentSlide}
|
|
123
|
+
isFirstSlide={isFirstSlide}
|
|
124
|
+
isLastSlide={isLastSlide}
|
|
125
|
+
currentIndex={currentIndex}
|
|
126
|
+
totalSlides={filteredSlides.length}
|
|
127
|
+
currentAnswer={currentAnswer}
|
|
128
|
+
isAnswerValid={isAnswerValid}
|
|
129
|
+
showBackButton={showBackButton}
|
|
130
|
+
showSkipButton={showSkipButton}
|
|
131
|
+
showProgressBar={showProgressBar}
|
|
132
|
+
showDots={showDots}
|
|
133
|
+
showProgressText={showProgressText}
|
|
134
|
+
skipButtonText={skipButtonText}
|
|
135
|
+
nextButtonText={nextButtonText}
|
|
136
|
+
getStartedButtonText={getStartedButtonText}
|
|
137
|
+
onBack={handlePrevious}
|
|
138
|
+
onSkip={handleSkip}
|
|
139
|
+
onNext={handleNext}
|
|
140
|
+
onAnswerChange={setCurrentAnswer}
|
|
141
|
+
renderHeader={renderHeader}
|
|
142
|
+
renderFooter={renderFooter}
|
|
143
|
+
renderSlide={renderSlide}
|
|
144
|
+
onUpgrade={onUpgrade}
|
|
145
|
+
showPaywallOnComplete={showPaywallOnComplete}
|
|
146
|
+
variant={themeVariant}
|
|
147
|
+
/>
|
|
148
|
+
</OnboardingThemeProvider>
|
|
146
149
|
);
|
|
147
150
|
};
|
|
148
151
|
|