Skip to content

Commit 3ec353c

Browse files
authored
cherry pick swiper update (#963)
1 parent 3e3fe72 commit 3ec353c

File tree

2 files changed

+182
-115
lines changed

2 files changed

+182
-115
lines changed
Lines changed: 48 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import * as React from "react";
22
import { Image, Text, StyleSheet, View } from "react-native";
3-
import { Swiper, SwiperItem } from "@draftbit/ui";
3+
import { Swiper, SwiperItem, Button } from "@draftbit/ui";
44
import Section, { Container } from "./Section";
55

66
const style = StyleSheet.create({
@@ -10,8 +10,9 @@ const style = StyleSheet.create({
1010
},
1111
});
1212

13-
function SwiperExample({ theme }) {
13+
function SwiperExample() {
1414
const [data, setData] = React.useState();
15+
const swiperRef = React.useRef(null);
1516

1617
React.useEffect(() => {
1718
fetch("https://example-data.draftbit.com/properties?_limit=10")
@@ -22,7 +23,7 @@ function SwiperExample({ theme }) {
2223
}, []);
2324

2425
if (!data) {
25-
return <Text>"Loading..."</Text>;
26+
return <Text>Loading...</Text>;
2627
}
2728

2829
return (
@@ -64,7 +65,7 @@ function SwiperExample({ theme }) {
6465
<Text>Test Slide 2</Text>
6566
</SwiperItem>
6667
<SwiperItem style={[style.item, { backgroundColor: "#c9fdd9" }]}>
67-
<Text>Test Slide 2</Text>
68+
<Text>Test Slide 3</Text>
6869
</SwiperItem>
6970
</Swiper>
7071
</Section>
@@ -95,6 +96,49 @@ function SwiperExample({ theme }) {
9596
)}
9697
/>
9798
</Section>
99+
<Section title="Interaction Example">
100+
<Swiper
101+
ref={swiperRef}
102+
vertical={false}
103+
loop={true}
104+
style={{ width: "100%", height: 300 }}
105+
onSwipe={(index) => console.log("Swiped", index)}
106+
onSwipedNext={(index) => console.log("Swiped next", index)}
107+
onSwipedPrevious={(index) => console.log("Swiped previous", index)}
108+
onIndexChanged={(index) => console.log("onIndexChanged: ", index)}
109+
>
110+
<SwiperItem style={[style.item, { backgroundColor: "#fdd3d3" }]}>
111+
<Text>Test Slide 1</Text>
112+
</SwiperItem>
113+
<SwiperItem style={[style.item, { backgroundColor: "#d6d3fd" }]}>
114+
<Text>Test Slide 2</Text>
115+
</SwiperItem>
116+
<SwiperItem style={[style.item, { backgroundColor: "#c9fdd9" }]}>
117+
<Text>Test Slide 3</Text>
118+
</SwiperItem>
119+
</Swiper>
120+
121+
<View
122+
style={{
123+
flexDirection: "row",
124+
justifyContent: "space-around",
125+
marginTop: 10,
126+
}}
127+
>
128+
<Button
129+
title="Swipe Prev"
130+
onPress={() => swiperRef.current?.swipePrev()}
131+
/>
132+
<Button
133+
title="Swipe To 1"
134+
onPress={() => swiperRef.current?.swipeTo(1)}
135+
/>
136+
<Button
137+
title="Swipe Next"
138+
onPress={() => swiperRef.current?.swipeNext()}
139+
/>
140+
</View>
141+
</Section>
98142
</Container>
99143
);
100144
}
Lines changed: 134 additions & 111 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,14 @@
11
import { ReadTheme, withTheme } from "@draftbit/theme";
2-
import React from "react";
2+
import React, { forwardRef, useImperativeHandle } from "react";
33
import { View, StyleProp, ViewStyle } from "react-native";
44
import SwiperComponent from "react-native-web-swiper";
55

6+
export interface SwiperRef {
7+
swipeTo: (index: number) => void;
8+
swipeNext: () => void;
9+
swipePrev: () => void;
10+
}
11+
612
export interface SwiperProps<T> {
713
onSwipe?: (index: number) => void;
814
onSwipedNext?: (index: number) => void;
@@ -29,123 +35,140 @@ export interface SwiperProps<T> {
2935
theme: ReadTheme;
3036
}
3137

32-
const Swiper = ({
33-
theme,
34-
vertical = false,
35-
loop = false,
36-
timeout = 0,
37-
from = 0,
38-
prevTitle = "",
39-
nextTitle = "",
40-
prevTitleColor,
41-
nextTitleColor,
42-
dotsTouchable = true,
43-
dotColor = theme.colors.foreground.brand,
44-
dotActiveColor = theme.colors.branding.primary,
45-
data,
46-
keyExtractor,
47-
renderItem,
48-
children: childrenProp,
49-
onIndexChanged: onIndexChangedProp,
50-
onSwipe,
51-
onSwipedNext,
52-
onSwipedPrevious,
53-
minDistanceForAction,
54-
minDistanceToCapture,
55-
style,
56-
}: SwiperProps<any>) => {
57-
const [currentIndex, setCurrentIndex] = React.useState(0);
58-
const numberOfItems = data?.length ?? React.Children.count(childrenProp);
59-
const swiperRef = React.useRef<any>(null);
60-
61-
const onIndexChanged = (index: number) => {
62-
const previous = currentIndex;
63-
const current = index;
38+
const Swiper = forwardRef<SwiperRef, SwiperProps<any>>(
39+
(
40+
{
41+
theme,
42+
vertical = false,
43+
loop = false,
44+
timeout = 0,
45+
from = 0,
46+
prevTitle = "",
47+
nextTitle = "",
48+
prevTitleColor,
49+
nextTitleColor,
50+
dotsTouchable = true,
51+
dotColor = theme?.colors.foreground.brand,
52+
dotActiveColor = theme?.colors.branding.primary,
53+
data,
54+
keyExtractor,
55+
renderItem,
56+
children: childrenProp,
57+
onIndexChanged: onIndexChangedProp,
58+
onSwipe,
59+
onSwipedNext,
60+
onSwipedPrevious,
61+
minDistanceForAction,
62+
minDistanceToCapture,
63+
style,
64+
}: SwiperProps<any>,
65+
ref
66+
) => {
67+
const [currentIndex, setCurrentIndex] = React.useState(0);
68+
const numberOfItems = data?.length ?? React.Children.count(childrenProp);
69+
const swiperRef = React.useRef<any>(null);
6470

65-
onIndexChangedProp?.(index);
66-
setCurrentIndex(index);
71+
const onIndexChanged = (index: number) => {
72+
const previous = currentIndex;
73+
const current = index;
6774

68-
if (previous === numberOfItems - 1 && current === 0) {
69-
//Last -> first swipe
70-
onSwipedNext?.(previous);
71-
} else if (previous === 0 && current === numberOfItems - 1) {
72-
//First -> last swipe
73-
onSwipedPrevious?.(previous);
74-
} else if (current > previous) {
75-
onSwipedNext?.(previous);
76-
} else if (current < previous) {
77-
onSwipedPrevious?.(previous);
78-
}
79-
onSwipe?.(previous);
80-
};
75+
onIndexChangedProp?.(index);
76+
setCurrentIndex(index);
8177

82-
const children: React.ReactNode = React.useMemo(
83-
() =>
84-
Array.isArray(data) && renderItem
85-
? data.map((item, index) => {
86-
const component = renderItem({ item, index });
78+
if (previous === numberOfItems - 1 && current === 0) {
79+
//Last -> first swipe
80+
onSwipedNext?.(previous);
81+
} else if (previous === 0 && current === numberOfItems - 1) {
82+
//First -> last swipe
83+
onSwipedPrevious?.(previous);
84+
} else if (current > previous) {
85+
onSwipedNext?.(previous);
86+
} else if (current < previous) {
87+
onSwipedPrevious?.(previous);
88+
}
89+
onSwipe?.(previous);
90+
};
8791

88-
if (!component) {
89-
return null;
90-
}
92+
const children: React.ReactNode = React.useMemo(
93+
() =>
94+
Array.isArray(data) && renderItem
95+
? data.map((item, index) => {
96+
const component = renderItem({ item, index });
9197

92-
const key = keyExtractor ? keyExtractor(item, index) : index;
93-
return React.cloneElement(component, {
94-
key,
95-
});
96-
})
97-
: childrenProp,
98-
[childrenProp, data, renderItem, keyExtractor]
99-
);
98+
if (!component) {
99+
return null;
100+
}
100101

101-
/*
102-
react-native-web-swiper assigns it's 'children' attribute as follows: `children = (() => React.Children.toArray(this.props.children))();`
103-
This is probelematic when state is involved due to anoynmous function effectivley creating new components everytime, losing any state
104-
105-
This is a monkey patch that updates the 'children' attribute to just use the children from the props
106-
Can be removed when/if https://github.com/reactrondev/react-native-web-swiper/pull/102 is merged
107-
*/
108-
React.useEffect(() => {
109-
const childrenArray = React.Children.toArray(
110-
swiperRef.current?.props?.children
102+
const key = keyExtractor ? keyExtractor(item, index) : index;
103+
return React.cloneElement(component, {
104+
key,
105+
});
106+
})
107+
: childrenProp,
108+
[childrenProp, data, renderItem, keyExtractor]
111109
);
112-
if (swiperRef.current) {
113-
swiperRef.current.children = childrenArray;
114-
swiperRef.current.count = childrenArray.length;
115-
swiperRef.current.forceUpdate();
116-
}
117-
}, [children]);
118110

119-
return (
120-
<View style={style}>
121-
{/* @ts-ignore */}
122-
<SwiperComponent
123-
ref={swiperRef}
124-
from={from}
125-
loop={loop}
126-
timeout={timeout}
127-
vertical={vertical}
128-
onIndexChanged={onIndexChanged}
129-
minDistanceForAction={minDistanceForAction}
130-
minDistanceToCapture={minDistanceToCapture}
131-
controlsProps={{
132-
prevTitle,
133-
nextTitle,
134-
prevTitleStyle: { color: prevTitleColor },
135-
nextTitleStyle: { color: nextTitleColor },
136-
dotsTouchable,
137-
...(dotColor
138-
? { dotProps: { badgeStyle: { backgroundColor: dotColor } } }
139-
: {}),
140-
...(dotActiveColor
141-
? { dotActiveStyle: { backgroundColor: dotActiveColor } }
142-
: {}),
143-
}}
144-
>
145-
{children}
146-
</SwiperComponent>
147-
</View>
148-
);
149-
};
111+
/*
112+
react-native-web-swiper assigns it's 'children' attribute as follows: `children = (() => React.Children.toArray(this.props.children))();`
113+
This is probelematic when state is involved due to anoynmous function effectivley creating new components everytime, losing any state
114+
115+
This is a monkey patch that updates the 'children' attribute to just use the children from the props
116+
Can be removed when/if https://github.com/reactrondev/react-native-web-swiper/pull/102 is merged
117+
*/
118+
React.useEffect(() => {
119+
const childrenArray = React.Children.toArray(
120+
swiperRef.current?.props?.children
121+
);
122+
if (swiperRef.current) {
123+
swiperRef.current.children = childrenArray;
124+
swiperRef.current.count = childrenArray.length;
125+
swiperRef.current.forceUpdate();
126+
}
127+
}, [children]);
128+
129+
useImperativeHandle(ref, () => ({
130+
swipeTo: (index: number) => {
131+
swiperRef.current?.goTo(index);
132+
},
133+
swipeNext: () => {
134+
swiperRef.current?.goToNext();
135+
},
136+
swipePrev: () => {
137+
swiperRef.current?.goToPrev();
138+
},
139+
}));
140+
141+
return (
142+
<View style={style}>
143+
{/* @ts-ignore */}
144+
<SwiperComponent
145+
ref={swiperRef}
146+
from={from}
147+
loop={loop}
148+
timeout={timeout}
149+
vertical={vertical}
150+
onIndexChanged={onIndexChanged}
151+
minDistanceForAction={minDistanceForAction}
152+
minDistanceToCapture={minDistanceToCapture}
153+
controlsProps={{
154+
prevTitle,
155+
nextTitle,
156+
prevTitleStyle: { color: prevTitleColor },
157+
nextTitleStyle: { color: nextTitleColor },
158+
dotsTouchable,
159+
...(dotColor
160+
? { dotProps: { badgeStyle: { backgroundColor: dotColor } } }
161+
: {}),
162+
...(dotActiveColor
163+
? { dotActiveStyle: { backgroundColor: dotActiveColor } }
164+
: {}),
165+
}}
166+
>
167+
{children}
168+
</SwiperComponent>
169+
</View>
170+
);
171+
}
172+
);
150173

151174
export default withTheme(Swiper);

0 commit comments

Comments
 (0)