@momo-kits/carousel 0.165.2-beta.3 → 0.167.1-beta.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (40) hide show
  1. package/animation.ts +62 -0
  2. package/index.tsx +455 -390
  3. package/package.json +5 -6
  4. package/types.ts +48 -123
  5. package/core/LICENSE +0 -21
  6. package/core/README.md +0 -16
  7. package/core/components/Carousel.tsx +0 -28
  8. package/core/components/CarouselLayout.tsx +0 -231
  9. package/core/components/ItemLayout.tsx +0 -215
  10. package/core/components/ItemRenderer.tsx +0 -126
  11. package/core/components/LazyView.tsx +0 -14
  12. package/core/components/ScrollViewGesture.tsx +0 -443
  13. package/core/constants/index.ts +0 -11
  14. package/core/hooks/useAutoPlay.ts +0 -63
  15. package/core/hooks/useCarouselController.tsx +0 -295
  16. package/core/hooks/useCheckMounted.ts +0 -14
  17. package/core/hooks/useCommonVariables.ts +0 -261
  18. package/core/hooks/useInitProps.ts +0 -177
  19. package/core/hooks/useLayoutConfig.ts +0 -35
  20. package/core/hooks/useOffsetX.ts +0 -81
  21. package/core/hooks/useOnProgressChange.ts +0 -33
  22. package/core/hooks/usePanGestureProxy.ts +0 -144
  23. package/core/hooks/usePropsErrorBoundary.ts +0 -41
  24. package/core/hooks/useUpdateGestureConfig.ts +0 -17
  25. package/core/hooks/useVisibleRanges.tsx +0 -141
  26. package/core/layouts/index.tsx +0 -10
  27. package/core/layouts/normal.ts +0 -32
  28. package/core/layouts/parallax.ts +0 -50
  29. package/core/layouts/stack.ts +0 -266
  30. package/core/public-types.ts +0 -209
  31. package/core/store/index.tsx +0 -63
  32. package/core/types.ts +0 -32
  33. package/core/utils/carousel-direction.ts +0 -39
  34. package/core/utils/carousel-math.ts +0 -156
  35. package/core/utils/compute-gesture-translation.ts +0 -29
  36. package/core/utils/compute-offset-if-size-changed.ts +0 -14
  37. package/core/utils/computed-with-auto-fill-data.ts +0 -92
  38. package/core/utils/deal-with-animation.ts +0 -19
  39. package/core/utils/log.ts +0 -12
  40. package/core/utils/sanitize-animation-style.ts +0 -20
@@ -1,215 +0,0 @@
1
- import React from "react";
2
- import type { LayoutChangeEvent, ViewStyle } from "react-native";
3
- import { StyleSheet } from "react-native";
4
- import type { SharedValue } from "react-native-reanimated";
5
- import Animated, {
6
- useAnimatedReaction,
7
- useAnimatedStyle,
8
- useDerivedValue,
9
- useSharedValue,
10
- } from "react-native-reanimated";
11
- import { scheduleOnRN, scheduleOnUI } from "react-native-worklets";
12
-
13
- import type { OffsetOptions } from "../hooks/useOffsetX";
14
- import { useOffsetX } from "../hooks/useOffsetX";
15
- import type { VisibleRangesValue } from "../hooks/useVisibleRanges";
16
- import { useGlobalState } from "../store";
17
- import type { CarouselItemAnimation } from "../types";
18
- import { getHorizontalStackOffsetType } from "../utils/carousel-direction";
19
- import { positiveModulo } from "../utils/carousel-math";
20
- import { sanitizeAnimationStyle } from "../utils/sanitize-animation-style";
21
-
22
- export type ItemAnimationStyle = CarouselItemAnimation;
23
-
24
- export function resolveItemMainAxisSize(params: {
25
- explicitSize?: number | undefined;
26
- effectivePageSize?: number | undefined;
27
- measuredSize: number | null;
28
- }) {
29
- "worklet";
30
-
31
- const { explicitSize, effectivePageSize, measuredSize } = params;
32
-
33
- return typeof explicitSize === "number"
34
- ? explicitSize
35
- : (effectivePageSize ?? measuredSize ?? "100%");
36
- }
37
-
38
- export const ItemLayout: React.FC<{
39
- index: number;
40
- rawIndex: number;
41
- handlerOffset: SharedValue<number>;
42
- visibleRanges: VisibleRangesValue;
43
- animationStyle: ItemAnimationStyle;
44
- children: (ctx: {
45
- relativeProgress: SharedValue<number>;
46
- }) => React.ReactElement;
47
- }> = (props) => {
48
- const { handlerOffset, index, rawIndex, children, visibleRanges, animationStyle } = props;
49
-
50
- const {
51
- props: { loop, dataLength, directionSign, orientation, layout, style, itemSize },
52
- common,
53
- layout: { updateItemDimensions },
54
- } = useGlobalState();
55
-
56
- const measuredSize = useSharedValue<{ width: number | null; height: number | null }>({
57
- width: null,
58
- height: null,
59
- });
60
-
61
- const fallbackSize = common.size;
62
- const isVertical = orientation === "vertical";
63
- const { width: styleWidth, height: styleHeight } = StyleSheet.flatten(style) || {};
64
- const styleWidthNumber = typeof styleWidth === "number" ? styleWidth : undefined;
65
- const styleHeightNumber = typeof styleHeight === "number" ? styleHeight : undefined;
66
-
67
- const size = (itemSize ?? fallbackSize) || 0;
68
- const effectivePageSize = size > 0 ? size : undefined;
69
-
70
- const dimensionsStyle = useAnimatedStyle<ViewStyle>(() => {
71
- const widthCandidate = isVertical ? styleWidthNumber : itemSize;
72
- const heightCandidate = isVertical ? itemSize : styleHeightNumber;
73
-
74
- const computedWidth = isVertical
75
- ? typeof widthCandidate === "number"
76
- ? widthCandidate
77
- : (measuredSize.value.width ?? "100%")
78
- : resolveItemMainAxisSize({
79
- explicitSize: widthCandidate,
80
- effectivePageSize,
81
- measuredSize: measuredSize.value.width,
82
- });
83
-
84
- const computedHeight = isVertical
85
- ? resolveItemMainAxisSize({
86
- explicitSize: heightCandidate,
87
- effectivePageSize,
88
- measuredSize: measuredSize.value.height,
89
- })
90
- : typeof heightCandidate === "number"
91
- ? heightCandidate
92
- : (measuredSize.value.height ?? "100%");
93
-
94
- return {
95
- width: computedWidth,
96
- height: computedHeight,
97
- };
98
- }, [effectivePageSize, isVertical, itemSize, styleHeightNumber, styleWidthNumber]);
99
-
100
- let offsetXConfig: OffsetOptions = {
101
- handlerOffset,
102
- index,
103
- size,
104
- dataLength,
105
- loop,
106
- };
107
-
108
- if (layout?.type === "horizontal-stack") {
109
- offsetXConfig = {
110
- handlerOffset,
111
- index,
112
- size,
113
- dataLength,
114
- loop,
115
- type: getHorizontalStackOffsetType(layout.exitDirection ?? "left", directionSign),
116
- viewCount: layout.visibleCount,
117
- };
118
- }
119
-
120
- const x = useOffsetX(offsetXConfig, visibleRanges);
121
- const isCurrentForAccessibility = React.useCallback(() => {
122
- "worklet";
123
-
124
- return (
125
- size > 0 &&
126
- dataLength > 0 &&
127
- positiveModulo(Math.round(-handlerOffset.value / size), dataLength) === index
128
- );
129
- }, [dataLength, handlerOffset, index, size]);
130
- const [isAccessibilityCurrent, setIsAccessibilityCurrent] =
131
- React.useState(isCurrentForAccessibility);
132
-
133
- useAnimatedReaction(
134
- isCurrentForAccessibility,
135
- (isCurrent, wasCurrent) => {
136
- if (isCurrent !== wasCurrent) {
137
- scheduleOnRN(setIsAccessibilityCurrent, isCurrent);
138
- }
139
- },
140
- [isCurrentForAccessibility]
141
- );
142
-
143
- const relativeProgress = useDerivedValue(() => {
144
- if (!size) return 0;
145
- return x.value / size;
146
- }, [x, size]);
147
- const animatedStyle = useAnimatedStyle<ViewStyle>(() => {
148
- const safeSize = size || 1;
149
- return sanitizeAnimationStyle(animationStyle(x.value / safeSize, rawIndex));
150
- }, [animationStyle, rawIndex, x, size]);
151
-
152
- // TODO: For dynamic dimension in the future
153
- // function handleLayout(e: LayoutChangeEvent) {
154
- // const { width, height } = e.nativeEvent.layout;
155
- // updateItemDimensions(index, { width, height });
156
- // }
157
-
158
- const child = children({ relativeProgress });
159
-
160
- type LayoutableProps = {
161
- collapsable?: boolean;
162
- onLayout?: (event: LayoutChangeEvent) => void;
163
- };
164
-
165
- const enhancedChild = React.isValidElement<LayoutableProps>(child)
166
- ? React.cloneElement(child, {
167
- collapsable: false,
168
- onLayout: (event: LayoutChangeEvent) => {
169
- const { width: layoutWidth, height: layoutHeight } = event.nativeEvent.layout;
170
- if (layoutWidth > 0 && layoutHeight > 0) {
171
- scheduleOnUI(() => {
172
- const { width: prevWidth, height: prevHeight } = measuredSize.value;
173
- if (prevWidth === layoutWidth && prevHeight === layoutHeight) return;
174
-
175
- measuredSize.value = {
176
- width: layoutWidth,
177
- height: layoutHeight,
178
- };
179
- updateItemDimensions(index, {
180
- width: layoutWidth,
181
- height: layoutHeight,
182
- });
183
- });
184
- }
185
-
186
- child.props?.onLayout?.(event);
187
- },
188
- })
189
- : child;
190
-
191
- return (
192
- <Animated.View
193
- accessibilityElementsHidden={!isAccessibilityCurrent}
194
- accessible={isAccessibilityCurrent ? undefined : false}
195
- aria-hidden={!isAccessibilityCurrent}
196
- importantForAccessibility={isAccessibilityCurrent ? "auto" : "no-hide-descendants"}
197
- style={[
198
- {
199
- position: "absolute",
200
- pointerEvents: "box-none",
201
- },
202
- dimensionsStyle,
203
- animatedStyle,
204
- ]}
205
- /**
206
- * We use this testID to know when the carousel item is ready to be tested in test.
207
- * e.g.
208
- * The testID of first item will be changed to __CAROUSEL_ITEM_0_READY__ from __CAROUSEL_ITEM_0_NOT_READY__ when the item is ready.
209
- * */
210
- testID={`__CAROUSEL_ITEM_${index}__`}
211
- >
212
- {enhancedChild}
213
- </Animated.View>
214
- );
215
- };
@@ -1,126 +0,0 @@
1
- import React from "react";
2
- import type { FC } from "react";
3
- import type { SharedValue } from "react-native-reanimated";
4
- import { useAnimatedReaction } from "react-native-reanimated";
5
- import { scheduleOnRN } from "react-native-worklets";
6
-
7
- import type { ItemAnimationStyle } from "./ItemLayout";
8
- import { ItemLayout } from "./ItemLayout";
9
-
10
- import type { VisibleRanges } from "../hooks/useVisibleRanges";
11
- import { computeVisibleRanges, useVisibleRanges } from "../hooks/useVisibleRanges";
12
- import type { CarouselItemAnimation, CarouselRenderItem } from "../types";
13
- import { computedRealIndexWithAutoFillData } from "../utils/computed-with-auto-fill-data";
14
-
15
- interface Props {
16
- data: unknown[];
17
- rawData: unknown[];
18
- dataLength: number;
19
- rawDataLength: number;
20
- loop: boolean;
21
- size: number;
22
- renderWindowSize?: number | undefined;
23
- defaultIndex: number;
24
- autoFillData: boolean;
25
- offsetX: SharedValue<number>;
26
- handlerOffset: SharedValue<number>;
27
- layoutConfig: ItemAnimationStyle;
28
- renderItem: CarouselRenderItem<unknown>;
29
- itemAnimation?: CarouselItemAnimation | undefined;
30
- keyExtractor?: ((item: unknown, index: number) => string) | undefined;
31
- }
32
-
33
- export const ItemRenderer: FC<Props> = (props) => {
34
- const {
35
- data,
36
- rawData,
37
- size,
38
- renderWindowSize,
39
- defaultIndex,
40
- handlerOffset,
41
- offsetX,
42
- dataLength,
43
- rawDataLength,
44
- loop,
45
- autoFillData,
46
- layoutConfig,
47
- renderItem,
48
- itemAnimation,
49
- keyExtractor,
50
- } = props;
51
-
52
- const visibleRanges = useVisibleRanges({
53
- total: dataLength,
54
- viewSize: size,
55
- translation: handlerOffset,
56
- windowSize: renderWindowSize,
57
- loop,
58
- });
59
-
60
- // Initialize with a sensible default to avoid blank render on first frame
61
- const initialRanges: VisibleRanges = React.useMemo(
62
- () =>
63
- computeVisibleRanges({
64
- total: dataLength,
65
- windowSize: renderWindowSize,
66
- currentIndex: defaultIndex,
67
- loop,
68
- }),
69
- [dataLength, defaultIndex, loop, renderWindowSize]
70
- );
71
-
72
- const [displayedItems, setDisplayedItems] = React.useState<VisibleRanges>(initialRanges);
73
-
74
- useAnimatedReaction(
75
- () => visibleRanges.value,
76
- (ranges) => scheduleOnRN(setDisplayedItems, ranges),
77
- [visibleRanges]
78
- );
79
-
80
- return (
81
- <>
82
- {data.map((item, index) => {
83
- const realIndex = computedRealIndexWithAutoFillData({
84
- index,
85
- dataLength: rawDataLength,
86
- loop,
87
- autoFillData,
88
- });
89
-
90
- const { negativeRange, positiveRange } = displayedItems;
91
-
92
- const shouldRender =
93
- (index >= negativeRange[0] && index <= negativeRange[1]) ||
94
- (index >= positiveRange[0] && index <= positiveRange[1]);
95
-
96
- if (!shouldRender) return null;
97
-
98
- const rawItem = rawData[realIndex] ?? item;
99
- const key = keyExtractor
100
- ? `${keyExtractor(rawItem, realIndex)}::rnrc-copy-${Math.floor(
101
- index / Math.max(rawDataLength, 1)
102
- )}`
103
- : String(index);
104
-
105
- return (
106
- <ItemLayout
107
- key={key}
108
- index={index}
109
- rawIndex={realIndex}
110
- handlerOffset={offsetX}
111
- visibleRanges={visibleRanges}
112
- animationStyle={itemAnimation || layoutConfig}
113
- >
114
- {({ relativeProgress }) =>
115
- renderItem({
116
- item: rawItem,
117
- index: realIndex,
118
- relativeProgress,
119
- })
120
- }
121
- </ItemLayout>
122
- );
123
- })}
124
- </>
125
- );
126
- };
@@ -1,14 +0,0 @@
1
- import type { PropsWithChildren } from "react";
2
- import React from "react";
3
-
4
- interface Props {
5
- shouldUpdate: boolean;
6
- }
7
-
8
- export const LazyView: React.FC<PropsWithChildren<Props>> = (props) => {
9
- const { shouldUpdate, children } = props;
10
-
11
- if (!shouldUpdate) return <></>;
12
-
13
- return <>{children}</>;
14
- };