diff --git a/Libraries/Components/Button.js b/Libraries/Components/Button.js index fc3f2ef5aab3ad..44444b9f6ff414 100644 --- a/Libraries/Components/Button.js +++ b/Libraries/Components/Button.js @@ -11,6 +11,7 @@ */ 'use strict'; +const Animated = require('Animated'); const ColorPropType = require('ColorPropType'); const Platform = require('Platform'); const React = require('React'); @@ -19,6 +20,7 @@ const StyleSheet = require('StyleSheet'); const Text = require('Text'); const TouchableNativeFeedback = require('TouchableNativeFeedback'); const TouchableOpacity = require('TouchableOpacity'); +const TouchableWithoutFeedback = require('TouchableWithoutFeedback'); const View = require('View'); const invariant = require('fbjs/lib/invariant'); @@ -52,14 +54,20 @@ const invariant = require('fbjs/lib/invariant'); */ class Button extends React.Component<{ - title: string, - onPress: () => any, - color?: ?string, accessibilityLabel?: ?string, + bold?: ?boolean, + bordered?: ?boolean, + color?: ?string, disabled?: ?boolean, - testID?: ?string, hasTVPreferredFocus?: ?boolean, -}> { + noShadow?: ?boolean, + onPress: () => any, + testID?: ?string, + title: string, + transparent?: ?boolean, +}, { + activeAnim: typeof Animated.Value + }> { static propTypes = { /** * Text to display inside the button @@ -91,96 +99,228 @@ class Button extends React.Component<{ * @platform ios */ hasTVPreferredFocus: PropTypes.bool, + /** + * *(iOS only)* If true, title will be be bold (on Android the title is always bold regardless of this prop). + * + * @platform ios + */ + bold: PropTypes.bool, + /** + * *(Android only)* If true color will affect the title, and background will be transparent. On iOS the background is always transparent regardless of this. If set transparent to true, you cannot set bordered to true. + * + * @platform android + */ + transparent: PropTypes.bool, + /** + * *(Android only)* If true, no shadow is drawn. + * + * @platform android + */ + noShadow: PropTypes.bool, + /** + * The border and title will be same as "color". On Android, the background is white, and on iOS the background remains transparent. iOS also has a different onPressIn and onPressOut animation where the text goes to white and the background goes to "color". + */ + bordered: PropTypes.bool, }; + state = { + activeAnim: new Animated.Value(0) + } + render() { const { accessibilityLabel, + bold, + bordered, color, - onPress, - title, - hasTVPreferredFocus, disabled, + hasTVPreferredFocus, + noShadow, + onPress, testID, + title, + transparent } = this.props; + const { activeAnim } = this.state; + + const isIOS = Platform.OS === 'ios'; + const isAndroid = !isIOS; + const buttonStyles = [styles.button]; const textStyles = [styles.text]; - if (color) { - if (Platform.OS === 'ios') { - textStyles.push({color: color}); - } else { - buttonStyles.push({backgroundColor: color}); - } - } const accessibilityTraits = ['button']; + if (disabled) { + accessibilityTraits.push('disabled'); buttonStyles.push(styles.buttonDisabled); textStyles.push(styles.textDisabled); - accessibilityTraits.push('disabled'); + if (bordered) { + buttonStyles.push(styles.buttonBordered, styles.buttonBorderedDisabled); + } + if (isAndroid) { + if (transparent) { + buttonStyles.push(styles.buttonTransparentDisabledAndroid); + } + } + } else { + if (transparent && isAndroid) { + buttonStyles.push(styles.buttonTransparentAndroid); + } + if (bordered && !(isAndroid && transparent)) { + buttonStyles.push(styles.buttonBordered); + } + if (color) { + if (bordered) { + buttonStyles.push({ borderColor: color }); + } + if (isIOS || (isAndroid && (transparent || bordered))) { + textStyles.push({ color }); + } else if (!transparent) { + buttonStyles.push({ backgroundColor: color }); // isAndroid on this line + } + } else { + if (isAndroid && (transparent || bordered)) { + textStyles.push(styles.textBorderedAndroid); + } + } + if (noShadow && isAndroid && !bordered && !transparent) { + buttonStyles.push(styles.buttonUnelevatedAndroid); + } + } + + if (bold && isIOS) { + textStyles.push(styles.textBold); } invariant( typeof title === 'string', 'The title prop of a Button must be a string', ); - const formattedTitle = Platform.OS === 'android' ? title.toUpperCase() : title; - const Touchable = Platform.OS === 'android' ? TouchableNativeFeedback : TouchableOpacity; - return ( - + const formattedTitle = isAndroid ? title.toUpperCase() : title; + const Touchable = isAndroid ? TouchableNativeFeedback : TouchableOpacity; + if (isIOS && bordered) { + return ( {formattedTitle} + + + {formattedTitle} + + - - ); + ); + } else { + return ( + + + {formattedTitle} + + + ); + } + } + + activateIOS = () => { + const { activeAnim } = this.state; + activeAnim.stopAnimation(); + Animated.timing(activeAnim, { toValue: 1, duration: 200, useNativeDriver: true }).start(); + } + deactivateIOS = () => { + const { activeAnim } = this.state; + activeAnim.stopAnimation(); + Animated.timing(activeAnim, { toValue: 0, duration: 200, useNativeDriver: true }).start(); } } const styles = StyleSheet.create({ button: Platform.select({ - ios: {}, + ios: undefined, android: { elevation: 4, - // Material design blue from https://material.google.com/style/color.html#color-color-palette - backgroundColor: '#2196F3', - borderRadius: 2, + backgroundColor: '#2196F3', // Material design blue from https://material.google.com/style/color.html#color-color-palette + borderRadius: 2 + } + }), + buttonTransparentAndroid: { + backgroundColor: 'transparent', + elevation: 0 + }, + buttonBordered: Platform.select({ + ios: { + borderWidth: 1, + borderColor: '#007AFF', + borderRadius: 6, + overflow: 'hidden' // otherwse backgroundColor onPress messes up borderRadius }, + android: { + elevation: 0, + borderColor: '#2196F3', + borderWidth: 1, + backgroundColor: '#FFFFFF' + } }), + buttonUnelevatedAndroid: { + elevation: 0 + }, text: Platform.select({ ios: { - // iOS blue from https://developer.apple.com/ios/human-interface-guidelines/visual-design/color/ - color: '#007AFF', + color: '#007AFF', // iOS blue from https://developer.apple.com/ios/human-interface-guidelines/visual-design/color/ textAlign: 'center', padding: 8, - fontSize: 18, + paddingVertical: 12, + fontSize: 18 }, android: { - color: 'white', + color: '#FFFFFF', textAlign: 'center', padding: 8, - fontWeight: '500', + fontWeight: '500' + } + }), + textBold: Platform.select({ + ios: { + fontWeight: '500' }, + android: undefined }), + textBorderedAndroid: { + color: '#2196F3' + }, buttonDisabled: Platform.select({ - ios: {}, + ios: undefined, android: { elevation: 0, - backgroundColor: '#dfdfdf', + backgroundColor: '#DFDFDF' + } + }), + buttonBorderedDisabled: Platform.select({ + ios: { + borderColor: '#CDCDCD' + }, + android: { + borderColor: '#DFDFDF', + backgroundColor: '#FFFFFF' } }), + buttonTransparentDisabledAndroid: { + backgroundColor: 'transparent' + }, textDisabled: Platform.select({ ios: { - color: '#cdcdcd', + color: '#CDCDCD' }, android: { - color: '#a1a1a1', + color: '#A1A1A1' } }), + textBorderedActiveIOS: { + color: '#FFFFFF' + }, + buttonBorderedActiveIOS: { + position: 'absolute', + width: '100%', + height: '100%', + backgroundColor: '#007AFF' + } }); module.exports = Button;