flowboard-react 0.6.1 → 0.6.3
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 +168 -52
- package/app.plugin.js +341 -0
- package/bin/setup.js +427 -0
- package/lib/module/Flowboard.js +1 -2
- package/lib/module/Flowboard.js.map +1 -1
- package/lib/module/FlowboardProvider.js +1 -1
- package/lib/module/FlowboardProvider.js.map +1 -1
- package/lib/module/components/FlowboardFlow.js +31 -9
- package/lib/module/components/FlowboardFlow.js.map +1 -1
- package/lib/module/components/FlowboardRenderer.js +419 -85
- package/lib/module/components/FlowboardRenderer.js.map +1 -1
- package/lib/module/core/clientContext.js +10 -29
- package/lib/module/core/clientContext.js.map +1 -1
- package/lib/module/core/fontAwesome.js +2 -9
- package/lib/module/core/fontAwesome.js.map +1 -1
- package/lib/module/core/onboardingRepository.js +13 -13
- package/lib/module/core/onboardingRepository.js.map +1 -1
- package/lib/module/native/asyncStorage.js +55 -0
- package/lib/module/native/asyncStorage.js.map +1 -0
- package/lib/module/native/deviceInfo.js +46 -0
- package/lib/module/native/deviceInfo.js.map +1 -0
- package/lib/module/native/inAppReview.js +17 -0
- package/lib/module/native/inAppReview.js.map +1 -0
- package/lib/module/native/linearGradient.js +29 -0
- package/lib/module/native/linearGradient.js.map +1 -0
- package/lib/module/native/lottie.js +20 -0
- package/lib/module/native/lottie.js.map +1 -0
- package/lib/module/native/maskedView.js +26 -0
- package/lib/module/native/maskedView.js.map +1 -0
- package/lib/module/native/pagerView.js +79 -0
- package/lib/module/native/pagerView.js.map +1 -0
- package/lib/module/native/permissions.js +30 -0
- package/lib/module/native/permissions.js.map +1 -0
- package/lib/module/native/runtime.js +81 -0
- package/lib/module/native/runtime.js.map +1 -0
- package/lib/module/native/safeAreaContext.js +43 -0
- package/lib/module/native/safeAreaContext.js.map +1 -0
- package/lib/module/native/svg.js +83 -0
- package/lib/module/native/svg.js.map +1 -0
- package/lib/module/native/vectorIcons.js +41 -0
- package/lib/module/native/vectorIcons.js.map +1 -0
- package/lib/module/utils/flowboardUtils.js +4 -1
- package/lib/module/utils/flowboardUtils.js.map +1 -1
- 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/clientContext.d.ts.map +1 -1
- package/lib/typescript/src/core/fontAwesome.d.ts +1 -1
- package/lib/typescript/src/core/fontAwesome.d.ts.map +1 -1
- package/lib/typescript/src/core/onboardingRepository.d.ts.map +1 -1
- package/lib/typescript/src/native/asyncStorage.d.ts +16 -0
- package/lib/typescript/src/native/asyncStorage.d.ts.map +1 -0
- package/lib/typescript/src/native/deviceInfo.d.ts +11 -0
- package/lib/typescript/src/native/deviceInfo.d.ts.map +1 -0
- package/lib/typescript/src/native/inAppReview.d.ts +4 -0
- package/lib/typescript/src/native/inAppReview.d.ts.map +1 -0
- package/lib/typescript/src/native/linearGradient.d.ts +3 -0
- package/lib/typescript/src/native/linearGradient.d.ts.map +1 -0
- package/lib/typescript/src/native/lottie.d.ts +3 -0
- package/lib/typescript/src/native/lottie.d.ts.map +1 -0
- package/lib/typescript/src/native/maskedView.d.ts +3 -0
- package/lib/typescript/src/native/maskedView.d.ts.map +1 -0
- package/lib/typescript/src/native/pagerView.d.ts +14 -0
- package/lib/typescript/src/native/pagerView.d.ts.map +1 -0
- package/lib/typescript/src/native/permissions.d.ts +9 -0
- package/lib/typescript/src/native/permissions.d.ts.map +1 -0
- package/lib/typescript/src/native/runtime.d.ts +13 -0
- package/lib/typescript/src/native/runtime.d.ts.map +1 -0
- package/lib/typescript/src/native/safeAreaContext.d.ts +15 -0
- package/lib/typescript/src/native/safeAreaContext.d.ts.map +1 -0
- package/lib/typescript/src/native/svg.d.ts +8 -0
- package/lib/typescript/src/native/svg.d.ts.map +1 -0
- package/lib/typescript/src/native/vectorIcons.d.ts +4 -0
- package/lib/typescript/src/native/vectorIcons.d.ts.map +1 -0
- package/lib/typescript/src/utils/flowboardUtils.d.ts.map +1 -1
- package/package.json +24 -19
- package/src/Flowboard.ts +1 -2
- package/src/FlowboardProvider.tsx +1 -1
- package/src/components/FlowboardFlow.tsx +47 -9
- package/src/components/FlowboardRenderer.tsx +689 -113
- package/src/core/clientContext.ts +10 -32
- package/src/core/fontAwesome.ts +2 -9
- package/src/core/onboardingRepository.ts +18 -13
- package/src/native/asyncStorage.ts +99 -0
- package/src/native/deviceInfo.ts +88 -0
- package/src/native/inAppReview.ts +34 -0
- package/src/native/linearGradient.tsx +24 -0
- package/src/native/lottie.tsx +17 -0
- package/src/native/maskedView.tsx +19 -0
- package/src/native/pagerView.tsx +95 -0
- package/src/native/permissions.ts +59 -0
- package/src/native/runtime.ts +110 -0
- package/src/native/safeAreaContext.tsx +44 -0
- package/src/native/svg.tsx +82 -0
- package/src/native/vectorIcons.tsx +50 -0
- package/src/utils/flowboardUtils.ts +9 -1
|
@@ -14,21 +14,16 @@ import {
|
|
|
14
14
|
type TextStyle,
|
|
15
15
|
type ViewStyle,
|
|
16
16
|
} from 'react-native';
|
|
17
|
-
import { useSafeAreaInsets } from 'react-native-safe-area-context';
|
|
18
|
-
import MaskedView from '@react-native-masked-view/masked-view';
|
|
19
|
-
import LinearGradient from 'react-native-linear-gradient';
|
|
20
|
-
import LottieView from 'lottie-react-native';
|
|
21
17
|
import MaskInput from 'react-native-mask-input';
|
|
22
|
-
import
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
Line,
|
|
26
|
-
Polygon,
|
|
27
|
-
Text as SvgText,
|
|
28
|
-
} from 'react-native-svg';
|
|
18
|
+
import LinearGradient from '../native/linearGradient';
|
|
19
|
+
import LottieView from '../native/lottie';
|
|
20
|
+
import MaskedView from '../native/maskedView';
|
|
29
21
|
import PagerView, {
|
|
22
|
+
type PagerViewHandle,
|
|
30
23
|
type PagerViewOnPageSelectedEvent,
|
|
31
|
-
} from '
|
|
24
|
+
} from '../native/pagerView';
|
|
25
|
+
import { useSafeAreaInsets } from '../native/safeAreaContext';
|
|
26
|
+
import Svg, { Circle, G, Line, Polygon, SvgText } from '../native/svg';
|
|
32
27
|
import {
|
|
33
28
|
insetsToStyle,
|
|
34
29
|
insetsToMarginStyle,
|
|
@@ -563,6 +558,7 @@ function isFillDimensionValue(value: unknown) {
|
|
|
563
558
|
const normalized = value.trim().toLowerCase();
|
|
564
559
|
return (
|
|
565
560
|
normalized === 'infinity' ||
|
|
561
|
+
normalized === 'infinite' ||
|
|
566
562
|
normalized === 'double.infinity' ||
|
|
567
563
|
normalized === '+infinity' ||
|
|
568
564
|
normalized === '100%'
|
|
@@ -575,33 +571,63 @@ function isFixedDimensionValue(value: unknown) {
|
|
|
575
571
|
return Number.isFinite(parsed);
|
|
576
572
|
}
|
|
577
573
|
|
|
574
|
+
function normalizeStackSizeMode(
|
|
575
|
+
value: unknown
|
|
576
|
+
): 'fill' | 'fit' | 'fixed' | undefined {
|
|
577
|
+
if (typeof value !== 'string') return undefined;
|
|
578
|
+
const normalized = value.trim().toLowerCase();
|
|
579
|
+
if (normalized === 'fit' || normalized === 'fixed') return normalized;
|
|
580
|
+
if (
|
|
581
|
+
normalized === 'fill' ||
|
|
582
|
+
normalized === 'infinity' ||
|
|
583
|
+
normalized === 'infinite' ||
|
|
584
|
+
normalized === 'double.infinity' ||
|
|
585
|
+
normalized === '+infinity'
|
|
586
|
+
) {
|
|
587
|
+
return 'fill';
|
|
588
|
+
}
|
|
589
|
+
return undefined;
|
|
590
|
+
}
|
|
591
|
+
|
|
592
|
+
function wantsFillWidth(props: Record<string, any>) {
|
|
593
|
+
return (
|
|
594
|
+
normalizeStackSizeMode(props.size?.width) === 'fill' ||
|
|
595
|
+
isFillDimensionValue(props.width)
|
|
596
|
+
);
|
|
597
|
+
}
|
|
598
|
+
|
|
599
|
+
function wantsFillHeight(props: Record<string, any>) {
|
|
600
|
+
return (
|
|
601
|
+
normalizeStackSizeMode(props.size?.height) === 'fill' ||
|
|
602
|
+
isFillDimensionValue(props.height)
|
|
603
|
+
);
|
|
604
|
+
}
|
|
605
|
+
|
|
578
606
|
function normalizeStackSize(
|
|
579
607
|
props: Record<string, any>,
|
|
580
608
|
axis: 'vertical' | 'horizontal' | 'overlay'
|
|
581
609
|
) {
|
|
582
|
-
const widthMode =
|
|
583
|
-
const heightMode =
|
|
584
|
-
|
|
585
|
-
const width =
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
? 'fit'
|
|
604
|
-
: 'fill';
|
|
610
|
+
const widthMode = normalizeStackSizeMode(props.size?.width);
|
|
611
|
+
const heightMode = normalizeStackSizeMode(props.size?.height);
|
|
612
|
+
|
|
613
|
+
const width = widthMode
|
|
614
|
+
? widthMode
|
|
615
|
+
: props.fit === 'expand' || isFillDimensionValue(props.width)
|
|
616
|
+
? 'fill'
|
|
617
|
+
: isFixedDimensionValue(props.width)
|
|
618
|
+
? 'fixed'
|
|
619
|
+
: axis === 'horizontal'
|
|
620
|
+
? 'fit'
|
|
621
|
+
: 'fill';
|
|
622
|
+
const height = heightMode
|
|
623
|
+
? heightMode
|
|
624
|
+
: props.fit === 'expand' || isFillDimensionValue(props.height)
|
|
625
|
+
? 'fill'
|
|
626
|
+
: isFixedDimensionValue(props.height)
|
|
627
|
+
? 'fixed'
|
|
628
|
+
: axis === 'vertical'
|
|
629
|
+
? 'fit'
|
|
630
|
+
: 'fill';
|
|
605
631
|
|
|
606
632
|
return { width, height };
|
|
607
633
|
}
|
|
@@ -687,8 +713,6 @@ function mergeStackProps(
|
|
|
687
713
|
const fallbackAxisSource =
|
|
688
714
|
axisSource === childProps ? parentProps : childProps;
|
|
689
715
|
|
|
690
|
-
const parentPadding = spacingToInsets(parentProps.layout?.padding);
|
|
691
|
-
const childPadding = spacingToInsets(childProps.layout?.padding);
|
|
692
716
|
const parentMargin = spacingToInsets(parentProps.layout?.margin);
|
|
693
717
|
const childMargin = spacingToInsets(childProps.layout?.margin);
|
|
694
718
|
|
|
@@ -705,12 +729,6 @@ function mergeStackProps(
|
|
|
705
729
|
height: childProps.size?.height ?? parentProps.size?.height ?? 'fit',
|
|
706
730
|
},
|
|
707
731
|
layout: {
|
|
708
|
-
padding: insetsToSpacing({
|
|
709
|
-
top: parentPadding.top + childPadding.top,
|
|
710
|
-
right: parentPadding.right + childPadding.right,
|
|
711
|
-
bottom: parentPadding.bottom + childPadding.bottom,
|
|
712
|
-
left: parentPadding.left + childPadding.left,
|
|
713
|
-
}),
|
|
714
732
|
margin: insetsToSpacing({
|
|
715
733
|
top: parentMargin.top + childMargin.top,
|
|
716
734
|
right: parentMargin.right + childMargin.right,
|
|
@@ -783,9 +801,6 @@ function normalizeStackLikeNode(
|
|
|
783
801
|
childSpacing: Number(props.childSpacing ?? props.gap ?? props.spacing ?? 0),
|
|
784
802
|
size,
|
|
785
803
|
layout: {
|
|
786
|
-
padding: normalizeStackSpacingConfig(
|
|
787
|
-
props.layout?.padding ?? props.padding
|
|
788
|
-
),
|
|
789
804
|
margin: normalizeStackSpacingConfig(props.layout?.margin ?? props.margin),
|
|
790
805
|
},
|
|
791
806
|
appearance: {
|
|
@@ -881,11 +896,12 @@ function resolveStackDimension(
|
|
|
881
896
|
) {
|
|
882
897
|
const mode = props.size?.[key];
|
|
883
898
|
const legacy = normalizeDimension(parseLayoutDimension(props[key]));
|
|
899
|
+
const fixedPx = parseLayoutDimension(props.size?.[`${key}Px`], true);
|
|
884
900
|
const isBounded =
|
|
885
901
|
key === 'width' ? axisBounds.widthBounded : axisBounds.heightBounded;
|
|
886
902
|
if (mode === 'fill') return isBounded ? '100%' : legacy;
|
|
887
903
|
if (mode === 'fit') return legacy;
|
|
888
|
-
if (mode === 'fixed') return legacy;
|
|
904
|
+
if (mode === 'fixed') return legacy ?? fixedPx;
|
|
889
905
|
if (legacy !== undefined) return legacy;
|
|
890
906
|
if (key === 'width' && axis !== 'horizontal' && axisBounds.widthBounded)
|
|
891
907
|
return '100%';
|
|
@@ -1485,19 +1501,38 @@ function buildWidget(
|
|
|
1485
1501
|
break;
|
|
1486
1502
|
}
|
|
1487
1503
|
case 'slider': {
|
|
1504
|
+
const direction =
|
|
1505
|
+
props.direction === 'vertical' ? 'vertical' : 'horizontal';
|
|
1506
|
+
const interaction = props.interaction ?? {};
|
|
1507
|
+
const pageControl = props.pageControl ?? {};
|
|
1508
|
+
const sliderChildren = (childrenJson ?? []).filter(Boolean);
|
|
1488
1509
|
node = (
|
|
1489
1510
|
<SliderWidget
|
|
1490
1511
|
id={json._internalId}
|
|
1491
|
-
|
|
1492
|
-
|
|
1493
|
-
|
|
1494
|
-
|
|
1495
|
-
|
|
1496
|
-
|
|
1497
|
-
|
|
1512
|
+
sliderProps={props}
|
|
1513
|
+
slidePropsList={sliderChildren.map(
|
|
1514
|
+
(child) => child?.properties ?? {}
|
|
1515
|
+
)}
|
|
1516
|
+
direction={direction}
|
|
1517
|
+
pageAlignment={
|
|
1518
|
+
props.pageAlignment === 'start' || props.pageAlignment === 'end'
|
|
1519
|
+
? props.pageAlignment
|
|
1520
|
+
: 'center'
|
|
1521
|
+
}
|
|
1522
|
+
pageSpacing={Number(props.pageSpacing ?? 16)}
|
|
1523
|
+
pagePeek={Number(props.pagePeek ?? 16)}
|
|
1524
|
+
loop={interaction.loop === true}
|
|
1525
|
+
autoAdvance={interaction.autoAdvance === true}
|
|
1526
|
+
autoAdvanceIntervalMs={Number(
|
|
1527
|
+
interaction.autoAdvanceIntervalMs ?? 4000
|
|
1528
|
+
)}
|
|
1529
|
+
pageControlEnabled={pageControl.enabled !== false}
|
|
1530
|
+
pageControlPosition={
|
|
1531
|
+
pageControl.position === 'top' ? 'top' : 'bottom'
|
|
1532
|
+
}
|
|
1498
1533
|
>
|
|
1499
|
-
{
|
|
1500
|
-
<View key={`slider-${index}`}
|
|
1534
|
+
{sliderChildren.map((child, index) => (
|
|
1535
|
+
<View key={`slider-${index}`}>
|
|
1501
1536
|
{buildWidget(child, { ...params })}
|
|
1502
1537
|
</View>
|
|
1503
1538
|
))}
|
|
@@ -1505,6 +1540,19 @@ function buildWidget(
|
|
|
1505
1540
|
);
|
|
1506
1541
|
break;
|
|
1507
1542
|
}
|
|
1543
|
+
case 'slide': {
|
|
1544
|
+
node = buildWidget(
|
|
1545
|
+
{
|
|
1546
|
+
...json,
|
|
1547
|
+
type: 'stack',
|
|
1548
|
+
properties: {
|
|
1549
|
+
...(props || {}),
|
|
1550
|
+
},
|
|
1551
|
+
},
|
|
1552
|
+
{ ...params }
|
|
1553
|
+
);
|
|
1554
|
+
break;
|
|
1555
|
+
}
|
|
1508
1556
|
case 'pageview_indicator': {
|
|
1509
1557
|
node = (
|
|
1510
1558
|
<PageViewIndicator
|
|
@@ -1558,9 +1606,6 @@ function buildWidget(
|
|
|
1558
1606
|
const applySpacing = !STACK_SPACE_DISTRIBUTIONS.has(
|
|
1559
1607
|
String(props.distribution ?? 'start')
|
|
1560
1608
|
);
|
|
1561
|
-
const paddingInsets = stackSpacingToInsets(
|
|
1562
|
-
props.layout?.padding ?? props.padding
|
|
1563
|
-
);
|
|
1564
1609
|
const width = resolveStackDimension(props, axis, 'width', pageAxisBounds);
|
|
1565
1610
|
const height = resolveStackDimension(
|
|
1566
1611
|
props,
|
|
@@ -1626,12 +1671,22 @@ function buildWidget(
|
|
|
1626
1671
|
overflow: borderRadius > 0 ? 'hidden' : undefined,
|
|
1627
1672
|
borderWidth: borderColor ? borderWidth : undefined,
|
|
1628
1673
|
borderColor: borderColor ?? undefined,
|
|
1629
|
-
...insetsToStyle(paddingInsets),
|
|
1630
1674
|
...shadowStyle,
|
|
1631
1675
|
};
|
|
1676
|
+
const stackContentSizeStyle = {
|
|
1677
|
+
width: width !== undefined ? '100%' : undefined,
|
|
1678
|
+
height: height !== undefined ? '100%' : undefined,
|
|
1679
|
+
};
|
|
1632
1680
|
|
|
1633
1681
|
const content = isOverlay ? (
|
|
1634
|
-
<View
|
|
1682
|
+
<View
|
|
1683
|
+
style={{
|
|
1684
|
+
position: 'relative',
|
|
1685
|
+
minHeight: 0,
|
|
1686
|
+
minWidth: 0,
|
|
1687
|
+
...stackContentSizeStyle,
|
|
1688
|
+
}}
|
|
1689
|
+
>
|
|
1635
1690
|
{(childrenJson ?? []).map((child, index) => {
|
|
1636
1691
|
const alignment = parseOverlayGridAlignment(
|
|
1637
1692
|
props.overlayAlignment ?? props.alignment
|
|
@@ -1667,6 +1722,7 @@ function buildWidget(
|
|
|
1667
1722
|
alignItems: parseCrossAlignment(props.alignment),
|
|
1668
1723
|
minHeight: 0,
|
|
1669
1724
|
minWidth: 0,
|
|
1725
|
+
...stackContentSizeStyle,
|
|
1670
1726
|
}}
|
|
1671
1727
|
>
|
|
1672
1728
|
{(childrenJson ?? []).map((child, index) => (
|
|
@@ -1775,6 +1831,27 @@ function buildWidget(
|
|
|
1775
1831
|
);
|
|
1776
1832
|
}
|
|
1777
1833
|
|
|
1834
|
+
const shouldStretchCrossAxis =
|
|
1835
|
+
(params.parentFlexAxis === 'vertical' &&
|
|
1836
|
+
(type === 'selection_list' ||
|
|
1837
|
+
type === 'wheel_picker' ||
|
|
1838
|
+
wantsFillWidth(props))) ||
|
|
1839
|
+
(params.parentFlexAxis === 'horizontal' && wantsFillHeight(props));
|
|
1840
|
+
|
|
1841
|
+
if (shouldStretchCrossAxis) {
|
|
1842
|
+
node = (
|
|
1843
|
+
<View
|
|
1844
|
+
style={{
|
|
1845
|
+
alignSelf: 'stretch',
|
|
1846
|
+
minWidth: 0,
|
|
1847
|
+
minHeight: 0,
|
|
1848
|
+
}}
|
|
1849
|
+
>
|
|
1850
|
+
{node}
|
|
1851
|
+
</View>
|
|
1852
|
+
);
|
|
1853
|
+
}
|
|
1854
|
+
|
|
1778
1855
|
if (params.allowFlexExpansion) {
|
|
1779
1856
|
let shouldExpand = false;
|
|
1780
1857
|
const parentAxis = params.parentFlexAxis ?? 'vertical';
|
|
@@ -2188,6 +2265,7 @@ function parseLayoutDimension(
|
|
|
2188
2265
|
|
|
2189
2266
|
if (
|
|
2190
2267
|
lowered === 'double.infinity' ||
|
|
2268
|
+
lowered === 'infinite' ||
|
|
2191
2269
|
lowered === 'infinity' ||
|
|
2192
2270
|
lowered === '+infinity'
|
|
2193
2271
|
) {
|
|
@@ -2626,6 +2704,16 @@ function SelectionList({
|
|
|
2626
2704
|
|
|
2627
2705
|
const layout = properties.layout ?? 'column';
|
|
2628
2706
|
const spacing = Number(properties.spacing ?? 8);
|
|
2707
|
+
const shouldOptionFillWidth = (option: Record<string, any>) => {
|
|
2708
|
+
if (layout === 'grid') return true;
|
|
2709
|
+
const child =
|
|
2710
|
+
option.child && typeof option.child === 'object'
|
|
2711
|
+
? normalizeStackLikeNode(option.child)
|
|
2712
|
+
: option.child;
|
|
2713
|
+
const childProps =
|
|
2714
|
+
child && typeof child === 'object' ? child.properties ?? {} : {};
|
|
2715
|
+
return wantsFillWidth(childProps);
|
|
2716
|
+
};
|
|
2629
2717
|
|
|
2630
2718
|
const renderItem = (option: Record<string, any>, index: number) => {
|
|
2631
2719
|
const value = String(option.value ?? '');
|
|
@@ -2638,13 +2726,15 @@ function SelectionList({
|
|
|
2638
2726
|
const borderColor = parseColor(styleProps.borderColor);
|
|
2639
2727
|
const borderRadius = Number(styleProps.borderRadius ?? 8);
|
|
2640
2728
|
const borderWidth = Number(styleProps.borderWidth ?? 1);
|
|
2729
|
+
const fillWidth = shouldOptionFillWidth(option);
|
|
2641
2730
|
|
|
2642
2731
|
return (
|
|
2643
2732
|
<Pressable
|
|
2644
2733
|
key={`option-${index}`}
|
|
2645
2734
|
onPress={() => toggleSelection(value)}
|
|
2646
2735
|
style={{
|
|
2647
|
-
width: '100%',
|
|
2736
|
+
width: fillWidth ? '100%' : undefined,
|
|
2737
|
+
alignSelf: fillWidth ? 'stretch' : 'flex-start',
|
|
2648
2738
|
backgroundColor,
|
|
2649
2739
|
borderColor,
|
|
2650
2740
|
borderWidth,
|
|
@@ -2699,7 +2789,11 @@ function SelectionList({
|
|
|
2699
2789
|
{options.map((option, index) => (
|
|
2700
2790
|
<View
|
|
2701
2791
|
key={`column-option-${index}`}
|
|
2702
|
-
style={{
|
|
2792
|
+
style={{
|
|
2793
|
+
width: shouldOptionFillWidth(option) ? '100%' : undefined,
|
|
2794
|
+
alignSelf: shouldOptionFillWidth(option) ? 'stretch' : 'flex-start',
|
|
2795
|
+
marginBottom: spacing,
|
|
2796
|
+
}}
|
|
2703
2797
|
>
|
|
2704
2798
|
{renderItem(option, index)}
|
|
2705
2799
|
</View>
|
|
@@ -3529,40 +3623,358 @@ function FakeProgressBar({
|
|
|
3529
3623
|
|
|
3530
3624
|
const AnimatedCircle = Animated.createAnimatedComponent(Circle);
|
|
3531
3625
|
|
|
3626
|
+
type SliderSizeMode = 'fill' | 'fit' | 'fixed';
|
|
3627
|
+
|
|
3628
|
+
export function resolvePeekInsets(
|
|
3629
|
+
pageAlignment: 'start' | 'center' | 'end',
|
|
3630
|
+
pagePeek: number
|
|
3631
|
+
): { leading: number; trailing: number } {
|
|
3632
|
+
const leading =
|
|
3633
|
+
pageAlignment === 'start'
|
|
3634
|
+
? 0
|
|
3635
|
+
: pageAlignment === 'end'
|
|
3636
|
+
? pagePeek * 2
|
|
3637
|
+
: pagePeek;
|
|
3638
|
+
const trailing =
|
|
3639
|
+
pageAlignment === 'end'
|
|
3640
|
+
? 0
|
|
3641
|
+
: pageAlignment === 'start'
|
|
3642
|
+
? pagePeek * 2
|
|
3643
|
+
: pagePeek;
|
|
3644
|
+
return { leading, trailing };
|
|
3645
|
+
}
|
|
3646
|
+
|
|
3647
|
+
function resolveSliderSizeMode(
|
|
3648
|
+
sliderProps: Record<string, any>,
|
|
3649
|
+
key: 'width' | 'height',
|
|
3650
|
+
fallback: SliderSizeMode
|
|
3651
|
+
): SliderSizeMode {
|
|
3652
|
+
const raw = String(sliderProps?.size?.[key] ?? '').toLowerCase();
|
|
3653
|
+
if (raw === 'fill' || raw === 'fit' || raw === 'fixed') return raw;
|
|
3654
|
+
return sliderProps?.[key] !== undefined ? 'fixed' : fallback;
|
|
3655
|
+
}
|
|
3656
|
+
|
|
3657
|
+
function resolveSlideSizeMode(
|
|
3658
|
+
slideProps: Record<string, any>,
|
|
3659
|
+
key: 'width' | 'height'
|
|
3660
|
+
): SliderSizeMode {
|
|
3661
|
+
const raw = String(slideProps?.size?.[key] ?? '').toLowerCase();
|
|
3662
|
+
if (raw === 'fill' || raw === 'fit' || raw === 'fixed') return raw;
|
|
3663
|
+
return key === 'width' ? 'fill' : 'fit';
|
|
3664
|
+
}
|
|
3665
|
+
|
|
3666
|
+
function toFinite(value: unknown): number | null {
|
|
3667
|
+
if (typeof value === 'number' && Number.isFinite(value)) return value;
|
|
3668
|
+
if (typeof value === 'string') {
|
|
3669
|
+
const parsed = Number(value);
|
|
3670
|
+
if (Number.isFinite(parsed)) return parsed;
|
|
3671
|
+
}
|
|
3672
|
+
return null;
|
|
3673
|
+
}
|
|
3674
|
+
|
|
3675
|
+
export function resolveSliderContainerSize(args: {
|
|
3676
|
+
direction: 'horizontal' | 'vertical';
|
|
3677
|
+
widthMode: SliderSizeMode;
|
|
3678
|
+
heightMode: SliderSizeMode;
|
|
3679
|
+
availableWidth: number;
|
|
3680
|
+
availableHeight: number;
|
|
3681
|
+
activeSlideWidth: number;
|
|
3682
|
+
activeSlideHeight: number;
|
|
3683
|
+
fixedWidth: number | null;
|
|
3684
|
+
fixedHeight: number | null;
|
|
3685
|
+
leadingPeek: number;
|
|
3686
|
+
trailingPeek: number;
|
|
3687
|
+
showDots: boolean;
|
|
3688
|
+
}): {
|
|
3689
|
+
outerWidth: number | string;
|
|
3690
|
+
outerHeight: number | string;
|
|
3691
|
+
viewportWidth: number;
|
|
3692
|
+
viewportHeight: number;
|
|
3693
|
+
} {
|
|
3694
|
+
const dotsInset = args.showDots ? 8 + 6 + 8 : 0;
|
|
3695
|
+
const fitPrimary =
|
|
3696
|
+
args.direction === 'horizontal'
|
|
3697
|
+
? args.activeSlideWidth
|
|
3698
|
+
: args.activeSlideHeight;
|
|
3699
|
+
const fitViewportPrimary = Math.max(
|
|
3700
|
+
1,
|
|
3701
|
+
fitPrimary + args.leadingPeek + args.trailingPeek
|
|
3702
|
+
);
|
|
3703
|
+
|
|
3704
|
+
const viewportWidth =
|
|
3705
|
+
args.direction === 'horizontal'
|
|
3706
|
+
? fitViewportPrimary
|
|
3707
|
+
: Math.max(1, args.activeSlideWidth);
|
|
3708
|
+
const viewportHeight =
|
|
3709
|
+
args.direction === 'vertical'
|
|
3710
|
+
? fitViewportPrimary
|
|
3711
|
+
: Math.max(1, args.activeSlideHeight);
|
|
3712
|
+
|
|
3713
|
+
const fitOuterWidth = Math.min(
|
|
3714
|
+
Math.max(1, viewportWidth),
|
|
3715
|
+
Math.max(1, args.availableWidth || viewportWidth)
|
|
3716
|
+
);
|
|
3717
|
+
const fitOuterHeight = Math.max(1, viewportHeight + dotsInset);
|
|
3718
|
+
|
|
3719
|
+
const outerWidth =
|
|
3720
|
+
args.widthMode === 'fill'
|
|
3721
|
+
? '100%'
|
|
3722
|
+
: args.widthMode === 'fixed'
|
|
3723
|
+
? Math.max(1, args.fixedWidth ?? fitOuterWidth)
|
|
3724
|
+
: fitOuterWidth;
|
|
3725
|
+
const outerHeight =
|
|
3726
|
+
args.heightMode === 'fill'
|
|
3727
|
+
? '100%'
|
|
3728
|
+
: args.heightMode === 'fixed'
|
|
3729
|
+
? Math.max(1, args.fixedHeight ?? fitOuterHeight)
|
|
3730
|
+
: fitOuterHeight;
|
|
3731
|
+
|
|
3732
|
+
return {
|
|
3733
|
+
outerWidth,
|
|
3734
|
+
outerHeight,
|
|
3735
|
+
viewportWidth:
|
|
3736
|
+
args.widthMode === 'fit'
|
|
3737
|
+
? fitOuterWidth
|
|
3738
|
+
: Math.max(1, args.availableWidth),
|
|
3739
|
+
viewportHeight:
|
|
3740
|
+
args.heightMode === 'fit'
|
|
3741
|
+
? Math.max(1, fitOuterHeight - dotsInset)
|
|
3742
|
+
: Math.max(1, args.availableHeight || fitOuterHeight - dotsInset),
|
|
3743
|
+
};
|
|
3744
|
+
}
|
|
3745
|
+
|
|
3746
|
+
export function resolveSlidePageSize(args: {
|
|
3747
|
+
slideProps: Record<string, any>;
|
|
3748
|
+
direction: 'horizontal' | 'vertical';
|
|
3749
|
+
pagePrimary: number;
|
|
3750
|
+
crossSize: number | string;
|
|
3751
|
+
}): { width: number | string; height: number | string } {
|
|
3752
|
+
const widthMode = resolveSlideSizeMode(args.slideProps, 'width');
|
|
3753
|
+
const heightMode = resolveSlideSizeMode(args.slideProps, 'height');
|
|
3754
|
+
const fixedWidth = toFinite(
|
|
3755
|
+
parseLayoutDimension(
|
|
3756
|
+
args.slideProps.width ?? args.slideProps.size?.widthPx,
|
|
3757
|
+
true
|
|
3758
|
+
)
|
|
3759
|
+
);
|
|
3760
|
+
const fixedHeight = toFinite(
|
|
3761
|
+
parseLayoutDimension(
|
|
3762
|
+
args.slideProps.height ?? args.slideProps.size?.heightPx,
|
|
3763
|
+
true
|
|
3764
|
+
)
|
|
3765
|
+
);
|
|
3766
|
+
const width =
|
|
3767
|
+
widthMode === 'fixed'
|
|
3768
|
+
? Math.max(1, fixedWidth ?? args.pagePrimary)
|
|
3769
|
+
: widthMode === 'fit'
|
|
3770
|
+
? undefined
|
|
3771
|
+
: args.direction === 'horizontal'
|
|
3772
|
+
? args.pagePrimary
|
|
3773
|
+
: args.crossSize;
|
|
3774
|
+
const height =
|
|
3775
|
+
heightMode === 'fixed'
|
|
3776
|
+
? Math.max(1, fixedHeight ?? args.pagePrimary)
|
|
3777
|
+
: heightMode === 'fit'
|
|
3778
|
+
? undefined
|
|
3779
|
+
: args.direction === 'vertical'
|
|
3780
|
+
? args.pagePrimary
|
|
3781
|
+
: args.crossSize;
|
|
3782
|
+
return {
|
|
3783
|
+
width: width ?? 'auto',
|
|
3784
|
+
height: height ?? 'auto',
|
|
3785
|
+
};
|
|
3786
|
+
}
|
|
3787
|
+
|
|
3788
|
+
function resolveSliderBackgroundColor(
|
|
3789
|
+
sliderProps: Record<string, any>
|
|
3790
|
+
): string | undefined {
|
|
3791
|
+
const extractColorCandidate = (input: unknown): unknown => {
|
|
3792
|
+
if (input === null || input === undefined) return undefined;
|
|
3793
|
+
if (typeof input === 'string' || typeof input === 'number') return input;
|
|
3794
|
+
|
|
3795
|
+
if (Array.isArray(input)) {
|
|
3796
|
+
for (const value of input) {
|
|
3797
|
+
const nested = extractColorCandidate(value);
|
|
3798
|
+
if (nested !== undefined) return nested;
|
|
3799
|
+
}
|
|
3800
|
+
return undefined;
|
|
3801
|
+
}
|
|
3802
|
+
|
|
3803
|
+
if (typeof input !== 'object') return undefined;
|
|
3804
|
+
|
|
3805
|
+
const source = input as Record<string, any>;
|
|
3806
|
+
if (
|
|
3807
|
+
Number.isFinite(Number(source.r)) &&
|
|
3808
|
+
Number.isFinite(Number(source.g)) &&
|
|
3809
|
+
Number.isFinite(Number(source.b))
|
|
3810
|
+
) {
|
|
3811
|
+
const alphaRaw = Number.isFinite(Number(source.a)) ? Number(source.a) : 1;
|
|
3812
|
+
const alpha = Math.max(0, Math.min(1, alphaRaw));
|
|
3813
|
+
return `rgba(${Number(source.r)},${Number(source.g)},${Number(
|
|
3814
|
+
source.b
|
|
3815
|
+
)},${alpha})`;
|
|
3816
|
+
}
|
|
3817
|
+
|
|
3818
|
+
// If a gradient is provided for slider fill, use the first stop as fallback.
|
|
3819
|
+
if (
|
|
3820
|
+
String(source.type ?? '').toLowerCase() === 'gradient' &&
|
|
3821
|
+
Array.isArray(source.colors)
|
|
3822
|
+
) {
|
|
3823
|
+
const first = extractColorCandidate(source.colors);
|
|
3824
|
+
if (first !== undefined) return first;
|
|
3825
|
+
}
|
|
3826
|
+
|
|
3827
|
+
const candidateKeys = [
|
|
3828
|
+
'value',
|
|
3829
|
+
'color',
|
|
3830
|
+
'backgroundColor',
|
|
3831
|
+
'hex',
|
|
3832
|
+
'argb',
|
|
3833
|
+
'rgba',
|
|
3834
|
+
'raw',
|
|
3835
|
+
];
|
|
3836
|
+
for (const key of candidateKeys) {
|
|
3837
|
+
const nested = extractColorCandidate(source[key]);
|
|
3838
|
+
if (nested !== undefined) return nested;
|
|
3839
|
+
}
|
|
3840
|
+
|
|
3841
|
+
return undefined;
|
|
3842
|
+
};
|
|
3843
|
+
|
|
3844
|
+
const parseSliderFillColor = (color: unknown): string => {
|
|
3845
|
+
const candidate = extractColorCandidate(color);
|
|
3846
|
+
if (candidate === undefined) return 'transparent';
|
|
3847
|
+
|
|
3848
|
+
if (typeof candidate === 'string') {
|
|
3849
|
+
const trimmed = candidate.trim();
|
|
3850
|
+
if (/^#[\da-fA-F]{8}$/.test(trimmed)) {
|
|
3851
|
+
// Dashboard stores alpha-enabled web hex as #RRGGBBAA.
|
|
3852
|
+
const rrggbbaa = trimmed.slice(1);
|
|
3853
|
+
const aarrggbb = `${rrggbbaa.slice(6, 8)}${rrggbbaa.slice(0, 6)}`;
|
|
3854
|
+
return parseColor(`0x${aarrggbb}`);
|
|
3855
|
+
}
|
|
3856
|
+
return parseColor(trimmed);
|
|
3857
|
+
}
|
|
3858
|
+
|
|
3859
|
+
if (typeof candidate === 'number') {
|
|
3860
|
+
return parseColor(candidate);
|
|
3861
|
+
}
|
|
3862
|
+
|
|
3863
|
+
if (typeof color === 'string') {
|
|
3864
|
+
const trimmed = color.trim();
|
|
3865
|
+
if (/^#[\da-fA-F]{8}$/.test(trimmed)) {
|
|
3866
|
+
// Dashboard stores alpha-enabled web hex as #RRGGBBAA.
|
|
3867
|
+
const rrggbbaa = trimmed.slice(1);
|
|
3868
|
+
const aarrggbb = `${rrggbbaa.slice(6, 8)}${rrggbbaa.slice(0, 6)}`;
|
|
3869
|
+
return parseColor(`0x${aarrggbb}`);
|
|
3870
|
+
}
|
|
3871
|
+
}
|
|
3872
|
+
return parseColor(candidate as string | number | null | undefined);
|
|
3873
|
+
};
|
|
3874
|
+
|
|
3875
|
+
const appearanceFill = sliderProps?.appearance?.fill;
|
|
3876
|
+
if (
|
|
3877
|
+
typeof appearanceFill === 'string' ||
|
|
3878
|
+
typeof appearanceFill === 'number'
|
|
3879
|
+
) {
|
|
3880
|
+
return parseSliderFillColor(appearanceFill);
|
|
3881
|
+
}
|
|
3882
|
+
if (appearanceFill && typeof appearanceFill === 'object') {
|
|
3883
|
+
const type = String(appearanceFill.type ?? '').toLowerCase();
|
|
3884
|
+
if (type === 'color' || type === 'solid' || type === '') {
|
|
3885
|
+
const color = appearanceFill.value ?? appearanceFill.color;
|
|
3886
|
+
if (color !== undefined) return parseSliderFillColor(color);
|
|
3887
|
+
}
|
|
3888
|
+
}
|
|
3889
|
+
|
|
3890
|
+
const fill = sliderProps?.fill;
|
|
3891
|
+
if (typeof fill === 'string' || typeof fill === 'number') {
|
|
3892
|
+
return parseSliderFillColor(fill);
|
|
3893
|
+
}
|
|
3894
|
+
if (fill && typeof fill === 'object') {
|
|
3895
|
+
const type = String(fill.type ?? '').toLowerCase();
|
|
3896
|
+
if (type === 'color' || type === 'solid' || type === '') {
|
|
3897
|
+
const color = fill.value ?? fill.color;
|
|
3898
|
+
if (color !== undefined) return parseSliderFillColor(color);
|
|
3899
|
+
}
|
|
3900
|
+
}
|
|
3901
|
+
|
|
3902
|
+
const background = sliderProps?.background;
|
|
3903
|
+
if (background && typeof background === 'object') {
|
|
3904
|
+
const type = String(background.type ?? '').toLowerCase();
|
|
3905
|
+
if (type === 'color') {
|
|
3906
|
+
const color = background.value ?? background.color;
|
|
3907
|
+
if (color !== undefined) return parseSliderFillColor(color);
|
|
3908
|
+
}
|
|
3909
|
+
}
|
|
3910
|
+
|
|
3911
|
+
if (sliderProps?.backgroundColor !== undefined) {
|
|
3912
|
+
return parseSliderFillColor(sliderProps.backgroundColor);
|
|
3913
|
+
}
|
|
3914
|
+
|
|
3915
|
+
return undefined;
|
|
3916
|
+
}
|
|
3917
|
+
|
|
3532
3918
|
function SliderWidget({
|
|
3533
3919
|
id,
|
|
3534
|
-
|
|
3920
|
+
sliderProps,
|
|
3921
|
+
slidePropsList,
|
|
3535
3922
|
direction,
|
|
3536
|
-
|
|
3537
|
-
|
|
3923
|
+
pageAlignment,
|
|
3924
|
+
pageSpacing,
|
|
3925
|
+
pagePeek,
|
|
3538
3926
|
loop,
|
|
3539
|
-
|
|
3540
|
-
|
|
3927
|
+
autoAdvance,
|
|
3928
|
+
autoAdvanceIntervalMs,
|
|
3929
|
+
pageControlEnabled,
|
|
3930
|
+
pageControlPosition,
|
|
3541
3931
|
children,
|
|
3542
3932
|
}: {
|
|
3543
3933
|
id?: string;
|
|
3544
|
-
|
|
3934
|
+
sliderProps: Record<string, any>;
|
|
3935
|
+
slidePropsList: Record<string, any>[];
|
|
3545
3936
|
direction: 'horizontal' | 'vertical';
|
|
3546
|
-
|
|
3547
|
-
|
|
3937
|
+
pageAlignment: 'start' | 'center' | 'end';
|
|
3938
|
+
pageSpacing: number;
|
|
3939
|
+
pagePeek: number;
|
|
3548
3940
|
loop: boolean;
|
|
3549
|
-
|
|
3550
|
-
|
|
3941
|
+
autoAdvance: boolean;
|
|
3942
|
+
autoAdvanceIntervalMs: number;
|
|
3943
|
+
pageControlEnabled: boolean;
|
|
3944
|
+
pageControlPosition: 'top' | 'bottom';
|
|
3551
3945
|
children: React.ReactNode[];
|
|
3552
3946
|
}) {
|
|
3553
3947
|
const registry = useSliderRegistry();
|
|
3554
|
-
const pagerRef = React.useRef<
|
|
3948
|
+
const pagerRef = React.useRef<PagerViewHandle | null>(null);
|
|
3555
3949
|
const pageLength = children.length;
|
|
3556
3950
|
const pageCount = pageLength;
|
|
3557
3951
|
const [currentPage, setCurrentPage] = React.useState(0);
|
|
3558
3952
|
const timerRef = React.useRef<NodeJS.Timeout | null>(null);
|
|
3953
|
+
const [isInteracting, setIsInteracting] = React.useState(false);
|
|
3954
|
+
const idleTimerRef = React.useRef<NodeJS.Timeout | null>(null);
|
|
3955
|
+
const [availableSize, setAvailableSize] = React.useState({
|
|
3956
|
+
width: 1,
|
|
3957
|
+
height: 1,
|
|
3958
|
+
});
|
|
3959
|
+
const [activeSlideSize, setActiveSlideSize] = React.useState({
|
|
3960
|
+
width: 1,
|
|
3961
|
+
height: 1,
|
|
3962
|
+
});
|
|
3963
|
+
|
|
3964
|
+
const markInteracting = React.useCallback(() => {
|
|
3965
|
+
setIsInteracting(true);
|
|
3966
|
+
if (idleTimerRef.current) {
|
|
3967
|
+
clearTimeout(idleTimerRef.current);
|
|
3968
|
+
}
|
|
3969
|
+
idleTimerRef.current = setTimeout(() => setIsInteracting(false), 1400);
|
|
3970
|
+
}, []);
|
|
3559
3971
|
|
|
3560
3972
|
React.useEffect(() => {
|
|
3561
3973
|
setCurrentPage((prev) => clampSliderPage(prev, pageCount));
|
|
3562
3974
|
}, [pageCount]);
|
|
3563
3975
|
|
|
3564
3976
|
React.useEffect(() => {
|
|
3565
|
-
if (
|
|
3977
|
+
if (autoAdvance && !isInteracting && pageLength > 1) {
|
|
3566
3978
|
timerRef.current = setInterval(() => {
|
|
3567
3979
|
setCurrentPage((prev) => {
|
|
3568
3980
|
const safePrev = clampSliderPage(prev, pageCount);
|
|
@@ -3570,12 +3982,19 @@ function SliderWidget({
|
|
|
3570
3982
|
if (loop) return 0;
|
|
3571
3983
|
return safePrev;
|
|
3572
3984
|
});
|
|
3573
|
-
},
|
|
3985
|
+
}, autoAdvanceIntervalMs);
|
|
3574
3986
|
}
|
|
3575
3987
|
return () => {
|
|
3576
3988
|
if (timerRef.current) clearInterval(timerRef.current);
|
|
3577
3989
|
};
|
|
3578
|
-
}, [
|
|
3990
|
+
}, [
|
|
3991
|
+
autoAdvance,
|
|
3992
|
+
autoAdvanceIntervalMs,
|
|
3993
|
+
isInteracting,
|
|
3994
|
+
loop,
|
|
3995
|
+
pageCount,
|
|
3996
|
+
pageLength,
|
|
3997
|
+
]);
|
|
3579
3998
|
|
|
3580
3999
|
React.useEffect(() => {
|
|
3581
4000
|
if (pagerRef.current && pageCount > 0) {
|
|
@@ -3589,42 +4008,205 @@ function SliderWidget({
|
|
|
3589
4008
|
}
|
|
3590
4009
|
}, [registry, id, currentPage, children.length]);
|
|
3591
4010
|
|
|
3592
|
-
React.useEffect(() => {
|
|
3593
|
-
if (!linkedTo || !registry) return;
|
|
3594
|
-
return registry.getNotifier(linkedTo, (value) => {
|
|
3595
|
-
const normalized = normalizeLoopIndex(value, pageLength);
|
|
3596
|
-
const targetPage = clampSliderPage(normalized, pageCount);
|
|
3597
|
-
setCurrentPage(targetPage);
|
|
3598
|
-
if (pagerRef.current) {
|
|
3599
|
-
pagerRef.current.setPage(targetPage);
|
|
3600
|
-
}
|
|
3601
|
-
});
|
|
3602
|
-
}, [linkedTo, pageCount, pageLength, registry]);
|
|
3603
|
-
|
|
3604
4011
|
if (pageLength === 0) return null;
|
|
3605
4012
|
|
|
4013
|
+
// Slider layout parity rules copied from Web Preview:
|
|
4014
|
+
// 1) leadingPeek = start?0 : end?pagePeek*2 : pagePeek
|
|
4015
|
+
// 2) trailingPeek = end?0 : start?pagePeek*2 : pagePeek
|
|
4016
|
+
// 3) pagePrimary = viewportPrimary - leadingPeek - trailingPeek (clamped >= 1)
|
|
4017
|
+
// 4) track offset = activeIndex * (pagePrimary + pageSpacing)
|
|
4018
|
+
// 5) fit parent mode follows active slide size but is clamped to container width
|
|
4019
|
+
// 6) viewport always clips track content (no root horizontal overflow)
|
|
4020
|
+
const { leading, trailing } = resolvePeekInsets(pageAlignment, pagePeek);
|
|
4021
|
+
const showDots = pageControlEnabled && pageLength > 1;
|
|
4022
|
+
const widthMode = resolveSliderSizeMode(sliderProps, 'width', 'fill');
|
|
4023
|
+
const heightMode = resolveSliderSizeMode(sliderProps, 'height', 'fit');
|
|
4024
|
+
const fixedWidth = toFinite(parseLayoutDimension(sliderProps.width, true));
|
|
4025
|
+
const fixedHeight = toFinite(parseLayoutDimension(sliderProps.height, true));
|
|
4026
|
+
const sliderBackgroundColor = resolveSliderBackgroundColor(sliderProps);
|
|
4027
|
+
const resolved = resolveSliderContainerSize({
|
|
4028
|
+
direction,
|
|
4029
|
+
widthMode,
|
|
4030
|
+
heightMode,
|
|
4031
|
+
availableWidth: availableSize.width,
|
|
4032
|
+
availableHeight: availableSize.height,
|
|
4033
|
+
activeSlideWidth: activeSlideSize.width,
|
|
4034
|
+
activeSlideHeight: activeSlideSize.height,
|
|
4035
|
+
fixedWidth,
|
|
4036
|
+
fixedHeight,
|
|
4037
|
+
leadingPeek: leading,
|
|
4038
|
+
trailingPeek: trailing,
|
|
4039
|
+
showDots,
|
|
4040
|
+
});
|
|
4041
|
+
const viewportPrimary =
|
|
4042
|
+
direction === 'horizontal'
|
|
4043
|
+
? resolved.viewportWidth
|
|
4044
|
+
: resolved.viewportHeight;
|
|
4045
|
+
const pagePrimary = Math.max(1, viewportPrimary - leading - trailing);
|
|
4046
|
+
|
|
3606
4047
|
return (
|
|
3607
|
-
<
|
|
3608
|
-
|
|
3609
|
-
|
|
3610
|
-
|
|
3611
|
-
|
|
3612
|
-
|
|
3613
|
-
|
|
3614
|
-
const
|
|
3615
|
-
|
|
3616
|
-
|
|
3617
|
-
|
|
3618
|
-
|
|
3619
|
-
|
|
4048
|
+
<View
|
|
4049
|
+
onTouchStart={markInteracting}
|
|
4050
|
+
onTouchEnd={markInteracting}
|
|
4051
|
+
onTouchCancel={markInteracting}
|
|
4052
|
+
onPointerDown={markInteracting as any}
|
|
4053
|
+
onPointerUp={markInteracting as any}
|
|
4054
|
+
onLayout={(event) => {
|
|
4055
|
+
const nextWidth = Math.max(
|
|
4056
|
+
1,
|
|
4057
|
+
Math.round(event.nativeEvent.layout.width)
|
|
4058
|
+
);
|
|
4059
|
+
const nextHeight = Math.max(
|
|
4060
|
+
1,
|
|
4061
|
+
Math.round(event.nativeEvent.layout.height)
|
|
4062
|
+
);
|
|
4063
|
+
setAvailableSize((prev) =>
|
|
4064
|
+
prev.width === nextWidth && prev.height === nextHeight
|
|
4065
|
+
? prev
|
|
4066
|
+
: { width: nextWidth, height: nextHeight }
|
|
4067
|
+
);
|
|
4068
|
+
}}
|
|
4069
|
+
style={{
|
|
4070
|
+
width: resolved.outerWidth,
|
|
4071
|
+
height: resolved.outerHeight,
|
|
4072
|
+
minWidth: 1,
|
|
4073
|
+
minHeight: 1,
|
|
4074
|
+
backgroundColor: sliderBackgroundColor,
|
|
4075
|
+
borderRadius: 0,
|
|
4076
|
+
overflow: 'visible',
|
|
3620
4077
|
}}
|
|
3621
4078
|
>
|
|
3622
|
-
{
|
|
3623
|
-
<View
|
|
3624
|
-
{
|
|
4079
|
+
{showDots && pageControlPosition === 'top' ? (
|
|
4080
|
+
<View
|
|
4081
|
+
style={{
|
|
4082
|
+
marginBottom: 8,
|
|
4083
|
+
flexDirection: 'row',
|
|
4084
|
+
justifyContent: 'center',
|
|
4085
|
+
alignItems: 'center',
|
|
4086
|
+
}}
|
|
4087
|
+
>
|
|
4088
|
+
{Array.from({ length: pageLength }).map((_, index) => {
|
|
4089
|
+
const isActive = index === currentPage;
|
|
4090
|
+
return (
|
|
4091
|
+
<View
|
|
4092
|
+
key={`slider-dot-top-${index}`}
|
|
4093
|
+
style={{
|
|
4094
|
+
width: 6,
|
|
4095
|
+
height: 6,
|
|
4096
|
+
borderRadius: 3,
|
|
4097
|
+
marginHorizontal: 3,
|
|
4098
|
+
backgroundColor: isActive
|
|
4099
|
+
? parseColor('0xFF111827')
|
|
4100
|
+
: parseColor('0xFFD1D5DB'),
|
|
4101
|
+
}}
|
|
4102
|
+
/>
|
|
4103
|
+
);
|
|
4104
|
+
})}
|
|
3625
4105
|
</View>
|
|
3626
|
-
)
|
|
3627
|
-
|
|
4106
|
+
) : null}
|
|
4107
|
+
|
|
4108
|
+
<View
|
|
4109
|
+
style={{
|
|
4110
|
+
overflow: 'hidden',
|
|
4111
|
+
...(direction === 'horizontal'
|
|
4112
|
+
? {
|
|
4113
|
+
width: resolved.viewportWidth,
|
|
4114
|
+
height: resolved.viewportHeight,
|
|
4115
|
+
paddingLeft: leading,
|
|
4116
|
+
paddingRight: trailing,
|
|
4117
|
+
}
|
|
4118
|
+
: {
|
|
4119
|
+
width: resolved.viewportWidth,
|
|
4120
|
+
height: resolved.viewportHeight,
|
|
4121
|
+
paddingTop: leading,
|
|
4122
|
+
paddingBottom: trailing,
|
|
4123
|
+
}),
|
|
4124
|
+
}}
|
|
4125
|
+
>
|
|
4126
|
+
<PagerView
|
|
4127
|
+
ref={pagerRef}
|
|
4128
|
+
style={{ width: '100%', height: '100%', minHeight: 1 }}
|
|
4129
|
+
orientation={direction}
|
|
4130
|
+
pageMargin={Math.max(0, pageSpacing)}
|
|
4131
|
+
initialPage={clampSliderPage(currentPage, pageCount)}
|
|
4132
|
+
onPageSelected={(event: PagerViewOnPageSelectedEvent) => {
|
|
4133
|
+
const page = event.nativeEvent.position;
|
|
4134
|
+
const safePage = clampSliderPage(page, pageCount);
|
|
4135
|
+
setCurrentPage(safePage);
|
|
4136
|
+
markInteracting();
|
|
4137
|
+
if (registry && id) {
|
|
4138
|
+
registry.update(id, safePage % pageLength, pageLength);
|
|
4139
|
+
}
|
|
4140
|
+
}}
|
|
4141
|
+
>
|
|
4142
|
+
{Array.from({ length: pageCount }).map((_, index) => (
|
|
4143
|
+
<View
|
|
4144
|
+
key={`slider-page-${index}`}
|
|
4145
|
+
onLayout={
|
|
4146
|
+
index === currentPage
|
|
4147
|
+
? (event) => {
|
|
4148
|
+
const nextWidth = Math.max(
|
|
4149
|
+
1,
|
|
4150
|
+
Math.round(event.nativeEvent.layout.width)
|
|
4151
|
+
);
|
|
4152
|
+
const nextHeight = Math.max(
|
|
4153
|
+
1,
|
|
4154
|
+
Math.round(event.nativeEvent.layout.height)
|
|
4155
|
+
);
|
|
4156
|
+
setActiveSlideSize((prev) =>
|
|
4157
|
+
prev.width === nextWidth && prev.height === nextHeight
|
|
4158
|
+
? prev
|
|
4159
|
+
: { width: nextWidth, height: nextHeight }
|
|
4160
|
+
);
|
|
4161
|
+
}
|
|
4162
|
+
: undefined
|
|
4163
|
+
}
|
|
4164
|
+
style={{
|
|
4165
|
+
alignSelf: 'flex-start',
|
|
4166
|
+
overflow: 'hidden',
|
|
4167
|
+
...(resolveSlidePageSize({
|
|
4168
|
+
slideProps: slidePropsList[index] ?? {},
|
|
4169
|
+
direction,
|
|
4170
|
+
pagePrimary,
|
|
4171
|
+
crossSize: '100%',
|
|
4172
|
+
}) as ViewStyle),
|
|
4173
|
+
}}
|
|
4174
|
+
>
|
|
4175
|
+
{children[index % pageLength]}
|
|
4176
|
+
</View>
|
|
4177
|
+
))}
|
|
4178
|
+
</PagerView>
|
|
4179
|
+
</View>
|
|
4180
|
+
|
|
4181
|
+
{showDots && pageControlPosition === 'bottom' ? (
|
|
4182
|
+
<View
|
|
4183
|
+
style={{
|
|
4184
|
+
marginTop: 8,
|
|
4185
|
+
flexDirection: 'row',
|
|
4186
|
+
justifyContent: 'center',
|
|
4187
|
+
alignItems: 'center',
|
|
4188
|
+
}}
|
|
4189
|
+
>
|
|
4190
|
+
{Array.from({ length: pageLength }).map((_, index) => {
|
|
4191
|
+
const isActive = index === currentPage;
|
|
4192
|
+
return (
|
|
4193
|
+
<View
|
|
4194
|
+
key={`slider-dot-bottom-${index}`}
|
|
4195
|
+
style={{
|
|
4196
|
+
width: 6,
|
|
4197
|
+
height: 6,
|
|
4198
|
+
borderRadius: 3,
|
|
4199
|
+
marginHorizontal: 3,
|
|
4200
|
+
backgroundColor: isActive
|
|
4201
|
+
? parseColor('0xFF111827')
|
|
4202
|
+
: parseColor('0xFFD1D5DB'),
|
|
4203
|
+
}}
|
|
4204
|
+
/>
|
|
4205
|
+
);
|
|
4206
|
+
})}
|
|
4207
|
+
</View>
|
|
4208
|
+
) : null}
|
|
4209
|
+
</View>
|
|
3628
4210
|
);
|
|
3629
4211
|
}
|
|
3630
4212
|
|
|
@@ -3636,12 +4218,6 @@ function clampSliderPage(page: number, pageCount: number): number {
|
|
|
3636
4218
|
return Math.floor(page);
|
|
3637
4219
|
}
|
|
3638
4220
|
|
|
3639
|
-
function normalizeLoopIndex(index: number, size: number): number {
|
|
3640
|
-
if (size <= 0 || !Number.isFinite(index)) return 0;
|
|
3641
|
-
const normalized = Math.floor(index) % size;
|
|
3642
|
-
return normalized < 0 ? normalized + size : normalized;
|
|
3643
|
-
}
|
|
3644
|
-
|
|
3645
4221
|
function PageViewIndicator({
|
|
3646
4222
|
linkedTo,
|
|
3647
4223
|
activeColor,
|