react-native-reanimated-carousel 5.0.0-beta.5 → 5.0.0-beta.6
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 +28 -6
- package/lib/commonjs/components/CarouselLayout.js +1 -1
- package/lib/commonjs/components/CarouselLayout.js.map +1 -1
- package/lib/commonjs/components/ItemLayout.js +1 -1
- package/lib/commonjs/components/ItemLayout.js.map +1 -1
- package/lib/commonjs/hooks/useVisibleRanges.js +1 -1
- package/lib/commonjs/hooks/useVisibleRanges.js.map +1 -1
- package/lib/module/components/CarouselLayout.js +1 -1
- package/lib/module/components/CarouselLayout.js.map +1 -1
- package/lib/module/components/ItemLayout.js +1 -1
- package/lib/module/components/ItemLayout.js.map +1 -1
- package/lib/module/hooks/useVisibleRanges.js +1 -1
- package/lib/module/hooks/useVisibleRanges.js.map +1 -1
- package/lib/typescript/components/CarouselLayout.d.ts +11 -0
- package/lib/typescript/components/CarouselLayout.d.ts.map +1 -1
- package/lib/typescript/components/ItemLayout.d.ts +5 -0
- package/lib/typescript/components/ItemLayout.d.ts.map +1 -1
- package/lib/typescript/hooks/useVisibleRanges.d.ts.map +1 -1
- package/package.json +13 -4
- package/src/components/Carousel.test.tsx +152 -0
- package/src/components/CarouselLayout.tsx +51 -8
- package/src/components/ItemLayout.tsx +31 -7
- package/src/hooks/useVisibleRanges.test.tsx +29 -0
- package/src/hooks/useVisibleRanges.tsx +6 -1
|
@@ -15,6 +15,34 @@ import { ScrollViewGesture } from "./ScrollViewGesture";
|
|
|
15
15
|
|
|
16
16
|
export type TAnimationStyle = (value: number) => ViewStyle;
|
|
17
17
|
|
|
18
|
+
export function resolveCarouselLayoutStyle(params: {
|
|
19
|
+
flattenedStyle: Partial<ViewStyle>;
|
|
20
|
+
vertical: boolean;
|
|
21
|
+
measuredSize: number;
|
|
22
|
+
sizeExplicit: boolean;
|
|
23
|
+
legacyWidth?: number;
|
|
24
|
+
legacyHeight?: number;
|
|
25
|
+
}) {
|
|
26
|
+
"worklet";
|
|
27
|
+
|
|
28
|
+
const { flattenedStyle, vertical, measuredSize, sizeExplicit, legacyWidth, legacyHeight } =
|
|
29
|
+
params;
|
|
30
|
+
const { width, height } = flattenedStyle;
|
|
31
|
+
const hasLegacyMainAxisSize = vertical
|
|
32
|
+
? typeof legacyHeight === "number" && legacyHeight > 0
|
|
33
|
+
: typeof legacyWidth === "number" && legacyWidth > 0;
|
|
34
|
+
const shouldUseMeasuredMainAxisSize = sizeExplicit || hasLegacyMainAxisSize;
|
|
35
|
+
const resolvedMainAxisSize = measuredSize || "100%";
|
|
36
|
+
|
|
37
|
+
return {
|
|
38
|
+
width:
|
|
39
|
+
width ?? (vertical ? "100%" : shouldUseMeasuredMainAxisSize ? resolvedMainAxisSize : "100%"),
|
|
40
|
+
height:
|
|
41
|
+
height ??
|
|
42
|
+
(vertical ? (shouldUseMeasuredMainAxisSize ? resolvedMainAxisSize : "100%") : "100%"),
|
|
43
|
+
};
|
|
44
|
+
}
|
|
45
|
+
|
|
18
46
|
export const CarouselLayout = React.forwardRef<ICarouselInstance>((_props, ref) => {
|
|
19
47
|
const { props, common } = useGlobalState();
|
|
20
48
|
|
|
@@ -46,9 +74,11 @@ export const CarouselLayout = React.forwardRef<ICarouselInstance>((_props, ref)
|
|
|
46
74
|
onProgressChange,
|
|
47
75
|
customAnimation,
|
|
48
76
|
defaultIndex,
|
|
77
|
+
width: legacyWidth,
|
|
78
|
+
height: legacyHeight,
|
|
49
79
|
} = props;
|
|
50
80
|
|
|
51
|
-
const { size, handlerOffset, resolvedSize, sizePhase } = common;
|
|
81
|
+
const { size, handlerOffset, resolvedSize, sizePhase, sizeExplicit } = common;
|
|
52
82
|
const layoutConfig = useLayoutConfig({ ...props, size });
|
|
53
83
|
|
|
54
84
|
const isSizeReady = useDerivedValue(() => {
|
|
@@ -157,18 +187,31 @@ export const CarouselLayout = React.forwardRef<ICarouselInstance>((_props, ref)
|
|
|
157
187
|
const flattenedStyle = StyleSheet.flatten(style) || {};
|
|
158
188
|
|
|
159
189
|
const layoutStyle = useAnimatedStyle(() => {
|
|
160
|
-
const { width, height } = flattenedStyle;
|
|
161
190
|
const measuredSize = resolvedSize.value ?? 0;
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
191
|
+
const { width, height } = resolveCarouselLayoutStyle({
|
|
192
|
+
flattenedStyle,
|
|
193
|
+
vertical: !!vertical,
|
|
194
|
+
measuredSize,
|
|
195
|
+
sizeExplicit,
|
|
196
|
+
legacyWidth,
|
|
197
|
+
legacyHeight,
|
|
198
|
+
});
|
|
165
199
|
|
|
166
200
|
return {
|
|
167
|
-
width
|
|
168
|
-
height
|
|
201
|
+
width,
|
|
202
|
+
height,
|
|
169
203
|
opacity: isSizeReady.value ? 1 : 0,
|
|
170
204
|
};
|
|
171
|
-
}, [
|
|
205
|
+
}, [
|
|
206
|
+
flattenedStyle,
|
|
207
|
+
isSizeReady,
|
|
208
|
+
vertical,
|
|
209
|
+
resolvedSize,
|
|
210
|
+
sizePhase,
|
|
211
|
+
sizeExplicit,
|
|
212
|
+
legacyWidth,
|
|
213
|
+
legacyHeight,
|
|
214
|
+
]);
|
|
172
215
|
|
|
173
216
|
return (
|
|
174
217
|
<GestureHandlerRootView testID={testID} style={[styles.layoutContainer, style]}>
|
|
@@ -19,6 +19,20 @@ import { sanitizeAnimationStyle } from "../utils/sanitize-animation-style";
|
|
|
19
19
|
|
|
20
20
|
export type TAnimationStyle = NonNullable<TCarouselProps["customAnimation"]>;
|
|
21
21
|
|
|
22
|
+
export function resolveItemMainAxisSize(params: {
|
|
23
|
+
explicitSize?: number;
|
|
24
|
+
effectivePageSize?: number;
|
|
25
|
+
measuredSize: number | null;
|
|
26
|
+
}) {
|
|
27
|
+
"worklet";
|
|
28
|
+
|
|
29
|
+
const { explicitSize, effectivePageSize, measuredSize } = params;
|
|
30
|
+
|
|
31
|
+
return typeof explicitSize === "number"
|
|
32
|
+
? explicitSize
|
|
33
|
+
: (effectivePageSize ?? measuredSize ?? "100%");
|
|
34
|
+
}
|
|
35
|
+
|
|
22
36
|
export const ItemLayout: React.FC<{
|
|
23
37
|
index: number;
|
|
24
38
|
handlerOffset: SharedValue<number>;
|
|
@@ -71,15 +85,25 @@ export const ItemLayout: React.FC<{
|
|
|
71
85
|
const widthCandidate = vertical ? width : (explicitItemSize ?? explicitAxisSize);
|
|
72
86
|
const heightCandidate = vertical ? (explicitItemSize ?? explicitAxisSize) : height;
|
|
73
87
|
|
|
74
|
-
const computedWidth =
|
|
75
|
-
typeof widthCandidate === "number"
|
|
88
|
+
const computedWidth = vertical
|
|
89
|
+
? typeof widthCandidate === "number"
|
|
76
90
|
? widthCandidate
|
|
77
|
-
: (measuredSize.value.width ??
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
91
|
+
: (measuredSize.value.width ?? "100%")
|
|
92
|
+
: resolveItemMainAxisSize({
|
|
93
|
+
explicitSize: widthCandidate,
|
|
94
|
+
effectivePageSize,
|
|
95
|
+
measuredSize: measuredSize.value.width,
|
|
96
|
+
});
|
|
97
|
+
|
|
98
|
+
const computedHeight = vertical
|
|
99
|
+
? resolveItemMainAxisSize({
|
|
100
|
+
explicitSize: heightCandidate,
|
|
101
|
+
effectivePageSize,
|
|
102
|
+
measuredSize: measuredSize.value.height,
|
|
103
|
+
})
|
|
104
|
+
: typeof heightCandidate === "number"
|
|
81
105
|
? heightCandidate
|
|
82
|
-
: (measuredSize.value.height ??
|
|
106
|
+
: (measuredSize.value.height ?? "100%");
|
|
83
107
|
|
|
84
108
|
return {
|
|
85
109
|
width: computedWidth,
|
|
@@ -67,6 +67,35 @@ describe("useVisibleRanges", () => {
|
|
|
67
67
|
`);
|
|
68
68
|
});
|
|
69
69
|
|
|
70
|
+
it.each([
|
|
71
|
+
{ total: 1, windowSize: 1, currentIndex: 0 },
|
|
72
|
+
{ total: 2, windowSize: 5, currentIndex: 1 },
|
|
73
|
+
{ total: 3, windowSize: 3, currentIndex: 2 },
|
|
74
|
+
{ total: 4, windowSize: 5, currentIndex: 1 },
|
|
75
|
+
])(
|
|
76
|
+
"should display every item when windowSize=$windowSize is at least total=$total in loop mode",
|
|
77
|
+
({ total, windowSize, currentIndex }) => {
|
|
78
|
+
const ranges = renderHook(() => {
|
|
79
|
+
const translation = useSharedValue(-currentIndex * viewSize);
|
|
80
|
+
return useVisibleRanges({
|
|
81
|
+
total,
|
|
82
|
+
translation,
|
|
83
|
+
viewSize,
|
|
84
|
+
windowSize,
|
|
85
|
+
loop: true,
|
|
86
|
+
});
|
|
87
|
+
}).result.current.value;
|
|
88
|
+
|
|
89
|
+
const visibleIndexes = Array.from({ length: total }, (_, index) => index).filter(
|
|
90
|
+
(index) =>
|
|
91
|
+
(index >= ranges.negativeRange[0] && index <= ranges.negativeRange[1]) ||
|
|
92
|
+
(index >= ranges.positiveRange[0] && index <= ranges.positiveRange[1])
|
|
93
|
+
);
|
|
94
|
+
|
|
95
|
+
expect(visibleIndexes).toEqual(Array.from({ length: total }, (_, index) => index));
|
|
96
|
+
}
|
|
97
|
+
);
|
|
98
|
+
|
|
70
99
|
it("should shows the increased range of the list when the loop is false and swiped the carousel.", async () => {
|
|
71
100
|
const slide0hook = renderHook(() => {
|
|
72
101
|
const translation = useSharedValue(-0 * viewSize);
|
|
@@ -50,7 +50,12 @@ export function useVisibleRanges(options: {
|
|
|
50
50
|
|
|
51
51
|
let newRanges: VisibleRanges;
|
|
52
52
|
|
|
53
|
-
if (
|
|
53
|
+
if (loop && windowSize >= total) {
|
|
54
|
+
newRanges = {
|
|
55
|
+
negativeRange: [0, 0],
|
|
56
|
+
positiveRange: [0, total - 1],
|
|
57
|
+
};
|
|
58
|
+
} else if (!loop) {
|
|
54
59
|
// Clamp currentIndex to valid range [0, total-1] for non-loop mode
|
|
55
60
|
// When overdragging right, translation.value becomes positive, making currentIndex negative
|
|
56
61
|
currentIndex = Math.max(0, Math.min(total - 1, currentIndex));
|