@@ -2,6 +2,10 @@ import React from "react";
22import { StyleProp , ViewStyle , StyleSheet , View } from "react-native" ;
33import DeckSwiperComponent from "react-native-deck-swiper" ;
44
5+ export interface DeckSwiperRef {
6+ swipeCard : ( direction : "left" | "right" | "top" | "bottom" ) => void ;
7+ }
8+
59export interface DeckSwiperProps < T > {
610 onStartSwipe ?: ( ) => void ;
711 onEndSwipe ?: ( ) => void ;
@@ -23,145 +27,171 @@ export interface DeckSwiperProps<T> {
2327 style ?: StyleProp < ViewStyle > ;
2428}
2529
26- const DeckSwiper = < T extends object > ( {
27- onStartSwipe,
28- onEndSwipe,
29- onSwipe,
30- onSwipedLeft,
31- onSwipedRight,
32- onSwipedUp,
33- onSwipedDown,
34- onIndexChanged,
35- onEndReached,
36- startCardIndex = 0 ,
37- infiniteSwiping = false ,
38- verticalEnabled = true ,
39- horizontalEnabled = true ,
40- visibleCardCount = 1 ,
41- data,
42- keyExtractor,
43- renderItem,
44- style,
45- children,
46- } : React . PropsWithChildren < DeckSwiperProps < T > > ) => {
47- //Both 'renderItem' and 'data' are optional to allow direct children. But if one is included, both need to be included
48- if ( ( data && ! renderItem ) || ( renderItem && ! data ) ) {
49- throw new Error (
50- "'renderItem' and 'data' need to both be provided to lazily render. Either remove them entirley or include both"
51- ) ;
52- }
30+ const DeckSwiper = React . forwardRef < DeckSwiperRef , DeckSwiperProps < any > > (
31+ < T extends object > (
32+ {
33+ onStartSwipe,
34+ onEndSwipe,
35+ onSwipe,
36+ onSwipedLeft,
37+ onSwipedRight,
38+ onSwipedUp,
39+ onSwipedDown,
40+ onIndexChanged,
41+ onEndReached,
42+ startCardIndex = 0 ,
43+ infiniteSwiping = false ,
44+ verticalEnabled = true ,
45+ horizontalEnabled = true ,
46+ visibleCardCount = 1 ,
47+ data,
48+ keyExtractor,
49+ renderItem,
50+ style,
51+ children,
52+ } : React . PropsWithChildren < DeckSwiperProps < T > > ,
53+ ref : React . Ref < DeckSwiperRef >
54+ ) => {
55+ //Both 'renderItem' and 'data' are optional to allow direct children. But if one is included, both need to be included
56+ if ( ( data && ! renderItem ) || ( renderItem && ! data ) ) {
57+ throw new Error (
58+ "'renderItem' and 'data' need to both be provided to lazily render. Either remove them entirley or include both"
59+ ) ;
60+ }
5361
54- if ( data && renderItem && children ) {
55- console . warn (
56- "'children' of DeckSwiper ignored due to usage of 'data' and 'renderItem'"
57- ) ;
58- }
62+ if ( data && renderItem && children ) {
63+ console . warn (
64+ "'children' of DeckSwiper ignored due to usage of 'data' and 'renderItem'"
65+ ) ;
66+ }
5967
60- const deckSwiperRef = React . useRef < DeckSwiperComponent < T > > ( null ) ;
68+ const deckSwiperRef = React . useRef < DeckSwiperComponent < T > > ( null ) ;
6169
62- const childrenArray = React . useMemo (
63- ( ) => React . Children . toArray ( children ) ,
64- [ children ]
65- ) ;
70+ const childrenArray = React . useMemo (
71+ ( ) => React . Children . toArray ( children ) ,
72+ [ children ]
73+ ) ;
6674
67- // an array of indices based on children count
68- const cardsFillerData = React . useMemo (
69- ( ) => Array . from ( Array ( childrenArray . length ) . keys ( ) ) ,
70- [ childrenArray ]
71- ) ;
75+ // an array of indices based on children count
76+ const cardsFillerData = React . useMemo (
77+ ( ) => Array . from ( Array ( childrenArray . length ) . keys ( ) ) ,
78+ [ childrenArray ]
79+ ) ;
7280
73- const cardsData = Array . isArray ( data ) ? data : cardsFillerData ;
81+ const cardsData = Array . isArray ( data ) ? data : cardsFillerData ;
7482
75- const renderCard = ( card : any , index : number ) : JSX . Element => {
76- if ( renderItem ) {
77- return renderItem ( { item : card , index } ) ;
78- } else {
79- return < > { childrenArray [ index ] } </ > ;
80- }
81- } ;
83+ const renderCard = ( card : any , index : number ) : JSX . Element => {
84+ if ( renderItem ) {
85+ return renderItem ( { item : card , index } ) ;
86+ } else {
87+ return < > { childrenArray [ index ] } </ > ;
88+ }
89+ } ;
8290
83- const renderFirstCard = ( ) : JSX . Element | undefined => {
84- if ( cardsData . length ) {
85- return renderCard ( cardsData [ 0 ] , 0 ) ;
86- }
87- return undefined ;
88- } ;
89-
90- const cardKeyExtractor = ( card : any ) => {
91- if ( keyExtractor ) {
92- return keyExtractor ( card ) ;
93- } else {
94- return card ?. toString ( ) ;
95- }
96- } ;
91+ const renderFirstCard = ( ) : JSX . Element | undefined => {
92+ if ( cardsData . length ) {
93+ return renderCard ( cardsData [ 0 ] , 0 ) ;
94+ }
95+ return undefined ;
96+ } ;
9797
98- /*
98+ const cardKeyExtractor = ( card : any ) => {
99+ if ( keyExtractor ) {
100+ return keyExtractor ( card ) ;
101+ } else {
102+ return card ?. toString ( ) ;
103+ }
104+ } ;
105+
106+ /*
99107 react-native-deck-swiper does not re-render cards when parent state changes
100108 This forces an update on every re-render to reflect any parent state changes
101109 */
102- React . useEffect ( ( ) => {
103- deckSwiperRef . current ?. forceUpdate ( ) ;
104- } ) ;
105-
106- /**
107- * By default react-native-deck-swiper positions everything with absolute position.
108- * To overcome this, it is wrapped in a View to be able to add the component in any layout structure.
109- *
110- *
111- * Since all children of that View are absolutley positioned, the View does not have a height and still looks and behaves weird.
112- * To fix/mitage this without setting a static height, the first card is rendered in invisible state to take up space.
113- * This effectivley makes the default height of the container be the height of the first card.
114- */
115-
116- return (
117- < View >
118- < View style = { styles . containerHeightFiller } > { renderFirstCard ( ) } </ View >
119- < DeckSwiperComponent
120- ref = { deckSwiperRef }
121- cards = { cardsData as any [ ] }
122- renderCard = { renderCard }
123- keyExtractor = { cardKeyExtractor }
124- containerStyle = {
125- StyleSheet . flatten ( [ styles . cardsContainer , style ] ) as
126- | object
127- | undefined
110+ React . useEffect ( ( ) => {
111+ deckSwiperRef . current ?. forceUpdate ( ) ;
112+ } ) ;
113+
114+ React . useImperativeHandle ( ref , ( ) => ( {
115+ swipeCard : ( direction : "left" | "right" | "top" | "bottom" ) => {
116+ switch ( direction ) {
117+ case "left" :
118+ deckSwiperRef . current ?. swipeLeft ( ) ;
119+ break ;
120+ case "right" :
121+ deckSwiperRef . current ?. swipeRight ( ) ;
122+ break ;
123+ case "top" :
124+ deckSwiperRef . current ?. swipeTop ( ) ;
125+ break ;
126+ case "bottom" :
127+ deckSwiperRef . current ?. swipeBottom ( ) ;
128+ break ;
129+ default :
130+ deckSwiperRef . current ?. swipeLeft ( ) ;
128131 }
129- cardStyle = { styles . card as object | undefined }
130- onSwiped = { onIndexChanged }
131- onSwipedAll = { onEndReached }
132- cardIndex = { startCardIndex }
133- infinite = { infiniteSwiping }
134- verticalSwipe = { verticalEnabled }
135- horizontalSwipe = { horizontalEnabled }
136- showSecondCard = { visibleCardCount > 1 }
137- stackSize = { visibleCardCount }
138- backgroundColor = "transparent"
139- cardVerticalMargin = { 0 }
140- cardHorizontalMargin = { 0 }
141- onSwipedLeft = { ( index ) => {
142- onSwipedLeft ?.( index ) ;
143- onSwipe ?.( index ) ;
144- } }
145- onSwipedRight = { ( index ) => {
146- onSwipedRight ?.( index ) ;
147- onSwipe ?.( index ) ;
148- } }
149- onSwipedTop = { ( index ) => {
150- onSwipedUp ?.( index ) ;
151- onSwipe ?.( index ) ;
152- } }
153- onSwipedBottom = { ( index ) => {
154- onSwipedDown ?.( index ) ;
155- onSwipe ?.( index ) ;
156- } }
157- //@ts -ignore Not typed, but is implemented and works
158- dragStart = { onStartSwipe }
159- //@ts -ignore
160- dragEnd = { onEndSwipe }
161- />
162- </ View >
163- ) ;
164- } ;
132+ } ,
133+ } ) ) ;
134+
135+ /**
136+ * By default react-native-deck-swiper positions everything with absolute position.
137+ * To overcome this, it is wrapped in a View to be able to add the component in any layout structure.
138+ *
139+ *
140+ * Since all children of that View are absolutley positioned, the View does not have a height and still looks and behaves weird.
141+ * To fix/mitage this without setting a static height, the first card is rendered in invisible state to take up space.
142+ * This effectivley makes the default height of the container be the height of the first card.
143+ */
144+
145+ return (
146+ < View >
147+ < View style = { styles . containerHeightFiller } > { renderFirstCard ( ) } </ View >
148+ < DeckSwiperComponent
149+ ref = { deckSwiperRef }
150+ cards = { cardsData as any [ ] }
151+ renderCard = { renderCard }
152+ keyExtractor = { cardKeyExtractor }
153+ containerStyle = {
154+ StyleSheet . flatten ( [ styles . cardsContainer , style ] ) as
155+ | object
156+ | undefined
157+ }
158+ cardStyle = { styles . card as object | undefined }
159+ onSwiped = { onIndexChanged }
160+ onSwipedAll = { onEndReached }
161+ cardIndex = { startCardIndex }
162+ infinite = { infiniteSwiping }
163+ verticalSwipe = { verticalEnabled }
164+ horizontalSwipe = { horizontalEnabled }
165+ showSecondCard = { visibleCardCount > 1 }
166+ stackSize = { visibleCardCount }
167+ backgroundColor = "transparent"
168+ cardVerticalMargin = { 0 }
169+ cardHorizontalMargin = { 0 }
170+ onSwipedLeft = { ( index ) => {
171+ onSwipedLeft ?.( index ) ;
172+ onSwipe ?.( index ) ;
173+ } }
174+ onSwipedRight = { ( index ) => {
175+ onSwipedRight ?.( index ) ;
176+ onSwipe ?.( index ) ;
177+ } }
178+ onSwipedTop = { ( index ) => {
179+ onSwipedUp ?.( index ) ;
180+ onSwipe ?.( index ) ;
181+ } }
182+ onSwipedBottom = { ( index ) => {
183+ onSwipedDown ?.( index ) ;
184+ onSwipe ?.( index ) ;
185+ } }
186+ //@ts -ignore Not typed, but is implemented and works
187+ dragStart = { onStartSwipe }
188+ //@ts -ignore
189+ dragEnd = { onEndSwipe }
190+ />
191+ </ View >
192+ ) ;
193+ }
194+ ) ;
165195
166196const styles = StyleSheet . create ( {
167197 cardsContainer : {
0 commit comments