Skip to content

Commit

Permalink
Improve Android button appearances (closes GeekyAnts#849, GeekyAnts#896)
Browse files Browse the repository at this point in the history
This implements proper behavior for Material Design flat buttons and icon toggles - these buttons ripple as soon as the touch starts. Conveniently, React Native supports masking these ripples to the parent View's border radius. This also fixes a breakage introduced in GeekyAnts#808 that closes GeekyAnts#896.
  • Loading branch information
kherock committed May 31, 2017
1 parent decae81 commit 7dc594c
Show file tree
Hide file tree
Showing 7 changed files with 606 additions and 587 deletions.
19 changes: 14 additions & 5 deletions index.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -117,7 +117,7 @@ declare module 'native-base' {
* see Widget Title.js
*/
interface Title {
style?: ReactNative.ViewStyle
style?: ReactNative.TextStyle & ReactNative.ViewStyle
}
/**
* see Widget Subtitle/index.js
Expand Down Expand Up @@ -149,7 +149,7 @@ declare module 'native-base' {
/**
* see Widget Button.js
*/
interface Button extends ReactNative.TouchableOpacityProperties,BsStyle {
interface Button extends ReactNative.TouchableNativeFeedbackProperties, ReactNative.TouchableOpacityProperties, BsStyle {
/**
* Defines button style
*/
Expand All @@ -173,6 +173,10 @@ declare module 'native-base' {
// warning?: boolean,
//info?: boolean,
color?: string,
/**
* The ripple color used if `background` from TouchableNativeFeedback isn't explicitly given
*/
androidRippleColor?: string,
/**
* Applies outline button style.
*/
Expand All @@ -189,6 +193,11 @@ declare module 'native-base' {
* For small size button
*/
small?: boolean,
/**
* Renders the the button as a square with a 50% border radius. This is typically used
* in conjunction with `transparent`.
*/
iconButton?: boolean,
/**
* Used for Icon alignment.
* Aligns icon to the left in button.
Expand All @@ -206,9 +215,9 @@ declare module 'native-base' {
disabled?: boolean,
active?: boolean,
inputButton?: boolean,
full?:boolean,
light?:boolean,
dark?:boolean
full?: boolean,
light?: boolean,
dark?: boolean,
}
/**
* see Widget List.js
Expand Down
88 changes: 63 additions & 25 deletions src/basic/Button.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,8 @@


import React, { Component } from 'react';
import { TouchableOpacity, Platform, View, TouchableNativeFeedback } from 'react-native';
import { Platform, StyleSheet, TouchableNativeFeedback, TouchableOpacity, View } from 'react-native';
import color from 'color';
import { connectStyle } from 'native-base-shoutem-theme';
import variables from './../theme/variables/platform';
import { Text } from './Text';
Expand All @@ -11,48 +12,85 @@ import computeProps from '../Utils/computeProps';
import mapPropsToStyleNames from '../Utils/mapPropsToStyleNames';

class Button extends Component {

getInitialStyle() {
return {
borderedBtn: {
borderWidth: (this.props.bordered) ? 1 : undefined,
borderRadius: (this.props.rounded && this.props.bordered) ? variables.borderRadiusLarge : 2,
},
};
}
static contextTypes = {
theme: React.PropTypes.object,
};

prepareRootProps() {
const themeStyle = (this.context.theme) ? this.context.theme['@@shoutem.theme/themeStyle'] : { variables };

const defaultProps = {
style: this.getInitialStyle().borderedBtn,
style: StyleSheet.flatten(this.props.style),
androidRippleColor: themeStyle.variables.androidRippleColorDark,
};

if (this.props.transparent) {
if (defaultProps.style.backgroundColor) {
defaultProps.androidRippleColor = color(defaultProps.style.backgroundColor).alpha(0.26).toString();
}
defaultProps.style.backgroundColor = 'transparent';
}
// Maintain a 3:1 contrast ratio, preferring dark backgrounds
else if (color(defaultProps.style.backgroundColor || '#fff').lightness() < 100 / 3) {
defaultProps.androidRippleColor = variables.androidRippleColor;
}

return computeProps(this.props, defaultProps);
}
render() {
const children = Platform.OS === 'ios'
const themeStyle = (this.context.theme) ? this.context.theme['@@shoutem.theme/themeStyle'] : { variables };

const children = (Platform.OS === 'ios')
? this.props.children
: React.Children.map(this.props.children, child => child.type === Text ? React.cloneElement(child, { uppercase: true, ...child.props }) : child);
if (Platform.OS==='ios' || variables.androidRipple===false || Platform['Version'] <= 21) {
: React.Children.map(this.props.children, child => child && child.type === Text ? React.cloneElement(child, { uppercase: true, ...child.props }) : child);
const rootProps = this.prepareRootProps();
const outerStyle = {};
const innerStyle = { flex: 1 };
for (const prop in rootProps.style) {
if (prop.match(/^border.*Radius$/)) {
outerStyle[prop] = innerStyle[prop] = rootProps.style[prop];
}
else if (prop.match(/^(padding.*|flexDirection|flexWrap|alignContent|alignItems|justifyContent)$/)) {
innerStyle[prop] = rootProps.style[prop];
}
else {
outerStyle[prop] = rootProps.style[prop];
}
};

if (Platform.OS === 'ios' || themeStyle.variables.androidRipple === false || Platform['Version'] <= 21) {
return (
<TouchableOpacity
{...this.prepareRootProps()}
ref={c => this._root = c}
activeOpacity={(this.props.activeOpacity) ? this.props.activeOpacity : 0.5}
activeOpacity={0.5}
{...rootProps}
style={outerStyle}
>
{children}
<View
style={innerStyle}
pointerEvents="box-only">
{children}
</View>
</TouchableOpacity>
);
}
else {
return(
<TouchableNativeFeedback ref={c => this._root = c}
onPress={this.props.onPress}
background={(this.props.androidRippleColor) ? TouchableNativeFeedback.Ripple(this.props.androidRippleColor) : TouchableNativeFeedback.Ripple(variables.androidRippleColor)}
{...this.prepareRootProps()}>
<View {...this.prepareRootProps()}>
{children}
return (
<View style={outerStyle}>
<TouchableNativeFeedback
ref={c => this._root = c}
background={TouchableNativeFeedback.Ripple(rootProps.androidRippleColor, rootProps.transparent)}
{...rootProps}
>
<View
pointerEvents="box-only"
{...rootProps}
style={innerStyle}
>
{children}
</View>
</TouchableNativeFeedback>
</TouchableNativeFeedback>
</View>
);
}
}
Expand Down
Loading

0 comments on commit 7dc594c

Please sign in to comment.