flowboard-react 0.4.3 → 0.5.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 +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 +1092 -148
- 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 +1609 -197
- package/src/types/flowboard.ts +1 -0
- package/src/utils/flowboardUtils.ts +4 -0
|
@@ -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, 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,7 @@ 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,
|
|
72
100
|
contentContainerStyle: {
|
|
73
101
|
...contentPaddingStyle,
|
|
74
102
|
paddingTop: showProgress ? 0 : padding.top
|
|
@@ -76,14 +104,20 @@ export default function FlowboardRenderer(props) {
|
|
|
76
104
|
children: /*#__PURE__*/_jsx(View, {
|
|
77
105
|
style: {
|
|
78
106
|
flexGrow: 1,
|
|
107
|
+
flexDirection: 'column',
|
|
79
108
|
justifyContent: parseFlexAlignment(screenData.mainAxisAlignment),
|
|
80
|
-
alignItems: parseRootCrossAlignment(rootCrossAxisAlignment)
|
|
109
|
+
alignItems: parseRootCrossAlignment(rootCrossAxisAlignment),
|
|
110
|
+
minHeight: 0,
|
|
111
|
+
minWidth: 0
|
|
81
112
|
},
|
|
82
113
|
children: childrenData.map((child, index) => buildWidget(child, {
|
|
83
114
|
onAction,
|
|
84
115
|
formData,
|
|
85
116
|
onInputChange,
|
|
86
117
|
allowFlexExpansion: true,
|
|
118
|
+
parentFlexAxis: rootAxis,
|
|
119
|
+
parentMainAxisBounded: pageAxisBounds.heightBounded,
|
|
120
|
+
pageAxisBounds,
|
|
87
121
|
screenData,
|
|
88
122
|
enableFontAwesomeIcons,
|
|
89
123
|
key: `child-${index}`
|
|
@@ -102,6 +136,9 @@ export default function FlowboardRenderer(props) {
|
|
|
102
136
|
formData,
|
|
103
137
|
onInputChange,
|
|
104
138
|
allowFlexExpansion: true,
|
|
139
|
+
parentFlexAxis: rootAxis,
|
|
140
|
+
parentMainAxisBounded: true,
|
|
141
|
+
pageAxisBounds,
|
|
105
142
|
screenData,
|
|
106
143
|
enableFontAwesomeIcons,
|
|
107
144
|
key: `child-${index}`
|
|
@@ -110,12 +147,10 @@ export default function FlowboardRenderer(props) {
|
|
|
110
147
|
});
|
|
111
148
|
return /*#__PURE__*/_jsxs(View, {
|
|
112
149
|
style: styles.root,
|
|
113
|
-
children: [renderBackground(backgroundData, bgColorCode),
|
|
114
|
-
style: styles.
|
|
115
|
-
mode: "padding",
|
|
116
|
-
edges: ['top', 'right', 'bottom', 'left'],
|
|
150
|
+
children: [renderBackground(backgroundData, bgColorCode), /*#__PURE__*/_jsx(View, {
|
|
151
|
+
style: [styles.contentLayer, safeAreaPaddingStyle],
|
|
117
152
|
children: content
|
|
118
|
-
})
|
|
153
|
+
})]
|
|
119
154
|
});
|
|
120
155
|
}
|
|
121
156
|
function renderBackground(bgData, legacyColorCode) {
|
|
@@ -272,17 +307,411 @@ function renderProgressBar(currentIndex, totalScreens, color, thickness, radius,
|
|
|
272
307
|
})
|
|
273
308
|
});
|
|
274
309
|
}
|
|
310
|
+
const STACK_SPACE_DISTRIBUTIONS = new Set(['spaceBetween', 'spaceAround', 'spaceEvenly']);
|
|
311
|
+
function normalizeStackSpacingConfig(value) {
|
|
312
|
+
if (typeof value === 'number' && Number.isFinite(value)) {
|
|
313
|
+
return {
|
|
314
|
+
x: value,
|
|
315
|
+
y: value
|
|
316
|
+
};
|
|
317
|
+
}
|
|
318
|
+
if (!value || typeof value !== 'object') {
|
|
319
|
+
return {
|
|
320
|
+
x: 0,
|
|
321
|
+
y: 0
|
|
322
|
+
};
|
|
323
|
+
}
|
|
324
|
+
const x = Number(value.x ?? value.horizontal);
|
|
325
|
+
const y = Number(value.y ?? value.vertical);
|
|
326
|
+
const top = Number(value.top);
|
|
327
|
+
const right = Number(value.right);
|
|
328
|
+
const bottom = Number(value.bottom);
|
|
329
|
+
const left = Number(value.left);
|
|
330
|
+
const normalized = {};
|
|
331
|
+
if (!Number.isNaN(x)) normalized.x = x;
|
|
332
|
+
if (!Number.isNaN(y)) normalized.y = y;
|
|
333
|
+
if (!Number.isNaN(top)) normalized.top = top;
|
|
334
|
+
if (!Number.isNaN(right)) normalized.right = right;
|
|
335
|
+
if (!Number.isNaN(bottom)) normalized.bottom = bottom;
|
|
336
|
+
if (!Number.isNaN(left)) normalized.left = left;
|
|
337
|
+
if (Object.keys(normalized).length === 0) {
|
|
338
|
+
return {
|
|
339
|
+
x: 0,
|
|
340
|
+
y: 0
|
|
341
|
+
};
|
|
342
|
+
}
|
|
343
|
+
return normalized;
|
|
344
|
+
}
|
|
345
|
+
function stackSpacingToInsets(value) {
|
|
346
|
+
const normalized = normalizeStackSpacingConfig(value);
|
|
347
|
+
return parseInsets({
|
|
348
|
+
horizontal: normalized.x,
|
|
349
|
+
vertical: normalized.y,
|
|
350
|
+
top: normalized.top,
|
|
351
|
+
right: normalized.right,
|
|
352
|
+
bottom: normalized.bottom,
|
|
353
|
+
left: normalized.left
|
|
354
|
+
});
|
|
355
|
+
}
|
|
356
|
+
function isLegacyOverlayAlignment(value) {
|
|
357
|
+
return ['topLeft', 'topCenter', 'topRight', 'centerLeft', 'center', 'centerRight', 'bottomLeft', 'bottomCenter', 'bottomRight'].includes(String(value ?? ''));
|
|
358
|
+
}
|
|
359
|
+
function normalizeStackAxis(type, props) {
|
|
360
|
+
const explicit = String(props.axis ?? '').toLowerCase();
|
|
361
|
+
if (explicit === 'vertical' || explicit === 'horizontal' || explicit === 'overlay') {
|
|
362
|
+
return explicit;
|
|
363
|
+
}
|
|
364
|
+
if (type === 'container') return 'overlay';
|
|
365
|
+
if (type === 'row') return 'horizontal';
|
|
366
|
+
if (type === 'column') return 'vertical';
|
|
367
|
+
if (type === 'layout') {
|
|
368
|
+
return props.direction === 'horizontal' ? 'horizontal' : 'vertical';
|
|
369
|
+
}
|
|
370
|
+
if (type === 'stack' && (props.fit !== undefined || isLegacyOverlayAlignment(props.alignment))) {
|
|
371
|
+
return 'overlay';
|
|
372
|
+
}
|
|
373
|
+
return 'vertical';
|
|
374
|
+
}
|
|
375
|
+
function normalizeStackDistribution(value) {
|
|
376
|
+
const normalized = String(value ?? '').trim();
|
|
377
|
+
if (normalized === 'center' || normalized === 'end' || normalized === 'spaceBetween' || normalized === 'spaceAround' || normalized === 'spaceEvenly') {
|
|
378
|
+
return normalized;
|
|
379
|
+
}
|
|
380
|
+
return 'start';
|
|
381
|
+
}
|
|
382
|
+
function normalizeStackCrossAlignment(value) {
|
|
383
|
+
const raw = String(value ?? '').toLowerCase();
|
|
384
|
+
if (raw === 'center' || raw.includes('center')) return 'center';
|
|
385
|
+
if (raw === 'end' || raw.endsWith('right') || raw.startsWith('bottom')) return 'end';
|
|
386
|
+
return 'start';
|
|
387
|
+
}
|
|
388
|
+
function normalizeOverlayAlignment(value) {
|
|
389
|
+
const raw = String(value ?? '').trim();
|
|
390
|
+
switch (raw) {
|
|
391
|
+
case 'topStart':
|
|
392
|
+
case 'top':
|
|
393
|
+
case 'topEnd':
|
|
394
|
+
case 'start':
|
|
395
|
+
case 'center':
|
|
396
|
+
case 'end':
|
|
397
|
+
case 'bottomStart':
|
|
398
|
+
case 'bottom':
|
|
399
|
+
case 'bottomEnd':
|
|
400
|
+
return raw;
|
|
401
|
+
case 'topLeft':
|
|
402
|
+
return 'topStart';
|
|
403
|
+
case 'topCenter':
|
|
404
|
+
return 'top';
|
|
405
|
+
case 'topRight':
|
|
406
|
+
return 'topEnd';
|
|
407
|
+
case 'centerLeft':
|
|
408
|
+
return 'start';
|
|
409
|
+
case 'centerRight':
|
|
410
|
+
return 'end';
|
|
411
|
+
case 'bottomLeft':
|
|
412
|
+
return 'bottomStart';
|
|
413
|
+
case 'bottomCenter':
|
|
414
|
+
return 'bottom';
|
|
415
|
+
case 'bottomRight':
|
|
416
|
+
return 'bottomEnd';
|
|
417
|
+
default:
|
|
418
|
+
return 'center';
|
|
419
|
+
}
|
|
420
|
+
}
|
|
421
|
+
function isFillDimensionValue(value) {
|
|
422
|
+
if (typeof value === 'number') return value === Number.POSITIVE_INFINITY;
|
|
423
|
+
if (typeof value !== 'string') return false;
|
|
424
|
+
const normalized = value.trim().toLowerCase();
|
|
425
|
+
return normalized === 'infinity' || normalized === 'double.infinity' || normalized === '+infinity' || normalized === '100%';
|
|
426
|
+
}
|
|
427
|
+
function isFixedDimensionValue(value) {
|
|
428
|
+
if (isFillDimensionValue(value)) return false;
|
|
429
|
+
const parsed = Number(value);
|
|
430
|
+
return Number.isFinite(parsed);
|
|
431
|
+
}
|
|
432
|
+
function normalizeStackSize(props, axis) {
|
|
433
|
+
const widthMode = String(props.size?.width ?? '').toLowerCase();
|
|
434
|
+
const heightMode = String(props.size?.height ?? '').toLowerCase();
|
|
435
|
+
const width = widthMode === 'fill' || widthMode === 'fit' || widthMode === 'fixed' ? widthMode : props.fit === 'expand' || props.width === 'infinity' ? 'fill' : isFixedDimensionValue(props.width) ? 'fixed' : axis === 'horizontal' ? 'fit' : 'fill';
|
|
436
|
+
const height = heightMode === 'fill' || heightMode === 'fit' || heightMode === 'fixed' ? heightMode : props.fit === 'expand' || props.height === 'infinity' ? 'fill' : isFixedDimensionValue(props.height) ? 'fixed' : axis === 'vertical' ? 'fit' : 'fill';
|
|
437
|
+
return {
|
|
438
|
+
width,
|
|
439
|
+
height
|
|
440
|
+
};
|
|
441
|
+
}
|
|
442
|
+
function normalizeStackFill(props) {
|
|
443
|
+
if (props.fill && typeof props.fill === 'object') {
|
|
444
|
+
const next = {
|
|
445
|
+
...props.fill
|
|
446
|
+
};
|
|
447
|
+
if (!next.type && next.color) next.type = 'solid';
|
|
448
|
+
return next;
|
|
449
|
+
}
|
|
450
|
+
if (props.background && typeof props.background === 'object') {
|
|
451
|
+
if (props.background.type === 'color') {
|
|
452
|
+
return {
|
|
453
|
+
type: 'solid',
|
|
454
|
+
color: props.background.color
|
|
455
|
+
};
|
|
456
|
+
}
|
|
457
|
+
return {
|
|
458
|
+
...props.background
|
|
459
|
+
};
|
|
460
|
+
}
|
|
461
|
+
if (props.backgroundColor) {
|
|
462
|
+
return {
|
|
463
|
+
type: 'solid',
|
|
464
|
+
color: props.backgroundColor
|
|
465
|
+
};
|
|
466
|
+
}
|
|
467
|
+
return undefined;
|
|
468
|
+
}
|
|
469
|
+
function normalizeStackBorder(props) {
|
|
470
|
+
if (props.border && typeof props.border === 'object') {
|
|
471
|
+
return {
|
|
472
|
+
...props.border
|
|
473
|
+
};
|
|
474
|
+
}
|
|
475
|
+
if (props.borderColor || props.borderWidth !== undefined) {
|
|
476
|
+
return {
|
|
477
|
+
width: Number(props.borderWidth ?? 1),
|
|
478
|
+
color: props.borderColor
|
|
479
|
+
};
|
|
480
|
+
}
|
|
481
|
+
return undefined;
|
|
482
|
+
}
|
|
483
|
+
function normalizeStackShadow(props) {
|
|
484
|
+
if (props.shadow && typeof props.shadow === 'object') {
|
|
485
|
+
return {
|
|
486
|
+
...props.shadow
|
|
487
|
+
};
|
|
488
|
+
}
|
|
489
|
+
return undefined;
|
|
490
|
+
}
|
|
491
|
+
function spacingToInsets(value) {
|
|
492
|
+
const normalized = normalizeStackSpacingConfig(value);
|
|
493
|
+
const x = normalized.x ?? 0;
|
|
494
|
+
const y = normalized.y ?? 0;
|
|
495
|
+
return {
|
|
496
|
+
top: normalized.top ?? y,
|
|
497
|
+
right: normalized.right ?? x,
|
|
498
|
+
bottom: normalized.bottom ?? y,
|
|
499
|
+
left: normalized.left ?? x
|
|
500
|
+
};
|
|
501
|
+
}
|
|
502
|
+
function insetsToSpacing(insets) {
|
|
503
|
+
if (insets.top === insets.bottom && insets.left === insets.right) {
|
|
504
|
+
return {
|
|
505
|
+
x: insets.left,
|
|
506
|
+
y: insets.top
|
|
507
|
+
};
|
|
508
|
+
}
|
|
509
|
+
return insets;
|
|
510
|
+
}
|
|
511
|
+
function mergeStackProps(parentProps, childProps) {
|
|
512
|
+
const parentAxis = String(parentProps.axis ?? 'vertical');
|
|
513
|
+
const childAxis = String(childProps.axis ?? 'vertical');
|
|
514
|
+
let axis = childAxis;
|
|
515
|
+
if (parentAxis !== childAxis) {
|
|
516
|
+
if (parentAxis === 'overlay' && childAxis !== 'overlay') axis = childAxis;else if (childAxis === 'overlay' && parentAxis !== 'overlay') axis = parentAxis;
|
|
517
|
+
}
|
|
518
|
+
const axisSource = axis === parentAxis && axis !== childAxis ? parentProps : childProps;
|
|
519
|
+
const fallbackAxisSource = axisSource === childProps ? parentProps : childProps;
|
|
520
|
+
const parentPadding = spacingToInsets(parentProps.layout?.padding);
|
|
521
|
+
const childPadding = spacingToInsets(childProps.layout?.padding);
|
|
522
|
+
const parentMargin = spacingToInsets(parentProps.layout?.margin);
|
|
523
|
+
const childMargin = spacingToInsets(childProps.layout?.margin);
|
|
524
|
+
const mergedProps = {
|
|
525
|
+
axis,
|
|
526
|
+
alignment: axisSource.alignment ?? fallbackAxisSource.alignment ?? 'start',
|
|
527
|
+
distribution: axisSource.distribution ?? fallbackAxisSource.distribution ?? 'start',
|
|
528
|
+
childSpacing: Number(axisSource.childSpacing ?? fallbackAxisSource.childSpacing ?? 0) || 0,
|
|
529
|
+
size: {
|
|
530
|
+
width: childProps.size?.width ?? parentProps.size?.width ?? 'fill',
|
|
531
|
+
height: childProps.size?.height ?? parentProps.size?.height ?? 'fit'
|
|
532
|
+
},
|
|
533
|
+
layout: {
|
|
534
|
+
padding: insetsToSpacing({
|
|
535
|
+
top: parentPadding.top + childPadding.top,
|
|
536
|
+
right: parentPadding.right + childPadding.right,
|
|
537
|
+
bottom: parentPadding.bottom + childPadding.bottom,
|
|
538
|
+
left: parentPadding.left + childPadding.left
|
|
539
|
+
}),
|
|
540
|
+
margin: insetsToSpacing({
|
|
541
|
+
top: parentMargin.top + childMargin.top,
|
|
542
|
+
right: parentMargin.right + childMargin.right,
|
|
543
|
+
bottom: parentMargin.bottom + childMargin.bottom,
|
|
544
|
+
left: parentMargin.left + childMargin.left
|
|
545
|
+
})
|
|
546
|
+
},
|
|
547
|
+
appearance: {
|
|
548
|
+
shape: 'rectangle',
|
|
549
|
+
cornerRadius: Number(childProps.appearance?.cornerRadius ?? parentProps.appearance?.cornerRadius ?? 0)
|
|
550
|
+
}
|
|
551
|
+
};
|
|
552
|
+
if (axis === 'overlay') {
|
|
553
|
+
mergedProps.overlayAlignment = childProps.overlayAlignment ?? parentProps.overlayAlignment ?? 'center';
|
|
554
|
+
}
|
|
555
|
+
if (parentProps.fill || childProps.fill) {
|
|
556
|
+
mergedProps.fill = childProps.fill ?? parentProps.fill;
|
|
557
|
+
}
|
|
558
|
+
if (parentProps.border || childProps.border) {
|
|
559
|
+
mergedProps.border = childProps.border ?? parentProps.border;
|
|
560
|
+
}
|
|
561
|
+
if (parentProps.shadow || childProps.shadow) {
|
|
562
|
+
mergedProps.shadow = childProps.shadow ?? parentProps.shadow;
|
|
563
|
+
}
|
|
564
|
+
if (childProps.width !== undefined || parentProps.width !== undefined) {
|
|
565
|
+
mergedProps.width = childProps.width ?? parentProps.width;
|
|
566
|
+
}
|
|
567
|
+
if (childProps.height !== undefined || parentProps.height !== undefined) {
|
|
568
|
+
mergedProps.height = childProps.height ?? parentProps.height;
|
|
569
|
+
}
|
|
570
|
+
return mergedProps;
|
|
571
|
+
}
|
|
572
|
+
function normalizeStackLikeNode(json) {
|
|
573
|
+
if (!json || typeof json !== 'object') return json;
|
|
574
|
+
const type = String(json.type ?? '');
|
|
575
|
+
if (!['stack', 'container', 'layout', 'row', 'column', 'grid'].includes(type)) {
|
|
576
|
+
return json;
|
|
577
|
+
}
|
|
578
|
+
const props = {
|
|
579
|
+
...(json.properties ?? {})
|
|
580
|
+
};
|
|
581
|
+
const axis = normalizeStackAxis(type, props);
|
|
582
|
+
const size = normalizeStackSize(props, axis);
|
|
583
|
+
const children = Array.isArray(json.children) ? [...json.children] : [];
|
|
584
|
+
if (json.child) {
|
|
585
|
+
children.push(json.child);
|
|
586
|
+
}
|
|
587
|
+
const normalizedProps = {
|
|
588
|
+
axis,
|
|
589
|
+
alignment: normalizeStackCrossAlignment(props.alignment ?? props.crossAxisAlignment),
|
|
590
|
+
distribution: normalizeStackDistribution(props.distribution ?? props.mainAxisAlignment),
|
|
591
|
+
childSpacing: Number(props.childSpacing ?? props.gap ?? props.spacing ?? 0),
|
|
592
|
+
size,
|
|
593
|
+
layout: {
|
|
594
|
+
padding: normalizeStackSpacingConfig(props.layout?.padding ?? props.padding),
|
|
595
|
+
margin: normalizeStackSpacingConfig(props.layout?.margin ?? props.margin)
|
|
596
|
+
},
|
|
597
|
+
appearance: {
|
|
598
|
+
shape: 'rectangle',
|
|
599
|
+
cornerRadius: Number(props.appearance?.cornerRadius ?? props.borderRadius ?? 0)
|
|
600
|
+
}
|
|
601
|
+
};
|
|
602
|
+
if (axis === 'overlay') {
|
|
603
|
+
normalizedProps.overlayAlignment = normalizeOverlayAlignment(props.overlayAlignment ?? props.alignment);
|
|
604
|
+
}
|
|
605
|
+
const fill = normalizeStackFill(props);
|
|
606
|
+
if (fill) normalizedProps.fill = fill;
|
|
607
|
+
const border = normalizeStackBorder(props);
|
|
608
|
+
if (border) normalizedProps.border = border;
|
|
609
|
+
const shadow = normalizeStackShadow(props);
|
|
610
|
+
if (shadow) normalizedProps.shadow = shadow;
|
|
611
|
+
if (props.width !== undefined) normalizedProps.width = props.width;
|
|
612
|
+
if (props.height !== undefined) normalizedProps.height = props.height;
|
|
613
|
+
if ((type === 'container' || type === 'layout') && children.length === 1) {
|
|
614
|
+
const childCandidate = children[0];
|
|
615
|
+
if (childCandidate && typeof childCandidate === 'object') {
|
|
616
|
+
const normalizedChild = normalizeStackLikeNode(childCandidate);
|
|
617
|
+
if (normalizedChild?.type === 'stack') {
|
|
618
|
+
const childProps = {
|
|
619
|
+
...(normalizedChild.properties ?? {})
|
|
620
|
+
};
|
|
621
|
+
const mergedProps = mergeStackProps(normalizedProps, childProps);
|
|
622
|
+
return {
|
|
623
|
+
...json,
|
|
624
|
+
type: 'stack',
|
|
625
|
+
properties: mergedProps,
|
|
626
|
+
children: Array.isArray(normalizedChild.children) ? normalizedChild.children : [],
|
|
627
|
+
child: undefined
|
|
628
|
+
};
|
|
629
|
+
}
|
|
630
|
+
}
|
|
631
|
+
}
|
|
632
|
+
return {
|
|
633
|
+
...json,
|
|
634
|
+
type: 'stack',
|
|
635
|
+
properties: normalizedProps,
|
|
636
|
+
children,
|
|
637
|
+
child: undefined
|
|
638
|
+
};
|
|
639
|
+
}
|
|
640
|
+
function parseOverlayGridAlignment(value) {
|
|
641
|
+
switch (normalizeOverlayAlignment(value)) {
|
|
642
|
+
case 'topStart':
|
|
643
|
+
return {
|
|
644
|
+
justifyContent: 'flex-start',
|
|
645
|
+
alignItems: 'flex-start'
|
|
646
|
+
};
|
|
647
|
+
case 'top':
|
|
648
|
+
return {
|
|
649
|
+
justifyContent: 'flex-start',
|
|
650
|
+
alignItems: 'center'
|
|
651
|
+
};
|
|
652
|
+
case 'topEnd':
|
|
653
|
+
return {
|
|
654
|
+
justifyContent: 'flex-start',
|
|
655
|
+
alignItems: 'flex-end'
|
|
656
|
+
};
|
|
657
|
+
case 'start':
|
|
658
|
+
return {
|
|
659
|
+
justifyContent: 'center',
|
|
660
|
+
alignItems: 'flex-start'
|
|
661
|
+
};
|
|
662
|
+
case 'end':
|
|
663
|
+
return {
|
|
664
|
+
justifyContent: 'center',
|
|
665
|
+
alignItems: 'flex-end'
|
|
666
|
+
};
|
|
667
|
+
case 'bottomStart':
|
|
668
|
+
return {
|
|
669
|
+
justifyContent: 'flex-end',
|
|
670
|
+
alignItems: 'flex-start'
|
|
671
|
+
};
|
|
672
|
+
case 'bottom':
|
|
673
|
+
return {
|
|
674
|
+
justifyContent: 'flex-end',
|
|
675
|
+
alignItems: 'center'
|
|
676
|
+
};
|
|
677
|
+
case 'bottomEnd':
|
|
678
|
+
return {
|
|
679
|
+
justifyContent: 'flex-end',
|
|
680
|
+
alignItems: 'flex-end'
|
|
681
|
+
};
|
|
682
|
+
case 'center':
|
|
683
|
+
default:
|
|
684
|
+
return {
|
|
685
|
+
justifyContent: 'center',
|
|
686
|
+
alignItems: 'center'
|
|
687
|
+
};
|
|
688
|
+
}
|
|
689
|
+
}
|
|
690
|
+
function resolveStackDimension(props, axis, key, axisBounds) {
|
|
691
|
+
const mode = props.size?.[key];
|
|
692
|
+
const legacy = normalizeDimension(parseLayoutDimension(props[key]));
|
|
693
|
+
const isBounded = key === 'width' ? axisBounds.widthBounded : axisBounds.heightBounded;
|
|
694
|
+
if (mode === 'fill') return isBounded ? '100%' : legacy;
|
|
695
|
+
if (mode === 'fit') return legacy;
|
|
696
|
+
if (mode === 'fixed') return legacy;
|
|
697
|
+
if (legacy !== undefined) return legacy;
|
|
698
|
+
if (key === 'width' && axis !== 'horizontal' && axisBounds.widthBounded) return '100%';
|
|
699
|
+
if (key === 'height' && axis === 'horizontal' && axisBounds.heightBounded) return '100%';
|
|
700
|
+
return undefined;
|
|
701
|
+
}
|
|
275
702
|
function buildWidget(json, params) {
|
|
276
703
|
if (!json || typeof json !== 'object') return null;
|
|
277
|
-
const
|
|
278
|
-
const
|
|
704
|
+
const normalizedJson = normalizeStackLikeNode(json);
|
|
705
|
+
const type = normalizedJson.type;
|
|
706
|
+
const id = normalizedJson.id;
|
|
279
707
|
const props = {
|
|
280
|
-
...(
|
|
708
|
+
...(normalizedJson.properties ?? {})
|
|
281
709
|
};
|
|
282
|
-
const
|
|
283
|
-
const
|
|
284
|
-
const
|
|
285
|
-
const
|
|
710
|
+
const pageAxisBounds = params.pageAxisBounds ?? getAxisBoundsForPageScroll(resolvePageScrollAxis(params.screenData));
|
|
711
|
+
const childrenJson = Array.isArray(normalizedJson.children) ? normalizedJson.children : undefined;
|
|
712
|
+
const childJson = normalizedJson.child;
|
|
713
|
+
const action = normalizedJson.action;
|
|
714
|
+
const actionPayload = action ? buildActionPayload(props, normalizedJson) : undefined;
|
|
286
715
|
let node = null;
|
|
287
716
|
switch (type) {
|
|
288
717
|
case 'column':
|
|
@@ -457,55 +886,83 @@ function buildWidget(json, params) {
|
|
|
457
886
|
const width = normalizeDimension(parseLayoutDimension(props.width)) ?? '100%';
|
|
458
887
|
const height = normalizeDimension(parseLayoutDimension(props.height)) ?? 50;
|
|
459
888
|
const label = props.label ?? 'Button';
|
|
889
|
+
const normalizedStroke = normalizeButtonStroke(props.stroke);
|
|
890
|
+
const normalizedEffects = normalizeButtonEffects(props.effects, props.shadow);
|
|
460
891
|
const textStyle = getTextStyle({
|
|
461
892
|
...props,
|
|
462
893
|
height: null
|
|
463
894
|
}, 'textColor', '0xFFFFFFFF');
|
|
464
|
-
const
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
895
|
+
const backgroundColor = parseColor(props.color ?? '0xFF2196F3');
|
|
896
|
+
const shadowBounds = getButtonShadowLayerBounds(normalizedStroke, borderRadius);
|
|
897
|
+
const webShadowStyle = buildButtonWebShadowStyle(normalizedEffects);
|
|
898
|
+
const firstNativeEffect = normalizedEffects.length > 0 ? normalizedEffects[0] : null;
|
|
899
|
+
const nativePrimaryShadowStyle = Platform.OS === 'web' || !firstNativeEffect ? null : buildButtonNativeShadowStyle(firstNativeEffect);
|
|
900
|
+
const centeredStrokeStyle = normalizedStroke && normalizedStroke.position === 'center' ? {
|
|
901
|
+
borderWidth: normalizedStroke.width,
|
|
902
|
+
borderColor: normalizedStroke.color
|
|
903
|
+
} : null;
|
|
904
|
+
const fillLayerStyle = {
|
|
905
|
+
position: 'absolute',
|
|
906
|
+
top: 0,
|
|
907
|
+
right: 0,
|
|
908
|
+
bottom: 0,
|
|
909
|
+
left: 0,
|
|
910
|
+
borderRadius,
|
|
911
|
+
overflow: 'hidden',
|
|
912
|
+
...(centeredStrokeStyle ?? {})
|
|
913
|
+
};
|
|
914
|
+
const nativeShadowEffectsInPaintOrder = normalizedEffects.slice().reverse();
|
|
915
|
+
const pressableStyle = {
|
|
916
|
+
width,
|
|
917
|
+
height,
|
|
918
|
+
borderRadius,
|
|
919
|
+
justifyContent: 'center',
|
|
920
|
+
alignItems: 'center',
|
|
921
|
+
overflow: 'visible',
|
|
922
|
+
...(nativePrimaryShadowStyle ?? {})
|
|
923
|
+
};
|
|
924
|
+
node = /*#__PURE__*/_jsxs(Pressable, {
|
|
925
|
+
collapsable: false,
|
|
926
|
+
onPress: action ? () => params.onAction(action, actionPayload ?? props) : undefined,
|
|
927
|
+
style: pressableStyle,
|
|
928
|
+
children: [Platform.OS === 'web' ? webShadowStyle ? /*#__PURE__*/_jsx(View, {
|
|
929
|
+
pointerEvents: "none",
|
|
478
930
|
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,
|
|
931
|
+
position: 'absolute',
|
|
932
|
+
...shadowBounds,
|
|
933
|
+
...webShadowStyle
|
|
934
|
+
}
|
|
935
|
+
}) : null : nativeShadowEffectsInPaintOrder.map((effect, index) => /*#__PURE__*/_jsx(View, {
|
|
936
|
+
collapsable: false,
|
|
937
|
+
pointerEvents: "none",
|
|
498
938
|
style: {
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
939
|
+
position: 'absolute',
|
|
940
|
+
...shadowBounds,
|
|
941
|
+
...buildButtonNativeShadowStyle(effect)
|
|
942
|
+
}
|
|
943
|
+
}, `shadow-${index}`)), gradient ? /*#__PURE__*/_jsx(LinearGradient, {
|
|
944
|
+
colors: gradient.colors,
|
|
945
|
+
start: gradient.start,
|
|
946
|
+
end: gradient.end,
|
|
947
|
+
locations: gradient.stops,
|
|
948
|
+
style: fillLayerStyle
|
|
949
|
+
}) : /*#__PURE__*/_jsx(View, {
|
|
950
|
+
style: {
|
|
951
|
+
...fillLayerStyle,
|
|
952
|
+
backgroundColor
|
|
953
|
+
}
|
|
954
|
+
}), renderButtonStrokeOverlay(normalizedStroke, borderRadius), /*#__PURE__*/_jsx(View, {
|
|
955
|
+
style: {
|
|
956
|
+
flex: 1,
|
|
503
957
|
justifyContent: 'center',
|
|
504
958
|
alignItems: 'center'
|
|
505
959
|
},
|
|
506
|
-
children:
|
|
507
|
-
|
|
508
|
-
|
|
960
|
+
children: /*#__PURE__*/_jsx(Text, {
|
|
961
|
+
style: textStyle,
|
|
962
|
+
children: label
|
|
963
|
+
})
|
|
964
|
+
})]
|
|
965
|
+
});
|
|
509
966
|
break;
|
|
510
967
|
}
|
|
511
968
|
case 'text_input':
|
|
@@ -819,22 +1276,112 @@ function buildWidget(json, params) {
|
|
|
819
1276
|
}
|
|
820
1277
|
case 'stack':
|
|
821
1278
|
{
|
|
822
|
-
const
|
|
823
|
-
const
|
|
824
|
-
|
|
1279
|
+
const axis = props.axis === 'horizontal' || props.axis === 'overlay' ? props.axis : 'vertical';
|
|
1280
|
+
const isOverlay = axis === 'overlay';
|
|
1281
|
+
const spacing = Number(props.childSpacing ?? 0);
|
|
1282
|
+
const applySpacing = !STACK_SPACE_DISTRIBUTIONS.has(String(props.distribution ?? 'start'));
|
|
1283
|
+
const paddingInsets = stackSpacingToInsets(props.layout?.padding ?? props.padding);
|
|
1284
|
+
const width = resolveStackDimension(props, axis, 'width', pageAxisBounds);
|
|
1285
|
+
const height = resolveStackDimension(props, axis, 'height', pageAxisBounds);
|
|
1286
|
+
const mainAxisBounded = axis === 'horizontal' ? pageAxisBounds.widthBounded : pageAxisBounds.heightBounded;
|
|
1287
|
+
const borderRadius = Number(props.appearance?.cornerRadius ?? props.borderRadius ?? 0);
|
|
1288
|
+
const borderWidth = Number(props.border?.width ?? 0);
|
|
1289
|
+
const borderColor = borderWidth > 0 ? parseColor(props.border?.color ?? '#E5E7EB') : null;
|
|
1290
|
+
const shadowColor = props.shadow?.color ? parseColor(props.shadow.color) : null;
|
|
1291
|
+
const shadowStyle = shadowColor && props.shadow ? Platform.OS === 'web' ? {
|
|
1292
|
+
boxShadow: `${Number(props.shadow.x ?? 0)}px ${Number(props.shadow.y ?? 8)}px ${Number(props.shadow.blur ?? 24)}px ${shadowColor}`
|
|
1293
|
+
} : {
|
|
1294
|
+
shadowColor,
|
|
1295
|
+
shadowOffset: {
|
|
1296
|
+
width: Number(props.shadow.x ?? 0),
|
|
1297
|
+
height: Number(props.shadow.y ?? 8)
|
|
1298
|
+
},
|
|
1299
|
+
shadowOpacity: 0.35,
|
|
1300
|
+
shadowRadius: Math.max(0, Number(props.shadow.blur ?? 24) / 2),
|
|
1301
|
+
elevation: Math.max(1, Math.round((Number(props.shadow.blur ?? 24) + Math.abs(Number(props.shadow.y ?? 8))) / 3))
|
|
1302
|
+
} : {};
|
|
1303
|
+
const fill = props.fill;
|
|
1304
|
+
const stackGradient = fill?.type === 'gradient' ? parseGradient({
|
|
1305
|
+
...fill,
|
|
1306
|
+
type: 'gradient'
|
|
1307
|
+
}) : undefined;
|
|
1308
|
+
const stackColor = fill?.type === 'solid' || !fill?.type && fill?.color ? parseColor(fill?.color ?? '#FFFFFFFF') : parseColor(props.backgroundColor ?? '#00000000');
|
|
1309
|
+
const baseStackStyle = {
|
|
1310
|
+
width,
|
|
1311
|
+
height,
|
|
1312
|
+
borderRadius: borderRadius > 0 ? borderRadius : undefined,
|
|
1313
|
+
overflow: borderRadius > 0 ? 'hidden' : undefined,
|
|
1314
|
+
borderWidth: borderColor ? borderWidth : undefined,
|
|
1315
|
+
borderColor: borderColor ?? undefined,
|
|
1316
|
+
...insetsToStyle(paddingInsets),
|
|
1317
|
+
...shadowStyle
|
|
1318
|
+
};
|
|
1319
|
+
const content = isOverlay ? /*#__PURE__*/_jsxs(View, {
|
|
825
1320
|
style: {
|
|
826
1321
|
position: 'relative',
|
|
827
|
-
|
|
828
|
-
|
|
829
|
-
justifyContent: alignment.justifyContent,
|
|
830
|
-
alignItems: alignment.alignItems
|
|
1322
|
+
minHeight: 0,
|
|
1323
|
+
minWidth: 0
|
|
831
1324
|
},
|
|
832
|
-
children: (childrenJson ?? []).map((child, index) =>
|
|
833
|
-
|
|
834
|
-
|
|
835
|
-
|
|
1325
|
+
children: [(childrenJson ?? []).map((child, index) => {
|
|
1326
|
+
const alignment = parseOverlayGridAlignment(props.overlayAlignment ?? props.alignment);
|
|
1327
|
+
return /*#__PURE__*/_jsx(View, {
|
|
1328
|
+
style: {
|
|
1329
|
+
position: 'absolute',
|
|
1330
|
+
top: 0,
|
|
1331
|
+
right: 0,
|
|
1332
|
+
bottom: 0,
|
|
1333
|
+
left: 0,
|
|
1334
|
+
justifyContent: alignment.justifyContent,
|
|
1335
|
+
alignItems: alignment.alignItems
|
|
1336
|
+
},
|
|
1337
|
+
children: buildWidget(child, {
|
|
1338
|
+
...params,
|
|
1339
|
+
allowFlexExpansion: true,
|
|
1340
|
+
pageAxisBounds
|
|
1341
|
+
})
|
|
1342
|
+
}, `stack-overlay-${index}`);
|
|
1343
|
+
}), (childrenJson ?? []).length === 0 ? null : null]
|
|
1344
|
+
}) : /*#__PURE__*/_jsx(View, {
|
|
1345
|
+
style: {
|
|
1346
|
+
flexDirection: axis === 'horizontal' ? 'row' : 'column',
|
|
1347
|
+
justifyContent: parseFlexAlignment(props.distribution),
|
|
1348
|
+
alignItems: parseCrossAlignment(props.alignment),
|
|
1349
|
+
minHeight: 0,
|
|
1350
|
+
minWidth: 0
|
|
1351
|
+
},
|
|
1352
|
+
children: (childrenJson ?? []).map((child, index) => /*#__PURE__*/_jsxs(React.Fragment, {
|
|
1353
|
+
children: [buildWidget(child, {
|
|
1354
|
+
...params,
|
|
1355
|
+
allowFlexExpansion: true,
|
|
1356
|
+
parentFlexAxis: axis === 'horizontal' ? 'horizontal' : 'vertical',
|
|
1357
|
+
parentMainAxisBounded: mainAxisBounded,
|
|
1358
|
+
pageAxisBounds
|
|
1359
|
+
}), applySpacing && spacing > 0 && index < (childrenJson?.length ?? 0) - 1 ? /*#__PURE__*/_jsx(View, {
|
|
1360
|
+
style: {
|
|
1361
|
+
width: axis === 'horizontal' ? spacing : undefined,
|
|
1362
|
+
height: axis === 'vertical' ? spacing : undefined
|
|
1363
|
+
}
|
|
1364
|
+
}) : null]
|
|
836
1365
|
}, `stack-${index}`))
|
|
837
1366
|
});
|
|
1367
|
+
if (stackGradient) {
|
|
1368
|
+
node = /*#__PURE__*/_jsx(LinearGradient, {
|
|
1369
|
+
colors: stackGradient.colors,
|
|
1370
|
+
start: stackGradient.start,
|
|
1371
|
+
end: stackGradient.end,
|
|
1372
|
+
locations: stackGradient.stops,
|
|
1373
|
+
style: baseStackStyle,
|
|
1374
|
+
children: content
|
|
1375
|
+
});
|
|
1376
|
+
} else {
|
|
1377
|
+
node = /*#__PURE__*/_jsx(View, {
|
|
1378
|
+
style: {
|
|
1379
|
+
...baseStackStyle,
|
|
1380
|
+
backgroundColor: stackColor
|
|
1381
|
+
},
|
|
1382
|
+
children: content
|
|
1383
|
+
});
|
|
1384
|
+
}
|
|
838
1385
|
break;
|
|
839
1386
|
}
|
|
840
1387
|
case 'positioned':
|
|
@@ -865,35 +1412,55 @@ function buildWidget(json, params) {
|
|
|
865
1412
|
node = null;
|
|
866
1413
|
}
|
|
867
1414
|
if (!node) return null;
|
|
868
|
-
const marginVal = props.margin;
|
|
1415
|
+
const marginVal = type === 'stack' ? props.layout?.margin ?? props.margin : props.margin;
|
|
869
1416
|
if (marginVal !== undefined && marginVal !== null) {
|
|
870
|
-
const marginInsets = parseInsets(marginVal);
|
|
1417
|
+
const marginInsets = type === 'stack' ? stackSpacingToInsets(marginVal) : parseInsets(marginVal);
|
|
871
1418
|
node = /*#__PURE__*/_jsx(View, {
|
|
872
1419
|
style: insetsToMarginStyle(marginInsets),
|
|
873
1420
|
children: node
|
|
874
1421
|
});
|
|
875
|
-
} else if (type !== 'container' && type !== 'padding' && props.padding) {
|
|
1422
|
+
} else if (type !== 'container' && type !== 'padding' && type !== 'stack' && props.padding) {
|
|
876
1423
|
const paddingInsets = parseInsets(props.padding);
|
|
877
1424
|
node = /*#__PURE__*/_jsx(View, {
|
|
878
1425
|
style: insetsToStyle(paddingInsets),
|
|
879
1426
|
children: node
|
|
880
1427
|
});
|
|
881
1428
|
}
|
|
882
|
-
if (params.allowFlexExpansion
|
|
1429
|
+
if (params.allowFlexExpansion) {
|
|
883
1430
|
let shouldExpand = false;
|
|
884
|
-
|
|
885
|
-
|
|
886
|
-
|
|
887
|
-
|
|
888
|
-
|
|
889
|
-
|
|
890
|
-
|
|
1431
|
+
const parentAxis = params.parentFlexAxis ?? 'vertical';
|
|
1432
|
+
const parentMainAxisBounded = params.parentMainAxisBounded ?? true;
|
|
1433
|
+
if (parentMainAxisBounded) {
|
|
1434
|
+
if (type === 'stack') {
|
|
1435
|
+
const legacyExpand = props.fit === 'expand';
|
|
1436
|
+
const widthMode = String(props.size?.width ?? '').toLowerCase();
|
|
1437
|
+
const heightMode = String(props.size?.height ?? '').toLowerCase();
|
|
1438
|
+
if (parentAxis === 'vertical' && (legacyExpand || heightMode === 'fill')) {
|
|
1439
|
+
shouldExpand = true;
|
|
1440
|
+
}
|
|
1441
|
+
if (parentAxis === 'horizontal' && (legacyExpand || widthMode === 'fill')) {
|
|
1442
|
+
shouldExpand = true;
|
|
1443
|
+
}
|
|
1444
|
+
}
|
|
1445
|
+
if (parentAxis === 'vertical' && props.height !== undefined) {
|
|
1446
|
+
const heightVal = parseLayoutDimension(props.height);
|
|
1447
|
+
if (heightVal === Number.POSITIVE_INFINITY) {
|
|
1448
|
+
shouldExpand = true;
|
|
1449
|
+
}
|
|
1450
|
+
}
|
|
1451
|
+
if (parentAxis === 'horizontal' && props.width !== undefined) {
|
|
1452
|
+
const widthVal = parseLayoutDimension(props.width);
|
|
1453
|
+
if (widthVal === Number.POSITIVE_INFINITY) {
|
|
1454
|
+
shouldExpand = true;
|
|
1455
|
+
}
|
|
891
1456
|
}
|
|
892
1457
|
}
|
|
893
1458
|
if (shouldExpand) {
|
|
894
1459
|
node = /*#__PURE__*/_jsx(View, {
|
|
895
1460
|
style: {
|
|
896
|
-
flex: 1
|
|
1461
|
+
flex: 1,
|
|
1462
|
+
minHeight: 0,
|
|
1463
|
+
minWidth: 0
|
|
897
1464
|
},
|
|
898
1465
|
children: node
|
|
899
1466
|
});
|
|
@@ -958,6 +1525,132 @@ function parseGradient(value) {
|
|
|
958
1525
|
stops
|
|
959
1526
|
};
|
|
960
1527
|
}
|
|
1528
|
+
function normalizeButtonStroke(input) {
|
|
1529
|
+
if (!input || typeof input !== 'object') return null;
|
|
1530
|
+
const legacyStrokeWithoutEnabled = input.enabled === undefined;
|
|
1531
|
+
const enabled = legacyStrokeWithoutEnabled ? true : input.enabled === true;
|
|
1532
|
+
if (!enabled) return null;
|
|
1533
|
+
const width = Number(input.width ?? 1);
|
|
1534
|
+
if (!Number.isFinite(width) || width <= 0) return null;
|
|
1535
|
+
const position = input.position === 'inside' || input.position === 'center' || input.position === 'outside' ? input.position : 'center';
|
|
1536
|
+
const opacity = clamp01(Number(input.opacity ?? 1));
|
|
1537
|
+
const color = normalizeColorWithOpacity(input.color, opacity);
|
|
1538
|
+
if (color === 'transparent') return null;
|
|
1539
|
+
return {
|
|
1540
|
+
color,
|
|
1541
|
+
width,
|
|
1542
|
+
position
|
|
1543
|
+
};
|
|
1544
|
+
}
|
|
1545
|
+
function normalizeButtonEffects(effectsInput, legacyShadowInput) {
|
|
1546
|
+
if (Array.isArray(effectsInput)) {
|
|
1547
|
+
return effectsInput.map(normalizeDropShadowEffect).filter(effect => effect !== null);
|
|
1548
|
+
}
|
|
1549
|
+
if (!legacyShadowInput || typeof legacyShadowInput !== 'object') {
|
|
1550
|
+
return [];
|
|
1551
|
+
}
|
|
1552
|
+
if (legacyShadowInput.enabled !== true) return [];
|
|
1553
|
+
const migrated = normalizeDropShadowEffect({
|
|
1554
|
+
type: 'dropShadow',
|
|
1555
|
+
enabled: true,
|
|
1556
|
+
x: legacyShadowInput.x,
|
|
1557
|
+
y: legacyShadowInput.y,
|
|
1558
|
+
blur: legacyShadowInput.blur,
|
|
1559
|
+
spread: 0,
|
|
1560
|
+
color: legacyShadowInput.color,
|
|
1561
|
+
opacity: 1
|
|
1562
|
+
});
|
|
1563
|
+
return migrated ? [migrated] : [];
|
|
1564
|
+
}
|
|
1565
|
+
function normalizeDropShadowEffect(effect) {
|
|
1566
|
+
if (!effect || typeof effect !== 'object') return null;
|
|
1567
|
+
if ((effect.type ?? 'dropShadow') !== 'dropShadow') return null;
|
|
1568
|
+
if (effect.enabled === false) return null;
|
|
1569
|
+
const opacity = clamp01(Number(effect.opacity ?? 1));
|
|
1570
|
+
const color = normalizeColorWithOpacity(effect.color, opacity);
|
|
1571
|
+
if (color === 'transparent') return null;
|
|
1572
|
+
return {
|
|
1573
|
+
x: Number(effect.x ?? 0),
|
|
1574
|
+
y: Number(effect.y ?? 0),
|
|
1575
|
+
blur: Math.max(0, Number(effect.blur ?? 0)),
|
|
1576
|
+
spread: Number(effect.spread ?? 0),
|
|
1577
|
+
color,
|
|
1578
|
+
opacity
|
|
1579
|
+
};
|
|
1580
|
+
}
|
|
1581
|
+
function normalizeColorWithOpacity(input, opacity) {
|
|
1582
|
+
const raw = typeof input === 'string' ? input.trim() : '';
|
|
1583
|
+
const normalizedOpacity = clamp01(opacity);
|
|
1584
|
+
if (raw.startsWith('rgba(') || raw.startsWith('rgb(')) {
|
|
1585
|
+
return withOpacity(raw, normalizedOpacity);
|
|
1586
|
+
}
|
|
1587
|
+
return withOpacity(parseColor(input ?? '0xFF000000'), normalizedOpacity);
|
|
1588
|
+
}
|
|
1589
|
+
function buildButtonWebShadowStyle(effects) {
|
|
1590
|
+
if (effects.length === 0) return null;
|
|
1591
|
+
const layers = effects.map(effect => `${effect.x}px ${effect.y}px ${effect.blur}px ${effect.spread}px ${effect.color}`);
|
|
1592
|
+
return {
|
|
1593
|
+
boxShadow: layers.join(', ')
|
|
1594
|
+
};
|
|
1595
|
+
}
|
|
1596
|
+
function buildButtonNativeShadowStyle(effect) {
|
|
1597
|
+
const rgba = parseRgbaColor(effect.color);
|
|
1598
|
+
return {
|
|
1599
|
+
// Android elevation requires a drawable host shape; keep it effectively invisible.
|
|
1600
|
+
backgroundColor: 'rgba(255,255,255,0.02)',
|
|
1601
|
+
shadowColor: rgba ? `rgba(${rgba.r},${rgba.g},${rgba.b},1)` : effect.color,
|
|
1602
|
+
shadowOffset: {
|
|
1603
|
+
width: effect.x,
|
|
1604
|
+
height: effect.y
|
|
1605
|
+
},
|
|
1606
|
+
shadowOpacity: rgba ? rgba.a : effect.opacity,
|
|
1607
|
+
shadowRadius: effect.blur / 2,
|
|
1608
|
+
elevation: Math.max(1, Math.round((effect.blur + Math.abs(effect.y)) / 3))
|
|
1609
|
+
};
|
|
1610
|
+
}
|
|
1611
|
+
function getButtonShadowLayerBounds(stroke, borderRadius) {
|
|
1612
|
+
const expandBy = !stroke || stroke.position === 'inside' ? 0 : stroke.position === 'center' ? stroke.width / 2 : stroke.width;
|
|
1613
|
+
return {
|
|
1614
|
+
top: -expandBy,
|
|
1615
|
+
right: -expandBy,
|
|
1616
|
+
bottom: -expandBy,
|
|
1617
|
+
left: -expandBy,
|
|
1618
|
+
borderRadius: borderRadius + expandBy
|
|
1619
|
+
};
|
|
1620
|
+
}
|
|
1621
|
+
function renderButtonStrokeOverlay(stroke, borderRadius) {
|
|
1622
|
+
if (!stroke || stroke.position === 'center') {
|
|
1623
|
+
return null;
|
|
1624
|
+
}
|
|
1625
|
+
if (stroke.position === 'inside') {
|
|
1626
|
+
return /*#__PURE__*/_jsx(View, {
|
|
1627
|
+
pointerEvents: "none",
|
|
1628
|
+
style: {
|
|
1629
|
+
position: 'absolute',
|
|
1630
|
+
top: stroke.width / 2,
|
|
1631
|
+
left: stroke.width / 2,
|
|
1632
|
+
right: stroke.width / 2,
|
|
1633
|
+
bottom: stroke.width / 2,
|
|
1634
|
+
borderRadius: Math.max(0, borderRadius - stroke.width / 2),
|
|
1635
|
+
borderWidth: stroke.width,
|
|
1636
|
+
borderColor: stroke.color
|
|
1637
|
+
}
|
|
1638
|
+
});
|
|
1639
|
+
}
|
|
1640
|
+
return /*#__PURE__*/_jsx(View, {
|
|
1641
|
+
pointerEvents: "none",
|
|
1642
|
+
style: {
|
|
1643
|
+
position: 'absolute',
|
|
1644
|
+
top: -stroke.width,
|
|
1645
|
+
left: -stroke.width,
|
|
1646
|
+
right: -stroke.width,
|
|
1647
|
+
bottom: -stroke.width,
|
|
1648
|
+
borderRadius: borderRadius + stroke.width,
|
|
1649
|
+
borderWidth: stroke.width,
|
|
1650
|
+
borderColor: stroke.color
|
|
1651
|
+
}
|
|
1652
|
+
});
|
|
1653
|
+
}
|
|
961
1654
|
function alignmentToGradient(value) {
|
|
962
1655
|
switch (value) {
|
|
963
1656
|
case 'topCenter':
|
|
@@ -1009,10 +1702,23 @@ function alignmentToGradient(value) {
|
|
|
1009
1702
|
}
|
|
1010
1703
|
}
|
|
1011
1704
|
function withOpacity(color, opacity) {
|
|
1012
|
-
|
|
1013
|
-
|
|
1014
|
-
}
|
|
1015
|
-
|
|
1705
|
+
const rgba = parseRgbaColor(color);
|
|
1706
|
+
if (!rgba) return color;
|
|
1707
|
+
return `rgba(${rgba.r},${rgba.g},${rgba.b},${clamp01(rgba.a * opacity)})`;
|
|
1708
|
+
}
|
|
1709
|
+
function parseRgbaColor(color) {
|
|
1710
|
+
const match = color.match(/^rgba?\(\s*([0-9.]+)\s*,\s*([0-9.]+)\s*,\s*([0-9.]+)(?:\s*,\s*([0-9.]+))?\s*\)$/i);
|
|
1711
|
+
if (!match) return null;
|
|
1712
|
+
return {
|
|
1713
|
+
r: Math.round(Number(match[1])),
|
|
1714
|
+
g: Math.round(Number(match[2])),
|
|
1715
|
+
b: Math.round(Number(match[3])),
|
|
1716
|
+
a: match[4] === undefined ? 1 : clamp01(Number(match[4]))
|
|
1717
|
+
};
|
|
1718
|
+
}
|
|
1719
|
+
function clamp01(value) {
|
|
1720
|
+
if (!Number.isFinite(value)) return 1;
|
|
1721
|
+
return Math.max(0, Math.min(1, value));
|
|
1016
1722
|
}
|
|
1017
1723
|
function normalizeDimension(value) {
|
|
1018
1724
|
if (value === Number.POSITIVE_INFINITY) {
|
|
@@ -1140,6 +1846,44 @@ function GradientText({
|
|
|
1140
1846
|
})
|
|
1141
1847
|
});
|
|
1142
1848
|
}
|
|
1849
|
+
function resolveTextInputBackgroundColor(properties) {
|
|
1850
|
+
const canonical = properties.backgroundColor;
|
|
1851
|
+
if (typeof canonical === 'string' && canonical.trim().length > 0) {
|
|
1852
|
+
return parseColor(canonical);
|
|
1853
|
+
}
|
|
1854
|
+
const legacyBackground = properties.background;
|
|
1855
|
+
if (legacyBackground && typeof legacyBackground === 'object' && !Array.isArray(legacyBackground)) {
|
|
1856
|
+
const type = String(legacyBackground.type ?? '').toLowerCase();
|
|
1857
|
+
if (type === 'color' && typeof legacyBackground.color === 'string') {
|
|
1858
|
+
return parseColor(legacyBackground.color);
|
|
1859
|
+
}
|
|
1860
|
+
if (type === 'gradient' && Array.isArray(legacyBackground.colors)) {
|
|
1861
|
+
const firstGradientColor = legacyBackground.colors.find(value => typeof value === 'string' && value.trim().length > 0);
|
|
1862
|
+
if (typeof firstGradientColor === 'string') {
|
|
1863
|
+
return parseColor(firstGradientColor);
|
|
1864
|
+
}
|
|
1865
|
+
}
|
|
1866
|
+
} else if (typeof legacyBackground === 'string' && legacyBackground.trim().length > 0) {
|
|
1867
|
+
return parseColor(legacyBackground);
|
|
1868
|
+
}
|
|
1869
|
+
return parseColor('0xFFF0F0F0');
|
|
1870
|
+
}
|
|
1871
|
+
function normalizeTextInputStroke(properties) {
|
|
1872
|
+
const parsedWidth = Number(properties.strokeWidth ?? 0);
|
|
1873
|
+
const width = Number.isFinite(parsedWidth) && parsedWidth > 0 ? parsedWidth : 0;
|
|
1874
|
+
const enabled = typeof properties.strokeEnabled === 'boolean' ? properties.strokeEnabled : width > 0;
|
|
1875
|
+
const normalizedWidth = enabled ? width : 0;
|
|
1876
|
+
const color = normalizedWidth > 0 ? parseColor(properties.strokeColor ?? '#000000') : 'transparent';
|
|
1877
|
+
const parsedRadius = Number(properties.strokeRadius ?? properties.borderRadius ?? 8);
|
|
1878
|
+
const radius = Number.isFinite(parsedRadius) ? Math.max(0, parsedRadius) : 8;
|
|
1879
|
+
const style = properties.strokeStyle === 'dashed' || properties.strokeStyle === 'dotted' ? properties.strokeStyle : 'solid';
|
|
1880
|
+
return {
|
|
1881
|
+
width: normalizedWidth,
|
|
1882
|
+
color,
|
|
1883
|
+
radius,
|
|
1884
|
+
style
|
|
1885
|
+
};
|
|
1886
|
+
}
|
|
1143
1887
|
function DynamicInput({
|
|
1144
1888
|
initialValue,
|
|
1145
1889
|
properties,
|
|
@@ -1163,8 +1907,10 @@ function DynamicInput({
|
|
|
1163
1907
|
...properties,
|
|
1164
1908
|
height: null
|
|
1165
1909
|
}, 'textColor');
|
|
1166
|
-
const backgroundColor =
|
|
1167
|
-
const
|
|
1910
|
+
const backgroundColor = resolveTextInputBackgroundColor(properties);
|
|
1911
|
+
const stroke = normalizeTextInputStroke(properties);
|
|
1912
|
+
const parsedPadding = Number(properties.padding ?? 12);
|
|
1913
|
+
const padding = Number.isFinite(parsedPadding) ? Math.max(0, parsedPadding) : 12;
|
|
1168
1914
|
return /*#__PURE__*/_jsxs(View, {
|
|
1169
1915
|
children: [label ? /*#__PURE__*/_jsx(Text, {
|
|
1170
1916
|
style: labelStyle,
|
|
@@ -1172,9 +1918,11 @@ function DynamicInput({
|
|
|
1172
1918
|
}) : null, /*#__PURE__*/_jsx(View, {
|
|
1173
1919
|
style: {
|
|
1174
1920
|
backgroundColor,
|
|
1175
|
-
borderRadius,
|
|
1176
|
-
|
|
1177
|
-
|
|
1921
|
+
borderRadius: stroke.radius,
|
|
1922
|
+
borderStyle: stroke.width > 0 ? stroke.style : undefined,
|
|
1923
|
+
borderWidth: stroke.width,
|
|
1924
|
+
borderColor: stroke.color,
|
|
1925
|
+
padding
|
|
1178
1926
|
},
|
|
1179
1927
|
children: mask ? /*#__PURE__*/_jsx(MaskInput, {
|
|
1180
1928
|
value: value,
|
|
@@ -1313,6 +2061,7 @@ function SelectionList({
|
|
|
1313
2061
|
if (layout === 'row') {
|
|
1314
2062
|
return /*#__PURE__*/_jsx(View, {
|
|
1315
2063
|
style: {
|
|
2064
|
+
width: '100%',
|
|
1316
2065
|
flexDirection: 'row',
|
|
1317
2066
|
flexWrap: 'wrap'
|
|
1318
2067
|
},
|
|
@@ -1330,6 +2079,7 @@ function SelectionList({
|
|
|
1330
2079
|
const aspectRatio = Number(properties.gridAspectRatio ?? 1);
|
|
1331
2080
|
return /*#__PURE__*/_jsx(View, {
|
|
1332
2081
|
style: {
|
|
2082
|
+
width: '100%',
|
|
1333
2083
|
flexDirection: 'row',
|
|
1334
2084
|
flexWrap: 'wrap'
|
|
1335
2085
|
},
|
|
@@ -1345,8 +2095,12 @@ function SelectionList({
|
|
|
1345
2095
|
});
|
|
1346
2096
|
}
|
|
1347
2097
|
return /*#__PURE__*/_jsx(View, {
|
|
2098
|
+
style: {
|
|
2099
|
+
width: '100%'
|
|
2100
|
+
},
|
|
1348
2101
|
children: options.map((option, index) => /*#__PURE__*/_jsx(View, {
|
|
1349
2102
|
style: {
|
|
2103
|
+
width: '100%',
|
|
1350
2104
|
marginBottom: spacing
|
|
1351
2105
|
},
|
|
1352
2106
|
children: renderItem(option, index)
|
|
@@ -1566,6 +2320,8 @@ function WheelPicker({
|
|
|
1566
2320
|
const baseUnselectedStyle = properties.unselectedStyle ?? properties.selectedStyle ?? {};
|
|
1567
2321
|
const overlayBorderColor = parseColor(properties.wheelCenterBorderColor ?? properties.wheelSelectedBorderColor ?? baseUnselectedStyle.borderColor ?? '#D6D6DC');
|
|
1568
2322
|
const overlayBackgroundColor = parseColor(properties.wheelCenterBackgroundColor ?? '#24FFFFFF');
|
|
2323
|
+
const wheelEdgeFadeOpacity = Math.max(0, Math.min(1, Number(properties.wheelEdgeFadeOpacity ?? 0)));
|
|
2324
|
+
const wheelEdgeFadeColor = parseColor(properties.wheelEdgeFadeColor ?? '#FFFFFF');
|
|
1569
2325
|
const handleWheelMomentumEnd = event => {
|
|
1570
2326
|
const offsetY = Number(event?.nativeEvent?.contentOffset?.y ?? 0);
|
|
1571
2327
|
const settledIndex = clampWheelIndex(Math.round(offsetY / wheelRowStride));
|
|
@@ -1664,9 +2420,9 @@ function WheelPicker({
|
|
|
1664
2420
|
borderColor: overlayBorderColor,
|
|
1665
2421
|
backgroundColor: overlayBackgroundColor
|
|
1666
2422
|
}
|
|
1667
|
-
}), /*#__PURE__*/_jsx(LinearGradient, {
|
|
2423
|
+
}), wheelEdgeFadeOpacity > 0 && centerPadding > 0 ? /*#__PURE__*/_jsx(LinearGradient, {
|
|
1668
2424
|
pointerEvents: "none",
|
|
1669
|
-
colors: [
|
|
2425
|
+
colors: [withOpacity(wheelEdgeFadeColor, wheelEdgeFadeOpacity), withOpacity(wheelEdgeFadeColor, 0)],
|
|
1670
2426
|
style: {
|
|
1671
2427
|
position: 'absolute',
|
|
1672
2428
|
top: 0,
|
|
@@ -1674,9 +2430,9 @@ function WheelPicker({
|
|
|
1674
2430
|
right: 0,
|
|
1675
2431
|
height: centerPadding
|
|
1676
2432
|
}
|
|
1677
|
-
}), /*#__PURE__*/_jsx(LinearGradient, {
|
|
2433
|
+
}) : null, wheelEdgeFadeOpacity > 0 && centerPadding > 0 ? /*#__PURE__*/_jsx(LinearGradient, {
|
|
1678
2434
|
pointerEvents: "none",
|
|
1679
|
-
colors: [
|
|
2435
|
+
colors: [withOpacity(wheelEdgeFadeColor, 0), withOpacity(wheelEdgeFadeColor, wheelEdgeFadeOpacity)],
|
|
1680
2436
|
style: {
|
|
1681
2437
|
position: 'absolute',
|
|
1682
2438
|
bottom: 0,
|
|
@@ -1684,7 +2440,7 @@ function WheelPicker({
|
|
|
1684
2440
|
right: 0,
|
|
1685
2441
|
height: centerPadding
|
|
1686
2442
|
}
|
|
1687
|
-
})]
|
|
2443
|
+
}) : null]
|
|
1688
2444
|
});
|
|
1689
2445
|
}
|
|
1690
2446
|
return /*#__PURE__*/_jsx(View, {
|
|
@@ -2113,16 +2869,33 @@ function RadarChart({
|
|
|
2113
2869
|
const axisColor = parseColor(properties.axisColor ?? '0xFF9E9E9E');
|
|
2114
2870
|
const axisStrokeWidth = Number(properties.axisStrokeWidth ?? 1);
|
|
2115
2871
|
const axisLabelStyle = getTextStyle(properties.axisLabelStyle ?? {}, 'color', '0xFF424242');
|
|
2872
|
+
const resolvedAxisLabelFontSize = resolveRadarAxisLabelFontSize(axisLabelStyle.fontSize);
|
|
2873
|
+
const autoFitLabels = properties.autoFitLabels !== false;
|
|
2874
|
+
const resolvedLabelOffset = resolveRadarLabelOffset(properties.labelOffset, resolvedAxisLabelFontSize);
|
|
2116
2875
|
const showLegend = properties.showLegend !== false;
|
|
2117
2876
|
const legendPosition = (properties.legendPosition ?? 'bottom').toLowerCase();
|
|
2118
2877
|
const legendSpacing = Number(properties.legendSpacing ?? 12);
|
|
2119
2878
|
const legendStyle = getTextStyle(properties.legendStyle ?? {}, 'color', '0xFF212121');
|
|
2120
2879
|
const shape = (properties.shape ?? 'polygon').toLowerCase();
|
|
2121
|
-
const
|
|
2122
|
-
const
|
|
2880
|
+
const innerWidth = Math.max(1, width - padding.left - padding.right);
|
|
2881
|
+
const innerHeight = Math.max(1, height - padding.top - padding.bottom);
|
|
2882
|
+
const radius = calculateRadarChartRadius({
|
|
2883
|
+
width: innerWidth,
|
|
2884
|
+
height: innerHeight,
|
|
2885
|
+
padding: {
|
|
2886
|
+
left: 0,
|
|
2887
|
+
right: 0,
|
|
2888
|
+
top: 0,
|
|
2889
|
+
bottom: 0
|
|
2890
|
+
},
|
|
2891
|
+
axisLabels: axes.map(axis => axis.label),
|
|
2892
|
+
fontSize: resolvedAxisLabelFontSize,
|
|
2893
|
+
autoFitLabels,
|
|
2894
|
+
labelOffset: resolvedLabelOffset
|
|
2895
|
+
});
|
|
2123
2896
|
const center = {
|
|
2124
|
-
x:
|
|
2125
|
-
y:
|
|
2897
|
+
x: padding.left + innerWidth / 2,
|
|
2898
|
+
y: padding.top + innerHeight / 2
|
|
2126
2899
|
};
|
|
2127
2900
|
const axisAngle = Math.PI * 2 / axes.length;
|
|
2128
2901
|
const gridPolygons = Array.from({
|
|
@@ -2143,7 +2916,8 @@ function RadarChart({
|
|
|
2143
2916
|
const points = dataset.values.map((value, index) => {
|
|
2144
2917
|
const axis = normalizedAxes[index];
|
|
2145
2918
|
const maxValue = axis?.maxValue ?? 0;
|
|
2146
|
-
const
|
|
2919
|
+
const normalizedRatio = maxValue > 0 ? value / maxValue : 0;
|
|
2920
|
+
const ratio = Math.max(0, Math.min(1, normalizedRatio)) * Math.max(0, Math.min(1, lineProgress));
|
|
2147
2921
|
const angle = index * axisAngle - Math.PI / 2;
|
|
2148
2922
|
const x = center.x + radius * ratio * Math.cos(angle);
|
|
2149
2923
|
const y = center.y + radius * ratio * Math.sin(angle);
|
|
@@ -2154,27 +2928,32 @@ function RadarChart({
|
|
|
2154
2928
|
points
|
|
2155
2929
|
};
|
|
2156
2930
|
});
|
|
2931
|
+
const isVerticalLegend = legendPosition === 'left' || legendPosition === 'right';
|
|
2157
2932
|
const legend = /*#__PURE__*/_jsx(View, {
|
|
2158
2933
|
style: {
|
|
2159
|
-
flexDirection: 'row',
|
|
2160
|
-
flexWrap: 'wrap'
|
|
2934
|
+
flexDirection: isVerticalLegend ? 'column' : 'row',
|
|
2935
|
+
flexWrap: isVerticalLegend ? 'nowrap' : 'wrap',
|
|
2936
|
+
alignItems: isVerticalLegend ? 'flex-start' : 'center'
|
|
2161
2937
|
},
|
|
2162
2938
|
children: datasets.map((dataset, index) => /*#__PURE__*/_jsxs(View, {
|
|
2163
2939
|
style: {
|
|
2164
2940
|
flexDirection: 'row',
|
|
2165
2941
|
alignItems: 'center',
|
|
2166
|
-
marginRight: legendSpacing,
|
|
2167
|
-
marginBottom: 6
|
|
2942
|
+
marginRight: isVerticalLegend ? 0 : legendSpacing,
|
|
2943
|
+
marginBottom: isVerticalLegend ? legendSpacing : 6
|
|
2168
2944
|
},
|
|
2169
2945
|
children: [/*#__PURE__*/_jsx(View, {
|
|
2170
2946
|
style: {
|
|
2171
2947
|
width: 12,
|
|
2172
2948
|
height: 12,
|
|
2173
2949
|
borderRadius: 6,
|
|
2174
|
-
backgroundColor: dataset.borderColor,
|
|
2950
|
+
backgroundColor: dataset.fillColor ?? dataset.borderColor,
|
|
2951
|
+
borderWidth: 1,
|
|
2952
|
+
borderColor: dataset.borderColor,
|
|
2175
2953
|
marginRight: 6
|
|
2176
2954
|
}
|
|
2177
2955
|
}), /*#__PURE__*/_jsx(Text, {
|
|
2956
|
+
numberOfLines: 1,
|
|
2178
2957
|
style: legendStyle,
|
|
2179
2958
|
children: dataset.label
|
|
2180
2959
|
})]
|
|
@@ -2182,12 +2961,10 @@ function RadarChart({
|
|
|
2182
2961
|
});
|
|
2183
2962
|
const chart = /*#__PURE__*/_jsx(Animated.View, {
|
|
2184
2963
|
style: {
|
|
2185
|
-
width: normalizedWidth ??
|
|
2964
|
+
width: isVerticalLegend ? width : normalizedWidth ?? '100%',
|
|
2965
|
+
maxWidth: '100%',
|
|
2966
|
+
flexShrink: 1,
|
|
2186
2967
|
height,
|
|
2187
|
-
paddingLeft: padding.left,
|
|
2188
|
-
paddingRight: padding.right,
|
|
2189
|
-
paddingTop: padding.top,
|
|
2190
|
-
paddingBottom: padding.bottom,
|
|
2191
2968
|
opacity: reveal
|
|
2192
2969
|
},
|
|
2193
2970
|
onLayout: event => {
|
|
@@ -2226,24 +3003,28 @@ function RadarChart({
|
|
|
2226
3003
|
}, `axis-${index}`);
|
|
2227
3004
|
}), axes.map((axis, index) => {
|
|
2228
3005
|
const angle = index * axisAngle - Math.PI / 2;
|
|
2229
|
-
const labelX = center.x + (radius +
|
|
2230
|
-
const labelY = center.y + (radius +
|
|
3006
|
+
const labelX = center.x + (radius + resolvedLabelOffset) * Math.cos(angle);
|
|
3007
|
+
const labelY = center.y + (radius + resolvedLabelOffset) * Math.sin(angle);
|
|
2231
3008
|
return /*#__PURE__*/_jsx(SvgText, {
|
|
2232
3009
|
x: labelX,
|
|
2233
3010
|
y: labelY,
|
|
2234
|
-
fontSize:
|
|
3011
|
+
fontSize: resolvedAxisLabelFontSize,
|
|
2235
3012
|
fontWeight: axisLabelStyle.fontWeight,
|
|
2236
3013
|
fill: axisLabelStyle.color,
|
|
2237
3014
|
textAnchor: "middle",
|
|
2238
3015
|
children: axis.label
|
|
2239
3016
|
}, `label-${index}`);
|
|
2240
|
-
}), datasetPolygons.map((entry, index) =>
|
|
2241
|
-
|
|
2242
|
-
|
|
2243
|
-
|
|
2244
|
-
|
|
2245
|
-
|
|
2246
|
-
|
|
3017
|
+
}), datasetPolygons.map((entry, index) => {
|
|
3018
|
+
const strokePattern = resolveRadarStrokePattern(entry.dataset);
|
|
3019
|
+
return /*#__PURE__*/_jsx(Polygon, {
|
|
3020
|
+
points: entry.points.join(' '),
|
|
3021
|
+
stroke: entry.dataset.borderColor,
|
|
3022
|
+
strokeWidth: entry.dataset.borderWidth,
|
|
3023
|
+
fill: entry.dataset.fillColor ?? 'transparent',
|
|
3024
|
+
strokeDasharray: strokePattern.strokeDasharray,
|
|
3025
|
+
strokeLinecap: strokePattern.strokeLinecap
|
|
3026
|
+
}, `dataset-${index}`);
|
|
3027
|
+
}), datasetPolygons.map((entry, datasetIndex) => entry.dataset.showPoints ? entry.points.map((point, pointIndex) => {
|
|
2247
3028
|
const [x, y] = point.split(',').map(val => Number(val));
|
|
2248
3029
|
return /*#__PURE__*/_jsx(Circle, {
|
|
2249
3030
|
cx: x,
|
|
@@ -2255,23 +3036,23 @@ function RadarChart({
|
|
|
2255
3036
|
})
|
|
2256
3037
|
})
|
|
2257
3038
|
});
|
|
2258
|
-
|
|
2259
|
-
|
|
2260
|
-
backgroundColor
|
|
2261
|
-
},
|
|
2262
|
-
children: [chart, showLegend && legendPosition === 'bottom' ? /*#__PURE__*/_jsx(View, {
|
|
3039
|
+
if (!showLegend) {
|
|
3040
|
+
return /*#__PURE__*/_jsx(View, {
|
|
2263
3041
|
style: {
|
|
2264
|
-
|
|
3042
|
+
backgroundColor
|
|
2265
3043
|
},
|
|
2266
|
-
children:
|
|
2267
|
-
})
|
|
2268
|
-
}
|
|
2269
|
-
if (!showLegend || legendPosition === 'bottom') return composed;
|
|
3044
|
+
children: chart
|
|
3045
|
+
});
|
|
3046
|
+
}
|
|
2270
3047
|
if (legendPosition === 'top') {
|
|
2271
3048
|
return /*#__PURE__*/_jsxs(View, {
|
|
3049
|
+
style: {
|
|
3050
|
+
backgroundColor,
|
|
3051
|
+
alignItems: 'center'
|
|
3052
|
+
},
|
|
2272
3053
|
children: [legend, /*#__PURE__*/_jsx(View, {
|
|
2273
3054
|
style: {
|
|
2274
|
-
height:
|
|
3055
|
+
height: legendSpacing
|
|
2275
3056
|
}
|
|
2276
3057
|
}), chart]
|
|
2277
3058
|
});
|
|
@@ -2279,11 +3060,13 @@ function RadarChart({
|
|
|
2279
3060
|
if (legendPosition === 'left') {
|
|
2280
3061
|
return /*#__PURE__*/_jsxs(View, {
|
|
2281
3062
|
style: {
|
|
2282
|
-
|
|
3063
|
+
backgroundColor,
|
|
3064
|
+
flexDirection: 'row',
|
|
3065
|
+
alignItems: 'center'
|
|
2283
3066
|
},
|
|
2284
3067
|
children: [legend, /*#__PURE__*/_jsx(View, {
|
|
2285
3068
|
style: {
|
|
2286
|
-
width:
|
|
3069
|
+
width: legendSpacing
|
|
2287
3070
|
}
|
|
2288
3071
|
}), chart]
|
|
2289
3072
|
});
|
|
@@ -2291,16 +3074,167 @@ function RadarChart({
|
|
|
2291
3074
|
if (legendPosition === 'right') {
|
|
2292
3075
|
return /*#__PURE__*/_jsxs(View, {
|
|
2293
3076
|
style: {
|
|
2294
|
-
|
|
3077
|
+
backgroundColor,
|
|
3078
|
+
flexDirection: 'row',
|
|
3079
|
+
alignItems: 'center'
|
|
2295
3080
|
},
|
|
2296
3081
|
children: [chart, /*#__PURE__*/_jsx(View, {
|
|
2297
3082
|
style: {
|
|
2298
|
-
width:
|
|
3083
|
+
width: legendSpacing
|
|
2299
3084
|
}
|
|
2300
3085
|
}), legend]
|
|
2301
3086
|
});
|
|
2302
3087
|
}
|
|
2303
|
-
return
|
|
3088
|
+
return /*#__PURE__*/_jsxs(View, {
|
|
3089
|
+
style: {
|
|
3090
|
+
backgroundColor,
|
|
3091
|
+
alignItems: 'center'
|
|
3092
|
+
},
|
|
3093
|
+
children: [chart, /*#__PURE__*/_jsx(View, {
|
|
3094
|
+
style: {
|
|
3095
|
+
height: legendSpacing
|
|
3096
|
+
}
|
|
3097
|
+
}), legend]
|
|
3098
|
+
});
|
|
3099
|
+
}
|
|
3100
|
+
const RADAR_DASH_DEFAULT_LENGTH = 8;
|
|
3101
|
+
const RADAR_DASH_GAP_FACTOR = 0.6;
|
|
3102
|
+
const RADAR_DOT_DASH_LENGTH = 0.001;
|
|
3103
|
+
const RADAR_DOT_GAP_FACTOR = 2.4;
|
|
3104
|
+
const RADAR_DOT_MIN_GAP = 3;
|
|
3105
|
+
const radarDashConflictWarnings = new Set();
|
|
3106
|
+
function isDevelopmentEnvironment() {
|
|
3107
|
+
return process.env.NODE_ENV !== 'production';
|
|
3108
|
+
}
|
|
3109
|
+
function warnRadarDashConflictOnce(label) {
|
|
3110
|
+
if (!isDevelopmentEnvironment()) return;
|
|
3111
|
+
if (radarDashConflictWarnings.has(label)) return;
|
|
3112
|
+
radarDashConflictWarnings.add(label);
|
|
3113
|
+
// Keep API backward-compatible but deterministic when both flags are passed.
|
|
3114
|
+
console.warn(`[RadarChart] Dataset "${label}" received both dotted=true and dashed=true. dashed takes precedence.`);
|
|
3115
|
+
}
|
|
3116
|
+
function resolveRadarBorderStyle(dataset, fallbackLabel) {
|
|
3117
|
+
const dotted = dataset.dotted === true;
|
|
3118
|
+
const dashed = dataset.dashed === true;
|
|
3119
|
+
if (dotted && dashed) {
|
|
3120
|
+
warnRadarDashConflictOnce(fallbackLabel);
|
|
3121
|
+
return 'dashed';
|
|
3122
|
+
}
|
|
3123
|
+
if (dashed) return 'dashed';
|
|
3124
|
+
if (dotted) return 'dotted';
|
|
3125
|
+
const borderStyle = (dataset.borderStyle ?? 'solid').toString().toLowerCase();
|
|
3126
|
+
if (borderStyle === 'dotted' || borderStyle === 'dashed') {
|
|
3127
|
+
return borderStyle;
|
|
3128
|
+
}
|
|
3129
|
+
return 'solid';
|
|
3130
|
+
}
|
|
3131
|
+
function resolveRadarStrokePattern(dataset) {
|
|
3132
|
+
if (dataset.borderStyle === 'dotted') {
|
|
3133
|
+
// Tiny dash + round caps renders circular dots more reliably than short dashes.
|
|
3134
|
+
const gap = Math.max(RADAR_DOT_MIN_GAP, dataset.borderWidth * RADAR_DOT_GAP_FACTOR);
|
|
3135
|
+
return {
|
|
3136
|
+
strokeDasharray: `${RADAR_DOT_DASH_LENGTH} ${gap}`,
|
|
3137
|
+
strokeLinecap: 'round'
|
|
3138
|
+
};
|
|
3139
|
+
}
|
|
3140
|
+
if (dataset.borderStyle === 'dashed') {
|
|
3141
|
+
if (Array.isArray(dataset.dashArray) && dataset.dashArray.length > 0) {
|
|
3142
|
+
const dash = Math.max(1, dataset.dashArray[0] ?? RADAR_DASH_DEFAULT_LENGTH);
|
|
3143
|
+
const gap = Math.max(1, dataset.dashArray[1] ?? dash * RADAR_DASH_GAP_FACTOR);
|
|
3144
|
+
return {
|
|
3145
|
+
strokeDasharray: `${dash} ${gap}`,
|
|
3146
|
+
strokeLinecap: 'butt'
|
|
3147
|
+
};
|
|
3148
|
+
}
|
|
3149
|
+
const dashLength = typeof dataset.dashLength === 'number' && Number.isFinite(dataset.dashLength) && dataset.dashLength > 0 ? dataset.dashLength : RADAR_DASH_DEFAULT_LENGTH;
|
|
3150
|
+
const gap = Math.max(2, dashLength * RADAR_DASH_GAP_FACTOR);
|
|
3151
|
+
return {
|
|
3152
|
+
strokeDasharray: `${dashLength} ${gap}`,
|
|
3153
|
+
strokeLinecap: 'butt'
|
|
3154
|
+
};
|
|
3155
|
+
}
|
|
3156
|
+
return {};
|
|
3157
|
+
}
|
|
3158
|
+
export const RADAR_LABEL_ESTIMATION = {
|
|
3159
|
+
minRadius: 10,
|
|
3160
|
+
safeEdge: 4,
|
|
3161
|
+
minLabelWidthFactor: 1.2,
|
|
3162
|
+
avgCharWidthFactor: 0.58,
|
|
3163
|
+
lineHeightFactor: 1.2,
|
|
3164
|
+
defaultFontSize: 14,
|
|
3165
|
+
minLabelOffset: 20,
|
|
3166
|
+
labelOffsetFactor: 1.5
|
|
3167
|
+
};
|
|
3168
|
+
export function resolveRadarAxisLabelFontSize(value) {
|
|
3169
|
+
const parsed = Number(value);
|
|
3170
|
+
if (!Number.isFinite(parsed) || parsed <= 0) {
|
|
3171
|
+
return RADAR_LABEL_ESTIMATION.defaultFontSize;
|
|
3172
|
+
}
|
|
3173
|
+
return parsed;
|
|
3174
|
+
}
|
|
3175
|
+
export function resolveRadarLabelOffset(value, fontSize) {
|
|
3176
|
+
const parsed = Number(value);
|
|
3177
|
+
if (Number.isFinite(parsed) && parsed > 0) {
|
|
3178
|
+
return parsed;
|
|
3179
|
+
}
|
|
3180
|
+
return Math.max(RADAR_LABEL_ESTIMATION.minLabelOffset, fontSize * RADAR_LABEL_ESTIMATION.labelOffsetFactor);
|
|
3181
|
+
}
|
|
3182
|
+
export function estimateRadarLabelSize(label, fontSize) {
|
|
3183
|
+
const safeLabel = `${label ?? ''}`;
|
|
3184
|
+
const width = Math.max(safeLabel.length * fontSize * RADAR_LABEL_ESTIMATION.avgCharWidthFactor, fontSize * RADAR_LABEL_ESTIMATION.minLabelWidthFactor);
|
|
3185
|
+
const height = fontSize * RADAR_LABEL_ESTIMATION.lineHeightFactor;
|
|
3186
|
+
return {
|
|
3187
|
+
width,
|
|
3188
|
+
height
|
|
3189
|
+
};
|
|
3190
|
+
}
|
|
3191
|
+
export function calculateRadarChartRadius({
|
|
3192
|
+
width,
|
|
3193
|
+
height,
|
|
3194
|
+
padding,
|
|
3195
|
+
axisLabels,
|
|
3196
|
+
fontSize,
|
|
3197
|
+
autoFitLabels = true,
|
|
3198
|
+
labelOffset
|
|
3199
|
+
}) {
|
|
3200
|
+
const center = {
|
|
3201
|
+
x: width / 2,
|
|
3202
|
+
y: height / 2
|
|
3203
|
+
};
|
|
3204
|
+
const baseRadius = Math.max(Math.min(width, height) / 2 - Math.max(padding.left, padding.right, padding.top, padding.bottom), RADAR_LABEL_ESTIMATION.minRadius);
|
|
3205
|
+
if (!autoFitLabels || axisLabels.length === 0) {
|
|
3206
|
+
return baseRadius;
|
|
3207
|
+
}
|
|
3208
|
+
const angleStep = Math.PI * 2 / axisLabels.length;
|
|
3209
|
+
let maxAllowedRadius = baseRadius;
|
|
3210
|
+
for (let index = 0; index < axisLabels.length; index += 1) {
|
|
3211
|
+
const {
|
|
3212
|
+
width: labelWidth,
|
|
3213
|
+
height: labelHeight
|
|
3214
|
+
} = estimateRadarLabelSize(axisLabels[index] ?? '', fontSize);
|
|
3215
|
+
const halfWidth = labelWidth / 2;
|
|
3216
|
+
const halfHeight = labelHeight / 2;
|
|
3217
|
+
const angle = index * angleStep - Math.PI / 2;
|
|
3218
|
+
const dx = Math.cos(angle);
|
|
3219
|
+
const dy = Math.sin(angle);
|
|
3220
|
+
|
|
3221
|
+
// Solve per-axis line constraints so estimated label bounds stay inside.
|
|
3222
|
+
if (dx > 0.0001) {
|
|
3223
|
+
maxAllowedRadius = Math.min(maxAllowedRadius, (width - RADAR_LABEL_ESTIMATION.safeEdge - halfWidth - center.x) / dx - labelOffset);
|
|
3224
|
+
} else if (dx < -0.0001) {
|
|
3225
|
+
maxAllowedRadius = Math.min(maxAllowedRadius, (RADAR_LABEL_ESTIMATION.safeEdge + halfWidth - center.x) / dx - labelOffset);
|
|
3226
|
+
}
|
|
3227
|
+
if (dy > 0.0001) {
|
|
3228
|
+
maxAllowedRadius = Math.min(maxAllowedRadius, (height - RADAR_LABEL_ESTIMATION.safeEdge - halfHeight - center.y) / dy - labelOffset);
|
|
3229
|
+
} else if (dy < -0.0001) {
|
|
3230
|
+
maxAllowedRadius = Math.min(maxAllowedRadius, (RADAR_LABEL_ESTIMATION.safeEdge + halfHeight - center.y) / dy - labelOffset);
|
|
3231
|
+
}
|
|
3232
|
+
}
|
|
3233
|
+
const boundedRadius = Math.min(maxAllowedRadius, baseRadius);
|
|
3234
|
+
if (!Number.isFinite(boundedRadius)) {
|
|
3235
|
+
return baseRadius;
|
|
3236
|
+
}
|
|
3237
|
+
return Math.max(boundedRadius, RADAR_LABEL_ESTIMATION.minRadius);
|
|
2304
3238
|
}
|
|
2305
3239
|
export function parseRadarAxes(rawAxes) {
|
|
2306
3240
|
if (!Array.isArray(rawAxes)) return [];
|
|
@@ -2310,9 +3244,10 @@ export function parseRadarAxes(rawAxes) {
|
|
|
2310
3244
|
const label = axis.label ?? '';
|
|
2311
3245
|
if (!label) return;
|
|
2312
3246
|
const maxValue = resolveNumericValue(axis.maxValue, {}) ?? 100;
|
|
3247
|
+
const normalizedMaxValue = Number.isFinite(maxValue) && maxValue > 0 ? maxValue : 100;
|
|
2313
3248
|
axes.push({
|
|
2314
3249
|
label,
|
|
2315
|
-
maxValue:
|
|
3250
|
+
maxValue: normalizedMaxValue
|
|
2316
3251
|
});
|
|
2317
3252
|
});
|
|
2318
3253
|
return axes;
|
|
@@ -2320,24 +3255,27 @@ export function parseRadarAxes(rawAxes) {
|
|
|
2320
3255
|
export function parseRadarDatasets(raw, axisLength, formData) {
|
|
2321
3256
|
if (!Array.isArray(raw) || axisLength === 0) return [];
|
|
2322
3257
|
const datasets = [];
|
|
2323
|
-
raw.forEach(dataset => {
|
|
3258
|
+
raw.forEach((dataset, datasetIndex) => {
|
|
2324
3259
|
if (!dataset || typeof dataset !== 'object') return;
|
|
2325
3260
|
const valuesRaw = Array.isArray(dataset.data) ? dataset.data : [];
|
|
2326
3261
|
const values = [];
|
|
2327
3262
|
for (let i = 0; i < axisLength; i += 1) {
|
|
2328
3263
|
const rawValue = i < valuesRaw.length ? valuesRaw[i] : null;
|
|
2329
|
-
const resolved = resolveNumericValue(rawValue, formData)
|
|
2330
|
-
values.push(resolved);
|
|
3264
|
+
const resolved = resolveNumericValue(rawValue, formData);
|
|
3265
|
+
values.push(typeof resolved === 'number' && Number.isFinite(resolved) ? resolved : 0);
|
|
2331
3266
|
}
|
|
2332
3267
|
const label = dataset.label ?? `Dataset ${datasets.length + 1}`;
|
|
2333
3268
|
const borderColor = parseColor(dataset.borderColor ?? '0xFF2196F3');
|
|
2334
3269
|
const fillColor = dataset.fillColor ? parseColor(dataset.fillColor) : undefined;
|
|
2335
3270
|
const pointColor = parseColor(dataset.pointColor ?? dataset.borderColor ?? '0xFF2196F3');
|
|
2336
|
-
const
|
|
2337
|
-
const
|
|
2338
|
-
const
|
|
2339
|
-
const
|
|
2340
|
-
const
|
|
3271
|
+
const rawBorderWidth = Number(dataset.borderWidth ?? 2);
|
|
3272
|
+
const borderWidth = Number.isFinite(rawBorderWidth) && rawBorderWidth > 0 ? rawBorderWidth : 2;
|
|
3273
|
+
const rawPointRadius = Number(dataset.pointRadius ?? 4);
|
|
3274
|
+
const pointRadius = Number.isFinite(rawPointRadius) && rawPointRadius >= 0 ? rawPointRadius : 4;
|
|
3275
|
+
const normalizedStyle = resolveRadarBorderStyle(dataset, label ?? `Dataset ${datasetIndex + 1}`);
|
|
3276
|
+
const dashArray = Array.isArray(dataset.dashArray) ? dataset.dashArray.map(val => Number(val)).filter(val => Number.isFinite(val) && val > 0) : undefined;
|
|
3277
|
+
const rawDashLength = Number(dataset.dashLength);
|
|
3278
|
+
const dashLength = Number.isFinite(rawDashLength) && rawDashLength > 0 ? rawDashLength : undefined;
|
|
2341
3279
|
const showPoints = dataset.showPoints !== false;
|
|
2342
3280
|
datasets.push({
|
|
2343
3281
|
label,
|
|
@@ -2346,6 +3284,7 @@ export function parseRadarDatasets(raw, axisLength, formData) {
|
|
|
2346
3284
|
borderWidth,
|
|
2347
3285
|
borderStyle: normalizedStyle,
|
|
2348
3286
|
dashArray,
|
|
3287
|
+
dashLength,
|
|
2349
3288
|
showPoints,
|
|
2350
3289
|
pointRadius,
|
|
2351
3290
|
pointColor,
|
|
@@ -2359,7 +3298,9 @@ export function normalizeAxisMaximums(axes, datasets) {
|
|
|
2359
3298
|
let maxValue = axis.maxValue;
|
|
2360
3299
|
datasets.forEach(dataset => {
|
|
2361
3300
|
const value = dataset.values[index];
|
|
2362
|
-
if (value !== undefined && value > maxValue)
|
|
3301
|
+
if (value !== undefined && Number.isFinite(value) && value > maxValue) {
|
|
3302
|
+
maxValue = value;
|
|
3303
|
+
}
|
|
2363
3304
|
});
|
|
2364
3305
|
return {
|
|
2365
3306
|
...axis,
|
|
@@ -2368,12 +3309,15 @@ export function normalizeAxisMaximums(axes, datasets) {
|
|
|
2368
3309
|
});
|
|
2369
3310
|
}
|
|
2370
3311
|
function resolveRadarWidth(parsedWidth, measuredWidth) {
|
|
3312
|
+
if (measuredWidth > 0) {
|
|
3313
|
+
if (typeof parsedWidth === 'number' && Number.isFinite(parsedWidth)) {
|
|
3314
|
+
return Math.max(1, Math.min(parsedWidth, measuredWidth));
|
|
3315
|
+
}
|
|
3316
|
+
return Math.max(1, measuredWidth);
|
|
3317
|
+
}
|
|
2371
3318
|
if (typeof parsedWidth === 'number' && Number.isFinite(parsedWidth)) {
|
|
2372
3319
|
return parsedWidth;
|
|
2373
3320
|
}
|
|
2374
|
-
if (measuredWidth > 0) {
|
|
2375
|
-
return measuredWidth;
|
|
2376
|
-
}
|
|
2377
3321
|
return 300;
|
|
2378
3322
|
}
|
|
2379
3323
|
function radarPlaceholder(message) {
|