flowboard-react 0.4.3 → 0.5.1
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 +32 -7
- package/lib/module/components/FlowboardFlow.js +47 -30
- package/lib/module/components/FlowboardFlow.js.map +1 -1
- package/lib/module/components/FlowboardRenderer.js +1174 -160
- package/lib/module/components/FlowboardRenderer.js.map +1 -1
- package/lib/module/utils/flowboardUtils.js +3 -0
- package/lib/module/utils/flowboardUtils.js.map +1 -1
- package/lib/typescript/src/components/FlowboardFlow.d.ts.map +1 -1
- package/lib/typescript/src/components/FlowboardRenderer.d.ts +33 -1
- package/lib/typescript/src/components/FlowboardRenderer.d.ts.map +1 -1
- package/lib/typescript/src/types/flowboard.d.ts +1 -0
- package/lib/typescript/src/types/flowboard.d.ts.map +1 -1
- package/lib/typescript/src/utils/flowboardUtils.d.ts.map +1 -1
- package/package.json +1 -1
- package/src/components/FlowboardFlow.tsx +69 -40
- package/src/components/FlowboardRenderer.tsx +1742 -224
- package/src/types/flowboard.ts +1 -0
- package/src/utils/flowboardUtils.ts +4 -0
- package/lib/typescript/package.json +0 -1
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
|
|
3
3
|
import React, { useMemo } from 'react';
|
|
4
|
-
import { Animated, Pressable, ScrollView, StyleSheet, Text, TextInput, View, Image, Vibration } from 'react-native';
|
|
5
|
-
import {
|
|
4
|
+
import { Animated, KeyboardAvoidingView, Platform, Pressable, ScrollView, StyleSheet, Text, TextInput, View, Image, Vibration } from 'react-native';
|
|
5
|
+
import { useSafeAreaInsets } from 'react-native-safe-area-context';
|
|
6
6
|
import MaskedView from '@react-native-masked-view/masked-view';
|
|
7
7
|
import LinearGradient from 'react-native-linear-gradient';
|
|
8
8
|
import LottieView from 'lottie-react-native';
|
|
@@ -24,13 +24,30 @@ const styles = StyleSheet.create({
|
|
|
24
24
|
whiteBg: {
|
|
25
25
|
backgroundColor: '#ffffff'
|
|
26
26
|
},
|
|
27
|
-
|
|
27
|
+
contentLayer: {
|
|
28
28
|
flex: 1
|
|
29
29
|
},
|
|
30
30
|
progressWrapper: {
|
|
31
31
|
width: '100%'
|
|
32
32
|
}
|
|
33
33
|
});
|
|
34
|
+
function resolvePageScrollAxis(screenData) {
|
|
35
|
+
const raw = String(screenData.pageScroll ?? screenData.scrollDirection ?? (screenData.scrollable === true ? 'vertical' : 'none')).toLowerCase();
|
|
36
|
+
if (raw === 'none') return 'none';
|
|
37
|
+
return 'vertical';
|
|
38
|
+
}
|
|
39
|
+
function getAxisBoundsForPageScroll(pageScroll) {
|
|
40
|
+
if (pageScroll === 'vertical') {
|
|
41
|
+
return {
|
|
42
|
+
widthBounded: true,
|
|
43
|
+
heightBounded: false
|
|
44
|
+
};
|
|
45
|
+
}
|
|
46
|
+
return {
|
|
47
|
+
widthBounded: true,
|
|
48
|
+
heightBounded: true
|
|
49
|
+
};
|
|
50
|
+
}
|
|
34
51
|
export default function FlowboardRenderer(props) {
|
|
35
52
|
const {
|
|
36
53
|
screenData,
|
|
@@ -49,8 +66,12 @@ export default function FlowboardRenderer(props) {
|
|
|
49
66
|
const progressThickness = Number(screenData.progressThickness ?? 4);
|
|
50
67
|
const progressRadius = Number(screenData.progressRadius ?? 0);
|
|
51
68
|
const progressStyle = screenData.progressStyle ?? 'linear';
|
|
52
|
-
const
|
|
69
|
+
const pageScroll = resolvePageScrollAxis(screenData);
|
|
70
|
+
const pageAxisBounds = getAxisBoundsForPageScroll(pageScroll);
|
|
71
|
+
const scrollable = pageScroll !== 'none';
|
|
53
72
|
const safeArea = screenData.safeArea !== false;
|
|
73
|
+
const safeAreaInsets = useSafeAreaInsets();
|
|
74
|
+
const rootAxis = 'vertical';
|
|
54
75
|
const padding = parseInsets(screenData.padding);
|
|
55
76
|
const contentPaddingStyle = insetsToStyle(padding);
|
|
56
77
|
const progressPaddingStyle = {
|
|
@@ -59,6 +80,12 @@ export default function FlowboardRenderer(props) {
|
|
|
59
80
|
paddingLeft: padding.left
|
|
60
81
|
};
|
|
61
82
|
const rootCrossAxisAlignment = screenData.crossAxisAlignment ?? screenData.crossAxis;
|
|
83
|
+
const safeAreaPaddingStyle = safeArea ? {
|
|
84
|
+
paddingTop: safeAreaInsets.top,
|
|
85
|
+
paddingRight: safeAreaInsets.right,
|
|
86
|
+
paddingBottom: safeAreaInsets.bottom,
|
|
87
|
+
paddingLeft: safeAreaInsets.left
|
|
88
|
+
} : null;
|
|
62
89
|
const content = /*#__PURE__*/_jsxs(View, {
|
|
63
90
|
style: {
|
|
64
91
|
flex: 1
|
|
@@ -69,6 +96,9 @@ export default function FlowboardRenderer(props) {
|
|
|
69
96
|
}],
|
|
70
97
|
children: renderProgressBar(currentIndex, totalScreens, progressColor, progressThickness, progressRadius, progressStyle)
|
|
71
98
|
}), scrollable ? /*#__PURE__*/_jsx(ScrollView, {
|
|
99
|
+
horizontal: false,
|
|
100
|
+
keyboardShouldPersistTaps: "handled",
|
|
101
|
+
keyboardDismissMode: Platform.OS === 'ios' ? 'interactive' : 'on-drag',
|
|
72
102
|
contentContainerStyle: {
|
|
73
103
|
...contentPaddingStyle,
|
|
74
104
|
paddingTop: showProgress ? 0 : padding.top
|
|
@@ -76,14 +106,20 @@ export default function FlowboardRenderer(props) {
|
|
|
76
106
|
children: /*#__PURE__*/_jsx(View, {
|
|
77
107
|
style: {
|
|
78
108
|
flexGrow: 1,
|
|
109
|
+
flexDirection: 'column',
|
|
79
110
|
justifyContent: parseFlexAlignment(screenData.mainAxisAlignment),
|
|
80
|
-
alignItems: parseRootCrossAlignment(rootCrossAxisAlignment)
|
|
111
|
+
alignItems: parseRootCrossAlignment(rootCrossAxisAlignment),
|
|
112
|
+
minHeight: 0,
|
|
113
|
+
minWidth: 0
|
|
81
114
|
},
|
|
82
115
|
children: childrenData.map((child, index) => buildWidget(child, {
|
|
83
116
|
onAction,
|
|
84
117
|
formData,
|
|
85
118
|
onInputChange,
|
|
86
119
|
allowFlexExpansion: true,
|
|
120
|
+
parentFlexAxis: rootAxis,
|
|
121
|
+
parentMainAxisBounded: pageAxisBounds.heightBounded,
|
|
122
|
+
pageAxisBounds,
|
|
87
123
|
screenData,
|
|
88
124
|
enableFontAwesomeIcons,
|
|
89
125
|
key: `child-${index}`
|
|
@@ -102,6 +138,9 @@ export default function FlowboardRenderer(props) {
|
|
|
102
138
|
formData,
|
|
103
139
|
onInputChange,
|
|
104
140
|
allowFlexExpansion: true,
|
|
141
|
+
parentFlexAxis: rootAxis,
|
|
142
|
+
parentMainAxisBounded: true,
|
|
143
|
+
pageAxisBounds,
|
|
105
144
|
screenData,
|
|
106
145
|
enableFontAwesomeIcons,
|
|
107
146
|
key: `child-${index}`
|
|
@@ -110,12 +149,15 @@ export default function FlowboardRenderer(props) {
|
|
|
110
149
|
});
|
|
111
150
|
return /*#__PURE__*/_jsxs(View, {
|
|
112
151
|
style: styles.root,
|
|
113
|
-
children: [renderBackground(backgroundData, bgColorCode),
|
|
114
|
-
style: styles.
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
children:
|
|
118
|
-
|
|
152
|
+
children: [renderBackground(backgroundData, bgColorCode), /*#__PURE__*/_jsx(KeyboardAvoidingView, {
|
|
153
|
+
style: styles.contentLayer,
|
|
154
|
+
behavior: Platform.OS === 'ios' ? 'padding' : Platform.OS === 'android' ? 'height' : undefined,
|
|
155
|
+
keyboardVerticalOffset: 0,
|
|
156
|
+
children: /*#__PURE__*/_jsx(View, {
|
|
157
|
+
style: [styles.contentLayer, safeAreaPaddingStyle],
|
|
158
|
+
children: content
|
|
159
|
+
})
|
|
160
|
+
})]
|
|
119
161
|
});
|
|
120
162
|
}
|
|
121
163
|
function renderBackground(bgData, legacyColorCode) {
|
|
@@ -272,17 +314,411 @@ function renderProgressBar(currentIndex, totalScreens, color, thickness, radius,
|
|
|
272
314
|
})
|
|
273
315
|
});
|
|
274
316
|
}
|
|
317
|
+
const STACK_SPACE_DISTRIBUTIONS = new Set(['spaceBetween', 'spaceAround', 'spaceEvenly']);
|
|
318
|
+
function normalizeStackSpacingConfig(value) {
|
|
319
|
+
if (typeof value === 'number' && Number.isFinite(value)) {
|
|
320
|
+
return {
|
|
321
|
+
x: value,
|
|
322
|
+
y: value
|
|
323
|
+
};
|
|
324
|
+
}
|
|
325
|
+
if (!value || typeof value !== 'object') {
|
|
326
|
+
return {
|
|
327
|
+
x: 0,
|
|
328
|
+
y: 0
|
|
329
|
+
};
|
|
330
|
+
}
|
|
331
|
+
const x = Number(value.x ?? value.horizontal);
|
|
332
|
+
const y = Number(value.y ?? value.vertical);
|
|
333
|
+
const top = Number(value.top);
|
|
334
|
+
const right = Number(value.right);
|
|
335
|
+
const bottom = Number(value.bottom);
|
|
336
|
+
const left = Number(value.left);
|
|
337
|
+
const normalized = {};
|
|
338
|
+
if (!Number.isNaN(x)) normalized.x = x;
|
|
339
|
+
if (!Number.isNaN(y)) normalized.y = y;
|
|
340
|
+
if (!Number.isNaN(top)) normalized.top = top;
|
|
341
|
+
if (!Number.isNaN(right)) normalized.right = right;
|
|
342
|
+
if (!Number.isNaN(bottom)) normalized.bottom = bottom;
|
|
343
|
+
if (!Number.isNaN(left)) normalized.left = left;
|
|
344
|
+
if (Object.keys(normalized).length === 0) {
|
|
345
|
+
return {
|
|
346
|
+
x: 0,
|
|
347
|
+
y: 0
|
|
348
|
+
};
|
|
349
|
+
}
|
|
350
|
+
return normalized;
|
|
351
|
+
}
|
|
352
|
+
function stackSpacingToInsets(value) {
|
|
353
|
+
const normalized = normalizeStackSpacingConfig(value);
|
|
354
|
+
return parseInsets({
|
|
355
|
+
horizontal: normalized.x,
|
|
356
|
+
vertical: normalized.y,
|
|
357
|
+
top: normalized.top,
|
|
358
|
+
right: normalized.right,
|
|
359
|
+
bottom: normalized.bottom,
|
|
360
|
+
left: normalized.left
|
|
361
|
+
});
|
|
362
|
+
}
|
|
363
|
+
function isLegacyOverlayAlignment(value) {
|
|
364
|
+
return ['topLeft', 'topCenter', 'topRight', 'centerLeft', 'center', 'centerRight', 'bottomLeft', 'bottomCenter', 'bottomRight'].includes(String(value ?? ''));
|
|
365
|
+
}
|
|
366
|
+
function normalizeStackAxis(type, props) {
|
|
367
|
+
const explicit = String(props.axis ?? '').toLowerCase();
|
|
368
|
+
if (explicit === 'vertical' || explicit === 'horizontal' || explicit === 'overlay') {
|
|
369
|
+
return explicit;
|
|
370
|
+
}
|
|
371
|
+
if (type === 'container') return 'overlay';
|
|
372
|
+
if (type === 'row') return 'horizontal';
|
|
373
|
+
if (type === 'column') return 'vertical';
|
|
374
|
+
if (type === 'layout') {
|
|
375
|
+
return props.direction === 'horizontal' ? 'horizontal' : 'vertical';
|
|
376
|
+
}
|
|
377
|
+
if (type === 'stack' && (props.fit !== undefined || isLegacyOverlayAlignment(props.alignment))) {
|
|
378
|
+
return 'overlay';
|
|
379
|
+
}
|
|
380
|
+
return 'vertical';
|
|
381
|
+
}
|
|
382
|
+
function normalizeStackDistribution(value) {
|
|
383
|
+
const normalized = String(value ?? '').trim();
|
|
384
|
+
if (normalized === 'center' || normalized === 'end' || normalized === 'spaceBetween' || normalized === 'spaceAround' || normalized === 'spaceEvenly') {
|
|
385
|
+
return normalized;
|
|
386
|
+
}
|
|
387
|
+
return 'start';
|
|
388
|
+
}
|
|
389
|
+
function normalizeStackCrossAlignment(value) {
|
|
390
|
+
const raw = String(value ?? '').toLowerCase();
|
|
391
|
+
if (raw === 'center' || raw.includes('center')) return 'center';
|
|
392
|
+
if (raw === 'end' || raw.endsWith('right') || raw.startsWith('bottom')) return 'end';
|
|
393
|
+
return 'start';
|
|
394
|
+
}
|
|
395
|
+
function normalizeOverlayAlignment(value) {
|
|
396
|
+
const raw = String(value ?? '').trim();
|
|
397
|
+
switch (raw) {
|
|
398
|
+
case 'topStart':
|
|
399
|
+
case 'top':
|
|
400
|
+
case 'topEnd':
|
|
401
|
+
case 'start':
|
|
402
|
+
case 'center':
|
|
403
|
+
case 'end':
|
|
404
|
+
case 'bottomStart':
|
|
405
|
+
case 'bottom':
|
|
406
|
+
case 'bottomEnd':
|
|
407
|
+
return raw;
|
|
408
|
+
case 'topLeft':
|
|
409
|
+
return 'topStart';
|
|
410
|
+
case 'topCenter':
|
|
411
|
+
return 'top';
|
|
412
|
+
case 'topRight':
|
|
413
|
+
return 'topEnd';
|
|
414
|
+
case 'centerLeft':
|
|
415
|
+
return 'start';
|
|
416
|
+
case 'centerRight':
|
|
417
|
+
return 'end';
|
|
418
|
+
case 'bottomLeft':
|
|
419
|
+
return 'bottomStart';
|
|
420
|
+
case 'bottomCenter':
|
|
421
|
+
return 'bottom';
|
|
422
|
+
case 'bottomRight':
|
|
423
|
+
return 'bottomEnd';
|
|
424
|
+
default:
|
|
425
|
+
return 'center';
|
|
426
|
+
}
|
|
427
|
+
}
|
|
428
|
+
function isFillDimensionValue(value) {
|
|
429
|
+
if (typeof value === 'number') return value === Number.POSITIVE_INFINITY;
|
|
430
|
+
if (typeof value !== 'string') return false;
|
|
431
|
+
const normalized = value.trim().toLowerCase();
|
|
432
|
+
return normalized === 'infinity' || normalized === 'double.infinity' || normalized === '+infinity' || normalized === '100%';
|
|
433
|
+
}
|
|
434
|
+
function isFixedDimensionValue(value) {
|
|
435
|
+
if (isFillDimensionValue(value)) return false;
|
|
436
|
+
const parsed = Number(value);
|
|
437
|
+
return Number.isFinite(parsed);
|
|
438
|
+
}
|
|
439
|
+
function normalizeStackSize(props, axis) {
|
|
440
|
+
const widthMode = String(props.size?.width ?? '').toLowerCase();
|
|
441
|
+
const heightMode = String(props.size?.height ?? '').toLowerCase();
|
|
442
|
+
const width = widthMode === 'fill' || widthMode === 'fit' || widthMode === 'fixed' ? widthMode : props.fit === 'expand' || props.width === 'infinity' ? 'fill' : isFixedDimensionValue(props.width) ? 'fixed' : axis === 'horizontal' ? 'fit' : 'fill';
|
|
443
|
+
const height = heightMode === 'fill' || heightMode === 'fit' || heightMode === 'fixed' ? heightMode : props.fit === 'expand' || props.height === 'infinity' ? 'fill' : isFixedDimensionValue(props.height) ? 'fixed' : axis === 'vertical' ? 'fit' : 'fill';
|
|
444
|
+
return {
|
|
445
|
+
width,
|
|
446
|
+
height
|
|
447
|
+
};
|
|
448
|
+
}
|
|
449
|
+
function normalizeStackFill(props) {
|
|
450
|
+
if (props.fill && typeof props.fill === 'object') {
|
|
451
|
+
const next = {
|
|
452
|
+
...props.fill
|
|
453
|
+
};
|
|
454
|
+
if (!next.type && next.color) next.type = 'solid';
|
|
455
|
+
return next;
|
|
456
|
+
}
|
|
457
|
+
if (props.background && typeof props.background === 'object') {
|
|
458
|
+
if (props.background.type === 'color') {
|
|
459
|
+
return {
|
|
460
|
+
type: 'solid',
|
|
461
|
+
color: props.background.color
|
|
462
|
+
};
|
|
463
|
+
}
|
|
464
|
+
return {
|
|
465
|
+
...props.background
|
|
466
|
+
};
|
|
467
|
+
}
|
|
468
|
+
if (props.backgroundColor) {
|
|
469
|
+
return {
|
|
470
|
+
type: 'solid',
|
|
471
|
+
color: props.backgroundColor
|
|
472
|
+
};
|
|
473
|
+
}
|
|
474
|
+
return undefined;
|
|
475
|
+
}
|
|
476
|
+
function normalizeStackBorder(props) {
|
|
477
|
+
if (props.border && typeof props.border === 'object') {
|
|
478
|
+
return {
|
|
479
|
+
...props.border
|
|
480
|
+
};
|
|
481
|
+
}
|
|
482
|
+
if (props.borderColor || props.borderWidth !== undefined) {
|
|
483
|
+
return {
|
|
484
|
+
width: Number(props.borderWidth ?? 1),
|
|
485
|
+
color: props.borderColor
|
|
486
|
+
};
|
|
487
|
+
}
|
|
488
|
+
return undefined;
|
|
489
|
+
}
|
|
490
|
+
function normalizeStackShadow(props) {
|
|
491
|
+
if (props.shadow && typeof props.shadow === 'object') {
|
|
492
|
+
return {
|
|
493
|
+
...props.shadow
|
|
494
|
+
};
|
|
495
|
+
}
|
|
496
|
+
return undefined;
|
|
497
|
+
}
|
|
498
|
+
function spacingToInsets(value) {
|
|
499
|
+
const normalized = normalizeStackSpacingConfig(value);
|
|
500
|
+
const x = normalized.x ?? 0;
|
|
501
|
+
const y = normalized.y ?? 0;
|
|
502
|
+
return {
|
|
503
|
+
top: normalized.top ?? y,
|
|
504
|
+
right: normalized.right ?? x,
|
|
505
|
+
bottom: normalized.bottom ?? y,
|
|
506
|
+
left: normalized.left ?? x
|
|
507
|
+
};
|
|
508
|
+
}
|
|
509
|
+
function insetsToSpacing(insets) {
|
|
510
|
+
if (insets.top === insets.bottom && insets.left === insets.right) {
|
|
511
|
+
return {
|
|
512
|
+
x: insets.left,
|
|
513
|
+
y: insets.top
|
|
514
|
+
};
|
|
515
|
+
}
|
|
516
|
+
return insets;
|
|
517
|
+
}
|
|
518
|
+
function mergeStackProps(parentProps, childProps) {
|
|
519
|
+
const parentAxis = String(parentProps.axis ?? 'vertical');
|
|
520
|
+
const childAxis = String(childProps.axis ?? 'vertical');
|
|
521
|
+
let axis = childAxis;
|
|
522
|
+
if (parentAxis !== childAxis) {
|
|
523
|
+
if (parentAxis === 'overlay' && childAxis !== 'overlay') axis = childAxis;else if (childAxis === 'overlay' && parentAxis !== 'overlay') axis = parentAxis;
|
|
524
|
+
}
|
|
525
|
+
const axisSource = axis === parentAxis && axis !== childAxis ? parentProps : childProps;
|
|
526
|
+
const fallbackAxisSource = axisSource === childProps ? parentProps : childProps;
|
|
527
|
+
const parentPadding = spacingToInsets(parentProps.layout?.padding);
|
|
528
|
+
const childPadding = spacingToInsets(childProps.layout?.padding);
|
|
529
|
+
const parentMargin = spacingToInsets(parentProps.layout?.margin);
|
|
530
|
+
const childMargin = spacingToInsets(childProps.layout?.margin);
|
|
531
|
+
const mergedProps = {
|
|
532
|
+
axis,
|
|
533
|
+
alignment: axisSource.alignment ?? fallbackAxisSource.alignment ?? 'start',
|
|
534
|
+
distribution: axisSource.distribution ?? fallbackAxisSource.distribution ?? 'start',
|
|
535
|
+
childSpacing: Number(axisSource.childSpacing ?? fallbackAxisSource.childSpacing ?? 0) || 0,
|
|
536
|
+
size: {
|
|
537
|
+
width: childProps.size?.width ?? parentProps.size?.width ?? 'fill',
|
|
538
|
+
height: childProps.size?.height ?? parentProps.size?.height ?? 'fit'
|
|
539
|
+
},
|
|
540
|
+
layout: {
|
|
541
|
+
padding: insetsToSpacing({
|
|
542
|
+
top: parentPadding.top + childPadding.top,
|
|
543
|
+
right: parentPadding.right + childPadding.right,
|
|
544
|
+
bottom: parentPadding.bottom + childPadding.bottom,
|
|
545
|
+
left: parentPadding.left + childPadding.left
|
|
546
|
+
}),
|
|
547
|
+
margin: insetsToSpacing({
|
|
548
|
+
top: parentMargin.top + childMargin.top,
|
|
549
|
+
right: parentMargin.right + childMargin.right,
|
|
550
|
+
bottom: parentMargin.bottom + childMargin.bottom,
|
|
551
|
+
left: parentMargin.left + childMargin.left
|
|
552
|
+
})
|
|
553
|
+
},
|
|
554
|
+
appearance: {
|
|
555
|
+
shape: 'rectangle',
|
|
556
|
+
cornerRadius: Number(childProps.appearance?.cornerRadius ?? parentProps.appearance?.cornerRadius ?? 0)
|
|
557
|
+
}
|
|
558
|
+
};
|
|
559
|
+
if (axis === 'overlay') {
|
|
560
|
+
mergedProps.overlayAlignment = childProps.overlayAlignment ?? parentProps.overlayAlignment ?? 'center';
|
|
561
|
+
}
|
|
562
|
+
if (parentProps.fill || childProps.fill) {
|
|
563
|
+
mergedProps.fill = childProps.fill ?? parentProps.fill;
|
|
564
|
+
}
|
|
565
|
+
if (parentProps.border || childProps.border) {
|
|
566
|
+
mergedProps.border = childProps.border ?? parentProps.border;
|
|
567
|
+
}
|
|
568
|
+
if (parentProps.shadow || childProps.shadow) {
|
|
569
|
+
mergedProps.shadow = childProps.shadow ?? parentProps.shadow;
|
|
570
|
+
}
|
|
571
|
+
if (childProps.width !== undefined || parentProps.width !== undefined) {
|
|
572
|
+
mergedProps.width = childProps.width ?? parentProps.width;
|
|
573
|
+
}
|
|
574
|
+
if (childProps.height !== undefined || parentProps.height !== undefined) {
|
|
575
|
+
mergedProps.height = childProps.height ?? parentProps.height;
|
|
576
|
+
}
|
|
577
|
+
return mergedProps;
|
|
578
|
+
}
|
|
579
|
+
function normalizeStackLikeNode(json) {
|
|
580
|
+
if (!json || typeof json !== 'object') return json;
|
|
581
|
+
const type = String(json.type ?? '');
|
|
582
|
+
if (!['stack', 'container', 'layout', 'row', 'column', 'grid'].includes(type)) {
|
|
583
|
+
return json;
|
|
584
|
+
}
|
|
585
|
+
const props = {
|
|
586
|
+
...(json.properties ?? {})
|
|
587
|
+
};
|
|
588
|
+
const axis = normalizeStackAxis(type, props);
|
|
589
|
+
const size = normalizeStackSize(props, axis);
|
|
590
|
+
const children = Array.isArray(json.children) ? [...json.children] : [];
|
|
591
|
+
if (json.child) {
|
|
592
|
+
children.push(json.child);
|
|
593
|
+
}
|
|
594
|
+
const normalizedProps = {
|
|
595
|
+
axis,
|
|
596
|
+
alignment: normalizeStackCrossAlignment(props.alignment ?? props.crossAxisAlignment),
|
|
597
|
+
distribution: normalizeStackDistribution(props.distribution ?? props.mainAxisAlignment),
|
|
598
|
+
childSpacing: Number(props.childSpacing ?? props.gap ?? props.spacing ?? 0),
|
|
599
|
+
size,
|
|
600
|
+
layout: {
|
|
601
|
+
padding: normalizeStackSpacingConfig(props.layout?.padding ?? props.padding),
|
|
602
|
+
margin: normalizeStackSpacingConfig(props.layout?.margin ?? props.margin)
|
|
603
|
+
},
|
|
604
|
+
appearance: {
|
|
605
|
+
shape: 'rectangle',
|
|
606
|
+
cornerRadius: Number(props.appearance?.cornerRadius ?? props.borderRadius ?? 0)
|
|
607
|
+
}
|
|
608
|
+
};
|
|
609
|
+
if (axis === 'overlay') {
|
|
610
|
+
normalizedProps.overlayAlignment = normalizeOverlayAlignment(props.overlayAlignment ?? props.alignment);
|
|
611
|
+
}
|
|
612
|
+
const fill = normalizeStackFill(props);
|
|
613
|
+
if (fill) normalizedProps.fill = fill;
|
|
614
|
+
const border = normalizeStackBorder(props);
|
|
615
|
+
if (border) normalizedProps.border = border;
|
|
616
|
+
const shadow = normalizeStackShadow(props);
|
|
617
|
+
if (shadow) normalizedProps.shadow = shadow;
|
|
618
|
+
if (props.width !== undefined) normalizedProps.width = props.width;
|
|
619
|
+
if (props.height !== undefined) normalizedProps.height = props.height;
|
|
620
|
+
if ((type === 'container' || type === 'layout') && children.length === 1) {
|
|
621
|
+
const childCandidate = children[0];
|
|
622
|
+
if (childCandidate && typeof childCandidate === 'object') {
|
|
623
|
+
const normalizedChild = normalizeStackLikeNode(childCandidate);
|
|
624
|
+
if (normalizedChild?.type === 'stack') {
|
|
625
|
+
const childProps = {
|
|
626
|
+
...(normalizedChild.properties ?? {})
|
|
627
|
+
};
|
|
628
|
+
const mergedProps = mergeStackProps(normalizedProps, childProps);
|
|
629
|
+
return {
|
|
630
|
+
...json,
|
|
631
|
+
type: 'stack',
|
|
632
|
+
properties: mergedProps,
|
|
633
|
+
children: Array.isArray(normalizedChild.children) ? normalizedChild.children : [],
|
|
634
|
+
child: undefined
|
|
635
|
+
};
|
|
636
|
+
}
|
|
637
|
+
}
|
|
638
|
+
}
|
|
639
|
+
return {
|
|
640
|
+
...json,
|
|
641
|
+
type: 'stack',
|
|
642
|
+
properties: normalizedProps,
|
|
643
|
+
children,
|
|
644
|
+
child: undefined
|
|
645
|
+
};
|
|
646
|
+
}
|
|
647
|
+
function parseOverlayGridAlignment(value) {
|
|
648
|
+
switch (normalizeOverlayAlignment(value)) {
|
|
649
|
+
case 'topStart':
|
|
650
|
+
return {
|
|
651
|
+
justifyContent: 'flex-start',
|
|
652
|
+
alignItems: 'flex-start'
|
|
653
|
+
};
|
|
654
|
+
case 'top':
|
|
655
|
+
return {
|
|
656
|
+
justifyContent: 'flex-start',
|
|
657
|
+
alignItems: 'center'
|
|
658
|
+
};
|
|
659
|
+
case 'topEnd':
|
|
660
|
+
return {
|
|
661
|
+
justifyContent: 'flex-start',
|
|
662
|
+
alignItems: 'flex-end'
|
|
663
|
+
};
|
|
664
|
+
case 'start':
|
|
665
|
+
return {
|
|
666
|
+
justifyContent: 'center',
|
|
667
|
+
alignItems: 'flex-start'
|
|
668
|
+
};
|
|
669
|
+
case 'end':
|
|
670
|
+
return {
|
|
671
|
+
justifyContent: 'center',
|
|
672
|
+
alignItems: 'flex-end'
|
|
673
|
+
};
|
|
674
|
+
case 'bottomStart':
|
|
675
|
+
return {
|
|
676
|
+
justifyContent: 'flex-end',
|
|
677
|
+
alignItems: 'flex-start'
|
|
678
|
+
};
|
|
679
|
+
case 'bottom':
|
|
680
|
+
return {
|
|
681
|
+
justifyContent: 'flex-end',
|
|
682
|
+
alignItems: 'center'
|
|
683
|
+
};
|
|
684
|
+
case 'bottomEnd':
|
|
685
|
+
return {
|
|
686
|
+
justifyContent: 'flex-end',
|
|
687
|
+
alignItems: 'flex-end'
|
|
688
|
+
};
|
|
689
|
+
case 'center':
|
|
690
|
+
default:
|
|
691
|
+
return {
|
|
692
|
+
justifyContent: 'center',
|
|
693
|
+
alignItems: 'center'
|
|
694
|
+
};
|
|
695
|
+
}
|
|
696
|
+
}
|
|
697
|
+
function resolveStackDimension(props, axis, key, axisBounds) {
|
|
698
|
+
const mode = props.size?.[key];
|
|
699
|
+
const legacy = normalizeDimension(parseLayoutDimension(props[key]));
|
|
700
|
+
const isBounded = key === 'width' ? axisBounds.widthBounded : axisBounds.heightBounded;
|
|
701
|
+
if (mode === 'fill') return isBounded ? '100%' : legacy;
|
|
702
|
+
if (mode === 'fit') return legacy;
|
|
703
|
+
if (mode === 'fixed') return legacy;
|
|
704
|
+
if (legacy !== undefined) return legacy;
|
|
705
|
+
if (key === 'width' && axis !== 'horizontal' && axisBounds.widthBounded) return '100%';
|
|
706
|
+
if (key === 'height' && axis === 'horizontal' && axisBounds.heightBounded) return '100%';
|
|
707
|
+
return undefined;
|
|
708
|
+
}
|
|
275
709
|
function buildWidget(json, params) {
|
|
276
710
|
if (!json || typeof json !== 'object') return null;
|
|
277
|
-
const
|
|
278
|
-
const
|
|
711
|
+
const normalizedJson = normalizeStackLikeNode(json);
|
|
712
|
+
const type = normalizedJson.type;
|
|
713
|
+
const id = normalizedJson.id;
|
|
279
714
|
const props = {
|
|
280
|
-
...(
|
|
715
|
+
...(normalizedJson.properties ?? {})
|
|
281
716
|
};
|
|
282
|
-
const
|
|
283
|
-
const
|
|
284
|
-
const
|
|
285
|
-
const
|
|
717
|
+
const pageAxisBounds = params.pageAxisBounds ?? getAxisBoundsForPageScroll(resolvePageScrollAxis(params.screenData));
|
|
718
|
+
const childrenJson = Array.isArray(normalizedJson.children) ? normalizedJson.children : undefined;
|
|
719
|
+
const childJson = normalizedJson.child;
|
|
720
|
+
const action = normalizedJson.action;
|
|
721
|
+
const actionPayload = action ? buildActionPayload(props, normalizedJson) : undefined;
|
|
286
722
|
let node = null;
|
|
287
723
|
switch (type) {
|
|
288
724
|
case 'column':
|
|
@@ -457,55 +893,83 @@ function buildWidget(json, params) {
|
|
|
457
893
|
const width = normalizeDimension(parseLayoutDimension(props.width)) ?? '100%';
|
|
458
894
|
const height = normalizeDimension(parseLayoutDimension(props.height)) ?? 50;
|
|
459
895
|
const label = props.label ?? 'Button';
|
|
896
|
+
const normalizedStroke = normalizeButtonStroke(props.stroke);
|
|
897
|
+
const normalizedEffects = normalizeButtonEffects(props.effects, props.shadow);
|
|
460
898
|
const textStyle = getTextStyle({
|
|
461
899
|
...props,
|
|
462
900
|
height: null
|
|
463
901
|
}, 'textColor', '0xFFFFFFFF');
|
|
464
|
-
const
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
902
|
+
const backgroundColor = parseColor(props.color ?? '0xFF2196F3');
|
|
903
|
+
const shadowBounds = getButtonShadowLayerBounds(normalizedStroke, borderRadius);
|
|
904
|
+
const webShadowStyle = buildButtonWebShadowStyle(normalizedEffects);
|
|
905
|
+
const firstNativeEffect = normalizedEffects.length > 0 ? normalizedEffects[0] : null;
|
|
906
|
+
const nativePrimaryShadowStyle = Platform.OS === 'web' || !firstNativeEffect ? null : buildButtonNativeShadowStyle(firstNativeEffect);
|
|
907
|
+
const centeredStrokeStyle = normalizedStroke && normalizedStroke.position === 'center' ? {
|
|
908
|
+
borderWidth: normalizedStroke.width,
|
|
909
|
+
borderColor: normalizedStroke.color
|
|
910
|
+
} : null;
|
|
911
|
+
const fillLayerStyle = {
|
|
912
|
+
position: 'absolute',
|
|
913
|
+
top: 0,
|
|
914
|
+
right: 0,
|
|
915
|
+
bottom: 0,
|
|
916
|
+
left: 0,
|
|
917
|
+
borderRadius,
|
|
918
|
+
overflow: 'hidden',
|
|
919
|
+
...(centeredStrokeStyle ?? {})
|
|
920
|
+
};
|
|
921
|
+
const nativeShadowEffectsInPaintOrder = normalizedEffects.slice().reverse();
|
|
922
|
+
const pressableStyle = {
|
|
923
|
+
width,
|
|
924
|
+
height,
|
|
925
|
+
borderRadius,
|
|
926
|
+
justifyContent: 'center',
|
|
927
|
+
alignItems: 'center',
|
|
928
|
+
overflow: 'visible',
|
|
929
|
+
...(nativePrimaryShadowStyle ?? {})
|
|
930
|
+
};
|
|
931
|
+
node = /*#__PURE__*/_jsxs(Pressable, {
|
|
932
|
+
collapsable: false,
|
|
933
|
+
onPress: action ? () => params.onAction(action, actionPayload ?? props) : undefined,
|
|
934
|
+
style: pressableStyle,
|
|
935
|
+
children: [Platform.OS === 'web' ? webShadowStyle ? /*#__PURE__*/_jsx(View, {
|
|
936
|
+
pointerEvents: "none",
|
|
478
937
|
style: {
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
locations: gradient.stops,
|
|
487
|
-
style: {
|
|
488
|
-
flex: 1,
|
|
489
|
-
borderRadius,
|
|
490
|
-
overflow: 'hidden'
|
|
491
|
-
},
|
|
492
|
-
children: content
|
|
493
|
-
})
|
|
494
|
-
});
|
|
495
|
-
} else {
|
|
496
|
-
node = /*#__PURE__*/_jsx(Pressable, {
|
|
497
|
-
onPress: action ? () => params.onAction(action, actionPayload ?? props) : undefined,
|
|
938
|
+
position: 'absolute',
|
|
939
|
+
...shadowBounds,
|
|
940
|
+
...webShadowStyle
|
|
941
|
+
}
|
|
942
|
+
}) : null : nativeShadowEffectsInPaintOrder.map((effect, index) => /*#__PURE__*/_jsx(View, {
|
|
943
|
+
collapsable: false,
|
|
944
|
+
pointerEvents: "none",
|
|
498
945
|
style: {
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
946
|
+
position: 'absolute',
|
|
947
|
+
...shadowBounds,
|
|
948
|
+
...buildButtonNativeShadowStyle(effect)
|
|
949
|
+
}
|
|
950
|
+
}, `shadow-${index}`)), gradient ? /*#__PURE__*/_jsx(LinearGradient, {
|
|
951
|
+
colors: gradient.colors,
|
|
952
|
+
start: gradient.start,
|
|
953
|
+
end: gradient.end,
|
|
954
|
+
locations: gradient.stops,
|
|
955
|
+
style: fillLayerStyle
|
|
956
|
+
}) : /*#__PURE__*/_jsx(View, {
|
|
957
|
+
style: {
|
|
958
|
+
...fillLayerStyle,
|
|
959
|
+
backgroundColor
|
|
960
|
+
}
|
|
961
|
+
}), renderButtonStrokeOverlay(normalizedStroke, borderRadius), /*#__PURE__*/_jsx(View, {
|
|
962
|
+
style: {
|
|
963
|
+
flex: 1,
|
|
503
964
|
justifyContent: 'center',
|
|
504
965
|
alignItems: 'center'
|
|
505
966
|
},
|
|
506
|
-
children:
|
|
507
|
-
|
|
508
|
-
|
|
967
|
+
children: /*#__PURE__*/_jsx(Text, {
|
|
968
|
+
style: textStyle,
|
|
969
|
+
children: label
|
|
970
|
+
})
|
|
971
|
+
})]
|
|
972
|
+
});
|
|
509
973
|
break;
|
|
510
974
|
}
|
|
511
975
|
case 'text_input':
|
|
@@ -661,16 +1125,16 @@ function buildWidget(json, params) {
|
|
|
661
1125
|
const source = props.source;
|
|
662
1126
|
const width = normalizeDimension(parseLayoutDimension(props.width, true));
|
|
663
1127
|
const height = normalizeDimension(parseLayoutDimension(props.height, true));
|
|
1128
|
+
const style = resolveLottieStyle(width, height);
|
|
1129
|
+
const resizeMode = parseResizeMode(props.fit);
|
|
664
1130
|
const loop = props.loop === true;
|
|
665
1131
|
if (source === 'asset') {
|
|
666
1132
|
node = /*#__PURE__*/_jsx(LottieView, {
|
|
667
1133
|
source: {
|
|
668
1134
|
uri: props.path ?? ''
|
|
669
1135
|
},
|
|
670
|
-
style:
|
|
671
|
-
|
|
672
|
-
height
|
|
673
|
-
},
|
|
1136
|
+
style: style,
|
|
1137
|
+
resizeMode: resizeMode,
|
|
674
1138
|
autoPlay: true,
|
|
675
1139
|
loop: loop
|
|
676
1140
|
});
|
|
@@ -680,10 +1144,8 @@ function buildWidget(json, params) {
|
|
|
680
1144
|
source: {
|
|
681
1145
|
uri: props.url
|
|
682
1146
|
},
|
|
683
|
-
style:
|
|
684
|
-
|
|
685
|
-
height
|
|
686
|
-
},
|
|
1147
|
+
style: style,
|
|
1148
|
+
resizeMode: resizeMode,
|
|
687
1149
|
autoPlay: true,
|
|
688
1150
|
loop: loop
|
|
689
1151
|
});
|
|
@@ -819,22 +1281,112 @@ function buildWidget(json, params) {
|
|
|
819
1281
|
}
|
|
820
1282
|
case 'stack':
|
|
821
1283
|
{
|
|
822
|
-
const
|
|
823
|
-
const
|
|
824
|
-
|
|
1284
|
+
const axis = props.axis === 'horizontal' || props.axis === 'overlay' ? props.axis : 'vertical';
|
|
1285
|
+
const isOverlay = axis === 'overlay';
|
|
1286
|
+
const spacing = Number(props.childSpacing ?? 0);
|
|
1287
|
+
const applySpacing = !STACK_SPACE_DISTRIBUTIONS.has(String(props.distribution ?? 'start'));
|
|
1288
|
+
const paddingInsets = stackSpacingToInsets(props.layout?.padding ?? props.padding);
|
|
1289
|
+
const width = resolveStackDimension(props, axis, 'width', pageAxisBounds);
|
|
1290
|
+
const height = resolveStackDimension(props, axis, 'height', pageAxisBounds);
|
|
1291
|
+
const mainAxisBounded = axis === 'horizontal' ? pageAxisBounds.widthBounded : pageAxisBounds.heightBounded;
|
|
1292
|
+
const borderRadius = Number(props.appearance?.cornerRadius ?? props.borderRadius ?? 0);
|
|
1293
|
+
const borderWidth = Number(props.border?.width ?? 0);
|
|
1294
|
+
const borderColor = borderWidth > 0 ? parseColor(props.border?.color ?? '#E5E7EB') : null;
|
|
1295
|
+
const shadowColor = props.shadow?.color ? parseColor(props.shadow.color) : null;
|
|
1296
|
+
const shadowStyle = shadowColor && props.shadow ? Platform.OS === 'web' ? {
|
|
1297
|
+
boxShadow: `${Number(props.shadow.x ?? 0)}px ${Number(props.shadow.y ?? 8)}px ${Number(props.shadow.blur ?? 24)}px ${shadowColor}`
|
|
1298
|
+
} : {
|
|
1299
|
+
shadowColor,
|
|
1300
|
+
shadowOffset: {
|
|
1301
|
+
width: Number(props.shadow.x ?? 0),
|
|
1302
|
+
height: Number(props.shadow.y ?? 8)
|
|
1303
|
+
},
|
|
1304
|
+
shadowOpacity: 0.35,
|
|
1305
|
+
shadowRadius: Math.max(0, Number(props.shadow.blur ?? 24) / 2),
|
|
1306
|
+
elevation: Math.max(1, Math.round((Number(props.shadow.blur ?? 24) + Math.abs(Number(props.shadow.y ?? 8))) / 3))
|
|
1307
|
+
} : {};
|
|
1308
|
+
const fill = props.fill;
|
|
1309
|
+
const stackGradient = fill?.type === 'gradient' ? parseGradient({
|
|
1310
|
+
...fill,
|
|
1311
|
+
type: 'gradient'
|
|
1312
|
+
}) : undefined;
|
|
1313
|
+
const stackColor = fill?.type === 'solid' || !fill?.type && fill?.color ? parseColor(fill?.color ?? '#FFFFFFFF') : parseColor(props.backgroundColor ?? '#00000000');
|
|
1314
|
+
const baseStackStyle = {
|
|
1315
|
+
width,
|
|
1316
|
+
height,
|
|
1317
|
+
borderRadius: borderRadius > 0 ? borderRadius : undefined,
|
|
1318
|
+
overflow: borderRadius > 0 ? 'hidden' : undefined,
|
|
1319
|
+
borderWidth: borderColor ? borderWidth : undefined,
|
|
1320
|
+
borderColor: borderColor ?? undefined,
|
|
1321
|
+
...insetsToStyle(paddingInsets),
|
|
1322
|
+
...shadowStyle
|
|
1323
|
+
};
|
|
1324
|
+
const content = isOverlay ? /*#__PURE__*/_jsxs(View, {
|
|
825
1325
|
style: {
|
|
826
1326
|
position: 'relative',
|
|
827
|
-
|
|
828
|
-
|
|
829
|
-
justifyContent: alignment.justifyContent,
|
|
830
|
-
alignItems: alignment.alignItems
|
|
1327
|
+
minHeight: 0,
|
|
1328
|
+
minWidth: 0
|
|
831
1329
|
},
|
|
832
|
-
children: (childrenJson ?? []).map((child, index) =>
|
|
833
|
-
|
|
834
|
-
|
|
835
|
-
|
|
1330
|
+
children: [(childrenJson ?? []).map((child, index) => {
|
|
1331
|
+
const alignment = parseOverlayGridAlignment(props.overlayAlignment ?? props.alignment);
|
|
1332
|
+
return /*#__PURE__*/_jsx(View, {
|
|
1333
|
+
style: {
|
|
1334
|
+
position: 'absolute',
|
|
1335
|
+
top: 0,
|
|
1336
|
+
right: 0,
|
|
1337
|
+
bottom: 0,
|
|
1338
|
+
left: 0,
|
|
1339
|
+
justifyContent: alignment.justifyContent,
|
|
1340
|
+
alignItems: alignment.alignItems
|
|
1341
|
+
},
|
|
1342
|
+
children: buildWidget(child, {
|
|
1343
|
+
...params,
|
|
1344
|
+
allowFlexExpansion: true,
|
|
1345
|
+
pageAxisBounds
|
|
1346
|
+
})
|
|
1347
|
+
}, `stack-overlay-${index}`);
|
|
1348
|
+
}), (childrenJson ?? []).length === 0 ? null : null]
|
|
1349
|
+
}) : /*#__PURE__*/_jsx(View, {
|
|
1350
|
+
style: {
|
|
1351
|
+
flexDirection: axis === 'horizontal' ? 'row' : 'column',
|
|
1352
|
+
justifyContent: parseFlexAlignment(props.distribution),
|
|
1353
|
+
alignItems: parseCrossAlignment(props.alignment),
|
|
1354
|
+
minHeight: 0,
|
|
1355
|
+
minWidth: 0
|
|
1356
|
+
},
|
|
1357
|
+
children: (childrenJson ?? []).map((child, index) => /*#__PURE__*/_jsxs(React.Fragment, {
|
|
1358
|
+
children: [buildWidget(child, {
|
|
1359
|
+
...params,
|
|
1360
|
+
allowFlexExpansion: true,
|
|
1361
|
+
parentFlexAxis: axis === 'horizontal' ? 'horizontal' : 'vertical',
|
|
1362
|
+
parentMainAxisBounded: mainAxisBounded,
|
|
1363
|
+
pageAxisBounds
|
|
1364
|
+
}), applySpacing && spacing > 0 && index < (childrenJson?.length ?? 0) - 1 ? /*#__PURE__*/_jsx(View, {
|
|
1365
|
+
style: {
|
|
1366
|
+
width: axis === 'horizontal' ? spacing : undefined,
|
|
1367
|
+
height: axis === 'vertical' ? spacing : undefined
|
|
1368
|
+
}
|
|
1369
|
+
}) : null]
|
|
836
1370
|
}, `stack-${index}`))
|
|
837
1371
|
});
|
|
1372
|
+
if (stackGradient) {
|
|
1373
|
+
node = /*#__PURE__*/_jsx(LinearGradient, {
|
|
1374
|
+
colors: stackGradient.colors,
|
|
1375
|
+
start: stackGradient.start,
|
|
1376
|
+
end: stackGradient.end,
|
|
1377
|
+
locations: stackGradient.stops,
|
|
1378
|
+
style: baseStackStyle,
|
|
1379
|
+
children: content
|
|
1380
|
+
});
|
|
1381
|
+
} else {
|
|
1382
|
+
node = /*#__PURE__*/_jsx(View, {
|
|
1383
|
+
style: {
|
|
1384
|
+
...baseStackStyle,
|
|
1385
|
+
backgroundColor: stackColor
|
|
1386
|
+
},
|
|
1387
|
+
children: content
|
|
1388
|
+
});
|
|
1389
|
+
}
|
|
838
1390
|
break;
|
|
839
1391
|
}
|
|
840
1392
|
case 'positioned':
|
|
@@ -865,35 +1417,66 @@ function buildWidget(json, params) {
|
|
|
865
1417
|
node = null;
|
|
866
1418
|
}
|
|
867
1419
|
if (!node) return null;
|
|
868
|
-
const marginVal = props.margin;
|
|
1420
|
+
const marginVal = type === 'stack' ? props.layout?.margin ?? props.margin : props.margin;
|
|
869
1421
|
if (marginVal !== undefined && marginVal !== null) {
|
|
870
|
-
const marginInsets = parseInsets(marginVal);
|
|
1422
|
+
const marginInsets = type === 'stack' ? stackSpacingToInsets(marginVal) : parseInsets(marginVal);
|
|
871
1423
|
node = /*#__PURE__*/_jsx(View, {
|
|
872
1424
|
style: insetsToMarginStyle(marginInsets),
|
|
873
1425
|
children: node
|
|
874
1426
|
});
|
|
875
|
-
} else if (type !== 'container' && type !== 'padding' && props.padding) {
|
|
1427
|
+
} else if (type !== 'container' && type !== 'padding' && type !== 'stack' && props.padding) {
|
|
876
1428
|
const paddingInsets = parseInsets(props.padding);
|
|
877
1429
|
node = /*#__PURE__*/_jsx(View, {
|
|
878
1430
|
style: insetsToStyle(paddingInsets),
|
|
879
1431
|
children: node
|
|
880
1432
|
});
|
|
881
1433
|
}
|
|
882
|
-
|
|
1434
|
+
const shouldStretchTextInput = type === 'text_input' && props.width === undefined && props.size?.width === undefined;
|
|
1435
|
+
if (shouldStretchTextInput) {
|
|
1436
|
+
node = /*#__PURE__*/_jsx(View, {
|
|
1437
|
+
style: {
|
|
1438
|
+
width: '100%',
|
|
1439
|
+
alignSelf: 'stretch',
|
|
1440
|
+
minWidth: 0
|
|
1441
|
+
},
|
|
1442
|
+
children: node
|
|
1443
|
+
});
|
|
1444
|
+
}
|
|
1445
|
+
if (params.allowFlexExpansion) {
|
|
883
1446
|
let shouldExpand = false;
|
|
884
|
-
|
|
885
|
-
|
|
886
|
-
|
|
887
|
-
|
|
888
|
-
|
|
889
|
-
|
|
890
|
-
|
|
1447
|
+
const parentAxis = params.parentFlexAxis ?? 'vertical';
|
|
1448
|
+
const parentMainAxisBounded = params.parentMainAxisBounded ?? true;
|
|
1449
|
+
if (parentMainAxisBounded) {
|
|
1450
|
+
if (type === 'stack') {
|
|
1451
|
+
const legacyExpand = props.fit === 'expand';
|
|
1452
|
+
const widthMode = String(props.size?.width ?? '').toLowerCase();
|
|
1453
|
+
const heightMode = String(props.size?.height ?? '').toLowerCase();
|
|
1454
|
+
if (parentAxis === 'vertical' && (legacyExpand || heightMode === 'fill')) {
|
|
1455
|
+
shouldExpand = true;
|
|
1456
|
+
}
|
|
1457
|
+
if (parentAxis === 'horizontal' && (legacyExpand || widthMode === 'fill')) {
|
|
1458
|
+
shouldExpand = true;
|
|
1459
|
+
}
|
|
1460
|
+
}
|
|
1461
|
+
if (parentAxis === 'vertical' && props.height !== undefined) {
|
|
1462
|
+
const heightVal = parseLayoutDimension(props.height);
|
|
1463
|
+
if (heightVal === Number.POSITIVE_INFINITY) {
|
|
1464
|
+
shouldExpand = true;
|
|
1465
|
+
}
|
|
1466
|
+
}
|
|
1467
|
+
if (parentAxis === 'horizontal' && props.width !== undefined) {
|
|
1468
|
+
const widthVal = parseLayoutDimension(props.width);
|
|
1469
|
+
if (widthVal === Number.POSITIVE_INFINITY) {
|
|
1470
|
+
shouldExpand = true;
|
|
1471
|
+
}
|
|
891
1472
|
}
|
|
892
1473
|
}
|
|
893
1474
|
if (shouldExpand) {
|
|
894
1475
|
node = /*#__PURE__*/_jsx(View, {
|
|
895
1476
|
style: {
|
|
896
|
-
flex: 1
|
|
1477
|
+
flex: 1,
|
|
1478
|
+
minHeight: 0,
|
|
1479
|
+
minWidth: 0
|
|
897
1480
|
},
|
|
898
1481
|
children: node
|
|
899
1482
|
});
|
|
@@ -958,6 +1541,132 @@ function parseGradient(value) {
|
|
|
958
1541
|
stops
|
|
959
1542
|
};
|
|
960
1543
|
}
|
|
1544
|
+
function normalizeButtonStroke(input) {
|
|
1545
|
+
if (!input || typeof input !== 'object') return null;
|
|
1546
|
+
const legacyStrokeWithoutEnabled = input.enabled === undefined;
|
|
1547
|
+
const enabled = legacyStrokeWithoutEnabled ? true : input.enabled === true;
|
|
1548
|
+
if (!enabled) return null;
|
|
1549
|
+
const width = Number(input.width ?? 1);
|
|
1550
|
+
if (!Number.isFinite(width) || width <= 0) return null;
|
|
1551
|
+
const position = input.position === 'inside' || input.position === 'center' || input.position === 'outside' ? input.position : 'center';
|
|
1552
|
+
const opacity = clamp01(Number(input.opacity ?? 1));
|
|
1553
|
+
const color = normalizeColorWithOpacity(input.color, opacity);
|
|
1554
|
+
if (color === 'transparent') return null;
|
|
1555
|
+
return {
|
|
1556
|
+
color,
|
|
1557
|
+
width,
|
|
1558
|
+
position
|
|
1559
|
+
};
|
|
1560
|
+
}
|
|
1561
|
+
function normalizeButtonEffects(effectsInput, legacyShadowInput) {
|
|
1562
|
+
if (Array.isArray(effectsInput)) {
|
|
1563
|
+
return effectsInput.map(normalizeDropShadowEffect).filter(effect => effect !== null);
|
|
1564
|
+
}
|
|
1565
|
+
if (!legacyShadowInput || typeof legacyShadowInput !== 'object') {
|
|
1566
|
+
return [];
|
|
1567
|
+
}
|
|
1568
|
+
if (legacyShadowInput.enabled !== true) return [];
|
|
1569
|
+
const migrated = normalizeDropShadowEffect({
|
|
1570
|
+
type: 'dropShadow',
|
|
1571
|
+
enabled: true,
|
|
1572
|
+
x: legacyShadowInput.x,
|
|
1573
|
+
y: legacyShadowInput.y,
|
|
1574
|
+
blur: legacyShadowInput.blur,
|
|
1575
|
+
spread: 0,
|
|
1576
|
+
color: legacyShadowInput.color,
|
|
1577
|
+
opacity: 1
|
|
1578
|
+
});
|
|
1579
|
+
return migrated ? [migrated] : [];
|
|
1580
|
+
}
|
|
1581
|
+
function normalizeDropShadowEffect(effect) {
|
|
1582
|
+
if (!effect || typeof effect !== 'object') return null;
|
|
1583
|
+
if ((effect.type ?? 'dropShadow') !== 'dropShadow') return null;
|
|
1584
|
+
if (effect.enabled === false) return null;
|
|
1585
|
+
const opacity = clamp01(Number(effect.opacity ?? 1));
|
|
1586
|
+
const color = normalizeColorWithOpacity(effect.color, opacity);
|
|
1587
|
+
if (color === 'transparent') return null;
|
|
1588
|
+
return {
|
|
1589
|
+
x: Number(effect.x ?? 0),
|
|
1590
|
+
y: Number(effect.y ?? 0),
|
|
1591
|
+
blur: Math.max(0, Number(effect.blur ?? 0)),
|
|
1592
|
+
spread: Number(effect.spread ?? 0),
|
|
1593
|
+
color,
|
|
1594
|
+
opacity
|
|
1595
|
+
};
|
|
1596
|
+
}
|
|
1597
|
+
function normalizeColorWithOpacity(input, opacity) {
|
|
1598
|
+
const raw = typeof input === 'string' ? input.trim() : '';
|
|
1599
|
+
const normalizedOpacity = clamp01(opacity);
|
|
1600
|
+
if (raw.startsWith('rgba(') || raw.startsWith('rgb(')) {
|
|
1601
|
+
return withOpacity(raw, normalizedOpacity);
|
|
1602
|
+
}
|
|
1603
|
+
return withOpacity(parseColor(input ?? '0xFF000000'), normalizedOpacity);
|
|
1604
|
+
}
|
|
1605
|
+
function buildButtonWebShadowStyle(effects) {
|
|
1606
|
+
if (effects.length === 0) return null;
|
|
1607
|
+
const layers = effects.map(effect => `${effect.x}px ${effect.y}px ${effect.blur}px ${effect.spread}px ${effect.color}`);
|
|
1608
|
+
return {
|
|
1609
|
+
boxShadow: layers.join(', ')
|
|
1610
|
+
};
|
|
1611
|
+
}
|
|
1612
|
+
function buildButtonNativeShadowStyle(effect) {
|
|
1613
|
+
const rgba = parseRgbaColor(effect.color);
|
|
1614
|
+
return {
|
|
1615
|
+
// Android elevation requires a drawable host shape; keep it effectively invisible.
|
|
1616
|
+
backgroundColor: 'rgba(255,255,255,0.02)',
|
|
1617
|
+
shadowColor: rgba ? `rgba(${rgba.r},${rgba.g},${rgba.b},1)` : effect.color,
|
|
1618
|
+
shadowOffset: {
|
|
1619
|
+
width: effect.x,
|
|
1620
|
+
height: effect.y
|
|
1621
|
+
},
|
|
1622
|
+
shadowOpacity: rgba ? rgba.a : effect.opacity,
|
|
1623
|
+
shadowRadius: effect.blur / 2,
|
|
1624
|
+
elevation: Math.max(1, Math.round((effect.blur + Math.abs(effect.y)) / 3))
|
|
1625
|
+
};
|
|
1626
|
+
}
|
|
1627
|
+
function getButtonShadowLayerBounds(stroke, borderRadius) {
|
|
1628
|
+
const expandBy = !stroke || stroke.position === 'inside' ? 0 : stroke.position === 'center' ? stroke.width / 2 : stroke.width;
|
|
1629
|
+
return {
|
|
1630
|
+
top: -expandBy,
|
|
1631
|
+
right: -expandBy,
|
|
1632
|
+
bottom: -expandBy,
|
|
1633
|
+
left: -expandBy,
|
|
1634
|
+
borderRadius: borderRadius + expandBy
|
|
1635
|
+
};
|
|
1636
|
+
}
|
|
1637
|
+
function renderButtonStrokeOverlay(stroke, borderRadius) {
|
|
1638
|
+
if (!stroke || stroke.position === 'center') {
|
|
1639
|
+
return null;
|
|
1640
|
+
}
|
|
1641
|
+
if (stroke.position === 'inside') {
|
|
1642
|
+
return /*#__PURE__*/_jsx(View, {
|
|
1643
|
+
pointerEvents: "none",
|
|
1644
|
+
style: {
|
|
1645
|
+
position: 'absolute',
|
|
1646
|
+
top: stroke.width / 2,
|
|
1647
|
+
left: stroke.width / 2,
|
|
1648
|
+
right: stroke.width / 2,
|
|
1649
|
+
bottom: stroke.width / 2,
|
|
1650
|
+
borderRadius: Math.max(0, borderRadius - stroke.width / 2),
|
|
1651
|
+
borderWidth: stroke.width,
|
|
1652
|
+
borderColor: stroke.color
|
|
1653
|
+
}
|
|
1654
|
+
});
|
|
1655
|
+
}
|
|
1656
|
+
return /*#__PURE__*/_jsx(View, {
|
|
1657
|
+
pointerEvents: "none",
|
|
1658
|
+
style: {
|
|
1659
|
+
position: 'absolute',
|
|
1660
|
+
top: -stroke.width,
|
|
1661
|
+
left: -stroke.width,
|
|
1662
|
+
right: -stroke.width,
|
|
1663
|
+
bottom: -stroke.width,
|
|
1664
|
+
borderRadius: borderRadius + stroke.width,
|
|
1665
|
+
borderWidth: stroke.width,
|
|
1666
|
+
borderColor: stroke.color
|
|
1667
|
+
}
|
|
1668
|
+
});
|
|
1669
|
+
}
|
|
961
1670
|
function alignmentToGradient(value) {
|
|
962
1671
|
switch (value) {
|
|
963
1672
|
case 'topCenter':
|
|
@@ -1009,10 +1718,23 @@ function alignmentToGradient(value) {
|
|
|
1009
1718
|
}
|
|
1010
1719
|
}
|
|
1011
1720
|
function withOpacity(color, opacity) {
|
|
1012
|
-
|
|
1013
|
-
|
|
1014
|
-
}
|
|
1015
|
-
|
|
1721
|
+
const rgba = parseRgbaColor(color);
|
|
1722
|
+
if (!rgba) return color;
|
|
1723
|
+
return `rgba(${rgba.r},${rgba.g},${rgba.b},${clamp01(rgba.a * opacity)})`;
|
|
1724
|
+
}
|
|
1725
|
+
function parseRgbaColor(color) {
|
|
1726
|
+
const match = color.match(/^rgba?\(\s*([0-9.]+)\s*,\s*([0-9.]+)\s*,\s*([0-9.]+)(?:\s*,\s*([0-9.]+))?\s*\)$/i);
|
|
1727
|
+
if (!match) return null;
|
|
1728
|
+
return {
|
|
1729
|
+
r: Math.round(Number(match[1])),
|
|
1730
|
+
g: Math.round(Number(match[2])),
|
|
1731
|
+
b: Math.round(Number(match[3])),
|
|
1732
|
+
a: match[4] === undefined ? 1 : clamp01(Number(match[4]))
|
|
1733
|
+
};
|
|
1734
|
+
}
|
|
1735
|
+
function clamp01(value) {
|
|
1736
|
+
if (!Number.isFinite(value)) return 1;
|
|
1737
|
+
return Math.max(0, Math.min(1, value));
|
|
1016
1738
|
}
|
|
1017
1739
|
function normalizeDimension(value) {
|
|
1018
1740
|
if (value === Number.POSITIVE_INFINITY) {
|
|
@@ -1114,6 +1836,35 @@ function SduiImage({
|
|
|
1114
1836
|
onError: () => setHasError(true)
|
|
1115
1837
|
});
|
|
1116
1838
|
}
|
|
1839
|
+
function resolveLottieStyle(width, height) {
|
|
1840
|
+
const hasExplicitWidth = width !== undefined;
|
|
1841
|
+
const hasExplicitHeight = height !== undefined;
|
|
1842
|
+
const style = {};
|
|
1843
|
+
if (hasExplicitWidth) {
|
|
1844
|
+
style.width = width;
|
|
1845
|
+
} else {
|
|
1846
|
+
style.width = 'auto';
|
|
1847
|
+
}
|
|
1848
|
+
if (hasExplicitHeight) {
|
|
1849
|
+
style.height = height;
|
|
1850
|
+
} else {
|
|
1851
|
+
style.height = 'auto';
|
|
1852
|
+
}
|
|
1853
|
+
if (width === '100%') {
|
|
1854
|
+
style.width = '100%';
|
|
1855
|
+
style.alignSelf = 'stretch';
|
|
1856
|
+
style.minWidth = 0;
|
|
1857
|
+
}
|
|
1858
|
+
if (hasExplicitWidth && !hasExplicitHeight) {
|
|
1859
|
+
delete style.height;
|
|
1860
|
+
style.aspectRatio = 1;
|
|
1861
|
+
}
|
|
1862
|
+
if (!hasExplicitWidth && hasExplicitHeight) {
|
|
1863
|
+
delete style.width;
|
|
1864
|
+
style.aspectRatio = 1;
|
|
1865
|
+
}
|
|
1866
|
+
return style;
|
|
1867
|
+
}
|
|
1117
1868
|
function GradientText({
|
|
1118
1869
|
text,
|
|
1119
1870
|
gradient,
|
|
@@ -1140,6 +1891,44 @@ function GradientText({
|
|
|
1140
1891
|
})
|
|
1141
1892
|
});
|
|
1142
1893
|
}
|
|
1894
|
+
function resolveTextInputBackgroundColor(properties) {
|
|
1895
|
+
const canonical = properties.backgroundColor;
|
|
1896
|
+
if (typeof canonical === 'string' && canonical.trim().length > 0) {
|
|
1897
|
+
return parseColor(canonical);
|
|
1898
|
+
}
|
|
1899
|
+
const legacyBackground = properties.background;
|
|
1900
|
+
if (legacyBackground && typeof legacyBackground === 'object' && !Array.isArray(legacyBackground)) {
|
|
1901
|
+
const type = String(legacyBackground.type ?? '').toLowerCase();
|
|
1902
|
+
if (type === 'color' && typeof legacyBackground.color === 'string') {
|
|
1903
|
+
return parseColor(legacyBackground.color);
|
|
1904
|
+
}
|
|
1905
|
+
if (type === 'gradient' && Array.isArray(legacyBackground.colors)) {
|
|
1906
|
+
const firstGradientColor = legacyBackground.colors.find(value => typeof value === 'string' && value.trim().length > 0);
|
|
1907
|
+
if (typeof firstGradientColor === 'string') {
|
|
1908
|
+
return parseColor(firstGradientColor);
|
|
1909
|
+
}
|
|
1910
|
+
}
|
|
1911
|
+
} else if (typeof legacyBackground === 'string' && legacyBackground.trim().length > 0) {
|
|
1912
|
+
return parseColor(legacyBackground);
|
|
1913
|
+
}
|
|
1914
|
+
return parseColor('0xFFF0F0F0');
|
|
1915
|
+
}
|
|
1916
|
+
function normalizeTextInputStroke(properties) {
|
|
1917
|
+
const parsedWidth = Number(properties.strokeWidth ?? 0);
|
|
1918
|
+
const width = Number.isFinite(parsedWidth) && parsedWidth > 0 ? parsedWidth : 0;
|
|
1919
|
+
const enabled = typeof properties.strokeEnabled === 'boolean' ? properties.strokeEnabled : width > 0;
|
|
1920
|
+
const normalizedWidth = enabled ? width : 0;
|
|
1921
|
+
const color = normalizedWidth > 0 ? parseColor(properties.strokeColor ?? '#000000') : 'transparent';
|
|
1922
|
+
const parsedRadius = Number(properties.strokeRadius ?? properties.borderRadius ?? 8);
|
|
1923
|
+
const radius = Number.isFinite(parsedRadius) ? Math.max(0, parsedRadius) : 8;
|
|
1924
|
+
const style = properties.strokeStyle === 'dashed' || properties.strokeStyle === 'dotted' ? properties.strokeStyle : 'solid';
|
|
1925
|
+
return {
|
|
1926
|
+
width: normalizedWidth,
|
|
1927
|
+
color,
|
|
1928
|
+
radius,
|
|
1929
|
+
style
|
|
1930
|
+
};
|
|
1931
|
+
}
|
|
1143
1932
|
function DynamicInput({
|
|
1144
1933
|
initialValue,
|
|
1145
1934
|
properties,
|
|
@@ -1163,18 +1952,26 @@ function DynamicInput({
|
|
|
1163
1952
|
...properties,
|
|
1164
1953
|
height: null
|
|
1165
1954
|
}, 'textColor');
|
|
1166
|
-
const backgroundColor =
|
|
1167
|
-
const
|
|
1955
|
+
const backgroundColor = resolveTextInputBackgroundColor(properties);
|
|
1956
|
+
const stroke = normalizeTextInputStroke(properties);
|
|
1957
|
+
const parsedPadding = Number(properties.padding ?? 12);
|
|
1958
|
+
const padding = Number.isFinite(parsedPadding) ? Math.max(0, parsedPadding) : 12;
|
|
1168
1959
|
return /*#__PURE__*/_jsxs(View, {
|
|
1960
|
+
style: {
|
|
1961
|
+
width: '100%'
|
|
1962
|
+
},
|
|
1169
1963
|
children: [label ? /*#__PURE__*/_jsx(Text, {
|
|
1170
1964
|
style: labelStyle,
|
|
1171
1965
|
children: label
|
|
1172
1966
|
}) : null, /*#__PURE__*/_jsx(View, {
|
|
1173
1967
|
style: {
|
|
1968
|
+
width: '100%',
|
|
1174
1969
|
backgroundColor,
|
|
1175
|
-
borderRadius,
|
|
1176
|
-
|
|
1177
|
-
|
|
1970
|
+
borderRadius: stroke.radius,
|
|
1971
|
+
borderStyle: stroke.width > 0 ? stroke.style : undefined,
|
|
1972
|
+
borderWidth: stroke.width,
|
|
1973
|
+
borderColor: stroke.color,
|
|
1974
|
+
padding
|
|
1178
1975
|
},
|
|
1179
1976
|
children: mask ? /*#__PURE__*/_jsx(MaskInput, {
|
|
1180
1977
|
value: value,
|
|
@@ -1313,6 +2110,7 @@ function SelectionList({
|
|
|
1313
2110
|
if (layout === 'row') {
|
|
1314
2111
|
return /*#__PURE__*/_jsx(View, {
|
|
1315
2112
|
style: {
|
|
2113
|
+
width: '100%',
|
|
1316
2114
|
flexDirection: 'row',
|
|
1317
2115
|
flexWrap: 'wrap'
|
|
1318
2116
|
},
|
|
@@ -1330,6 +2128,7 @@ function SelectionList({
|
|
|
1330
2128
|
const aspectRatio = Number(properties.gridAspectRatio ?? 1);
|
|
1331
2129
|
return /*#__PURE__*/_jsx(View, {
|
|
1332
2130
|
style: {
|
|
2131
|
+
width: '100%',
|
|
1333
2132
|
flexDirection: 'row',
|
|
1334
2133
|
flexWrap: 'wrap'
|
|
1335
2134
|
},
|
|
@@ -1345,8 +2144,12 @@ function SelectionList({
|
|
|
1345
2144
|
});
|
|
1346
2145
|
}
|
|
1347
2146
|
return /*#__PURE__*/_jsx(View, {
|
|
2147
|
+
style: {
|
|
2148
|
+
width: '100%'
|
|
2149
|
+
},
|
|
1348
2150
|
children: options.map((option, index) => /*#__PURE__*/_jsx(View, {
|
|
1349
2151
|
style: {
|
|
2152
|
+
width: '100%',
|
|
1350
2153
|
marginBottom: spacing
|
|
1351
2154
|
},
|
|
1352
2155
|
children: renderItem(option, index)
|
|
@@ -1529,6 +2332,8 @@ function WheelPicker({
|
|
|
1529
2332
|
if (layout === 'row') {
|
|
1530
2333
|
return /*#__PURE__*/_jsx(View, {
|
|
1531
2334
|
style: {
|
|
2335
|
+
width: '100%',
|
|
2336
|
+
alignSelf: 'stretch',
|
|
1532
2337
|
flexDirection: 'row',
|
|
1533
2338
|
flexWrap: 'wrap'
|
|
1534
2339
|
},
|
|
@@ -1546,6 +2351,8 @@ function WheelPicker({
|
|
|
1546
2351
|
const aspectRatio = Number(properties.gridAspectRatio ?? 1);
|
|
1547
2352
|
return /*#__PURE__*/_jsx(View, {
|
|
1548
2353
|
style: {
|
|
2354
|
+
width: '100%',
|
|
2355
|
+
alignSelf: 'stretch',
|
|
1549
2356
|
flexDirection: 'row',
|
|
1550
2357
|
flexWrap: 'wrap'
|
|
1551
2358
|
},
|
|
@@ -1566,6 +2373,8 @@ function WheelPicker({
|
|
|
1566
2373
|
const baseUnselectedStyle = properties.unselectedStyle ?? properties.selectedStyle ?? {};
|
|
1567
2374
|
const overlayBorderColor = parseColor(properties.wheelCenterBorderColor ?? properties.wheelSelectedBorderColor ?? baseUnselectedStyle.borderColor ?? '#D6D6DC');
|
|
1568
2375
|
const overlayBackgroundColor = parseColor(properties.wheelCenterBackgroundColor ?? '#24FFFFFF');
|
|
2376
|
+
const wheelEdgeFadeOpacity = Math.max(0, Math.min(1, Number(properties.wheelEdgeFadeOpacity ?? 0)));
|
|
2377
|
+
const wheelEdgeFadeColor = parseColor(properties.wheelEdgeFadeColor ?? '#FFFFFF');
|
|
1569
2378
|
const handleWheelMomentumEnd = event => {
|
|
1570
2379
|
const offsetY = Number(event?.nativeEvent?.contentOffset?.y ?? 0);
|
|
1571
2380
|
const settledIndex = clampWheelIndex(Math.round(offsetY / wheelRowStride));
|
|
@@ -1580,11 +2389,17 @@ function WheelPicker({
|
|
|
1580
2389
|
};
|
|
1581
2390
|
return /*#__PURE__*/_jsxs(View, {
|
|
1582
2391
|
style: {
|
|
2392
|
+
width: '100%',
|
|
2393
|
+
alignSelf: 'stretch',
|
|
1583
2394
|
height: wheelContainerHeight,
|
|
1584
|
-
overflow: 'hidden'
|
|
2395
|
+
overflow: 'hidden',
|
|
2396
|
+
justifyContent: 'center'
|
|
1585
2397
|
},
|
|
1586
2398
|
children: [/*#__PURE__*/_jsx(Animated.ScrollView, {
|
|
1587
2399
|
ref: wheelScrollRef,
|
|
2400
|
+
style: {
|
|
2401
|
+
width: '100%'
|
|
2402
|
+
},
|
|
1588
2403
|
showsVerticalScrollIndicator: false,
|
|
1589
2404
|
bounces: false,
|
|
1590
2405
|
decelerationRate: "fast",
|
|
@@ -1593,7 +2408,9 @@ function WheelPicker({
|
|
|
1593
2408
|
disableIntervalMomentum: false,
|
|
1594
2409
|
onMomentumScrollEnd: handleWheelMomentumEnd,
|
|
1595
2410
|
contentContainerStyle: {
|
|
1596
|
-
|
|
2411
|
+
width: '100%',
|
|
2412
|
+
paddingVertical: centerPadding,
|
|
2413
|
+
alignItems: 'center'
|
|
1597
2414
|
},
|
|
1598
2415
|
onScroll: Animated.event([{
|
|
1599
2416
|
nativeEvent: {
|
|
@@ -1629,8 +2446,10 @@ function WheelPicker({
|
|
|
1629
2446
|
});
|
|
1630
2447
|
return /*#__PURE__*/_jsx(Animated.View, {
|
|
1631
2448
|
style: {
|
|
2449
|
+
width: '100%',
|
|
1632
2450
|
height: wheelRowStride,
|
|
1633
2451
|
justifyContent: 'center',
|
|
2452
|
+
alignItems: 'center',
|
|
1634
2453
|
opacity,
|
|
1635
2454
|
transform: [{
|
|
1636
2455
|
perspective: 1200
|
|
@@ -1644,8 +2463,10 @@ function WheelPicker({
|
|
|
1644
2463
|
},
|
|
1645
2464
|
children: /*#__PURE__*/_jsx(View, {
|
|
1646
2465
|
style: {
|
|
2466
|
+
width: '100%',
|
|
1647
2467
|
minHeight: wheelItemHeight,
|
|
1648
|
-
justifyContent: 'center'
|
|
2468
|
+
justifyContent: 'center',
|
|
2469
|
+
alignItems: 'center'
|
|
1649
2470
|
},
|
|
1650
2471
|
children: renderItem(option, index)
|
|
1651
2472
|
})
|
|
@@ -1664,9 +2485,9 @@ function WheelPicker({
|
|
|
1664
2485
|
borderColor: overlayBorderColor,
|
|
1665
2486
|
backgroundColor: overlayBackgroundColor
|
|
1666
2487
|
}
|
|
1667
|
-
}), /*#__PURE__*/_jsx(LinearGradient, {
|
|
2488
|
+
}), wheelEdgeFadeOpacity > 0 && centerPadding > 0 ? /*#__PURE__*/_jsx(LinearGradient, {
|
|
1668
2489
|
pointerEvents: "none",
|
|
1669
|
-
colors: [
|
|
2490
|
+
colors: [withOpacity(wheelEdgeFadeColor, wheelEdgeFadeOpacity), withOpacity(wheelEdgeFadeColor, 0)],
|
|
1670
2491
|
style: {
|
|
1671
2492
|
position: 'absolute',
|
|
1672
2493
|
top: 0,
|
|
@@ -1674,9 +2495,9 @@ function WheelPicker({
|
|
|
1674
2495
|
right: 0,
|
|
1675
2496
|
height: centerPadding
|
|
1676
2497
|
}
|
|
1677
|
-
}), /*#__PURE__*/_jsx(LinearGradient, {
|
|
2498
|
+
}) : null, wheelEdgeFadeOpacity > 0 && centerPadding > 0 ? /*#__PURE__*/_jsx(LinearGradient, {
|
|
1678
2499
|
pointerEvents: "none",
|
|
1679
|
-
colors: [
|
|
2500
|
+
colors: [withOpacity(wheelEdgeFadeColor, 0), withOpacity(wheelEdgeFadeColor, wheelEdgeFadeOpacity)],
|
|
1680
2501
|
style: {
|
|
1681
2502
|
position: 'absolute',
|
|
1682
2503
|
bottom: 0,
|
|
@@ -1684,12 +2505,17 @@ function WheelPicker({
|
|
|
1684
2505
|
right: 0,
|
|
1685
2506
|
height: centerPadding
|
|
1686
2507
|
}
|
|
1687
|
-
})]
|
|
2508
|
+
}) : null]
|
|
1688
2509
|
});
|
|
1689
2510
|
}
|
|
1690
2511
|
return /*#__PURE__*/_jsx(View, {
|
|
2512
|
+
style: {
|
|
2513
|
+
width: '100%',
|
|
2514
|
+
alignSelf: 'stretch'
|
|
2515
|
+
},
|
|
1691
2516
|
children: resolvedOptions.map((option, index) => /*#__PURE__*/_jsx(View, {
|
|
1692
2517
|
style: {
|
|
2518
|
+
width: '100%',
|
|
1693
2519
|
marginBottom: spacing
|
|
1694
2520
|
},
|
|
1695
2521
|
children: renderItem(option, index)
|
|
@@ -2113,16 +2939,33 @@ function RadarChart({
|
|
|
2113
2939
|
const axisColor = parseColor(properties.axisColor ?? '0xFF9E9E9E');
|
|
2114
2940
|
const axisStrokeWidth = Number(properties.axisStrokeWidth ?? 1);
|
|
2115
2941
|
const axisLabelStyle = getTextStyle(properties.axisLabelStyle ?? {}, 'color', '0xFF424242');
|
|
2942
|
+
const resolvedAxisLabelFontSize = resolveRadarAxisLabelFontSize(axisLabelStyle.fontSize);
|
|
2943
|
+
const autoFitLabels = properties.autoFitLabels !== false;
|
|
2944
|
+
const resolvedLabelOffset = resolveRadarLabelOffset(properties.labelOffset, resolvedAxisLabelFontSize);
|
|
2116
2945
|
const showLegend = properties.showLegend !== false;
|
|
2117
2946
|
const legendPosition = (properties.legendPosition ?? 'bottom').toLowerCase();
|
|
2118
2947
|
const legendSpacing = Number(properties.legendSpacing ?? 12);
|
|
2119
2948
|
const legendStyle = getTextStyle(properties.legendStyle ?? {}, 'color', '0xFF212121');
|
|
2120
2949
|
const shape = (properties.shape ?? 'polygon').toLowerCase();
|
|
2121
|
-
const
|
|
2122
|
-
const
|
|
2950
|
+
const innerWidth = Math.max(1, width - padding.left - padding.right);
|
|
2951
|
+
const innerHeight = Math.max(1, height - padding.top - padding.bottom);
|
|
2952
|
+
const radius = calculateRadarChartRadius({
|
|
2953
|
+
width: innerWidth,
|
|
2954
|
+
height: innerHeight,
|
|
2955
|
+
padding: {
|
|
2956
|
+
left: 0,
|
|
2957
|
+
right: 0,
|
|
2958
|
+
top: 0,
|
|
2959
|
+
bottom: 0
|
|
2960
|
+
},
|
|
2961
|
+
axisLabels: axes.map(axis => axis.label),
|
|
2962
|
+
fontSize: resolvedAxisLabelFontSize,
|
|
2963
|
+
autoFitLabels,
|
|
2964
|
+
labelOffset: resolvedLabelOffset
|
|
2965
|
+
});
|
|
2123
2966
|
const center = {
|
|
2124
|
-
x:
|
|
2125
|
-
y:
|
|
2967
|
+
x: padding.left + innerWidth / 2,
|
|
2968
|
+
y: padding.top + innerHeight / 2
|
|
2126
2969
|
};
|
|
2127
2970
|
const axisAngle = Math.PI * 2 / axes.length;
|
|
2128
2971
|
const gridPolygons = Array.from({
|
|
@@ -2143,7 +2986,8 @@ function RadarChart({
|
|
|
2143
2986
|
const points = dataset.values.map((value, index) => {
|
|
2144
2987
|
const axis = normalizedAxes[index];
|
|
2145
2988
|
const maxValue = axis?.maxValue ?? 0;
|
|
2146
|
-
const
|
|
2989
|
+
const normalizedRatio = maxValue > 0 ? value / maxValue : 0;
|
|
2990
|
+
const ratio = Math.max(0, Math.min(1, normalizedRatio)) * Math.max(0, Math.min(1, lineProgress));
|
|
2147
2991
|
const angle = index * axisAngle - Math.PI / 2;
|
|
2148
2992
|
const x = center.x + radius * ratio * Math.cos(angle);
|
|
2149
2993
|
const y = center.y + radius * ratio * Math.sin(angle);
|
|
@@ -2154,27 +2998,32 @@ function RadarChart({
|
|
|
2154
2998
|
points
|
|
2155
2999
|
};
|
|
2156
3000
|
});
|
|
3001
|
+
const isVerticalLegend = legendPosition === 'left' || legendPosition === 'right';
|
|
2157
3002
|
const legend = /*#__PURE__*/_jsx(View, {
|
|
2158
3003
|
style: {
|
|
2159
|
-
flexDirection: 'row',
|
|
2160
|
-
flexWrap: 'wrap'
|
|
3004
|
+
flexDirection: isVerticalLegend ? 'column' : 'row',
|
|
3005
|
+
flexWrap: isVerticalLegend ? 'nowrap' : 'wrap',
|
|
3006
|
+
alignItems: isVerticalLegend ? 'flex-start' : 'center'
|
|
2161
3007
|
},
|
|
2162
3008
|
children: datasets.map((dataset, index) => /*#__PURE__*/_jsxs(View, {
|
|
2163
3009
|
style: {
|
|
2164
3010
|
flexDirection: 'row',
|
|
2165
3011
|
alignItems: 'center',
|
|
2166
|
-
marginRight: legendSpacing,
|
|
2167
|
-
marginBottom: 6
|
|
3012
|
+
marginRight: isVerticalLegend ? 0 : legendSpacing,
|
|
3013
|
+
marginBottom: isVerticalLegend ? legendSpacing : 6
|
|
2168
3014
|
},
|
|
2169
3015
|
children: [/*#__PURE__*/_jsx(View, {
|
|
2170
3016
|
style: {
|
|
2171
3017
|
width: 12,
|
|
2172
3018
|
height: 12,
|
|
2173
3019
|
borderRadius: 6,
|
|
2174
|
-
backgroundColor: dataset.borderColor,
|
|
3020
|
+
backgroundColor: dataset.fillColor ?? dataset.borderColor,
|
|
3021
|
+
borderWidth: 1,
|
|
3022
|
+
borderColor: dataset.borderColor,
|
|
2175
3023
|
marginRight: 6
|
|
2176
3024
|
}
|
|
2177
3025
|
}), /*#__PURE__*/_jsx(Text, {
|
|
3026
|
+
numberOfLines: 1,
|
|
2178
3027
|
style: legendStyle,
|
|
2179
3028
|
children: dataset.label
|
|
2180
3029
|
})]
|
|
@@ -2182,12 +3031,10 @@ function RadarChart({
|
|
|
2182
3031
|
});
|
|
2183
3032
|
const chart = /*#__PURE__*/_jsx(Animated.View, {
|
|
2184
3033
|
style: {
|
|
2185
|
-
width: normalizedWidth ??
|
|
3034
|
+
width: isVerticalLegend ? width : normalizedWidth ?? '100%',
|
|
3035
|
+
maxWidth: '100%',
|
|
3036
|
+
flexShrink: 1,
|
|
2186
3037
|
height,
|
|
2187
|
-
paddingLeft: padding.left,
|
|
2188
|
-
paddingRight: padding.right,
|
|
2189
|
-
paddingTop: padding.top,
|
|
2190
|
-
paddingBottom: padding.bottom,
|
|
2191
3038
|
opacity: reveal
|
|
2192
3039
|
},
|
|
2193
3040
|
onLayout: event => {
|
|
@@ -2226,24 +3073,28 @@ function RadarChart({
|
|
|
2226
3073
|
}, `axis-${index}`);
|
|
2227
3074
|
}), axes.map((axis, index) => {
|
|
2228
3075
|
const angle = index * axisAngle - Math.PI / 2;
|
|
2229
|
-
const labelX = center.x + (radius +
|
|
2230
|
-
const labelY = center.y + (radius +
|
|
3076
|
+
const labelX = center.x + (radius + resolvedLabelOffset) * Math.cos(angle);
|
|
3077
|
+
const labelY = center.y + (radius + resolvedLabelOffset) * Math.sin(angle);
|
|
2231
3078
|
return /*#__PURE__*/_jsx(SvgText, {
|
|
2232
3079
|
x: labelX,
|
|
2233
3080
|
y: labelY,
|
|
2234
|
-
fontSize:
|
|
3081
|
+
fontSize: resolvedAxisLabelFontSize,
|
|
2235
3082
|
fontWeight: axisLabelStyle.fontWeight,
|
|
2236
3083
|
fill: axisLabelStyle.color,
|
|
2237
3084
|
textAnchor: "middle",
|
|
2238
3085
|
children: axis.label
|
|
2239
3086
|
}, `label-${index}`);
|
|
2240
|
-
}), datasetPolygons.map((entry, index) =>
|
|
2241
|
-
|
|
2242
|
-
|
|
2243
|
-
|
|
2244
|
-
|
|
2245
|
-
|
|
2246
|
-
|
|
3087
|
+
}), datasetPolygons.map((entry, index) => {
|
|
3088
|
+
const strokePattern = resolveRadarStrokePattern(entry.dataset);
|
|
3089
|
+
return /*#__PURE__*/_jsx(Polygon, {
|
|
3090
|
+
points: entry.points.join(' '),
|
|
3091
|
+
stroke: entry.dataset.borderColor,
|
|
3092
|
+
strokeWidth: entry.dataset.borderWidth,
|
|
3093
|
+
fill: entry.dataset.fillColor ?? 'transparent',
|
|
3094
|
+
strokeDasharray: strokePattern.strokeDasharray,
|
|
3095
|
+
strokeLinecap: strokePattern.strokeLinecap
|
|
3096
|
+
}, `dataset-${index}`);
|
|
3097
|
+
}), datasetPolygons.map((entry, datasetIndex) => entry.dataset.showPoints ? entry.points.map((point, pointIndex) => {
|
|
2247
3098
|
const [x, y] = point.split(',').map(val => Number(val));
|
|
2248
3099
|
return /*#__PURE__*/_jsx(Circle, {
|
|
2249
3100
|
cx: x,
|
|
@@ -2255,23 +3106,23 @@ function RadarChart({
|
|
|
2255
3106
|
})
|
|
2256
3107
|
})
|
|
2257
3108
|
});
|
|
2258
|
-
|
|
2259
|
-
|
|
2260
|
-
backgroundColor
|
|
2261
|
-
},
|
|
2262
|
-
children: [chart, showLegend && legendPosition === 'bottom' ? /*#__PURE__*/_jsx(View, {
|
|
3109
|
+
if (!showLegend) {
|
|
3110
|
+
return /*#__PURE__*/_jsx(View, {
|
|
2263
3111
|
style: {
|
|
2264
|
-
|
|
3112
|
+
backgroundColor
|
|
2265
3113
|
},
|
|
2266
|
-
children:
|
|
2267
|
-
})
|
|
2268
|
-
}
|
|
2269
|
-
if (!showLegend || legendPosition === 'bottom') return composed;
|
|
3114
|
+
children: chart
|
|
3115
|
+
});
|
|
3116
|
+
}
|
|
2270
3117
|
if (legendPosition === 'top') {
|
|
2271
3118
|
return /*#__PURE__*/_jsxs(View, {
|
|
3119
|
+
style: {
|
|
3120
|
+
backgroundColor,
|
|
3121
|
+
alignItems: 'center'
|
|
3122
|
+
},
|
|
2272
3123
|
children: [legend, /*#__PURE__*/_jsx(View, {
|
|
2273
3124
|
style: {
|
|
2274
|
-
height:
|
|
3125
|
+
height: legendSpacing
|
|
2275
3126
|
}
|
|
2276
3127
|
}), chart]
|
|
2277
3128
|
});
|
|
@@ -2279,11 +3130,13 @@ function RadarChart({
|
|
|
2279
3130
|
if (legendPosition === 'left') {
|
|
2280
3131
|
return /*#__PURE__*/_jsxs(View, {
|
|
2281
3132
|
style: {
|
|
2282
|
-
|
|
3133
|
+
backgroundColor,
|
|
3134
|
+
flexDirection: 'row',
|
|
3135
|
+
alignItems: 'center'
|
|
2283
3136
|
},
|
|
2284
3137
|
children: [legend, /*#__PURE__*/_jsx(View, {
|
|
2285
3138
|
style: {
|
|
2286
|
-
width:
|
|
3139
|
+
width: legendSpacing
|
|
2287
3140
|
}
|
|
2288
3141
|
}), chart]
|
|
2289
3142
|
});
|
|
@@ -2291,16 +3144,167 @@ function RadarChart({
|
|
|
2291
3144
|
if (legendPosition === 'right') {
|
|
2292
3145
|
return /*#__PURE__*/_jsxs(View, {
|
|
2293
3146
|
style: {
|
|
2294
|
-
|
|
3147
|
+
backgroundColor,
|
|
3148
|
+
flexDirection: 'row',
|
|
3149
|
+
alignItems: 'center'
|
|
2295
3150
|
},
|
|
2296
3151
|
children: [chart, /*#__PURE__*/_jsx(View, {
|
|
2297
3152
|
style: {
|
|
2298
|
-
width:
|
|
3153
|
+
width: legendSpacing
|
|
2299
3154
|
}
|
|
2300
3155
|
}), legend]
|
|
2301
3156
|
});
|
|
2302
3157
|
}
|
|
2303
|
-
return
|
|
3158
|
+
return /*#__PURE__*/_jsxs(View, {
|
|
3159
|
+
style: {
|
|
3160
|
+
backgroundColor,
|
|
3161
|
+
alignItems: 'center'
|
|
3162
|
+
},
|
|
3163
|
+
children: [chart, /*#__PURE__*/_jsx(View, {
|
|
3164
|
+
style: {
|
|
3165
|
+
height: legendSpacing
|
|
3166
|
+
}
|
|
3167
|
+
}), legend]
|
|
3168
|
+
});
|
|
3169
|
+
}
|
|
3170
|
+
const RADAR_DASH_DEFAULT_LENGTH = 8;
|
|
3171
|
+
const RADAR_DASH_GAP_FACTOR = 0.6;
|
|
3172
|
+
const RADAR_DOT_DASH_LENGTH = 0.001;
|
|
3173
|
+
const RADAR_DOT_GAP_FACTOR = 2.4;
|
|
3174
|
+
const RADAR_DOT_MIN_GAP = 3;
|
|
3175
|
+
const radarDashConflictWarnings = new Set();
|
|
3176
|
+
function isDevelopmentEnvironment() {
|
|
3177
|
+
return process.env.NODE_ENV !== 'production';
|
|
3178
|
+
}
|
|
3179
|
+
function warnRadarDashConflictOnce(label) {
|
|
3180
|
+
if (!isDevelopmentEnvironment()) return;
|
|
3181
|
+
if (radarDashConflictWarnings.has(label)) return;
|
|
3182
|
+
radarDashConflictWarnings.add(label);
|
|
3183
|
+
// Keep API backward-compatible but deterministic when both flags are passed.
|
|
3184
|
+
console.warn(`[RadarChart] Dataset "${label}" received both dotted=true and dashed=true. dashed takes precedence.`);
|
|
3185
|
+
}
|
|
3186
|
+
function resolveRadarBorderStyle(dataset, fallbackLabel) {
|
|
3187
|
+
const dotted = dataset.dotted === true;
|
|
3188
|
+
const dashed = dataset.dashed === true;
|
|
3189
|
+
if (dotted && dashed) {
|
|
3190
|
+
warnRadarDashConflictOnce(fallbackLabel);
|
|
3191
|
+
return 'dashed';
|
|
3192
|
+
}
|
|
3193
|
+
if (dashed) return 'dashed';
|
|
3194
|
+
if (dotted) return 'dotted';
|
|
3195
|
+
const borderStyle = (dataset.borderStyle ?? 'solid').toString().toLowerCase();
|
|
3196
|
+
if (borderStyle === 'dotted' || borderStyle === 'dashed') {
|
|
3197
|
+
return borderStyle;
|
|
3198
|
+
}
|
|
3199
|
+
return 'solid';
|
|
3200
|
+
}
|
|
3201
|
+
function resolveRadarStrokePattern(dataset) {
|
|
3202
|
+
if (dataset.borderStyle === 'dotted') {
|
|
3203
|
+
// Tiny dash + round caps renders circular dots more reliably than short dashes.
|
|
3204
|
+
const gap = Math.max(RADAR_DOT_MIN_GAP, dataset.borderWidth * RADAR_DOT_GAP_FACTOR);
|
|
3205
|
+
return {
|
|
3206
|
+
strokeDasharray: `${RADAR_DOT_DASH_LENGTH} ${gap}`,
|
|
3207
|
+
strokeLinecap: 'round'
|
|
3208
|
+
};
|
|
3209
|
+
}
|
|
3210
|
+
if (dataset.borderStyle === 'dashed') {
|
|
3211
|
+
if (Array.isArray(dataset.dashArray) && dataset.dashArray.length > 0) {
|
|
3212
|
+
const dash = Math.max(1, dataset.dashArray[0] ?? RADAR_DASH_DEFAULT_LENGTH);
|
|
3213
|
+
const gap = Math.max(1, dataset.dashArray[1] ?? dash * RADAR_DASH_GAP_FACTOR);
|
|
3214
|
+
return {
|
|
3215
|
+
strokeDasharray: `${dash} ${gap}`,
|
|
3216
|
+
strokeLinecap: 'butt'
|
|
3217
|
+
};
|
|
3218
|
+
}
|
|
3219
|
+
const dashLength = typeof dataset.dashLength === 'number' && Number.isFinite(dataset.dashLength) && dataset.dashLength > 0 ? dataset.dashLength : RADAR_DASH_DEFAULT_LENGTH;
|
|
3220
|
+
const gap = Math.max(2, dashLength * RADAR_DASH_GAP_FACTOR);
|
|
3221
|
+
return {
|
|
3222
|
+
strokeDasharray: `${dashLength} ${gap}`,
|
|
3223
|
+
strokeLinecap: 'butt'
|
|
3224
|
+
};
|
|
3225
|
+
}
|
|
3226
|
+
return {};
|
|
3227
|
+
}
|
|
3228
|
+
export const RADAR_LABEL_ESTIMATION = {
|
|
3229
|
+
minRadius: 10,
|
|
3230
|
+
safeEdge: 4,
|
|
3231
|
+
minLabelWidthFactor: 1.2,
|
|
3232
|
+
avgCharWidthFactor: 0.58,
|
|
3233
|
+
lineHeightFactor: 1.2,
|
|
3234
|
+
defaultFontSize: 14,
|
|
3235
|
+
minLabelOffset: 20,
|
|
3236
|
+
labelOffsetFactor: 1.5
|
|
3237
|
+
};
|
|
3238
|
+
export function resolveRadarAxisLabelFontSize(value) {
|
|
3239
|
+
const parsed = Number(value);
|
|
3240
|
+
if (!Number.isFinite(parsed) || parsed <= 0) {
|
|
3241
|
+
return RADAR_LABEL_ESTIMATION.defaultFontSize;
|
|
3242
|
+
}
|
|
3243
|
+
return parsed;
|
|
3244
|
+
}
|
|
3245
|
+
export function resolveRadarLabelOffset(value, fontSize) {
|
|
3246
|
+
const parsed = Number(value);
|
|
3247
|
+
if (Number.isFinite(parsed) && parsed > 0) {
|
|
3248
|
+
return parsed;
|
|
3249
|
+
}
|
|
3250
|
+
return Math.max(RADAR_LABEL_ESTIMATION.minLabelOffset, fontSize * RADAR_LABEL_ESTIMATION.labelOffsetFactor);
|
|
3251
|
+
}
|
|
3252
|
+
export function estimateRadarLabelSize(label, fontSize) {
|
|
3253
|
+
const safeLabel = `${label ?? ''}`;
|
|
3254
|
+
const width = Math.max(safeLabel.length * fontSize * RADAR_LABEL_ESTIMATION.avgCharWidthFactor, fontSize * RADAR_LABEL_ESTIMATION.minLabelWidthFactor);
|
|
3255
|
+
const height = fontSize * RADAR_LABEL_ESTIMATION.lineHeightFactor;
|
|
3256
|
+
return {
|
|
3257
|
+
width,
|
|
3258
|
+
height
|
|
3259
|
+
};
|
|
3260
|
+
}
|
|
3261
|
+
export function calculateRadarChartRadius({
|
|
3262
|
+
width,
|
|
3263
|
+
height,
|
|
3264
|
+
padding,
|
|
3265
|
+
axisLabels,
|
|
3266
|
+
fontSize,
|
|
3267
|
+
autoFitLabels = true,
|
|
3268
|
+
labelOffset
|
|
3269
|
+
}) {
|
|
3270
|
+
const center = {
|
|
3271
|
+
x: width / 2,
|
|
3272
|
+
y: height / 2
|
|
3273
|
+
};
|
|
3274
|
+
const baseRadius = Math.max(Math.min(width, height) / 2 - Math.max(padding.left, padding.right, padding.top, padding.bottom), RADAR_LABEL_ESTIMATION.minRadius);
|
|
3275
|
+
if (!autoFitLabels || axisLabels.length === 0) {
|
|
3276
|
+
return baseRadius;
|
|
3277
|
+
}
|
|
3278
|
+
const angleStep = Math.PI * 2 / axisLabels.length;
|
|
3279
|
+
let maxAllowedRadius = baseRadius;
|
|
3280
|
+
for (let index = 0; index < axisLabels.length; index += 1) {
|
|
3281
|
+
const {
|
|
3282
|
+
width: labelWidth,
|
|
3283
|
+
height: labelHeight
|
|
3284
|
+
} = estimateRadarLabelSize(axisLabels[index] ?? '', fontSize);
|
|
3285
|
+
const halfWidth = labelWidth / 2;
|
|
3286
|
+
const halfHeight = labelHeight / 2;
|
|
3287
|
+
const angle = index * angleStep - Math.PI / 2;
|
|
3288
|
+
const dx = Math.cos(angle);
|
|
3289
|
+
const dy = Math.sin(angle);
|
|
3290
|
+
|
|
3291
|
+
// Solve per-axis line constraints so estimated label bounds stay inside.
|
|
3292
|
+
if (dx > 0.0001) {
|
|
3293
|
+
maxAllowedRadius = Math.min(maxAllowedRadius, (width - RADAR_LABEL_ESTIMATION.safeEdge - halfWidth - center.x) / dx - labelOffset);
|
|
3294
|
+
} else if (dx < -0.0001) {
|
|
3295
|
+
maxAllowedRadius = Math.min(maxAllowedRadius, (RADAR_LABEL_ESTIMATION.safeEdge + halfWidth - center.x) / dx - labelOffset);
|
|
3296
|
+
}
|
|
3297
|
+
if (dy > 0.0001) {
|
|
3298
|
+
maxAllowedRadius = Math.min(maxAllowedRadius, (height - RADAR_LABEL_ESTIMATION.safeEdge - halfHeight - center.y) / dy - labelOffset);
|
|
3299
|
+
} else if (dy < -0.0001) {
|
|
3300
|
+
maxAllowedRadius = Math.min(maxAllowedRadius, (RADAR_LABEL_ESTIMATION.safeEdge + halfHeight - center.y) / dy - labelOffset);
|
|
3301
|
+
}
|
|
3302
|
+
}
|
|
3303
|
+
const boundedRadius = Math.min(maxAllowedRadius, baseRadius);
|
|
3304
|
+
if (!Number.isFinite(boundedRadius)) {
|
|
3305
|
+
return baseRadius;
|
|
3306
|
+
}
|
|
3307
|
+
return Math.max(boundedRadius, RADAR_LABEL_ESTIMATION.minRadius);
|
|
2304
3308
|
}
|
|
2305
3309
|
export function parseRadarAxes(rawAxes) {
|
|
2306
3310
|
if (!Array.isArray(rawAxes)) return [];
|
|
@@ -2310,9 +3314,10 @@ export function parseRadarAxes(rawAxes) {
|
|
|
2310
3314
|
const label = axis.label ?? '';
|
|
2311
3315
|
if (!label) return;
|
|
2312
3316
|
const maxValue = resolveNumericValue(axis.maxValue, {}) ?? 100;
|
|
3317
|
+
const normalizedMaxValue = Number.isFinite(maxValue) && maxValue > 0 ? maxValue : 100;
|
|
2313
3318
|
axes.push({
|
|
2314
3319
|
label,
|
|
2315
|
-
maxValue:
|
|
3320
|
+
maxValue: normalizedMaxValue
|
|
2316
3321
|
});
|
|
2317
3322
|
});
|
|
2318
3323
|
return axes;
|
|
@@ -2320,24 +3325,27 @@ export function parseRadarAxes(rawAxes) {
|
|
|
2320
3325
|
export function parseRadarDatasets(raw, axisLength, formData) {
|
|
2321
3326
|
if (!Array.isArray(raw) || axisLength === 0) return [];
|
|
2322
3327
|
const datasets = [];
|
|
2323
|
-
raw.forEach(dataset => {
|
|
3328
|
+
raw.forEach((dataset, datasetIndex) => {
|
|
2324
3329
|
if (!dataset || typeof dataset !== 'object') return;
|
|
2325
3330
|
const valuesRaw = Array.isArray(dataset.data) ? dataset.data : [];
|
|
2326
3331
|
const values = [];
|
|
2327
3332
|
for (let i = 0; i < axisLength; i += 1) {
|
|
2328
3333
|
const rawValue = i < valuesRaw.length ? valuesRaw[i] : null;
|
|
2329
|
-
const resolved = resolveNumericValue(rawValue, formData)
|
|
2330
|
-
values.push(resolved);
|
|
3334
|
+
const resolved = resolveNumericValue(rawValue, formData);
|
|
3335
|
+
values.push(typeof resolved === 'number' && Number.isFinite(resolved) ? resolved : 0);
|
|
2331
3336
|
}
|
|
2332
3337
|
const label = dataset.label ?? `Dataset ${datasets.length + 1}`;
|
|
2333
3338
|
const borderColor = parseColor(dataset.borderColor ?? '0xFF2196F3');
|
|
2334
3339
|
const fillColor = dataset.fillColor ? parseColor(dataset.fillColor) : undefined;
|
|
2335
3340
|
const pointColor = parseColor(dataset.pointColor ?? dataset.borderColor ?? '0xFF2196F3');
|
|
2336
|
-
const
|
|
2337
|
-
const
|
|
2338
|
-
const
|
|
2339
|
-
const
|
|
2340
|
-
const
|
|
3341
|
+
const rawBorderWidth = Number(dataset.borderWidth ?? 2);
|
|
3342
|
+
const borderWidth = Number.isFinite(rawBorderWidth) && rawBorderWidth > 0 ? rawBorderWidth : 2;
|
|
3343
|
+
const rawPointRadius = Number(dataset.pointRadius ?? 4);
|
|
3344
|
+
const pointRadius = Number.isFinite(rawPointRadius) && rawPointRadius >= 0 ? rawPointRadius : 4;
|
|
3345
|
+
const normalizedStyle = resolveRadarBorderStyle(dataset, label ?? `Dataset ${datasetIndex + 1}`);
|
|
3346
|
+
const dashArray = Array.isArray(dataset.dashArray) ? dataset.dashArray.map(val => Number(val)).filter(val => Number.isFinite(val) && val > 0) : undefined;
|
|
3347
|
+
const rawDashLength = Number(dataset.dashLength);
|
|
3348
|
+
const dashLength = Number.isFinite(rawDashLength) && rawDashLength > 0 ? rawDashLength : undefined;
|
|
2341
3349
|
const showPoints = dataset.showPoints !== false;
|
|
2342
3350
|
datasets.push({
|
|
2343
3351
|
label,
|
|
@@ -2346,6 +3354,7 @@ export function parseRadarDatasets(raw, axisLength, formData) {
|
|
|
2346
3354
|
borderWidth,
|
|
2347
3355
|
borderStyle: normalizedStyle,
|
|
2348
3356
|
dashArray,
|
|
3357
|
+
dashLength,
|
|
2349
3358
|
showPoints,
|
|
2350
3359
|
pointRadius,
|
|
2351
3360
|
pointColor,
|
|
@@ -2359,7 +3368,9 @@ export function normalizeAxisMaximums(axes, datasets) {
|
|
|
2359
3368
|
let maxValue = axis.maxValue;
|
|
2360
3369
|
datasets.forEach(dataset => {
|
|
2361
3370
|
const value = dataset.values[index];
|
|
2362
|
-
if (value !== undefined && value > maxValue)
|
|
3371
|
+
if (value !== undefined && Number.isFinite(value) && value > maxValue) {
|
|
3372
|
+
maxValue = value;
|
|
3373
|
+
}
|
|
2363
3374
|
});
|
|
2364
3375
|
return {
|
|
2365
3376
|
...axis,
|
|
@@ -2368,12 +3379,15 @@ export function normalizeAxisMaximums(axes, datasets) {
|
|
|
2368
3379
|
});
|
|
2369
3380
|
}
|
|
2370
3381
|
function resolveRadarWidth(parsedWidth, measuredWidth) {
|
|
3382
|
+
if (measuredWidth > 0) {
|
|
3383
|
+
if (typeof parsedWidth === 'number' && Number.isFinite(parsedWidth)) {
|
|
3384
|
+
return Math.max(1, Math.min(parsedWidth, measuredWidth));
|
|
3385
|
+
}
|
|
3386
|
+
return Math.max(1, measuredWidth);
|
|
3387
|
+
}
|
|
2371
3388
|
if (typeof parsedWidth === 'number' && Number.isFinite(parsedWidth)) {
|
|
2372
3389
|
return parsedWidth;
|
|
2373
3390
|
}
|
|
2374
|
-
if (measuredWidth > 0) {
|
|
2375
|
-
return measuredWidth;
|
|
2376
|
-
}
|
|
2377
3391
|
return 300;
|
|
2378
3392
|
}
|
|
2379
3393
|
function radarPlaceholder(message) {
|