flowboard-react 0.2.0 → 0.4.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.
- package/README.md +15 -2
- package/lib/module/Flowboard.js +50 -11
- package/lib/module/Flowboard.js.map +1 -1
- package/lib/module/FlowboardProvider.js +18 -14
- package/lib/module/FlowboardProvider.js.map +1 -1
- package/lib/module/components/FlowboardFlow.js +70 -37
- package/lib/module/components/FlowboardFlow.js.map +1 -1
- package/lib/module/components/FlowboardRenderer.js +622 -105
- package/lib/module/components/FlowboardRenderer.js.map +1 -1
- package/lib/module/core/assetPreloader.js +20 -18
- package/lib/module/core/assetPreloader.js.map +1 -1
- package/lib/module/core/resolverService.js +31 -1
- package/lib/module/core/resolverService.js.map +1 -1
- package/lib/module/index.js +1 -0
- package/lib/module/index.js.map +1 -1
- package/lib/module/types/react-native-peers.d.js +2 -0
- package/lib/module/types/react-native-peers.d.js.map +1 -0
- package/lib/module/utils/flowboardUtils.js +20 -14
- package/lib/module/utils/flowboardUtils.js.map +1 -1
- package/lib/typescript/src/Flowboard.d.ts +5 -1
- package/lib/typescript/src/Flowboard.d.ts.map +1 -1
- package/lib/typescript/src/FlowboardProvider.d.ts.map +1 -1
- package/lib/typescript/src/components/FlowboardFlow.d.ts +1 -2
- package/lib/typescript/src/components/FlowboardFlow.d.ts.map +1 -1
- package/lib/typescript/src/components/FlowboardRenderer.d.ts.map +1 -1
- package/lib/typescript/src/core/assetPreloader.d.ts.map +1 -1
- package/lib/typescript/src/core/resolverService.d.ts +6 -0
- package/lib/typescript/src/core/resolverService.d.ts.map +1 -1
- package/lib/typescript/src/index.d.ts +3 -1
- package/lib/typescript/src/index.d.ts.map +1 -1
- package/lib/typescript/src/types/flowboard.d.ts +4 -0
- package/lib/typescript/src/types/flowboard.d.ts.map +1 -1
- package/lib/typescript/src/utils/flowboardUtils.d.ts +6 -0
- package/lib/typescript/src/utils/flowboardUtils.d.ts.map +1 -1
- package/package.json +4 -3
- package/src/Flowboard.ts +86 -16
- package/src/FlowboardProvider.tsx +20 -16
- package/src/components/FlowboardFlow.tsx +89 -49
- package/src/components/FlowboardRenderer.tsx +771 -98
- package/src/core/assetPreloader.ts +21 -32
- package/src/core/resolverService.ts +42 -2
- package/src/index.tsx +3 -0
- package/src/types/flowboard.ts +5 -0
- package/src/types/react-native-peers.d.ts +106 -0
- package/src/utils/flowboardUtils.ts +28 -14
|
@@ -9,17 +9,21 @@ import LottieView from 'lottie-react-native';
|
|
|
9
9
|
import MaskInput from 'react-native-mask-input';
|
|
10
10
|
import Svg, { Circle, G, Line, Polygon, Text as SvgText } from 'react-native-svg';
|
|
11
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";
|
|
12
|
+
import { insetsToStyle, insetsToMarginStyle, parseAlignment, parseColor, parseCrossAlignment, parseDimension, parseFlexAlignment, parseFontWeight, parseInsets, parseResizeMode, parseTextAlign, parseTextDecoration, resolveNumericValue, resolveText } from "../utils/flowboardUtils.js";
|
|
13
13
|
import { resolveFontAwesomeIcon, FontAwesome6 } from "../core/fontAwesome.js";
|
|
14
14
|
import { useSliderRegistry } from "./widgets/sliderRegistry.js";
|
|
15
15
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
16
16
|
const styles = StyleSheet.create({
|
|
17
17
|
root: {
|
|
18
|
-
flex: 1
|
|
18
|
+
flex: 1,
|
|
19
|
+
backgroundColor: '#ffffff'
|
|
19
20
|
},
|
|
20
21
|
background: {
|
|
21
22
|
...StyleSheet.absoluteFillObject
|
|
22
23
|
},
|
|
24
|
+
whiteBg: {
|
|
25
|
+
backgroundColor: '#ffffff'
|
|
26
|
+
},
|
|
23
27
|
safeArea: {
|
|
24
28
|
flex: 1
|
|
25
29
|
},
|
|
@@ -48,26 +52,32 @@ export default function FlowboardRenderer(props) {
|
|
|
48
52
|
const scrollable = screenData.scrollable === true;
|
|
49
53
|
const safeArea = screenData.safeArea !== false;
|
|
50
54
|
const padding = parseInsets(screenData.padding);
|
|
51
|
-
const
|
|
55
|
+
const contentPaddingStyle = insetsToStyle(padding);
|
|
56
|
+
const progressPaddingStyle = {
|
|
57
|
+
paddingTop: padding.top,
|
|
58
|
+
paddingRight: padding.right,
|
|
59
|
+
paddingLeft: padding.left
|
|
60
|
+
};
|
|
61
|
+
const rootCrossAxisAlignment = screenData.crossAxisAlignment ?? screenData.crossAxis;
|
|
52
62
|
const content = /*#__PURE__*/_jsxs(View, {
|
|
53
63
|
style: {
|
|
54
64
|
flex: 1
|
|
55
65
|
},
|
|
56
66
|
children: [showProgress && /*#__PURE__*/_jsx(View, {
|
|
57
|
-
style: [
|
|
67
|
+
style: [progressPaddingStyle, {
|
|
58
68
|
paddingBottom: 8
|
|
59
69
|
}],
|
|
60
70
|
children: renderProgressBar(currentIndex, totalScreens, progressColor, progressThickness, progressRadius, progressStyle)
|
|
61
71
|
}), scrollable ? /*#__PURE__*/_jsx(ScrollView, {
|
|
62
72
|
contentContainerStyle: {
|
|
63
|
-
...
|
|
73
|
+
...contentPaddingStyle,
|
|
64
74
|
paddingTop: showProgress ? 0 : padding.top
|
|
65
75
|
},
|
|
66
76
|
children: /*#__PURE__*/_jsx(View, {
|
|
67
77
|
style: {
|
|
68
78
|
flexGrow: 1,
|
|
69
79
|
justifyContent: parseFlexAlignment(screenData.mainAxisAlignment),
|
|
70
|
-
alignItems:
|
|
80
|
+
alignItems: parseRootCrossAlignment(rootCrossAxisAlignment)
|
|
71
81
|
},
|
|
72
82
|
children: childrenData.map((child, index) => buildWidget(child, {
|
|
73
83
|
onAction,
|
|
@@ -82,10 +92,10 @@ export default function FlowboardRenderer(props) {
|
|
|
82
92
|
}) : /*#__PURE__*/_jsx(View, {
|
|
83
93
|
style: {
|
|
84
94
|
flex: 1,
|
|
85
|
-
...
|
|
95
|
+
...contentPaddingStyle,
|
|
86
96
|
paddingTop: showProgress ? 0 : padding.top,
|
|
87
97
|
justifyContent: parseFlexAlignment(screenData.mainAxisAlignment),
|
|
88
|
-
alignItems:
|
|
98
|
+
alignItems: parseRootCrossAlignment(rootCrossAxisAlignment)
|
|
89
99
|
},
|
|
90
100
|
children: childrenData.map((child, index) => buildWidget(child, {
|
|
91
101
|
onAction,
|
|
@@ -102,15 +112,18 @@ export default function FlowboardRenderer(props) {
|
|
|
102
112
|
style: styles.root,
|
|
103
113
|
children: [renderBackground(backgroundData, bgColorCode), safeArea ? /*#__PURE__*/_jsx(SafeAreaView, {
|
|
104
114
|
style: styles.safeArea,
|
|
115
|
+
mode: "padding",
|
|
116
|
+
edges: ['top', 'right', 'bottom', 'left'],
|
|
105
117
|
children: content
|
|
106
118
|
}) : content]
|
|
107
119
|
});
|
|
108
120
|
}
|
|
109
121
|
function renderBackground(bgData, legacyColorCode) {
|
|
110
122
|
if (!bgData) {
|
|
123
|
+
const fallbackBackground = parseColor(legacyColorCode);
|
|
111
124
|
return /*#__PURE__*/_jsx(View, {
|
|
112
125
|
style: [styles.background, {
|
|
113
|
-
backgroundColor:
|
|
126
|
+
backgroundColor: fallbackBackground === 'transparent' ? 'rgba(255,255,255,1)' : fallbackBackground
|
|
114
127
|
}]
|
|
115
128
|
});
|
|
116
129
|
}
|
|
@@ -136,25 +149,27 @@ function renderBackground(bgData, legacyColorCode) {
|
|
|
136
149
|
case 'image':
|
|
137
150
|
{
|
|
138
151
|
const url = bgData.url;
|
|
139
|
-
if (!url)
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
152
|
+
if (!hasImageUri(url)) {
|
|
153
|
+
return /*#__PURE__*/_jsx(View, {
|
|
154
|
+
style: [styles.background, styles.whiteBg]
|
|
155
|
+
});
|
|
156
|
+
}
|
|
157
|
+
return /*#__PURE__*/_jsx(BackgroundImageLayer, {
|
|
158
|
+
uri: String(url),
|
|
159
|
+
fit: fit
|
|
146
160
|
});
|
|
147
161
|
}
|
|
148
162
|
case 'asset':
|
|
149
163
|
{
|
|
150
164
|
const path = bgData.path;
|
|
151
|
-
if (!path)
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
165
|
+
if (!hasImageUri(path)) {
|
|
166
|
+
return /*#__PURE__*/_jsx(View, {
|
|
167
|
+
style: [styles.background, styles.whiteBg]
|
|
168
|
+
});
|
|
169
|
+
}
|
|
170
|
+
return /*#__PURE__*/_jsx(BackgroundImageLayer, {
|
|
171
|
+
uri: String(path),
|
|
172
|
+
fit: fit
|
|
158
173
|
});
|
|
159
174
|
}
|
|
160
175
|
case 'color':
|
|
@@ -175,6 +190,47 @@ function renderBackground(bgData, legacyColorCode) {
|
|
|
175
190
|
}]
|
|
176
191
|
});
|
|
177
192
|
}
|
|
193
|
+
function BackgroundImageLayer({
|
|
194
|
+
uri,
|
|
195
|
+
fit
|
|
196
|
+
}) {
|
|
197
|
+
const [hasError, setHasError] = React.useState(false);
|
|
198
|
+
const opacity = React.useRef(new Animated.Value(0)).current;
|
|
199
|
+
React.useEffect(() => {
|
|
200
|
+
opacity.setValue(0);
|
|
201
|
+
}, [uri, opacity]);
|
|
202
|
+
const fadeIn = React.useCallback(() => {
|
|
203
|
+
Animated.timing(opacity, {
|
|
204
|
+
toValue: 1,
|
|
205
|
+
duration: 160,
|
|
206
|
+
useNativeDriver: true
|
|
207
|
+
}).start();
|
|
208
|
+
}, [opacity]);
|
|
209
|
+
if (hasError) {
|
|
210
|
+
return /*#__PURE__*/_jsx(View, {
|
|
211
|
+
style: [styles.background, styles.whiteBg]
|
|
212
|
+
});
|
|
213
|
+
}
|
|
214
|
+
return /*#__PURE__*/_jsx(Animated.Image, {
|
|
215
|
+
source: {
|
|
216
|
+
uri
|
|
217
|
+
},
|
|
218
|
+
style: [styles.background, {
|
|
219
|
+
opacity
|
|
220
|
+
}],
|
|
221
|
+
resizeMode: fit,
|
|
222
|
+
onLoad: fadeIn,
|
|
223
|
+
onError: () => setHasError(true)
|
|
224
|
+
});
|
|
225
|
+
}
|
|
226
|
+
function hasImageUri(value) {
|
|
227
|
+
if (typeof value !== 'string') return false;
|
|
228
|
+
return value.trim().length > 0;
|
|
229
|
+
}
|
|
230
|
+
function isNetworkLikeUri(value) {
|
|
231
|
+
if (!hasImageUri(value)) return false;
|
|
232
|
+
return /^(https?:|file:|content:|data:)/i.test(value.trim());
|
|
233
|
+
}
|
|
178
234
|
function renderProgressBar(currentIndex, totalScreens, color, thickness, radius, style) {
|
|
179
235
|
if (style === 'dotted') {
|
|
180
236
|
return /*#__PURE__*/_jsx(View, {
|
|
@@ -282,8 +338,8 @@ function buildWidget(json, params) {
|
|
|
282
338
|
case 'container':
|
|
283
339
|
{
|
|
284
340
|
const gradient = parseGradient(props.background);
|
|
285
|
-
const width = normalizeDimension(
|
|
286
|
-
const height = normalizeDimension(
|
|
341
|
+
const width = normalizeDimension(parseLayoutDimension(props.width));
|
|
342
|
+
const height = normalizeDimension(parseLayoutDimension(props.height));
|
|
287
343
|
const borderRadius = props.borderRadius ? Number(props.borderRadius) : undefined;
|
|
288
344
|
const borderColor = props.borderColor ? parseColor(props.borderColor) : undefined;
|
|
289
345
|
const borderWidth = borderColor ? 1 : 0;
|
|
@@ -398,8 +454,8 @@ function buildWidget(json, params) {
|
|
|
398
454
|
{
|
|
399
455
|
const gradient = parseGradient(props.background);
|
|
400
456
|
const borderRadius = Number(props.borderRadius ?? 10);
|
|
401
|
-
const width = normalizeDimension(
|
|
402
|
-
const height = normalizeDimension(
|
|
457
|
+
const width = normalizeDimension(parseLayoutDimension(props.width)) ?? '100%';
|
|
458
|
+
const height = normalizeDimension(parseLayoutDimension(props.height)) ?? 50;
|
|
403
459
|
const label = props.label ?? 'Button';
|
|
404
460
|
const textStyle = getTextStyle({
|
|
405
461
|
...props,
|
|
@@ -469,8 +525,8 @@ function buildWidget(json, params) {
|
|
|
469
525
|
{
|
|
470
526
|
node = /*#__PURE__*/_jsx(View, {
|
|
471
527
|
style: {
|
|
472
|
-
height: normalizeDimension(
|
|
473
|
-
width: normalizeDimension(
|
|
528
|
+
height: normalizeDimension(parseLayoutDimension(props.height)),
|
|
529
|
+
width: normalizeDimension(parseLayoutDimension(props.width))
|
|
474
530
|
}
|
|
475
531
|
});
|
|
476
532
|
break;
|
|
@@ -481,15 +537,13 @@ function buildWidget(json, params) {
|
|
|
481
537
|
...params,
|
|
482
538
|
allowFlexExpansion: true
|
|
483
539
|
}) : null;
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
}
|
|
492
|
-
return expandedChild;
|
|
540
|
+
node = params.allowFlexExpansion ? /*#__PURE__*/_jsx(View, {
|
|
541
|
+
style: {
|
|
542
|
+
flex: 1
|
|
543
|
+
},
|
|
544
|
+
children: expandedChild
|
|
545
|
+
}) : expandedChild;
|
|
546
|
+
break;
|
|
493
547
|
}
|
|
494
548
|
case 'padding':
|
|
495
549
|
{
|
|
@@ -527,7 +581,48 @@ function buildWidget(json, params) {
|
|
|
527
581
|
params.onInputChange(id, value);
|
|
528
582
|
}
|
|
529
583
|
},
|
|
530
|
-
onAction:
|
|
584
|
+
onAction: (actionName, payload) => {
|
|
585
|
+
if (actionName === 'next' && params.onInputChange && id && Object.prototype.hasOwnProperty.call(payload ?? {}, 'selectedValue')) {
|
|
586
|
+
params.onInputChange(id, payload?.selectedValue);
|
|
587
|
+
}
|
|
588
|
+
params.onAction(actionName, {
|
|
589
|
+
...(payload ?? {}),
|
|
590
|
+
...(id ? {
|
|
591
|
+
fieldId: id
|
|
592
|
+
} : {})
|
|
593
|
+
});
|
|
594
|
+
},
|
|
595
|
+
buildWidget: widgetJson => buildWidget(widgetJson, {
|
|
596
|
+
...params,
|
|
597
|
+
allowFlexExpansion: true
|
|
598
|
+
})
|
|
599
|
+
});
|
|
600
|
+
break;
|
|
601
|
+
}
|
|
602
|
+
case 'wheel_picker':
|
|
603
|
+
{
|
|
604
|
+
node = /*#__PURE__*/_jsx(WheelPicker, {
|
|
605
|
+
properties: props,
|
|
606
|
+
options: Array.isArray(json.options) ? json.options : [],
|
|
607
|
+
itemTemplate: json.itemTemplate ?? props.itemTemplate,
|
|
608
|
+
formData: params.formData,
|
|
609
|
+
initialValue: params.formData[id ?? ''],
|
|
610
|
+
onChanged: value => {
|
|
611
|
+
if (params.onInputChange && id) {
|
|
612
|
+
params.onInputChange(id, value);
|
|
613
|
+
}
|
|
614
|
+
},
|
|
615
|
+
onAction: (actionName, payload) => {
|
|
616
|
+
if (actionName === 'next' && params.onInputChange && id && Object.prototype.hasOwnProperty.call(payload ?? {}, 'selectedValue')) {
|
|
617
|
+
params.onInputChange(id, payload?.selectedValue);
|
|
618
|
+
}
|
|
619
|
+
params.onAction(actionName, {
|
|
620
|
+
...(payload ?? {}),
|
|
621
|
+
...(id ? {
|
|
622
|
+
fieldId: id
|
|
623
|
+
} : {})
|
|
624
|
+
});
|
|
625
|
+
},
|
|
531
626
|
buildWidget: widgetJson => buildWidget(widgetJson, {
|
|
532
627
|
...params,
|
|
533
628
|
allowFlexExpansion: true
|
|
@@ -538,30 +633,23 @@ function buildWidget(json, params) {
|
|
|
538
633
|
case 'image':
|
|
539
634
|
{
|
|
540
635
|
const source = props.source;
|
|
541
|
-
const width = normalizeDimension(
|
|
542
|
-
const height = normalizeDimension(
|
|
636
|
+
const width = normalizeDimension(parseLayoutDimension(props.width, true));
|
|
637
|
+
const height = normalizeDimension(parseLayoutDimension(props.height, true));
|
|
543
638
|
if (source === 'asset') {
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
height
|
|
551
|
-
},
|
|
552
|
-
resizeMode: parseResizeMode(props.fit)
|
|
639
|
+
if (!hasImageUri(props.path)) return null;
|
|
640
|
+
node = /*#__PURE__*/_jsx(SduiImage, {
|
|
641
|
+
uri: String(props.path),
|
|
642
|
+
fit: parseResizeMode(props.fit),
|
|
643
|
+
width: width,
|
|
644
|
+
height: height
|
|
553
645
|
});
|
|
554
646
|
} else if (source === 'network') {
|
|
555
|
-
if (!props.url) return null;
|
|
556
|
-
node = /*#__PURE__*/_jsx(
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
width,
|
|
562
|
-
height
|
|
563
|
-
},
|
|
564
|
-
resizeMode: parseResizeMode(props.fit)
|
|
647
|
+
if (!isNetworkLikeUri(props.url)) return null;
|
|
648
|
+
node = /*#__PURE__*/_jsx(SduiImage, {
|
|
649
|
+
uri: String(props.url),
|
|
650
|
+
fit: parseResizeMode(props.fit),
|
|
651
|
+
width: width,
|
|
652
|
+
height: height
|
|
565
653
|
});
|
|
566
654
|
} else {
|
|
567
655
|
node = null;
|
|
@@ -571,8 +659,8 @@ function buildWidget(json, params) {
|
|
|
571
659
|
case 'lottie':
|
|
572
660
|
{
|
|
573
661
|
const source = props.source;
|
|
574
|
-
const width = normalizeDimension(
|
|
575
|
-
const height = normalizeDimension(
|
|
662
|
+
const width = normalizeDimension(parseLayoutDimension(props.width, true));
|
|
663
|
+
const height = normalizeDimension(parseLayoutDimension(props.height, true));
|
|
576
664
|
const loop = props.loop === true;
|
|
577
665
|
if (source === 'asset') {
|
|
578
666
|
node = /*#__PURE__*/_jsx(LottieView, {
|
|
@@ -661,7 +749,7 @@ function buildWidget(json, params) {
|
|
|
661
749
|
autoPlay: props.autoPlay === true,
|
|
662
750
|
autoPlayDelay: Number(props.autoPlayDelay ?? 3000),
|
|
663
751
|
loop: props.loop === true,
|
|
664
|
-
height:
|
|
752
|
+
height: parseLayoutDimension(props.height),
|
|
665
753
|
physics: props.physics,
|
|
666
754
|
children: (childrenJson ?? []).map((child, index) => /*#__PURE__*/_jsx(View, {
|
|
667
755
|
style: {
|
|
@@ -751,13 +839,13 @@ function buildWidget(json, params) {
|
|
|
751
839
|
}
|
|
752
840
|
case 'positioned':
|
|
753
841
|
{
|
|
754
|
-
const left =
|
|
755
|
-
const top =
|
|
756
|
-
const right =
|
|
757
|
-
const bottom =
|
|
758
|
-
const width =
|
|
759
|
-
const height =
|
|
760
|
-
|
|
842
|
+
const left = parseLayoutDimension(props.left);
|
|
843
|
+
const top = parseLayoutDimension(props.top);
|
|
844
|
+
const right = parseLayoutDimension(props.right);
|
|
845
|
+
const bottom = parseLayoutDimension(props.bottom);
|
|
846
|
+
const width = parseLayoutDimension(props.width);
|
|
847
|
+
const height = parseLayoutDimension(props.height);
|
|
848
|
+
node = /*#__PURE__*/_jsx(View, {
|
|
761
849
|
style: {
|
|
762
850
|
position: 'absolute',
|
|
763
851
|
left,
|
|
@@ -771,6 +859,7 @@ function buildWidget(json, params) {
|
|
|
771
859
|
...params
|
|
772
860
|
}) : null
|
|
773
861
|
});
|
|
862
|
+
break;
|
|
774
863
|
}
|
|
775
864
|
default:
|
|
776
865
|
node = null;
|
|
@@ -780,7 +869,7 @@ function buildWidget(json, params) {
|
|
|
780
869
|
if (marginVal !== undefined && marginVal !== null) {
|
|
781
870
|
const marginInsets = parseInsets(marginVal);
|
|
782
871
|
node = /*#__PURE__*/_jsx(View, {
|
|
783
|
-
style:
|
|
872
|
+
style: insetsToMarginStyle(marginInsets),
|
|
784
873
|
children: node
|
|
785
874
|
});
|
|
786
875
|
} else if (type !== 'container' && type !== 'padding' && props.padding) {
|
|
@@ -796,13 +885,13 @@ function buildWidget(json, params) {
|
|
|
796
885
|
shouldExpand = true;
|
|
797
886
|
}
|
|
798
887
|
if (props.height !== undefined) {
|
|
799
|
-
const heightVal =
|
|
888
|
+
const heightVal = parseLayoutDimension(props.height);
|
|
800
889
|
if (heightVal === Number.POSITIVE_INFINITY) {
|
|
801
890
|
shouldExpand = true;
|
|
802
891
|
}
|
|
803
892
|
}
|
|
804
893
|
if (shouldExpand) {
|
|
805
|
-
|
|
894
|
+
node = /*#__PURE__*/_jsx(View, {
|
|
806
895
|
style: {
|
|
807
896
|
flex: 1
|
|
808
897
|
},
|
|
@@ -810,6 +899,17 @@ function buildWidget(json, params) {
|
|
|
810
899
|
});
|
|
811
900
|
}
|
|
812
901
|
}
|
|
902
|
+
if (params.key !== undefined && params.key !== null) {
|
|
903
|
+
if (/*#__PURE__*/React.isValidElement(node)) {
|
|
904
|
+
node = /*#__PURE__*/React.cloneElement(node, {
|
|
905
|
+
key: params.key
|
|
906
|
+
});
|
|
907
|
+
} else {
|
|
908
|
+
node = /*#__PURE__*/_jsx(React.Fragment, {
|
|
909
|
+
children: node
|
|
910
|
+
}, params.key);
|
|
911
|
+
}
|
|
912
|
+
}
|
|
813
913
|
return node;
|
|
814
914
|
}
|
|
815
915
|
function buildActionPayload(props, json) {
|
|
@@ -920,6 +1020,100 @@ function normalizeDimension(value) {
|
|
|
920
1020
|
}
|
|
921
1021
|
return value;
|
|
922
1022
|
}
|
|
1023
|
+
function parseRootCrossAlignment(value) {
|
|
1024
|
+
if (!value) return 'stretch';
|
|
1025
|
+
return parseCrossAlignment(value);
|
|
1026
|
+
}
|
|
1027
|
+
function parseLayoutDimension(value, zeroIsNull = false) {
|
|
1028
|
+
if (typeof value === 'string') {
|
|
1029
|
+
const trimmed = value.trim();
|
|
1030
|
+
const lowered = trimmed.toLowerCase();
|
|
1031
|
+
if (lowered === 'double.infinity' || lowered === 'infinity' || lowered === '+infinity') {
|
|
1032
|
+
return Number.POSITIVE_INFINITY;
|
|
1033
|
+
}
|
|
1034
|
+
if (trimmed.endsWith('%')) {
|
|
1035
|
+
const numeric = Number(trimmed.slice(0, -1));
|
|
1036
|
+
if (!Number.isNaN(numeric) && Number.isFinite(numeric)) {
|
|
1037
|
+
if (zeroIsNull && numeric === 0) return undefined;
|
|
1038
|
+
return `${numeric}%`;
|
|
1039
|
+
}
|
|
1040
|
+
}
|
|
1041
|
+
}
|
|
1042
|
+
return parseDimension(value, zeroIsNull);
|
|
1043
|
+
}
|
|
1044
|
+
function SduiImage({
|
|
1045
|
+
uri,
|
|
1046
|
+
fit,
|
|
1047
|
+
width,
|
|
1048
|
+
height
|
|
1049
|
+
}) {
|
|
1050
|
+
const hasExplicitWidth = width !== undefined;
|
|
1051
|
+
const hasExplicitHeight = height !== undefined;
|
|
1052
|
+
const [aspectRatio, setAspectRatio] = React.useState(undefined);
|
|
1053
|
+
const [hasError, setHasError] = React.useState(false);
|
|
1054
|
+
const opacity = React.useRef(new Animated.Value(0)).current;
|
|
1055
|
+
const updateAspectRatio = (w, h) => {
|
|
1056
|
+
if (!w || !h) return;
|
|
1057
|
+
if (!Number.isFinite(w) || !Number.isFinite(h) || h <= 0) return;
|
|
1058
|
+
const ratio = w / h;
|
|
1059
|
+
if (Number.isFinite(ratio) && ratio > 0) {
|
|
1060
|
+
setAspectRatio(ratio);
|
|
1061
|
+
}
|
|
1062
|
+
};
|
|
1063
|
+
React.useEffect(() => {
|
|
1064
|
+
if (hasExplicitWidth && hasExplicitHeight) return;
|
|
1065
|
+
Image.getSize(uri, (w, h) => updateAspectRatio(w, h), () => null);
|
|
1066
|
+
}, [uri, hasExplicitWidth, hasExplicitHeight]);
|
|
1067
|
+
React.useEffect(() => {
|
|
1068
|
+
opacity.setValue(0);
|
|
1069
|
+
}, [uri, opacity]);
|
|
1070
|
+
if (hasError) {
|
|
1071
|
+
return null;
|
|
1072
|
+
}
|
|
1073
|
+
const style = {
|
|
1074
|
+
width,
|
|
1075
|
+
height
|
|
1076
|
+
};
|
|
1077
|
+
if (!hasExplicitWidth && !hasExplicitHeight) {
|
|
1078
|
+
style.width = '100%';
|
|
1079
|
+
if (aspectRatio) {
|
|
1080
|
+
style.aspectRatio = aspectRatio;
|
|
1081
|
+
} else {
|
|
1082
|
+
style.minHeight = 160;
|
|
1083
|
+
}
|
|
1084
|
+
} else if (hasExplicitWidth && !hasExplicitHeight) {
|
|
1085
|
+
if (aspectRatio) {
|
|
1086
|
+
style.aspectRatio = aspectRatio;
|
|
1087
|
+
} else {
|
|
1088
|
+
style.minHeight = 100;
|
|
1089
|
+
}
|
|
1090
|
+
} else if (!hasExplicitWidth && hasExplicitHeight) {
|
|
1091
|
+
if (aspectRatio) {
|
|
1092
|
+
style.aspectRatio = aspectRatio;
|
|
1093
|
+
} else {
|
|
1094
|
+
style.width = '100%';
|
|
1095
|
+
}
|
|
1096
|
+
}
|
|
1097
|
+
return /*#__PURE__*/_jsx(Animated.Image, {
|
|
1098
|
+
source: {
|
|
1099
|
+
uri
|
|
1100
|
+
},
|
|
1101
|
+
style: [style, {
|
|
1102
|
+
opacity
|
|
1103
|
+
}],
|
|
1104
|
+
resizeMode: fit,
|
|
1105
|
+
onLoad: event => {
|
|
1106
|
+
const source = event?.nativeEvent?.source;
|
|
1107
|
+
updateAspectRatio(source?.width, source?.height);
|
|
1108
|
+
Animated.timing(opacity, {
|
|
1109
|
+
toValue: 1,
|
|
1110
|
+
duration: 160,
|
|
1111
|
+
useNativeDriver: true
|
|
1112
|
+
}).start();
|
|
1113
|
+
},
|
|
1114
|
+
onError: () => setHasError(true)
|
|
1115
|
+
});
|
|
1116
|
+
}
|
|
923
1117
|
function GradientText({
|
|
924
1118
|
text,
|
|
925
1119
|
gradient,
|
|
@@ -1085,7 +1279,9 @@ function SelectionList({
|
|
|
1085
1279
|
next = [value];
|
|
1086
1280
|
onChanged(value);
|
|
1087
1281
|
if (properties.autoGoNext === true) {
|
|
1088
|
-
onAction('next'
|
|
1282
|
+
requestAnimationFrame(() => onAction('next', {
|
|
1283
|
+
selectedValue: value
|
|
1284
|
+
}));
|
|
1089
1285
|
}
|
|
1090
1286
|
}
|
|
1091
1287
|
return next;
|
|
@@ -1157,6 +1353,274 @@ function SelectionList({
|
|
|
1157
1353
|
}, `column-option-${index}`))
|
|
1158
1354
|
});
|
|
1159
1355
|
}
|
|
1356
|
+
function WheelPicker({
|
|
1357
|
+
properties,
|
|
1358
|
+
options,
|
|
1359
|
+
itemTemplate,
|
|
1360
|
+
formData,
|
|
1361
|
+
initialValue,
|
|
1362
|
+
onChanged,
|
|
1363
|
+
onAction,
|
|
1364
|
+
buildWidget
|
|
1365
|
+
}) {
|
|
1366
|
+
const multiSelect = properties.multiSelect === true;
|
|
1367
|
+
const [selectedValues, setSelectedValues] = React.useState(() => {
|
|
1368
|
+
if (initialValue === null || initialValue === undefined) return [];
|
|
1369
|
+
if (Array.isArray(initialValue)) return initialValue.map(String);
|
|
1370
|
+
if (typeof initialValue === 'string') {
|
|
1371
|
+
if (multiSelect) {
|
|
1372
|
+
try {
|
|
1373
|
+
return initialValue.replace('[', '').replace(']', '').split(',').map(val => val.trim()).filter(val => val.length > 0);
|
|
1374
|
+
} catch {
|
|
1375
|
+
return [initialValue];
|
|
1376
|
+
}
|
|
1377
|
+
}
|
|
1378
|
+
return [initialValue];
|
|
1379
|
+
}
|
|
1380
|
+
return [];
|
|
1381
|
+
});
|
|
1382
|
+
const resolvedOptions = React.useMemo(() => buildWheelPickerOptions({
|
|
1383
|
+
options,
|
|
1384
|
+
properties,
|
|
1385
|
+
formData,
|
|
1386
|
+
itemTemplate
|
|
1387
|
+
}), [formData, itemTemplate, options, properties]);
|
|
1388
|
+
const toggleSelection = value => {
|
|
1389
|
+
setSelectedValues(prev => {
|
|
1390
|
+
let next = [...prev];
|
|
1391
|
+
if (multiSelect) {
|
|
1392
|
+
if (next.includes(value)) {
|
|
1393
|
+
next = next.filter(item => item !== value);
|
|
1394
|
+
} else {
|
|
1395
|
+
next.push(value);
|
|
1396
|
+
}
|
|
1397
|
+
onChanged(next.join(','));
|
|
1398
|
+
} else {
|
|
1399
|
+
next = [value];
|
|
1400
|
+
onChanged(value);
|
|
1401
|
+
if (properties.autoGoNext === true) {
|
|
1402
|
+
requestAnimationFrame(() => onAction('next', {
|
|
1403
|
+
selectedValue: value
|
|
1404
|
+
}));
|
|
1405
|
+
}
|
|
1406
|
+
}
|
|
1407
|
+
return next;
|
|
1408
|
+
});
|
|
1409
|
+
};
|
|
1410
|
+
const layout = properties.layout ?? 'wheel';
|
|
1411
|
+
const spacing = Number(properties.spacing ?? 8);
|
|
1412
|
+
const renderItem = (option, index) => {
|
|
1413
|
+
const value = String(option.value ?? '');
|
|
1414
|
+
const isSelected = selectedValues.includes(value);
|
|
1415
|
+
const styleProps = isSelected ? option.selectedStyle ?? properties.selectedStyle ?? {
|
|
1416
|
+
backgroundColor: properties.wheelSelectedBackgroundColor,
|
|
1417
|
+
borderColor: properties.wheelSelectedBorderColor
|
|
1418
|
+
} : option.unselectedStyle ?? properties.unselectedStyle ?? {
|
|
1419
|
+
backgroundColor: properties.wheelUnselectedBackgroundColor,
|
|
1420
|
+
borderColor: properties.wheelUnselectedBorderColor
|
|
1421
|
+
};
|
|
1422
|
+
const backgroundColor = parseColor(styleProps.backgroundColor);
|
|
1423
|
+
const borderColor = parseColor(styleProps.borderColor);
|
|
1424
|
+
const borderRadius = Number(styleProps.borderRadius ?? 8);
|
|
1425
|
+
const borderWidth = Number(styleProps.borderWidth ?? 1);
|
|
1426
|
+
return /*#__PURE__*/_jsx(Pressable, {
|
|
1427
|
+
onPress: () => toggleSelection(value),
|
|
1428
|
+
style: {
|
|
1429
|
+
width: '100%',
|
|
1430
|
+
backgroundColor,
|
|
1431
|
+
borderColor,
|
|
1432
|
+
borderWidth,
|
|
1433
|
+
borderRadius,
|
|
1434
|
+
padding: 0
|
|
1435
|
+
},
|
|
1436
|
+
children: buildWidget(option.child)
|
|
1437
|
+
}, `wheel-option-${index}`);
|
|
1438
|
+
};
|
|
1439
|
+
if (layout === 'row') {
|
|
1440
|
+
return /*#__PURE__*/_jsx(View, {
|
|
1441
|
+
style: {
|
|
1442
|
+
flexDirection: 'row',
|
|
1443
|
+
flexWrap: 'wrap'
|
|
1444
|
+
},
|
|
1445
|
+
children: resolvedOptions.map((option, index) => /*#__PURE__*/_jsx(View, {
|
|
1446
|
+
style: {
|
|
1447
|
+
marginRight: spacing,
|
|
1448
|
+
marginBottom: spacing
|
|
1449
|
+
},
|
|
1450
|
+
children: renderItem(option, index)
|
|
1451
|
+
}, `wheel-row-option-${index}`))
|
|
1452
|
+
});
|
|
1453
|
+
}
|
|
1454
|
+
if (layout === 'grid') {
|
|
1455
|
+
const crossAxisCount = Number(properties.gridCrossAxisCount ?? 2);
|
|
1456
|
+
const aspectRatio = Number(properties.gridAspectRatio ?? 1);
|
|
1457
|
+
return /*#__PURE__*/_jsx(View, {
|
|
1458
|
+
style: {
|
|
1459
|
+
flexDirection: 'row',
|
|
1460
|
+
flexWrap: 'wrap'
|
|
1461
|
+
},
|
|
1462
|
+
children: resolvedOptions.map((option, index) => /*#__PURE__*/_jsx(View, {
|
|
1463
|
+
style: {
|
|
1464
|
+
width: `${100 / crossAxisCount}%`,
|
|
1465
|
+
paddingRight: (index + 1) % crossAxisCount === 0 ? 0 : spacing,
|
|
1466
|
+
paddingBottom: spacing,
|
|
1467
|
+
aspectRatio
|
|
1468
|
+
},
|
|
1469
|
+
children: renderItem(option, index)
|
|
1470
|
+
}, `wheel-grid-option-${index}`))
|
|
1471
|
+
});
|
|
1472
|
+
}
|
|
1473
|
+
if (layout === 'wheel') {
|
|
1474
|
+
const itemHeight = Math.max(1, Number(properties.wheelItemHeight ?? 48));
|
|
1475
|
+
const requestedVisible = Math.max(3, Math.floor(Number(properties.visibleItems ?? 5)));
|
|
1476
|
+
const visibleItems = requestedVisible % 2 === 0 ? requestedVisible + 1 : requestedVisible;
|
|
1477
|
+
const containerHeight = itemHeight * visibleItems;
|
|
1478
|
+
const centerPadding = Math.max(0, (containerHeight - itemHeight) / 2);
|
|
1479
|
+
return /*#__PURE__*/_jsx(View, {
|
|
1480
|
+
style: {
|
|
1481
|
+
height: containerHeight
|
|
1482
|
+
},
|
|
1483
|
+
children: /*#__PURE__*/_jsx(ScrollView, {
|
|
1484
|
+
showsVerticalScrollIndicator: false,
|
|
1485
|
+
contentContainerStyle: {
|
|
1486
|
+
paddingVertical: centerPadding
|
|
1487
|
+
},
|
|
1488
|
+
children: resolvedOptions.map((option, index) => /*#__PURE__*/_jsx(View, {
|
|
1489
|
+
style: {
|
|
1490
|
+
minHeight: itemHeight,
|
|
1491
|
+
justifyContent: 'center',
|
|
1492
|
+
marginBottom: spacing > 0 && index < resolvedOptions.length - 1 ? spacing : 0
|
|
1493
|
+
},
|
|
1494
|
+
children: renderItem(option, index)
|
|
1495
|
+
}, `wheel-column-option-${index}`))
|
|
1496
|
+
})
|
|
1497
|
+
});
|
|
1498
|
+
}
|
|
1499
|
+
return /*#__PURE__*/_jsx(View, {
|
|
1500
|
+
children: resolvedOptions.map((option, index) => /*#__PURE__*/_jsx(View, {
|
|
1501
|
+
style: {
|
|
1502
|
+
marginBottom: spacing
|
|
1503
|
+
},
|
|
1504
|
+
children: renderItem(option, index)
|
|
1505
|
+
}, `wheel-list-option-${index}`))
|
|
1506
|
+
});
|
|
1507
|
+
}
|
|
1508
|
+
function buildWheelPickerOptions({
|
|
1509
|
+
options,
|
|
1510
|
+
properties,
|
|
1511
|
+
formData,
|
|
1512
|
+
itemTemplate
|
|
1513
|
+
}) {
|
|
1514
|
+
const templateContext = buildWheelTemplateBaseContext(formData, properties);
|
|
1515
|
+
const itemCountRaw = properties.itemCount ?? properties.item_count;
|
|
1516
|
+
const itemCountResolved = resolveNumericValue(itemCountRaw, templateContext);
|
|
1517
|
+
const itemCount = normalizeItemCount(itemCountResolved);
|
|
1518
|
+
if (itemCount > 0) {
|
|
1519
|
+
const generatedTemplate = normalizeWheelOptionTemplate(itemTemplate ?? properties.itemTemplate ?? options[0]);
|
|
1520
|
+
if (!generatedTemplate) return [];
|
|
1521
|
+
const start = resolveNumericValue(properties.start ?? properties.itemStart ?? 0, templateContext) ?? 0;
|
|
1522
|
+
const step = resolveNumericValue(properties.step ?? properties.itemStep ?? 1, templateContext) ?? 1;
|
|
1523
|
+
return Array.from({
|
|
1524
|
+
length: itemCount
|
|
1525
|
+
}).map((_, index) => {
|
|
1526
|
+
const itemValue = start + step * index;
|
|
1527
|
+
const context = {
|
|
1528
|
+
...templateContext,
|
|
1529
|
+
start,
|
|
1530
|
+
step,
|
|
1531
|
+
index,
|
|
1532
|
+
item: itemValue
|
|
1533
|
+
};
|
|
1534
|
+
const resolvedOption = resolveWheelTemplateValue(generatedTemplate, context);
|
|
1535
|
+
return ensureWheelOptionShape(resolvedOption, String(itemValue));
|
|
1536
|
+
});
|
|
1537
|
+
}
|
|
1538
|
+
return options.map((option, index) => {
|
|
1539
|
+
const itemValue = option?.value ?? index;
|
|
1540
|
+
const context = {
|
|
1541
|
+
...templateContext,
|
|
1542
|
+
index,
|
|
1543
|
+
item: itemValue
|
|
1544
|
+
};
|
|
1545
|
+
const resolvedOption = resolveWheelTemplateValue(option, context);
|
|
1546
|
+
return ensureWheelOptionShape(resolvedOption, String(itemValue));
|
|
1547
|
+
});
|
|
1548
|
+
}
|
|
1549
|
+
function normalizeItemCount(value) {
|
|
1550
|
+
if (value === null || !Number.isFinite(value)) return 0;
|
|
1551
|
+
return Math.max(0, Math.floor(value));
|
|
1552
|
+
}
|
|
1553
|
+
function buildWheelTemplateBaseContext(formData, properties) {
|
|
1554
|
+
const context = {
|
|
1555
|
+
...formData
|
|
1556
|
+
};
|
|
1557
|
+
const start = resolveNumericValue(properties.start ?? properties.itemStart, context);
|
|
1558
|
+
const step = resolveNumericValue(properties.step ?? properties.itemStep, context);
|
|
1559
|
+
if (start !== null) context.start = start;
|
|
1560
|
+
if (step !== null) context.step = step;
|
|
1561
|
+
return context;
|
|
1562
|
+
}
|
|
1563
|
+
function normalizeWheelOptionTemplate(template) {
|
|
1564
|
+
if (!template || typeof template !== 'object') return null;
|
|
1565
|
+
const asMap = template;
|
|
1566
|
+
if (Object.prototype.hasOwnProperty.call(asMap, 'value') || Object.prototype.hasOwnProperty.call(asMap, 'child')) {
|
|
1567
|
+
return {
|
|
1568
|
+
...asMap
|
|
1569
|
+
};
|
|
1570
|
+
}
|
|
1571
|
+
if (typeof asMap.type === 'string') {
|
|
1572
|
+
return {
|
|
1573
|
+
value: '{{item}}',
|
|
1574
|
+
child: asMap
|
|
1575
|
+
};
|
|
1576
|
+
}
|
|
1577
|
+
return null;
|
|
1578
|
+
}
|
|
1579
|
+
function ensureWheelOptionShape(option, fallbackValue) {
|
|
1580
|
+
if (!option || typeof option !== 'object') {
|
|
1581
|
+
return {
|
|
1582
|
+
value: fallbackValue,
|
|
1583
|
+
child: {
|
|
1584
|
+
type: 'text',
|
|
1585
|
+
properties: {
|
|
1586
|
+
text: fallbackValue
|
|
1587
|
+
}
|
|
1588
|
+
}
|
|
1589
|
+
};
|
|
1590
|
+
}
|
|
1591
|
+
const normalized = {
|
|
1592
|
+
...option
|
|
1593
|
+
};
|
|
1594
|
+
const rawValue = normalized.value ?? fallbackValue;
|
|
1595
|
+
const value = String(rawValue);
|
|
1596
|
+
const child = normalized.child && typeof normalized.child === 'object' ? normalized.child : {
|
|
1597
|
+
type: 'text',
|
|
1598
|
+
properties: {
|
|
1599
|
+
text: value
|
|
1600
|
+
}
|
|
1601
|
+
};
|
|
1602
|
+
return {
|
|
1603
|
+
...normalized,
|
|
1604
|
+
value,
|
|
1605
|
+
child
|
|
1606
|
+
};
|
|
1607
|
+
}
|
|
1608
|
+
function resolveWheelTemplateValue(value, context) {
|
|
1609
|
+
if (typeof value === 'string') {
|
|
1610
|
+
return resolveText(value, context);
|
|
1611
|
+
}
|
|
1612
|
+
if (Array.isArray(value)) {
|
|
1613
|
+
return value.map(entry => resolveWheelTemplateValue(entry, context));
|
|
1614
|
+
}
|
|
1615
|
+
if (value && typeof value === 'object') {
|
|
1616
|
+
const result = {};
|
|
1617
|
+
Object.keys(value).forEach(key => {
|
|
1618
|
+
result[key] = resolveWheelTemplateValue(value[key], context);
|
|
1619
|
+
});
|
|
1620
|
+
return result;
|
|
1621
|
+
}
|
|
1622
|
+
return value;
|
|
1623
|
+
}
|
|
1160
1624
|
function FakeProgressBar({
|
|
1161
1625
|
progressColor,
|
|
1162
1626
|
backgroundColor,
|
|
@@ -1278,27 +1742,32 @@ function SliderWidget({
|
|
|
1278
1742
|
const registry = useSliderRegistry();
|
|
1279
1743
|
const pagerRef = React.useRef(null);
|
|
1280
1744
|
const pageLength = children.length;
|
|
1281
|
-
const
|
|
1745
|
+
const pageCount = pageLength;
|
|
1746
|
+
const [currentPage, setCurrentPage] = React.useState(0);
|
|
1282
1747
|
const timerRef = React.useRef(null);
|
|
1283
1748
|
React.useEffect(() => {
|
|
1284
|
-
|
|
1749
|
+
setCurrentPage(prev => clampSliderPage(prev, pageCount));
|
|
1750
|
+
}, [pageCount]);
|
|
1751
|
+
React.useEffect(() => {
|
|
1752
|
+
if (autoPlay && !linkedTo && pageLength > 0) {
|
|
1285
1753
|
timerRef.current = setInterval(() => {
|
|
1286
1754
|
setCurrentPage(prev => {
|
|
1287
|
-
|
|
1288
|
-
if (
|
|
1289
|
-
return 0;
|
|
1755
|
+
const safePrev = clampSliderPage(prev, pageCount);
|
|
1756
|
+
if (safePrev < pageLength - 1) return safePrev + 1;
|
|
1757
|
+
if (loop) return 0;
|
|
1758
|
+
return safePrev;
|
|
1290
1759
|
});
|
|
1291
1760
|
}, autoPlayDelay);
|
|
1292
1761
|
}
|
|
1293
1762
|
return () => {
|
|
1294
1763
|
if (timerRef.current) clearInterval(timerRef.current);
|
|
1295
1764
|
};
|
|
1296
|
-
}, [autoPlay, autoPlayDelay, loop,
|
|
1765
|
+
}, [autoPlay, autoPlayDelay, linkedTo, loop, pageCount, pageLength]);
|
|
1297
1766
|
React.useEffect(() => {
|
|
1298
|
-
if (pagerRef.current) {
|
|
1299
|
-
pagerRef.current.setPage(currentPage);
|
|
1767
|
+
if (pagerRef.current && pageCount > 0) {
|
|
1768
|
+
pagerRef.current.setPage(clampSliderPage(currentPage, pageCount));
|
|
1300
1769
|
}
|
|
1301
|
-
}, [currentPage]);
|
|
1770
|
+
}, [currentPage, pageCount]);
|
|
1302
1771
|
React.useEffect(() => {
|
|
1303
1772
|
if (registry && id && children.length > 0) {
|
|
1304
1773
|
registry.update(id, currentPage % children.length, children.length);
|
|
@@ -1307,26 +1776,31 @@ function SliderWidget({
|
|
|
1307
1776
|
React.useEffect(() => {
|
|
1308
1777
|
if (!linkedTo || !registry) return;
|
|
1309
1778
|
return registry.getNotifier(linkedTo, value => {
|
|
1779
|
+
const normalized = normalizeLoopIndex(value, pageLength);
|
|
1780
|
+
const targetPage = clampSliderPage(normalized, pageCount);
|
|
1781
|
+
setCurrentPage(targetPage);
|
|
1310
1782
|
if (pagerRef.current) {
|
|
1311
|
-
pagerRef.current.setPage(
|
|
1783
|
+
pagerRef.current.setPage(targetPage);
|
|
1312
1784
|
}
|
|
1313
1785
|
});
|
|
1314
|
-
}, [linkedTo, registry]);
|
|
1786
|
+
}, [linkedTo, pageCount, pageLength, registry]);
|
|
1315
1787
|
if (pageLength === 0) return null;
|
|
1316
|
-
const pageCount = loop ? pageLength * 1000 : pageLength;
|
|
1317
1788
|
return /*#__PURE__*/_jsx(PagerView, {
|
|
1318
1789
|
ref: pagerRef,
|
|
1319
|
-
style: {
|
|
1320
|
-
|
|
1790
|
+
style: height === undefined ? {
|
|
1791
|
+
flex: 1
|
|
1792
|
+
} : {
|
|
1793
|
+
height
|
|
1321
1794
|
},
|
|
1322
1795
|
orientation: direction,
|
|
1323
1796
|
scrollEnabled: physics !== 'never',
|
|
1324
|
-
initialPage: currentPage,
|
|
1797
|
+
initialPage: clampSliderPage(currentPage, pageCount),
|
|
1325
1798
|
onPageSelected: event => {
|
|
1326
1799
|
const page = event.nativeEvent.position;
|
|
1327
|
-
|
|
1800
|
+
const safePage = clampSliderPage(page, pageCount);
|
|
1801
|
+
setCurrentPage(safePage);
|
|
1328
1802
|
if (registry && id) {
|
|
1329
|
-
registry.update(id,
|
|
1803
|
+
registry.update(id, safePage % pageLength, pageLength);
|
|
1330
1804
|
}
|
|
1331
1805
|
},
|
|
1332
1806
|
children: Array.from({
|
|
@@ -1339,6 +1813,18 @@ function SliderWidget({
|
|
|
1339
1813
|
}, `slider-page-${index}`))
|
|
1340
1814
|
});
|
|
1341
1815
|
}
|
|
1816
|
+
function clampSliderPage(page, pageCount) {
|
|
1817
|
+
if (pageCount <= 0) return 0;
|
|
1818
|
+
if (!Number.isFinite(page)) return 0;
|
|
1819
|
+
if (page < 0) return 0;
|
|
1820
|
+
if (page >= pageCount) return pageCount - 1;
|
|
1821
|
+
return Math.floor(page);
|
|
1822
|
+
}
|
|
1823
|
+
function normalizeLoopIndex(index, size) {
|
|
1824
|
+
if (size <= 0 || !Number.isFinite(index)) return 0;
|
|
1825
|
+
const normalized = Math.floor(index) % size;
|
|
1826
|
+
return normalized < 0 ? normalized + size : normalized;
|
|
1827
|
+
}
|
|
1342
1828
|
function PageViewIndicator({
|
|
1343
1829
|
linkedTo,
|
|
1344
1830
|
activeColor,
|
|
@@ -1383,15 +1869,34 @@ function RadarChart({
|
|
|
1383
1869
|
}) {
|
|
1384
1870
|
const animate = properties.animate !== false;
|
|
1385
1871
|
const animationDurationMs = Number(properties.animationDurationMs ?? 800);
|
|
1386
|
-
const
|
|
1872
|
+
const reveal = useMemo(() => new Animated.Value(animate ? 0 : 1), [animate]);
|
|
1873
|
+
const [lineProgress, setLineProgress] = React.useState(animate ? 0 : 1);
|
|
1387
1874
|
React.useEffect(() => {
|
|
1388
|
-
if (!animate)
|
|
1389
|
-
|
|
1875
|
+
if (!animate) {
|
|
1876
|
+
reveal.setValue(1);
|
|
1877
|
+
setLineProgress(1);
|
|
1878
|
+
return;
|
|
1879
|
+
}
|
|
1880
|
+
reveal.setValue(0);
|
|
1881
|
+
setLineProgress(0);
|
|
1882
|
+
const listenerId = reveal.addListener(({
|
|
1883
|
+
value
|
|
1884
|
+
}) => {
|
|
1885
|
+
setLineProgress(Math.max(0, Math.min(1, value)));
|
|
1886
|
+
});
|
|
1887
|
+
Animated.timing(reveal, {
|
|
1390
1888
|
toValue: 1,
|
|
1391
1889
|
duration: animationDurationMs,
|
|
1392
|
-
useNativeDriver:
|
|
1890
|
+
useNativeDriver: false
|
|
1393
1891
|
}).start();
|
|
1394
|
-
|
|
1892
|
+
return () => {
|
|
1893
|
+
reveal.removeListener(listenerId);
|
|
1894
|
+
};
|
|
1895
|
+
}, [animate, animationDurationMs, reveal]);
|
|
1896
|
+
const parsedWidth = parseLayoutDimension(properties.width);
|
|
1897
|
+
const normalizedWidth = normalizeDimension(parsedWidth);
|
|
1898
|
+
const [measuredWidth, setMeasuredWidth] = React.useState(0);
|
|
1899
|
+
const width = resolveRadarWidth(parsedWidth, measuredWidth);
|
|
1395
1900
|
const axes = parseRadarAxes(properties.axes);
|
|
1396
1901
|
if (axes.length < 3) {
|
|
1397
1902
|
return radarPlaceholder('Radar chart requires at least 3 axes.');
|
|
@@ -1402,9 +1907,8 @@ function RadarChart({
|
|
|
1402
1907
|
}
|
|
1403
1908
|
const normalizedAxes = normalizeAxisMaximums(axes, datasets);
|
|
1404
1909
|
const backgroundColor = parseColor(properties.backgroundColor);
|
|
1405
|
-
const
|
|
1406
|
-
const
|
|
1407
|
-
const height = rawHeight === undefined || rawHeight === Number.POSITIVE_INFINITY ? 300 : rawHeight;
|
|
1910
|
+
const rawHeight = parseLayoutDimension(properties.height);
|
|
1911
|
+
const height = typeof rawHeight === 'number' && Number.isFinite(rawHeight) && rawHeight > 0 ? rawHeight : 300;
|
|
1408
1912
|
const padding = parseInsets(properties.padding);
|
|
1409
1913
|
const gridLevels = Math.min(Math.max(Number(properties.gridLevels ?? 5), 1), 12);
|
|
1410
1914
|
const gridColor = parseColor(properties.gridColor ?? '0xFFE0E0E0');
|
|
@@ -1418,7 +1922,7 @@ function RadarChart({
|
|
|
1418
1922
|
const legendStyle = getTextStyle(properties.legendStyle ?? {}, 'color', '0xFF212121');
|
|
1419
1923
|
const shape = (properties.shape ?? 'polygon').toLowerCase();
|
|
1420
1924
|
const chartSize = Math.min(width, height);
|
|
1421
|
-
const radius = chartSize / 2 - Math.max(padding.left, padding.right, padding.top, padding.bottom);
|
|
1925
|
+
const radius = Math.max(chartSize / 2 - Math.max(padding.left, padding.right, padding.top, padding.bottom), 8);
|
|
1422
1926
|
const center = {
|
|
1423
1927
|
x: width / 2,
|
|
1424
1928
|
y: height / 2
|
|
@@ -1442,7 +1946,7 @@ function RadarChart({
|
|
|
1442
1946
|
const points = dataset.values.map((value, index) => {
|
|
1443
1947
|
const axis = normalizedAxes[index];
|
|
1444
1948
|
const maxValue = axis?.maxValue ?? 0;
|
|
1445
|
-
const ratio = maxValue > 0 ? value / maxValue : 0;
|
|
1949
|
+
const ratio = (maxValue > 0 ? value / maxValue : 0) * lineProgress;
|
|
1446
1950
|
const angle = index * axisAngle - Math.PI / 2;
|
|
1447
1951
|
const x = center.x + radius * ratio * Math.cos(angle);
|
|
1448
1952
|
const y = center.y + radius * ratio * Math.sin(angle);
|
|
@@ -1481,15 +1985,19 @@ function RadarChart({
|
|
|
1481
1985
|
});
|
|
1482
1986
|
const chart = /*#__PURE__*/_jsx(Animated.View, {
|
|
1483
1987
|
style: {
|
|
1484
|
-
width,
|
|
1988
|
+
width: normalizedWidth ?? width,
|
|
1485
1989
|
height,
|
|
1486
1990
|
paddingLeft: padding.left,
|
|
1487
1991
|
paddingRight: padding.right,
|
|
1488
1992
|
paddingTop: padding.top,
|
|
1489
1993
|
paddingBottom: padding.bottom,
|
|
1490
|
-
|
|
1491
|
-
|
|
1492
|
-
|
|
1994
|
+
opacity: reveal
|
|
1995
|
+
},
|
|
1996
|
+
onLayout: event => {
|
|
1997
|
+
const nextWidth = Math.floor(event.nativeEvent.layout.width);
|
|
1998
|
+
if (nextWidth > 0 && nextWidth !== measuredWidth) {
|
|
1999
|
+
setMeasuredWidth(nextWidth);
|
|
2000
|
+
}
|
|
1493
2001
|
},
|
|
1494
2002
|
children: /*#__PURE__*/_jsx(Svg, {
|
|
1495
2003
|
width: width,
|
|
@@ -1662,6 +2170,15 @@ export function normalizeAxisMaximums(axes, datasets) {
|
|
|
1662
2170
|
};
|
|
1663
2171
|
});
|
|
1664
2172
|
}
|
|
2173
|
+
function resolveRadarWidth(parsedWidth, measuredWidth) {
|
|
2174
|
+
if (typeof parsedWidth === 'number' && Number.isFinite(parsedWidth)) {
|
|
2175
|
+
return parsedWidth;
|
|
2176
|
+
}
|
|
2177
|
+
if (measuredWidth > 0) {
|
|
2178
|
+
return measuredWidth;
|
|
2179
|
+
}
|
|
2180
|
+
return 300;
|
|
2181
|
+
}
|
|
1665
2182
|
function radarPlaceholder(message) {
|
|
1666
2183
|
return /*#__PURE__*/_jsx(View, {
|
|
1667
2184
|
style: {
|