Skip to content

Instantly share code, notes, and snippets.

@CyxouD
Last active December 26, 2019 15:56
Show Gist options
  • Save CyxouD/42723de50446d85c6b4f0122b0c68c15 to your computer and use it in GitHub Desktop.
Save CyxouD/42723de50446d85c6b4f0122b0c68c15 to your computer and use it in GitHub Desktop.
BaseCard component to fix losing background on Android when borderRadius is set, which happens in some cases on React-Native 0.60+ (original issue https://github.com/facebook/react-native/issues/15826). Card, TouchableCard are it's implementations. You can disable this fix for some cases with `fixAndroidBehaviour` prop.
import * as React from 'react';
import { PropsWithChildren } from 'react';
import { Platform, StyleSheet, View } from 'react-native';
const CARD_RADIUS = 16;
const SHADOW = '#0000004D';
export interface FixAndroidBehaviourProp {
// disable it if have issues with rendering without it and already have correct background color under your component
// (fixes this https://github.com/facebook/react-native/issues/15826)
fixAndroidBehaviour?: boolean;
withoutShadow?: boolean;
}
export interface IsAnimatedProp {
isAnimated?: boolean;
}
export interface CardProps
extends PropsWithChildren<any>,
FixAndroidBehaviourProp {
Component: React.ComponentType<PropsWithChildren<any>>;
}
const shadowStyleProps = {
shadowColor: SHADOW,
shadowOffset: {
height: 2,
width: 0,
},
shadowOpacity: 1,
shadowRadius: 10,
elevation: 2,
};
export default class BaseCard extends React.Component<CardProps> {
public render() {
const { fixAndroidBehaviour } = this.props;
if (fixAndroidBehaviour === undefined ? true : fixAndroidBehaviour) {
return this.renderFixContainer();
} else {
return this.renderNormalContainer();
}
}
/** This container is only needed because of this issue on Android with background color not visible
* https://github.com/facebook/react-native/issues/15826.
* TODO When fixed, remove this method
*/
private renderFixContainer() {
const { Component, children, style, withoutShadow, ...other } = this.props;
const {
alignItems,
padding,
paddingBottom,
paddingHorizontal,
paddingTop,
paddingStart,
paddingVertical,
paddingEnd,
paddingLeft,
paddingRight,
flexDirection,
justifyContent,
width,
height,
aspectRatio,
...otherStyle
} = StyleSheet.flatten(style);
const borderRadiuses = {
borderBottomEndRadius: StyleSheet.flatten(style).borderBottomEndRadius,
borderBottomLeftRadius: StyleSheet.flatten(style).borderBottomLeftRadius,
borderBottomRightRadius: StyleSheet.flatten(style)
.borderBottomRightRadius,
borderBottomStartRadius: StyleSheet.flatten(style)
.borderBottomStartRadius,
borderRadius: !this.isNil(StyleSheet.flatten(style).borderRadius)
? StyleSheet.flatten(style).borderRadius
: CARD_RADIUS,
borderTopEndRadius: StyleSheet.flatten(style).borderTopEndRadius,
borderTopLeftRadius: StyleSheet.flatten(style).borderTopLeftRadius,
borderTopRightRadius: StyleSheet.flatten(style).borderTopRightRadius,
borderTopStartRadius: StyleSheet.flatten(style).borderTopStartRadius,
};
return (
<Component
style={[
{
borderRadius: CARD_RADIUS,
overflow: Platform.OS === 'ios' ? 'visible' : 'hidden',
},
withoutShadow ? {} : shadowStyleProps,
otherStyle,
]}
{...other}
>
<View
style={[
{
alignItems,
backgroundColor: StyleSheet.flatten(style).backgroundColor
? StyleSheet.flatten(style).backgroundColor
: 'white',
flex:
StyleSheet.flatten(style).position &&
StyleSheet.flatten(style).left === 0 &&
StyleSheet.flatten(style).right === 0
? 1
: StyleSheet.flatten(style).flex,
flexDirection,
height:
StyleSheet.flatten(style).aspectRatio &&
StyleSheet.flatten(style).width &&
!StyleSheet.flatten(style).height
? (typeof StyleSheet.flatten(style).width === 'number'
? (StyleSheet.flatten(style).width as number)
: parseInt(
StyleSheet.flatten(style).width as string,
10
)) / StyleSheet.flatten(style).aspectRatio!
: StyleSheet.flatten(style).height,
justifyContent,
padding,
paddingBottom,
paddingEnd,
paddingHorizontal,
paddingLeft,
paddingRight,
paddingStart,
paddingTop,
paddingVertical,
width:
StyleSheet.flatten(style).aspectRatio &&
StyleSheet.flatten(style).height &&
!StyleSheet.flatten(style).width
? (typeof StyleSheet.flatten(style).height === 'number'
? (StyleSheet.flatten(style).height as number)
: parseInt(
StyleSheet.flatten(style).height as string,
10
)) * StyleSheet.flatten(style).aspectRatio!
: StyleSheet.flatten(style).width,
},
Platform.OS === 'ios' ? borderRadiuses : {},
]}
>
{children}
</View>
</Component>
);
}
private renderNormalContainer() {
const { Component, children, style, withoutShadow, ...other } = this.props;
return (
<Component
style={[
{ backgroundColor: 'white', borderRadius: CARD_RADIUS },
withoutShadow ? {} : shadowStyleProps,
style,
]}
{...other}
>
{children}
</Component>
);
}
private isNil(value: any) {
return value === null || value === undefined;
}
}
import * as React from 'react';
import { View, ViewProps } from 'react-native';
import BaseCard, { FixAndroidBehaviourProp } from './styled/BaseCard';
export default class Card extends React.Component<
React.PropsWithChildren<ViewProps> & FixAndroidBehaviourProp
> {
public render() {
return <BaseCard Component={View} {...this.props} />;
}
}
import * as React from 'react';
import {
Animated,
TouchableOpacity,
TouchableOpacityProps,
} from 'react-native';
import BaseCard, {
FixAndroidBehaviourProp,
IsAnimatedProp,
} from './styled/BaseCard';
export default class TouchableCard extends React.Component<
React.PropsWithChildren<TouchableOpacityProps> &
FixAndroidBehaviourProp &
IsAnimatedProp
> {
public render() {
const { isAnimated } = this.props;
return (
<BaseCard
Component={
isAnimated
? Animated.createAnimatedComponent(TouchableOpacity)
: TouchableOpacity
}
{...this.props}
/>
);
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment