@rocapine/react-native-onboarding-ui 1.6.0 → 1.8.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.
Files changed (84) hide show
  1. package/dist/UI/OnboardingPage.js +1 -1
  2. package/dist/UI/OnboardingPage.js.map +1 -1
  3. package/dist/UI/Pages/ComposableScreen/Renderer.d.ts +0 -2
  4. package/dist/UI/Pages/ComposableScreen/Renderer.d.ts.map +1 -1
  5. package/dist/UI/Pages/ComposableScreen/Renderer.js +13 -273
  6. package/dist/UI/Pages/ComposableScreen/Renderer.js.map +1 -1
  7. package/dist/UI/Pages/ComposableScreen/elements/BaseBoxProps.d.ts +30 -0
  8. package/dist/UI/Pages/ComposableScreen/elements/BaseBoxProps.d.ts.map +1 -0
  9. package/dist/UI/Pages/ComposableScreen/elements/BaseBoxProps.js +19 -0
  10. package/dist/UI/Pages/ComposableScreen/elements/BaseBoxProps.js.map +1 -0
  11. package/dist/UI/Pages/ComposableScreen/elements/ButtonElement.d.ts +67 -0
  12. package/dist/UI/Pages/ComposableScreen/elements/ButtonElement.d.ts.map +1 -0
  13. package/dist/UI/Pages/ComposableScreen/elements/ButtonElement.js +65 -0
  14. package/dist/UI/Pages/ComposableScreen/elements/ButtonElement.js.map +1 -0
  15. package/dist/UI/Pages/ComposableScreen/elements/IconElement.d.ts +41 -0
  16. package/dist/UI/Pages/ComposableScreen/elements/IconElement.d.ts.map +1 -0
  17. package/dist/UI/Pages/ComposableScreen/elements/IconElement.js +37 -0
  18. package/dist/UI/Pages/ComposableScreen/elements/IconElement.js.map +1 -0
  19. package/dist/UI/Pages/ComposableScreen/elements/ImageElement.d.ts +42 -0
  20. package/dist/UI/Pages/ComposableScreen/elements/ImageElement.d.ts.map +1 -0
  21. package/dist/UI/Pages/ComposableScreen/elements/ImageElement.js +34 -0
  22. package/dist/UI/Pages/ComposableScreen/elements/ImageElement.js.map +1 -0
  23. package/dist/UI/Pages/ComposableScreen/elements/InputElement.d.ts +102 -0
  24. package/dist/UI/Pages/ComposableScreen/elements/InputElement.d.ts.map +1 -0
  25. package/dist/UI/Pages/ComposableScreen/elements/InputElement.js +68 -0
  26. package/dist/UI/Pages/ComposableScreen/elements/InputElement.js.map +1 -0
  27. package/dist/UI/Pages/ComposableScreen/elements/LottieElement.d.ts +39 -0
  28. package/dist/UI/Pages/ComposableScreen/elements/LottieElement.d.ts.map +1 -0
  29. package/dist/UI/Pages/ComposableScreen/elements/LottieElement.js +60 -0
  30. package/dist/UI/Pages/ComposableScreen/elements/LottieElement.js.map +1 -0
  31. package/dist/UI/Pages/ComposableScreen/elements/RadioGroupElement.d.ts +78 -0
  32. package/dist/UI/Pages/ComposableScreen/elements/RadioGroupElement.d.ts.map +1 -0
  33. package/dist/UI/Pages/ComposableScreen/elements/RadioGroupElement.js +119 -0
  34. package/dist/UI/Pages/ComposableScreen/elements/RadioGroupElement.js.map +1 -0
  35. package/dist/UI/Pages/ComposableScreen/elements/RiveElement.d.ts +62 -0
  36. package/dist/UI/Pages/ComposableScreen/elements/RiveElement.d.ts.map +1 -0
  37. package/dist/UI/Pages/ComposableScreen/elements/RiveElement.js +68 -0
  38. package/dist/UI/Pages/ComposableScreen/elements/RiveElement.js.map +1 -0
  39. package/dist/UI/Pages/ComposableScreen/elements/StackElement.d.ts +85 -0
  40. package/dist/UI/Pages/ComposableScreen/elements/StackElement.d.ts.map +1 -0
  41. package/dist/UI/Pages/ComposableScreen/elements/StackElement.js +64 -0
  42. package/dist/UI/Pages/ComposableScreen/elements/StackElement.js.map +1 -0
  43. package/dist/UI/Pages/ComposableScreen/elements/TextElement.d.ts +66 -0
  44. package/dist/UI/Pages/ComposableScreen/elements/TextElement.d.ts.map +1 -0
  45. package/dist/UI/Pages/ComposableScreen/elements/TextElement.js +59 -0
  46. package/dist/UI/Pages/ComposableScreen/elements/TextElement.js.map +1 -0
  47. package/dist/UI/Pages/ComposableScreen/elements/VideoElement.d.ts +41 -0
  48. package/dist/UI/Pages/ComposableScreen/elements/VideoElement.d.ts.map +1 -0
  49. package/dist/UI/Pages/ComposableScreen/elements/VideoElement.js +84 -0
  50. package/dist/UI/Pages/ComposableScreen/elements/VideoElement.js.map +1 -0
  51. package/dist/UI/Pages/ComposableScreen/elements/renderElement.d.ts +5 -0
  52. package/dist/UI/Pages/ComposableScreen/elements/renderElement.d.ts.map +1 -0
  53. package/dist/UI/Pages/ComposableScreen/elements/renderElement.js +49 -0
  54. package/dist/UI/Pages/ComposableScreen/elements/renderElement.js.map +1 -0
  55. package/dist/UI/Pages/ComposableScreen/elements/shared.d.ts +13 -0
  56. package/dist/UI/Pages/ComposableScreen/elements/shared.d.ts.map +1 -0
  57. package/dist/UI/Pages/ComposableScreen/elements/shared.js +6 -0
  58. package/dist/UI/Pages/ComposableScreen/elements/shared.js.map +1 -0
  59. package/dist/UI/Pages/ComposableScreen/types.d.ts +40 -113
  60. package/dist/UI/Pages/ComposableScreen/types.d.ts.map +1 -1
  61. package/dist/UI/Pages/ComposableScreen/types.js +33 -121
  62. package/dist/UI/Pages/ComposableScreen/types.js.map +1 -1
  63. package/dist/UI/Provider/OnboardingProgressProvider.d.ts +6 -2
  64. package/dist/UI/Provider/OnboardingProgressProvider.d.ts.map +1 -1
  65. package/dist/UI/Provider/OnboardingProgressProvider.js +4 -3
  66. package/dist/UI/Provider/OnboardingProgressProvider.js.map +1 -1
  67. package/package.json +2 -2
  68. package/src/UI/OnboardingPage.tsx +1 -1
  69. package/src/UI/Pages/ComposableScreen/Renderer.tsx +22 -430
  70. package/src/UI/Pages/ComposableScreen/elements/BaseBoxProps.ts +31 -0
  71. package/src/UI/Pages/ComposableScreen/elements/ButtonElement.tsx +96 -0
  72. package/src/UI/Pages/ComposableScreen/elements/IconElement.tsx +67 -0
  73. package/src/UI/Pages/ComposableScreen/elements/ImageElement.tsx +52 -0
  74. package/src/UI/Pages/ComposableScreen/elements/InputElement.tsx +115 -0
  75. package/src/UI/Pages/ComposableScreen/elements/LottieElement.tsx +97 -0
  76. package/src/UI/Pages/ComposableScreen/elements/RadioGroupElement.tsx +181 -0
  77. package/src/UI/Pages/ComposableScreen/elements/RiveElement.tsx +105 -0
  78. package/src/UI/Pages/ComposableScreen/elements/StackElement.tsx +103 -0
  79. package/src/UI/Pages/ComposableScreen/elements/TextElement.tsx +95 -0
  80. package/src/UI/Pages/ComposableScreen/elements/VideoElement.tsx +113 -0
  81. package/src/UI/Pages/ComposableScreen/elements/renderElement.tsx +61 -0
  82. package/src/UI/Pages/ComposableScreen/elements/shared.ts +15 -0
  83. package/src/UI/Pages/ComposableScreen/types.ts +56 -233
  84. package/src/UI/Provider/OnboardingProgressProvider.tsx +8 -5
@@ -0,0 +1,105 @@
1
+ import React from "react";
2
+ import { z } from "zod";
3
+ import { View, Text, StyleSheet } from "react-native";
4
+ import { BaseBoxProps, BaseBoxPropsSchema } from "./BaseBoxProps";
5
+ import { UIElement } from "../types";
6
+ import { RenderContext } from "./shared";
7
+ import { getTextStyle } from "../../../Theme/helpers";
8
+
9
+ export type RiveElementProps = BaseBoxProps & {
10
+ url: string;
11
+ autoplay?: boolean;
12
+ fit?: "Contain" | "Cover" | "Fill" | "FitWidth" | "FitHeight" | "None" | "ScaleDown" | "Layout";
13
+ alignment?: "TopLeft" | "TopCenter" | "TopRight" | "CenterLeft" | "Center" | "CenterRight" | "BottomLeft" | "BottomCenter" | "BottomRight";
14
+ artboardName?: string;
15
+ stateMachineName?: string;
16
+ };
17
+
18
+ export const RiveElementPropsSchema = BaseBoxPropsSchema.extend({
19
+ url: z.string().min(1, "url must not be empty"),
20
+ autoplay: z.boolean().optional(),
21
+ fit: z.enum(["Contain", "Cover", "Fill", "FitWidth", "FitHeight", "None", "ScaleDown", "Layout"]).optional(),
22
+ alignment: z.enum(["TopLeft", "TopCenter", "TopRight", "CenterLeft", "Center", "CenterRight", "BottomLeft", "BottomCenter", "BottomRight"]).optional(),
23
+ artboardName: z.string().optional(),
24
+ stateMachineName: z.string().optional(),
25
+ });
26
+
27
+ type RiveUIElement = Extract<UIElement, { type: "Rive" }>;
28
+
29
+ let RiveElementComponent: React.ComponentType<{ element: RiveUIElement; riveStyle: object }> | null = null;
30
+ try {
31
+ const riveModule = require("rive-react-native");
32
+ const Rive = riveModule.default;
33
+ const { Fit, Alignment } = riveModule;
34
+ RiveElementComponent = ({ element, riveStyle }: { element: RiveUIElement; riveStyle: object }) => {
35
+ return (
36
+ <Rive
37
+ url={element.props.url}
38
+ autoplay={element.props.autoplay ?? true}
39
+ fit={element.props.fit ? Fit[element.props.fit] : Fit.Contain}
40
+ alignment={element.props.alignment ? Alignment[element.props.alignment] : Alignment.Center}
41
+ artboardName={element.props.artboardName}
42
+ stateMachineName={element.props.stateMachineName}
43
+ style={riveStyle}
44
+ onError={console.error}
45
+ />
46
+ );
47
+ };
48
+ } catch {
49
+ // rive-react-native not installed - will show fallback if Rive is used
50
+ }
51
+
52
+ type Props = {
53
+ element: RiveUIElement;
54
+ ctx: RenderContext;
55
+ };
56
+
57
+ export const RiveElementRenderer = ({ element, ctx }: Props): React.ReactElement => {
58
+ const { theme } = ctx;
59
+ const wrapperStyle = {
60
+ width: element.props.width ?? ("100%" as `${number}%`),
61
+ height: element.props.height ?? 200,
62
+ opacity: element.props.opacity,
63
+ margin: element.props.margin,
64
+ marginHorizontal: element.props.marginHorizontal,
65
+ marginVertical: element.props.marginVertical,
66
+ padding: element.props.padding,
67
+ paddingHorizontal: element.props.paddingHorizontal,
68
+ paddingVertical: element.props.paddingVertical,
69
+ borderWidth: element.props.borderWidth,
70
+ borderRadius: element.props.borderRadius,
71
+ borderColor: element.props.borderColor,
72
+ overflow: "hidden" as const,
73
+ };
74
+
75
+ if (!RiveElementComponent) {
76
+ return (
77
+ <View style={[wrapperStyle, styles.mediaFallback, { backgroundColor: theme.colors.neutral.lowest }]}>
78
+ <Text style={[styles.mediaFallbackText, getTextStyle(theme, "caption"), { color: theme.colors.text.tertiary }]}>
79
+ Install rive-react-native to render Rive animations.
80
+ </Text>
81
+ </View>
82
+ );
83
+ }
84
+
85
+ return (
86
+ <View style={wrapperStyle}>
87
+ <RiveElementComponent element={element} riveStyle={styles.fill} />
88
+ </View>
89
+ );
90
+ };
91
+
92
+ const styles = StyleSheet.create({
93
+ fill: {
94
+ width: "100%",
95
+ height: "100%",
96
+ },
97
+ mediaFallback: {
98
+ alignItems: "center",
99
+ justifyContent: "center",
100
+ },
101
+ mediaFallbackText: {
102
+ textAlign: "center",
103
+ paddingHorizontal: 16,
104
+ },
105
+ });
@@ -0,0 +1,103 @@
1
+ import React from "react";
2
+ import { z } from "zod";
3
+ import { View } from "react-native";
4
+ import { UIElement } from "../types";
5
+ import { RenderContext } from "./shared";
6
+
7
+ export type StackElementProps = {
8
+ gap?: number;
9
+ padding?: number;
10
+ paddingHorizontal?: number;
11
+ paddingVertical?: number;
12
+ margin?: number;
13
+ marginHorizontal?: number;
14
+ marginVertical?: number;
15
+ flex?: number;
16
+ width?: number;
17
+ height?: number;
18
+ minWidth?: number;
19
+ maxWidth?: number;
20
+ minHeight?: number;
21
+ maxHeight?: number;
22
+ alignItems?: "flex-start" | "center" | "flex-end" | "stretch";
23
+ justifyContent?: "flex-start" | "center" | "flex-end" | "space-between" | "space-around";
24
+ backgroundColor?: string;
25
+ flexWrap?: "wrap" | "nowrap";
26
+ flexShrink?: number;
27
+ borderWidth?: number;
28
+ borderRadius?: number;
29
+ borderColor?: string;
30
+ overflow?: "hidden" | "visible" | "scroll";
31
+ opacity?: number;
32
+ };
33
+
34
+ export const StackElementPropsSchema = z.object({
35
+ gap: z.number().optional(),
36
+ padding: z.number().optional(),
37
+ paddingHorizontal: z.number().optional(),
38
+ paddingVertical: z.number().optional(),
39
+ margin: z.number().optional(),
40
+ marginHorizontal: z.number().optional(),
41
+ marginVertical: z.number().optional(),
42
+ flex: z.number().optional(),
43
+ width: z.number().optional(),
44
+ height: z.number().optional(),
45
+ minWidth: z.number().optional(),
46
+ maxWidth: z.number().optional(),
47
+ minHeight: z.number().optional(),
48
+ maxHeight: z.number().optional(),
49
+ alignItems: z.enum(["flex-start", "center", "flex-end", "stretch"]).optional(),
50
+ justifyContent: z.enum(["flex-start", "center", "flex-end", "space-between", "space-around"]).optional(),
51
+ backgroundColor: z.string().optional(),
52
+ flexWrap: z.enum(["wrap", "nowrap"]).optional(),
53
+ flexShrink: z.number().optional(),
54
+ borderWidth: z.number().optional(),
55
+ borderRadius: z.number().optional(),
56
+ borderColor: z.string().optional(),
57
+ overflow: z.enum(["hidden", "visible", "scroll"]).optional(),
58
+ opacity: z.number().min(0).max(1).optional(),
59
+ });
60
+
61
+ type StackUIElement = Extract<UIElement, { type: "YStack" | "XStack" }>;
62
+
63
+ type Props = {
64
+ element: StackUIElement;
65
+ ctx: RenderContext;
66
+ parentType?: "XStack" | "YStack";
67
+ };
68
+
69
+ export const StackElementComponent = ({ element, ctx, parentType }: Props): React.ReactElement => {
70
+ return (
71
+ <View
72
+ style={{
73
+ flexDirection: element.type === "XStack" ? "row" : "column",
74
+ gap: element.props.gap,
75
+ padding: element.props.padding,
76
+ paddingHorizontal: element.props.paddingHorizontal,
77
+ paddingVertical: element.props.paddingVertical,
78
+ margin: element.props.margin,
79
+ marginHorizontal: element.props.marginHorizontal,
80
+ marginVertical: element.props.marginVertical,
81
+ flex: element.props.flex,
82
+ width: element.props.width,
83
+ height: element.props.height,
84
+ minWidth: element.props.minWidth,
85
+ maxWidth: element.props.maxWidth,
86
+ minHeight: element.props.minHeight,
87
+ maxHeight: element.props.maxHeight,
88
+ flexShrink: element.props.flexShrink ?? (parentType === "XStack" ? 1 : undefined),
89
+ flexWrap: element.props.flexWrap,
90
+ alignItems: element.props.alignItems,
91
+ justifyContent: element.props.justifyContent,
92
+ backgroundColor: element.props.backgroundColor,
93
+ borderWidth: element.props.borderWidth,
94
+ borderRadius: element.props.borderRadius,
95
+ borderColor: element.props.borderColor,
96
+ overflow: element.props.overflow,
97
+ opacity: element.props.opacity,
98
+ }}
99
+ >
100
+ {ctx.renderChildren(element.children, element.type)}
101
+ </View>
102
+ );
103
+ };
@@ -0,0 +1,95 @@
1
+ import React from "react";
2
+ import { z } from "zod";
3
+ import { Text } from "react-native";
4
+ import { UIElement } from "../types";
5
+ import { RenderContext, interpolate } from "./shared";
6
+
7
+ export type TextElementProps = {
8
+ content: string;
9
+ mode?: "plain" | "expression";
10
+ fontSize?: number;
11
+ fontWeight?: string;
12
+ fontFamily?: string;
13
+ color?: string;
14
+ textAlign?: "left" | "center" | "right";
15
+ letterSpacing?: number;
16
+ lineHeight?: number;
17
+ backgroundColor?: string;
18
+ padding?: number;
19
+ paddingHorizontal?: number;
20
+ paddingVertical?: number;
21
+ margin?: number;
22
+ marginHorizontal?: number;
23
+ marginVertical?: number;
24
+ borderWidth?: number;
25
+ borderRadius?: number;
26
+ borderColor?: string;
27
+ opacity?: number;
28
+ };
29
+
30
+ export const TextElementPropsSchema = z.object({
31
+ content: z.string(),
32
+ mode: z.enum(["plain", "expression"]).optional(),
33
+ fontSize: z.number().optional(),
34
+ fontWeight: z.string().optional(),
35
+ fontFamily: z.string().optional(),
36
+ color: z.string().optional(),
37
+ textAlign: z.enum(["left", "center", "right"]).optional(),
38
+ letterSpacing: z.number().optional(),
39
+ lineHeight: z.number().optional(),
40
+ backgroundColor: z.string().optional(),
41
+ padding: z.number().optional(),
42
+ paddingHorizontal: z.number().optional(),
43
+ paddingVertical: z.number().optional(),
44
+ margin: z.number().optional(),
45
+ marginHorizontal: z.number().optional(),
46
+ marginVertical: z.number().optional(),
47
+ borderWidth: z.number().optional(),
48
+ borderRadius: z.number().optional(),
49
+ borderColor: z.string().optional(),
50
+ opacity: z.number().min(0).max(1).optional(),
51
+ });
52
+
53
+ type TextUIElement = Extract<UIElement, { type: "Text" }>;
54
+
55
+ type Props = {
56
+ element: TextUIElement;
57
+ ctx: RenderContext;
58
+ parentType?: "XStack" | "YStack";
59
+ };
60
+
61
+ export const TextElementComponent = ({ element, ctx, parentType }: Props): React.ReactElement => {
62
+ const { theme, variables } = ctx;
63
+ const content =
64
+ element.props.mode === "expression"
65
+ ? interpolate(element.props.content, variables)
66
+ : element.props.content;
67
+
68
+ return (
69
+ <Text
70
+ style={{
71
+ fontSize: element.props.fontSize,
72
+ fontWeight: element.props.fontWeight as any,
73
+ fontFamily: element.props.fontFamily,
74
+ color: element.props.color ?? theme.colors.text.primary,
75
+ textAlign: element.props.textAlign,
76
+ letterSpacing: element.props.letterSpacing,
77
+ lineHeight: element.props.lineHeight,
78
+ backgroundColor: element.props.backgroundColor,
79
+ padding: element.props.padding,
80
+ paddingHorizontal: element.props.paddingHorizontal,
81
+ paddingVertical: element.props.paddingVertical,
82
+ margin: element.props.margin,
83
+ marginHorizontal: element.props.marginHorizontal,
84
+ marginVertical: element.props.marginVertical,
85
+ borderWidth: element.props.borderWidth,
86
+ borderRadius: element.props.borderRadius,
87
+ borderColor: element.props.borderColor,
88
+ opacity: element.props.opacity,
89
+ flexShrink: parentType === "XStack" ? 1 : undefined,
90
+ }}
91
+ >
92
+ {content}
93
+ </Text>
94
+ );
95
+ };
@@ -0,0 +1,113 @@
1
+ import React, { useEffect } from "react";
2
+ import { z } from "zod";
3
+ import { View, Text, StyleSheet } from "react-native";
4
+ import { BaseBoxProps, BaseBoxPropsSchema } from "./BaseBoxProps";
5
+ import { UIElement } from "../types";
6
+ import { RenderContext } from "./shared";
7
+ import { getTextStyle } from "../../../Theme/helpers";
8
+
9
+ export type VideoElementProps = BaseBoxProps & {
10
+ url: string;
11
+ autoPlay?: boolean;
12
+ loop?: boolean;
13
+ muted?: boolean;
14
+ controls?: boolean;
15
+ };
16
+
17
+ export const VideoElementPropsSchema = BaseBoxPropsSchema.extend({
18
+ url: z.string().min(1, "url must not be empty"),
19
+ autoPlay: z.boolean().optional(),
20
+ loop: z.boolean().optional(),
21
+ muted: z.boolean().optional(),
22
+ controls: z.boolean().optional(),
23
+ });
24
+
25
+ type VideoUIElement = Extract<UIElement, { type: "Video" }>;
26
+
27
+ let VideoElementComponent: React.ComponentType<{ element: VideoUIElement; style: object }> | null = null;
28
+ try {
29
+ const { VideoView, useVideoPlayer } = require("expo-video");
30
+ VideoElementComponent = ({ element, style }: { element: VideoUIElement; style: object }) => {
31
+ const player = useVideoPlayer(element.props.url, (p: any) => {
32
+ p.loop = element.props.loop ?? false;
33
+ p.muted = element.props.muted ?? true;
34
+ if (element.props.autoPlay) p.play();
35
+ });
36
+
37
+ useEffect(() => {
38
+ player.loop = element.props.loop ?? false;
39
+ player.muted = element.props.muted ?? true;
40
+ if (element.props.autoPlay) {
41
+ player.play();
42
+ } else {
43
+ player.pause();
44
+ }
45
+ }, [element.props.loop, element.props.muted, element.props.autoPlay]);
46
+
47
+ return (
48
+ <VideoView
49
+ player={player}
50
+ style={style}
51
+ allowsFullscreen={false}
52
+ nativeControls={element.props.controls ?? false}
53
+ />
54
+ );
55
+ };
56
+ } catch {
57
+ // expo-video not installed
58
+ }
59
+
60
+ type Props = {
61
+ element: VideoUIElement;
62
+ ctx: RenderContext;
63
+ };
64
+
65
+ export const VideoElementRenderer = ({ element, ctx }: Props): React.ReactElement => {
66
+ const { theme } = ctx;
67
+ const wrapperStyle = {
68
+ width: element.props.width ?? ("100%" as `${number}%`),
69
+ height: element.props.height ?? 200,
70
+ opacity: element.props.opacity,
71
+ margin: element.props.margin,
72
+ marginHorizontal: element.props.marginHorizontal,
73
+ marginVertical: element.props.marginVertical,
74
+ padding: element.props.padding,
75
+ paddingHorizontal: element.props.paddingHorizontal,
76
+ paddingVertical: element.props.paddingVertical,
77
+ borderWidth: element.props.borderWidth,
78
+ borderRadius: element.props.borderRadius,
79
+ borderColor: element.props.borderColor,
80
+ overflow: "hidden" as const,
81
+ };
82
+
83
+ if (!VideoElementComponent) {
84
+ return (
85
+ <View style={[wrapperStyle, styles.mediaFallback, { backgroundColor: theme.colors.neutral.lowest }]}>
86
+ <Text style={[styles.mediaFallbackText, getTextStyle(theme, "caption"), { color: theme.colors.text.tertiary }]}>
87
+ Install expo-video to render videos.
88
+ </Text>
89
+ </View>
90
+ );
91
+ }
92
+
93
+ return (
94
+ <View style={wrapperStyle}>
95
+ <VideoElementComponent element={element} style={styles.fill} />
96
+ </View>
97
+ );
98
+ };
99
+
100
+ const styles = StyleSheet.create({
101
+ fill: {
102
+ width: "100%",
103
+ height: "100%",
104
+ },
105
+ mediaFallback: {
106
+ alignItems: "center",
107
+ justifyContent: "center",
108
+ },
109
+ mediaFallbackText: {
110
+ textAlign: "center",
111
+ paddingHorizontal: 16,
112
+ },
113
+ });
@@ -0,0 +1,61 @@
1
+ import React from "react";
2
+ import { UIElement } from "../types";
3
+ import { RenderContext } from "./shared";
4
+ import { StackElementComponent } from "./StackElement";
5
+ import { TextElementComponent } from "./TextElement";
6
+ import { ImageElementComponent } from "./ImageElement";
7
+ import { LottieElementComponent } from "./LottieElement";
8
+ import { RiveElementRenderer } from "./RiveElement";
9
+ import { IconElementComponent } from "./IconElement";
10
+ import { VideoElementRenderer } from "./VideoElement";
11
+ import { InputElementComponent } from "./InputElement";
12
+ import { RadioGroupComponent } from "./RadioGroupElement";
13
+ import { ButtonElementComponent } from "./ButtonElement";
14
+
15
+ export const renderElement = (
16
+ element: UIElement,
17
+ ctx: RenderContext,
18
+ parentType?: "XStack" | "YStack"
19
+ ): React.ReactNode => {
20
+ if (element.type === "YStack" || element.type === "XStack") {
21
+ return <StackElementComponent key={element.id} element={element} ctx={ctx} parentType={parentType} />;
22
+ }
23
+
24
+ if (element.type === "Text") {
25
+ return <TextElementComponent key={element.id} element={element} ctx={ctx} parentType={parentType} />;
26
+ }
27
+
28
+ if (element.type === "Image") {
29
+ return <ImageElementComponent key={element.id} element={element} ctx={ctx} />;
30
+ }
31
+
32
+ if (element.type === "Lottie") {
33
+ return <LottieElementComponent key={element.id} element={element} ctx={ctx} />;
34
+ }
35
+
36
+ if (element.type === "Rive") {
37
+ return <RiveElementRenderer key={element.id} element={element} ctx={ctx} />;
38
+ }
39
+
40
+ if (element.type === "Icon") {
41
+ return <IconElementComponent key={element.id} element={element} ctx={ctx} />;
42
+ }
43
+
44
+ if (element.type === "Video") {
45
+ return <VideoElementRenderer key={element.id} element={element} ctx={ctx} />;
46
+ }
47
+
48
+ if (element.type === "Input") {
49
+ return <InputElementComponent key={element.id} element={element} ctx={ctx} />;
50
+ }
51
+
52
+ if (element.type === "RadioGroup") {
53
+ return <RadioGroupComponent key={element.id} element={element} ctx={ctx} />;
54
+ }
55
+
56
+ if (element.type === "Button") {
57
+ return <ButtonElementComponent key={element.id} element={element} ctx={ctx} />;
58
+ }
59
+
60
+ return null;
61
+ };
@@ -0,0 +1,15 @@
1
+ import React from "react";
2
+ import { UIElement } from "../types";
3
+ import { Theme } from "../../../Theme/types";
4
+ import { ComposableVariableEntry } from "../../../Provider/OnboardingProgressProvider";
5
+
6
+ export type RenderContext = {
7
+ theme: Theme;
8
+ variables: Record<string, ComposableVariableEntry>;
9
+ setVariable: (key: string, entry: ComposableVariableEntry) => void;
10
+ onContinue: () => void;
11
+ renderChildren: (elements: UIElement[], parentType: "XStack" | "YStack") => React.ReactNode;
12
+ };
13
+
14
+ export const interpolate = (template: string, variables: Record<string, ComposableVariableEntry>): string =>
15
+ template.replace(/\{\{([^}]+?)\}\}/g, (_, key) => variables[key]?.label ?? variables[key]?.value ?? "");