mirror of
https://github.com/zhigang1992/react-native-paper.git
synced 2026-06-19 18:23:27 +08:00
fix: fix visibility and touch target of action in Snackbar
- Animate the Snackbar on layout instead of on mount because we need the layout for animation, this fixes snackbar not being visible on initial mount if visible=true. - Fix the touch target of the action button to be larger. Previously the touch target was as small as the action text and it was difficult to tap on it.
This commit is contained in:
@@ -1,14 +1,9 @@
|
||||
/* @flow */
|
||||
|
||||
import * as React from 'react';
|
||||
import {
|
||||
StyleSheet,
|
||||
Animated,
|
||||
Text,
|
||||
View,
|
||||
TouchableWithoutFeedback,
|
||||
} from 'react-native';
|
||||
import { StyleSheet, Animated } from 'react-native';
|
||||
|
||||
import Text from './Typography/Text';
|
||||
import ThemedPortal from './Portal/ThemedPortal';
|
||||
import withTheme from '../core/withTheme';
|
||||
import { white } from '../styles/colors';
|
||||
@@ -48,8 +43,10 @@ type Props = {
|
||||
};
|
||||
|
||||
type State = {
|
||||
rendered: boolean,
|
||||
height: number,
|
||||
layout: {
|
||||
height: number,
|
||||
measured: boolean,
|
||||
},
|
||||
opacity: Animated.Value,
|
||||
translateY: Animated.Value,
|
||||
};
|
||||
@@ -129,25 +126,17 @@ class Snackbar extends React.Component<Props, State> {
|
||||
};
|
||||
|
||||
state = {
|
||||
rendered: false,
|
||||
height: 0,
|
||||
layout: {
|
||||
height: 0,
|
||||
measured: false,
|
||||
},
|
||||
opacity: new Animated.Value(0),
|
||||
translateY: new Animated.Value(0),
|
||||
};
|
||||
|
||||
componentDidMount() {
|
||||
if (this.props.visible) {
|
||||
this._show();
|
||||
}
|
||||
}
|
||||
|
||||
componentDidUpdate(prevProps) {
|
||||
if (prevProps.visible !== this.props.visible) {
|
||||
if (this.props.visible) {
|
||||
this._show();
|
||||
} else {
|
||||
this._hide();
|
||||
}
|
||||
this._toggle();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -159,16 +148,38 @@ class Snackbar extends React.Component<Props, State> {
|
||||
|
||||
_handleLayout = e => {
|
||||
const { height } = e.nativeEvent.layout;
|
||||
const { measured } = this.state.layout;
|
||||
|
||||
this.setState({
|
||||
height,
|
||||
rendered: true,
|
||||
this.setState({ layout: { height, measured: true } }, () => {
|
||||
if (measured) {
|
||||
if (!this.props.visible) {
|
||||
// If height changed and Snackbar was hidden, adjust the translate to keep it hidden
|
||||
this.state.translateY.setValue(height);
|
||||
}
|
||||
} else {
|
||||
// Set the appropriate initial values if height was previously unknown
|
||||
this.state.translateY.setValue(height);
|
||||
this.state.opacity.setValue(0);
|
||||
|
||||
// Perform the animation only if we're showing
|
||||
if (this.props.visible) {
|
||||
this._show();
|
||||
}
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
this.state.translateY.setValue(height);
|
||||
_toggle = () => {
|
||||
if (this.props.visible) {
|
||||
this._show();
|
||||
} else {
|
||||
this._hide();
|
||||
}
|
||||
};
|
||||
|
||||
_show = () => {
|
||||
clearTimeout(this._hideTimeout);
|
||||
|
||||
Animated.parallel([
|
||||
Animated.timing(this.state.opacity, {
|
||||
toValue: 1,
|
||||
@@ -199,7 +210,7 @@ class Snackbar extends React.Component<Props, State> {
|
||||
useNativeDriver: true,
|
||||
}),
|
||||
Animated.timing(this.state.translateY, {
|
||||
toValue: this.state.height,
|
||||
toValue: this.state.layout.height,
|
||||
duration: SNACKBAR_ANIMATION_DURATION,
|
||||
useNativeDriver: true,
|
||||
}),
|
||||
@@ -210,9 +221,6 @@ class Snackbar extends React.Component<Props, State> {
|
||||
const { children, action, onDismiss, theme, style } = this.props;
|
||||
const { fonts, colors } = theme;
|
||||
|
||||
const buttonMargin = action ? 24 : 0;
|
||||
const contentRightMargin = action ? 0 : 24;
|
||||
|
||||
return (
|
||||
<ThemedPortal>
|
||||
<Animated.View
|
||||
@@ -220,7 +228,7 @@ class Snackbar extends React.Component<Props, State> {
|
||||
style={[
|
||||
styles.wrapper,
|
||||
{
|
||||
opacity: this.state.rendered ? 1 : 0,
|
||||
opacity: this.state.layout.measured ? 1 : 0,
|
||||
transform: [
|
||||
{
|
||||
translateY: this.state.translateY,
|
||||
@@ -241,36 +249,22 @@ class Snackbar extends React.Component<Props, State> {
|
||||
},
|
||||
]}
|
||||
>
|
||||
<Text
|
||||
style={[
|
||||
styles.content,
|
||||
{
|
||||
fontFamily: fonts.regular,
|
||||
marginRight: contentRightMargin,
|
||||
},
|
||||
]}
|
||||
>
|
||||
<Text style={[styles.content, { marginRight: action ? 0 : 24 }]}>
|
||||
{children}
|
||||
</Text>
|
||||
{action ? (
|
||||
<View
|
||||
style={{
|
||||
marginHorizontal: buttonMargin,
|
||||
<Text
|
||||
style={[
|
||||
styles.button,
|
||||
{ color: colors.accent, fontFamily: fonts.medium },
|
||||
]}
|
||||
onPress={() => {
|
||||
action.onPress();
|
||||
onDismiss();
|
||||
}}
|
||||
>
|
||||
<TouchableWithoutFeedback
|
||||
onPress={() => {
|
||||
action.onPress();
|
||||
onDismiss();
|
||||
}}
|
||||
>
|
||||
<View>
|
||||
<Text style={{ color: colors.accent }}>
|
||||
{action.label.toUpperCase()}
|
||||
</Text>
|
||||
</View>
|
||||
</TouchableWithoutFeedback>
|
||||
</View>
|
||||
{action.label.toUpperCase()}
|
||||
</Text>
|
||||
) : null}
|
||||
</Animated.View>
|
||||
</Animated.View>
|
||||
@@ -299,6 +293,10 @@ const styles = StyleSheet.create({
|
||||
flexWrap: 'wrap',
|
||||
flex: 1,
|
||||
},
|
||||
button: {
|
||||
paddingHorizontal: 24,
|
||||
paddingVertical: 14,
|
||||
},
|
||||
});
|
||||
|
||||
export default withTheme(Snackbar);
|
||||
|
||||
@@ -61,18 +61,45 @@ exports[`renders not visible snackbar with content 1`] = `
|
||||
style={
|
||||
Array [
|
||||
Object {
|
||||
"color": "#ffffff",
|
||||
"flex": 1,
|
||||
"flexWrap": "wrap",
|
||||
"marginLeft": 24,
|
||||
"marginVertical": 14,
|
||||
},
|
||||
Object {
|
||||
"color": "#000000",
|
||||
"fontFamily": "Helvetica Neue",
|
||||
"marginRight": 24,
|
||||
},
|
||||
Array [
|
||||
Object {
|
||||
"color": "#ffffff",
|
||||
"flex": 1,
|
||||
"flexWrap": "wrap",
|
||||
"marginLeft": 24,
|
||||
"marginVertical": 14,
|
||||
},
|
||||
Object {
|
||||
"marginRight": 24,
|
||||
},
|
||||
],
|
||||
]
|
||||
}
|
||||
theme={
|
||||
Object {
|
||||
"colors": Object {
|
||||
"accent": "#ff4081",
|
||||
"background": "#fafafa",
|
||||
"disabled": "rgba(0, 0, 0, 0.26)",
|
||||
"error": "#ff1744",
|
||||
"paper": "#ffffff",
|
||||
"placeholder": "rgba(0, 0, 0, 0.38)",
|
||||
"primary": "#3f51b5",
|
||||
"text": "#000000",
|
||||
},
|
||||
"dark": false,
|
||||
"fonts": Object {
|
||||
"light": "HelveticaNeue-Light",
|
||||
"medium": "HelveticaNeue-Medium",
|
||||
"regular": "Helvetica Neue",
|
||||
"thin": "HelveticaNeue-Thin",
|
||||
},
|
||||
"roundness": 2,
|
||||
}
|
||||
}
|
||||
>
|
||||
Snackbar content
|
||||
</Text>
|
||||
@@ -143,18 +170,45 @@ exports[`renders snackbar with Text as a child 1`] = `
|
||||
style={
|
||||
Array [
|
||||
Object {
|
||||
"color": "#ffffff",
|
||||
"flex": 1,
|
||||
"flexWrap": "wrap",
|
||||
"marginLeft": 24,
|
||||
"marginVertical": 14,
|
||||
},
|
||||
Object {
|
||||
"color": "#000000",
|
||||
"fontFamily": "Helvetica Neue",
|
||||
"marginRight": 24,
|
||||
},
|
||||
Array [
|
||||
Object {
|
||||
"color": "#ffffff",
|
||||
"flex": 1,
|
||||
"flexWrap": "wrap",
|
||||
"marginLeft": 24,
|
||||
"marginVertical": 14,
|
||||
},
|
||||
Object {
|
||||
"marginRight": 24,
|
||||
},
|
||||
],
|
||||
]
|
||||
}
|
||||
theme={
|
||||
Object {
|
||||
"colors": Object {
|
||||
"accent": "#ff4081",
|
||||
"background": "#fafafa",
|
||||
"disabled": "rgba(0, 0, 0, 0.26)",
|
||||
"error": "#ff1744",
|
||||
"paper": "#ffffff",
|
||||
"placeholder": "rgba(0, 0, 0, 0.38)",
|
||||
"primary": "#3f51b5",
|
||||
"text": "#000000",
|
||||
},
|
||||
"dark": false,
|
||||
"fonts": Object {
|
||||
"light": "HelveticaNeue-Light",
|
||||
"medium": "HelveticaNeue-Medium",
|
||||
"regular": "Helvetica Neue",
|
||||
"thin": "HelveticaNeue-Thin",
|
||||
},
|
||||
"roundness": 2,
|
||||
}
|
||||
}
|
||||
>
|
||||
<Text
|
||||
accessible={true}
|
||||
@@ -231,59 +285,96 @@ exports[`renders snackbar with action button 1`] = `
|
||||
style={
|
||||
Array [
|
||||
Object {
|
||||
"color": "#ffffff",
|
||||
"flex": 1,
|
||||
"flexWrap": "wrap",
|
||||
"marginLeft": 24,
|
||||
"marginVertical": 14,
|
||||
},
|
||||
Object {
|
||||
"color": "#000000",
|
||||
"fontFamily": "Helvetica Neue",
|
||||
"marginRight": 0,
|
||||
},
|
||||
Array [
|
||||
Object {
|
||||
"color": "#ffffff",
|
||||
"flex": 1,
|
||||
"flexWrap": "wrap",
|
||||
"marginLeft": 24,
|
||||
"marginVertical": 14,
|
||||
},
|
||||
Object {
|
||||
"marginRight": 0,
|
||||
},
|
||||
],
|
||||
]
|
||||
}
|
||||
theme={
|
||||
Object {
|
||||
"colors": Object {
|
||||
"accent": "#ff4081",
|
||||
"background": "#fafafa",
|
||||
"disabled": "rgba(0, 0, 0, 0.26)",
|
||||
"error": "#ff1744",
|
||||
"paper": "#ffffff",
|
||||
"placeholder": "rgba(0, 0, 0, 0.38)",
|
||||
"primary": "#3f51b5",
|
||||
"text": "#000000",
|
||||
},
|
||||
"dark": false,
|
||||
"fonts": Object {
|
||||
"light": "HelveticaNeue-Light",
|
||||
"medium": "HelveticaNeue-Medium",
|
||||
"regular": "Helvetica Neue",
|
||||
"thin": "HelveticaNeue-Thin",
|
||||
},
|
||||
"roundness": 2,
|
||||
}
|
||||
}
|
||||
>
|
||||
Snackbar content
|
||||
</Text>
|
||||
<View
|
||||
<Text
|
||||
accessible={true}
|
||||
allowFontScaling={true}
|
||||
ellipsizeMode="tail"
|
||||
onPress={[Function]}
|
||||
style={
|
||||
Array [
|
||||
Object {
|
||||
"color": "#000000",
|
||||
"fontFamily": "Helvetica Neue",
|
||||
},
|
||||
Array [
|
||||
Object {
|
||||
"paddingHorizontal": 24,
|
||||
"paddingVertical": 14,
|
||||
},
|
||||
Object {
|
||||
"color": "#ff4081",
|
||||
"fontFamily": "HelveticaNeue-Medium",
|
||||
},
|
||||
],
|
||||
]
|
||||
}
|
||||
theme={
|
||||
Object {
|
||||
"marginHorizontal": 24,
|
||||
"colors": Object {
|
||||
"accent": "#ff4081",
|
||||
"background": "#fafafa",
|
||||
"disabled": "rgba(0, 0, 0, 0.26)",
|
||||
"error": "#ff1744",
|
||||
"paper": "#ffffff",
|
||||
"placeholder": "rgba(0, 0, 0, 0.38)",
|
||||
"primary": "#3f51b5",
|
||||
"text": "#000000",
|
||||
},
|
||||
"dark": false,
|
||||
"fonts": Object {
|
||||
"light": "HelveticaNeue-Light",
|
||||
"medium": "HelveticaNeue-Medium",
|
||||
"regular": "Helvetica Neue",
|
||||
"thin": "HelveticaNeue-Thin",
|
||||
},
|
||||
"roundness": 2,
|
||||
}
|
||||
}
|
||||
>
|
||||
<View
|
||||
accessibilityComponentType={undefined}
|
||||
accessibilityLabel={undefined}
|
||||
accessibilityTraits={undefined}
|
||||
accessible={true}
|
||||
hitSlop={undefined}
|
||||
nativeID={undefined}
|
||||
onLayout={undefined}
|
||||
onResponderGrant={[Function]}
|
||||
onResponderMove={[Function]}
|
||||
onResponderRelease={[Function]}
|
||||
onResponderTerminate={[Function]}
|
||||
onResponderTerminationRequest={[Function]}
|
||||
onStartShouldSetResponder={[Function]}
|
||||
style={undefined}
|
||||
testID={undefined}
|
||||
>
|
||||
<Text
|
||||
accessible={true}
|
||||
allowFontScaling={true}
|
||||
ellipsizeMode="tail"
|
||||
style={
|
||||
Object {
|
||||
"color": "#ff4081",
|
||||
}
|
||||
}
|
||||
>
|
||||
UNDO
|
||||
</Text>
|
||||
</View>
|
||||
</View>
|
||||
UNDO
|
||||
</Text>
|
||||
</View>
|
||||
</View>
|
||||
</View>
|
||||
@@ -351,18 +442,45 @@ exports[`renders snackbar with content 1`] = `
|
||||
style={
|
||||
Array [
|
||||
Object {
|
||||
"color": "#ffffff",
|
||||
"flex": 1,
|
||||
"flexWrap": "wrap",
|
||||
"marginLeft": 24,
|
||||
"marginVertical": 14,
|
||||
},
|
||||
Object {
|
||||
"color": "#000000",
|
||||
"fontFamily": "Helvetica Neue",
|
||||
"marginRight": 24,
|
||||
},
|
||||
Array [
|
||||
Object {
|
||||
"color": "#ffffff",
|
||||
"flex": 1,
|
||||
"flexWrap": "wrap",
|
||||
"marginLeft": 24,
|
||||
"marginVertical": 14,
|
||||
},
|
||||
Object {
|
||||
"marginRight": 24,
|
||||
},
|
||||
],
|
||||
]
|
||||
}
|
||||
theme={
|
||||
Object {
|
||||
"colors": Object {
|
||||
"accent": "#ff4081",
|
||||
"background": "#fafafa",
|
||||
"disabled": "rgba(0, 0, 0, 0.26)",
|
||||
"error": "#ff1744",
|
||||
"paper": "#ffffff",
|
||||
"placeholder": "rgba(0, 0, 0, 0.38)",
|
||||
"primary": "#3f51b5",
|
||||
"text": "#000000",
|
||||
},
|
||||
"dark": false,
|
||||
"fonts": Object {
|
||||
"light": "HelveticaNeue-Light",
|
||||
"medium": "HelveticaNeue-Medium",
|
||||
"regular": "Helvetica Neue",
|
||||
"thin": "HelveticaNeue-Thin",
|
||||
},
|
||||
"roundness": 2,
|
||||
}
|
||||
}
|
||||
>
|
||||
Snackbar content
|
||||
</Text>
|
||||
|
||||
Reference in New Issue
Block a user