@momo-kits/swipe 0.0.65-alpha.16 → 0.0.65-alpha.22

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/SwipeAction.js CHANGED
@@ -10,19 +10,19 @@
10
10
  /* eslint-disable no-unused-expressions */
11
11
  /* eslint-disable react/destructuring-assignment */
12
12
 
13
- import React, {Component} from 'react';
13
+ import React, { Component } from 'react';
14
14
  import PropTypes from 'prop-types';
15
15
  import {
16
- Dimensions,
17
- Animated,
18
- PanResponder,
19
- StyleSheet,
20
- TouchableOpacity,
21
- View,
22
- Image,
16
+ Dimensions,
17
+ Animated,
18
+ PanResponder,
19
+ StyleSheet,
20
+ TouchableOpacity,
21
+ View,
22
+ Image,
23
23
  } from 'react-native';
24
- import {Colors, ValueUtil, Text} from '@momo-kits/core';
25
- import {TouchableHighlight} from 'react-native';
24
+ import { Colors, ValueUtil, Text } from '@momo-kits/core';
25
+ import { TouchableHighlight } from 'react-native';
26
26
 
27
27
  const DEFAULT_PREVIEW_OPEN_DELAY = 700;
28
28
  const PREVIEW_CLOSE_DELAY = 300;
@@ -30,679 +30,689 @@ const MAX_VELOCITY_CONTRIBUTION = 5;
30
30
  const SCROLL_LOCK_MILLISECONDS = 300;
31
31
  const SCREEN_WIDTH = Dimensions.get('window').width;
32
32
  const WIDTH_HIDDEN_RIGHT = 190;
33
-
34
33
  class SwipeAction extends Component {
35
- constructor(props) {
36
- super(props);
37
- this.isOpen = false;
38
- this.previousTrackedTranslateX = 0;
39
- this.previousTrackedDirection = null;
40
- this.horizontalSwipeGestureBegan = false;
41
- this.swipeInitialX = null;
42
- this.parentScrollEnabled = true;
43
- this.ranPreview = false;
44
- this._ensureScrollEnabledTimer = null;
45
- this.isForceClosing = false;
46
- this.state = {
47
- dimensionsSet: false,
48
- hiddenHeight: this.props.disableHiddenLayoutCalculation ? '100%' : 0,
49
- hiddenWidth: this.props.disableHiddenLayoutCalculation ? '100%' : 0,
50
- isLeftActionVisible: false,
51
- isRightActionVisible: false,
52
- leftOpenValue: 0,
53
- rightOpenValue: 0,
54
- stopLeftSwipe: SCREEN_WIDTH,
55
- stopRightSwipe: -SCREEN_WIDTH,
56
- };
57
- const {leftAction, rightAction} = this.props;
58
- const buttonCellWidth = WIDTH_HIDDEN_RIGHT / 2;
59
- // leftAction ? this.leftOpenValue = leftAction.length * buttonCellWidth : 0;
60
- // rightAction ? this.rightOpenValue = -rightAction.length * buttonCellWidth : 0;
61
- this._translateX = new Animated.Value(0);
62
- if (this.props.onSwipeValueChange) {
63
- this._translateX.addListener(({value}) => {
64
- let direction = this.previousTrackedDirection;
65
- if (value !== this.previousTrackedTranslateX) {
66
- direction = value > this.previousTrackedTranslateX ? 'right' : 'left';
34
+ constructor(props) {
35
+ super(props);
36
+ this.isOpen = false;
37
+ this.previousTrackedTranslateX = 0;
38
+ this.previousTrackedDirection = null;
39
+ this.horizontalSwipeGestureBegan = false;
40
+ this.swipeInitialX = null;
41
+ this.parentScrollEnabled = true;
42
+ this.ranPreview = false;
43
+ this._ensureScrollEnabledTimer = null;
44
+ this.isForceClosing = false;
45
+ this.state = {
46
+ dimensionsSet: false,
47
+ hiddenHeight: this.props.disableHiddenLayoutCalculation
48
+ ? '100%'
49
+ : 0,
50
+ hiddenWidth: this.props.disableHiddenLayoutCalculation ? '100%' : 0,
51
+ isLeftActionVisible: false,
52
+ isRightActionVisible: false,
53
+ leftOpenValue: 0,
54
+ rightOpenValue: 0,
55
+ };
56
+ const { leftAction, rightAction } = this.props;
57
+ const buttonCellWidth = WIDTH_HIDDEN_RIGHT / 2;
58
+ // leftAction ? this.leftOpenValue = leftAction.length * buttonCellWidth : 0;
59
+ // rightAction ? this.rightOpenValue = -rightAction.length * buttonCellWidth : 0;
60
+ this.stopLeftSwipe = SCREEN_WIDTH;
61
+ this.stopRightSwipe = -SCREEN_WIDTH;
62
+ this._translateX = new Animated.Value(0);
63
+ if (this.props.onSwipeValueChange) {
64
+ this._translateX.addListener(({ value }) => {
65
+ let direction = this.previousTrackedDirection;
66
+ if (value !== this.previousTrackedTranslateX) {
67
+ direction =
68
+ value > this.previousTrackedTranslateX
69
+ ? 'right'
70
+ : 'left';
71
+ }
72
+ this.props.onSwipeValueChange &&
73
+ this.props.onSwipeValueChange({
74
+ isOpen: this.isOpen,
75
+ direction,
76
+ value,
77
+ });
78
+ this.previousTrackedTranslateX = value;
79
+ this.previousTrackedDirection = direction;
80
+ });
67
81
  }
68
- this.props.onSwipeValueChange &&
69
- this.props.onSwipeValueChange({
70
- isOpen: this.isOpen,
71
- direction,
72
- value,
73
- });
74
- this.previousTrackedTranslateX = value;
75
- this.previousTrackedDirection = direction;
76
- });
77
- }
78
82
 
79
- if (
80
- this.props.forceCloseToRightThreshold &&
81
- this.props.forceCloseToRightThreshold > 0
82
- ) {
83
- this._translateX.addListener(({value}) => {
84
83
  if (
85
- !this.isForceClosing &&
86
- SCREEN_WIDTH + value < this.props.forceCloseToRightThreshold
84
+ this.props.forceCloseToRightThreshold &&
85
+ this.props.forceCloseToRightThreshold > 0
87
86
  ) {
88
- this.isForceClosing = true;
89
- this.forceCloseRow('right');
90
- if (this.props.onForceCloseToRight) {
91
- this.props.onForceCloseToRight();
92
- }
87
+ this._translateX.addListener(({ value }) => {
88
+ if (
89
+ !this.isForceClosing &&
90
+ SCREEN_WIDTH + value < this.props.forceCloseToRightThreshold
91
+ ) {
92
+ this.isForceClosing = true;
93
+ this.forceCloseRow('right');
94
+ if (this.props.onForceCloseToRight) {
95
+ this.props.onForceCloseToRight();
96
+ }
97
+ }
98
+ });
93
99
  }
94
- });
95
- }
96
100
 
97
- if (
98
- this.props.forceCloseToLeftThreshold &&
99
- this.props.forceCloseToLeftThreshold > 0
100
- ) {
101
- this._translateX.addListener(({value}) => {
102
101
  if (
103
- !this.isForceClosing &&
104
- SCREEN_WIDTH - value < this.props.forceCloseToLeftThreshold
102
+ this.props.forceCloseToLeftThreshold &&
103
+ this.props.forceCloseToLeftThreshold > 0
105
104
  ) {
106
- this.isForceClosing = true;
107
- this.forceCloseRow('left');
108
- if (this.props.onForceCloseToLeft) {
109
- this.props.onForceCloseToLeft();
110
- }
105
+ this._translateX.addListener(({ value }) => {
106
+ if (
107
+ !this.isForceClosing &&
108
+ SCREEN_WIDTH - value < this.props.forceCloseToLeftThreshold
109
+ ) {
110
+ this.isForceClosing = true;
111
+ this.forceCloseRow('left');
112
+ if (this.props.onForceCloseToLeft) {
113
+ this.props.onForceCloseToLeft();
114
+ }
115
+ }
116
+ });
111
117
  }
112
- });
113
- }
114
- }
115
-
116
- UNSAFE_componentWillMount() {
117
- this._panResponder = PanResponder.create({
118
- onMoveShouldSetPanResponder: (e, gs) =>
119
- this.handleOnMoveShouldSetPanResponder(e, gs),
120
- onPanResponderMove: (e, gs) => this.handlePanResponderMove(e, gs),
121
- onPanResponderRelease: (e, gs) => this.handlePanResponderEnd(e, gs),
122
- onPanResponderTerminate: (e, gs) => this.handlePanResponderEnd(e, gs),
123
- onShouldBlockNativeResponder: _ => false,
124
- });
125
- }
126
-
127
- componentWillUnmount() {
128
- clearTimeout(this._ensureScrollEnabledTimer);
129
- this._translateX.removeAllListeners();
130
- }
131
-
132
- shouldComponentUpdate(nextProps, nextState) {
133
- if (
134
- this.state.hiddenHeight !== nextState.hiddenHeight ||
135
- this.state.hiddenWidth !== nextState.hiddenWidth ||
136
- !this.props.shouldItemUpdate ||
137
- (this.props.shouldItemUpdate &&
138
- this.props.shouldItemUpdate(this.props.item, nextProps.item))
139
- ) {
140
- return true;
141
118
  }
142
119
 
143
- return false;
144
- }
145
-
146
- getPreviewAnimation(toValue, delay) {
147
- return Animated.timing(this._translateX, {
148
- duration: this.props.previewDuration,
149
- toValue,
150
- delay,
151
- useNativeDriver: this.props.useNativeDriver,
152
- });
153
- }
154
-
155
- onContentLayout(e) {
156
- this.setState({
157
- dimensionsSet: !this.props.recalculateHiddenLayout,
158
- ...(!this.props.disableHiddenLayoutCalculation
159
- ? {
160
- hiddenHeight: e.nativeEvent.layout.height,
161
- hiddenWidth: e.nativeEvent.layout.width,
162
- }
163
- : {}),
164
- });
165
-
166
- if (this.props.preview && !this.ranPreview) {
167
- this.ranPreview = true;
168
- const previewOpenValue =
169
- this.props.previewOpenValue || this.state.rightOpenValue * 0.5;
170
- this.getPreviewAnimation(
171
- previewOpenValue,
172
- this.props.previewOpenDelay,
173
- ).start(_ => {
174
- this.getPreviewAnimation(0, PREVIEW_CLOSE_DELAY).start();
175
- });
120
+ UNSAFE_componentWillMount() {
121
+ this._panResponder = PanResponder.create({
122
+ onMoveShouldSetPanResponder: (e, gs) =>
123
+ this.handleOnMoveShouldSetPanResponder(e, gs),
124
+ onPanResponderMove: (e, gs) => this.handlePanResponderMove(e, gs),
125
+ onPanResponderRelease: (e, gs) => this.handlePanResponderEnd(e, gs),
126
+ onPanResponderTerminate: (e, gs) =>
127
+ this.handlePanResponderEnd(e, gs),
128
+ onShouldBlockNativeResponder: (_) => false,
129
+ });
176
130
  }
177
- }
178
-
179
- onPress() {
180
- if (this.swipeInitialX == null || this.swipeInitialX === 0) {
181
- if (this.props.onPress && typeof this.props.onPress === 'function') {
182
- this.props.onPress();
183
- }
184
- } else {
185
- if (this.props.closeOnPress) {
186
- this.closeRow();
187
- }
131
+
132
+ componentWillUnmount() {
133
+ clearTimeout(this._ensureScrollEnabledTimer);
134
+ this._translateX.removeAllListeners();
188
135
  }
189
- }
190
136
 
191
- handleOnMoveShouldSetPanResponder(e, gs) {
192
- const {dx} = gs;
193
- return Math.abs(dx) > this.props.directionalDistanceChangeThreshold;
194
- }
137
+ shouldComponentUpdate(nextProps, nextState) {
138
+ if (
139
+ this.state.hiddenHeight !== nextState.hiddenHeight ||
140
+ this.state.hiddenWidth !== nextState.hiddenWidth ||
141
+ !this.props.shouldItemUpdate ||
142
+ (this.props.shouldItemUpdate &&
143
+ this.props.shouldItemUpdate(this.props.item, nextProps.item))
144
+ ) {
145
+ return true;
146
+ }
195
147
 
196
- handlePanResponderMove(e, gestureState) {
197
- /* If the view is force closing, then ignore Moves. Return */
198
- if (this.isForceClosing) {
199
- return;
148
+ return false;
200
149
  }
201
150
 
202
- /* Else, do normal job */
203
- const {dx, dy} = gestureState;
204
- const absDx = Math.abs(dx);
205
- const absDy = Math.abs(dy);
206
-
207
- // this check may not be necessary because we don't capture the move until we pass the threshold
208
- // just being extra safe here
209
- if (
210
- absDx > this.props.directionalDistanceChangeThreshold ||
211
- absDy > this.props.directionalDistanceChangeThreshold
212
- ) {
213
- // we have enough to determine direction
214
- if (absDy > absDx && !this.horizontalSwipeGestureBegan) {
215
- // user is moving vertically, do nothing, listView will handle
216
- return;
217
- }
218
-
219
- // user is moving horizontally
220
- if (this.parentScrollEnabled) {
221
- // disable scrolling on the listView parent
222
- this.parentScrollEnabled = false;
223
- this.props.setScrollEnabled && this.props.setScrollEnabled(false);
224
- }
225
-
226
- if (this.swipeInitialX === null) {
227
- // set tranlateX value when user started swiping
228
- this.swipeInitialX = this._translateX._value;
229
- }
230
- if (!this.horizontalSwipeGestureBegan) {
231
- this.horizontalSwipeGestureBegan = true;
232
- const direction = dx > 0 ? 'TO_RIGHT' : 'TO_LEFT';
233
- this.props.swipeGestureBegan && this.props.swipeGestureBegan(direction);
234
- }
235
-
236
- let newDX = this.swipeInitialX + dx;
237
- if (this.props.disableRightAction && newDX < 0) {
238
- newDX = 0;
239
- }
240
- if (this.props.disableLeftAction && newDX > 0) {
241
- newDX = 0;
242
- }
243
-
244
- if (this.state.stopLeftSwipe && newDX > this.state.stopLeftSwipe) {
245
- newDX = this.state.stopLeftSwipe;
246
- }
247
- if (this.state.stopRightSwipe && newDX < this.state.stopRightSwipe) {
248
- newDX = this.state.stopRightSwipe;
249
- }
250
-
251
- // set action buttons visibility
252
- if (newDX > 0) {
253
- this.setState({
254
- isLeftActionVisible: true,
255
- isRightActionVisible: false,
151
+ getPreviewAnimation(toValue, delay) {
152
+ return Animated.timing(this._translateX, {
153
+ duration: this.props.previewDuration,
154
+ toValue,
155
+ delay,
156
+ useNativeDriver: this.props.useNativeDriver,
256
157
  });
257
- } else if (newDX < 0) {
158
+ }
159
+
160
+ onContentLayout(e) {
258
161
  this.setState({
259
- isRightActionVisible: true,
260
- isLeftActionVisible: false,
162
+ dimensionsSet: !this.props.recalculateHiddenLayout,
163
+ ...(!this.props.disableHiddenLayoutCalculation
164
+ ? {
165
+ hiddenHeight: e.nativeEvent.layout.height,
166
+ hiddenWidth: e.nativeEvent.layout.width,
167
+ }
168
+ : {}),
261
169
  });
262
- }
263
170
 
264
- this._translateX.setValue(newDX);
171
+ if (this.props.preview && !this.ranPreview) {
172
+ this.ranPreview = true;
173
+ const previewOpenValue =
174
+ this.props.previewOpenValue || this.state.rightOpenValue * 0.5;
175
+ this.getPreviewAnimation(
176
+ previewOpenValue,
177
+ this.props.previewOpenDelay,
178
+ ).start((_) => {
179
+ this.getPreviewAnimation(0, PREVIEW_CLOSE_DELAY).start();
180
+ });
181
+ }
265
182
  }
266
- }
267
183
 
268
- ensureScrollEnabled = () => {
269
- if (!this.parentScrollEnabled) {
270
- this.parentScrollEnabled = true;
271
- this.props.setScrollEnabled && this.props.setScrollEnabled(true);
184
+ onPress() {
185
+ if (this.swipeInitialX == null || this.swipeInitialX === 0) {
186
+ if (
187
+ this.props.onPress &&
188
+ typeof this.props.onPress === 'function'
189
+ ) {
190
+ this.props.onPress();
191
+ }
192
+ } else {
193
+ if (this.props.closeOnPress) {
194
+ this.closeRow();
195
+ }
196
+ }
272
197
  }
273
- };
274
198
 
275
- handlePanResponderEnd(e, gestureState) {
276
- /* PandEnd will reset the force-closing state when it's true. */
277
- if (this.isForceClosing) {
278
- this.isForceClosing = false;
199
+ handleOnMoveShouldSetPanResponder(e, gs) {
200
+ const { dx } = gs;
201
+ return Math.abs(dx) > this.props.directionalDistanceChangeThreshold;
279
202
  }
280
- // decide how much the velocity will affect the final position that the list item settles in.
281
- const {swipeToOpenVelocityContribution} = this.props;
282
- const possibleExtraPixels =
283
- this.state.rightOpenValue * swipeToOpenVelocityContribution;
284
- const clampedVelocity = Math.min(
285
- gestureState.vx,
286
- MAX_VELOCITY_CONTRIBUTION,
287
- );
288
- const projectedExtraPixels =
289
- possibleExtraPixels * (clampedVelocity / MAX_VELOCITY_CONTRIBUTION);
290
-
291
- // re-enable scrolling on listView parent
292
- this._ensureScrollEnabledTimer = setTimeout(
293
- this.ensureScrollEnabled,
294
- SCROLL_LOCK_MILLISECONDS,
295
- );
296
-
297
- // finish up the animation
298
- let toValue = 0;
299
- if (this._translateX._value >= 0) {
300
- // trying to swipe right
301
- if (this.swipeInitialX < this._translateX._value) {
302
- if (
303
- this._translateX._value - projectedExtraPixels >
304
- this.state.leftOpenValue * (this.props.swipeToOpenPercent / 100)
305
- ) {
306
- // we're more than halfway
307
- toValue = this.state.leftOpenValue;
203
+
204
+ handlePanResponderMove(e, gestureState) {
205
+ /* If the view is force closing, then ignore Moves. Return */
206
+ if (this.isForceClosing) {
207
+ return;
308
208
  }
309
- } else if (
310
- this._translateX._value - projectedExtraPixels >
311
- this.state.leftOpenValue * (1 - this.props.swipeToClosePercent / 100)
312
- ) {
313
- toValue = this.state.leftOpenValue;
314
- }
315
- } else {
316
- // trying to swipe left
317
- if (this.swipeInitialX > this._translateX._value) {
209
+
210
+ /* Else, do normal job */
211
+ const { dx, dy } = gestureState;
212
+ const absDx = Math.abs(dx);
213
+ const absDy = Math.abs(dy);
214
+
215
+ // this check may not be necessary because we don't capture the move until we pass the threshold
216
+ // just being extra safe here
318
217
  if (
319
- this._translateX._value - projectedExtraPixels <
320
- this.state.rightOpenValue * (this.props.swipeToOpenPercent / 100)
218
+ absDx > this.props.directionalDistanceChangeThreshold ||
219
+ absDy > this.props.directionalDistanceChangeThreshold
321
220
  ) {
322
- // we're more than halfway
323
- toValue = this.state.rightOpenValue;
221
+ // we have enough to determine direction
222
+ if (absDy > absDx && !this.horizontalSwipeGestureBegan) {
223
+ // user is moving vertically, do nothing, listView will handle
224
+ return;
225
+ }
226
+
227
+ // user is moving horizontally
228
+ if (this.parentScrollEnabled) {
229
+ // disable scrolling on the listView parent
230
+ this.parentScrollEnabled = false;
231
+ this.props.setScrollEnabled &&
232
+ this.props.setScrollEnabled(false);
233
+ }
234
+
235
+ if (this.swipeInitialX === null) {
236
+ // set tranlateX value when user started swiping
237
+ this.swipeInitialX = this._translateX._value;
238
+ }
239
+ if (!this.horizontalSwipeGestureBegan) {
240
+ this.horizontalSwipeGestureBegan = true;
241
+ const direction = dx > 0 ? 'TO_RIGHT' : 'TO_LEFT';
242
+ this.props.swipeGestureBegan &&
243
+ this.props.swipeGestureBegan(direction);
244
+ }
245
+
246
+ let newDX = this.swipeInitialX + dx;
247
+ if (this.props.disableRightAction && newDX < 0) {
248
+ newDX = 0;
249
+ }
250
+ if (this.props.disableLeftAction && newDX > 0) {
251
+ newDX = 0;
252
+ }
253
+
254
+ if (this.stopLeftSwipe && newDX > this.stopLeftSwipe) {
255
+ newDX = this.stopLeftSwipe;
256
+ }
257
+ if (this.stopRightSwipe && newDX < this.stopRightSwipe) {
258
+ newDX = this.stopRightSwipe;
259
+ }
260
+
261
+ // set action buttons visibility
262
+ if (newDX > 0) {
263
+ this.setState({
264
+ isLeftActionVisible: true,
265
+ isRightActionVisible: false,
266
+ });
267
+ } else if (newDX < 0) {
268
+ this.setState({
269
+ isRightActionVisible: true,
270
+ isLeftActionVisible: false,
271
+ });
272
+ }
273
+
274
+ this._translateX.setValue(newDX);
275
+ }
276
+ }
277
+
278
+ ensureScrollEnabled = () => {
279
+ if (!this.parentScrollEnabled) {
280
+ this.parentScrollEnabled = true;
281
+ this.props.setScrollEnabled && this.props.setScrollEnabled(true);
282
+ }
283
+ };
284
+
285
+ handlePanResponderEnd(e, gestureState) {
286
+ /* PandEnd will reset the force-closing state when it's true. */
287
+ if (this.isForceClosing) {
288
+ this.isForceClosing = false;
289
+ }
290
+ // decide how much the velocity will affect the final position that the list item settles in.
291
+ const { swipeToOpenVelocityContribution } = this.props;
292
+ const possibleExtraPixels =
293
+ this.state.rightOpenValue * swipeToOpenVelocityContribution;
294
+ const clampedVelocity = Math.min(
295
+ gestureState.vx,
296
+ MAX_VELOCITY_CONTRIBUTION,
297
+ );
298
+ const projectedExtraPixels =
299
+ possibleExtraPixels * (clampedVelocity / MAX_VELOCITY_CONTRIBUTION);
300
+
301
+ // re-enable scrolling on listView parent
302
+ this._ensureScrollEnabledTimer = setTimeout(
303
+ this.ensureScrollEnabled,
304
+ SCROLL_LOCK_MILLISECONDS,
305
+ );
306
+
307
+ // finish up the animation
308
+ let toValue = 0;
309
+ if (this._translateX._value >= 0) {
310
+ // trying to swipe right
311
+ if (this.swipeInitialX < this._translateX._value) {
312
+ if (
313
+ this._translateX._value - projectedExtraPixels >
314
+ this.state.leftOpenValue *
315
+ (this.props.swipeToOpenPercent / 100)
316
+ ) {
317
+ // we're more than halfway
318
+ toValue = this.state.leftOpenValue;
319
+ }
320
+ } else if (
321
+ this._translateX._value - projectedExtraPixels >
322
+ this.state.leftOpenValue *
323
+ (1 - this.props.swipeToClosePercent / 100)
324
+ ) {
325
+ toValue = this.state.leftOpenValue;
326
+ }
327
+ } else {
328
+ // trying to swipe left
329
+ if (this.swipeInitialX > this._translateX._value) {
330
+ if (
331
+ this._translateX._value - projectedExtraPixels <
332
+ this.state.rightOpenValue *
333
+ (this.props.swipeToOpenPercent / 100)
334
+ ) {
335
+ // we're more than halfway
336
+ toValue = this.state.rightOpenValue;
337
+ }
338
+ } else if (
339
+ this._translateX._value - projectedExtraPixels <
340
+ this.state.rightOpenValue *
341
+ (1 - this.props.swipeToClosePercent / 100)
342
+ ) {
343
+ toValue = this.state.rightOpenValue;
344
+ }
324
345
  }
325
- } else if (
326
- this._translateX._value - projectedExtraPixels <
327
- this.state.rightOpenValue * (1 - this.props.swipeToClosePercent / 100)
328
- ) {
329
- toValue = this.state.rightOpenValue;
330
- }
346
+
347
+ this.props.swipeGestureEnd && this.props.swipeGestureEnd();
348
+ this.manuallySwipeRow(toValue);
349
+ }
350
+
351
+ /*
352
+ * This method is called by SwipeListView
353
+ */
354
+ closeRow() {
355
+ this.manuallySwipeRow(0);
331
356
  }
332
357
 
333
- this.props.swipeGestureEnd && this.props.swipeGestureEnd();
334
- this.manuallySwipeRow(toValue);
335
- }
336
-
337
- /*
338
- * This method is called by SwipeListView
339
- */
340
- closeRow() {
341
- this.manuallySwipeRow(0);
342
- }
343
-
344
- /**
345
- * Force close the row toward the end of the given direction.
346
- * @param {String} direction The direction to force close.
347
- */
348
- forceCloseRow(direction) {
349
- this.manuallySwipeRow(0, () => {
350
- if (direction === 'right' && this.props.onForceCloseToRightEnd) {
351
- this.props.onForceCloseToRightEnd();
352
- } else if (direction === 'left' && this.props.onForceCloseToLeftEnd) {
353
- this.props.onForceCloseToLeftEnd();
354
- }
355
- });
356
- }
357
-
358
- closeRowWithoutAnimation() {
359
- this._translateX.setValue(0);
360
-
361
- this.ensureScrollEnabled();
362
- this.isOpen = false;
363
- this.props.onDidClose && this.props.onDidClose();
364
-
365
- this.props.onClose && this.props.onClose();
366
-
367
- this.swipeInitialX = null;
368
- this.horizontalSwipeGestureBegan = false;
369
- }
370
-
371
- manuallySwipeRow(toValue, onAnimationEnd) {
372
- Animated.spring(this._translateX, {
373
- toValue,
374
- friction: this.props.friction,
375
- tension: this.props.tension,
376
- useNativeDriver: this.props.useNativeDriver,
377
- }).start(_ => {
378
- this._translateX.setValue(toValue);
379
- this.ensureScrollEnabled();
380
- if (toValue === 0) {
358
+ /**
359
+ * Force close the row toward the end of the given direction.
360
+ * @param {String} direction The direction to force close.
361
+ */
362
+ forceCloseRow(direction) {
363
+ this.manuallySwipeRow(0, () => {
364
+ if (direction === 'right' && this.props.onForceCloseToRightEnd) {
365
+ this.props.onForceCloseToRightEnd();
366
+ } else if (
367
+ direction === 'left' &&
368
+ this.props.onForceCloseToLeftEnd
369
+ ) {
370
+ this.props.onForceCloseToLeftEnd();
371
+ }
372
+ });
373
+ }
374
+
375
+ closeRowWithoutAnimation() {
376
+ this._translateX.setValue(0);
377
+
378
+ this.ensureScrollEnabled();
381
379
  this.isOpen = false;
382
380
  this.props.onDidClose && this.props.onDidClose();
383
- } else {
384
- this.isOpen = true;
385
- this.props.onDidOpen && this.props.onDidOpen(toValue);
386
- }
387
- if (onAnimationEnd) {
388
- onAnimationEnd();
389
- }
390
- });
391
-
392
- if (toValue === 0) {
393
- this.props.onClose && this.props.onClose();
394
- } else {
395
- this.props.onOpen && this.props.onOpen(toValue);
396
- }
397
381
 
398
- // reset everything
399
- this.swipeInitialX = toValue;
400
- this.horizontalSwipeGestureBegan = false;
401
- }
382
+ this.props.onClose && this.props.onClose();
402
383
 
403
- renderVisibleContent() {
404
- if (this.props.children) {
405
- // handle touchables
406
- const {onPress} = this.props.children.props;
384
+ this.swipeInitialX = null;
385
+ this.horizontalSwipeGestureBegan = false;
386
+ }
407
387
 
408
- if (onPress) {
409
- const newOnPress = _ => {
410
- this.onPress();
411
- onPress();
412
- };
413
- return React.cloneElement(this.props.children, {
414
- ...this.props.children.props,
415
- onPress: newOnPress,
388
+ manuallySwipeRow(toValue, onAnimationEnd) {
389
+ Animated.spring(this._translateX, {
390
+ toValue,
391
+ friction: this.props.friction,
392
+ tension: this.props.tension,
393
+ useNativeDriver: this.props.useNativeDriver,
394
+ }).start((_) => {
395
+ this._translateX.setValue(toValue);
396
+ this.ensureScrollEnabled();
397
+ if (toValue === 0) {
398
+ this.isOpen = false;
399
+ this.props.onDidClose && this.props.onDidClose();
400
+ } else {
401
+ this.isOpen = true;
402
+ this.props.onDidOpen && this.props.onDidOpen(toValue);
403
+ }
404
+ if (onAnimationEnd) {
405
+ onAnimationEnd();
406
+ }
416
407
  });
417
- }
418
- return (
419
- <TouchableHighlight
420
- activeOpacity={0.8}
421
- underlayColor="white"
422
- onPress={_ => this.onPress()}>
423
- {this.props.children}
424
- </TouchableHighlight>
425
- );
408
+
409
+ if (toValue === 0) {
410
+ this.props.onClose && this.props.onClose();
411
+ } else {
412
+ this.props.onOpen && this.props.onOpen(toValue);
413
+ }
414
+
415
+ // reset everything
416
+ this.swipeInitialX = toValue;
417
+ this.horizontalSwipeGestureBegan = false;
418
+ }
419
+
420
+ renderVisibleContent() {
421
+ if (this.props.children) {
422
+ // handle touchables
423
+ const { onPress } = this.props.children.props;
424
+
425
+ if (onPress) {
426
+ const newOnPress = (_) => {
427
+ this.onPress();
428
+ onPress();
429
+ };
430
+ return React.cloneElement(this.props.children, {
431
+ ...this.props.children.props,
432
+ onPress: newOnPress,
433
+ });
434
+ }
435
+ return (
436
+ <TouchableHighlight
437
+ activeOpacity={0.8}
438
+ underlayColor="white"
439
+ onPress={(_) => this.onPress()}>
440
+ {this.props.children}
441
+ </TouchableHighlight>
442
+ );
443
+ }
426
444
  }
427
- }
428
-
429
- renderRowContent() {
430
- // We do this annoying if statement for performance.
431
- // We don't want the onLayout func to run after it runs once.
432
- if (this.state.dimensionsSet) {
433
- return (
434
- <Animated.View
435
- manipulationModes={['translateX']}
436
- {...this._panResponder.panHandlers}
437
- style={{
438
- zIndex: 2,
439
- transform: [{translateX: this._translateX}],
440
- }}>
441
- {this.renderVisibleContent()}
442
- </Animated.View>
443
- );
445
+
446
+ renderRowContent() {
447
+ // We do this annoying if statement for performance.
448
+ // We don't want the onLayout func to run after it runs once.
449
+ if (this.state.dimensionsSet) {
450
+ return (
451
+ <Animated.View
452
+ manipulationModes={['translateX']}
453
+ {...this._panResponder.panHandlers}
454
+ style={{
455
+ zIndex: 2,
456
+ transform: [{ translateX: this._translateX }],
457
+ }}>
458
+ {this.renderVisibleContent()}
459
+ </Animated.View>
460
+ );
461
+ }
462
+ return (
463
+ <Animated.View
464
+ manipulationModes={['translateX']}
465
+ {...this._panResponder.panHandlers}
466
+ onLayout={(e) => this.onContentLayout(e)}
467
+ style={{
468
+ zIndex: 2,
469
+ transform: [{ translateX: this._translateX }],
470
+ }}>
471
+ {this.renderVisibleContent()}
472
+ </Animated.View>
473
+ );
444
474
  }
445
- return (
446
- <Animated.View
447
- manipulationModes={['translateX']}
448
- {...this._panResponder.panHandlers}
449
- onLayout={e => this.onContentLayout(e)}
450
- style={{
451
- zIndex: 2,
452
- transform: [{translateX: this._translateX}],
453
- }}>
454
- {this.renderVisibleContent()}
455
- </Animated.View>
456
- );
457
- }
458
-
459
- onLeftActionLayout = ({nativeEvent}) => {
460
- this.setState({
461
- leftOpenValue: nativeEvent.layout.width,
462
- stopLeftSwipe: nativeEvent.layout.width,
463
- });
464
- };
465
-
466
- onRightActionLayout = ({nativeEvent}) => {
467
- this.setState({
468
- rightOpenValue: -nativeEvent.layout.width,
469
- stopRightSwipe: -nativeEvent.layout.width,
470
- });
471
- };
472
-
473
- renderHiddenContent = () => {
474
- const {actionBackground, rowIndex, rightAction, leftAction} = this.props;
475
- const hiddenRowStyle = {
476
- ...styles.standaloneRowBack,
477
- ...{backgroundColor: actionBackground},
475
+
476
+ onLeftActionLayout = ({ nativeEvent }) => {
477
+ this.setState({
478
+ leftOpenValue: nativeEvent.layout.width,
479
+ });
478
480
  };
479
- console.log(this.state.stopLeftSwipe);
480
- return (
481
- <View style={hiddenRowStyle}>
482
- {this.state.isLeftActionVisible && (
483
- <View style={styles.leftItemRow}>
484
- <View
485
- style={{flexDirection: 'row'}}
486
- onLayout={this.onLeftActionLayout}>
487
- {leftAction?.map?.((item, index) =>
488
- this.renderLeftAction(item, index),
489
- )}
481
+
482
+ onRightActionLayout = ({ nativeEvent }) => {
483
+ this.setState({
484
+ rightOpenValue: -nativeEvent.layout.width,
485
+ });
486
+ };
487
+
488
+ renderHiddenContent = () => {
489
+ const { actionBackground, rowIndex, rightAction, leftAction } =
490
+ this.props;
491
+ const hiddenRowStyle = {
492
+ ...styles.standaloneRowBack,
493
+ ...{ backgroundColor: actionBackground },
494
+ };
495
+ return (
496
+ <View style={hiddenRowStyle}>
497
+ {this.state.isLeftActionVisible && (
498
+ <View style={styles.leftItemRow}>
499
+ <View
500
+ style={{ flexDirection: 'row' }}
501
+ onLayout={this.onLeftActionLayout}>
502
+ {leftAction?.map?.((item, index) =>
503
+ this.renderLeftAction(item, index),
504
+ )}
505
+ </View>
506
+ </View>
507
+ )}
508
+ {this.state.isRightActionVisible && (
509
+ <View style={styles.rightItemRow}>
510
+ <View
511
+ style={{ flexDirection: 'row' }}
512
+ onLayout={this.onRightActionLayout}>
513
+ {rightAction
514
+ ?.slice?.(0)
515
+ ?.reverse()
516
+ .map((item, index) =>
517
+ this.renderRightAction(item, index),
518
+ )}
519
+ </View>
520
+ </View>
521
+ )}
490
522
  </View>
491
- </View>
492
- )}
493
- {this.state.isRightActionVisible && (
494
- <View style={styles.rightItemRow}>
523
+ );
524
+ };
525
+
526
+ renderIcon = (icon) => {
527
+ const { actionIconStyle } = this.props;
528
+ const iconSource = ValueUtil.getImageSource(icon);
529
+ return (
530
+ <Image
531
+ source={iconSource}
532
+ resizeMode="contain"
533
+ style={[styles.icon, actionIconStyle]}
534
+ />
535
+ );
536
+ };
537
+
538
+ renderTitle = (title) => {
539
+ const { actionTextStyle } = this.props;
540
+ return (
541
+ <Text.SubTitle style={[styles.text, actionTextStyle]}>
542
+ {title}
543
+ </Text.SubTitle>
544
+ );
545
+ };
546
+
547
+ onActionPress = (item, rowIndex) => () => {
548
+ const { onPress } = item;
549
+ onPress?.(item, rowIndex);
550
+ };
551
+
552
+ renderRightAction = (item, index) => {
553
+ const { rowIndex, renderRightAction } = this.props;
554
+ if (renderRightAction) {
555
+ return (
556
+ <View key={item + index}>
557
+ {renderRightAction({ item, index, rowIndex })}
558
+ </View>
559
+ );
560
+ }
561
+ return (
562
+ <TouchableOpacity
563
+ key={index.toString()}
564
+ style={[
565
+ styles.buttonContainer,
566
+ {
567
+ backgroundColor: item.color,
568
+ height: this.state.hiddenHeight,
569
+ },
570
+ ]}
571
+ onPress={this.onActionPress(item, rowIndex)}>
572
+ {item.icon && this.renderIcon(item.icon)}
573
+ {item.title && this.renderTitle(item.title)}
574
+ </TouchableOpacity>
575
+ );
576
+ };
577
+
578
+ renderLeftAction = (item, index) => {
579
+ const { rowIndex, renderLeftAction } = this.props;
580
+ if (renderLeftAction) {
581
+ return (
582
+ <View key={item + index}>
583
+ {renderLeftAction({ item, index, rowIndex })}
584
+ </View>
585
+ );
586
+ }
587
+ return (
588
+ <TouchableOpacity
589
+ key={index.toString()}
590
+ style={[
591
+ styles.buttonContainer,
592
+ {
593
+ backgroundColor: item.color,
594
+ height: this.state.hiddenHeight,
595
+ },
596
+ ]}
597
+ onPress={this.onActionPress(item, rowIndex)}>
598
+ {item.icon && this.renderIcon(item.icon)}
599
+ {item.title && this.renderTitle(item.title)}
600
+ </TouchableOpacity>
601
+ );
602
+ };
603
+
604
+ render() {
605
+ return (
495
606
  <View
496
- style={{flexDirection: 'row'}}
497
- onLayout={this.onRightActionLayout}>
498
- {rightAction
499
- ?.slice?.(0)
500
- ?.reverse()
501
- .map((item, index) => this.renderRightAction(item, index))}
607
+ style={this.props.style ? this.props.style : styles.container}>
608
+ <View
609
+ style={[
610
+ styles.hidden,
611
+ {
612
+ height: this.state.hiddenHeight,
613
+ width: this.state.hiddenWidth,
614
+ },
615
+ ]}>
616
+ {this.renderHiddenContent()}
617
+ </View>
618
+ {this.renderRowContent()}
502
619
  </View>
503
- </View>
504
- )}
505
- </View>
506
- );
507
- };
508
-
509
- renderIcon = icon => {
510
- const {actionIconStyle} = this.props;
511
- const iconSource = ValueUtil.getImageSource(icon);
512
- return (
513
- <Image
514
- source={iconSource}
515
- resizeMode="contain"
516
- style={[styles.icon, actionIconStyle]}
517
- />
518
- );
519
- };
520
-
521
- renderTitle = title => {
522
- const {actionTextStyle} = this.props;
523
- return (
524
- <Text.SubTitle style={[styles.text, actionTextStyle]}>
525
- {title}
526
- </Text.SubTitle>
527
- );
528
- };
529
-
530
- onActionPress = (item, rowIndex) => () => {
531
- const {onPress} = item;
532
- onPress?.(item, rowIndex);
533
- };
534
-
535
- renderRightAction = (item, index) => {
536
- const {rowIndex, renderRightAction} = this.props;
537
- if (renderRightAction) {
538
- return (
539
- <View key={item + index}>
540
- {renderRightAction({
541
- item,
542
- index,
543
- rowIndex,
544
- })}
545
- </View>
546
- );
547
- }
548
- return (
549
- <TouchableOpacity
550
- key={index.toString()}
551
- style={[
552
- styles.buttonContainer,
553
- {
554
- backgroundColor: item.color,
555
- height: this.state.hiddenHeight,
556
- },
557
- ]}
558
- onPress={this.onActionPress(item, rowIndex)}>
559
- {item.icon && this.renderIcon(item.icon)}
560
- {item.title && this.renderTitle(item.title)}
561
- </TouchableOpacity>
562
- );
563
- };
564
-
565
- renderLeftAction = (item, index) => {
566
- const {rowIndex, renderLeftAction} = this.props;
567
- if (renderLeftAction) {
568
- return (
569
- <View key={item + index}>
570
- {renderLeftAction({
571
- item,
572
- index,
573
- rowIndex,
574
- })}
575
- </View>
576
- );
620
+ );
577
621
  }
578
- return (
579
- <TouchableOpacity
580
- key={index.toString()}
581
- style={[
582
- styles.buttonContainer,
583
- {
584
- backgroundColor: item.color,
585
- height: this.state.hiddenHeight,
586
- },
587
- ]}
588
- onPress={this.onActionPress(item, rowIndex)}>
589
- {item.icon && this.renderIcon(item.icon)}
590
- {item.title && this.renderTitle(item.title)}
591
- </TouchableOpacity>
592
- );
593
- };
594
-
595
- render() {
596
- return (
597
- <View style={this.props.style ? this.props.style : styles.container}>
598
- <View
599
- style={[
600
- styles.hidden,
601
- {
602
- height: this.state.hiddenHeight,
603
- width: this.state.hiddenWidth,
604
- },
605
- ]}>
606
- {this.renderHiddenContent()}
607
- </View>
608
- {this.renderRowContent()}
609
- </View>
610
- );
611
- }
612
622
  }
613
623
 
614
624
  const styles = StyleSheet.create({
615
- container: {
616
- // As of RN 0.29 flex: 1 is causing all rows to be the same height
617
- // flex: 1
618
- },
619
- hidden: {
620
- zIndex: 1,
621
- bottom: 0,
622
- left: 0,
623
- overflow: 'hidden',
624
- position: 'absolute',
625
- right: 0,
626
- top: 0,
627
- },
628
- standaloneRowBack: {
629
- backgroundColor: Colors.white,
630
- alignItems: 'center',
631
- flex: 1,
632
- flexDirection: 'row',
633
- justifyContent: 'space-between',
634
- },
635
- leftItemRow: {
636
- alignItems: 'center',
637
- flex: 1,
638
- flexDirection: 'row',
639
- justifyContent: 'flex-start',
640
- paddingRight: 10,
641
- },
642
- rightItemRow: {
643
- alignItems: 'center',
644
- flex: 1,
645
- flexDirection: 'row',
646
- justifyContent: 'flex-end',
647
- paddingLeft: 10,
648
- },
649
- buttonContainer: {
650
- backgroundColor: 'red',
651
- alignItems: 'center',
652
- justifyContent: 'center',
653
- width: 88,
654
- },
655
- icon: {
656
- height: 24,
657
- width: 24,
658
- tintColor: 'white',
659
- },
660
- text: {
661
- color: Colors.white,
662
- textAlign: 'center',
663
- marginHorizontal: 10,
664
- marginTop: 10,
665
- },
625
+ container: {
626
+ // As of RN 0.29 flex: 1 is causing all rows to be the same height
627
+ // flex: 1
628
+ },
629
+ hidden: {
630
+ zIndex: 1,
631
+ bottom: 0,
632
+ left: 0,
633
+ overflow: 'hidden',
634
+ position: 'absolute',
635
+ right: 0,
636
+ top: 0,
637
+ },
638
+ standaloneRowBack: {
639
+ backgroundColor: Colors.white,
640
+ alignItems: 'center',
641
+ flex: 1,
642
+ flexDirection: 'row',
643
+ justifyContent: 'space-between',
644
+ },
645
+ leftItemRow: {
646
+ alignItems: 'center',
647
+ flex: 1,
648
+ flexDirection: 'row',
649
+ justifyContent: 'flex-start',
650
+ paddingRight: 10,
651
+ },
652
+ rightItemRow: {
653
+ alignItems: 'center',
654
+ flex: 1,
655
+ flexDirection: 'row',
656
+ justifyContent: 'flex-end',
657
+ paddingLeft: 10,
658
+ },
659
+ buttonContainer: {
660
+ backgroundColor: 'red',
661
+ alignItems: 'center',
662
+ justifyContent: 'center',
663
+ width: 88,
664
+ },
665
+ icon: {
666
+ height: 24,
667
+ width: 24,
668
+ tintColor: 'white',
669
+ },
670
+ text: {
671
+ color: Colors.white,
672
+ textAlign: 'center',
673
+ marginHorizontal: 10,
674
+ marginTop: 10,
675
+ },
666
676
  });
667
677
 
668
678
  SwipeAction.propTypes = {
669
- onOpen: PropTypes.func,
670
- closeOnPress: PropTypes.bool,
671
- disableRightAction: PropTypes.bool,
672
- disableLeftAction: PropTypes.bool,
673
- onClose: PropTypes.func,
674
- style: PropTypes.oneOfType([PropTypes.object, PropTypes.array]),
675
- leftAction: PropTypes.array,
676
- rightAction: PropTypes.array,
677
- renderRightAction: PropTypes.func,
678
- renderLeftAction: PropTypes.func,
679
- onPress: PropTypes.func,
680
- children: PropTypes.element.isRequired,
681
- actionBackground: PropTypes.string,
682
- actionTextStyle: PropTypes.object,
683
- swipeGestureBegan: PropTypes.func,
684
- swipeGestureEnd: PropTypes.func,
679
+ onOpen: PropTypes.func,
680
+ closeOnPress: PropTypes.bool,
681
+ disableRightAction: PropTypes.bool,
682
+ disableLeftAction: PropTypes.bool,
683
+ onClose: PropTypes.func,
684
+ style: PropTypes.oneOfType([PropTypes.object, PropTypes.array]),
685
+ leftAction: PropTypes.array,
686
+ rightAction: PropTypes.array,
687
+ renderRightAction: PropTypes.func,
688
+ renderLeftAction: PropTypes.func,
689
+ onPress: PropTypes.func,
690
+ children: PropTypes.element.isRequired,
691
+ actionBackground: PropTypes.string,
692
+ actionTextStyle: PropTypes.object,
693
+ swipeGestureBegan: PropTypes.func,
694
+ swipeGestureEnd: PropTypes.func,
685
695
  };
686
696
 
687
697
  SwipeAction.defaultProps = {
688
- closeOnPress: true,
689
- disableRightAction: true,
690
- disableLeftAction: true,
691
- recalculateHiddenLayout: false,
692
- disableHiddenLayoutCalculation: false,
693
- preview: false,
694
- previewDuration: 300,
695
- previewOpenDelay: DEFAULT_PREVIEW_OPEN_DELAY,
696
- directionalDistanceChangeThreshold: 2,
697
- swipeToOpenPercent: 50,
698
- swipeToOpenVelocityContribution: 0,
699
- swipeToClosePercent: 50,
700
- swipeToPerformActionPercent: 50,
701
- item: {},
702
- useNativeDriver: true,
703
- rightAction: [],
704
- leftAction: [],
705
- actionBackground: Colors.white,
698
+ closeOnPress: true,
699
+ disableRightAction: true,
700
+ disableLeftAction: true,
701
+ recalculateHiddenLayout: false,
702
+ disableHiddenLayoutCalculation: false,
703
+ preview: false,
704
+ previewDuration: 300,
705
+ previewOpenDelay: DEFAULT_PREVIEW_OPEN_DELAY,
706
+ directionalDistanceChangeThreshold: 2,
707
+ swipeToOpenPercent: 50,
708
+ swipeToOpenVelocityContribution: 0,
709
+ swipeToClosePercent: 50,
710
+ swipeToPerformActionPercent: 50,
711
+ item: {},
712
+ useNativeDriver: true,
713
+ rightAction: [],
714
+ leftAction: [],
715
+ actionBackground: Colors.white,
706
716
  };
707
717
 
708
718
  export default SwipeAction;