@rocapine/react-native-onboarding-ui 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/dist/UI/Components/CircularProgress.d.ts +8 -0
- package/dist/UI/Components/CircularProgress.d.ts.map +1 -0
- package/dist/UI/Components/CircularProgress.js +104 -0
- package/dist/UI/Components/CircularProgress.js.map +1 -0
- package/dist/UI/Components/ProgressBar.d.ts +12 -0
- package/dist/UI/Components/ProgressBar.d.ts.map +1 -0
- package/dist/UI/Components/ProgressBar.js +121 -0
- package/dist/UI/Components/ProgressBar.js.map +1 -0
- package/dist/UI/Components/StaggeredTextList.d.ts +11 -0
- package/dist/UI/Components/StaggeredTextList.d.ts.map +1 -0
- package/dist/UI/Components/StaggeredTextList.js +111 -0
- package/dist/UI/Components/StaggeredTextList.js.map +1 -0
- package/dist/UI/Components/index.d.ts +4 -0
- package/dist/UI/Components/index.d.ts.map +1 -0
- package/dist/UI/Components/index.js +20 -0
- package/dist/UI/Components/index.js.map +1 -0
- package/dist/UI/ErrorBoundary/ErrorBoundary.d.ts +19 -0
- package/dist/UI/ErrorBoundary/ErrorBoundary.d.ts.map +1 -0
- package/dist/UI/ErrorBoundary/ErrorBoundary.js +123 -0
- package/dist/UI/ErrorBoundary/ErrorBoundary.js.map +1 -0
- package/dist/UI/ErrorBoundary/index.d.ts +3 -0
- package/dist/UI/ErrorBoundary/index.d.ts.map +1 -0
- package/dist/UI/ErrorBoundary/index.js +8 -0
- package/dist/UI/ErrorBoundary/index.js.map +1 -0
- package/dist/UI/ErrorBoundary/withErrorBoundary.d.ts +6 -0
- package/dist/UI/ErrorBoundary/withErrorBoundary.d.ts.map +1 -0
- package/dist/UI/ErrorBoundary/withErrorBoundary.js +13 -0
- package/dist/UI/ErrorBoundary/withErrorBoundary.js.map +1 -0
- package/dist/UI/OnboardingPage.d.ts +16 -0
- package/dist/UI/OnboardingPage.d.ts.map +1 -0
- package/dist/UI/OnboardingPage.js +38 -0
- package/dist/UI/OnboardingPage.js.map +1 -0
- package/dist/UI/Pages/Carousel/Renderer.d.ts +13 -0
- package/dist/UI/Pages/Carousel/Renderer.d.ts.map +1 -0
- package/dist/UI/Pages/Carousel/Renderer.js +121 -0
- package/dist/UI/Pages/Carousel/Renderer.js.map +1 -0
- package/dist/UI/Pages/Carousel/index.d.ts +3 -0
- package/dist/UI/Pages/Carousel/index.d.ts.map +1 -0
- package/dist/UI/Pages/Carousel/index.js +19 -0
- package/dist/UI/Pages/Carousel/index.js.map +1 -0
- package/dist/UI/Pages/Carousel/types.d.ts +32 -0
- package/dist/UI/Pages/Carousel/types.d.ts.map +1 -0
- package/dist/UI/Pages/Carousel/types.js +24 -0
- package/dist/UI/Pages/Carousel/types.js.map +1 -0
- package/dist/UI/Pages/Commitment/Renderer.d.ts +13 -0
- package/dist/UI/Pages/Commitment/Renderer.d.ts.map +1 -0
- package/dist/UI/Pages/Commitment/Renderer.js +173 -0
- package/dist/UI/Pages/Commitment/Renderer.js.map +1 -0
- package/dist/UI/Pages/Commitment/index.d.ts +3 -0
- package/dist/UI/Pages/Commitment/index.d.ts.map +1 -0
- package/dist/UI/Pages/Commitment/index.js +19 -0
- package/dist/UI/Pages/Commitment/index.js.map +1 -0
- package/dist/UI/Pages/Commitment/types.d.ts +41 -0
- package/dist/UI/Pages/Commitment/types.d.ts.map +1 -0
- package/dist/UI/Pages/Commitment/types.js +27 -0
- package/dist/UI/Pages/Commitment/types.js.map +1 -0
- package/dist/UI/Pages/Loader/Renderer.d.ts +10 -0
- package/dist/UI/Pages/Loader/Renderer.d.ts.map +1 -0
- package/dist/UI/Pages/Loader/Renderer.js +215 -0
- package/dist/UI/Pages/Loader/Renderer.js.map +1 -0
- package/dist/UI/Pages/Loader/index.d.ts +3 -0
- package/dist/UI/Pages/Loader/index.d.ts.map +1 -0
- package/dist/UI/Pages/Loader/index.js +19 -0
- package/dist/UI/Pages/Loader/index.js.map +1 -0
- package/dist/UI/Pages/Loader/types.d.ts +57 -0
- package/dist/UI/Pages/Loader/types.d.ts.map +1 -0
- package/dist/UI/Pages/Loader/types.js +30 -0
- package/dist/UI/Pages/Loader/types.js.map +1 -0
- package/dist/UI/Pages/MediaContent/Renderer.d.ts +13 -0
- package/dist/UI/Pages/MediaContent/Renderer.d.ts.map +1 -0
- package/dist/UI/Pages/MediaContent/Renderer.js +76 -0
- package/dist/UI/Pages/MediaContent/Renderer.js.map +1 -0
- package/dist/UI/Pages/MediaContent/index.d.ts +3 -0
- package/dist/UI/Pages/MediaContent/index.d.ts.map +1 -0
- package/dist/UI/Pages/MediaContent/index.js +19 -0
- package/dist/UI/Pages/MediaContent/index.js.map +1 -0
- package/dist/UI/Pages/MediaContent/types.d.ts +44 -0
- package/dist/UI/Pages/MediaContent/types.d.ts.map +1 -0
- package/dist/UI/Pages/MediaContent/types.js +22 -0
- package/dist/UI/Pages/MediaContent/types.js.map +1 -0
- package/dist/UI/Pages/Picker/Renderer.d.ts +13 -0
- package/dist/UI/Pages/Picker/Renderer.d.ts.map +1 -0
- package/dist/UI/Pages/Picker/Renderer.js +268 -0
- package/dist/UI/Pages/Picker/Renderer.js.map +1 -0
- package/dist/UI/Pages/Picker/index.d.ts +3 -0
- package/dist/UI/Pages/Picker/index.d.ts.map +1 -0
- package/dist/UI/Pages/Picker/index.js +19 -0
- package/dist/UI/Pages/Picker/index.js.map +1 -0
- package/dist/UI/Pages/Picker/types.d.ts +49 -0
- package/dist/UI/Pages/Picker/types.d.ts.map +1 -0
- package/dist/UI/Pages/Picker/types.js +30 -0
- package/dist/UI/Pages/Picker/types.js.map +1 -0
- package/dist/UI/Pages/Question/Renderer.d.ts +18 -0
- package/dist/UI/Pages/Question/Renderer.d.ts.map +1 -0
- package/dist/UI/Pages/Question/Renderer.js +128 -0
- package/dist/UI/Pages/Question/Renderer.js.map +1 -0
- package/dist/UI/Pages/Question/components.d.ts +57 -0
- package/dist/UI/Pages/Question/components.d.ts.map +1 -0
- package/dist/UI/Pages/Question/components.js +57 -0
- package/dist/UI/Pages/Question/components.js.map +1 -0
- package/dist/UI/Pages/Question/index.d.ts +4 -0
- package/dist/UI/Pages/Question/index.d.ts.map +1 -0
- package/dist/UI/Pages/Question/index.js +20 -0
- package/dist/UI/Pages/Question/index.js.map +1 -0
- package/dist/UI/Pages/Question/types.d.ts +47 -0
- package/dist/UI/Pages/Question/types.d.ts.map +1 -0
- package/dist/UI/Pages/Question/types.js +28 -0
- package/dist/UI/Pages/Question/types.js.map +1 -0
- package/dist/UI/Pages/Ratings/Renderer.d.ts +13 -0
- package/dist/UI/Pages/Ratings/Renderer.d.ts.map +1 -0
- package/dist/UI/Pages/Ratings/Renderer.js +201 -0
- package/dist/UI/Pages/Ratings/Renderer.js.map +1 -0
- package/dist/UI/Pages/Ratings/index.d.ts +3 -0
- package/dist/UI/Pages/Ratings/index.d.ts.map +1 -0
- package/dist/UI/Pages/Ratings/index.js +19 -0
- package/dist/UI/Pages/Ratings/index.js.map +1 -0
- package/dist/UI/Pages/Ratings/types.d.ts +32 -0
- package/dist/UI/Pages/Ratings/types.d.ts.map +1 -0
- package/dist/UI/Pages/Ratings/types.js +25 -0
- package/dist/UI/Pages/Ratings/types.js.map +1 -0
- package/dist/UI/Pages/index.d.ts +9 -0
- package/dist/UI/Pages/index.d.ts.map +1 -0
- package/dist/UI/Pages/index.js +26 -0
- package/dist/UI/Pages/index.js.map +1 -0
- package/dist/UI/Pages/types.d.ts +19 -0
- package/dist/UI/Pages/types.d.ts.map +1 -0
- package/dist/UI/Pages/types.js +25 -0
- package/dist/UI/Pages/types.js.map +1 -0
- package/dist/UI/Provider/OnboardingProgressProvider.d.ts +18 -0
- package/dist/UI/Provider/OnboardingProgressProvider.d.ts.map +1 -0
- package/dist/UI/Provider/OnboardingProgressProvider.js +23 -0
- package/dist/UI/Provider/OnboardingProgressProvider.js.map +1 -0
- package/dist/UI/Provider/index.d.ts +2 -0
- package/dist/UI/Provider/index.d.ts.map +1 -0
- package/dist/UI/Provider/index.js +7 -0
- package/dist/UI/Provider/index.js.map +1 -0
- package/dist/UI/Templates/OnboardingTemplate.d.ts +15 -0
- package/dist/UI/Templates/OnboardingTemplate.d.ts.map +1 -0
- package/dist/UI/Templates/OnboardingTemplate.js +48 -0
- package/dist/UI/Templates/OnboardingTemplate.js.map +1 -0
- package/dist/UI/Templates/index.d.ts +2 -0
- package/dist/UI/Templates/index.d.ts.map +1 -0
- package/dist/UI/Templates/index.js +6 -0
- package/dist/UI/Templates/index.js.map +1 -0
- package/dist/UI/Theme/ThemeProvider.d.ts +27 -0
- package/dist/UI/Theme/ThemeProvider.d.ts.map +1 -0
- package/dist/UI/Theme/ThemeProvider.js +49 -0
- package/dist/UI/Theme/ThemeProvider.js.map +1 -0
- package/dist/UI/Theme/defaultTheme.d.ts +7 -0
- package/dist/UI/Theme/defaultTheme.d.ts.map +1 -0
- package/dist/UI/Theme/defaultTheme.js +14 -0
- package/dist/UI/Theme/defaultTheme.js.map +1 -0
- package/dist/UI/Theme/helpers.d.ts +12 -0
- package/dist/UI/Theme/helpers.d.ts.map +1 -0
- package/dist/UI/Theme/helpers.js +21 -0
- package/dist/UI/Theme/helpers.js.map +1 -0
- package/dist/UI/Theme/index.d.ts +8 -0
- package/dist/UI/Theme/index.d.ts.map +1 -0
- package/dist/UI/Theme/index.js +24 -0
- package/dist/UI/Theme/index.js.map +1 -0
- package/dist/UI/Theme/token.d.ts +107 -0
- package/dist/UI/Theme/token.d.ts.map +1 -0
- package/dist/UI/Theme/token.js +108 -0
- package/dist/UI/Theme/token.js.map +1 -0
- package/dist/UI/Theme/tokens/darkTokens.d.ts +24 -0
- package/dist/UI/Theme/tokens/darkTokens.d.ts.map +1 -0
- package/dist/UI/Theme/tokens/darkTokens.js +27 -0
- package/dist/UI/Theme/tokens/darkTokens.js.map +1 -0
- package/dist/UI/Theme/tokens/index.d.ts +4 -0
- package/dist/UI/Theme/tokens/index.d.ts.map +1 -0
- package/dist/UI/Theme/tokens/index.js +20 -0
- package/dist/UI/Theme/tokens/index.js.map +1 -0
- package/dist/UI/Theme/tokens/lightTokens.d.ts +24 -0
- package/dist/UI/Theme/tokens/lightTokens.d.ts.map +1 -0
- package/dist/UI/Theme/tokens/lightTokens.js +27 -0
- package/dist/UI/Theme/tokens/lightTokens.js.map +1 -0
- package/dist/UI/Theme/tokens/typography.d.ts +65 -0
- package/dist/UI/Theme/tokens/typography.d.ts.map +1 -0
- package/dist/UI/Theme/tokens/typography.js +68 -0
- package/dist/UI/Theme/tokens/typography.js.map +1 -0
- package/dist/UI/Theme/types.d.ts +65 -0
- package/dist/UI/Theme/types.d.ts.map +1 -0
- package/dist/UI/Theme/types.js +3 -0
- package/dist/UI/Theme/types.js.map +1 -0
- package/dist/UI/Theme/useTheme.d.ts +7 -0
- package/dist/UI/Theme/useTheme.d.ts.map +1 -0
- package/dist/UI/Theme/useTheme.js +23 -0
- package/dist/UI/Theme/useTheme.js.map +1 -0
- package/dist/UI/Theme/utils.d.ts +12 -0
- package/dist/UI/Theme/utils.d.ts.map +1 -0
- package/dist/UI/Theme/utils.js +55 -0
- package/dist/UI/Theme/utils.js.map +1 -0
- package/dist/UI/index.d.ts +8 -0
- package/dist/UI/index.d.ts.map +1 -0
- package/dist/UI/index.js +24 -0
- package/dist/UI/index.js.map +1 -0
- package/dist/UI/types.d.ts +23 -0
- package/dist/UI/types.d.ts.map +1 -0
- package/dist/UI/types.js +3 -0
- package/dist/UI/types.js.map +1 -0
- package/dist/assets/laurel-left.png +0 -0
- package/dist/assets/laurel-right.png +0 -0
- package/dist/assets/star-filled.png +0 -0
- package/dist/index.d.ts +9 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +33 -0
- package/dist/index.js.map +1 -0
- package/dist/provider/CustomComponentsContext.d.ts +57 -0
- package/dist/provider/CustomComponentsContext.d.ts.map +1 -0
- package/dist/provider/CustomComponentsContext.js +19 -0
- package/dist/provider/CustomComponentsContext.js.map +1 -0
- package/dist/provider/OnboardingUIProvider.d.ts +44 -0
- package/dist/provider/OnboardingUIProvider.d.ts.map +1 -0
- package/dist/provider/OnboardingUIProvider.js +33 -0
- package/dist/provider/OnboardingUIProvider.js.map +1 -0
- package/dist/provider/OnboardingUIProvider.old.d.ts +60 -0
- package/dist/provider/OnboardingUIProvider.old.d.ts.map +1 -0
- package/dist/provider/OnboardingUIProvider.old.js +53 -0
- package/dist/provider/OnboardingUIProvider.old.js.map +1 -0
- package/package.json +77 -0
- package/src/UI/Components/CircularProgress.tsx +146 -0
- package/src/UI/Components/ProgressBar.tsx +143 -0
- package/src/UI/Components/StaggeredTextList.tsx +152 -0
- package/src/UI/Components/index.ts +3 -0
- package/src/UI/ErrorBoundary/ErrorBoundary.tsx +181 -0
- package/src/UI/ErrorBoundary/README.md +71 -0
- package/src/UI/ErrorBoundary/index.ts +2 -0
- package/src/UI/ErrorBoundary/withErrorBoundary.tsx +19 -0
- package/src/UI/OnboardingPage.tsx +53 -0
- package/src/UI/Pages/Carousel/Renderer.tsx +210 -0
- package/src/UI/Pages/Carousel/index.ts +2 -0
- package/src/UI/Pages/Carousel/types.ts +26 -0
- package/src/UI/Pages/Commitment/Renderer.tsx +312 -0
- package/src/UI/Pages/Commitment/index.ts +2 -0
- package/src/UI/Pages/Commitment/types.ts +28 -0
- package/src/UI/Pages/Loader/Renderer.tsx +417 -0
- package/src/UI/Pages/Loader/index.ts +2 -0
- package/src/UI/Pages/Loader/types.ts +32 -0
- package/src/UI/Pages/MediaContent/Renderer.tsx +130 -0
- package/src/UI/Pages/MediaContent/index.ts +2 -0
- package/src/UI/Pages/MediaContent/types.ts +26 -0
- package/src/UI/Pages/Picker/Renderer.tsx +618 -0
- package/src/UI/Pages/Picker/index.ts +2 -0
- package/src/UI/Pages/Picker/types.ts +34 -0
- package/src/UI/Pages/Question/Renderer.tsx +208 -0
- package/src/UI/Pages/Question/components.tsx +130 -0
- package/src/UI/Pages/Question/index.ts +3 -0
- package/src/UI/Pages/Question/types.ts +29 -0
- package/src/UI/Pages/Ratings/Renderer.tsx +282 -0
- package/src/UI/Pages/Ratings/index.ts +2 -0
- package/src/UI/Pages/Ratings/types.ts +22 -0
- package/src/UI/Pages/index.ts +10 -0
- package/src/UI/Pages/types.ts +25 -0
- package/src/UI/Provider/OnboardingProgressProvider.tsx +40 -0
- package/src/UI/Provider/index.ts +1 -0
- package/src/UI/Templates/OnboardingTemplate.tsx +86 -0
- package/src/UI/Templates/index.ts +1 -0
- package/src/UI/Theme/ThemeProvider.tsx +100 -0
- package/src/UI/Theme/defaultTheme.ts +12 -0
- package/src/UI/Theme/helpers.ts +24 -0
- package/src/UI/Theme/index.ts +7 -0
- package/src/UI/Theme/token.ts +106 -0
- package/src/UI/Theme/tokens/darkTokens.ts +25 -0
- package/src/UI/Theme/tokens/index.ts +3 -0
- package/src/UI/Theme/tokens/lightTokens.ts +25 -0
- package/src/UI/Theme/tokens/typography.ts +66 -0
- package/src/UI/Theme/types.ts +72 -0
- package/src/UI/Theme/useTheme.ts +22 -0
- package/src/UI/Theme/utils.ts +67 -0
- package/src/UI/index.ts +7 -0
- package/src/UI/types.ts +41 -0
- package/src/assets/laurel-left.png +0 -0
- package/src/assets/laurel-right.png +0 -0
- package/src/assets/star-filled.png +0 -0
- package/src/index.ts +28 -0
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
|
|
3
|
+
export const CustomPayloadSchema = z.record(z.string(), z.any()).nullish();
|
|
4
|
+
|
|
5
|
+
export const MediaSourceSchema = z.union([
|
|
6
|
+
z.object({
|
|
7
|
+
type: z.literal("image").or(z.literal("lottie")).or(z.literal("rive")),
|
|
8
|
+
localPathId: z.string(),
|
|
9
|
+
}),
|
|
10
|
+
z.object({
|
|
11
|
+
type: z.literal("image").or(z.literal("lottie")).or(z.literal("rive")),
|
|
12
|
+
url: z.string(),
|
|
13
|
+
}),
|
|
14
|
+
]);
|
|
15
|
+
|
|
16
|
+
export const SocialProofSchema = z.object({
|
|
17
|
+
numberOfStar: z.number(),
|
|
18
|
+
content: z.string(),
|
|
19
|
+
authorName: z.string(),
|
|
20
|
+
});
|
|
21
|
+
|
|
22
|
+
export const InfoBoxSchema = z.object({
|
|
23
|
+
title: z.string(),
|
|
24
|
+
content: z.string(),
|
|
25
|
+
});
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import { createContext, useState } from "react";
|
|
2
|
+
import { SafeAreaProvider } from "react-native-safe-area-context";
|
|
3
|
+
import { ThemeProvider } from "../Theme/ThemeProvider";
|
|
4
|
+
import { ColorScheme } from "../Theme/types";
|
|
5
|
+
|
|
6
|
+
export const OnboardingProgressProvider = ({
|
|
7
|
+
children,
|
|
8
|
+
initialColorScheme = "light",
|
|
9
|
+
}: {
|
|
10
|
+
children: React.ReactNode;
|
|
11
|
+
initialColorScheme?: ColorScheme;
|
|
12
|
+
}) => {
|
|
13
|
+
const [activeStep, setActiveStep] = useState({
|
|
14
|
+
number: 0,
|
|
15
|
+
displayProgressHeader: false,
|
|
16
|
+
});
|
|
17
|
+
const [totalSteps, setTotalSteps] = useState(0);
|
|
18
|
+
|
|
19
|
+
return (
|
|
20
|
+
<SafeAreaProvider>
|
|
21
|
+
<ThemeProvider initialColorScheme={initialColorScheme}>
|
|
22
|
+
<OnboardingProgressContext.Provider
|
|
23
|
+
value={{ activeStep, setActiveStep, totalSteps, setTotalSteps }}
|
|
24
|
+
>
|
|
25
|
+
{children}
|
|
26
|
+
</OnboardingProgressContext.Provider>
|
|
27
|
+
</ThemeProvider>
|
|
28
|
+
</SafeAreaProvider>
|
|
29
|
+
);
|
|
30
|
+
};
|
|
31
|
+
|
|
32
|
+
export const OnboardingProgressContext = createContext({
|
|
33
|
+
activeStep: { number: 0, displayProgressHeader: false },
|
|
34
|
+
setActiveStep: (step: {
|
|
35
|
+
number: number;
|
|
36
|
+
displayProgressHeader: boolean;
|
|
37
|
+
}) => {},
|
|
38
|
+
totalSteps: 0,
|
|
39
|
+
setTotalSteps: (steps: number) => {},
|
|
40
|
+
});
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { ThemeProvider, useTheme } from "../Theme";
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
import { StyleSheet, Text, TouchableOpacity, View } from "react-native";
|
|
2
|
+
import { OnboardingStepType } from "../types";
|
|
3
|
+
import { useSafeAreaInsets } from "react-native-safe-area-context";
|
|
4
|
+
import { getTextStyle } from "../Theme/helpers";
|
|
5
|
+
import { Theme } from "../Theme/types";
|
|
6
|
+
import { defaultTheme } from "../Theme/defaultTheme";
|
|
7
|
+
|
|
8
|
+
type OnboardingTemplateProps = {
|
|
9
|
+
children: React.ReactNode;
|
|
10
|
+
onContinue: () => void;
|
|
11
|
+
button?: {
|
|
12
|
+
text: string;
|
|
13
|
+
disabled?: boolean;
|
|
14
|
+
};
|
|
15
|
+
step: OnboardingStepType;
|
|
16
|
+
theme?: Theme;
|
|
17
|
+
};
|
|
18
|
+
|
|
19
|
+
export const OnboardingTemplate = ({
|
|
20
|
+
children,
|
|
21
|
+
onContinue,
|
|
22
|
+
step,
|
|
23
|
+
button,
|
|
24
|
+
theme = defaultTheme,
|
|
25
|
+
}: OnboardingTemplateProps) => {
|
|
26
|
+
const { top, bottom } = useSafeAreaInsets();
|
|
27
|
+
|
|
28
|
+
return (
|
|
29
|
+
<View
|
|
30
|
+
style={[
|
|
31
|
+
styles.container,
|
|
32
|
+
{
|
|
33
|
+
backgroundColor: theme.colors.neutral.lowest,
|
|
34
|
+
paddingTop: step.displayProgressHeader ? top + 40 : top,
|
|
35
|
+
paddingBottom: bottom
|
|
36
|
+
},
|
|
37
|
+
]}
|
|
38
|
+
>
|
|
39
|
+
{children}
|
|
40
|
+
{button && (
|
|
41
|
+
<View style={styles.bottomSection}>
|
|
42
|
+
<TouchableOpacity
|
|
43
|
+
style={[
|
|
44
|
+
styles.ctaButton,
|
|
45
|
+
{ backgroundColor: theme.colors.primary },
|
|
46
|
+
button.disabled && { backgroundColor: theme.colors.disable },
|
|
47
|
+
]}
|
|
48
|
+
onPress={onContinue}
|
|
49
|
+
activeOpacity={0.8}
|
|
50
|
+
disabled={button.disabled}
|
|
51
|
+
>
|
|
52
|
+
<Text
|
|
53
|
+
style={[
|
|
54
|
+
getTextStyle(theme, "button"),
|
|
55
|
+
styles.ctaButtonText,
|
|
56
|
+
{ color: theme.colors.text.opposite },
|
|
57
|
+
button.disabled && { color: theme.colors.text.disable },
|
|
58
|
+
]}
|
|
59
|
+
>
|
|
60
|
+
{button.text}
|
|
61
|
+
</Text>
|
|
62
|
+
</TouchableOpacity>
|
|
63
|
+
</View>
|
|
64
|
+
)}
|
|
65
|
+
</View>
|
|
66
|
+
);
|
|
67
|
+
};
|
|
68
|
+
|
|
69
|
+
const styles = StyleSheet.create({
|
|
70
|
+
container: {
|
|
71
|
+
flex: 1,
|
|
72
|
+
},
|
|
73
|
+
bottomSection: {
|
|
74
|
+
paddingHorizontal: 32,
|
|
75
|
+
gap: 24,
|
|
76
|
+
alignItems: "center",
|
|
77
|
+
},
|
|
78
|
+
ctaButton: {
|
|
79
|
+
borderRadius: 90,
|
|
80
|
+
paddingVertical: 18,
|
|
81
|
+
paddingHorizontal: 24,
|
|
82
|
+
minWidth: 234,
|
|
83
|
+
alignItems: "center",
|
|
84
|
+
},
|
|
85
|
+
ctaButtonText: {},
|
|
86
|
+
});
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { OnboardingTemplate } from "./OnboardingTemplate";
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
import React, { createContext, useState, ReactNode, useMemo } from "react";
|
|
2
|
+
import { lightTokens, darkTokens, typography } from "./tokens";
|
|
3
|
+
import { Theme, ColorScheme, DeepPartial } from "./types";
|
|
4
|
+
import { mergeThemeTokens, deepMerge } from "./utils";
|
|
5
|
+
|
|
6
|
+
type ThemeContextType = {
|
|
7
|
+
theme: Theme;
|
|
8
|
+
colorScheme: ColorScheme;
|
|
9
|
+
toggleTheme: () => void;
|
|
10
|
+
};
|
|
11
|
+
|
|
12
|
+
export const ThemeContext = createContext<ThemeContextType | undefined>(
|
|
13
|
+
undefined
|
|
14
|
+
);
|
|
15
|
+
|
|
16
|
+
type ThemeProviderProps = {
|
|
17
|
+
children: ReactNode;
|
|
18
|
+
initialColorScheme?: ColorScheme;
|
|
19
|
+
/**
|
|
20
|
+
* Custom theme to override both light and dark mode tokens.
|
|
21
|
+
*/
|
|
22
|
+
customTheme?: DeepPartial<Theme>;
|
|
23
|
+
/**
|
|
24
|
+
* Custom theme tokens for light mode only.
|
|
25
|
+
*/
|
|
26
|
+
customLightTheme?: DeepPartial<Theme>;
|
|
27
|
+
/**
|
|
28
|
+
* Custom theme tokens for dark mode only.
|
|
29
|
+
*/
|
|
30
|
+
customDarkTheme?: DeepPartial<Theme>;
|
|
31
|
+
};
|
|
32
|
+
|
|
33
|
+
export const ThemeProvider = ({
|
|
34
|
+
children,
|
|
35
|
+
initialColorScheme = "light",
|
|
36
|
+
customTheme,
|
|
37
|
+
customLightTheme,
|
|
38
|
+
customDarkTheme,
|
|
39
|
+
}: ThemeProviderProps) => {
|
|
40
|
+
const [colorScheme, setColorScheme] =
|
|
41
|
+
useState<ColorScheme>(initialColorScheme);
|
|
42
|
+
|
|
43
|
+
// Merge custom themes with defaults (memoized for performance)
|
|
44
|
+
const mergedLightTheme = useMemo(() => {
|
|
45
|
+
let baseLight = mergeThemeTokens(
|
|
46
|
+
lightTokens.colors,
|
|
47
|
+
typography,
|
|
48
|
+
customLightTheme
|
|
49
|
+
);
|
|
50
|
+
|
|
51
|
+
// Apply global customTheme on top if provided
|
|
52
|
+
if (customTheme) {
|
|
53
|
+
baseLight = {
|
|
54
|
+
colors: customTheme.colors
|
|
55
|
+
? deepMerge(baseLight.colors, customTheme.colors)
|
|
56
|
+
: baseLight.colors,
|
|
57
|
+
typography: customTheme.typography
|
|
58
|
+
? deepMerge(baseLight.typography, customTheme.typography)
|
|
59
|
+
: baseLight.typography,
|
|
60
|
+
};
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
return baseLight;
|
|
64
|
+
}, [customTheme, customLightTheme]);
|
|
65
|
+
|
|
66
|
+
const mergedDarkTheme = useMemo(() => {
|
|
67
|
+
let baseDark = mergeThemeTokens(
|
|
68
|
+
darkTokens.colors,
|
|
69
|
+
typography,
|
|
70
|
+
customDarkTheme
|
|
71
|
+
);
|
|
72
|
+
|
|
73
|
+
// Apply global customTheme on top if provided
|
|
74
|
+
if (customTheme) {
|
|
75
|
+
baseDark = {
|
|
76
|
+
colors: customTheme.colors
|
|
77
|
+
? deepMerge(baseDark.colors, customTheme.colors)
|
|
78
|
+
: baseDark.colors,
|
|
79
|
+
typography: customTheme.typography
|
|
80
|
+
? deepMerge(baseDark.typography, customTheme.typography)
|
|
81
|
+
: baseDark.typography,
|
|
82
|
+
};
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
return baseDark;
|
|
86
|
+
}, [customTheme, customDarkTheme]);
|
|
87
|
+
|
|
88
|
+
const theme: Theme =
|
|
89
|
+
colorScheme === "light" ? mergedLightTheme : mergedDarkTheme;
|
|
90
|
+
|
|
91
|
+
const toggleTheme = () => {
|
|
92
|
+
setColorScheme((prev) => (prev === "light" ? "dark" : "light"));
|
|
93
|
+
};
|
|
94
|
+
|
|
95
|
+
return (
|
|
96
|
+
<ThemeContext.Provider value={{ theme, colorScheme, toggleTheme }}>
|
|
97
|
+
{children}
|
|
98
|
+
</ThemeContext.Provider>
|
|
99
|
+
);
|
|
100
|
+
};
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { Theme } from "./types";
|
|
2
|
+
import { lightTokens } from "./tokens/lightTokens";
|
|
3
|
+
import { typography } from "./tokens/typography";
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Default theme that combines light color tokens and typography.
|
|
7
|
+
* This is used as a fallback when no theme is provided to components.
|
|
8
|
+
*/
|
|
9
|
+
export const defaultTheme: Theme = {
|
|
10
|
+
colors: lightTokens.colors,
|
|
11
|
+
typography: typography,
|
|
12
|
+
};
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { Theme, TextStyles } from "./types";
|
|
2
|
+
import { TextStyle } from "react-native";
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Get a semantic text style from the theme with resolved font family.
|
|
6
|
+
*
|
|
7
|
+
* @example
|
|
8
|
+
* <Text style={[getTextStyle(theme, "heading1"), { color: theme.colors.text.primary }]}>
|
|
9
|
+
* Title
|
|
10
|
+
* </Text>
|
|
11
|
+
*/
|
|
12
|
+
export function getTextStyle(
|
|
13
|
+
theme: Theme,
|
|
14
|
+
styleName: keyof TextStyles
|
|
15
|
+
): TextStyle {
|
|
16
|
+
const style = theme.typography.textStyles[styleName];
|
|
17
|
+
|
|
18
|
+
return {
|
|
19
|
+
fontFamily: style.fontFamily,
|
|
20
|
+
fontSize: style.fontSize,
|
|
21
|
+
fontWeight: style.fontWeight,
|
|
22
|
+
lineHeight: style.fontSize * style.lineHeight,
|
|
23
|
+
};
|
|
24
|
+
}
|
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
const lightMode = {
|
|
2
|
+
color: {
|
|
3
|
+
disable: "#6d6d6d",
|
|
4
|
+
primary: "#264653",
|
|
5
|
+
secondary: "#3d3d3d",
|
|
6
|
+
tertiary1: "#f4a261",
|
|
7
|
+
tertiary2: "#e76f51",
|
|
8
|
+
tertiary3: "#e9c46a",
|
|
9
|
+
colorsNeutralHigh: "#4f4f4f",
|
|
10
|
+
colorsNeutralHigher: "#3d3d3d",
|
|
11
|
+
colorsNeutralHighest: "#262626",
|
|
12
|
+
colorsNeutralLow: "#b0b0b0",
|
|
13
|
+
colorsNeutralLower: "#e7e7e7",
|
|
14
|
+
colorsNeutralLowest: "#f6f6f6",
|
|
15
|
+
colorsNeutralMedium: "#6d6d6d",
|
|
16
|
+
colorsSurfaceHigh: "#6d6d6d",
|
|
17
|
+
colorsSurfaceHigher: "#4f4f4f",
|
|
18
|
+
colorsSurfaceHighest: "#3d3d3d",
|
|
19
|
+
colorsSurfaceLow: "#e7e7e7",
|
|
20
|
+
colorsSurfaceLower: "#f6f6f6",
|
|
21
|
+
colorsSurfaceLowest: "#ffffff",
|
|
22
|
+
colorsSurfaceMedium: "#b0b0b0",
|
|
23
|
+
colorsSurfaceOpposite: "#262626",
|
|
24
|
+
colorsTextDisable: "#b0b0b0",
|
|
25
|
+
colorsTextOpposite: "#ffffff",
|
|
26
|
+
colorsTextPrimary: "#262626",
|
|
27
|
+
colorsTextSecondary: "#3d3d3d",
|
|
28
|
+
colorsTextTerciary: "#6d6d6d",
|
|
29
|
+
},
|
|
30
|
+
};
|
|
31
|
+
const darkMode = {
|
|
32
|
+
color: {
|
|
33
|
+
disable: "#6d6d6d",
|
|
34
|
+
primary: "#c8ff2f",
|
|
35
|
+
secondary: "#000000",
|
|
36
|
+
tertiary1: "#f4a261",
|
|
37
|
+
tertiary2: "#e76f51",
|
|
38
|
+
tertiary3: "#e9c46a",
|
|
39
|
+
colorsNeutralHigh: "#6d6d6d",
|
|
40
|
+
colorsNeutralHigher: "#b0b0b0",
|
|
41
|
+
colorsNeutralHighest: "#f6f6f6",
|
|
42
|
+
colorsNeutralLow: "#4f4f4f",
|
|
43
|
+
colorsNeutralLower: "#3d3d3d",
|
|
44
|
+
colorsNeutralLowest: "#262626",
|
|
45
|
+
colorsNeutralMedium: "#6d6d6d",
|
|
46
|
+
colorsSurfaceHigh: "#575757",
|
|
47
|
+
colorsSurfaceHigher: "#888888",
|
|
48
|
+
colorsSurfaceHighest: "#e7e7e7",
|
|
49
|
+
colorsSurfaceLow: "#3d3d3d",
|
|
50
|
+
colorsSurfaceLower: "#262626",
|
|
51
|
+
colorsSurfaceLowest: "#000000",
|
|
52
|
+
colorsSurfaceMedium: "#454545",
|
|
53
|
+
colorsSurfaceOpposite: "#ffffff",
|
|
54
|
+
colorsTextDisable: "#6d6d6d",
|
|
55
|
+
colorsTextOpposite: "#262626",
|
|
56
|
+
colorsTextPrimary: "#ffffff",
|
|
57
|
+
colorsTextSecondary: "#d1d1d1",
|
|
58
|
+
colorsTextTerciary: "#b0b0b0",
|
|
59
|
+
},
|
|
60
|
+
};
|
|
61
|
+
const brand1 = {
|
|
62
|
+
number: {
|
|
63
|
+
fontSizeXs: "12px",
|
|
64
|
+
fontSizeSm: "14px",
|
|
65
|
+
fontSizeMd: "16px",
|
|
66
|
+
fontSizeLg: "20px",
|
|
67
|
+
fontSizeXl: "24px",
|
|
68
|
+
fontSize2xl: "32px",
|
|
69
|
+
fontSize3xl: "40px",
|
|
70
|
+
fontSize4xl: "72px",
|
|
71
|
+
},
|
|
72
|
+
string: {
|
|
73
|
+
fontFamilyTagline: "Inter",
|
|
74
|
+
fontFamilyText: "Inter",
|
|
75
|
+
fontFamilyTitle: "Inter",
|
|
76
|
+
fontWeightBold: "bold",
|
|
77
|
+
fontWeightExtraBold: "extrabold",
|
|
78
|
+
fontWeightItalic: "italic",
|
|
79
|
+
fontWeightMedium: "medium",
|
|
80
|
+
fontWeightRegular: "regular",
|
|
81
|
+
fontWeightSemiBold: "semibold",
|
|
82
|
+
},
|
|
83
|
+
};
|
|
84
|
+
const brand2 = {
|
|
85
|
+
number: {
|
|
86
|
+
fontSizeXs: "12px",
|
|
87
|
+
fontSizeSm: "14px",
|
|
88
|
+
fontSizeMd: "16px",
|
|
89
|
+
fontSizeLg: "20px",
|
|
90
|
+
fontSizeXl: "24px",
|
|
91
|
+
fontSize2xl: "32px",
|
|
92
|
+
fontSize3xl: "40px",
|
|
93
|
+
fontSize4xl: "72px",
|
|
94
|
+
},
|
|
95
|
+
string: {
|
|
96
|
+
fontFamilyTagline: "Montserrat",
|
|
97
|
+
fontFamilyText: "ubuntu",
|
|
98
|
+
fontFamilyTitle: "ubuntu",
|
|
99
|
+
fontWeightBold: "bold",
|
|
100
|
+
fontWeightExtraBold: "extrabold",
|
|
101
|
+
fontWeightItalic: "italic",
|
|
102
|
+
fontWeightMedium: "medium",
|
|
103
|
+
fontWeightRegular: "regular",
|
|
104
|
+
fontWeightSemiBold: "semibold",
|
|
105
|
+
},
|
|
106
|
+
};
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { ColorTokens } from "../types";
|
|
2
|
+
|
|
3
|
+
export const darkTokens = {
|
|
4
|
+
colors: {
|
|
5
|
+
primary: "#c8ff2f",
|
|
6
|
+
disable: "#6d6d6d",
|
|
7
|
+
neutral: {
|
|
8
|
+
high: "#6d6d6d",
|
|
9
|
+
higher: "#b0b0b0",
|
|
10
|
+
highest: "#f6f6f6",
|
|
11
|
+
low: "#4f4f4f",
|
|
12
|
+
lower: "#3d3d3d",
|
|
13
|
+
lowest: "#262626",
|
|
14
|
+
lowestest: "#000000",
|
|
15
|
+
medium: "#6d6d6d",
|
|
16
|
+
},
|
|
17
|
+
text: {
|
|
18
|
+
disable: "#6d6d6d",
|
|
19
|
+
opposite: "#262626",
|
|
20
|
+
primary: "#ffffff",
|
|
21
|
+
secondary: "#d1d1d1",
|
|
22
|
+
tertiary: "#b0b0b0",
|
|
23
|
+
},
|
|
24
|
+
},
|
|
25
|
+
} satisfies { colors: ColorTokens };
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { ColorTokens } from "../types";
|
|
2
|
+
|
|
3
|
+
export const lightTokens = {
|
|
4
|
+
colors: {
|
|
5
|
+
primary: "#264653",
|
|
6
|
+
disable: "#6d6d6d",
|
|
7
|
+
neutral: {
|
|
8
|
+
high: "#4f4f4f",
|
|
9
|
+
higher: "#3d3d3d",
|
|
10
|
+
highest: "#262626",
|
|
11
|
+
low: "#b0b0b0",
|
|
12
|
+
lower: "#e7e7e7",
|
|
13
|
+
lowest: "#f6f6f6",
|
|
14
|
+
lowestest: "#ffffff",
|
|
15
|
+
medium: "#6d6d6d",
|
|
16
|
+
},
|
|
17
|
+
text: {
|
|
18
|
+
disable: "#b0b0b0",
|
|
19
|
+
opposite: "#ffffff",
|
|
20
|
+
primary: "#262626",
|
|
21
|
+
secondary: "#3d3d3d",
|
|
22
|
+
tertiary: "#6d6d6d",
|
|
23
|
+
},
|
|
24
|
+
},
|
|
25
|
+
} satisfies { colors: ColorTokens };
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
import { TypographyTokens } from "../types";
|
|
2
|
+
|
|
3
|
+
export const typography = {
|
|
4
|
+
fontWeight: {
|
|
5
|
+
regular: "400" as const,
|
|
6
|
+
medium: "500" as const,
|
|
7
|
+
semibold: "600" as const,
|
|
8
|
+
bold: "700" as const,
|
|
9
|
+
extrabold: "800" as const,
|
|
10
|
+
},
|
|
11
|
+
lineHeight: {
|
|
12
|
+
tight: 1.25,
|
|
13
|
+
normal: 1.3,
|
|
14
|
+
relaxed: 1.4,
|
|
15
|
+
},
|
|
16
|
+
textStyles: {
|
|
17
|
+
heading1: {
|
|
18
|
+
fontSize: 32,
|
|
19
|
+
fontWeight: "600" as const,
|
|
20
|
+
lineHeight: 1.25,
|
|
21
|
+
fontFamily: "Inter",
|
|
22
|
+
},
|
|
23
|
+
heading2: {
|
|
24
|
+
fontSize: 24,
|
|
25
|
+
fontWeight: "600" as const,
|
|
26
|
+
lineHeight: 1.3,
|
|
27
|
+
fontFamily: "Inter",
|
|
28
|
+
},
|
|
29
|
+
heading3: {
|
|
30
|
+
fontSize: 18,
|
|
31
|
+
fontWeight: "500" as const,
|
|
32
|
+
lineHeight: 1.3,
|
|
33
|
+
fontFamily: "Inter",
|
|
34
|
+
},
|
|
35
|
+
body: {
|
|
36
|
+
fontSize: 16,
|
|
37
|
+
fontWeight: "400" as const,
|
|
38
|
+
lineHeight: 1.3,
|
|
39
|
+
fontFamily: "Inter",
|
|
40
|
+
},
|
|
41
|
+
bodyMedium: {
|
|
42
|
+
fontSize: 16,
|
|
43
|
+
fontWeight: "500" as const,
|
|
44
|
+
lineHeight: 1.3,
|
|
45
|
+
fontFamily: "Inter",
|
|
46
|
+
},
|
|
47
|
+
label: {
|
|
48
|
+
fontSize: 14,
|
|
49
|
+
fontWeight: "500" as const,
|
|
50
|
+
lineHeight: 1.3,
|
|
51
|
+
fontFamily: "Inter",
|
|
52
|
+
},
|
|
53
|
+
caption: {
|
|
54
|
+
fontSize: 12,
|
|
55
|
+
fontWeight: "400" as const,
|
|
56
|
+
lineHeight: 1.3,
|
|
57
|
+
fontFamily: "Inter",
|
|
58
|
+
},
|
|
59
|
+
button: {
|
|
60
|
+
fontSize: 16,
|
|
61
|
+
fontWeight: "500" as const,
|
|
62
|
+
lineHeight: 1.5,
|
|
63
|
+
fontFamily: "Inter",
|
|
64
|
+
},
|
|
65
|
+
},
|
|
66
|
+
} satisfies TypographyTokens;
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
export type ColorTokens = {
|
|
2
|
+
primary: string;
|
|
3
|
+
disable: string;
|
|
4
|
+
neutral: {
|
|
5
|
+
high: string;
|
|
6
|
+
higher: string;
|
|
7
|
+
highest: string;
|
|
8
|
+
low: string;
|
|
9
|
+
lower: string;
|
|
10
|
+
lowest: string;
|
|
11
|
+
lowestest: string;
|
|
12
|
+
medium: string;
|
|
13
|
+
};
|
|
14
|
+
text: {
|
|
15
|
+
disable: string;
|
|
16
|
+
opposite: string;
|
|
17
|
+
primary: string;
|
|
18
|
+
secondary: string;
|
|
19
|
+
tertiary: string;
|
|
20
|
+
};
|
|
21
|
+
};
|
|
22
|
+
|
|
23
|
+
export type TextStyle = {
|
|
24
|
+
fontSize: number;
|
|
25
|
+
fontWeight: "400" | "500" | "600" | "700" | "800";
|
|
26
|
+
lineHeight: number;
|
|
27
|
+
fontFamily: string;
|
|
28
|
+
};
|
|
29
|
+
|
|
30
|
+
export type TextStyles = {
|
|
31
|
+
heading1: TextStyle;
|
|
32
|
+
heading2: TextStyle;
|
|
33
|
+
heading3: TextStyle;
|
|
34
|
+
body: TextStyle;
|
|
35
|
+
bodyMedium: TextStyle;
|
|
36
|
+
label: TextStyle;
|
|
37
|
+
caption: TextStyle;
|
|
38
|
+
button: TextStyle;
|
|
39
|
+
};
|
|
40
|
+
|
|
41
|
+
export type TypographyTokens = {
|
|
42
|
+
fontWeight: {
|
|
43
|
+
regular: "400";
|
|
44
|
+
medium: "500";
|
|
45
|
+
semibold: "600";
|
|
46
|
+
bold: "700";
|
|
47
|
+
extrabold: "800";
|
|
48
|
+
};
|
|
49
|
+
lineHeight: {
|
|
50
|
+
tight: number;
|
|
51
|
+
normal: number;
|
|
52
|
+
relaxed: number;
|
|
53
|
+
};
|
|
54
|
+
textStyles: TextStyles;
|
|
55
|
+
};
|
|
56
|
+
|
|
57
|
+
export type Theme = {
|
|
58
|
+
colors: ColorTokens;
|
|
59
|
+
typography: TypographyTokens;
|
|
60
|
+
};
|
|
61
|
+
|
|
62
|
+
export type ColorScheme = "light" | "dark";
|
|
63
|
+
|
|
64
|
+
/**
|
|
65
|
+
* Deep partial utility type for nested objects.
|
|
66
|
+
* Makes all properties optional recursively.
|
|
67
|
+
*/
|
|
68
|
+
export type DeepPartial<T> = T extends object
|
|
69
|
+
? {
|
|
70
|
+
[P in keyof T]?: DeepPartial<T[P]>;
|
|
71
|
+
}
|
|
72
|
+
: T;
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { useContext } from "react";
|
|
2
|
+
import { ThemeContext } from "./ThemeProvider";
|
|
3
|
+
import { Theme } from "./types";
|
|
4
|
+
import { lightTokens, typography } from "./tokens";
|
|
5
|
+
|
|
6
|
+
export const useTheme = () => {
|
|
7
|
+
const context = useContext(ThemeContext);
|
|
8
|
+
|
|
9
|
+
if (context === undefined) {
|
|
10
|
+
const theme: Theme = {
|
|
11
|
+
colors: lightTokens.colors,
|
|
12
|
+
typography,
|
|
13
|
+
};
|
|
14
|
+
return {
|
|
15
|
+
theme: theme,
|
|
16
|
+
colorScheme: "light",
|
|
17
|
+
toggleTheme: () => {},
|
|
18
|
+
};
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
return context;
|
|
22
|
+
};
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
import { Theme, ColorTokens, TypographyTokens, DeepPartial } from "./types";
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Deep merge utility for theme tokens.
|
|
5
|
+
* Recursively merges custom theme values into default theme.
|
|
6
|
+
*/
|
|
7
|
+
export function deepMerge<T extends Record<string, any>>(
|
|
8
|
+
target: T,
|
|
9
|
+
source: DeepPartial<T> | undefined
|
|
10
|
+
): T {
|
|
11
|
+
if (!source) return target;
|
|
12
|
+
|
|
13
|
+
const result = { ...target };
|
|
14
|
+
|
|
15
|
+
for (const key in source) {
|
|
16
|
+
const sourceValue = source[key];
|
|
17
|
+
const targetValue = result[key];
|
|
18
|
+
|
|
19
|
+
if (
|
|
20
|
+
sourceValue &&
|
|
21
|
+
typeof sourceValue === "object" &&
|
|
22
|
+
!Array.isArray(sourceValue) &&
|
|
23
|
+
targetValue &&
|
|
24
|
+
typeof targetValue === "object" &&
|
|
25
|
+
!Array.isArray(targetValue)
|
|
26
|
+
) {
|
|
27
|
+
// Recursively merge nested objects
|
|
28
|
+
result[key] = deepMerge(targetValue, sourceValue);
|
|
29
|
+
} else if (sourceValue !== undefined) {
|
|
30
|
+
// Override primitive values
|
|
31
|
+
result[key] = sourceValue as any;
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
return result;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* Merge custom theme tokens with default tokens.
|
|
40
|
+
* Supports partial Theme objects with nested property overrides.
|
|
41
|
+
*/
|
|
42
|
+
export function mergeThemeTokens(
|
|
43
|
+
defaultColors: ColorTokens,
|
|
44
|
+
defaultTypography: TypographyTokens,
|
|
45
|
+
customTheme?: DeepPartial<Theme>
|
|
46
|
+
): Theme {
|
|
47
|
+
if (!customTheme) {
|
|
48
|
+
return {
|
|
49
|
+
colors: defaultColors,
|
|
50
|
+
typography: defaultTypography,
|
|
51
|
+
};
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
// Check if customTheme has colors and/or typography
|
|
55
|
+
const hasColors = "colors" in customTheme && customTheme.colors !== undefined;
|
|
56
|
+
const hasTypography =
|
|
57
|
+
"typography" in customTheme && customTheme.typography !== undefined;
|
|
58
|
+
|
|
59
|
+
return {
|
|
60
|
+
colors: hasColors
|
|
61
|
+
? deepMerge(defaultColors, customTheme.colors)
|
|
62
|
+
: defaultColors,
|
|
63
|
+
typography: hasTypography
|
|
64
|
+
? deepMerge(defaultTypography, customTheme.typography)
|
|
65
|
+
: defaultTypography,
|
|
66
|
+
};
|
|
67
|
+
}
|