noboarding 0.1.0-alpha
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/README.md +515 -0
- package/REVENUECAT_SETUP.md +756 -0
- package/SETUP_GUIDE.md +873 -0
- package/cusomte_screens.md +1964 -0
- package/lib/OnboardingFlow.d.ts +3 -0
- package/lib/OnboardingFlow.js +235 -0
- package/lib/analytics.d.ts +25 -0
- package/lib/analytics.js +72 -0
- package/lib/api.d.ts +31 -0
- package/lib/api.js +149 -0
- package/lib/components/ElementRenderer.d.ts +13 -0
- package/lib/components/ElementRenderer.js +521 -0
- package/lib/index.d.ts +6 -0
- package/lib/index.js +18 -0
- package/lib/types.d.ts +185 -0
- package/lib/types.js +2 -0
- package/lib/variableUtils.d.ts +17 -0
- package/lib/variableUtils.js +118 -0
- package/logic.md +2095 -0
- package/package.json +44 -0
- package/src/OnboardingFlow.tsx +276 -0
- package/src/analytics.ts +84 -0
- package/src/api.ts +173 -0
- package/src/components/ElementRenderer.tsx +627 -0
- package/src/index.ts +32 -0
- package/src/types.ts +242 -0
- package/src/variableUtils.ts +133 -0
- package/tsconfig.json +20 -0
|
@@ -0,0 +1,521 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
+
var ownKeys = function(o) {
|
|
20
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
+
var ar = [];
|
|
22
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
+
return ar;
|
|
24
|
+
};
|
|
25
|
+
return ownKeys(o);
|
|
26
|
+
};
|
|
27
|
+
return function (mod) {
|
|
28
|
+
if (mod && mod.__esModule) return mod;
|
|
29
|
+
var result = {};
|
|
30
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
+
__setModuleDefault(result, mod);
|
|
32
|
+
return result;
|
|
33
|
+
};
|
|
34
|
+
})();
|
|
35
|
+
var __rest = (this && this.__rest) || function (s, e) {
|
|
36
|
+
var t = {};
|
|
37
|
+
for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0)
|
|
38
|
+
t[p] = s[p];
|
|
39
|
+
if (s != null && typeof Object.getOwnPropertySymbols === "function")
|
|
40
|
+
for (var i = 0, p = Object.getOwnPropertySymbols(s); i < p.length; i++) {
|
|
41
|
+
if (e.indexOf(p[i]) < 0 && Object.prototype.propertyIsEnumerable.call(s, p[i]))
|
|
42
|
+
t[p[i]] = s[p[i]];
|
|
43
|
+
}
|
|
44
|
+
return t;
|
|
45
|
+
};
|
|
46
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
47
|
+
exports.ElementRenderer = void 0;
|
|
48
|
+
const react_1 = __importStar(require("react"));
|
|
49
|
+
const react_native_1 = require("react-native");
|
|
50
|
+
const variableUtils_1 = require("../variableUtils");
|
|
51
|
+
// Try to import LinearGradient — optional peer dependency
|
|
52
|
+
let LinearGradient = null;
|
|
53
|
+
try {
|
|
54
|
+
LinearGradient = require('expo-linear-gradient').LinearGradient;
|
|
55
|
+
}
|
|
56
|
+
catch (_a) {
|
|
57
|
+
try {
|
|
58
|
+
LinearGradient = require('react-native-linear-gradient').default;
|
|
59
|
+
}
|
|
60
|
+
catch (_b) {
|
|
61
|
+
// Neither available — gradients will fall back to first color
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
// Try to import vector icons — optional peer dependency
|
|
65
|
+
let IconSets = {};
|
|
66
|
+
try {
|
|
67
|
+
const icons = require('@expo/vector-icons');
|
|
68
|
+
IconSets = {
|
|
69
|
+
lucide: icons.Feather, // Closest match to Lucide
|
|
70
|
+
feather: icons.Feather,
|
|
71
|
+
material: icons.MaterialIcons,
|
|
72
|
+
'material-community': icons.MaterialCommunityIcons,
|
|
73
|
+
ionicons: icons.Ionicons,
|
|
74
|
+
fontawesome: icons.FontAwesome,
|
|
75
|
+
'sf-symbols': icons.Ionicons, // Closest match to SF Symbols
|
|
76
|
+
};
|
|
77
|
+
}
|
|
78
|
+
catch (_c) {
|
|
79
|
+
// Not available — icons will fall back to text placeholder
|
|
80
|
+
}
|
|
81
|
+
const ElementRenderer = ({ elements, analytics, screenId, onNavigate, onDismiss, variables = {}, onSetVariable, }) => {
|
|
82
|
+
// Track toggled element IDs for toggle actions
|
|
83
|
+
const [toggledIds, setToggledIds] = (0, react_1.useState)(new Set());
|
|
84
|
+
// Track selection groups: group name → selected element ID
|
|
85
|
+
const [groupSelections, setGroupSelections] = (0, react_1.useState)({});
|
|
86
|
+
const executeAction = (0, react_1.useCallback)((action, element) => {
|
|
87
|
+
// Track the action
|
|
88
|
+
analytics === null || analytics === void 0 ? void 0 : analytics.track('element_action', {
|
|
89
|
+
screen_id: screenId,
|
|
90
|
+
element_id: element.id,
|
|
91
|
+
action_type: action.type,
|
|
92
|
+
destination: typeof action.destination === 'string' ? action.destination : 'conditional',
|
|
93
|
+
});
|
|
94
|
+
switch (action.type) {
|
|
95
|
+
case 'set_variable':
|
|
96
|
+
if (action.variable !== undefined && onSetVariable) {
|
|
97
|
+
onSetVariable(action.variable, action.value);
|
|
98
|
+
}
|
|
99
|
+
break;
|
|
100
|
+
case 'toggle': {
|
|
101
|
+
const group = action.group;
|
|
102
|
+
if (group) {
|
|
103
|
+
// Single-select group: deselect previous, select new
|
|
104
|
+
setGroupSelections((prev) => {
|
|
105
|
+
const prevSelected = prev[group];
|
|
106
|
+
if (prevSelected === element.id)
|
|
107
|
+
return prev; // already selected
|
|
108
|
+
return Object.assign(Object.assign({}, prev), { [group]: element.id });
|
|
109
|
+
});
|
|
110
|
+
setToggledIds((prev) => {
|
|
111
|
+
const next = new Set(prev);
|
|
112
|
+
const prevSelected = groupSelections[group];
|
|
113
|
+
if (prevSelected)
|
|
114
|
+
next.delete(prevSelected);
|
|
115
|
+
next.add(element.id);
|
|
116
|
+
return next;
|
|
117
|
+
});
|
|
118
|
+
}
|
|
119
|
+
else {
|
|
120
|
+
// Ungrouped toggle: multi-select
|
|
121
|
+
setToggledIds((prev) => {
|
|
122
|
+
const next = new Set(prev);
|
|
123
|
+
if (next.has(element.id)) {
|
|
124
|
+
next.delete(element.id);
|
|
125
|
+
}
|
|
126
|
+
else {
|
|
127
|
+
next.add(element.id);
|
|
128
|
+
}
|
|
129
|
+
return next;
|
|
130
|
+
});
|
|
131
|
+
}
|
|
132
|
+
break;
|
|
133
|
+
}
|
|
134
|
+
case 'navigate':
|
|
135
|
+
if (onNavigate && action.destination) {
|
|
136
|
+
onNavigate(action.destination);
|
|
137
|
+
}
|
|
138
|
+
break;
|
|
139
|
+
case 'link':
|
|
140
|
+
if (action.destination && typeof action.destination === 'string') {
|
|
141
|
+
react_native_1.Linking.openURL(action.destination).catch(() => { });
|
|
142
|
+
}
|
|
143
|
+
break;
|
|
144
|
+
case 'dismiss':
|
|
145
|
+
onDismiss === null || onDismiss === void 0 ? void 0 : onDismiss();
|
|
146
|
+
break;
|
|
147
|
+
case 'tap':
|
|
148
|
+
// Generic tap — analytics already tracked above
|
|
149
|
+
break;
|
|
150
|
+
}
|
|
151
|
+
}, [groupSelections, onNavigate, onDismiss, analytics, screenId, onSetVariable]);
|
|
152
|
+
const handleAction = (0, react_1.useCallback)((element) => {
|
|
153
|
+
// Execute single action (backward compatible)
|
|
154
|
+
if (element.action) {
|
|
155
|
+
executeAction(element.action, element);
|
|
156
|
+
}
|
|
157
|
+
// Execute all actions in the actions array
|
|
158
|
+
if (element.actions) {
|
|
159
|
+
for (const action of element.actions) {
|
|
160
|
+
executeAction(action, element);
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
}, [executeAction]);
|
|
164
|
+
if (!elements || elements.length === 0) {
|
|
165
|
+
return null;
|
|
166
|
+
}
|
|
167
|
+
return (<>
|
|
168
|
+
{elements.map((element) => (<RenderNode key={element.id} element={element} toggledIds={toggledIds} groupSelections={groupSelections} onAction={handleAction} variables={variables}/>))}
|
|
169
|
+
</>);
|
|
170
|
+
};
|
|
171
|
+
exports.ElementRenderer = ElementRenderer;
|
|
172
|
+
const RenderNode = ({ element, toggledIds, groupSelections, onAction, variables }) => {
|
|
173
|
+
var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m, _o, _p, _q, _r, _s, _t, _u, _v, _w, _x, _y, _z, _0, _1, _2, _3;
|
|
174
|
+
// Variable-based conditions — hide element if condition is not met
|
|
175
|
+
if ((_a = element.conditions) === null || _a === void 0 ? void 0 : _a.show_if) {
|
|
176
|
+
const shouldShow = (0, variableUtils_1.evaluateCondition)(element.conditions.show_if, variables);
|
|
177
|
+
if (!shouldShow)
|
|
178
|
+
return null;
|
|
179
|
+
}
|
|
180
|
+
const style = convertStyle(element.style || {});
|
|
181
|
+
const isToggled = toggledIds.has(element.id);
|
|
182
|
+
// Apply toggle visual state
|
|
183
|
+
const hasToggleAction = ((_b = element.action) === null || _b === void 0 ? void 0 : _b.type) === 'toggle' ||
|
|
184
|
+
((_c = element.actions) === null || _c === void 0 ? void 0 : _c.some(a => a.type === 'toggle'));
|
|
185
|
+
if (hasToggleAction) {
|
|
186
|
+
if (isToggled) {
|
|
187
|
+
style.borderWidth = 2;
|
|
188
|
+
style.borderColor = ((_d = element.style) === null || _d === void 0 ? void 0 : _d.borderColor) || '#000000';
|
|
189
|
+
}
|
|
190
|
+
else {
|
|
191
|
+
style.borderWidth = ((_e = element.style) === null || _e === void 0 ? void 0 : _e.borderWidth) || 2;
|
|
192
|
+
style.borderColor = 'transparent';
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
// Conditional visibility based on selection group state
|
|
196
|
+
if (element.visibleWhen) {
|
|
197
|
+
const groupHasSelection = !!groupSelections[element.visibleWhen.group];
|
|
198
|
+
const shouldShow = groupHasSelection === element.visibleWhen.hasSelection;
|
|
199
|
+
style.opacity = shouldShow ? 1 : 0;
|
|
200
|
+
style.pointerEvents = shouldShow ? 'auto' : 'none';
|
|
201
|
+
}
|
|
202
|
+
// Wrap in TouchableOpacity if element has an action or actions
|
|
203
|
+
const hasAction = !!element.action || (element.actions && element.actions.length > 0);
|
|
204
|
+
const wrapWithAction = (content) => {
|
|
205
|
+
if (!hasAction)
|
|
206
|
+
return content;
|
|
207
|
+
return (<react_native_1.TouchableOpacity key={element.id} activeOpacity={0.7} onPress={() => onAction(element)}>
|
|
208
|
+
{content}
|
|
209
|
+
</react_native_1.TouchableOpacity>);
|
|
210
|
+
};
|
|
211
|
+
const childProps = { toggledIds, groupSelections, onAction, variables };
|
|
212
|
+
switch (element.type) {
|
|
213
|
+
// ─── Containers ───
|
|
214
|
+
case 'vstack': {
|
|
215
|
+
const vstackContent = (<react_native_1.View style={[style, { flexDirection: 'column' }]}>
|
|
216
|
+
{(_f = element.children) === null || _f === void 0 ? void 0 : _f.map((child) => (<RenderNode key={child.id} element={child} {...childProps}/>))}
|
|
217
|
+
</react_native_1.View>);
|
|
218
|
+
return wrapWithAction(((_g = element.style) === null || _g === void 0 ? void 0 : _g.backgroundGradient)
|
|
219
|
+
? wrapWithGradient(vstackContent, element.style, Object.assign(Object.assign({}, style), { flexDirection: 'column' }))
|
|
220
|
+
: vstackContent);
|
|
221
|
+
}
|
|
222
|
+
case 'hstack': {
|
|
223
|
+
const hstackContent = (<react_native_1.View style={[style, { flexDirection: 'row' }]}>
|
|
224
|
+
{(_h = element.children) === null || _h === void 0 ? void 0 : _h.map((child) => (<RenderNode key={child.id} element={child} {...childProps}/>))}
|
|
225
|
+
</react_native_1.View>);
|
|
226
|
+
return wrapWithAction(((_j = element.style) === null || _j === void 0 ? void 0 : _j.backgroundGradient)
|
|
227
|
+
? wrapWithGradient(hstackContent, element.style, Object.assign(Object.assign({}, style), { flexDirection: 'row' }))
|
|
228
|
+
: hstackContent);
|
|
229
|
+
}
|
|
230
|
+
case 'zstack': {
|
|
231
|
+
const zstackContent = (<react_native_1.View style={style}>
|
|
232
|
+
{(_k = element.children) === null || _k === void 0 ? void 0 : _k.map((child, index) => {
|
|
233
|
+
var _a;
|
|
234
|
+
const childStyle = convertStyle(child.style || {});
|
|
235
|
+
if (index > 0 && !((_a = child.position) === null || _a === void 0 ? void 0 : _a.type)) {
|
|
236
|
+
return (<react_native_1.View key={child.id} style={{ position: 'absolute', top: 0, left: 0, right: 0, bottom: 0 }}>
|
|
237
|
+
<RenderNode element={child} {...childProps}/>
|
|
238
|
+
</react_native_1.View>);
|
|
239
|
+
}
|
|
240
|
+
return <RenderNode key={child.id} element={child} {...childProps}/>;
|
|
241
|
+
})}
|
|
242
|
+
</react_native_1.View>);
|
|
243
|
+
return wrapWithAction(((_l = element.style) === null || _l === void 0 ? void 0 : _l.backgroundGradient)
|
|
244
|
+
? wrapWithGradient(zstackContent, element.style, style)
|
|
245
|
+
: zstackContent);
|
|
246
|
+
}
|
|
247
|
+
case 'scrollview': {
|
|
248
|
+
const isHorizontal = ((_m = element.props) === null || _m === void 0 ? void 0 : _m.direction) === 'horizontal';
|
|
249
|
+
return (<react_native_1.ScrollView style={style} horizontal={isHorizontal} showsVerticalScrollIndicator={false} showsHorizontalScrollIndicator={false}>
|
|
250
|
+
{isHorizontal ? (<react_native_1.View style={{ flexDirection: 'row', gap: (_o = element.style) === null || _o === void 0 ? void 0 : _o.gap }}>
|
|
251
|
+
{(_p = element.children) === null || _p === void 0 ? void 0 : _p.map((child) => (<RenderNode key={child.id} element={child} {...childProps}/>))}
|
|
252
|
+
</react_native_1.View>) : ((_q = element.children) === null || _q === void 0 ? void 0 : _q.map((child) => (<RenderNode key={child.id} element={child} {...childProps}/>)))}
|
|
253
|
+
</react_native_1.ScrollView>);
|
|
254
|
+
}
|
|
255
|
+
// ─── Content Elements ───
|
|
256
|
+
case 'text': {
|
|
257
|
+
const resolvedText = (0, variableUtils_1.resolveTemplate)(((_r = element.props) === null || _r === void 0 ? void 0 : _r.text) || '', variables);
|
|
258
|
+
return (<react_native_1.Text style={style}>
|
|
259
|
+
{resolvedText}
|
|
260
|
+
</react_native_1.Text>);
|
|
261
|
+
}
|
|
262
|
+
case 'icon': {
|
|
263
|
+
if ((_s = element.props) === null || _s === void 0 ? void 0 : _s.emoji) {
|
|
264
|
+
return (<react_native_1.Text style={[style, { textAlign: 'center' }]}>
|
|
265
|
+
{element.props.emoji}
|
|
266
|
+
</react_native_1.Text>);
|
|
267
|
+
}
|
|
268
|
+
// Try to render a real vector icon
|
|
269
|
+
const library = (((_t = element.props) === null || _t === void 0 ? void 0 : _t.library) || 'material').toLowerCase();
|
|
270
|
+
const iconName = (_u = element.props) === null || _u === void 0 ? void 0 : _u.name;
|
|
271
|
+
const IconComponent = IconSets[library];
|
|
272
|
+
if (IconComponent && iconName) {
|
|
273
|
+
const iconSize = style.fontSize || 24;
|
|
274
|
+
const iconColor = style.color || '#000000';
|
|
275
|
+
return (<react_native_1.View style={[style, { alignItems: 'center', justifyContent: 'center' }]}>
|
|
276
|
+
<IconComponent name={iconName} size={iconSize} color={iconColor}/>
|
|
277
|
+
</react_native_1.View>);
|
|
278
|
+
}
|
|
279
|
+
// Fallback — render icon name as text placeholder
|
|
280
|
+
return (<react_native_1.View style={[
|
|
281
|
+
style,
|
|
282
|
+
{
|
|
283
|
+
alignItems: 'center',
|
|
284
|
+
justifyContent: 'center',
|
|
285
|
+
backgroundColor: style.backgroundColor || '#f0f0f0',
|
|
286
|
+
borderRadius: style.borderRadius || 6,
|
|
287
|
+
},
|
|
288
|
+
]}>
|
|
289
|
+
<react_native_1.Text style={{ fontSize: 10, color: '#666' }}>
|
|
290
|
+
{((_v = element.props) === null || _v === void 0 ? void 0 : _v.name) || '●'}
|
|
291
|
+
</react_native_1.Text>
|
|
292
|
+
</react_native_1.View>);
|
|
293
|
+
}
|
|
294
|
+
case 'image':
|
|
295
|
+
if ((_w = element.props) === null || _w === void 0 ? void 0 : _w.url) {
|
|
296
|
+
return (<react_native_1.Image source={{ uri: element.props.url }} style={[style, { resizeMode: 'cover' }]}/>);
|
|
297
|
+
}
|
|
298
|
+
// Placeholder for images without URL
|
|
299
|
+
return (<react_native_1.View style={[
|
|
300
|
+
style,
|
|
301
|
+
{
|
|
302
|
+
backgroundColor: style.backgroundColor || '#f0f0f0',
|
|
303
|
+
alignItems: 'center',
|
|
304
|
+
justifyContent: 'center',
|
|
305
|
+
},
|
|
306
|
+
]}>
|
|
307
|
+
<react_native_1.Text style={{ fontSize: 48 }}>🖼️</react_native_1.Text>
|
|
308
|
+
{((_x = element.props) === null || _x === void 0 ? void 0 : _x.imageDescription) && (<react_native_1.Text style={{ fontSize: 11, color: '#666', textAlign: 'center', padding: 8 }}>
|
|
309
|
+
{element.props.imageDescription}
|
|
310
|
+
</react_native_1.Text>)}
|
|
311
|
+
</react_native_1.View>);
|
|
312
|
+
case 'video':
|
|
313
|
+
// Video placeholder — actual implementation would use expo-av or react-native-video
|
|
314
|
+
return (<react_native_1.View style={[
|
|
315
|
+
style,
|
|
316
|
+
{
|
|
317
|
+
backgroundColor: style.backgroundColor || '#1a1a1a',
|
|
318
|
+
alignItems: 'center',
|
|
319
|
+
justifyContent: 'center',
|
|
320
|
+
},
|
|
321
|
+
]}>
|
|
322
|
+
<react_native_1.Text style={{ fontSize: 48 }}>🎬</react_native_1.Text>
|
|
323
|
+
{((_y = element.props) === null || _y === void 0 ? void 0 : _y.videoDescription) && (<react_native_1.Text style={{ fontSize: 11, color: '#aaa', textAlign: 'center', padding: 8 }}>
|
|
324
|
+
{element.props.videoDescription}
|
|
325
|
+
</react_native_1.Text>)}
|
|
326
|
+
</react_native_1.View>);
|
|
327
|
+
case 'lottie':
|
|
328
|
+
// Lottie placeholder — actual implementation would use lottie-react-native
|
|
329
|
+
return (<react_native_1.View style={[
|
|
330
|
+
style,
|
|
331
|
+
{
|
|
332
|
+
backgroundColor: style.backgroundColor || '#f8f8ff',
|
|
333
|
+
alignItems: 'center',
|
|
334
|
+
justifyContent: 'center',
|
|
335
|
+
},
|
|
336
|
+
]}>
|
|
337
|
+
<react_native_1.Text style={{ fontSize: 48 }}>✨</react_native_1.Text>
|
|
338
|
+
{((_z = element.props) === null || _z === void 0 ? void 0 : _z.animationDescription) && (<react_native_1.Text style={{ fontSize: 11, color: '#666', textAlign: 'center', padding: 8 }}>
|
|
339
|
+
{element.props.animationDescription}
|
|
340
|
+
</react_native_1.Text>)}
|
|
341
|
+
</react_native_1.View>);
|
|
342
|
+
case 'input':
|
|
343
|
+
return (<react_native_1.TextInput style={[style, { borderWidth: 1, borderColor: '#E5E5E5' }]} placeholder={((_0 = element.props) === null || _0 === void 0 ? void 0 : _0.placeholder) || 'Enter text...'} keyboardType={getKeyboardType((_1 = element.props) === null || _1 === void 0 ? void 0 : _1.type)} secureTextEntry={((_2 = element.props) === null || _2 === void 0 ? void 0 : _2.type) === 'password'} autoCapitalize={((_3 = element.props) === null || _3 === void 0 ? void 0 : _3.type) === 'email' ? 'none' : 'sentences'}/>);
|
|
344
|
+
case 'spacer':
|
|
345
|
+
return <react_native_1.View style={style || { flex: 1 }}/>;
|
|
346
|
+
case 'divider':
|
|
347
|
+
return (<react_native_1.View style={[
|
|
348
|
+
{
|
|
349
|
+
height: 1,
|
|
350
|
+
backgroundColor: '#e0e0e0',
|
|
351
|
+
width: '100%',
|
|
352
|
+
},
|
|
353
|
+
style,
|
|
354
|
+
]}/>);
|
|
355
|
+
default:
|
|
356
|
+
return null;
|
|
357
|
+
}
|
|
358
|
+
};
|
|
359
|
+
// ─── Style Converter ───
|
|
360
|
+
function convertStyle(style) {
|
|
361
|
+
var _a;
|
|
362
|
+
const rnStyle = {};
|
|
363
|
+
if (!style)
|
|
364
|
+
return rnStyle;
|
|
365
|
+
// Layout
|
|
366
|
+
if (style.flex !== undefined)
|
|
367
|
+
rnStyle.flex = style.flex;
|
|
368
|
+
if (style.justifyContent)
|
|
369
|
+
rnStyle.justifyContent = style.justifyContent;
|
|
370
|
+
if (style.alignItems)
|
|
371
|
+
rnStyle.alignItems = style.alignItems;
|
|
372
|
+
if (style.alignSelf)
|
|
373
|
+
rnStyle.alignSelf = style.alignSelf;
|
|
374
|
+
if (style.gap !== undefined)
|
|
375
|
+
rnStyle.gap = style.gap;
|
|
376
|
+
if (style.flexWrap)
|
|
377
|
+
rnStyle.flexWrap = style.flexWrap;
|
|
378
|
+
if (style.overflow)
|
|
379
|
+
rnStyle.overflow = style.overflow;
|
|
380
|
+
// Spacing
|
|
381
|
+
if (style.padding !== undefined)
|
|
382
|
+
rnStyle.padding = style.padding;
|
|
383
|
+
if (style.paddingTop !== undefined)
|
|
384
|
+
rnStyle.paddingTop = style.paddingTop;
|
|
385
|
+
if (style.paddingBottom !== undefined)
|
|
386
|
+
rnStyle.paddingBottom = style.paddingBottom;
|
|
387
|
+
if (style.paddingLeft !== undefined)
|
|
388
|
+
rnStyle.paddingLeft = style.paddingLeft;
|
|
389
|
+
if (style.paddingRight !== undefined)
|
|
390
|
+
rnStyle.paddingRight = style.paddingRight;
|
|
391
|
+
if (style.marginTop !== undefined)
|
|
392
|
+
rnStyle.marginTop = style.marginTop;
|
|
393
|
+
if (style.marginBottom !== undefined)
|
|
394
|
+
rnStyle.marginBottom = style.marginBottom;
|
|
395
|
+
if (style.marginLeft !== undefined)
|
|
396
|
+
rnStyle.marginLeft = style.marginLeft;
|
|
397
|
+
if (style.marginRight !== undefined)
|
|
398
|
+
rnStyle.marginRight = style.marginRight;
|
|
399
|
+
// Size
|
|
400
|
+
if (style.width !== undefined)
|
|
401
|
+
rnStyle.width = style.width;
|
|
402
|
+
if (style.height !== undefined)
|
|
403
|
+
rnStyle.height = style.height;
|
|
404
|
+
if (style.maxWidth !== undefined)
|
|
405
|
+
rnStyle.maxWidth = style.maxWidth;
|
|
406
|
+
if (style.minHeight !== undefined)
|
|
407
|
+
rnStyle.minHeight = style.minHeight;
|
|
408
|
+
// Visual
|
|
409
|
+
if (style.backgroundColor)
|
|
410
|
+
rnStyle.backgroundColor = style.backgroundColor;
|
|
411
|
+
if (style.opacity !== undefined)
|
|
412
|
+
rnStyle.opacity = style.opacity;
|
|
413
|
+
if (style.borderRadius !== undefined)
|
|
414
|
+
rnStyle.borderRadius = style.borderRadius;
|
|
415
|
+
if (style.borderWidth !== undefined)
|
|
416
|
+
rnStyle.borderWidth = style.borderWidth;
|
|
417
|
+
if (style.borderColor)
|
|
418
|
+
rnStyle.borderColor = style.borderColor;
|
|
419
|
+
if (style.borderBottomWidth !== undefined)
|
|
420
|
+
rnStyle.borderBottomWidth = style.borderBottomWidth;
|
|
421
|
+
if (style.borderBottomColor)
|
|
422
|
+
rnStyle.borderBottomColor = style.borderBottomColor;
|
|
423
|
+
// Shadow (React Native uses different shadow props)
|
|
424
|
+
if (style.shadowColor) {
|
|
425
|
+
rnStyle.shadowColor = style.shadowColor;
|
|
426
|
+
rnStyle.shadowOpacity = style.shadowOpacity || 0.2;
|
|
427
|
+
rnStyle.shadowRadius = style.shadowRadius || 4;
|
|
428
|
+
rnStyle.shadowOffset = {
|
|
429
|
+
width: style.shadowOffsetX || 0,
|
|
430
|
+
height: style.shadowOffsetY || 2,
|
|
431
|
+
};
|
|
432
|
+
// Android elevation approximation
|
|
433
|
+
rnStyle.elevation = style.shadowRadius || 4;
|
|
434
|
+
}
|
|
435
|
+
// Text
|
|
436
|
+
if (style.color)
|
|
437
|
+
rnStyle.color = style.color;
|
|
438
|
+
if (style.fontSize !== undefined)
|
|
439
|
+
rnStyle.fontSize = style.fontSize;
|
|
440
|
+
if (style.fontWeight)
|
|
441
|
+
rnStyle.fontWeight = style.fontWeight;
|
|
442
|
+
if (style.textAlign)
|
|
443
|
+
rnStyle.textAlign = style.textAlign;
|
|
444
|
+
if (style.lineHeight !== undefined) {
|
|
445
|
+
// React Native lineHeight is in pixels, not a multiplier
|
|
446
|
+
// If value is small (< 4), treat as multiplier and convert
|
|
447
|
+
rnStyle.lineHeight =
|
|
448
|
+
style.lineHeight > 4 ? style.lineHeight : (style.fontSize || 16) * style.lineHeight;
|
|
449
|
+
}
|
|
450
|
+
if (style.letterSpacing !== undefined)
|
|
451
|
+
rnStyle.letterSpacing = style.letterSpacing;
|
|
452
|
+
if (style.textTransform)
|
|
453
|
+
rnStyle.textTransform = style.textTransform;
|
|
454
|
+
if (style.textDecorationLine)
|
|
455
|
+
rnStyle.textDecorationLine = style.textDecorationLine;
|
|
456
|
+
// backgroundGradient is handled by wrapWithGradient at the component level.
|
|
457
|
+
// If LinearGradient is not available, fall back to the first gradient color.
|
|
458
|
+
if (style.backgroundGradient && !LinearGradient && ((_a = style.backgroundGradient.colors) === null || _a === void 0 ? void 0 : _a.length)) {
|
|
459
|
+
const firstColor = style.backgroundGradient.colors[0];
|
|
460
|
+
rnStyle.backgroundColor = typeof firstColor === 'string' ? firstColor : firstColor.color;
|
|
461
|
+
}
|
|
462
|
+
return rnStyle;
|
|
463
|
+
}
|
|
464
|
+
// ─── Gradient Wrapper ───
|
|
465
|
+
function angleToCoords(angle) {
|
|
466
|
+
// Convert CSS angle (0 = top, 90 = right) to LinearGradient coordinates
|
|
467
|
+
const rad = ((angle - 90) * Math.PI) / 180;
|
|
468
|
+
return {
|
|
469
|
+
start: { x: 0.5 - Math.cos(rad) * 0.5, y: 0.5 - Math.sin(rad) * 0.5 },
|
|
470
|
+
end: { x: 0.5 + Math.cos(rad) * 0.5, y: 0.5 + Math.sin(rad) * 0.5 },
|
|
471
|
+
};
|
|
472
|
+
}
|
|
473
|
+
function wrapWithGradient(content, elementStyle, viewStyle) {
|
|
474
|
+
var _a, _b;
|
|
475
|
+
const gradient = elementStyle === null || elementStyle === void 0 ? void 0 : elementStyle.backgroundGradient;
|
|
476
|
+
if (!gradient || !LinearGradient || !((_a = gradient.colors) === null || _a === void 0 ? void 0 : _a.length))
|
|
477
|
+
return content;
|
|
478
|
+
// Handle both { color, position } objects and plain color strings
|
|
479
|
+
const colors = gradient.colors.map((c) => typeof c === 'string' ? c : c.color);
|
|
480
|
+
const locations = gradient.colors.map((c, i, arr) => {
|
|
481
|
+
var _a;
|
|
482
|
+
if (typeof c === 'string')
|
|
483
|
+
return i / Math.max(arr.length - 1, 1);
|
|
484
|
+
return ((_a = c.position) !== null && _a !== void 0 ? _a : Math.round((i / Math.max(arr.length - 1, 1)) * 100)) / 100;
|
|
485
|
+
});
|
|
486
|
+
// Pull layout-affecting styles onto the gradient wrapper, keep inner styles on the content
|
|
487
|
+
const { backgroundColor } = viewStyle, innerStyle = __rest(viewStyle, ["backgroundColor"]);
|
|
488
|
+
const gradientStyle = Object.assign({}, innerStyle);
|
|
489
|
+
const gradientType = gradient.type || 'linear';
|
|
490
|
+
if (gradientType === 'radial') {
|
|
491
|
+
// No radial support in LinearGradient — use first color as fallback
|
|
492
|
+
return react_1.default.cloneElement(content, {
|
|
493
|
+
style: [viewStyle, { backgroundColor: colors[0] }],
|
|
494
|
+
});
|
|
495
|
+
}
|
|
496
|
+
// Support both angle and start/end array formats
|
|
497
|
+
let coords;
|
|
498
|
+
if (gradient.start && gradient.end) {
|
|
499
|
+
const s = Array.isArray(gradient.start) ? { x: gradient.start[0], y: gradient.start[1] } : gradient.start;
|
|
500
|
+
const e = Array.isArray(gradient.end) ? { x: gradient.end[0], y: gradient.end[1] } : gradient.end;
|
|
501
|
+
coords = { start: s, end: e };
|
|
502
|
+
}
|
|
503
|
+
else {
|
|
504
|
+
coords = angleToCoords((_b = gradient.angle) !== null && _b !== void 0 ? _b : 180);
|
|
505
|
+
}
|
|
506
|
+
return (<LinearGradient colors={colors} locations={locations} start={coords.start} end={coords.end} style={gradientStyle}>
|
|
507
|
+
{content.props.children}
|
|
508
|
+
</LinearGradient>);
|
|
509
|
+
}
|
|
510
|
+
// ─── Helpers ───
|
|
511
|
+
function getKeyboardType(type) {
|
|
512
|
+
switch (type) {
|
|
513
|
+
case 'email':
|
|
514
|
+
return 'email-address';
|
|
515
|
+
case 'tel':
|
|
516
|
+
case 'number':
|
|
517
|
+
return 'numeric';
|
|
518
|
+
default:
|
|
519
|
+
return 'default';
|
|
520
|
+
}
|
|
521
|
+
}
|
package/lib/index.d.ts
ADDED
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
export { OnboardingFlow } from './OnboardingFlow';
|
|
2
|
+
export type { OnboardingFlowProps, ScreenConfig, OnboardingConfig, AnalyticsEvent, BaseComponentProps, CustomScreenProps, ElementNode, ElementType, ElementAction, ElementStyle, ElementPosition, Condition, ComparisonOperator, ConditionalDestination, ConditionalRoutes, ElementConditions, } from './types';
|
|
3
|
+
export { ElementRenderer } from './components/ElementRenderer';
|
|
4
|
+
export { API } from './api';
|
|
5
|
+
export { AnalyticsManager } from './analytics';
|
|
6
|
+
export { resolveTemplate, evaluateCondition, resolveDestination } from './variableUtils';
|
package/lib/index.js
ADDED
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.resolveDestination = exports.evaluateCondition = exports.resolveTemplate = exports.AnalyticsManager = exports.API = exports.ElementRenderer = exports.OnboardingFlow = void 0;
|
|
4
|
+
// Main component
|
|
5
|
+
var OnboardingFlow_1 = require("./OnboardingFlow");
|
|
6
|
+
Object.defineProperty(exports, "OnboardingFlow", { enumerable: true, get: function () { return OnboardingFlow_1.OnboardingFlow; } });
|
|
7
|
+
// Components
|
|
8
|
+
var ElementRenderer_1 = require("./components/ElementRenderer");
|
|
9
|
+
Object.defineProperty(exports, "ElementRenderer", { enumerable: true, get: function () { return ElementRenderer_1.ElementRenderer; } });
|
|
10
|
+
// Utilities (if developers want to use them)
|
|
11
|
+
var api_1 = require("./api");
|
|
12
|
+
Object.defineProperty(exports, "API", { enumerable: true, get: function () { return api_1.API; } });
|
|
13
|
+
var analytics_1 = require("./analytics");
|
|
14
|
+
Object.defineProperty(exports, "AnalyticsManager", { enumerable: true, get: function () { return analytics_1.AnalyticsManager; } });
|
|
15
|
+
var variableUtils_1 = require("./variableUtils");
|
|
16
|
+
Object.defineProperty(exports, "resolveTemplate", { enumerable: true, get: function () { return variableUtils_1.resolveTemplate; } });
|
|
17
|
+
Object.defineProperty(exports, "evaluateCondition", { enumerable: true, get: function () { return variableUtils_1.evaluateCondition; } });
|
|
18
|
+
Object.defineProperty(exports, "resolveDestination", { enumerable: true, get: function () { return variableUtils_1.resolveDestination; } });
|