@momo-kits/carousel 0.165.2-beta.2 → 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.
- package/animation.ts +62 -0
- package/index.tsx +455 -390
- package/package.json +5 -6
- package/types.ts +48 -123
- package/core/LICENSE +0 -21
- package/core/README.md +0 -16
- package/core/components/Carousel.tsx +0 -28
- package/core/components/CarouselLayout.tsx +0 -231
- package/core/components/ItemLayout.tsx +0 -215
- package/core/components/ItemRenderer.tsx +0 -126
- package/core/components/LazyView.tsx +0 -14
- package/core/components/ScrollViewGesture.tsx +0 -443
- package/core/constants/index.ts +0 -11
- package/core/hooks/useAutoPlay.ts +0 -63
- package/core/hooks/useCarouselController.tsx +0 -295
- package/core/hooks/useCheckMounted.ts +0 -14
- package/core/hooks/useCommonVariables.ts +0 -261
- package/core/hooks/useInitProps.ts +0 -177
- package/core/hooks/useLayoutConfig.ts +0 -35
- package/core/hooks/useOffsetX.ts +0 -81
- package/core/hooks/useOnProgressChange.ts +0 -33
- package/core/hooks/usePanGestureProxy.ts +0 -144
- package/core/hooks/usePropsErrorBoundary.ts +0 -41
- package/core/hooks/useUpdateGestureConfig.ts +0 -17
- package/core/hooks/useVisibleRanges.tsx +0 -141
- package/core/layouts/index.tsx +0 -10
- package/core/layouts/normal.ts +0 -32
- package/core/layouts/parallax.ts +0 -50
- package/core/layouts/stack.ts +0 -266
- package/core/public-types.ts +0 -209
- package/core/store/index.tsx +0 -63
- package/core/types.ts +0 -32
- package/core/utils/carousel-direction.ts +0 -39
- package/core/utils/carousel-math.ts +0 -156
- package/core/utils/compute-gesture-translation.ts +0 -29
- package/core/utils/compute-offset-if-size-changed.ts +0 -14
- package/core/utils/computed-with-auto-fill-data.ts +0 -92
- package/core/utils/deal-with-animation.ts +0 -19
- package/core/utils/log.ts +0 -12
- package/core/utils/sanitize-animation-style.ts +0 -20
|
@@ -1,177 +0,0 @@
|
|
|
1
|
-
import React from "react";
|
|
2
|
-
import { I18nManager } from "react-native";
|
|
3
|
-
|
|
4
|
-
import { Easing } from "../constants";
|
|
5
|
-
import type { CarouselAnimation, CarouselProps } from "../types";
|
|
6
|
-
import { type CarouselDirectionSign, getCarouselDirectionSign } from "../utils/carousel-direction";
|
|
7
|
-
import { computedFillDataWithAutoFillData } from "../utils/computed-with-auto-fill-data";
|
|
8
|
-
|
|
9
|
-
type CarouselSnapMode = NonNullable<CarouselProps<unknown>["snapMode"]>;
|
|
10
|
-
|
|
11
|
-
export type InitializedCarouselProps<Item> = Omit<
|
|
12
|
-
CarouselProps<Item>,
|
|
13
|
-
| "autoplay"
|
|
14
|
-
| "autoplayDirection"
|
|
15
|
-
| "autoplayInterval"
|
|
16
|
-
| "data"
|
|
17
|
-
| "defaultIndex"
|
|
18
|
-
| "loop"
|
|
19
|
-
| "orientation"
|
|
20
|
-
| "overscrollEnabled"
|
|
21
|
-
| "scrollEnabled"
|
|
22
|
-
| "snapMode"
|
|
23
|
-
| "style"
|
|
24
|
-
> & {
|
|
25
|
-
autoplay: boolean;
|
|
26
|
-
autoplayDirection: "forward" | "backward";
|
|
27
|
-
autoplayInterval: number;
|
|
28
|
-
data: Item[];
|
|
29
|
-
defaultIndex: number;
|
|
30
|
-
loop: boolean;
|
|
31
|
-
orientation: "horizontal" | "vertical";
|
|
32
|
-
overscrollEnabled: boolean;
|
|
33
|
-
scrollEnabled: boolean;
|
|
34
|
-
snapMode: CarouselSnapMode;
|
|
35
|
-
style: NonNullable<CarouselProps<Item>["style"]>;
|
|
36
|
-
animation: CarouselAnimation;
|
|
37
|
-
rawData: Item[];
|
|
38
|
-
dataLength: number;
|
|
39
|
-
rawDataLength: number;
|
|
40
|
-
autoFillData: true;
|
|
41
|
-
directionSign: CarouselDirectionSign;
|
|
42
|
-
};
|
|
43
|
-
|
|
44
|
-
export function useInitProps<Item>(props: CarouselProps<Item>): InitializedCarouselProps<Item> {
|
|
45
|
-
validateStaticProps(props);
|
|
46
|
-
|
|
47
|
-
const rawData = props.data ?? [];
|
|
48
|
-
const requestedDefaultIndex = props.defaultIndex ?? 0;
|
|
49
|
-
const loop = props.loop ?? false;
|
|
50
|
-
const autoplay = props.autoplay ?? false;
|
|
51
|
-
const autoplayInterval = props.autoplayInterval ?? 3000;
|
|
52
|
-
const autoplayDirection = props.autoplayDirection ?? "forward";
|
|
53
|
-
const orientation = props.orientation ?? "horizontal";
|
|
54
|
-
const directionSign = getCarouselDirectionSign({
|
|
55
|
-
isRTL: I18nManager.isRTL,
|
|
56
|
-
orientation,
|
|
57
|
-
});
|
|
58
|
-
const scrollEnabled = props.scrollEnabled ?? true;
|
|
59
|
-
const snapMode = props.snapMode ?? "page";
|
|
60
|
-
const overscrollEnabled = props.overscrollEnabled ?? true;
|
|
61
|
-
const style = props.style ?? {};
|
|
62
|
-
const animation: CarouselAnimation = props.animation ?? {
|
|
63
|
-
type: "timing",
|
|
64
|
-
duration: 500,
|
|
65
|
-
easing: Easing.easeOutQuart,
|
|
66
|
-
};
|
|
67
|
-
|
|
68
|
-
const defaultIndexState = React.useRef<{
|
|
69
|
-
consumed: boolean;
|
|
70
|
-
requested: number;
|
|
71
|
-
resolved: number;
|
|
72
|
-
warned: boolean;
|
|
73
|
-
} | null>(null);
|
|
74
|
-
|
|
75
|
-
if (!defaultIndexState.current) {
|
|
76
|
-
if (rawData.length > 0) {
|
|
77
|
-
assertValidDefaultIndex(requestedDefaultIndex, rawData.length);
|
|
78
|
-
}
|
|
79
|
-
|
|
80
|
-
defaultIndexState.current = {
|
|
81
|
-
consumed: rawData.length > 0,
|
|
82
|
-
requested: requestedDefaultIndex,
|
|
83
|
-
resolved: rawData.length > 0 ? requestedDefaultIndex : 0,
|
|
84
|
-
warned: false,
|
|
85
|
-
};
|
|
86
|
-
} else if (!defaultIndexState.current.consumed && rawData.length > 0) {
|
|
87
|
-
const pendingIndex = defaultIndexState.current.requested;
|
|
88
|
-
defaultIndexState.current.consumed = true;
|
|
89
|
-
|
|
90
|
-
if (isValidDefaultIndex(pendingIndex, rawData.length)) {
|
|
91
|
-
defaultIndexState.current.resolved = pendingIndex;
|
|
92
|
-
} else {
|
|
93
|
-
defaultIndexState.current.resolved = 0;
|
|
94
|
-
if (__DEV__ && !defaultIndexState.current.warned) {
|
|
95
|
-
console.warn(
|
|
96
|
-
`[react-native-reanimated-carousel] Ignored defaultIndex ${pendingIndex} because it is outside the first non-empty data set.`
|
|
97
|
-
);
|
|
98
|
-
defaultIndexState.current.warned = true;
|
|
99
|
-
}
|
|
100
|
-
}
|
|
101
|
-
}
|
|
102
|
-
|
|
103
|
-
const defaultIndex = defaultIndexState.current.resolved;
|
|
104
|
-
const data = React.useMemo(
|
|
105
|
-
() =>
|
|
106
|
-
computedFillDataWithAutoFillData({
|
|
107
|
-
loop,
|
|
108
|
-
autoFillData: true,
|
|
109
|
-
data: rawData,
|
|
110
|
-
dataLength: rawData.length,
|
|
111
|
-
}),
|
|
112
|
-
[loop, rawData]
|
|
113
|
-
);
|
|
114
|
-
|
|
115
|
-
return {
|
|
116
|
-
...props,
|
|
117
|
-
autoplay,
|
|
118
|
-
autoplayDirection,
|
|
119
|
-
autoplayInterval,
|
|
120
|
-
defaultIndex,
|
|
121
|
-
loop,
|
|
122
|
-
orientation,
|
|
123
|
-
overscrollEnabled,
|
|
124
|
-
scrollEnabled,
|
|
125
|
-
snapMode,
|
|
126
|
-
style,
|
|
127
|
-
animation,
|
|
128
|
-
data,
|
|
129
|
-
rawData,
|
|
130
|
-
dataLength: data.length,
|
|
131
|
-
rawDataLength: rawData.length,
|
|
132
|
-
autoFillData: true,
|
|
133
|
-
directionSign,
|
|
134
|
-
};
|
|
135
|
-
}
|
|
136
|
-
|
|
137
|
-
function validateStaticProps<Item>(props: CarouselProps<Item>) {
|
|
138
|
-
if (props.layout && props.itemAnimation) {
|
|
139
|
-
throw new Error(
|
|
140
|
-
"[react-native-reanimated-carousel] `layout` and `itemAnimation` are mutually exclusive."
|
|
141
|
-
);
|
|
142
|
-
}
|
|
143
|
-
|
|
144
|
-
if (props.itemSize !== undefined && (!Number.isFinite(props.itemSize) || props.itemSize <= 0)) {
|
|
145
|
-
throw new Error("[react-native-reanimated-carousel] itemSize must be a positive number.");
|
|
146
|
-
}
|
|
147
|
-
|
|
148
|
-
if (
|
|
149
|
-
props.autoplayInterval !== undefined &&
|
|
150
|
-
(!Number.isFinite(props.autoplayInterval) || props.autoplayInterval < 0)
|
|
151
|
-
) {
|
|
152
|
-
throw new Error(
|
|
153
|
-
"[react-native-reanimated-carousel] autoplayInterval must be a non-negative number."
|
|
154
|
-
);
|
|
155
|
-
}
|
|
156
|
-
|
|
157
|
-
if (
|
|
158
|
-
props.renderWindowSize !== undefined &&
|
|
159
|
-
(!Number.isInteger(props.renderWindowSize) || props.renderWindowSize <= 0)
|
|
160
|
-
) {
|
|
161
|
-
throw new Error(
|
|
162
|
-
"[react-native-reanimated-carousel] renderWindowSize must be a positive integer."
|
|
163
|
-
);
|
|
164
|
-
}
|
|
165
|
-
}
|
|
166
|
-
|
|
167
|
-
function isValidDefaultIndex(defaultIndex: number, dataLength: number) {
|
|
168
|
-
return Number.isInteger(defaultIndex) && defaultIndex >= 0 && defaultIndex < dataLength;
|
|
169
|
-
}
|
|
170
|
-
|
|
171
|
-
function assertValidDefaultIndex(defaultIndex: number, dataLength: number) {
|
|
172
|
-
if (!isValidDefaultIndex(defaultIndex, dataLength)) {
|
|
173
|
-
throw new Error(
|
|
174
|
-
`[react-native-reanimated-carousel] defaultIndex must be an integer between 0 and ${dataLength - 1}.`
|
|
175
|
-
);
|
|
176
|
-
}
|
|
177
|
-
}
|
|
@@ -1,35 +0,0 @@
|
|
|
1
|
-
import React from "react";
|
|
2
|
-
|
|
3
|
-
import { Layouts } from "../layouts";
|
|
4
|
-
import type { CarouselItemAnimation } from "../types";
|
|
5
|
-
import type { InitializedCarouselProps } from "./useInitProps";
|
|
6
|
-
|
|
7
|
-
type LayoutConfigOptions<Item> = InitializedCarouselProps<Item> & { size: number };
|
|
8
|
-
|
|
9
|
-
export function useLayoutConfig<Item>(options: LayoutConfigOptions<Item>): CarouselItemAnimation {
|
|
10
|
-
const { dataLength, directionSign, layout, orientation, size } = options;
|
|
11
|
-
const isVertical = orientation === "vertical";
|
|
12
|
-
|
|
13
|
-
return React.useMemo(() => {
|
|
14
|
-
const baseConfig = { size, isVertical, directionSign };
|
|
15
|
-
switch (layout?.type) {
|
|
16
|
-
case "parallax":
|
|
17
|
-
return Layouts.parallax(baseConfig, layout);
|
|
18
|
-
case "horizontal-stack":
|
|
19
|
-
return Layouts.horizontalStack(
|
|
20
|
-
{
|
|
21
|
-
...layout,
|
|
22
|
-
visibleCount: layout.visibleCount ?? Math.max(0, dataLength - 1),
|
|
23
|
-
},
|
|
24
|
-
directionSign
|
|
25
|
-
);
|
|
26
|
-
case "vertical-stack":
|
|
27
|
-
return Layouts.verticalStack({
|
|
28
|
-
...layout,
|
|
29
|
-
visibleCount: layout.visibleCount ?? Math.max(0, dataLength - 1),
|
|
30
|
-
});
|
|
31
|
-
default:
|
|
32
|
-
return Layouts.normal(baseConfig);
|
|
33
|
-
}
|
|
34
|
-
}, [dataLength, directionSign, isVertical, layout, size]);
|
|
35
|
-
}
|
package/core/hooks/useOffsetX.ts
DELETED
|
@@ -1,81 +0,0 @@
|
|
|
1
|
-
import type { SharedValue } from "react-native-reanimated";
|
|
2
|
-
import { Extrapolation, interpolate, useDerivedValue } from "react-native-reanimated";
|
|
3
|
-
|
|
4
|
-
import type { VisibleRangesValue } from "./useVisibleRanges";
|
|
5
|
-
|
|
6
|
-
export interface OffsetOptions {
|
|
7
|
-
index: number;
|
|
8
|
-
size: number;
|
|
9
|
-
handlerOffset: SharedValue<number>;
|
|
10
|
-
dataLength: number;
|
|
11
|
-
type?: "positive" | "negative" | undefined;
|
|
12
|
-
viewCount?: number | undefined;
|
|
13
|
-
loop?: boolean | undefined;
|
|
14
|
-
}
|
|
15
|
-
|
|
16
|
-
export const useOffsetX = (opts: OffsetOptions, visibleRanges: VisibleRangesValue) => {
|
|
17
|
-
const {
|
|
18
|
-
handlerOffset,
|
|
19
|
-
index,
|
|
20
|
-
size,
|
|
21
|
-
loop,
|
|
22
|
-
dataLength,
|
|
23
|
-
type = "positive",
|
|
24
|
-
viewCount: _viewCount,
|
|
25
|
-
} = opts;
|
|
26
|
-
|
|
27
|
-
const ITEM_LENGTH = dataLength;
|
|
28
|
-
const VALID_LENGTH = ITEM_LENGTH - 1;
|
|
29
|
-
const TOTAL_WIDTH = size * ITEM_LENGTH;
|
|
30
|
-
const HALF_WIDTH = 0.5 * size;
|
|
31
|
-
|
|
32
|
-
const viewCount = _viewCount ?? Math.round((ITEM_LENGTH - 1) / 2);
|
|
33
|
-
const positiveCount = type === "positive" ? viewCount : VALID_LENGTH - viewCount;
|
|
34
|
-
|
|
35
|
-
let startPos = size * index;
|
|
36
|
-
if (index > positiveCount) startPos = (index - ITEM_LENGTH) * size;
|
|
37
|
-
|
|
38
|
-
const MAX = positiveCount * size;
|
|
39
|
-
const MIN = -((VALID_LENGTH - positiveCount) * size);
|
|
40
|
-
|
|
41
|
-
const x = useDerivedValue(() => {
|
|
42
|
-
if (!size) return 0;
|
|
43
|
-
|
|
44
|
-
const { negativeRange, positiveRange } = visibleRanges.value;
|
|
45
|
-
|
|
46
|
-
if (
|
|
47
|
-
(index >= negativeRange[0] && index <= negativeRange[1]) ||
|
|
48
|
-
(index >= positiveRange[0] && index <= positiveRange[1])
|
|
49
|
-
) {
|
|
50
|
-
if (loop) {
|
|
51
|
-
const inputRange = [
|
|
52
|
-
-TOTAL_WIDTH,
|
|
53
|
-
MIN - HALF_WIDTH - startPos - Number.MIN_VALUE,
|
|
54
|
-
MIN - HALF_WIDTH - startPos,
|
|
55
|
-
0,
|
|
56
|
-
MAX + HALF_WIDTH - startPos,
|
|
57
|
-
MAX + HALF_WIDTH - startPos + Number.MIN_VALUE,
|
|
58
|
-
TOTAL_WIDTH,
|
|
59
|
-
];
|
|
60
|
-
|
|
61
|
-
const outputRange = [
|
|
62
|
-
startPos,
|
|
63
|
-
MAX + HALF_WIDTH - Number.MIN_VALUE,
|
|
64
|
-
MIN - HALF_WIDTH,
|
|
65
|
-
startPos,
|
|
66
|
-
MAX + HALF_WIDTH,
|
|
67
|
-
MIN - HALF_WIDTH + Number.MIN_VALUE,
|
|
68
|
-
startPos,
|
|
69
|
-
];
|
|
70
|
-
|
|
71
|
-
return interpolate(handlerOffset.value, inputRange, outputRange, Extrapolation.CLAMP);
|
|
72
|
-
}
|
|
73
|
-
|
|
74
|
-
return handlerOffset.value + size * index;
|
|
75
|
-
}
|
|
76
|
-
|
|
77
|
-
return Number.MAX_SAFE_INTEGER;
|
|
78
|
-
}, [loop, dataLength, viewCount, type, size, visibleRanges, handlerOffset]);
|
|
79
|
-
|
|
80
|
-
return x;
|
|
81
|
-
};
|
|
@@ -1,33 +0,0 @@
|
|
|
1
|
-
import type { SharedValue } from "react-native-reanimated";
|
|
2
|
-
import { useAnimatedReaction } from "react-native-reanimated";
|
|
3
|
-
import { scheduleOnRN } from "react-native-worklets";
|
|
4
|
-
|
|
5
|
-
import type { CarouselProgressChangeHandler } from "../types";
|
|
6
|
-
import { getLogicalProgress } from "../utils/carousel-math";
|
|
7
|
-
|
|
8
|
-
export function useOnProgressChange(options: {
|
|
9
|
-
loop: boolean;
|
|
10
|
-
offset: SharedValue<number>;
|
|
11
|
-
onProgressChange?: CarouselProgressChangeHandler | undefined;
|
|
12
|
-
progress?: SharedValue<number> | undefined;
|
|
13
|
-
rawDataLength: number;
|
|
14
|
-
size: number;
|
|
15
|
-
sizeReady: SharedValue<boolean>;
|
|
16
|
-
}) {
|
|
17
|
-
const { loop, offset, onProgressChange, progress, rawDataLength, size, sizeReady } = options;
|
|
18
|
-
|
|
19
|
-
useAnimatedReaction(
|
|
20
|
-
() => {
|
|
21
|
-
if (!sizeReady.value || size <= 0 || rawDataLength <= 0) return null;
|
|
22
|
-
|
|
23
|
-
const logicalProgress = getLogicalProgress(offset.value, size);
|
|
24
|
-
return loop ? logicalProgress : Math.max(0, Math.min(rawDataLength - 1, logicalProgress));
|
|
25
|
-
},
|
|
26
|
-
(nextProgress) => {
|
|
27
|
-
if (nextProgress == null) return;
|
|
28
|
-
if (progress) progress.value = nextProgress;
|
|
29
|
-
if (onProgressChange) scheduleOnRN(onProgressChange, nextProgress);
|
|
30
|
-
},
|
|
31
|
-
[loop, offset, onProgressChange, progress, rawDataLength, size, sizeReady]
|
|
32
|
-
);
|
|
33
|
-
}
|
|
@@ -1,144 +0,0 @@
|
|
|
1
|
-
import { useMemo } from "react";
|
|
2
|
-
import type {
|
|
3
|
-
GestureStateChangeEvent,
|
|
4
|
-
GestureUpdateEvent,
|
|
5
|
-
PanGestureHandlerEventPayload,
|
|
6
|
-
} from "react-native-gesture-handler";
|
|
7
|
-
import { Gesture } from "react-native-gesture-handler";
|
|
8
|
-
|
|
9
|
-
import type { CarouselPanGesture } from "../types";
|
|
10
|
-
import type { GestureConfig } from "./useUpdateGestureConfig";
|
|
11
|
-
import { useUpdateGestureConfig } from "./useUpdateGestureConfig";
|
|
12
|
-
|
|
13
|
-
export const usePanGestureProxy = (customization: {
|
|
14
|
-
onConfigurePanGesture?: ((gesture: CarouselPanGesture) => void) | undefined;
|
|
15
|
-
onGestureStart: (event: GestureStateChangeEvent<PanGestureHandlerEventPayload>) => void;
|
|
16
|
-
onGestureUpdate: (event: GestureUpdateEvent<PanGestureHandlerEventPayload>) => void;
|
|
17
|
-
onGestureEnd: (
|
|
18
|
-
event: GestureStateChangeEvent<PanGestureHandlerEventPayload>,
|
|
19
|
-
success: boolean
|
|
20
|
-
) => void;
|
|
21
|
-
options?: GestureConfig | undefined;
|
|
22
|
-
}) => {
|
|
23
|
-
const {
|
|
24
|
-
onConfigurePanGesture,
|
|
25
|
-
onGestureStart,
|
|
26
|
-
onGestureUpdate,
|
|
27
|
-
onGestureEnd,
|
|
28
|
-
options = {},
|
|
29
|
-
} = customization;
|
|
30
|
-
|
|
31
|
-
const gesture = useMemo(() => {
|
|
32
|
-
const gesture = Gesture.Pan().withTestId("rnrc-gesture-handler");
|
|
33
|
-
|
|
34
|
-
// Save the original gesture callbacks
|
|
35
|
-
const originalGestures = {
|
|
36
|
-
onBegin: gesture.onBegin,
|
|
37
|
-
onStart: gesture.onStart,
|
|
38
|
-
onUpdate: gesture.onUpdate,
|
|
39
|
-
onEnd: gesture.onEnd,
|
|
40
|
-
onFinalize: gesture.onFinalize,
|
|
41
|
-
};
|
|
42
|
-
|
|
43
|
-
// Save the user defined gesture callbacks
|
|
44
|
-
const userDefinedConflictGestures: {
|
|
45
|
-
onBegin: Parameters<(typeof gesture)["onBegin"]>[0] | undefined;
|
|
46
|
-
onStart: Parameters<(typeof gesture)["onStart"]>[0] | undefined;
|
|
47
|
-
onUpdate: Parameters<(typeof gesture)["onUpdate"]>[0] | undefined;
|
|
48
|
-
onEnd: Parameters<(typeof gesture)["onEnd"]>[0] | undefined;
|
|
49
|
-
onFinalize: Parameters<(typeof gesture)["onFinalize"]>[0] | undefined;
|
|
50
|
-
} = {
|
|
51
|
-
onBegin: undefined,
|
|
52
|
-
onStart: undefined,
|
|
53
|
-
onUpdate: undefined,
|
|
54
|
-
onEnd: undefined,
|
|
55
|
-
onFinalize: undefined,
|
|
56
|
-
};
|
|
57
|
-
|
|
58
|
-
const fakeOnBegin: typeof gesture.onBegin = (cb) => {
|
|
59
|
-
// Using fakeOnBegin to save the user defined callback
|
|
60
|
-
userDefinedConflictGestures.onBegin = cb;
|
|
61
|
-
return gesture;
|
|
62
|
-
};
|
|
63
|
-
|
|
64
|
-
const fakeOnStart: typeof gesture.onStart = (cb) => {
|
|
65
|
-
// Using fakeOnStart to save the user defined callback
|
|
66
|
-
userDefinedConflictGestures.onStart = cb;
|
|
67
|
-
return gesture;
|
|
68
|
-
};
|
|
69
|
-
|
|
70
|
-
const fakeOnUpdate: typeof gesture.onUpdate = (cb) => {
|
|
71
|
-
// Using fakeOnUpdate to save the user defined callback
|
|
72
|
-
userDefinedConflictGestures.onUpdate = cb;
|
|
73
|
-
return gesture;
|
|
74
|
-
};
|
|
75
|
-
|
|
76
|
-
const fakeOnEnd: typeof gesture.onEnd = (cb) => {
|
|
77
|
-
// Using fakeOnEnd to save the user defined callback
|
|
78
|
-
userDefinedConflictGestures.onEnd = cb;
|
|
79
|
-
return gesture;
|
|
80
|
-
};
|
|
81
|
-
|
|
82
|
-
const fakeOnFinalize: typeof gesture.onFinalize = (cb) => {
|
|
83
|
-
// Using fakeOnFinalize to save the user defined callback
|
|
84
|
-
userDefinedConflictGestures.onFinalize = cb;
|
|
85
|
-
return gesture;
|
|
86
|
-
};
|
|
87
|
-
|
|
88
|
-
// Setup the fake callbacks
|
|
89
|
-
gesture.onBegin = fakeOnBegin;
|
|
90
|
-
gesture.onStart = fakeOnStart;
|
|
91
|
-
gesture.onUpdate = fakeOnUpdate;
|
|
92
|
-
gesture.onEnd = fakeOnEnd;
|
|
93
|
-
gesture.onFinalize = fakeOnFinalize;
|
|
94
|
-
|
|
95
|
-
if (onConfigurePanGesture)
|
|
96
|
-
// Get the gesture with the user defined configuration
|
|
97
|
-
onConfigurePanGesture(gesture);
|
|
98
|
-
|
|
99
|
-
// Restore the original callbacks
|
|
100
|
-
gesture.onBegin = originalGestures.onBegin;
|
|
101
|
-
gesture.onStart = originalGestures.onStart;
|
|
102
|
-
gesture.onUpdate = originalGestures.onUpdate;
|
|
103
|
-
gesture.onEnd = originalGestures.onEnd;
|
|
104
|
-
gesture.onFinalize = originalGestures.onFinalize;
|
|
105
|
-
|
|
106
|
-
// Setup the original callbacks with the user defined callbacks
|
|
107
|
-
gesture
|
|
108
|
-
.onBegin((e) => {
|
|
109
|
-
"worklet";
|
|
110
|
-
|
|
111
|
-
if (userDefinedConflictGestures.onBegin) userDefinedConflictGestures.onBegin(e);
|
|
112
|
-
})
|
|
113
|
-
.onStart((e) => {
|
|
114
|
-
"worklet";
|
|
115
|
-
onGestureStart(e);
|
|
116
|
-
|
|
117
|
-
if (userDefinedConflictGestures.onStart) userDefinedConflictGestures.onStart(e);
|
|
118
|
-
})
|
|
119
|
-
.onUpdate((e) => {
|
|
120
|
-
"worklet";
|
|
121
|
-
onGestureUpdate(e);
|
|
122
|
-
|
|
123
|
-
if (userDefinedConflictGestures.onUpdate) userDefinedConflictGestures.onUpdate(e);
|
|
124
|
-
})
|
|
125
|
-
.onEnd((e, success) => {
|
|
126
|
-
"worklet";
|
|
127
|
-
onGestureEnd(e, success);
|
|
128
|
-
|
|
129
|
-
if (userDefinedConflictGestures.onEnd) userDefinedConflictGestures.onEnd(e, success);
|
|
130
|
-
})
|
|
131
|
-
.onFinalize((e, success) => {
|
|
132
|
-
"worklet";
|
|
133
|
-
|
|
134
|
-
if (userDefinedConflictGestures.onFinalize)
|
|
135
|
-
userDefinedConflictGestures.onFinalize(e, success);
|
|
136
|
-
});
|
|
137
|
-
|
|
138
|
-
return gesture;
|
|
139
|
-
}, [onGestureStart, onGestureUpdate, onGestureEnd, onConfigurePanGesture]);
|
|
140
|
-
|
|
141
|
-
useUpdateGestureConfig(gesture, options);
|
|
142
|
-
|
|
143
|
-
return gesture;
|
|
144
|
-
};
|
|
@@ -1,41 +0,0 @@
|
|
|
1
|
-
import React from "react";
|
|
2
|
-
import { type StyleProp, StyleSheet, type ViewStyle } from "react-native";
|
|
3
|
-
|
|
4
|
-
import type { InitializedCarouselProps } from "./useInitProps";
|
|
5
|
-
|
|
6
|
-
export function usePropsErrorBoundary(props: InitializedCarouselProps<unknown>) {
|
|
7
|
-
const warned = React.useRef({
|
|
8
|
-
contentStyle: false,
|
|
9
|
-
missingMainAxisSize: false,
|
|
10
|
-
});
|
|
11
|
-
|
|
12
|
-
React.useEffect(() => {
|
|
13
|
-
if (!__DEV__) return;
|
|
14
|
-
|
|
15
|
-
const { contentContainerStyle, orientation, style } = props;
|
|
16
|
-
const flattenedContentStyle =
|
|
17
|
-
StyleSheet.flatten(contentContainerStyle as StyleProp<ViewStyle>) || {};
|
|
18
|
-
if (
|
|
19
|
-
(flattenedContentStyle.opacity !== undefined ||
|
|
20
|
-
flattenedContentStyle.transform !== undefined) &&
|
|
21
|
-
!warned.current.contentStyle
|
|
22
|
-
) {
|
|
23
|
-
console.warn(
|
|
24
|
-
"[react-native-reanimated-carousel] `contentContainerStyle` cannot set `opacity` or `transform`; those fields are owned by Carousel and were ignored."
|
|
25
|
-
);
|
|
26
|
-
warned.current.contentStyle = true;
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
const flattenedStyle = StyleSheet.flatten(style) || {};
|
|
30
|
-
const mainAxisSize = orientation === "vertical" ? flattenedStyle.height : flattenedStyle.width;
|
|
31
|
-
const hasFlexibleSize =
|
|
32
|
-
(typeof flattenedStyle.flex === "number" && flattenedStyle.flex > 0) ||
|
|
33
|
-
mainAxisSize !== undefined;
|
|
34
|
-
if (!hasFlexibleSize && !warned.current.missingMainAxisSize) {
|
|
35
|
-
console.warn(
|
|
36
|
-
`[react-native-reanimated-carousel] ${orientation} Carousel has no main-axis size in \`style\`; automatic layout measurement will be used.`
|
|
37
|
-
);
|
|
38
|
-
warned.current.missingMainAxisSize = true;
|
|
39
|
-
}
|
|
40
|
-
}, [props]);
|
|
41
|
-
}
|
|
@@ -1,17 +0,0 @@
|
|
|
1
|
-
import { useEffect } from "react";
|
|
2
|
-
|
|
3
|
-
export interface GestureConfig {
|
|
4
|
-
enabled?: boolean | undefined;
|
|
5
|
-
}
|
|
6
|
-
|
|
7
|
-
type ConfigurablePanGesture = {
|
|
8
|
-
enabled(enabled: boolean): unknown;
|
|
9
|
-
};
|
|
10
|
-
|
|
11
|
-
export const useUpdateGestureConfig = (gesture: ConfigurablePanGesture, config: GestureConfig) => {
|
|
12
|
-
const { enabled } = config;
|
|
13
|
-
|
|
14
|
-
useEffect(() => {
|
|
15
|
-
if (typeof enabled !== "undefined") gesture.enabled(enabled);
|
|
16
|
-
}, [enabled, gesture]);
|
|
17
|
-
};
|
|
@@ -1,141 +0,0 @@
|
|
|
1
|
-
import { useRef } from "react";
|
|
2
|
-
import type { SharedValue } from "react-native-reanimated";
|
|
3
|
-
import { useDerivedValue } from "react-native-reanimated";
|
|
4
|
-
|
|
5
|
-
type Range = [number, number];
|
|
6
|
-
|
|
7
|
-
export interface VisibleRanges {
|
|
8
|
-
negativeRange: Range;
|
|
9
|
-
positiveRange: Range;
|
|
10
|
-
}
|
|
11
|
-
|
|
12
|
-
export type VisibleRangesValue = SharedValue<VisibleRanges>;
|
|
13
|
-
|
|
14
|
-
function normalizeWindowSize(total: number, windowSize?: number) {
|
|
15
|
-
"worklet";
|
|
16
|
-
|
|
17
|
-
return typeof windowSize === "number" && Number.isFinite(windowSize) && windowSize > 0
|
|
18
|
-
? windowSize
|
|
19
|
-
: total;
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
function normalizeLoopIndex(currentIndex: number, total: number) {
|
|
23
|
-
"worklet";
|
|
24
|
-
|
|
25
|
-
return currentIndex < 0 ? (currentIndex % total) + total : currentIndex;
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
export function computeVisibleRanges(params: {
|
|
29
|
-
total: number;
|
|
30
|
-
windowSize?: number | undefined;
|
|
31
|
-
currentIndex: number;
|
|
32
|
-
loop?: boolean | undefined;
|
|
33
|
-
}): VisibleRanges {
|
|
34
|
-
"worklet";
|
|
35
|
-
|
|
36
|
-
const { total = 0, loop } = params;
|
|
37
|
-
const windowSize = normalizeWindowSize(total, params.windowSize);
|
|
38
|
-
|
|
39
|
-
if (total <= 0) {
|
|
40
|
-
return {
|
|
41
|
-
negativeRange: [0, 0],
|
|
42
|
-
positiveRange: [0, -1],
|
|
43
|
-
};
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
if (loop && windowSize >= total) {
|
|
47
|
-
return {
|
|
48
|
-
negativeRange: [0, 0],
|
|
49
|
-
positiveRange: [0, total - 1],
|
|
50
|
-
};
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
const positiveCount = Math.round(windowSize / 2);
|
|
54
|
-
const negativeCount = windowSize - positiveCount;
|
|
55
|
-
|
|
56
|
-
let currentIndex = params.currentIndex;
|
|
57
|
-
if (!Number.isFinite(currentIndex)) currentIndex = 0;
|
|
58
|
-
|
|
59
|
-
if (!loop) {
|
|
60
|
-
currentIndex = Math.max(0, Math.min(total - 1, currentIndex));
|
|
61
|
-
return {
|
|
62
|
-
negativeRange: [Math.max(0, currentIndex - (windowSize - 1)), currentIndex],
|
|
63
|
-
positiveRange: [currentIndex, Math.min(total - 1, currentIndex + (windowSize - 1))],
|
|
64
|
-
};
|
|
65
|
-
}
|
|
66
|
-
|
|
67
|
-
currentIndex = normalizeLoopIndex(currentIndex, total);
|
|
68
|
-
|
|
69
|
-
const negativeRange: Range = [
|
|
70
|
-
(currentIndex - negativeCount + total) % total,
|
|
71
|
-
(currentIndex - 1 + total) % total,
|
|
72
|
-
];
|
|
73
|
-
|
|
74
|
-
const positiveRange: Range = [
|
|
75
|
-
(currentIndex + total) % total,
|
|
76
|
-
(currentIndex + positiveCount + total) % total,
|
|
77
|
-
];
|
|
78
|
-
|
|
79
|
-
if (negativeRange[0] < total && negativeRange[0] > negativeRange[1]) {
|
|
80
|
-
negativeRange[1] = total - 1;
|
|
81
|
-
positiveRange[0] = 0;
|
|
82
|
-
}
|
|
83
|
-
if (positiveRange[0] > positiveRange[1]) {
|
|
84
|
-
negativeRange[1] = total - 1;
|
|
85
|
-
positiveRange[0] = 0;
|
|
86
|
-
}
|
|
87
|
-
|
|
88
|
-
return { negativeRange, positiveRange };
|
|
89
|
-
}
|
|
90
|
-
|
|
91
|
-
export function useVisibleRanges(options: {
|
|
92
|
-
total: number;
|
|
93
|
-
viewSize: number;
|
|
94
|
-
windowSize?: number | undefined;
|
|
95
|
-
translation: SharedValue<number>;
|
|
96
|
-
loop?: boolean | undefined;
|
|
97
|
-
}): VisibleRangesValue {
|
|
98
|
-
const { total = 0, viewSize, translation, windowSize: _windowSize, loop } = options;
|
|
99
|
-
|
|
100
|
-
const windowSize = normalizeWindowSize(total, _windowSize);
|
|
101
|
-
const cachedRanges = useRef<VisibleRanges | null>(null);
|
|
102
|
-
|
|
103
|
-
const ranges = useDerivedValue(() => {
|
|
104
|
-
if (total <= 0) {
|
|
105
|
-
return computeVisibleRanges({ total, currentIndex: 0, windowSize, loop });
|
|
106
|
-
}
|
|
107
|
-
|
|
108
|
-
// Prevent division by zero when viewSize is not yet measured
|
|
109
|
-
if (!Number.isFinite(viewSize) || viewSize <= 0) {
|
|
110
|
-
return {
|
|
111
|
-
negativeRange: [0, 0] as Range,
|
|
112
|
-
positiveRange: [0, Math.min(total - 1, windowSize - 1)] as Range,
|
|
113
|
-
};
|
|
114
|
-
}
|
|
115
|
-
|
|
116
|
-
let currentIndex = Math.round(-translation.value / viewSize);
|
|
117
|
-
if (!Number.isFinite(currentIndex)) currentIndex = 0;
|
|
118
|
-
|
|
119
|
-
const newRanges = computeVisibleRanges({ total, windowSize, currentIndex, loop });
|
|
120
|
-
|
|
121
|
-
if (
|
|
122
|
-
cachedRanges.current &&
|
|
123
|
-
isArraysEqual(cachedRanges.current.negativeRange, newRanges.negativeRange) &&
|
|
124
|
-
isArraysEqual(cachedRanges.current.positiveRange, newRanges.positiveRange)
|
|
125
|
-
) {
|
|
126
|
-
return cachedRanges.current;
|
|
127
|
-
}
|
|
128
|
-
|
|
129
|
-
cachedRanges.current = newRanges;
|
|
130
|
-
return newRanges;
|
|
131
|
-
}, [loop, total, windowSize, translation, viewSize]);
|
|
132
|
-
|
|
133
|
-
return ranges;
|
|
134
|
-
}
|
|
135
|
-
|
|
136
|
-
function isArraysEqual(a: number[], b: number[]): boolean {
|
|
137
|
-
"worklet";
|
|
138
|
-
if (a.length !== b.length) return false;
|
|
139
|
-
|
|
140
|
-
return a.every((value, index) => value === b[index]);
|
|
141
|
-
}
|
package/core/layouts/index.tsx
DELETED
|
@@ -1,10 +0,0 @@
|
|
|
1
|
-
import { normalLayout } from "./normal";
|
|
2
|
-
import { parallaxLayout } from "./parallax";
|
|
3
|
-
import { horizontalStackLayout, verticalStackLayout } from "./stack";
|
|
4
|
-
|
|
5
|
-
export const Layouts = {
|
|
6
|
-
normal: normalLayout,
|
|
7
|
-
parallax: parallaxLayout,
|
|
8
|
-
horizontalStack: horizontalStackLayout,
|
|
9
|
-
verticalStack: verticalStackLayout,
|
|
10
|
-
};
|