@rocapine/react-native-onboarding-ui 1.20.0 → 1.23.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 (34) hide show
  1. package/dist/UI/Pages/ComposableScreen/Renderer.d.ts.map +1 -1
  2. package/dist/UI/Pages/ComposableScreen/Renderer.js +8 -1
  3. package/dist/UI/Pages/ComposableScreen/Renderer.js.map +1 -1
  4. package/dist/UI/Pages/ComposableScreen/elements/ButtonElement.d.ts +42 -1
  5. package/dist/UI/Pages/ComposableScreen/elements/ButtonElement.d.ts.map +1 -1
  6. package/dist/UI/Pages/ComposableScreen/elements/ButtonElement.js +15 -1
  7. package/dist/UI/Pages/ComposableScreen/elements/ButtonElement.js.map +1 -1
  8. package/dist/UI/Pages/ComposableScreen/elements/CarouselElement.d.ts +4 -0
  9. package/dist/UI/Pages/ComposableScreen/elements/CarouselElement.d.ts.map +1 -1
  10. package/dist/UI/Pages/ComposableScreen/elements/CarouselElement.js +57 -12
  11. package/dist/UI/Pages/ComposableScreen/elements/CarouselElement.js.map +1 -1
  12. package/dist/UI/Pages/ComposableScreen/elements/collectDefaults.d.ts +11 -0
  13. package/dist/UI/Pages/ComposableScreen/elements/collectDefaults.d.ts.map +1 -0
  14. package/dist/UI/Pages/ComposableScreen/elements/collectDefaults.js +74 -0
  15. package/dist/UI/Pages/ComposableScreen/elements/collectDefaults.js.map +1 -0
  16. package/dist/UI/Pages/ComposableScreen/elements/expression.d.ts +17 -0
  17. package/dist/UI/Pages/ComposableScreen/elements/expression.d.ts.map +1 -0
  18. package/dist/UI/Pages/ComposableScreen/elements/expression.js +210 -0
  19. package/dist/UI/Pages/ComposableScreen/elements/expression.js.map +1 -0
  20. package/dist/UI/Pages/ComposableScreen/elements/renderElement.d.ts.map +1 -1
  21. package/dist/UI/Pages/ComposableScreen/elements/renderElement.js +6 -0
  22. package/dist/UI/Pages/ComposableScreen/elements/renderElement.js.map +1 -1
  23. package/dist/UI/Pages/ComposableScreen/types.d.ts +16 -0
  24. package/dist/UI/Pages/ComposableScreen/types.d.ts.map +1 -1
  25. package/dist/UI/Pages/ComposableScreen/types.js +16 -0
  26. package/dist/UI/Pages/ComposableScreen/types.js.map +1 -1
  27. package/package.json +2 -2
  28. package/src/UI/Pages/ComposableScreen/Renderer.tsx +12 -1
  29. package/src/UI/Pages/ComposableScreen/elements/ButtonElement.tsx +29 -1
  30. package/src/UI/Pages/ComposableScreen/elements/CarouselElement.tsx +45 -1
  31. package/src/UI/Pages/ComposableScreen/elements/collectDefaults.ts +76 -0
  32. package/src/UI/Pages/ComposableScreen/elements/expression.ts +199 -0
  33. package/src/UI/Pages/ComposableScreen/elements/renderElement.tsx +8 -0
  34. package/src/UI/Pages/ComposableScreen/types.ts +36 -0
@@ -1,4 +1,4 @@
1
- import React, { useRef, useState } from "react";
1
+ import React, { useEffect, useRef, useState } from "react";
2
2
  import { View, type LayoutChangeEvent } from "react-native";
3
3
  import { z } from "zod";
4
4
  import { useSharedValue } from "react-native-reanimated";
@@ -20,6 +20,8 @@ export type CarouselElementProps = BaseBoxProps & {
20
20
  dotHeight?: number;
21
21
  dotsGap?: number;
22
22
  dotsMarginTop?: number;
23
+ defaultIndex?: number | null;
24
+ variableName?: string;
23
25
  };
24
26
 
25
27
  export const CarouselElementPropsSchema = BaseBoxPropsSchema.extend({
@@ -34,6 +36,8 @@ export const CarouselElementPropsSchema = BaseBoxPropsSchema.extend({
34
36
  dotHeight: z.number().nonnegative().optional().default(4),
35
37
  dotsGap: z.number().nonnegative().optional().default(8),
36
38
  dotsMarginTop: z.number().optional().default(12),
39
+ defaultIndex: z.number().int().nonnegative().nullable().optional(),
40
+ variableName: z.string().min(1).optional(),
37
41
  });
38
42
 
39
43
  type CarouselUIElement = Extract<UIElement, { type: "Carousel" }>;
@@ -54,6 +58,39 @@ export function CarouselElementComponent({ element, ctx }: Props): React.ReactEl
54
58
 
55
59
  const carouselType = props.carouselType ?? "normal";
56
60
 
61
+ const variableName = props.variableName;
62
+ const variableValue = variableName ? ctx.variables[variableName]?.value : undefined;
63
+ const childrenCount = children.length;
64
+ const clampIndex = (n: number) => Math.max(0, Math.min(n, Math.max(0, childrenCount - 1)));
65
+
66
+ // Frozen on first mount — RNRC `defaultIndex` only applies at mount.
67
+ const initialIndexRef = useRef<number | null>(null);
68
+ if (initialIndexRef.current === null) {
69
+ const parsed = variableValue !== undefined ? parseInt(variableValue, 10) : NaN;
70
+ const fromVar = Number.isFinite(parsed) ? parsed : null;
71
+ initialIndexRef.current = clampIndex(fromVar ?? props.defaultIndex ?? 0);
72
+ }
73
+ const lastSyncedIndexRef = useRef<number>(initialIndexRef.current);
74
+
75
+ useEffect(() => {
76
+ if (!variableName) return;
77
+ const parsed = parseInt(variableValue ?? "", 10);
78
+ if (!Number.isFinite(parsed)) return;
79
+ const target = clampIndex(parsed);
80
+ if (target === lastSyncedIndexRef.current) return;
81
+ lastSyncedIndexRef.current = target;
82
+ ref.current?.scrollTo({ count: target - progress.value, animated: true });
83
+ }, [variableName, variableValue, childrenCount]);
84
+
85
+ // Persist the initial index into ctx.variables when no value exists yet, so the
86
+ // default reaches downstream renderWhen / interpolation across renders.
87
+ useEffect(() => {
88
+ if (!variableName) return;
89
+ if (variableValue !== undefined) return;
90
+ if (props.defaultIndex == null) return;
91
+ ctx.setVariable(variableName, { value: String(clampIndex(props.defaultIndex)) });
92
+ }, [variableName, variableValue, props.defaultIndex, childrenCount]);
93
+
57
94
  const onLayout = (e: LayoutChangeEvent) => {
58
95
  const { width, height } = e.nativeEvent.layout;
59
96
  if (!size || size.width !== width || size.height !== height) {
@@ -132,6 +169,7 @@ export function CarouselElementComponent({ element, ctx }: Props): React.ReactEl
132
169
  loop={props.loop}
133
170
  autoPlay={props.autoPlay}
134
171
  autoPlayInterval={props.autoPlayInterval}
172
+ defaultIndex={initialIndexRef.current ?? 0}
135
173
  snapEnabled={true}
136
174
  pagingEnabled={true}
137
175
  data={children}
@@ -142,6 +180,12 @@ export function CarouselElementComponent({ element, ctx }: Props): React.ReactEl
142
180
  onProgressChange={(_: number, absoluteProgress: number) => {
143
181
  progress.value = absoluteProgress;
144
182
  }}
183
+ onSnapToItem={(index: number) => {
184
+ if (!variableName) return;
185
+ if (index === lastSyncedIndexRef.current) return;
186
+ lastSyncedIndexRef.current = index;
187
+ ctx.setVariable(variableName, { value: String(index) });
188
+ }}
145
189
  {...(modeProps as any)}
146
190
  />
147
191
  )}
@@ -0,0 +1,76 @@
1
+ import { UIElement } from "../types";
2
+ import { ComposableVariableEntry } from "../../../Provider/OnboardingProgressProvider";
3
+
4
+ /**
5
+ * Walks the element tree and returns the initial variable map declared via
6
+ * element-level defaults (`defaultIndex`, `defaultValue`, `defaultValues`).
7
+ * Used by `Renderer` to overlay defaults onto `ctx.variables` so
8
+ * `renderWhen` / expressions evaluate against them on first render — before
9
+ * per-element seeding effects run.
10
+ */
11
+ export function collectElementDefaults(
12
+ elements: UIElement[]
13
+ ): Record<string, ComposableVariableEntry> {
14
+ const out: Record<string, ComposableVariableEntry> = {};
15
+ const visit = (el: UIElement) => {
16
+ switch (el.type) {
17
+ case "Carousel": {
18
+ const name = el.props.variableName;
19
+ if (name && el.props.defaultIndex != null) {
20
+ // Mirror CarouselElementComponent's clampIndex so the overlaid default
21
+ // matches the index the carousel actually mounts at.
22
+ const raw = Number(el.props.defaultIndex);
23
+ const safe = Number.isFinite(raw) ? raw : 0;
24
+ const maxIdx = Math.max(0, el.children.length - 1);
25
+ const clamped = Math.max(0, Math.min(safe, maxIdx));
26
+ out[name] = { value: String(clamped) };
27
+ }
28
+ el.children.forEach(visit);
29
+ break;
30
+ }
31
+ case "RadioGroup": {
32
+ const name = el.props.variableName;
33
+ const dv = el.props.defaultValue;
34
+ if (name && dv !== undefined) {
35
+ const item = el.props.items.find((i) => i.value === dv);
36
+ out[name] = { value: dv, label: item?.label };
37
+ }
38
+ break;
39
+ }
40
+ case "CheckboxGroup": {
41
+ const name = el.props.variableName;
42
+ const dvs = el.props.defaultValues;
43
+ if (name && dvs !== undefined) {
44
+ const labels = dvs.map(
45
+ (dv) => el.props.items.find((i) => i.value === dv)?.label ?? dv
46
+ );
47
+ out[name] = { value: JSON.stringify(dvs), label: labels.join(", ") };
48
+ }
49
+ break;
50
+ }
51
+ case "Input": {
52
+ const name = el.props.variableName;
53
+ if (name && el.props.defaultValue !== undefined) {
54
+ out[name] = { value: el.props.defaultValue };
55
+ }
56
+ break;
57
+ }
58
+ case "DatePicker": {
59
+ const name = el.props.variableName;
60
+ if (name && el.props.defaultValue !== undefined) {
61
+ const d = new Date(el.props.defaultValue);
62
+ if (!isNaN(d.getTime())) out[name] = { value: d.toISOString() };
63
+ }
64
+ break;
65
+ }
66
+ case "YStack":
67
+ case "XStack":
68
+ case "ZStack":
69
+ case "SafeAreaView":
70
+ el.children.forEach(visit);
71
+ break;
72
+ }
73
+ };
74
+ elements.forEach(visit);
75
+ return out;
76
+ }
@@ -0,0 +1,199 @@
1
+ import type { ComposableVariableEntry, ComposableVariableKind } from "@rocapine/react-native-onboarding";
2
+ import { interpolate } from "./shared";
3
+
4
+ type Token =
5
+ | { kind: "num"; value: number; isInt: boolean }
6
+ | { kind: "var"; name: string }
7
+ | { kind: "op"; op: "+" | "-" | "*" | "/" }
8
+ | { kind: "lparen" }
9
+ | { kind: "rparen" }
10
+ | { kind: "eof" };
11
+
12
+ type Value =
13
+ | { kind: "number"; n: number; isInt: boolean }
14
+ | { kind: "string"; s: string };
15
+
16
+ const isDigit = (c: string) => c >= "0" && c <= "9";
17
+ const isSpace = (c: string) => c === " " || c === "\t" || c === "\n" || c === "\r";
18
+
19
+ function tokenize(input: string): Token[] | null {
20
+ const tokens: Token[] = [];
21
+ let i = 0;
22
+ while (i < input.length) {
23
+ const c = input[i];
24
+ if (isSpace(c)) {
25
+ i++;
26
+ continue;
27
+ }
28
+ if (c === "{" && input[i + 1] === "{") {
29
+ const end = input.indexOf("}}", i + 2);
30
+ if (end === -1) return null;
31
+ const name = input.slice(i + 2, end).trim();
32
+ if (!name) return null;
33
+ tokens.push({ kind: "var", name });
34
+ i = end + 2;
35
+ continue;
36
+ }
37
+ if (c === "(") { tokens.push({ kind: "lparen" }); i++; continue; }
38
+ if (c === ")") { tokens.push({ kind: "rparen" }); i++; continue; }
39
+ if (c === "+" || c === "-" || c === "*" || c === "/") {
40
+ tokens.push({ kind: "op", op: c });
41
+ i++;
42
+ continue;
43
+ }
44
+ if (isDigit(c) || (c === "." && isDigit(input[i + 1] ?? ""))) {
45
+ let j = i;
46
+ let dot = false;
47
+ while (j < input.length && (isDigit(input[j]) || input[j] === ".")) {
48
+ if (input[j] === ".") {
49
+ if (dot) return null;
50
+ dot = true;
51
+ }
52
+ j++;
53
+ }
54
+ const num = parseFloat(input.slice(i, j));
55
+ if (!Number.isFinite(num)) return null;
56
+ tokens.push({ kind: "num", value: num, isInt: !dot });
57
+ i = j;
58
+ continue;
59
+ }
60
+ return null;
61
+ }
62
+ tokens.push({ kind: "eof" });
63
+ return tokens;
64
+ }
65
+
66
+ function resolveVar(name: string, vars: Record<string, ComposableVariableEntry>): Value {
67
+ const entry = vars[name];
68
+ // Missing variable in arithmetic context defaults to numeric 0 so increment
69
+ // / decrement patterns work on first click before the variable is seeded.
70
+ if (!entry) return { kind: "number", n: 0, isInt: true };
71
+ const raw = entry.value;
72
+ const k = entry.kind;
73
+ if (k === "string") return { kind: "string", s: raw };
74
+ if (k === "int") {
75
+ const n = parseInt(raw, 10);
76
+ return Number.isFinite(n) ? { kind: "number", n, isInt: true } : { kind: "string", s: raw };
77
+ }
78
+ if (k === "float") {
79
+ const n = parseFloat(raw);
80
+ return Number.isFinite(n) ? { kind: "number", n, isInt: false } : { kind: "string", s: raw };
81
+ }
82
+ // No kind tag — infer from string content.
83
+ const trimmed = raw.trim();
84
+ if (trimmed !== "" && /^-?\d+(\.\d+)?$/.test(trimmed)) {
85
+ const n = parseFloat(trimmed);
86
+ if (Number.isFinite(n)) return { kind: "number", n, isInt: Number.isInteger(n) && !trimmed.includes(".") };
87
+ }
88
+ return { kind: "string", s: raw };
89
+ }
90
+
91
+ function valueToString(v: Value): string {
92
+ if (v.kind === "string") return v.s;
93
+ if (v.isInt) return Math.trunc(v.n).toString();
94
+ return v.n.toString();
95
+ }
96
+
97
+ function parse(tokens: Token[], vars: Record<string, ComposableVariableEntry>): Value | null {
98
+ let pos = 0;
99
+ const peek = () => tokens[pos];
100
+ const advance = () => tokens[pos++];
101
+
102
+ const factor = (): Value | null => {
103
+ const t = peek();
104
+ if (t.kind === "lparen") {
105
+ advance();
106
+ const v = expr();
107
+ if (!v) return null;
108
+ if (peek().kind !== "rparen") return null;
109
+ advance();
110
+ return v;
111
+ }
112
+ if (t.kind === "op" && t.op === "-") {
113
+ advance();
114
+ const v = factor();
115
+ if (!v || v.kind !== "number") return null;
116
+ return { kind: "number", n: -v.n, isInt: v.isInt };
117
+ }
118
+ if (t.kind === "num") {
119
+ advance();
120
+ return { kind: "number", n: t.value, isInt: t.isInt };
121
+ }
122
+ if (t.kind === "var") {
123
+ advance();
124
+ return resolveVar(t.name, vars);
125
+ }
126
+ return null;
127
+ };
128
+
129
+ const term = (): Value | null => {
130
+ let left = factor();
131
+ if (!left) return null;
132
+ while (peek().kind === "op" && ((peek() as any).op === "*" || (peek() as any).op === "/")) {
133
+ const op = (advance() as any).op as "*" | "/";
134
+ const right = factor();
135
+ if (!right) return null;
136
+ if (left.kind !== "number" || right.kind !== "number") return null;
137
+ if (op === "/" && right.n === 0) return null;
138
+ const result: number = op === "*" ? left.n * right.n : left.n / right.n;
139
+ if (!Number.isFinite(result)) return null;
140
+ const isInt: boolean = op === "*" ? left.isInt && right.isInt : Number.isInteger(result);
141
+ left = { kind: "number", n: result, isInt };
142
+ }
143
+ return left;
144
+ };
145
+
146
+ const expr = (): Value | null => {
147
+ let left = term();
148
+ if (!left) return null;
149
+ while (peek().kind === "op" && ((peek() as any).op === "+" || (peek() as any).op === "-")) {
150
+ const op = (advance() as any).op as "+" | "-";
151
+ const right = term();
152
+ if (!right) return null;
153
+ if (op === "+") {
154
+ if (left.kind === "number" && right.kind === "number") {
155
+ left = { kind: "number", n: left.n + right.n, isInt: left.isInt && right.isInt };
156
+ } else {
157
+ left = { kind: "string", s: valueToString(left) + valueToString(right) };
158
+ }
159
+ } else {
160
+ if (left.kind !== "number" || right.kind !== "number") return null;
161
+ left = { kind: "number", n: left.n - right.n, isInt: left.isInt && right.isInt };
162
+ }
163
+ }
164
+ return left;
165
+ };
166
+
167
+ const result = expr();
168
+ if (!result) return null;
169
+ if (peek().kind !== "eof") return null;
170
+ return result;
171
+ }
172
+
173
+ /**
174
+ * Evaluate a `setVariable` expression-mode value template.
175
+ *
176
+ * Accepts `{{var}}` references, numeric literals (int / float), `+ - * /` and
177
+ * parentheses. Variable values are coerced according to their `kind` tag
178
+ * (string / int / float), or inferred from their string content when no tag
179
+ * is present. `+` on any non-numeric operand becomes string concat.
180
+ *
181
+ * On any parse or evaluation failure, falls back to plain interpolation
182
+ * (existing `interpolate()` semantics, returns a string).
183
+ */
184
+ export function evaluateSetVariableExpression(
185
+ template: string,
186
+ vars: Record<string, ComposableVariableEntry>
187
+ ): { value: string; kind: ComposableVariableKind } {
188
+ const tokens = tokenize(template);
189
+ if (tokens) {
190
+ const result = parse(tokens, vars);
191
+ if (result) {
192
+ if (result.kind === "number") {
193
+ return { value: valueToString(result), kind: result.isInt ? "int" : "float" };
194
+ }
195
+ return { value: result.s, kind: "string" };
196
+ }
197
+ }
198
+ return { value: interpolate(template, vars), kind: "string" };
199
+ }
@@ -1,4 +1,5 @@
1
1
  import React from "react";
2
+ import { evaluateCondition } from "@rocapine/react-native-onboarding";
2
3
  import { UIElement } from "../types";
3
4
  import { RenderContext } from "./shared";
4
5
  import { StackElementComponent } from "./StackElement";
@@ -22,6 +23,13 @@ export const renderElement = (
22
23
  ctx: RenderContext,
23
24
  parentType?: "XStack" | "YStack" | "ZStack"
24
25
  ): React.ReactNode => {
26
+ if (element.renderWhen) {
27
+ const flatVars = Object.fromEntries(
28
+ Object.entries(ctx.variables).map(([k, v]) => [k, v?.value])
29
+ );
30
+ if (!evaluateCondition(element.renderWhen, flatVars)) return null;
31
+ }
32
+
25
33
  if (element.type === "YStack" || element.type === "XStack") {
26
34
  return <StackElementComponent key={element.id} element={element} ctx={ctx} parentType={parentType} />;
27
35
  }
@@ -1,4 +1,10 @@
1
1
  import { z } from "zod";
2
+ import {
3
+ type LeafCondition,
4
+ type ConditionGroup,
5
+ LeafConditionSchema,
6
+ ConditionGroupSchema,
7
+ } from "@rocapine/react-native-onboarding";
2
8
  import { CustomPayloadSchema } from "../types";
3
9
  import { type StackElementProps, StackElementPropsSchema } from "./elements/StackElement";
4
10
  import { type TextElementProps, TextElementPropsSchema } from "./elements/TextElement";
@@ -40,6 +46,7 @@ export type UIElement =
40
46
  | {
41
47
  id: string;
42
48
  name?: string;
49
+ renderWhen?: LeafCondition | ConditionGroup;
43
50
  type: "YStack" | "XStack";
44
51
  props: StackElementProps;
45
52
  children: UIElement[];
@@ -47,72 +54,84 @@ export type UIElement =
47
54
  | {
48
55
  id: string;
49
56
  name?: string;
57
+ renderWhen?: LeafCondition | ConditionGroup;
50
58
  type: "Text";
51
59
  props: TextElementProps;
52
60
  }
53
61
  | {
54
62
  id: string;
55
63
  name?: string;
64
+ renderWhen?: LeafCondition | ConditionGroup;
56
65
  type: "Image";
57
66
  props: ImageElementProps;
58
67
  }
59
68
  | {
60
69
  id: string;
61
70
  name?: string;
71
+ renderWhen?: LeafCondition | ConditionGroup;
62
72
  type: "Lottie";
63
73
  props: LottieElementProps;
64
74
  }
65
75
  | {
66
76
  id: string;
67
77
  name?: string;
78
+ renderWhen?: LeafCondition | ConditionGroup;
68
79
  type: "Rive";
69
80
  props: RiveElementProps;
70
81
  }
71
82
  | {
72
83
  id: string;
73
84
  name?: string;
85
+ renderWhen?: LeafCondition | ConditionGroup;
74
86
  type: "Icon";
75
87
  props: IconElementProps;
76
88
  }
77
89
  | {
78
90
  id: string;
79
91
  name?: string;
92
+ renderWhen?: LeafCondition | ConditionGroup;
80
93
  type: "Video";
81
94
  props: VideoElementProps;
82
95
  }
83
96
  | {
84
97
  id: string;
85
98
  name?: string;
99
+ renderWhen?: LeafCondition | ConditionGroup;
86
100
  type: "Input";
87
101
  props: InputElementProps;
88
102
  }
89
103
  | {
90
104
  id: string;
91
105
  name?: string;
106
+ renderWhen?: LeafCondition | ConditionGroup;
92
107
  type: "Button";
93
108
  props: ButtonElementProps;
94
109
  }
95
110
  | {
96
111
  id: string;
97
112
  name?: string;
113
+ renderWhen?: LeafCondition | ConditionGroup;
98
114
  type: "RadioGroup";
99
115
  props: RadioGroupElementProps;
100
116
  }
101
117
  | {
102
118
  id: string;
103
119
  name?: string;
120
+ renderWhen?: LeafCondition | ConditionGroup;
104
121
  type: "CheckboxGroup";
105
122
  props: CheckboxGroupElementProps;
106
123
  }
107
124
  | {
108
125
  id: string;
109
126
  name?: string;
127
+ renderWhen?: LeafCondition | ConditionGroup;
110
128
  type: "DatePicker";
111
129
  props: DatePickerElementProps;
112
130
  }
113
131
  | {
114
132
  id: string;
115
133
  name?: string;
134
+ renderWhen?: LeafCondition | ConditionGroup;
116
135
  type: "Carousel";
117
136
  props: CarouselElementProps;
118
137
  children: UIElement[];
@@ -120,6 +139,7 @@ export type UIElement =
120
139
  | {
121
140
  id: string;
122
141
  name?: string;
142
+ renderWhen?: LeafCondition | ConditionGroup;
123
143
  type: "ZStack";
124
144
  props: ZStackElementProps;
125
145
  children: UIElement[];
@@ -127,6 +147,7 @@ export type UIElement =
127
147
  | {
128
148
  id: string;
129
149
  name?: string;
150
+ renderWhen?: LeafCondition | ConditionGroup;
130
151
  type: "SafeAreaView";
131
152
  props: SafeAreaViewElementProps;
132
153
  children: UIElement[];
@@ -137,6 +158,7 @@ export const UIElementSchema: z.ZodType<UIElement> = z.lazy(() =>
137
158
  z.object({
138
159
  id: z.string(),
139
160
  name: z.string().optional(),
161
+ renderWhen: z.union([LeafConditionSchema, ConditionGroupSchema]).optional(),
140
162
  type: z.union([z.literal("YStack"), z.literal("XStack")]),
141
163
  props: StackElementPropsSchema,
142
164
  children: z.array(UIElementSchema),
@@ -144,72 +166,84 @@ export const UIElementSchema: z.ZodType<UIElement> = z.lazy(() =>
144
166
  z.object({
145
167
  id: z.string(),
146
168
  name: z.string().optional(),
169
+ renderWhen: z.union([LeafConditionSchema, ConditionGroupSchema]).optional(),
147
170
  type: z.literal("Text"),
148
171
  props: TextElementPropsSchema,
149
172
  }),
150
173
  z.object({
151
174
  id: z.string(),
152
175
  name: z.string().optional(),
176
+ renderWhen: z.union([LeafConditionSchema, ConditionGroupSchema]).optional(),
153
177
  type: z.literal("Image"),
154
178
  props: ImageElementPropsSchema,
155
179
  }),
156
180
  z.object({
157
181
  id: z.string(),
158
182
  name: z.string().optional(),
183
+ renderWhen: z.union([LeafConditionSchema, ConditionGroupSchema]).optional(),
159
184
  type: z.literal("Lottie"),
160
185
  props: LottieElementPropsSchema,
161
186
  }),
162
187
  z.object({
163
188
  id: z.string(),
164
189
  name: z.string().optional(),
190
+ renderWhen: z.union([LeafConditionSchema, ConditionGroupSchema]).optional(),
165
191
  type: z.literal("Rive"),
166
192
  props: RiveElementPropsSchema,
167
193
  }),
168
194
  z.object({
169
195
  id: z.string(),
170
196
  name: z.string().optional(),
197
+ renderWhen: z.union([LeafConditionSchema, ConditionGroupSchema]).optional(),
171
198
  type: z.literal("Icon"),
172
199
  props: IconElementPropsSchema,
173
200
  }),
174
201
  z.object({
175
202
  id: z.string(),
176
203
  name: z.string().optional(),
204
+ renderWhen: z.union([LeafConditionSchema, ConditionGroupSchema]).optional(),
177
205
  type: z.literal("Video"),
178
206
  props: VideoElementPropsSchema,
179
207
  }),
180
208
  z.object({
181
209
  id: z.string(),
182
210
  name: z.string().optional(),
211
+ renderWhen: z.union([LeafConditionSchema, ConditionGroupSchema]).optional(),
183
212
  type: z.literal("Input"),
184
213
  props: InputElementPropsSchema,
185
214
  }),
186
215
  z.object({
187
216
  id: z.string(),
188
217
  name: z.string().optional(),
218
+ renderWhen: z.union([LeafConditionSchema, ConditionGroupSchema]).optional(),
189
219
  type: z.literal("Button"),
190
220
  props: ButtonElementPropsSchema,
191
221
  }),
192
222
  z.object({
193
223
  id: z.string(),
194
224
  name: z.string().optional(),
225
+ renderWhen: z.union([LeafConditionSchema, ConditionGroupSchema]).optional(),
195
226
  type: z.literal("RadioGroup"),
196
227
  props: RadioGroupElementPropsSchema,
197
228
  }),
198
229
  z.object({
199
230
  id: z.string(),
200
231
  name: z.string().optional(),
232
+ renderWhen: z.union([LeafConditionSchema, ConditionGroupSchema]).optional(),
201
233
  type: z.literal("CheckboxGroup"),
202
234
  props: CheckboxGroupElementPropsSchema,
203
235
  }),
204
236
  z.object({
205
237
  id: z.string(),
206
238
  name: z.string().optional(),
239
+ renderWhen: z.union([LeafConditionSchema, ConditionGroupSchema]).optional(),
207
240
  type: z.literal("DatePicker"),
208
241
  props: DatePickerElementPropsSchema,
209
242
  }),
210
243
  z.object({
211
244
  id: z.string(),
212
245
  name: z.string().optional(),
246
+ renderWhen: z.union([LeafConditionSchema, ConditionGroupSchema]).optional(),
213
247
  type: z.literal("Carousel"),
214
248
  props: CarouselElementPropsSchema,
215
249
  children: z.array(UIElementSchema),
@@ -217,6 +251,7 @@ export const UIElementSchema: z.ZodType<UIElement> = z.lazy(() =>
217
251
  z.object({
218
252
  id: z.string(),
219
253
  name: z.string().optional(),
254
+ renderWhen: z.union([LeafConditionSchema, ConditionGroupSchema]).optional(),
220
255
  type: z.literal("ZStack"),
221
256
  props: ZStackElementPropsSchema,
222
257
  children: z.array(UIElementSchema),
@@ -224,6 +259,7 @@ export const UIElementSchema: z.ZodType<UIElement> = z.lazy(() =>
224
259
  z.object({
225
260
  id: z.string(),
226
261
  name: z.string().optional(),
262
+ renderWhen: z.union([LeafConditionSchema, ConditionGroupSchema]).optional(),
227
263
  type: z.literal("SafeAreaView"),
228
264
  props: SafeAreaViewElementPropsSchema,
229
265
  children: z.array(UIElementSchema),