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.
@@ -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; } });