flowboard-react 0.1.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 (90) hide show
  1. package/FlowboardReact.podspec +20 -0
  2. package/LICENSE +20 -0
  3. package/README.md +122 -0
  4. package/android/build.gradle +67 -0
  5. package/android/src/main/AndroidManifest.xml +2 -0
  6. package/android/src/main/java/com/flowboardreact/FlowboardReactModule.kt +15 -0
  7. package/android/src/main/java/com/flowboardreact/FlowboardReactPackage.kt +33 -0
  8. package/ios/FlowboardReact.h +5 -0
  9. package/ios/FlowboardReact.mm +21 -0
  10. package/lib/module/Flowboard.js +167 -0
  11. package/lib/module/Flowboard.js.map +1 -0
  12. package/lib/module/FlowboardProvider.js +52 -0
  13. package/lib/module/FlowboardProvider.js.map +1 -0
  14. package/lib/module/NativeFlowboardReact.js +5 -0
  15. package/lib/module/NativeFlowboardReact.js.map +1 -0
  16. package/lib/module/components/FlowboardFlow.js +389 -0
  17. package/lib/module/components/FlowboardFlow.js.map +1 -0
  18. package/lib/module/components/FlowboardRenderer.js +1684 -0
  19. package/lib/module/components/FlowboardRenderer.js.map +1 -0
  20. package/lib/module/components/widgets/sliderRegistry.js +48 -0
  21. package/lib/module/components/widgets/sliderRegistry.js.map +1 -0
  22. package/lib/module/core/analyticsManager.js +110 -0
  23. package/lib/module/core/analyticsManager.js.map +1 -0
  24. package/lib/module/core/assetPreloader.js +72 -0
  25. package/lib/module/core/assetPreloader.js.map +1 -0
  26. package/lib/module/core/clientContext.js +105 -0
  27. package/lib/module/core/clientContext.js.map +1 -0
  28. package/lib/module/core/fontAwesome.js +110 -0
  29. package/lib/module/core/fontAwesome.js.map +1 -0
  30. package/lib/module/core/onboardingRepository.js +62 -0
  31. package/lib/module/core/onboardingRepository.js.map +1 -0
  32. package/lib/module/core/resolverService.js +58 -0
  33. package/lib/module/core/resolverService.js.map +1 -0
  34. package/lib/module/index.js +5 -0
  35. package/lib/module/index.js.map +1 -0
  36. package/lib/module/package.json +1 -0
  37. package/lib/module/types/flowboard.js +4 -0
  38. package/lib/module/types/flowboard.js.map +1 -0
  39. package/lib/module/types/react-native-vector-icons.d.js +2 -0
  40. package/lib/module/types/react-native-vector-icons.d.js.map +1 -0
  41. package/lib/module/utils/flowboardUtils.js +379 -0
  42. package/lib/module/utils/flowboardUtils.js.map +1 -0
  43. package/lib/typescript/package.json +1 -0
  44. package/lib/typescript/src/Flowboard.d.ts +33 -0
  45. package/lib/typescript/src/Flowboard.d.ts.map +1 -0
  46. package/lib/typescript/src/FlowboardProvider.d.ts +5 -0
  47. package/lib/typescript/src/FlowboardProvider.d.ts.map +1 -0
  48. package/lib/typescript/src/NativeFlowboardReact.d.ts +7 -0
  49. package/lib/typescript/src/NativeFlowboardReact.d.ts.map +1 -0
  50. package/lib/typescript/src/components/FlowboardFlow.d.ts +14 -0
  51. package/lib/typescript/src/components/FlowboardFlow.d.ts.map +1 -0
  52. package/lib/typescript/src/components/FlowboardRenderer.d.ts +31 -0
  53. package/lib/typescript/src/components/FlowboardRenderer.d.ts.map +1 -0
  54. package/lib/typescript/src/components/widgets/sliderRegistry.d.ts +16 -0
  55. package/lib/typescript/src/components/widgets/sliderRegistry.d.ts.map +1 -0
  56. package/lib/typescript/src/core/analyticsManager.d.ts +42 -0
  57. package/lib/typescript/src/core/analyticsManager.d.ts.map +1 -0
  58. package/lib/typescript/src/core/assetPreloader.d.ts +8 -0
  59. package/lib/typescript/src/core/assetPreloader.d.ts.map +1 -0
  60. package/lib/typescript/src/core/clientContext.d.ts +27 -0
  61. package/lib/typescript/src/core/clientContext.d.ts.map +1 -0
  62. package/lib/typescript/src/core/fontAwesome.d.ts +8 -0
  63. package/lib/typescript/src/core/fontAwesome.d.ts.map +1 -0
  64. package/lib/typescript/src/core/onboardingRepository.d.ts +15 -0
  65. package/lib/typescript/src/core/onboardingRepository.d.ts.map +1 -0
  66. package/lib/typescript/src/core/resolverService.d.ts +11 -0
  67. package/lib/typescript/src/core/resolverService.d.ts.map +1 -0
  68. package/lib/typescript/src/index.d.ts +4 -0
  69. package/lib/typescript/src/index.d.ts.map +1 -0
  70. package/lib/typescript/src/types/flowboard.d.ts +34 -0
  71. package/lib/typescript/src/types/flowboard.d.ts.map +1 -0
  72. package/lib/typescript/src/utils/flowboardUtils.d.ts +31 -0
  73. package/lib/typescript/src/utils/flowboardUtils.d.ts.map +1 -0
  74. package/package.json +192 -0
  75. package/src/Flowboard.ts +223 -0
  76. package/src/FlowboardProvider.tsx +60 -0
  77. package/src/NativeFlowboardReact.ts +7 -0
  78. package/src/components/FlowboardFlow.tsx +513 -0
  79. package/src/components/FlowboardRenderer.tsx +1957 -0
  80. package/src/components/widgets/sliderRegistry.tsx +56 -0
  81. package/src/core/analyticsManager.ts +125 -0
  82. package/src/core/assetPreloader.ts +103 -0
  83. package/src/core/clientContext.ts +132 -0
  84. package/src/core/fontAwesome.ts +90 -0
  85. package/src/core/onboardingRepository.ts +79 -0
  86. package/src/core/resolverService.ts +69 -0
  87. package/src/index.tsx +11 -0
  88. package/src/types/flowboard.ts +50 -0
  89. package/src/types/react-native-vector-icons.d.ts +15 -0
  90. package/src/utils/flowboardUtils.ts +400 -0
@@ -0,0 +1,1684 @@
1
+ "use strict";
2
+
3
+ import React, { useMemo } from 'react';
4
+ import { Animated, Pressable, ScrollView, StyleSheet, Text, TextInput, View, Image } from 'react-native';
5
+ import { SafeAreaView } from 'react-native-safe-area-context';
6
+ import MaskedView from '@react-native-masked-view/masked-view';
7
+ import LinearGradient from 'react-native-linear-gradient';
8
+ import LottieView from 'lottie-react-native';
9
+ import MaskInput from 'react-native-mask-input';
10
+ import Svg, { Circle, G, Line, Polygon, Text as SvgText } from 'react-native-svg';
11
+ import PagerView from 'react-native-pager-view';
12
+ import { insetsToStyle, parseAlignment, parseColor, parseCrossAlignment, parseDimension, parseFlexAlignment, parseFontWeight, parseInsets, parseResizeMode, parseTextAlign, parseTextDecoration, resolveNumericValue, resolveText } from "../utils/flowboardUtils.js";
13
+ import { resolveFontAwesomeIcon, FontAwesome6 } from "../core/fontAwesome.js";
14
+ import { useSliderRegistry } from "./widgets/sliderRegistry.js";
15
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
16
+ const styles = StyleSheet.create({
17
+ root: {
18
+ flex: 1
19
+ },
20
+ background: {
21
+ ...StyleSheet.absoluteFillObject
22
+ },
23
+ safeArea: {
24
+ flex: 1
25
+ },
26
+ progressWrapper: {
27
+ width: '100%'
28
+ }
29
+ });
30
+ export default function FlowboardRenderer(props) {
31
+ const {
32
+ screenData,
33
+ onAction,
34
+ formData = {},
35
+ onInputChange,
36
+ enableFontAwesomeIcons = true,
37
+ currentIndex = 0,
38
+ totalScreens = 1
39
+ } = props;
40
+ const childrenData = Array.isArray(screenData.children) ? screenData.children : [];
41
+ const backgroundData = screenData.background;
42
+ const bgColorCode = screenData.backgroundColor;
43
+ const showProgress = screenData.showProgress === true;
44
+ const progressColor = parseColor(screenData.progressColor ?? '0xFF000000');
45
+ const progressThickness = Number(screenData.progressThickness ?? 4);
46
+ const progressRadius = Number(screenData.progressRadius ?? 0);
47
+ const progressStyle = screenData.progressStyle ?? 'linear';
48
+ const scrollable = screenData.scrollable === true;
49
+ const safeArea = screenData.safeArea !== false;
50
+ const padding = parseInsets(screenData.padding);
51
+ const paddingStyle = insetsToStyle(padding);
52
+ const content = /*#__PURE__*/_jsxs(View, {
53
+ style: {
54
+ flex: 1
55
+ },
56
+ children: [showProgress && /*#__PURE__*/_jsx(View, {
57
+ style: [paddingStyle, {
58
+ paddingBottom: 8
59
+ }],
60
+ children: renderProgressBar(currentIndex, totalScreens, progressColor, progressThickness, progressRadius, progressStyle)
61
+ }), scrollable ? /*#__PURE__*/_jsx(ScrollView, {
62
+ contentContainerStyle: {
63
+ ...paddingStyle,
64
+ paddingTop: showProgress ? 0 : padding.top
65
+ },
66
+ children: /*#__PURE__*/_jsx(View, {
67
+ style: {
68
+ flexGrow: 1,
69
+ justifyContent: parseFlexAlignment(screenData.mainAxisAlignment),
70
+ alignItems: parseCrossAlignment(screenData.crossAxisAlignment)
71
+ },
72
+ children: childrenData.map((child, index) => buildWidget(child, {
73
+ onAction,
74
+ formData,
75
+ onInputChange,
76
+ allowFlexExpansion: true,
77
+ screenData,
78
+ enableFontAwesomeIcons,
79
+ key: `child-${index}`
80
+ }))
81
+ })
82
+ }) : /*#__PURE__*/_jsx(View, {
83
+ style: {
84
+ flex: 1,
85
+ ...paddingStyle,
86
+ paddingTop: showProgress ? 0 : padding.top,
87
+ justifyContent: parseFlexAlignment(screenData.mainAxisAlignment),
88
+ alignItems: parseCrossAlignment(screenData.crossAxisAlignment)
89
+ },
90
+ children: childrenData.map((child, index) => buildWidget(child, {
91
+ onAction,
92
+ formData,
93
+ onInputChange,
94
+ allowFlexExpansion: true,
95
+ screenData,
96
+ enableFontAwesomeIcons,
97
+ key: `child-${index}`
98
+ }))
99
+ })]
100
+ });
101
+ return /*#__PURE__*/_jsxs(View, {
102
+ style: styles.root,
103
+ children: [renderBackground(backgroundData, bgColorCode), safeArea ? /*#__PURE__*/_jsx(SafeAreaView, {
104
+ style: styles.safeArea,
105
+ children: content
106
+ }) : content]
107
+ });
108
+ }
109
+ function renderBackground(bgData, legacyColorCode) {
110
+ if (!bgData) {
111
+ return /*#__PURE__*/_jsx(View, {
112
+ style: [styles.background, {
113
+ backgroundColor: parseColor(legacyColorCode)
114
+ }]
115
+ });
116
+ }
117
+ if (typeof bgData === 'object') {
118
+ const type = bgData.type;
119
+ const fit = parseResizeMode(bgData.fit ?? 'cover');
120
+ switch (type) {
121
+ case 'gradient':
122
+ {
123
+ const colors = Array.isArray(bgData.colors) ? bgData.colors.map(c => parseColor(c)) : [];
124
+ if (colors.length === 0) return null;
125
+ const start = alignmentToGradient(bgData.begin ?? 'topLeft');
126
+ const end = alignmentToGradient(bgData.end ?? 'bottomRight');
127
+ const stops = Array.isArray(bgData.stops) ? bgData.stops.map(s => Number(s)) : undefined;
128
+ return /*#__PURE__*/_jsx(LinearGradient, {
129
+ colors: colors,
130
+ start: start,
131
+ end: end,
132
+ locations: stops,
133
+ style: styles.background
134
+ });
135
+ }
136
+ case 'image':
137
+ {
138
+ const url = bgData.url;
139
+ if (!url) return null;
140
+ return /*#__PURE__*/_jsx(Image, {
141
+ source: {
142
+ uri: url
143
+ },
144
+ style: styles.background,
145
+ resizeMode: fit
146
+ });
147
+ }
148
+ case 'asset':
149
+ {
150
+ const path = bgData.path;
151
+ if (!path) return null;
152
+ return /*#__PURE__*/_jsx(Image, {
153
+ source: {
154
+ uri: path
155
+ },
156
+ style: styles.background,
157
+ resizeMode: fit
158
+ });
159
+ }
160
+ case 'color':
161
+ {
162
+ return /*#__PURE__*/_jsx(View, {
163
+ style: [styles.background, {
164
+ backgroundColor: parseColor(bgData.color)
165
+ }]
166
+ });
167
+ }
168
+ default:
169
+ break;
170
+ }
171
+ }
172
+ return /*#__PURE__*/_jsx(View, {
173
+ style: [styles.background, {
174
+ backgroundColor: '#ffffff'
175
+ }]
176
+ });
177
+ }
178
+ function renderProgressBar(currentIndex, totalScreens, color, thickness, radius, style) {
179
+ if (style === 'dotted') {
180
+ return /*#__PURE__*/_jsx(View, {
181
+ style: {
182
+ flexDirection: 'row'
183
+ },
184
+ children: Array.from({
185
+ length: totalScreens
186
+ }).map((_, index) => {
187
+ const isActive = index <= currentIndex;
188
+ return /*#__PURE__*/_jsx(View, {
189
+ style: {
190
+ flex: 1,
191
+ height: thickness,
192
+ marginRight: index < totalScreens - 1 ? 4 : 0,
193
+ backgroundColor: isActive ? color : withOpacity(color, 0.1),
194
+ borderRadius: radius
195
+ }
196
+ }, `dot-${index}`);
197
+ })
198
+ });
199
+ }
200
+ const progress = totalScreens > 0 ? (currentIndex + 1) / totalScreens : 0;
201
+ return /*#__PURE__*/_jsx(View, {
202
+ style: {
203
+ width: '100%',
204
+ height: thickness,
205
+ borderRadius: radius,
206
+ backgroundColor: withOpacity(color, 0.1),
207
+ overflow: 'hidden'
208
+ },
209
+ children: /*#__PURE__*/_jsx(View, {
210
+ style: {
211
+ width: `${progress * 100}%`,
212
+ height: '100%',
213
+ backgroundColor: color,
214
+ borderRadius: radius
215
+ }
216
+ })
217
+ });
218
+ }
219
+ function buildWidget(json, params) {
220
+ if (!json || typeof json !== 'object') return null;
221
+ const type = json.type;
222
+ const id = json.id;
223
+ const props = {
224
+ ...(json.properties ?? {})
225
+ };
226
+ const childrenJson = Array.isArray(json.children) ? json.children : undefined;
227
+ const childJson = json.child;
228
+ const action = json.action;
229
+ const actionPayload = action ? buildActionPayload(props, json) : undefined;
230
+ let node = null;
231
+ switch (type) {
232
+ case 'column':
233
+ {
234
+ const spacing = Number(props.spacing ?? props.gap ?? 0);
235
+ const mainAxisSize = props.mainAxisSize === 'min' ? 'min' : 'max';
236
+ const childWidgets = (childrenJson ?? []).map((child, index) => /*#__PURE__*/_jsxs(React.Fragment, {
237
+ children: [buildWidget(child, {
238
+ ...params,
239
+ allowFlexExpansion: true
240
+ }), spacing > 0 && index < (childrenJson?.length ?? 0) - 1 ? /*#__PURE__*/_jsx(View, {
241
+ style: {
242
+ height: spacing
243
+ }
244
+ }) : null]
245
+ }, `column-${index}`));
246
+ node = /*#__PURE__*/_jsx(View, {
247
+ style: {
248
+ flexDirection: 'column',
249
+ justifyContent: parseFlexAlignment(props.mainAxisAlignment),
250
+ alignItems: parseCrossAlignment(props.crossAxisAlignment),
251
+ flexGrow: mainAxisSize === 'max' ? 1 : 0
252
+ },
253
+ children: childWidgets
254
+ });
255
+ break;
256
+ }
257
+ case 'row':
258
+ {
259
+ const spacing = Number(props.spacing ?? props.gap ?? 0);
260
+ const mainAxisSize = props.mainAxisSize === 'min' ? 'min' : 'max';
261
+ const childWidgets = (childrenJson ?? []).map((child, index) => /*#__PURE__*/_jsxs(React.Fragment, {
262
+ children: [buildWidget(child, {
263
+ ...params,
264
+ allowFlexExpansion: true
265
+ }), spacing > 0 && index < (childrenJson?.length ?? 0) - 1 ? /*#__PURE__*/_jsx(View, {
266
+ style: {
267
+ width: spacing
268
+ }
269
+ }) : null]
270
+ }, `row-${index}`));
271
+ node = /*#__PURE__*/_jsx(View, {
272
+ style: {
273
+ flexDirection: 'row',
274
+ justifyContent: parseFlexAlignment(props.mainAxisAlignment),
275
+ alignItems: parseCrossAlignment(props.crossAxisAlignment),
276
+ flexGrow: mainAxisSize === 'max' ? 1 : 0
277
+ },
278
+ children: childWidgets
279
+ });
280
+ break;
281
+ }
282
+ case 'container':
283
+ {
284
+ const gradient = parseGradient(props.background);
285
+ const width = normalizeDimension(parseDimension(props.width));
286
+ const height = normalizeDimension(parseDimension(props.height));
287
+ const borderRadius = props.borderRadius ? Number(props.borderRadius) : undefined;
288
+ const borderColor = props.borderColor ? parseColor(props.borderColor) : undefined;
289
+ const borderWidth = borderColor ? 1 : 0;
290
+ const padding = parseInsets(props.padding);
291
+ const content = childJson ? buildWidget(childJson, {
292
+ ...params,
293
+ allowFlexExpansion: false
294
+ }) : null;
295
+ if (gradient) {
296
+ node = /*#__PURE__*/_jsx(LinearGradient, {
297
+ colors: gradient.colors,
298
+ start: gradient.start,
299
+ end: gradient.end,
300
+ locations: gradient.stops,
301
+ style: {
302
+ width,
303
+ height,
304
+ borderRadius,
305
+ borderColor,
306
+ borderWidth,
307
+ overflow: 'hidden',
308
+ ...insetsToStyle(padding)
309
+ },
310
+ children: content
311
+ });
312
+ } else {
313
+ node = /*#__PURE__*/_jsx(View, {
314
+ style: {
315
+ width,
316
+ height,
317
+ backgroundColor: parseColor(props.backgroundColor),
318
+ borderRadius,
319
+ borderColor,
320
+ borderWidth,
321
+ ...insetsToStyle(padding)
322
+ },
323
+ children: content
324
+ });
325
+ }
326
+ break;
327
+ }
328
+ case 'text':
329
+ {
330
+ const rawText = props.text ?? '';
331
+ const resolvedText = resolveText(String(rawText), params.formData);
332
+ const gradient = parseGradient(props.foreground);
333
+ const textStyle = getTextStyle(props, 'color');
334
+ const align = parseTextAlign(props.textAlign);
335
+ if (gradient) {
336
+ node = /*#__PURE__*/_jsx(GradientText, {
337
+ text: resolvedText,
338
+ gradient: gradient,
339
+ style: [textStyle, {
340
+ textAlign: align
341
+ }]
342
+ });
343
+ } else {
344
+ node = /*#__PURE__*/_jsx(Text, {
345
+ style: [textStyle, {
346
+ textAlign: align
347
+ }],
348
+ children: resolvedText
349
+ });
350
+ }
351
+ break;
352
+ }
353
+ case 'rich_text':
354
+ {
355
+ const spansList = Array.isArray(props.spans) ? props.spans : [];
356
+ const richTextGradient = parseGradient(props.foreground);
357
+ const align = parseTextAlign(props.textAlign);
358
+ if (richTextGradient) {
359
+ const combined = spansList.map(span => resolveText(String(span.text ?? ''), params.formData)).join('');
360
+ node = /*#__PURE__*/_jsx(GradientText, {
361
+ text: combined,
362
+ gradient: richTextGradient,
363
+ style: {
364
+ textAlign: align
365
+ }
366
+ });
367
+ } else {
368
+ node = /*#__PURE__*/_jsx(View, {
369
+ style: {
370
+ flexDirection: 'row',
371
+ flexWrap: 'wrap'
372
+ },
373
+ children: spansList.map((span, index) => {
374
+ const spanText = resolveText(String(span.text ?? ''), params.formData);
375
+ const spanStyle = getTextStyle(span, 'color');
376
+ const spanGradient = parseGradient(span.foreground);
377
+ if (spanGradient) {
378
+ return /*#__PURE__*/_jsx(GradientText, {
379
+ text: spanText,
380
+ gradient: spanGradient,
381
+ style: [spanStyle, {
382
+ textAlign: align
383
+ }]
384
+ }, `span-${index}`);
385
+ }
386
+ return /*#__PURE__*/_jsx(Text, {
387
+ style: [spanStyle, {
388
+ textAlign: align
389
+ }],
390
+ children: spanText
391
+ }, `span-${index}`);
392
+ })
393
+ });
394
+ }
395
+ break;
396
+ }
397
+ case 'button':
398
+ {
399
+ const gradient = parseGradient(props.background);
400
+ const borderRadius = Number(props.borderRadius ?? 10);
401
+ const width = normalizeDimension(parseDimension(props.width)) ?? '100%';
402
+ const height = normalizeDimension(parseDimension(props.height)) ?? 50;
403
+ const label = props.label ?? 'Button';
404
+ const textStyle = getTextStyle({
405
+ ...props,
406
+ height: null
407
+ }, 'textColor', '0xFFFFFFFF');
408
+ const content = /*#__PURE__*/_jsx(View, {
409
+ style: {
410
+ flex: 1,
411
+ justifyContent: 'center',
412
+ alignItems: 'center'
413
+ },
414
+ children: /*#__PURE__*/_jsx(Text, {
415
+ style: textStyle,
416
+ children: label
417
+ })
418
+ });
419
+ if (gradient) {
420
+ node = /*#__PURE__*/_jsx(Pressable, {
421
+ onPress: action ? () => params.onAction(action, actionPayload ?? props) : undefined,
422
+ style: {
423
+ width,
424
+ height
425
+ },
426
+ children: /*#__PURE__*/_jsx(LinearGradient, {
427
+ colors: gradient.colors,
428
+ start: gradient.start,
429
+ end: gradient.end,
430
+ locations: gradient.stops,
431
+ style: {
432
+ flex: 1,
433
+ borderRadius,
434
+ overflow: 'hidden'
435
+ },
436
+ children: content
437
+ })
438
+ });
439
+ } else {
440
+ node = /*#__PURE__*/_jsx(Pressable, {
441
+ onPress: action ? () => params.onAction(action, actionPayload ?? props) : undefined,
442
+ style: {
443
+ width,
444
+ height,
445
+ backgroundColor: parseColor(props.color ?? '0xFF2196F3'),
446
+ borderRadius,
447
+ justifyContent: 'center',
448
+ alignItems: 'center'
449
+ },
450
+ children: content
451
+ });
452
+ }
453
+ break;
454
+ }
455
+ case 'text_input':
456
+ {
457
+ node = /*#__PURE__*/_jsx(DynamicInput, {
458
+ initialValue: params.formData[id ?? ''] ?? '',
459
+ properties: props,
460
+ onChanged: value => {
461
+ if (params.onInputChange && id) {
462
+ params.onInputChange(id, value);
463
+ }
464
+ }
465
+ });
466
+ break;
467
+ }
468
+ case 'spacer':
469
+ {
470
+ node = /*#__PURE__*/_jsx(View, {
471
+ style: {
472
+ height: normalizeDimension(parseDimension(props.height)),
473
+ width: normalizeDimension(parseDimension(props.width))
474
+ }
475
+ });
476
+ break;
477
+ }
478
+ case 'expanded':
479
+ {
480
+ const expandedChild = childJson ? buildWidget(childJson, {
481
+ ...params,
482
+ allowFlexExpansion: true
483
+ }) : null;
484
+ if (params.allowFlexExpansion) {
485
+ return /*#__PURE__*/_jsx(View, {
486
+ style: {
487
+ flex: 1
488
+ },
489
+ children: expandedChild
490
+ });
491
+ }
492
+ return expandedChild;
493
+ }
494
+ case 'padding':
495
+ {
496
+ const padding = parseInsets(props.padding ?? 0);
497
+ node = /*#__PURE__*/_jsx(View, {
498
+ style: insetsToStyle(padding),
499
+ children: childJson ? buildWidget(childJson, {
500
+ ...params
501
+ }) : null
502
+ });
503
+ break;
504
+ }
505
+ case 'align':
506
+ {
507
+ const alignment = parseAlignment(props.alignment ?? 'center');
508
+ node = /*#__PURE__*/_jsx(View, {
509
+ style: {
510
+ justifyContent: alignment.justifyContent,
511
+ alignItems: alignment.alignItems
512
+ },
513
+ children: childJson ? buildWidget(childJson, {
514
+ ...params
515
+ }) : null
516
+ });
517
+ break;
518
+ }
519
+ case 'selection_list':
520
+ {
521
+ node = /*#__PURE__*/_jsx(SelectionList, {
522
+ properties: props,
523
+ options: Array.isArray(json.options) ? json.options : [],
524
+ initialValue: params.formData[id ?? ''],
525
+ onChanged: value => {
526
+ if (params.onInputChange && id) {
527
+ params.onInputChange(id, value);
528
+ }
529
+ },
530
+ onAction: params.onAction,
531
+ buildWidget: widgetJson => buildWidget(widgetJson, {
532
+ ...params,
533
+ allowFlexExpansion: true
534
+ })
535
+ });
536
+ break;
537
+ }
538
+ case 'image':
539
+ {
540
+ const source = props.source;
541
+ const width = normalizeDimension(parseDimension(props.width, true));
542
+ const height = normalizeDimension(parseDimension(props.height, true));
543
+ if (source === 'asset') {
544
+ node = /*#__PURE__*/_jsx(Image, {
545
+ source: {
546
+ uri: props.path
547
+ },
548
+ style: {
549
+ width,
550
+ height
551
+ },
552
+ resizeMode: parseResizeMode(props.fit)
553
+ });
554
+ } else if (source === 'network') {
555
+ if (!props.url) return null;
556
+ node = /*#__PURE__*/_jsx(Image, {
557
+ source: {
558
+ uri: props.url
559
+ },
560
+ style: {
561
+ width,
562
+ height
563
+ },
564
+ resizeMode: parseResizeMode(props.fit)
565
+ });
566
+ } else {
567
+ node = null;
568
+ }
569
+ break;
570
+ }
571
+ case 'lottie':
572
+ {
573
+ const source = props.source;
574
+ const width = normalizeDimension(parseDimension(props.width, true));
575
+ const height = normalizeDimension(parseDimension(props.height, true));
576
+ const loop = props.loop === true;
577
+ if (source === 'asset') {
578
+ node = /*#__PURE__*/_jsx(LottieView, {
579
+ source: {
580
+ uri: props.path ?? ''
581
+ },
582
+ style: {
583
+ width,
584
+ height
585
+ },
586
+ autoPlay: true,
587
+ loop: loop
588
+ });
589
+ } else if (source === 'network') {
590
+ if (!props.url) return null;
591
+ node = /*#__PURE__*/_jsx(LottieView, {
592
+ source: {
593
+ uri: props.url
594
+ },
595
+ style: {
596
+ width,
597
+ height
598
+ },
599
+ autoPlay: true,
600
+ loop: loop
601
+ });
602
+ } else {
603
+ node = null;
604
+ }
605
+ break;
606
+ }
607
+ case 'grid':
608
+ {
609
+ const crossAxisCount = Number(props.crossAxisCount ?? 2);
610
+ const spacing = Number(props.spacing ?? 8);
611
+ const mainAxisSpacing = Number(props.mainAxisSpacing ?? spacing);
612
+ const crossAxisSpacing = Number(props.crossAxisSpacing ?? spacing);
613
+ const childAspectRatio = Number(props.childAspectRatio ?? 1);
614
+ node = /*#__PURE__*/_jsx(View, {
615
+ style: {
616
+ flexDirection: 'row',
617
+ flexWrap: 'wrap'
618
+ },
619
+ children: (childrenJson ?? []).map((child, index) => /*#__PURE__*/_jsx(View, {
620
+ style: {
621
+ width: `${100 / crossAxisCount}%`,
622
+ paddingRight: (index + 1) % crossAxisCount === 0 ? 0 : crossAxisSpacing,
623
+ paddingBottom: mainAxisSpacing,
624
+ aspectRatio: childAspectRatio
625
+ },
626
+ children: buildWidget(child, {
627
+ ...params
628
+ })
629
+ }, `grid-${index}`))
630
+ });
631
+ break;
632
+ }
633
+ case 'fake_progress_bar':
634
+ {
635
+ node = /*#__PURE__*/_jsx(FakeProgressBar, {
636
+ progressColor: parseColor(props.progressColor ?? '0xFF2196F3'),
637
+ backgroundColor: parseColor(props.backgroundColor ?? '0xFFE0E0E0'),
638
+ thickness: Number(props.thickness ?? 4),
639
+ borderRadius: Number(props.borderRadius ?? 0),
640
+ durationMs: Number(props.duration ?? 3000),
641
+ startDelayMs: Number(props.startDelay ?? 0),
642
+ style: props.style ?? 'linear',
643
+ showProgressText: props.showProgressText === true,
644
+ progressTextStyle: getTextStyle(props.progressTextStyle ?? {}, 'color'),
645
+ progressTextFormat: props.progressTextFormat ?? '{{progress}}%',
646
+ size: Number(props.size ?? 50),
647
+ onComplete: () => {
648
+ if (action) {
649
+ params.onAction(action, actionPayload ?? props);
650
+ }
651
+ }
652
+ });
653
+ break;
654
+ }
655
+ case 'slider':
656
+ {
657
+ node = /*#__PURE__*/_jsx(SliderWidget, {
658
+ id: json._internalId,
659
+ linkedTo: props.linkedTo,
660
+ direction: props.direction === 'vertical' ? 'vertical' : 'horizontal',
661
+ autoPlay: props.autoPlay === true,
662
+ autoPlayDelay: Number(props.autoPlayDelay ?? 3000),
663
+ loop: props.loop === true,
664
+ height: parseDimension(props.height),
665
+ physics: props.physics,
666
+ children: (childrenJson ?? []).map((child, index) => /*#__PURE__*/_jsx(View, {
667
+ style: {
668
+ flex: 1
669
+ },
670
+ children: buildWidget(child, {
671
+ ...params
672
+ })
673
+ }, `slider-${index}`))
674
+ });
675
+ break;
676
+ }
677
+ case 'pageview_indicator':
678
+ {
679
+ node = /*#__PURE__*/_jsx(PageViewIndicator, {
680
+ linkedTo: props.linkedTo,
681
+ activeColor: parseColor(props.activeColor ?? '0xFF2196F3'),
682
+ inactiveColor: parseColor(props.inactiveColor ?? '0xFFE0E0E0'),
683
+ dotSize: Number(props.dotSize ?? 8),
684
+ spacing: Number(props.spacing ?? 8),
685
+ style: props.style ?? 'dots'
686
+ });
687
+ break;
688
+ }
689
+ case 'radar_chart':
690
+ {
691
+ node = /*#__PURE__*/_jsx(RadarChart, {
692
+ properties: props,
693
+ formData: params.formData
694
+ });
695
+ break;
696
+ }
697
+ case 'icon':
698
+ {
699
+ const iconName = props.icon ?? props.name;
700
+ const iconStyle = props.style ?? 'solid';
701
+ const size = Number(props.size ?? 24);
702
+ const color = parseColor(props.color ?? '0xFF000000');
703
+ if (!params.enableFontAwesomeIcons) {
704
+ node = /*#__PURE__*/_jsx(Text, {
705
+ style: {
706
+ fontSize: size,
707
+ color
708
+ },
709
+ children: "?"
710
+ });
711
+ break;
712
+ }
713
+ const icon = resolveFontAwesomeIcon(iconName, iconStyle);
714
+ if (!icon.name) {
715
+ node = /*#__PURE__*/_jsx(Text, {
716
+ style: {
717
+ fontSize: size,
718
+ color
719
+ },
720
+ children: "?"
721
+ });
722
+ } else {
723
+ node = /*#__PURE__*/_jsx(FontAwesome6, {
724
+ name: icon.name,
725
+ size: size,
726
+ color: color,
727
+ ...icon.props
728
+ });
729
+ }
730
+ break;
731
+ }
732
+ case 'stack':
733
+ {
734
+ const alignment = parseAlignment(props.alignment ?? 'topLeft');
735
+ const fit = props.fit === 'expand' ? 'expand' : 'loose';
736
+ node = /*#__PURE__*/_jsx(View, {
737
+ style: {
738
+ position: 'relative',
739
+ width: fit === 'expand' ? '100%' : undefined,
740
+ height: fit === 'expand' ? '100%' : undefined,
741
+ justifyContent: alignment.justifyContent,
742
+ alignItems: alignment.alignItems
743
+ },
744
+ children: (childrenJson ?? []).map((child, index) => /*#__PURE__*/_jsx(React.Fragment, {
745
+ children: buildWidget(child, {
746
+ ...params
747
+ })
748
+ }, `stack-${index}`))
749
+ });
750
+ break;
751
+ }
752
+ case 'positioned':
753
+ {
754
+ const left = parseDimension(props.left);
755
+ const top = parseDimension(props.top);
756
+ const right = parseDimension(props.right);
757
+ const bottom = parseDimension(props.bottom);
758
+ const width = parseDimension(props.width);
759
+ const height = parseDimension(props.height);
760
+ return /*#__PURE__*/_jsx(View, {
761
+ style: {
762
+ position: 'absolute',
763
+ left,
764
+ top,
765
+ right,
766
+ bottom,
767
+ width,
768
+ height
769
+ },
770
+ children: childJson ? buildWidget(childJson, {
771
+ ...params
772
+ }) : null
773
+ });
774
+ }
775
+ default:
776
+ node = null;
777
+ }
778
+ if (!node) return null;
779
+ const marginVal = props.margin;
780
+ if (marginVal !== undefined && marginVal !== null) {
781
+ const marginInsets = parseInsets(marginVal);
782
+ node = /*#__PURE__*/_jsx(View, {
783
+ style: insetsToStyle(marginInsets),
784
+ children: node
785
+ });
786
+ } else if (type !== 'container' && type !== 'padding' && props.padding) {
787
+ const paddingInsets = parseInsets(props.padding);
788
+ node = /*#__PURE__*/_jsx(View, {
789
+ style: insetsToStyle(paddingInsets),
790
+ children: node
791
+ });
792
+ }
793
+ if (params.allowFlexExpansion && params.screenData.scrollable !== true) {
794
+ let shouldExpand = false;
795
+ if (type === 'stack' && props.fit === 'expand') {
796
+ shouldExpand = true;
797
+ }
798
+ if (props.height !== undefined) {
799
+ const heightVal = parseDimension(props.height);
800
+ if (heightVal === Number.POSITIVE_INFINITY) {
801
+ shouldExpand = true;
802
+ }
803
+ }
804
+ if (shouldExpand) {
805
+ return /*#__PURE__*/_jsx(View, {
806
+ style: {
807
+ flex: 1
808
+ },
809
+ children: node
810
+ });
811
+ }
812
+ }
813
+ return node;
814
+ }
815
+ function buildActionPayload(props, json) {
816
+ const payload = {
817
+ ...props
818
+ };
819
+ const actionPermission = json.actionPermission;
820
+ if (actionPermission !== undefined) payload.actionPermission = actionPermission;
821
+ const extraData = json.actionData;
822
+ if (extraData && typeof extraData === 'object') {
823
+ Object.assign(payload, extraData);
824
+ }
825
+ return payload;
826
+ }
827
+ function getTextStyle(props, colorKey, defaultColor) {
828
+ const fontFamily = props.fontFamily;
829
+ const fontSize = Number(props.fontSize ?? 14);
830
+ const fontWeight = parseFontWeight(props.fontWeight);
831
+ const fontStyle = props.fontStyle === 'italic' ? 'italic' : 'normal';
832
+ const color = parseColor(props[colorKey] ?? defaultColor ?? '0xFF000000');
833
+ const letterSpacing = props.letterSpacing !== undefined ? Number(props.letterSpacing) : undefined;
834
+ const height = props.height !== undefined ? Number(props.height) : undefined;
835
+ const textDecoration = parseTextDecoration(props.decoration);
836
+ return {
837
+ fontFamily,
838
+ fontSize,
839
+ fontWeight,
840
+ fontStyle,
841
+ color,
842
+ letterSpacing,
843
+ lineHeight: height ? height * fontSize : undefined,
844
+ textDecorationLine: textDecoration
845
+ };
846
+ }
847
+ function parseGradient(value) {
848
+ if (!value || typeof value !== 'object' || value.type !== 'gradient') return undefined;
849
+ const colors = Array.isArray(value.colors) ? value.colors.map(c => parseColor(c)) : [];
850
+ if (colors.length === 0) return undefined;
851
+ const start = alignmentToGradient(value.begin ?? 'topLeft');
852
+ const end = alignmentToGradient(value.end ?? 'bottomRight');
853
+ const stops = Array.isArray(value.stops) ? value.stops.map(s => Number(s)) : undefined;
854
+ return {
855
+ colors,
856
+ start,
857
+ end,
858
+ stops
859
+ };
860
+ }
861
+ function alignmentToGradient(value) {
862
+ switch (value) {
863
+ case 'topCenter':
864
+ return {
865
+ x: 0.5,
866
+ y: 0
867
+ };
868
+ case 'bottomCenter':
869
+ return {
870
+ x: 0.5,
871
+ y: 1
872
+ };
873
+ case 'centerLeft':
874
+ return {
875
+ x: 0,
876
+ y: 0.5
877
+ };
878
+ case 'centerRight':
879
+ return {
880
+ x: 1,
881
+ y: 0.5
882
+ };
883
+ case 'center':
884
+ return {
885
+ x: 0.5,
886
+ y: 0.5
887
+ };
888
+ case 'bottomLeft':
889
+ return {
890
+ x: 0,
891
+ y: 1
892
+ };
893
+ case 'bottomRight':
894
+ return {
895
+ x: 1,
896
+ y: 1
897
+ };
898
+ case 'topRight':
899
+ return {
900
+ x: 1,
901
+ y: 0
902
+ };
903
+ case 'topLeft':
904
+ default:
905
+ return {
906
+ x: 0,
907
+ y: 0
908
+ };
909
+ }
910
+ }
911
+ function withOpacity(color, opacity) {
912
+ if (color.startsWith('rgba')) {
913
+ return color.replace(/rgba\((\d+),(\d+),(\d+),([\d.]+)\)/, (_m, r, g, b) => `rgba(${r},${g},${b},${opacity})`);
914
+ }
915
+ return color;
916
+ }
917
+ function normalizeDimension(value) {
918
+ if (value === Number.POSITIVE_INFINITY) {
919
+ return '100%';
920
+ }
921
+ return value;
922
+ }
923
+ function GradientText({
924
+ text,
925
+ gradient,
926
+ style
927
+ }) {
928
+ return /*#__PURE__*/_jsx(MaskedView, {
929
+ maskElement: /*#__PURE__*/_jsx(Text, {
930
+ style: [style, {
931
+ backgroundColor: 'transparent'
932
+ }],
933
+ children: text
934
+ }),
935
+ children: /*#__PURE__*/_jsx(LinearGradient, {
936
+ colors: gradient.colors,
937
+ start: gradient.start,
938
+ end: gradient.end,
939
+ locations: gradient.stops,
940
+ children: /*#__PURE__*/_jsx(Text, {
941
+ style: [style, {
942
+ opacity: 0
943
+ }],
944
+ children: text
945
+ })
946
+ })
947
+ });
948
+ }
949
+ function DynamicInput({
950
+ initialValue,
951
+ properties,
952
+ onChanged
953
+ }) {
954
+ const [value, setValue] = React.useState(initialValue ?? '');
955
+ React.useEffect(() => {
956
+ setValue(initialValue ?? '');
957
+ }, [initialValue]);
958
+ const keyboardType = mapKeyboardType(properties.keyboardType);
959
+ const autoCapitalize = mapAutoCapitalize(properties.autoCapitalize);
960
+ const secureTextEntry = properties.obscureText === true;
961
+ const multiline = properties.keyboardType === 'multiline';
962
+ const maxLength = properties.maxLength ? Number(properties.maxLength) : undefined;
963
+ const mask = properties.mask ? String(properties.mask) : null;
964
+ const label = properties.label;
965
+ const placeholder = properties.placeholder;
966
+ const labelStyle = getTextStyle(properties.labelStyle ?? {}, 'color', '0xFF9E9E9E');
967
+ const hintStyle = getTextStyle(properties.hintStyle ?? {}, 'color', '0xFF9E9E9E');
968
+ const inputStyle = getTextStyle({
969
+ ...properties,
970
+ height: null
971
+ }, 'textColor');
972
+ const backgroundColor = parseColor(properties.backgroundColor ?? '0xFFF0F0F0');
973
+ const borderRadius = Number(properties.borderRadius ?? 8);
974
+ return /*#__PURE__*/_jsxs(View, {
975
+ children: [label ? /*#__PURE__*/_jsx(Text, {
976
+ style: labelStyle,
977
+ children: label
978
+ }) : null, /*#__PURE__*/_jsx(View, {
979
+ style: {
980
+ backgroundColor,
981
+ borderRadius,
982
+ paddingHorizontal: 16,
983
+ paddingVertical: 10
984
+ },
985
+ children: mask ? /*#__PURE__*/_jsx(MaskInput, {
986
+ value: value,
987
+ onChangeText: (_masked, unmasked) => {
988
+ setValue(unmasked);
989
+ onChanged(unmasked);
990
+ },
991
+ autoFocus: true,
992
+ autoCapitalize: autoCapitalize,
993
+ keyboardType: keyboardType,
994
+ secureTextEntry: secureTextEntry,
995
+ multiline: multiline,
996
+ maxLength: maxLength,
997
+ style: inputStyle,
998
+ mask: mask.split('').map(char => {
999
+ if (char === '#') return /\d/;
1000
+ if (char === 'A') return /[a-zA-Z]/;
1001
+ return char;
1002
+ }),
1003
+ placeholder: placeholder,
1004
+ placeholderTextColor: hintStyle.color
1005
+ }) : /*#__PURE__*/_jsx(TextInput, {
1006
+ value: value,
1007
+ onChangeText: text => {
1008
+ setValue(text);
1009
+ onChanged(text);
1010
+ },
1011
+ autoFocus: true,
1012
+ autoCapitalize: autoCapitalize,
1013
+ keyboardType: keyboardType,
1014
+ secureTextEntry: secureTextEntry,
1015
+ multiline: multiline,
1016
+ maxLength: maxLength,
1017
+ style: inputStyle,
1018
+ placeholder: placeholder,
1019
+ placeholderTextColor: hintStyle.color
1020
+ })
1021
+ })]
1022
+ });
1023
+ }
1024
+ function mapKeyboardType(type) {
1025
+ switch (type) {
1026
+ case 'email':
1027
+ return 'email-address';
1028
+ case 'number':
1029
+ return 'numeric';
1030
+ case 'phone':
1031
+ return 'phone-pad';
1032
+ case 'multiline':
1033
+ return 'default';
1034
+ default:
1035
+ return 'default';
1036
+ }
1037
+ }
1038
+ function mapAutoCapitalize(type) {
1039
+ switch (type) {
1040
+ case 'words':
1041
+ return 'words';
1042
+ case 'sentences':
1043
+ return 'sentences';
1044
+ case 'characters':
1045
+ return 'characters';
1046
+ default:
1047
+ return 'none';
1048
+ }
1049
+ }
1050
+ function SelectionList({
1051
+ properties,
1052
+ options,
1053
+ initialValue,
1054
+ onChanged,
1055
+ onAction,
1056
+ buildWidget
1057
+ }) {
1058
+ const multiSelect = properties.multiSelect === true;
1059
+ const [selectedValues, setSelectedValues] = React.useState(() => {
1060
+ if (initialValue === null || initialValue === undefined) return [];
1061
+ if (Array.isArray(initialValue)) return initialValue.map(String);
1062
+ if (typeof initialValue === 'string') {
1063
+ if (multiSelect) {
1064
+ try {
1065
+ return initialValue.replace('[', '').replace(']', '').split(',').map(val => val.trim());
1066
+ } catch {
1067
+ return [initialValue];
1068
+ }
1069
+ }
1070
+ return [initialValue];
1071
+ }
1072
+ return [];
1073
+ });
1074
+ const toggleSelection = value => {
1075
+ setSelectedValues(prev => {
1076
+ let next = [...prev];
1077
+ if (multiSelect) {
1078
+ if (next.includes(value)) {
1079
+ next = next.filter(item => item !== value);
1080
+ } else {
1081
+ next.push(value);
1082
+ }
1083
+ onChanged(next.join(','));
1084
+ } else {
1085
+ next = [value];
1086
+ onChanged(value);
1087
+ if (properties.autoGoNext === true) {
1088
+ onAction('next');
1089
+ }
1090
+ }
1091
+ return next;
1092
+ });
1093
+ };
1094
+ const layout = properties.layout ?? 'column';
1095
+ const spacing = Number(properties.spacing ?? 8);
1096
+ const renderItem = (option, index) => {
1097
+ const value = String(option.value ?? '');
1098
+ const isSelected = selectedValues.includes(value);
1099
+ const styleProps = isSelected ? option.selectedStyle ?? properties.selectedStyle ?? {} : option.unselectedStyle ?? properties.unselectedStyle ?? {};
1100
+ const backgroundColor = parseColor(styleProps.backgroundColor);
1101
+ const borderColor = parseColor(styleProps.borderColor);
1102
+ const borderRadius = Number(styleProps.borderRadius ?? 8);
1103
+ const borderWidth = Number(styleProps.borderWidth ?? 1);
1104
+ return /*#__PURE__*/_jsx(Pressable, {
1105
+ onPress: () => toggleSelection(value),
1106
+ style: {
1107
+ width: '100%',
1108
+ backgroundColor,
1109
+ borderColor,
1110
+ borderWidth,
1111
+ borderRadius,
1112
+ padding: 0
1113
+ },
1114
+ children: buildWidget(option.child)
1115
+ }, `option-${index}`);
1116
+ };
1117
+ if (layout === 'row') {
1118
+ return /*#__PURE__*/_jsx(View, {
1119
+ style: {
1120
+ flexDirection: 'row',
1121
+ flexWrap: 'wrap'
1122
+ },
1123
+ children: options.map((option, index) => /*#__PURE__*/_jsx(View, {
1124
+ style: {
1125
+ marginRight: spacing,
1126
+ marginBottom: spacing
1127
+ },
1128
+ children: renderItem(option, index)
1129
+ }, `row-option-${index}`))
1130
+ });
1131
+ }
1132
+ if (layout === 'grid') {
1133
+ const crossAxisCount = Number(properties.gridCrossAxisCount ?? 2);
1134
+ const aspectRatio = Number(properties.gridAspectRatio ?? 1);
1135
+ return /*#__PURE__*/_jsx(View, {
1136
+ style: {
1137
+ flexDirection: 'row',
1138
+ flexWrap: 'wrap'
1139
+ },
1140
+ children: options.map((option, index) => /*#__PURE__*/_jsx(View, {
1141
+ style: {
1142
+ width: `${100 / crossAxisCount}%`,
1143
+ paddingRight: (index + 1) % crossAxisCount === 0 ? 0 : spacing,
1144
+ paddingBottom: spacing,
1145
+ aspectRatio
1146
+ },
1147
+ children: renderItem(option, index)
1148
+ }, `grid-option-${index}`))
1149
+ });
1150
+ }
1151
+ return /*#__PURE__*/_jsx(View, {
1152
+ children: options.map((option, index) => /*#__PURE__*/_jsx(View, {
1153
+ style: {
1154
+ marginBottom: spacing
1155
+ },
1156
+ children: renderItem(option, index)
1157
+ }, `column-option-${index}`))
1158
+ });
1159
+ }
1160
+ function FakeProgressBar({
1161
+ progressColor,
1162
+ backgroundColor,
1163
+ thickness,
1164
+ borderRadius,
1165
+ durationMs,
1166
+ startDelayMs,
1167
+ style,
1168
+ showProgressText,
1169
+ progressTextStyle,
1170
+ progressTextFormat,
1171
+ size,
1172
+ onComplete
1173
+ }) {
1174
+ const animation = useMemo(() => new Animated.Value(0), []);
1175
+ const [progressValue, setProgressValue] = React.useState(0);
1176
+ React.useEffect(() => {
1177
+ const listenerId = animation.addListener(({
1178
+ value
1179
+ }) => {
1180
+ setProgressValue(Math.round(value * 100));
1181
+ });
1182
+ const timeout = setTimeout(() => {
1183
+ Animated.timing(animation, {
1184
+ toValue: 1,
1185
+ duration: durationMs,
1186
+ useNativeDriver: false
1187
+ }).start(({
1188
+ finished
1189
+ }) => {
1190
+ if (finished && onComplete) onComplete();
1191
+ });
1192
+ }, startDelayMs);
1193
+ return () => {
1194
+ animation.removeListener(listenerId);
1195
+ clearTimeout(timeout);
1196
+ animation.stopAnimation();
1197
+ };
1198
+ }, [animation, durationMs, startDelayMs, onComplete]);
1199
+ const progressText = progressTextFormat.replace('{{progress}}', progressValue.toString());
1200
+ if (style === 'circular') {
1201
+ const radius = size / 2 - thickness;
1202
+ const circumference = 2 * Math.PI * radius;
1203
+ const strokeDashoffset = animation.interpolate({
1204
+ inputRange: [0, 1],
1205
+ outputRange: [circumference, 0]
1206
+ });
1207
+ return /*#__PURE__*/_jsxs(View, {
1208
+ style: {
1209
+ alignItems: 'center'
1210
+ },
1211
+ children: [/*#__PURE__*/_jsxs(Svg, {
1212
+ width: size,
1213
+ height: size,
1214
+ children: [/*#__PURE__*/_jsx(Circle, {
1215
+ cx: size / 2,
1216
+ cy: size / 2,
1217
+ r: radius,
1218
+ stroke: backgroundColor,
1219
+ strokeWidth: thickness,
1220
+ fill: "none"
1221
+ }), /*#__PURE__*/_jsx(AnimatedCircle, {
1222
+ cx: size / 2,
1223
+ cy: size / 2,
1224
+ r: radius,
1225
+ stroke: progressColor,
1226
+ strokeWidth: thickness,
1227
+ strokeDasharray: `${circumference} ${circumference}`,
1228
+ strokeDashoffset: strokeDashoffset,
1229
+ fill: "none",
1230
+ strokeLinecap: "round"
1231
+ })]
1232
+ }), showProgressText && /*#__PURE__*/_jsx(Text, {
1233
+ style: progressTextStyle,
1234
+ children: progressText
1235
+ })]
1236
+ });
1237
+ }
1238
+ return /*#__PURE__*/_jsxs(View, {
1239
+ children: [/*#__PURE__*/_jsx(View, {
1240
+ style: {
1241
+ height: thickness,
1242
+ width: '100%',
1243
+ borderRadius,
1244
+ backgroundColor,
1245
+ overflow: 'hidden'
1246
+ },
1247
+ children: /*#__PURE__*/_jsx(Animated.View, {
1248
+ style: {
1249
+ height: '100%',
1250
+ width: animation.interpolate({
1251
+ inputRange: [0, 1],
1252
+ outputRange: ['0%', '100%']
1253
+ }),
1254
+ backgroundColor: progressColor,
1255
+ borderRadius
1256
+ }
1257
+ })
1258
+ }), showProgressText ? /*#__PURE__*/_jsx(Text, {
1259
+ style: [progressTextStyle, {
1260
+ marginTop: 8
1261
+ }],
1262
+ children: progressText
1263
+ }) : null]
1264
+ });
1265
+ }
1266
+ const AnimatedCircle = Animated.createAnimatedComponent(Circle);
1267
+ function SliderWidget({
1268
+ id,
1269
+ linkedTo,
1270
+ direction,
1271
+ autoPlay,
1272
+ autoPlayDelay,
1273
+ loop,
1274
+ height,
1275
+ physics,
1276
+ children
1277
+ }) {
1278
+ const registry = useSliderRegistry();
1279
+ const pagerRef = React.useRef(null);
1280
+ const pageLength = children.length;
1281
+ const [currentPage, setCurrentPage] = React.useState(() => loop && pageLength > 0 ? pageLength * 1000 : 0);
1282
+ const timerRef = React.useRef(null);
1283
+ React.useEffect(() => {
1284
+ if (autoPlay && !linkedTo) {
1285
+ timerRef.current = setInterval(() => {
1286
+ setCurrentPage(prev => {
1287
+ if (loop) return prev + 1;
1288
+ if (prev < children.length - 1) return prev + 1;
1289
+ return 0;
1290
+ });
1291
+ }, autoPlayDelay);
1292
+ }
1293
+ return () => {
1294
+ if (timerRef.current) clearInterval(timerRef.current);
1295
+ };
1296
+ }, [autoPlay, autoPlayDelay, loop, linkedTo, children.length]);
1297
+ React.useEffect(() => {
1298
+ if (pagerRef.current) {
1299
+ pagerRef.current.setPage(currentPage);
1300
+ }
1301
+ }, [currentPage]);
1302
+ React.useEffect(() => {
1303
+ if (registry && id && children.length > 0) {
1304
+ registry.update(id, currentPage % children.length, children.length);
1305
+ }
1306
+ }, [registry, id, currentPage, children.length]);
1307
+ React.useEffect(() => {
1308
+ if (!linkedTo || !registry) return;
1309
+ return registry.getNotifier(linkedTo, value => {
1310
+ if (pagerRef.current) {
1311
+ pagerRef.current.setPage(value);
1312
+ }
1313
+ });
1314
+ }, [linkedTo, registry]);
1315
+ if (pageLength === 0) return null;
1316
+ const pageCount = loop ? pageLength * 1000 : pageLength;
1317
+ return /*#__PURE__*/_jsx(PagerView, {
1318
+ ref: pagerRef,
1319
+ style: {
1320
+ height: height ?? 200
1321
+ },
1322
+ orientation: direction,
1323
+ scrollEnabled: physics !== 'never',
1324
+ initialPage: currentPage,
1325
+ onPageSelected: event => {
1326
+ const page = event.nativeEvent.position;
1327
+ setCurrentPage(page);
1328
+ if (registry && id) {
1329
+ registry.update(id, page % pageLength, pageLength);
1330
+ }
1331
+ },
1332
+ children: Array.from({
1333
+ length: pageCount
1334
+ }).map((_, index) => /*#__PURE__*/_jsx(View, {
1335
+ style: {
1336
+ flex: 1
1337
+ },
1338
+ children: children[index % pageLength]
1339
+ }, `slider-page-${index}`))
1340
+ });
1341
+ }
1342
+ function PageViewIndicator({
1343
+ linkedTo,
1344
+ activeColor,
1345
+ inactiveColor,
1346
+ dotSize,
1347
+ spacing,
1348
+ style
1349
+ }) {
1350
+ const registry = useSliderRegistry();
1351
+ const [current, setCurrent] = React.useState(0);
1352
+ const indicatorStyle = style ?? 'dots';
1353
+ const count = linkedTo && registry ? registry.getPageCount(linkedTo) : 0;
1354
+ React.useEffect(() => {
1355
+ if (!linkedTo || !registry) return;
1356
+ return registry.getNotifier(linkedTo, setCurrent);
1357
+ }, [linkedTo, registry]);
1358
+ if (!linkedTo || !registry || count <= 1) return null;
1359
+ if (indicatorStyle !== 'dots') {
1360
+ // Only dots are supported currently; fallback to dots.
1361
+ }
1362
+ return /*#__PURE__*/_jsx(View, {
1363
+ style: {
1364
+ flexDirection: 'row',
1365
+ justifyContent: 'center'
1366
+ },
1367
+ children: Array.from({
1368
+ length: count
1369
+ }).map((_, index) => /*#__PURE__*/_jsx(View, {
1370
+ style: {
1371
+ width: dotSize,
1372
+ height: dotSize,
1373
+ borderRadius: dotSize / 2,
1374
+ marginHorizontal: spacing / 2,
1375
+ backgroundColor: index === current ? activeColor : inactiveColor
1376
+ }
1377
+ }, `dot-${index}`))
1378
+ });
1379
+ }
1380
+ function RadarChart({
1381
+ properties,
1382
+ formData
1383
+ }) {
1384
+ const animate = properties.animate !== false;
1385
+ const animationDurationMs = Number(properties.animationDurationMs ?? 800);
1386
+ const scale = useMemo(() => new Animated.Value(animate ? 0 : 1), [animate]);
1387
+ React.useEffect(() => {
1388
+ if (!animate) return;
1389
+ Animated.timing(scale, {
1390
+ toValue: 1,
1391
+ duration: animationDurationMs,
1392
+ useNativeDriver: true
1393
+ }).start();
1394
+ }, [animate, animationDurationMs, scale]);
1395
+ const axes = parseRadarAxes(properties.axes);
1396
+ if (axes.length < 3) {
1397
+ return radarPlaceholder('Radar chart requires at least 3 axes.');
1398
+ }
1399
+ const datasets = parseRadarDatasets(properties.datasets, axes.length, formData);
1400
+ if (datasets.length === 0) {
1401
+ return radarPlaceholder('Add at least one dataset with the same number of values as axes.');
1402
+ }
1403
+ const normalizedAxes = normalizeAxisMaximums(axes, datasets);
1404
+ const backgroundColor = parseColor(properties.backgroundColor);
1405
+ const width = parseDimension(properties.width) ?? 300;
1406
+ const rawHeight = parseDimension(properties.height);
1407
+ const height = rawHeight === undefined || rawHeight === Number.POSITIVE_INFINITY ? 300 : rawHeight;
1408
+ const padding = parseInsets(properties.padding);
1409
+ const gridLevels = Math.min(Math.max(Number(properties.gridLevels ?? 5), 1), 12);
1410
+ const gridColor = parseColor(properties.gridColor ?? '0xFFE0E0E0');
1411
+ const gridStrokeWidth = Number(properties.gridStrokeWidth ?? 1);
1412
+ const axisColor = parseColor(properties.axisColor ?? '0xFF9E9E9E');
1413
+ const axisStrokeWidth = Number(properties.axisStrokeWidth ?? 1);
1414
+ const axisLabelStyle = getTextStyle(properties.axisLabelStyle ?? {}, 'color', '0xFF424242');
1415
+ const showLegend = properties.showLegend !== false;
1416
+ const legendPosition = (properties.legendPosition ?? 'bottom').toLowerCase();
1417
+ const legendSpacing = Number(properties.legendSpacing ?? 12);
1418
+ const legendStyle = getTextStyle(properties.legendStyle ?? {}, 'color', '0xFF212121');
1419
+ const shape = (properties.shape ?? 'polygon').toLowerCase();
1420
+ const chartSize = Math.min(width, height);
1421
+ const radius = chartSize / 2 - Math.max(padding.left, padding.right, padding.top, padding.bottom);
1422
+ const center = {
1423
+ x: width / 2,
1424
+ y: height / 2
1425
+ };
1426
+ const axisAngle = Math.PI * 2 / axes.length;
1427
+ const gridPolygons = Array.from({
1428
+ length: gridLevels
1429
+ }).map((_, level) => {
1430
+ const ratio = (level + 1) / gridLevels;
1431
+ const points = axes.map((_, index) => {
1432
+ const angle = index * axisAngle - Math.PI / 2;
1433
+ const x = center.x + radius * ratio * Math.cos(angle);
1434
+ const y = center.y + radius * ratio * Math.sin(angle);
1435
+ return `${x},${y}`;
1436
+ });
1437
+ return {
1438
+ points
1439
+ };
1440
+ });
1441
+ const datasetPolygons = datasets.map(dataset => {
1442
+ const points = dataset.values.map((value, index) => {
1443
+ const axis = normalizedAxes[index];
1444
+ const maxValue = axis?.maxValue ?? 0;
1445
+ const ratio = maxValue > 0 ? value / maxValue : 0;
1446
+ const angle = index * axisAngle - Math.PI / 2;
1447
+ const x = center.x + radius * ratio * Math.cos(angle);
1448
+ const y = center.y + radius * ratio * Math.sin(angle);
1449
+ return `${x},${y}`;
1450
+ });
1451
+ return {
1452
+ dataset,
1453
+ points
1454
+ };
1455
+ });
1456
+ const legend = /*#__PURE__*/_jsx(View, {
1457
+ style: {
1458
+ flexDirection: 'row',
1459
+ flexWrap: 'wrap'
1460
+ },
1461
+ children: datasets.map((dataset, index) => /*#__PURE__*/_jsxs(View, {
1462
+ style: {
1463
+ flexDirection: 'row',
1464
+ alignItems: 'center',
1465
+ marginRight: legendSpacing,
1466
+ marginBottom: 6
1467
+ },
1468
+ children: [/*#__PURE__*/_jsx(View, {
1469
+ style: {
1470
+ width: 12,
1471
+ height: 12,
1472
+ borderRadius: 6,
1473
+ backgroundColor: dataset.borderColor,
1474
+ marginRight: 6
1475
+ }
1476
+ }), /*#__PURE__*/_jsx(Text, {
1477
+ style: legendStyle,
1478
+ children: dataset.label
1479
+ })]
1480
+ }, `legend-${index}`))
1481
+ });
1482
+ const chart = /*#__PURE__*/_jsx(Animated.View, {
1483
+ style: {
1484
+ width,
1485
+ height,
1486
+ paddingLeft: padding.left,
1487
+ paddingRight: padding.right,
1488
+ paddingTop: padding.top,
1489
+ paddingBottom: padding.bottom,
1490
+ transform: [{
1491
+ scale
1492
+ }]
1493
+ },
1494
+ children: /*#__PURE__*/_jsx(Svg, {
1495
+ width: width,
1496
+ height: height,
1497
+ children: /*#__PURE__*/_jsxs(G, {
1498
+ children: [shape === 'circle' ? gridPolygons.map((_, level) => /*#__PURE__*/_jsx(Circle, {
1499
+ cx: center.x,
1500
+ cy: center.y,
1501
+ r: radius * ((level + 1) / gridLevels),
1502
+ stroke: gridColor,
1503
+ strokeWidth: gridStrokeWidth,
1504
+ fill: "none"
1505
+ }, `grid-circle-${level}`)) : gridPolygons.map((grid, level) => /*#__PURE__*/_jsx(Polygon, {
1506
+ points: grid.points.join(' '),
1507
+ stroke: gridColor,
1508
+ strokeWidth: gridStrokeWidth,
1509
+ fill: "none"
1510
+ }, `grid-${level}`)), axes.map((_axis, index) => {
1511
+ const angle = index * axisAngle - Math.PI / 2;
1512
+ const x = center.x + radius * Math.cos(angle);
1513
+ const y = center.y + radius * Math.sin(angle);
1514
+ return /*#__PURE__*/_jsx(Line, {
1515
+ x1: center.x,
1516
+ y1: center.y,
1517
+ x2: x,
1518
+ y2: y,
1519
+ stroke: axisColor,
1520
+ strokeWidth: axisStrokeWidth
1521
+ }, `axis-${index}`);
1522
+ }), axes.map((axis, index) => {
1523
+ const angle = index * axisAngle - Math.PI / 2;
1524
+ const labelX = center.x + (radius + 12) * Math.cos(angle);
1525
+ const labelY = center.y + (radius + 12) * Math.sin(angle);
1526
+ return /*#__PURE__*/_jsx(SvgText, {
1527
+ x: labelX,
1528
+ y: labelY,
1529
+ fontSize: axisLabelStyle.fontSize ?? 12,
1530
+ fontWeight: axisLabelStyle.fontWeight,
1531
+ fill: axisLabelStyle.color,
1532
+ textAnchor: "middle",
1533
+ children: axis.label
1534
+ }, `label-${index}`);
1535
+ }), datasetPolygons.map((entry, index) => /*#__PURE__*/_jsx(Polygon, {
1536
+ points: entry.points.join(' '),
1537
+ stroke: entry.dataset.borderColor,
1538
+ strokeWidth: entry.dataset.borderWidth,
1539
+ fill: entry.dataset.fillColor ?? 'transparent',
1540
+ strokeDasharray: entry.dataset.borderStyle === 'dotted' || entry.dataset.borderStyle === 'dashed' ? entry.dataset.dashArray ?? '6 4' : undefined
1541
+ }, `dataset-${index}`)), datasetPolygons.map((entry, datasetIndex) => entry.dataset.showPoints ? entry.points.map((point, pointIndex) => {
1542
+ const [x, y] = point.split(',').map(val => Number(val));
1543
+ return /*#__PURE__*/_jsx(Circle, {
1544
+ cx: x,
1545
+ cy: y,
1546
+ r: entry.dataset.pointRadius,
1547
+ fill: entry.dataset.pointColor
1548
+ }, `point-${datasetIndex}-${pointIndex}`);
1549
+ }) : null)]
1550
+ })
1551
+ })
1552
+ });
1553
+ const composed = /*#__PURE__*/_jsxs(View, {
1554
+ style: {
1555
+ backgroundColor
1556
+ },
1557
+ children: [chart, showLegend && legendPosition === 'bottom' ? /*#__PURE__*/_jsx(View, {
1558
+ style: {
1559
+ marginTop: 12
1560
+ },
1561
+ children: legend
1562
+ }) : null]
1563
+ });
1564
+ if (!showLegend || legendPosition === 'bottom') return composed;
1565
+ if (legendPosition === 'top') {
1566
+ return /*#__PURE__*/_jsxs(View, {
1567
+ children: [legend, /*#__PURE__*/_jsx(View, {
1568
+ style: {
1569
+ height: 12
1570
+ }
1571
+ }), chart]
1572
+ });
1573
+ }
1574
+ if (legendPosition === 'left') {
1575
+ return /*#__PURE__*/_jsxs(View, {
1576
+ style: {
1577
+ flexDirection: 'row'
1578
+ },
1579
+ children: [legend, /*#__PURE__*/_jsx(View, {
1580
+ style: {
1581
+ width: 12
1582
+ }
1583
+ }), chart]
1584
+ });
1585
+ }
1586
+ if (legendPosition === 'right') {
1587
+ return /*#__PURE__*/_jsxs(View, {
1588
+ style: {
1589
+ flexDirection: 'row'
1590
+ },
1591
+ children: [chart, /*#__PURE__*/_jsx(View, {
1592
+ style: {
1593
+ width: 12
1594
+ }
1595
+ }), legend]
1596
+ });
1597
+ }
1598
+ return composed;
1599
+ }
1600
+ export function parseRadarAxes(rawAxes) {
1601
+ if (!Array.isArray(rawAxes)) return [];
1602
+ const axes = [];
1603
+ rawAxes.forEach(axis => {
1604
+ if (!axis || typeof axis !== 'object') return;
1605
+ const label = axis.label ?? '';
1606
+ if (!label) return;
1607
+ const maxValue = resolveNumericValue(axis.maxValue, {}) ?? 100;
1608
+ axes.push({
1609
+ label,
1610
+ maxValue: maxValue > 0 ? maxValue : 100
1611
+ });
1612
+ });
1613
+ return axes;
1614
+ }
1615
+ export function parseRadarDatasets(raw, axisLength, formData) {
1616
+ if (!Array.isArray(raw) || axisLength === 0) return [];
1617
+ const datasets = [];
1618
+ raw.forEach(dataset => {
1619
+ if (!dataset || typeof dataset !== 'object') return;
1620
+ const valuesRaw = Array.isArray(dataset.data) ? dataset.data : [];
1621
+ const values = [];
1622
+ for (let i = 0; i < axisLength; i += 1) {
1623
+ const rawValue = i < valuesRaw.length ? valuesRaw[i] : null;
1624
+ const resolved = resolveNumericValue(rawValue, formData) ?? 0;
1625
+ values.push(resolved);
1626
+ }
1627
+ const label = dataset.label ?? `Dataset ${datasets.length + 1}`;
1628
+ const borderColor = parseColor(dataset.borderColor ?? '0xFF2196F3');
1629
+ const fillColor = dataset.fillColor ? parseColor(dataset.fillColor) : undefined;
1630
+ const pointColor = parseColor(dataset.pointColor ?? dataset.borderColor ?? '0xFF2196F3');
1631
+ const borderWidth = Number(dataset.borderWidth ?? 2);
1632
+ const pointRadius = Number(dataset.pointRadius ?? 4);
1633
+ const borderStyle = (dataset.borderStyle ?? 'solid').toString().toLowerCase();
1634
+ const normalizedStyle = borderStyle === 'dotted' || borderStyle === 'dashed' ? borderStyle : 'solid';
1635
+ const dashArray = Array.isArray(dataset.dashArray) ? dataset.dashArray.map(val => Number(val)) : undefined;
1636
+ const showPoints = dataset.showPoints !== false;
1637
+ datasets.push({
1638
+ label,
1639
+ values,
1640
+ borderColor,
1641
+ borderWidth,
1642
+ borderStyle: normalizedStyle,
1643
+ dashArray,
1644
+ showPoints,
1645
+ pointRadius,
1646
+ pointColor,
1647
+ fillColor
1648
+ });
1649
+ });
1650
+ return datasets;
1651
+ }
1652
+ export function normalizeAxisMaximums(axes, datasets) {
1653
+ return axes.map((axis, index) => {
1654
+ let maxValue = axis.maxValue;
1655
+ datasets.forEach(dataset => {
1656
+ const value = dataset.values[index];
1657
+ if (value !== undefined && value > maxValue) maxValue = value;
1658
+ });
1659
+ return {
1660
+ ...axis,
1661
+ maxValue
1662
+ };
1663
+ });
1664
+ }
1665
+ function radarPlaceholder(message) {
1666
+ return /*#__PURE__*/_jsx(View, {
1667
+ style: {
1668
+ padding: 16,
1669
+ borderRadius: 8,
1670
+ borderWidth: 1,
1671
+ borderColor: 'rgba(255,0,0,0.2)',
1672
+ backgroundColor: 'rgba(255,0,0,0.05)'
1673
+ },
1674
+ children: /*#__PURE__*/_jsx(Text, {
1675
+ style: {
1676
+ color: 'rgba(255,0,0,0.7)',
1677
+ fontSize: 12,
1678
+ textAlign: 'center'
1679
+ },
1680
+ children: message
1681
+ })
1682
+ });
1683
+ }
1684
+ //# sourceMappingURL=FlowboardRenderer.js.map