flowboard-react 0.6.0 → 0.6.2
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 +9 -9
- package/lib/module/Flowboard.js +16 -13
- package/lib/module/Flowboard.js.map +1 -1
- package/lib/module/components/FlowboardFlow.js +25 -3
- package/lib/module/components/FlowboardFlow.js.map +1 -1
- package/lib/module/components/FlowboardRenderer.js +413 -79
- package/lib/module/components/FlowboardRenderer.js.map +1 -1
- package/lib/module/core/apiConfig.js +47 -0
- package/lib/module/core/apiConfig.js.map +1 -0
- package/lib/module/core/resolverService.js +33 -37
- package/lib/module/core/resolverService.js.map +1 -1
- package/lib/module/utils/flowboardUtils.js +4 -1
- package/lib/module/utils/flowboardUtils.js.map +1 -1
- package/lib/typescript/src/Flowboard.d.ts +2 -4
- package/lib/typescript/src/Flowboard.d.ts.map +1 -1
- package/lib/typescript/src/components/FlowboardFlow.d.ts.map +1 -1
- package/lib/typescript/src/components/FlowboardRenderer.d.ts +33 -0
- package/lib/typescript/src/components/FlowboardRenderer.d.ts.map +1 -1
- package/lib/typescript/src/core/apiConfig.d.ts +6 -0
- package/lib/typescript/src/core/apiConfig.d.ts.map +1 -0
- package/lib/typescript/src/core/resolverService.d.ts +8 -5
- package/lib/typescript/src/core/resolverService.d.ts.map +1 -1
- package/lib/typescript/src/index.d.ts +1 -1
- package/lib/typescript/src/index.d.ts.map +1 -1
- package/lib/typescript/src/types/flowboard.d.ts +2 -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 +18 -19
- package/src/Flowboard.ts +17 -16
- package/src/components/FlowboardFlow.tsx +37 -2
- package/src/components/FlowboardRenderer.tsx +681 -100
- package/src/core/apiConfig.ts +59 -0
- package/src/core/resolverService.ts +40 -41
- package/src/index.tsx +1 -0
- package/src/types/flowboard.ts +3 -0
- package/src/utils/flowboardUtils.ts +9 -1
|
@@ -429,18 +429,33 @@ function isFillDimensionValue(value) {
|
|
|
429
429
|
if (typeof value === 'number') return value === Number.POSITIVE_INFINITY;
|
|
430
430
|
if (typeof value !== 'string') return false;
|
|
431
431
|
const normalized = value.trim().toLowerCase();
|
|
432
|
-
return normalized === 'infinity' || normalized === 'double.infinity' || normalized === '+infinity' || normalized === '100%';
|
|
432
|
+
return normalized === 'infinity' || normalized === 'infinite' || normalized === 'double.infinity' || normalized === '+infinity' || normalized === '100%';
|
|
433
433
|
}
|
|
434
434
|
function isFixedDimensionValue(value) {
|
|
435
435
|
if (isFillDimensionValue(value)) return false;
|
|
436
436
|
const parsed = Number(value);
|
|
437
437
|
return Number.isFinite(parsed);
|
|
438
438
|
}
|
|
439
|
+
function normalizeStackSizeMode(value) {
|
|
440
|
+
if (typeof value !== 'string') return undefined;
|
|
441
|
+
const normalized = value.trim().toLowerCase();
|
|
442
|
+
if (normalized === 'fit' || normalized === 'fixed') return normalized;
|
|
443
|
+
if (normalized === 'fill' || normalized === 'infinity' || normalized === 'infinite' || normalized === 'double.infinity' || normalized === '+infinity') {
|
|
444
|
+
return 'fill';
|
|
445
|
+
}
|
|
446
|
+
return undefined;
|
|
447
|
+
}
|
|
448
|
+
function wantsFillWidth(props) {
|
|
449
|
+
return normalizeStackSizeMode(props.size?.width) === 'fill' || isFillDimensionValue(props.width);
|
|
450
|
+
}
|
|
451
|
+
function wantsFillHeight(props) {
|
|
452
|
+
return normalizeStackSizeMode(props.size?.height) === 'fill' || isFillDimensionValue(props.height);
|
|
453
|
+
}
|
|
439
454
|
function normalizeStackSize(props, axis) {
|
|
440
|
-
const widthMode =
|
|
441
|
-
const heightMode =
|
|
442
|
-
const width = widthMode
|
|
443
|
-
const height = heightMode
|
|
455
|
+
const widthMode = normalizeStackSizeMode(props.size?.width);
|
|
456
|
+
const heightMode = normalizeStackSizeMode(props.size?.height);
|
|
457
|
+
const width = widthMode ? widthMode : props.fit === 'expand' || isFillDimensionValue(props.width) ? 'fill' : isFixedDimensionValue(props.width) ? 'fixed' : axis === 'horizontal' ? 'fit' : 'fill';
|
|
458
|
+
const height = heightMode ? heightMode : props.fit === 'expand' || isFillDimensionValue(props.height) ? 'fill' : isFixedDimensionValue(props.height) ? 'fixed' : axis === 'vertical' ? 'fit' : 'fill';
|
|
444
459
|
return {
|
|
445
460
|
width,
|
|
446
461
|
height
|
|
@@ -524,8 +539,6 @@ function mergeStackProps(parentProps, childProps) {
|
|
|
524
539
|
}
|
|
525
540
|
const axisSource = axis === parentAxis && axis !== childAxis ? parentProps : childProps;
|
|
526
541
|
const fallbackAxisSource = axisSource === childProps ? parentProps : childProps;
|
|
527
|
-
const parentPadding = spacingToInsets(parentProps.layout?.padding);
|
|
528
|
-
const childPadding = spacingToInsets(childProps.layout?.padding);
|
|
529
542
|
const parentMargin = spacingToInsets(parentProps.layout?.margin);
|
|
530
543
|
const childMargin = spacingToInsets(childProps.layout?.margin);
|
|
531
544
|
const mergedProps = {
|
|
@@ -538,12 +551,6 @@ function mergeStackProps(parentProps, childProps) {
|
|
|
538
551
|
height: childProps.size?.height ?? parentProps.size?.height ?? 'fit'
|
|
539
552
|
},
|
|
540
553
|
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
554
|
margin: insetsToSpacing({
|
|
548
555
|
top: parentMargin.top + childMargin.top,
|
|
549
556
|
right: parentMargin.right + childMargin.right,
|
|
@@ -598,7 +605,6 @@ function normalizeStackLikeNode(json) {
|
|
|
598
605
|
childSpacing: Number(props.childSpacing ?? props.gap ?? props.spacing ?? 0),
|
|
599
606
|
size,
|
|
600
607
|
layout: {
|
|
601
|
-
padding: normalizeStackSpacingConfig(props.layout?.padding ?? props.padding),
|
|
602
608
|
margin: normalizeStackSpacingConfig(props.layout?.margin ?? props.margin)
|
|
603
609
|
},
|
|
604
610
|
appearance: {
|
|
@@ -697,10 +703,11 @@ function parseOverlayGridAlignment(value) {
|
|
|
697
703
|
function resolveStackDimension(props, axis, key, axisBounds) {
|
|
698
704
|
const mode = props.size?.[key];
|
|
699
705
|
const legacy = normalizeDimension(parseLayoutDimension(props[key]));
|
|
706
|
+
const fixedPx = parseLayoutDimension(props.size?.[`${key}Px`], true);
|
|
700
707
|
const isBounded = key === 'width' ? axisBounds.widthBounded : axisBounds.heightBounded;
|
|
701
708
|
if (mode === 'fill') return isBounded ? '100%' : legacy;
|
|
702
709
|
if (mode === 'fit') return legacy;
|
|
703
|
-
if (mode === 'fixed') return legacy;
|
|
710
|
+
if (mode === 'fixed') return legacy ?? fixedPx;
|
|
704
711
|
if (legacy !== undefined) return legacy;
|
|
705
712
|
if (key === 'width' && axis !== 'horizontal' && axisBounds.widthBounded) return '100%';
|
|
706
713
|
if (key === 'height' && axis === 'horizontal' && axisBounds.heightBounded) return '100%';
|
|
@@ -1204,19 +1211,24 @@ function buildWidget(json, params) {
|
|
|
1204
1211
|
}
|
|
1205
1212
|
case 'slider':
|
|
1206
1213
|
{
|
|
1214
|
+
const direction = props.direction === 'vertical' ? 'vertical' : 'horizontal';
|
|
1215
|
+
const interaction = props.interaction ?? {};
|
|
1216
|
+
const pageControl = props.pageControl ?? {};
|
|
1217
|
+
const sliderChildren = (childrenJson ?? []).filter(Boolean);
|
|
1207
1218
|
node = /*#__PURE__*/_jsx(SliderWidget, {
|
|
1208
1219
|
id: json._internalId,
|
|
1209
|
-
|
|
1210
|
-
|
|
1211
|
-
|
|
1212
|
-
|
|
1213
|
-
|
|
1214
|
-
|
|
1215
|
-
|
|
1216
|
-
|
|
1217
|
-
|
|
1218
|
-
|
|
1219
|
-
|
|
1220
|
+
sliderProps: props,
|
|
1221
|
+
slidePropsList: sliderChildren.map(child => child?.properties ?? {}),
|
|
1222
|
+
direction: direction,
|
|
1223
|
+
pageAlignment: props.pageAlignment === 'start' || props.pageAlignment === 'end' ? props.pageAlignment : 'center',
|
|
1224
|
+
pageSpacing: Number(props.pageSpacing ?? 16),
|
|
1225
|
+
pagePeek: Number(props.pagePeek ?? 16),
|
|
1226
|
+
loop: interaction.loop === true,
|
|
1227
|
+
autoAdvance: interaction.autoAdvance === true,
|
|
1228
|
+
autoAdvanceIntervalMs: Number(interaction.autoAdvanceIntervalMs ?? 4000),
|
|
1229
|
+
pageControlEnabled: pageControl.enabled !== false,
|
|
1230
|
+
pageControlPosition: pageControl.position === 'top' ? 'top' : 'bottom',
|
|
1231
|
+
children: sliderChildren.map((child, index) => /*#__PURE__*/_jsx(View, {
|
|
1220
1232
|
children: buildWidget(child, {
|
|
1221
1233
|
...params
|
|
1222
1234
|
})
|
|
@@ -1224,6 +1236,19 @@ function buildWidget(json, params) {
|
|
|
1224
1236
|
});
|
|
1225
1237
|
break;
|
|
1226
1238
|
}
|
|
1239
|
+
case 'slide':
|
|
1240
|
+
{
|
|
1241
|
+
node = buildWidget({
|
|
1242
|
+
...json,
|
|
1243
|
+
type: 'stack',
|
|
1244
|
+
properties: {
|
|
1245
|
+
...(props || {})
|
|
1246
|
+
}
|
|
1247
|
+
}, {
|
|
1248
|
+
...params
|
|
1249
|
+
});
|
|
1250
|
+
break;
|
|
1251
|
+
}
|
|
1227
1252
|
case 'pageview_indicator':
|
|
1228
1253
|
{
|
|
1229
1254
|
node = /*#__PURE__*/_jsx(PageViewIndicator, {
|
|
@@ -1285,7 +1310,6 @@ function buildWidget(json, params) {
|
|
|
1285
1310
|
const isOverlay = axis === 'overlay';
|
|
1286
1311
|
const spacing = Number(props.childSpacing ?? 0);
|
|
1287
1312
|
const applySpacing = !STACK_SPACE_DISTRIBUTIONS.has(String(props.distribution ?? 'start'));
|
|
1288
|
-
const paddingInsets = stackSpacingToInsets(props.layout?.padding ?? props.padding);
|
|
1289
1313
|
const width = resolveStackDimension(props, axis, 'width', pageAxisBounds);
|
|
1290
1314
|
const height = resolveStackDimension(props, axis, 'height', pageAxisBounds);
|
|
1291
1315
|
const mainAxisBounded = axis === 'horizontal' ? pageAxisBounds.widthBounded : pageAxisBounds.heightBounded;
|
|
@@ -1318,14 +1342,18 @@ function buildWidget(json, params) {
|
|
|
1318
1342
|
overflow: borderRadius > 0 ? 'hidden' : undefined,
|
|
1319
1343
|
borderWidth: borderColor ? borderWidth : undefined,
|
|
1320
1344
|
borderColor: borderColor ?? undefined,
|
|
1321
|
-
...insetsToStyle(paddingInsets),
|
|
1322
1345
|
...shadowStyle
|
|
1323
1346
|
};
|
|
1347
|
+
const stackContentSizeStyle = {
|
|
1348
|
+
width: width !== undefined ? '100%' : undefined,
|
|
1349
|
+
height: height !== undefined ? '100%' : undefined
|
|
1350
|
+
};
|
|
1324
1351
|
const content = isOverlay ? /*#__PURE__*/_jsxs(View, {
|
|
1325
1352
|
style: {
|
|
1326
1353
|
position: 'relative',
|
|
1327
1354
|
minHeight: 0,
|
|
1328
|
-
minWidth: 0
|
|
1355
|
+
minWidth: 0,
|
|
1356
|
+
...stackContentSizeStyle
|
|
1329
1357
|
},
|
|
1330
1358
|
children: [(childrenJson ?? []).map((child, index) => {
|
|
1331
1359
|
const alignment = parseOverlayGridAlignment(props.overlayAlignment ?? props.alignment);
|
|
@@ -1352,7 +1380,8 @@ function buildWidget(json, params) {
|
|
|
1352
1380
|
justifyContent: parseFlexAlignment(props.distribution),
|
|
1353
1381
|
alignItems: parseCrossAlignment(props.alignment),
|
|
1354
1382
|
minHeight: 0,
|
|
1355
|
-
minWidth: 0
|
|
1383
|
+
minWidth: 0,
|
|
1384
|
+
...stackContentSizeStyle
|
|
1356
1385
|
},
|
|
1357
1386
|
children: (childrenJson ?? []).map((child, index) => /*#__PURE__*/_jsxs(React.Fragment, {
|
|
1358
1387
|
children: [buildWidget(child, {
|
|
@@ -1442,6 +1471,17 @@ function buildWidget(json, params) {
|
|
|
1442
1471
|
children: node
|
|
1443
1472
|
});
|
|
1444
1473
|
}
|
|
1474
|
+
const shouldStretchCrossAxis = params.parentFlexAxis === 'vertical' && (type === 'selection_list' || type === 'wheel_picker' || wantsFillWidth(props)) || params.parentFlexAxis === 'horizontal' && wantsFillHeight(props);
|
|
1475
|
+
if (shouldStretchCrossAxis) {
|
|
1476
|
+
node = /*#__PURE__*/_jsx(View, {
|
|
1477
|
+
style: {
|
|
1478
|
+
alignSelf: 'stretch',
|
|
1479
|
+
minWidth: 0,
|
|
1480
|
+
minHeight: 0
|
|
1481
|
+
},
|
|
1482
|
+
children: node
|
|
1483
|
+
});
|
|
1484
|
+
}
|
|
1445
1485
|
if (params.allowFlexExpansion) {
|
|
1446
1486
|
let shouldExpand = false;
|
|
1447
1487
|
const parentAxis = params.parentFlexAxis ?? 'vertical';
|
|
@@ -1750,7 +1790,7 @@ function parseLayoutDimension(value, zeroIsNull = false) {
|
|
|
1750
1790
|
if (typeof value === 'string') {
|
|
1751
1791
|
const trimmed = value.trim();
|
|
1752
1792
|
const lowered = trimmed.toLowerCase();
|
|
1753
|
-
if (lowered === 'double.infinity' || lowered === 'infinity' || lowered === '+infinity') {
|
|
1793
|
+
if (lowered === 'double.infinity' || lowered === 'infinite' || lowered === 'infinity' || lowered === '+infinity') {
|
|
1754
1794
|
return Number.POSITIVE_INFINITY;
|
|
1755
1795
|
}
|
|
1756
1796
|
if (trimmed.endsWith('%')) {
|
|
@@ -2086,6 +2126,12 @@ function SelectionList({
|
|
|
2086
2126
|
};
|
|
2087
2127
|
const layout = properties.layout ?? 'column';
|
|
2088
2128
|
const spacing = Number(properties.spacing ?? 8);
|
|
2129
|
+
const shouldOptionFillWidth = option => {
|
|
2130
|
+
if (layout === 'grid') return true;
|
|
2131
|
+
const child = option.child && typeof option.child === 'object' ? normalizeStackLikeNode(option.child) : option.child;
|
|
2132
|
+
const childProps = child && typeof child === 'object' ? child.properties ?? {} : {};
|
|
2133
|
+
return wantsFillWidth(childProps);
|
|
2134
|
+
};
|
|
2089
2135
|
const renderItem = (option, index) => {
|
|
2090
2136
|
const value = String(option.value ?? '');
|
|
2091
2137
|
const isSelected = selectedValues.includes(value);
|
|
@@ -2094,10 +2140,12 @@ function SelectionList({
|
|
|
2094
2140
|
const borderColor = parseColor(styleProps.borderColor);
|
|
2095
2141
|
const borderRadius = Number(styleProps.borderRadius ?? 8);
|
|
2096
2142
|
const borderWidth = Number(styleProps.borderWidth ?? 1);
|
|
2143
|
+
const fillWidth = shouldOptionFillWidth(option);
|
|
2097
2144
|
return /*#__PURE__*/_jsx(Pressable, {
|
|
2098
2145
|
onPress: () => toggleSelection(value),
|
|
2099
2146
|
style: {
|
|
2100
|
-
width: '100%',
|
|
2147
|
+
width: fillWidth ? '100%' : undefined,
|
|
2148
|
+
alignSelf: fillWidth ? 'stretch' : 'flex-start',
|
|
2101
2149
|
backgroundColor,
|
|
2102
2150
|
borderColor,
|
|
2103
2151
|
borderWidth,
|
|
@@ -2149,7 +2197,8 @@ function SelectionList({
|
|
|
2149
2197
|
},
|
|
2150
2198
|
children: options.map((option, index) => /*#__PURE__*/_jsx(View, {
|
|
2151
2199
|
style: {
|
|
2152
|
-
width: '100%',
|
|
2200
|
+
width: shouldOptionFillWidth(option) ? '100%' : undefined,
|
|
2201
|
+
alignSelf: shouldOptionFillWidth(option) ? 'stretch' : 'flex-start',
|
|
2153
2202
|
marginBottom: spacing
|
|
2154
2203
|
},
|
|
2155
2204
|
children: renderItem(option, index)
|
|
@@ -2751,15 +2800,167 @@ function FakeProgressBar({
|
|
|
2751
2800
|
});
|
|
2752
2801
|
}
|
|
2753
2802
|
const AnimatedCircle = Animated.createAnimatedComponent(Circle);
|
|
2803
|
+
export function resolvePeekInsets(pageAlignment, pagePeek) {
|
|
2804
|
+
const leading = pageAlignment === 'start' ? 0 : pageAlignment === 'end' ? pagePeek * 2 : pagePeek;
|
|
2805
|
+
const trailing = pageAlignment === 'end' ? 0 : pageAlignment === 'start' ? pagePeek * 2 : pagePeek;
|
|
2806
|
+
return {
|
|
2807
|
+
leading,
|
|
2808
|
+
trailing
|
|
2809
|
+
};
|
|
2810
|
+
}
|
|
2811
|
+
function resolveSliderSizeMode(sliderProps, key, fallback) {
|
|
2812
|
+
const raw = String(sliderProps?.size?.[key] ?? '').toLowerCase();
|
|
2813
|
+
if (raw === 'fill' || raw === 'fit' || raw === 'fixed') return raw;
|
|
2814
|
+
return sliderProps?.[key] !== undefined ? 'fixed' : fallback;
|
|
2815
|
+
}
|
|
2816
|
+
function resolveSlideSizeMode(slideProps, key) {
|
|
2817
|
+
const raw = String(slideProps?.size?.[key] ?? '').toLowerCase();
|
|
2818
|
+
if (raw === 'fill' || raw === 'fit' || raw === 'fixed') return raw;
|
|
2819
|
+
return key === 'width' ? 'fill' : 'fit';
|
|
2820
|
+
}
|
|
2821
|
+
function toFinite(value) {
|
|
2822
|
+
if (typeof value === 'number' && Number.isFinite(value)) return value;
|
|
2823
|
+
if (typeof value === 'string') {
|
|
2824
|
+
const parsed = Number(value);
|
|
2825
|
+
if (Number.isFinite(parsed)) return parsed;
|
|
2826
|
+
}
|
|
2827
|
+
return null;
|
|
2828
|
+
}
|
|
2829
|
+
export function resolveSliderContainerSize(args) {
|
|
2830
|
+
const dotsInset = args.showDots ? 8 + 6 + 8 : 0;
|
|
2831
|
+
const fitPrimary = args.direction === 'horizontal' ? args.activeSlideWidth : args.activeSlideHeight;
|
|
2832
|
+
const fitViewportPrimary = Math.max(1, fitPrimary + args.leadingPeek + args.trailingPeek);
|
|
2833
|
+
const viewportWidth = args.direction === 'horizontal' ? fitViewportPrimary : Math.max(1, args.activeSlideWidth);
|
|
2834
|
+
const viewportHeight = args.direction === 'vertical' ? fitViewportPrimary : Math.max(1, args.activeSlideHeight);
|
|
2835
|
+
const fitOuterWidth = Math.min(Math.max(1, viewportWidth), Math.max(1, args.availableWidth || viewportWidth));
|
|
2836
|
+
const fitOuterHeight = Math.max(1, viewportHeight + dotsInset);
|
|
2837
|
+
const outerWidth = args.widthMode === 'fill' ? '100%' : args.widthMode === 'fixed' ? Math.max(1, args.fixedWidth ?? fitOuterWidth) : fitOuterWidth;
|
|
2838
|
+
const outerHeight = args.heightMode === 'fill' ? '100%' : args.heightMode === 'fixed' ? Math.max(1, args.fixedHeight ?? fitOuterHeight) : fitOuterHeight;
|
|
2839
|
+
return {
|
|
2840
|
+
outerWidth,
|
|
2841
|
+
outerHeight,
|
|
2842
|
+
viewportWidth: args.widthMode === 'fit' ? fitOuterWidth : Math.max(1, args.availableWidth),
|
|
2843
|
+
viewportHeight: args.heightMode === 'fit' ? Math.max(1, fitOuterHeight - dotsInset) : Math.max(1, args.availableHeight || fitOuterHeight - dotsInset)
|
|
2844
|
+
};
|
|
2845
|
+
}
|
|
2846
|
+
export function resolveSlidePageSize(args) {
|
|
2847
|
+
const widthMode = resolveSlideSizeMode(args.slideProps, 'width');
|
|
2848
|
+
const heightMode = resolveSlideSizeMode(args.slideProps, 'height');
|
|
2849
|
+
const fixedWidth = toFinite(parseLayoutDimension(args.slideProps.width ?? args.slideProps.size?.widthPx, true));
|
|
2850
|
+
const fixedHeight = toFinite(parseLayoutDimension(args.slideProps.height ?? args.slideProps.size?.heightPx, true));
|
|
2851
|
+
const width = widthMode === 'fixed' ? Math.max(1, fixedWidth ?? args.pagePrimary) : widthMode === 'fit' ? undefined : args.direction === 'horizontal' ? args.pagePrimary : args.crossSize;
|
|
2852
|
+
const height = heightMode === 'fixed' ? Math.max(1, fixedHeight ?? args.pagePrimary) : heightMode === 'fit' ? undefined : args.direction === 'vertical' ? args.pagePrimary : args.crossSize;
|
|
2853
|
+
return {
|
|
2854
|
+
width: width ?? 'auto',
|
|
2855
|
+
height: height ?? 'auto'
|
|
2856
|
+
};
|
|
2857
|
+
}
|
|
2858
|
+
function resolveSliderBackgroundColor(sliderProps) {
|
|
2859
|
+
const extractColorCandidate = input => {
|
|
2860
|
+
if (input === null || input === undefined) return undefined;
|
|
2861
|
+
if (typeof input === 'string' || typeof input === 'number') return input;
|
|
2862
|
+
if (Array.isArray(input)) {
|
|
2863
|
+
for (const value of input) {
|
|
2864
|
+
const nested = extractColorCandidate(value);
|
|
2865
|
+
if (nested !== undefined) return nested;
|
|
2866
|
+
}
|
|
2867
|
+
return undefined;
|
|
2868
|
+
}
|
|
2869
|
+
if (typeof input !== 'object') return undefined;
|
|
2870
|
+
const source = input;
|
|
2871
|
+
if (Number.isFinite(Number(source.r)) && Number.isFinite(Number(source.g)) && Number.isFinite(Number(source.b))) {
|
|
2872
|
+
const alphaRaw = Number.isFinite(Number(source.a)) ? Number(source.a) : 1;
|
|
2873
|
+
const alpha = Math.max(0, Math.min(1, alphaRaw));
|
|
2874
|
+
return `rgba(${Number(source.r)},${Number(source.g)},${Number(source.b)},${alpha})`;
|
|
2875
|
+
}
|
|
2876
|
+
|
|
2877
|
+
// If a gradient is provided for slider fill, use the first stop as fallback.
|
|
2878
|
+
if (String(source.type ?? '').toLowerCase() === 'gradient' && Array.isArray(source.colors)) {
|
|
2879
|
+
const first = extractColorCandidate(source.colors);
|
|
2880
|
+
if (first !== undefined) return first;
|
|
2881
|
+
}
|
|
2882
|
+
const candidateKeys = ['value', 'color', 'backgroundColor', 'hex', 'argb', 'rgba', 'raw'];
|
|
2883
|
+
for (const key of candidateKeys) {
|
|
2884
|
+
const nested = extractColorCandidate(source[key]);
|
|
2885
|
+
if (nested !== undefined) return nested;
|
|
2886
|
+
}
|
|
2887
|
+
return undefined;
|
|
2888
|
+
};
|
|
2889
|
+
const parseSliderFillColor = color => {
|
|
2890
|
+
const candidate = extractColorCandidate(color);
|
|
2891
|
+
if (candidate === undefined) return 'transparent';
|
|
2892
|
+
if (typeof candidate === 'string') {
|
|
2893
|
+
const trimmed = candidate.trim();
|
|
2894
|
+
if (/^#[\da-fA-F]{8}$/.test(trimmed)) {
|
|
2895
|
+
// Dashboard stores alpha-enabled web hex as #RRGGBBAA.
|
|
2896
|
+
const rrggbbaa = trimmed.slice(1);
|
|
2897
|
+
const aarrggbb = `${rrggbbaa.slice(6, 8)}${rrggbbaa.slice(0, 6)}`;
|
|
2898
|
+
return parseColor(`0x${aarrggbb}`);
|
|
2899
|
+
}
|
|
2900
|
+
return parseColor(trimmed);
|
|
2901
|
+
}
|
|
2902
|
+
if (typeof candidate === 'number') {
|
|
2903
|
+
return parseColor(candidate);
|
|
2904
|
+
}
|
|
2905
|
+
if (typeof color === 'string') {
|
|
2906
|
+
const trimmed = color.trim();
|
|
2907
|
+
if (/^#[\da-fA-F]{8}$/.test(trimmed)) {
|
|
2908
|
+
// Dashboard stores alpha-enabled web hex as #RRGGBBAA.
|
|
2909
|
+
const rrggbbaa = trimmed.slice(1);
|
|
2910
|
+
const aarrggbb = `${rrggbbaa.slice(6, 8)}${rrggbbaa.slice(0, 6)}`;
|
|
2911
|
+
return parseColor(`0x${aarrggbb}`);
|
|
2912
|
+
}
|
|
2913
|
+
}
|
|
2914
|
+
return parseColor(candidate);
|
|
2915
|
+
};
|
|
2916
|
+
const appearanceFill = sliderProps?.appearance?.fill;
|
|
2917
|
+
if (typeof appearanceFill === 'string' || typeof appearanceFill === 'number') {
|
|
2918
|
+
return parseSliderFillColor(appearanceFill);
|
|
2919
|
+
}
|
|
2920
|
+
if (appearanceFill && typeof appearanceFill === 'object') {
|
|
2921
|
+
const type = String(appearanceFill.type ?? '').toLowerCase();
|
|
2922
|
+
if (type === 'color' || type === 'solid' || type === '') {
|
|
2923
|
+
const color = appearanceFill.value ?? appearanceFill.color;
|
|
2924
|
+
if (color !== undefined) return parseSliderFillColor(color);
|
|
2925
|
+
}
|
|
2926
|
+
}
|
|
2927
|
+
const fill = sliderProps?.fill;
|
|
2928
|
+
if (typeof fill === 'string' || typeof fill === 'number') {
|
|
2929
|
+
return parseSliderFillColor(fill);
|
|
2930
|
+
}
|
|
2931
|
+
if (fill && typeof fill === 'object') {
|
|
2932
|
+
const type = String(fill.type ?? '').toLowerCase();
|
|
2933
|
+
if (type === 'color' || type === 'solid' || type === '') {
|
|
2934
|
+
const color = fill.value ?? fill.color;
|
|
2935
|
+
if (color !== undefined) return parseSliderFillColor(color);
|
|
2936
|
+
}
|
|
2937
|
+
}
|
|
2938
|
+
const background = sliderProps?.background;
|
|
2939
|
+
if (background && typeof background === 'object') {
|
|
2940
|
+
const type = String(background.type ?? '').toLowerCase();
|
|
2941
|
+
if (type === 'color') {
|
|
2942
|
+
const color = background.value ?? background.color;
|
|
2943
|
+
if (color !== undefined) return parseSliderFillColor(color);
|
|
2944
|
+
}
|
|
2945
|
+
}
|
|
2946
|
+
if (sliderProps?.backgroundColor !== undefined) {
|
|
2947
|
+
return parseSliderFillColor(sliderProps.backgroundColor);
|
|
2948
|
+
}
|
|
2949
|
+
return undefined;
|
|
2950
|
+
}
|
|
2754
2951
|
function SliderWidget({
|
|
2755
2952
|
id,
|
|
2756
|
-
|
|
2953
|
+
sliderProps,
|
|
2954
|
+
slidePropsList,
|
|
2757
2955
|
direction,
|
|
2758
|
-
|
|
2759
|
-
|
|
2956
|
+
pageAlignment,
|
|
2957
|
+
pageSpacing,
|
|
2958
|
+
pagePeek,
|
|
2760
2959
|
loop,
|
|
2761
|
-
|
|
2762
|
-
|
|
2960
|
+
autoAdvance,
|
|
2961
|
+
autoAdvanceIntervalMs,
|
|
2962
|
+
pageControlEnabled,
|
|
2963
|
+
pageControlPosition,
|
|
2763
2964
|
children
|
|
2764
2965
|
}) {
|
|
2765
2966
|
const registry = useSliderRegistry();
|
|
@@ -2768,11 +2969,28 @@ function SliderWidget({
|
|
|
2768
2969
|
const pageCount = pageLength;
|
|
2769
2970
|
const [currentPage, setCurrentPage] = React.useState(0);
|
|
2770
2971
|
const timerRef = React.useRef(null);
|
|
2972
|
+
const [isInteracting, setIsInteracting] = React.useState(false);
|
|
2973
|
+
const idleTimerRef = React.useRef(null);
|
|
2974
|
+
const [availableSize, setAvailableSize] = React.useState({
|
|
2975
|
+
width: 1,
|
|
2976
|
+
height: 1
|
|
2977
|
+
});
|
|
2978
|
+
const [activeSlideSize, setActiveSlideSize] = React.useState({
|
|
2979
|
+
width: 1,
|
|
2980
|
+
height: 1
|
|
2981
|
+
});
|
|
2982
|
+
const markInteracting = React.useCallback(() => {
|
|
2983
|
+
setIsInteracting(true);
|
|
2984
|
+
if (idleTimerRef.current) {
|
|
2985
|
+
clearTimeout(idleTimerRef.current);
|
|
2986
|
+
}
|
|
2987
|
+
idleTimerRef.current = setTimeout(() => setIsInteracting(false), 1400);
|
|
2988
|
+
}, []);
|
|
2771
2989
|
React.useEffect(() => {
|
|
2772
2990
|
setCurrentPage(prev => clampSliderPage(prev, pageCount));
|
|
2773
2991
|
}, [pageCount]);
|
|
2774
2992
|
React.useEffect(() => {
|
|
2775
|
-
if (
|
|
2993
|
+
if (autoAdvance && !isInteracting && pageLength > 1) {
|
|
2776
2994
|
timerRef.current = setInterval(() => {
|
|
2777
2995
|
setCurrentPage(prev => {
|
|
2778
2996
|
const safePrev = clampSliderPage(prev, pageCount);
|
|
@@ -2780,12 +2998,12 @@ function SliderWidget({
|
|
|
2780
2998
|
if (loop) return 0;
|
|
2781
2999
|
return safePrev;
|
|
2782
3000
|
});
|
|
2783
|
-
},
|
|
3001
|
+
}, autoAdvanceIntervalMs);
|
|
2784
3002
|
}
|
|
2785
3003
|
return () => {
|
|
2786
3004
|
if (timerRef.current) clearInterval(timerRef.current);
|
|
2787
3005
|
};
|
|
2788
|
-
}, [
|
|
3006
|
+
}, [autoAdvance, autoAdvanceIntervalMs, isInteracting, loop, pageCount, pageLength]);
|
|
2789
3007
|
React.useEffect(() => {
|
|
2790
3008
|
if (pagerRef.current && pageCount > 0) {
|
|
2791
3009
|
pagerRef.current.setPage(clampSliderPage(currentPage, pageCount));
|
|
@@ -2796,44 +3014,165 @@ function SliderWidget({
|
|
|
2796
3014
|
registry.update(id, currentPage % children.length, children.length);
|
|
2797
3015
|
}
|
|
2798
3016
|
}, [registry, id, currentPage, children.length]);
|
|
2799
|
-
React.useEffect(() => {
|
|
2800
|
-
if (!linkedTo || !registry) return;
|
|
2801
|
-
return registry.getNotifier(linkedTo, value => {
|
|
2802
|
-
const normalized = normalizeLoopIndex(value, pageLength);
|
|
2803
|
-
const targetPage = clampSliderPage(normalized, pageCount);
|
|
2804
|
-
setCurrentPage(targetPage);
|
|
2805
|
-
if (pagerRef.current) {
|
|
2806
|
-
pagerRef.current.setPage(targetPage);
|
|
2807
|
-
}
|
|
2808
|
-
});
|
|
2809
|
-
}, [linkedTo, pageCount, pageLength, registry]);
|
|
2810
3017
|
if (pageLength === 0) return null;
|
|
2811
|
-
|
|
2812
|
-
|
|
2813
|
-
|
|
2814
|
-
|
|
2815
|
-
|
|
2816
|
-
|
|
3018
|
+
|
|
3019
|
+
// Slider layout parity rules copied from Web Preview:
|
|
3020
|
+
// 1) leadingPeek = start?0 : end?pagePeek*2 : pagePeek
|
|
3021
|
+
// 2) trailingPeek = end?0 : start?pagePeek*2 : pagePeek
|
|
3022
|
+
// 3) pagePrimary = viewportPrimary - leadingPeek - trailingPeek (clamped >= 1)
|
|
3023
|
+
// 4) track offset = activeIndex * (pagePrimary + pageSpacing)
|
|
3024
|
+
// 5) fit parent mode follows active slide size but is clamped to container width
|
|
3025
|
+
// 6) viewport always clips track content (no root horizontal overflow)
|
|
3026
|
+
const {
|
|
3027
|
+
leading,
|
|
3028
|
+
trailing
|
|
3029
|
+
} = resolvePeekInsets(pageAlignment, pagePeek);
|
|
3030
|
+
const showDots = pageControlEnabled && pageLength > 1;
|
|
3031
|
+
const widthMode = resolveSliderSizeMode(sliderProps, 'width', 'fill');
|
|
3032
|
+
const heightMode = resolveSliderSizeMode(sliderProps, 'height', 'fit');
|
|
3033
|
+
const fixedWidth = toFinite(parseLayoutDimension(sliderProps.width, true));
|
|
3034
|
+
const fixedHeight = toFinite(parseLayoutDimension(sliderProps.height, true));
|
|
3035
|
+
const sliderBackgroundColor = resolveSliderBackgroundColor(sliderProps);
|
|
3036
|
+
const resolved = resolveSliderContainerSize({
|
|
3037
|
+
direction,
|
|
3038
|
+
widthMode,
|
|
3039
|
+
heightMode,
|
|
3040
|
+
availableWidth: availableSize.width,
|
|
3041
|
+
availableHeight: availableSize.height,
|
|
3042
|
+
activeSlideWidth: activeSlideSize.width,
|
|
3043
|
+
activeSlideHeight: activeSlideSize.height,
|
|
3044
|
+
fixedWidth,
|
|
3045
|
+
fixedHeight,
|
|
3046
|
+
leadingPeek: leading,
|
|
3047
|
+
trailingPeek: trailing,
|
|
3048
|
+
showDots
|
|
3049
|
+
});
|
|
3050
|
+
const viewportPrimary = direction === 'horizontal' ? resolved.viewportWidth : resolved.viewportHeight;
|
|
3051
|
+
const pagePrimary = Math.max(1, viewportPrimary - leading - trailing);
|
|
3052
|
+
return /*#__PURE__*/_jsxs(View, {
|
|
3053
|
+
onTouchStart: markInteracting,
|
|
3054
|
+
onTouchEnd: markInteracting,
|
|
3055
|
+
onTouchCancel: markInteracting,
|
|
3056
|
+
onPointerDown: markInteracting,
|
|
3057
|
+
onPointerUp: markInteracting,
|
|
3058
|
+
onLayout: event => {
|
|
3059
|
+
const nextWidth = Math.max(1, Math.round(event.nativeEvent.layout.width));
|
|
3060
|
+
const nextHeight = Math.max(1, Math.round(event.nativeEvent.layout.height));
|
|
3061
|
+
setAvailableSize(prev => prev.width === nextWidth && prev.height === nextHeight ? prev : {
|
|
3062
|
+
width: nextWidth,
|
|
3063
|
+
height: nextHeight
|
|
3064
|
+
});
|
|
2817
3065
|
},
|
|
2818
|
-
|
|
2819
|
-
|
|
2820
|
-
|
|
2821
|
-
|
|
2822
|
-
|
|
2823
|
-
|
|
2824
|
-
|
|
2825
|
-
|
|
2826
|
-
registry.update(id, safePage % pageLength, pageLength);
|
|
2827
|
-
}
|
|
3066
|
+
style: {
|
|
3067
|
+
width: resolved.outerWidth,
|
|
3068
|
+
height: resolved.outerHeight,
|
|
3069
|
+
minWidth: 1,
|
|
3070
|
+
minHeight: 1,
|
|
3071
|
+
backgroundColor: sliderBackgroundColor,
|
|
3072
|
+
borderRadius: 0,
|
|
3073
|
+
overflow: 'visible'
|
|
2828
3074
|
},
|
|
2829
|
-
children:
|
|
2830
|
-
length: pageCount
|
|
2831
|
-
}).map((_, index) => /*#__PURE__*/_jsx(View, {
|
|
3075
|
+
children: [showDots && pageControlPosition === 'top' ? /*#__PURE__*/_jsx(View, {
|
|
2832
3076
|
style: {
|
|
2833
|
-
|
|
3077
|
+
marginBottom: 8,
|
|
3078
|
+
flexDirection: 'row',
|
|
3079
|
+
justifyContent: 'center',
|
|
3080
|
+
alignItems: 'center'
|
|
2834
3081
|
},
|
|
2835
|
-
children:
|
|
2836
|
-
|
|
3082
|
+
children: Array.from({
|
|
3083
|
+
length: pageLength
|
|
3084
|
+
}).map((_, index) => {
|
|
3085
|
+
const isActive = index === currentPage;
|
|
3086
|
+
return /*#__PURE__*/_jsx(View, {
|
|
3087
|
+
style: {
|
|
3088
|
+
width: 6,
|
|
3089
|
+
height: 6,
|
|
3090
|
+
borderRadius: 3,
|
|
3091
|
+
marginHorizontal: 3,
|
|
3092
|
+
backgroundColor: isActive ? parseColor('0xFF111827') : parseColor('0xFFD1D5DB')
|
|
3093
|
+
}
|
|
3094
|
+
}, `slider-dot-top-${index}`);
|
|
3095
|
+
})
|
|
3096
|
+
}) : null, /*#__PURE__*/_jsx(View, {
|
|
3097
|
+
style: {
|
|
3098
|
+
overflow: 'hidden',
|
|
3099
|
+
...(direction === 'horizontal' ? {
|
|
3100
|
+
width: resolved.viewportWidth,
|
|
3101
|
+
height: resolved.viewportHeight,
|
|
3102
|
+
paddingLeft: leading,
|
|
3103
|
+
paddingRight: trailing
|
|
3104
|
+
} : {
|
|
3105
|
+
width: resolved.viewportWidth,
|
|
3106
|
+
height: resolved.viewportHeight,
|
|
3107
|
+
paddingTop: leading,
|
|
3108
|
+
paddingBottom: trailing
|
|
3109
|
+
})
|
|
3110
|
+
},
|
|
3111
|
+
children: /*#__PURE__*/_jsx(PagerView, {
|
|
3112
|
+
ref: pagerRef,
|
|
3113
|
+
style: {
|
|
3114
|
+
width: '100%',
|
|
3115
|
+
height: '100%',
|
|
3116
|
+
minHeight: 1
|
|
3117
|
+
},
|
|
3118
|
+
orientation: direction,
|
|
3119
|
+
pageMargin: Math.max(0, pageSpacing),
|
|
3120
|
+
initialPage: clampSliderPage(currentPage, pageCount),
|
|
3121
|
+
onPageSelected: event => {
|
|
3122
|
+
const page = event.nativeEvent.position;
|
|
3123
|
+
const safePage = clampSliderPage(page, pageCount);
|
|
3124
|
+
setCurrentPage(safePage);
|
|
3125
|
+
markInteracting();
|
|
3126
|
+
if (registry && id) {
|
|
3127
|
+
registry.update(id, safePage % pageLength, pageLength);
|
|
3128
|
+
}
|
|
3129
|
+
},
|
|
3130
|
+
children: Array.from({
|
|
3131
|
+
length: pageCount
|
|
3132
|
+
}).map((_, index) => /*#__PURE__*/_jsx(View, {
|
|
3133
|
+
onLayout: index === currentPage ? event => {
|
|
3134
|
+
const nextWidth = Math.max(1, Math.round(event.nativeEvent.layout.width));
|
|
3135
|
+
const nextHeight = Math.max(1, Math.round(event.nativeEvent.layout.height));
|
|
3136
|
+
setActiveSlideSize(prev => prev.width === nextWidth && prev.height === nextHeight ? prev : {
|
|
3137
|
+
width: nextWidth,
|
|
3138
|
+
height: nextHeight
|
|
3139
|
+
});
|
|
3140
|
+
} : undefined,
|
|
3141
|
+
style: {
|
|
3142
|
+
alignSelf: 'flex-start',
|
|
3143
|
+
overflow: 'hidden',
|
|
3144
|
+
...resolveSlidePageSize({
|
|
3145
|
+
slideProps: slidePropsList[index] ?? {},
|
|
3146
|
+
direction,
|
|
3147
|
+
pagePrimary,
|
|
3148
|
+
crossSize: '100%'
|
|
3149
|
+
})
|
|
3150
|
+
},
|
|
3151
|
+
children: children[index % pageLength]
|
|
3152
|
+
}, `slider-page-${index}`))
|
|
3153
|
+
})
|
|
3154
|
+
}), showDots && pageControlPosition === 'bottom' ? /*#__PURE__*/_jsx(View, {
|
|
3155
|
+
style: {
|
|
3156
|
+
marginTop: 8,
|
|
3157
|
+
flexDirection: 'row',
|
|
3158
|
+
justifyContent: 'center',
|
|
3159
|
+
alignItems: 'center'
|
|
3160
|
+
},
|
|
3161
|
+
children: Array.from({
|
|
3162
|
+
length: pageLength
|
|
3163
|
+
}).map((_, index) => {
|
|
3164
|
+
const isActive = index === currentPage;
|
|
3165
|
+
return /*#__PURE__*/_jsx(View, {
|
|
3166
|
+
style: {
|
|
3167
|
+
width: 6,
|
|
3168
|
+
height: 6,
|
|
3169
|
+
borderRadius: 3,
|
|
3170
|
+
marginHorizontal: 3,
|
|
3171
|
+
backgroundColor: isActive ? parseColor('0xFF111827') : parseColor('0xFFD1D5DB')
|
|
3172
|
+
}
|
|
3173
|
+
}, `slider-dot-bottom-${index}`);
|
|
3174
|
+
})
|
|
3175
|
+
}) : null]
|
|
2837
3176
|
});
|
|
2838
3177
|
}
|
|
2839
3178
|
function clampSliderPage(page, pageCount) {
|
|
@@ -2843,11 +3182,6 @@ function clampSliderPage(page, pageCount) {
|
|
|
2843
3182
|
if (page >= pageCount) return pageCount - 1;
|
|
2844
3183
|
return Math.floor(page);
|
|
2845
3184
|
}
|
|
2846
|
-
function normalizeLoopIndex(index, size) {
|
|
2847
|
-
if (size <= 0 || !Number.isFinite(index)) return 0;
|
|
2848
|
-
const normalized = Math.floor(index) % size;
|
|
2849
|
-
return normalized < 0 ? normalized + size : normalized;
|
|
2850
|
-
}
|
|
2851
3185
|
function PageViewIndicator({
|
|
2852
3186
|
linkedTo,
|
|
2853
3187
|
activeColor,
|