@momo-kits/swipe 0.73.3-beta.4 → 0.74.2-react-native.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/SwipeAction.js DELETED
@@ -1,708 +0,0 @@
1
- /* eslint-disable react/default-props-match-prop-types */
2
- /* eslint-disable react/jsx-closing-tag-location */
3
- /* eslint-disable react/jsx-indent-props */
4
- /* eslint-disable indent */
5
- /* eslint-disable react/jsx-indent */
6
- /* eslint-disable no-unused-vars */
7
- /* eslint-disable no-mixed-spaces-and-tabs */
8
- /* eslint-disable no-lonely-if */
9
- /* eslint-disable no-tabs */
10
- /* eslint-disable no-unused-expressions */
11
- /* eslint-disable react/destructuring-assignment */
12
-
13
- import React, {Component} from 'react';
14
- import PropTypes from 'prop-types';
15
- import {
16
- Dimensions,
17
- Animated,
18
- PanResponder,
19
- StyleSheet,
20
- TouchableOpacity,
21
- View,
22
- Image,
23
- } from 'react-native';
24
- import {Colors, ValueUtil, Text} from '@momo-kits/core';
25
- import {TouchableHighlight} from 'react-native';
26
-
27
- const DEFAULT_PREVIEW_OPEN_DELAY = 700;
28
- const PREVIEW_CLOSE_DELAY = 300;
29
- const MAX_VELOCITY_CONTRIBUTION = 5;
30
- const SCROLL_LOCK_MILLISECONDS = 300;
31
- const SCREEN_WIDTH = Dimensions.get('window').width;
32
- const WIDTH_HIDDEN_RIGHT = 190;
33
-
34
- 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';
67
- }
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
-
79
- if (
80
- this.props.forceCloseToRightThreshold &&
81
- this.props.forceCloseToRightThreshold > 0
82
- ) {
83
- this._translateX.addListener(({value}) => {
84
- if (
85
- !this.isForceClosing &&
86
- SCREEN_WIDTH + value < this.props.forceCloseToRightThreshold
87
- ) {
88
- this.isForceClosing = true;
89
- this.forceCloseRow('right');
90
- if (this.props.onForceCloseToRight) {
91
- this.props.onForceCloseToRight();
92
- }
93
- }
94
- });
95
- }
96
-
97
- if (
98
- this.props.forceCloseToLeftThreshold &&
99
- this.props.forceCloseToLeftThreshold > 0
100
- ) {
101
- this._translateX.addListener(({value}) => {
102
- if (
103
- !this.isForceClosing &&
104
- SCREEN_WIDTH - value < this.props.forceCloseToLeftThreshold
105
- ) {
106
- this.isForceClosing = true;
107
- this.forceCloseRow('left');
108
- if (this.props.onForceCloseToLeft) {
109
- this.props.onForceCloseToLeft();
110
- }
111
- }
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
- }
142
-
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
- });
176
- }
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
- }
188
- }
189
- }
190
-
191
- handleOnMoveShouldSetPanResponder(e, gs) {
192
- const {dx} = gs;
193
- return Math.abs(dx) > this.props.directionalDistanceChangeThreshold;
194
- }
195
-
196
- handlePanResponderMove(e, gestureState) {
197
- /* If the view is force closing, then ignore Moves. Return */
198
- if (this.isForceClosing) {
199
- return;
200
- }
201
-
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,
256
- });
257
- } else if (newDX < 0) {
258
- this.setState({
259
- isRightActionVisible: true,
260
- isLeftActionVisible: false,
261
- });
262
- }
263
-
264
- this._translateX.setValue(newDX);
265
- }
266
- }
267
-
268
- ensureScrollEnabled = () => {
269
- if (!this.parentScrollEnabled) {
270
- this.parentScrollEnabled = true;
271
- this.props.setScrollEnabled && this.props.setScrollEnabled(true);
272
- }
273
- };
274
-
275
- handlePanResponderEnd(e, gestureState) {
276
- /* PandEnd will reset the force-closing state when it's true. */
277
- if (this.isForceClosing) {
278
- this.isForceClosing = false;
279
- }
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;
308
- }
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) {
318
- if (
319
- this._translateX._value - projectedExtraPixels <
320
- this.state.rightOpenValue * (this.props.swipeToOpenPercent / 100)
321
- ) {
322
- // we're more than halfway
323
- toValue = this.state.rightOpenValue;
324
- }
325
- } else if (
326
- this._translateX._value - projectedExtraPixels <
327
- this.state.rightOpenValue * (1 - this.props.swipeToClosePercent / 100)
328
- ) {
329
- toValue = this.state.rightOpenValue;
330
- }
331
- }
332
-
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) {
381
- this.isOpen = false;
382
- 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
-
398
- // reset everything
399
- this.swipeInitialX = toValue;
400
- this.horizontalSwipeGestureBegan = false;
401
- }
402
-
403
- renderVisibleContent() {
404
- if (this.props.children) {
405
- // handle touchables
406
- const {onPress} = this.props.children.props;
407
-
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,
416
- });
417
- }
418
- return (
419
- <TouchableHighlight
420
- activeOpacity={0.8}
421
- underlayColor="white"
422
- onPress={_ => this.onPress()}>
423
- {this.props.children}
424
- </TouchableHighlight>
425
- );
426
- }
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
- );
444
- }
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},
478
- };
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
- )}
490
- </View>
491
- </View>
492
- )}
493
- {this.state.isRightActionVisible && (
494
- <View style={styles.rightItemRow}>
495
- <View
496
- style={{flexDirection: 'row'}}
497
- onLayout={this.onRightActionLayout}>
498
- {rightAction
499
- ?.slice?.(0)
500
- ?.reverse()
501
- .map((item, index) => this.renderRightAction(item, index))}
502
- </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
- );
577
- }
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
- }
613
-
614
- 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
- },
666
- });
667
-
668
- 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,
685
- };
686
-
687
- 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,
706
- };
707
-
708
- export default SwipeAction;