Skip to content

Commit dbc63f0

Browse files
M-i-k-e-lethanshar
authored andcommitted
Re-introduce PanDismissibleView (#612)
1 parent 3a3b3f6 commit dbc63f0

File tree

6 files changed

+158
-10
lines changed

6 files changed

+158
-10
lines changed

demo/src/screens/MenuStructure.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@ export const navigationData = {
3535
{title: 'Hint', tags: 'hints tooltip', screen: 'unicorn.components.HintsScreen'},
3636
{title: 'Overlays', tags: 'overlay image', screen: 'unicorn.components.OverlaysScreen'},
3737
{title: 'Page Control', tags: 'page', screen: 'unicorn.components.PageControlScreen'},
38+
{title: 'Pan Dismissible', tags: 'pan swipe drag dismiss', screen: 'unicorn.components.PanDismissibleScreen'},
3839
{title: 'Pan Listener', tags: 'pan swipe drag listener', screen: 'unicorn.components.PanListenerScreen'},
3940
{title: 'Pan Responder', tags: 'pan swipe drag responder', screen: 'unicorn.components.PanResponderScreen'},
4041
{title: 'Shared Transition', tags: 'shared transition element', screen: 'unicorn.components.SharedTransitionScreen'},
Lines changed: 112 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,112 @@
1+
import React, {Component} from 'react';
2+
import {StyleSheet} from 'react-native';
3+
import {
4+
Button,
5+
View,
6+
Text,
7+
Switch,
8+
Colors,
9+
Typography,
10+
PanListenerView,
11+
PanningProvider,
12+
PanDismissibleView
13+
} from 'react-native-ui-lib';
14+
15+
const PAN_LISTENER_VIEW_HEIGHT = 100;
16+
17+
export default class PanDismissibleScreen extends Component {
18+
state = {
19+
location: {left: 50, top: 50},
20+
isCoupled: true,
21+
key: false
22+
};
23+
24+
switchExample = () => {
25+
const {isCoupled, location} = this.state;
26+
if (isCoupled) {
27+
this.setState({
28+
isCoupled: false,
29+
location: {left: location.left, top: location.top - PAN_LISTENER_VIEW_HEIGHT}
30+
});
31+
} else {
32+
this.setState({
33+
isCoupled: true,
34+
location: {left: location.left, top: location.top + PAN_LISTENER_VIEW_HEIGHT}
35+
});
36+
}
37+
};
38+
39+
reset = () => {
40+
this.setState({key: !this.state.key});
41+
};
42+
43+
renderPanListener = () => {
44+
return (
45+
<PanListenerView style={styles.panListener}>
46+
<Text style={styles.largeText}>Drag\Swipe here</Text>
47+
</PanListenerView>
48+
);
49+
};
50+
51+
render() {
52+
const {isCoupled, key} = this.state;
53+
const panListener = this.renderPanListener();
54+
55+
return (
56+
<View flex bg-dark80>
57+
<Text style={styles.largeText}>Pan Dismissible</Text>
58+
<View style={styles.container}>
59+
<Switch value={isCoupled} onValueChange={this.switchExample} style={styles.switch}/>
60+
<Text style={styles.smallText}>{isCoupled ? 'Coupled' : 'Uncoupled'}</Text>
61+
</View>
62+
<Button label="Reset" size={Button.sizes.medium} outline style={styles.button} onPress={this.reset}/>
63+
<View centerH>
64+
<PanningProvider>
65+
{!isCoupled && panListener}
66+
<PanDismissibleView
67+
key={key}
68+
style={[styles.panDismissible, {marginTop: isCoupled ? PAN_LISTENER_VIEW_HEIGHT : undefined}]}
69+
>
70+
{isCoupled && panListener}
71+
</PanDismissibleView>
72+
</PanningProvider>
73+
</View>
74+
</View>
75+
);
76+
}
77+
}
78+
79+
const styles = StyleSheet.create({
80+
largeText: {
81+
...Typography.text50,
82+
margin: 20
83+
},
84+
container: {
85+
flexDirection: 'row',
86+
marginBottom: 20,
87+
alignSelf: 'center'
88+
},
89+
panDismissible: {
90+
width: 250,
91+
height: 250,
92+
backgroundColor: Colors.blue30
93+
},
94+
panListener: {
95+
width: '100%',
96+
height: PAN_LISTENER_VIEW_HEIGHT,
97+
backgroundColor: Colors.blue60,
98+
justifyContent: 'center'
99+
},
100+
smallText: {
101+
...Typography.text70,
102+
marginLeft: 20
103+
},
104+
switch: {
105+
marginLeft: 20,
106+
alignSelf: 'center'
107+
},
108+
button: {
109+
alignSelf: 'center',
110+
marginBottom: 20
111+
}
112+
});

demo/src/screens/componentScreens/index.js

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ import InputsScreen from './InputsScreen';
1616
import MaskedInputScreen from './MaskedInputScreen';
1717
import OverlaysScreen from './OverlaysScreen';
1818
import PageControlScreen from './PageControlScreen';
19+
import PanDismissibleScreen from './PanDismissibleScreen';
1920
import PanListenerScreen from './PanListenerScreen';
2021
import PanResponderScreen from './PanResponderScreen';
2122
import PickerScreen from './PickerScreen';
@@ -56,6 +57,7 @@ Navigation.registerComponent('unicorn.components.InputsScreen', () => InputsScre
5657
Navigation.registerComponent('unicorn.components.MaskedInputScreen', () => MaskedInputScreen);
5758
Navigation.registerComponent('unicorn.components.OverlaysScreen', () => OverlaysScreen);
5859
Navigation.registerComponent('unicorn.components.PageControlScreen', () => PageControlScreen);
60+
Navigation.registerComponent('unicorn.components.PanDismissibleScreen', () => PanDismissibleScreen);
5961
Navigation.registerComponent('unicorn.components.PanListenerScreen', () => PanListenerScreen);
6062
Navigation.registerComponent('unicorn.components.PanResponderScreen', () => PanResponderScreen);
6163
Navigation.registerComponent('unicorn.components.PickerScreen', () => PickerScreen);

src/components/panningViews/asPanViewConsumer.js

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,6 @@ import PanningContext from './panningContext';
33

44
function asPanViewConsumer(WrappedComponent) {
55
class PanViewConsumer extends Component {
6-
// TODO: remove when removing PanDismissibleView
76
saveRef = r => {
87
this.contentRef = r;
98
};

src/components/panningViews/panDismissibleView.js

Lines changed: 35 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,12 @@ class PanDismissibleView extends PureComponent {
3939
speed: PropTypes.number,
4040
bounciness: PropTypes.number,
4141
duration: PropTypes.number
42-
})
42+
}),
43+
/**
44+
* Allow diagonal dismiss, this is false by default,
45+
* since it looks better and most cases.
46+
*/
47+
allowDiagonalDismiss: PropTypes.bool
4348
};
4449

4550
static defaultProps = {
@@ -54,7 +59,8 @@ class PanDismissibleView extends PureComponent {
5459
bounciness: DEFAULT_BOUNCINESS,
5560
duration: DEFAULT_DISMISS_ANIMATION_DURATION
5661
},
57-
onDismiss: _.noop
62+
onDismiss: _.noop,
63+
allowDiagonalDismiss: false
5864
};
5965

6066
constructor(props) {
@@ -209,13 +215,24 @@ class PanDismissibleView extends PureComponent {
209215
};
210216

211217
getDismissAnimationDirection = () => {
212-
const {swipeDirections, dragDirections} = this.props.context; // eslint-disable-line
218+
const {allowDiagonalDismiss} = this.props;
219+
const {swipeDirections, swipeVelocities, dragDirections, dragDeltas} = this.props.context; // eslint-disable-line
213220
const hasHorizontalSwipe = !_.isUndefined(swipeDirections.x);
214221
const hasVerticalSwipe = !_.isUndefined(swipeDirections.y);
215222
let isRight;
216223
let isDown;
217224

218225
if (hasHorizontalSwipe || hasVerticalSwipe) {
226+
if (!allowDiagonalDismiss && hasHorizontalSwipe && hasVerticalSwipe) {
227+
if (Math.abs(swipeVelocities.y) > Math.abs(swipeVelocities.x)) {
228+
isDown = swipeDirections.y === PanningProvider.Directions.DOWN;
229+
} else {
230+
isRight = swipeDirections.x === PanningProvider.Directions.RIGHT;
231+
}
232+
233+
return {isRight, isDown};
234+
}
235+
219236
if (hasHorizontalSwipe) {
220237
isRight = swipeDirections.x === PanningProvider.Directions.RIGHT;
221238
}
@@ -225,11 +242,23 @@ class PanDismissibleView extends PureComponent {
225242
}
226243
} else {
227244
// got here from a drag beyond threshold
228-
if (!_.isUndefined(dragDirections.x)) {
245+
const hasHorizontalDrag = !_.isUndefined(dragDirections.x);
246+
const hasVerticalDrag = !_.isUndefined(dragDirections.y);
247+
if (!allowDiagonalDismiss && hasHorizontalDrag && hasVerticalDrag) {
248+
if (Math.abs(dragDeltas.y) > Math.abs(dragDeltas.x)) {
249+
isDown = dragDirections.y === PanningProvider.Directions.DOWN;
250+
} else {
251+
isRight = dragDirections.x === PanningProvider.Directions.RIGHT;
252+
}
253+
254+
return {isRight, isDown};
255+
}
256+
257+
if (hasHorizontalDrag) {
229258
isRight = dragDirections.x === PanningProvider.Directions.RIGHT;
230259
}
231260

232-
if (!_.isUndefined(dragDirections.y)) {
261+
if (hasVerticalDrag) {
233262
isDown = dragDirections.y === PanningProvider.Directions.DOWN;
234263
}
235264
}
@@ -296,8 +325,7 @@ class PanDismissibleView extends PureComponent {
296325

297326
onDismissAnimationFinished = ({finished}) => {
298327
if (finished) {
299-
const {onDismiss} = this.props;
300-
onDismiss();
328+
_.invoke(this.props, 'onDismiss');
301329
}
302330
};
303331

src/components/panningViews/panListenerView.js

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -62,7 +62,12 @@ class PanListenerView extends PureBaseComponent {
6262
* The sensitivity beyond which a pan is no longer considered a drag, but a swipe (default is 1.8)
6363
* Note: a pan would have to occur (i.e. the panSensitivity has already been surpassed)
6464
*/
65-
swipeVelocitySensitivity: PropTypes.number
65+
swipeVelocitySensitivity: PropTypes.number,
66+
/**
67+
* Is there a view that is clickable (has onPress etc.) in the PanListenerView.
68+
* This can affect the panability of this component.
69+
*/
70+
isClickable: PropTypes.bool
6671
};
6772

6873
static defaultProps = {
@@ -81,8 +86,9 @@ class PanListenerView extends PureBaseComponent {
8186

8287
this.state = {};
8388

89+
const {isClickable} = props;
8490
this.panResponder = PanResponder.create({
85-
onStartShouldSetPanResponder: this.yes,
91+
onStartShouldSetPanResponder: isClickable ? this.shouldPan : this.yes,
8692
onMoveShouldSetPanResponder: this.shouldPan,
8793
onStartShouldSetPanResponderCapture: this.no,
8894
onMoveShouldSetPanResponderCapture: this.no,

0 commit comments

Comments
 (0)