UNPKG

react-native-ui-lib

Version:

[![Build Status](https://travis-ci.org/wix/react-native-ui-lib.svg?branch=master)](https://travis-ci.org/wix/react-native-ui-lib) [![npm](https://img.shields.io/npm/v/react-native-ui-lib.svg)](https://www.npmjs.com/package/react-native-ui-lib) [![NPM Down

205 lines (187 loc) • 5.86 kB
import _ from 'lodash'; import PropTypes from 'prop-types'; import React from 'react'; import {StyleSheet, Text} from 'react-native'; import {View as AnimatableView} from 'react-native-animatable'; import {BaseComponent} from '../../commons'; import {BorderRadiuses, Colors, ThemeManager, Typography} from '../../style'; import View from '../view'; const LABEL_FORMATTER_VALUES = [1, 2, 3, 4]; export const BADGE_SIZES = { pimpleSmall: 6, pimpleBig: 10, pimpleHuge: 14, small: 16, default: 20, }; /** * @description: Round colored badge, typically used to show a number * @extends: Animatable.View * @extendslink: https://github.com/oblador/react-native-animatable * @image: https://user-images.githubusercontent.com/33805983/34480753-df7a868a-efb6-11e7-9072-80f5c110a4f3.png * @example: https://github.com/wix/react-native-ui-lib/blob/master/demo/src/screens/componentScreens/BadgesScreen.js */ export default class Badge extends BaseComponent { static displayName = 'Badge'; static propTypes = { /** * Text to show inside the badge. * Not passing a label (undefined) will present a pimple badge. */ label: PropTypes.string, /** * Color of the badge background */ backgroundColor: PropTypes.string, /** * the badge size (default, small) */ size: PropTypes.oneOf(Object.keys(BADGE_SIZES)), /** * width of border around the badge */ borderWidth: PropTypes.number, /** * color of border around the badge */ borderColor: PropTypes.string, /** * Additional styles for the top container */ containerStyle: PropTypes.object, /** * Receives a number from 1 to 4, representing the label's max digit length. * Beyond the max number for that digit length, a "+" will show at the end. * If set to a value not included in LABEL_FORMATTER_VALUES, no formating will occur. * Example: labelLengthFormater={2}, label={124}, label will present "99+". */ labelFormatterLimit: PropTypes.oneOf(LABEL_FORMATTER_VALUES), /** * Use to identify the badge in tests */ testId: PropTypes.string, }; static defaultProps = { size: 'default', }; constructor(props) { super(props); if (props.testId) { console.warn("Badge prop 'testId' is deprecated. Please use RN 'testID' prop instead."); } } isSmallBadge() { const {size} = this.props; return size === 'small'; } generateStyles() { this.styles = createStyles(this.props); } getBadgeSizeStyle() { const {borderWidth, size} = this.props; const label = this.getFormattedLabel(); const badgeHeight = this.isSmallBadge() ? BADGE_SIZES.small : BADGE_SIZES.default; const style = { paddingHorizontal: this.isSmallBadge() ? 4 : 6, height: badgeHeight, minWidth: badgeHeight, }; if (label === undefined) { const pimpleSizes = ['pimpleSmall', 'pimpleBig', 'pimpleHuge']; if (pimpleSizes.includes(size)) { style.minWidth = BADGE_SIZES[size]; style.height = BADGE_SIZES[size]; style.paddingHorizontal = 0; } else { style.minWidth = BADGE_SIZES.pimpleSmall; style.height = BADGE_SIZES.pimpleSmall; style.paddingHorizontal = 0; } } if (borderWidth) { style.minWidth += borderWidth * 2; style.height += borderWidth * 2; } return style; } getFormattedLabel() { const {labelFormatterLimit, label} = this.getThemeProps(); if (isNaN(label)) { return label; } if (LABEL_FORMATTER_VALUES.includes(labelFormatterLimit)) { const maxLabelNumber = 10 ** labelFormatterLimit - 1; let formattedLabel = label; if (formattedLabel > maxLabelNumber) { formattedLabel = `${maxLabelNumber}+`; } return formattedLabel; } else { return label; } } renderLabel() { return ( <Text style={[this.styles.label, this.isSmallBadge() && this.styles.labelSmall]} allowFontScaling={false} numberOfLines={1} testID="badge" > {this.getFormattedLabel()} </Text> ); } render() { // TODO: remove testId after deprecation const {borderWidth, borderColor, containerStyle, testId, testID, ...others} = this.props; const backgroundStyle = this.props.backgroundColor && {backgroundColor: this.props.backgroundColor}; const sizeStyle = this.getBadgeSizeStyle(); const animationProps = this.extractAnimationProps(); const Container = !_.isEmpty(animationProps) ? AnimatableView : View; if (!_.isEmpty(animationProps)) { console.warn( 'Badge component will soon stop supporting animationProps.' + 'Please wrap your Badge component with your own animation component, such as Animatable.View', ); } return ( // The extra View wrapper is to break badge's flex-ness <View style={containerStyle} {...others} backgroundColor={undefined}> <Container testID={testID || testId} style={[ sizeStyle, this.styles.badge, borderWidth && {borderWidth}, borderColor && {borderColor}, backgroundStyle, ]} {...animationProps} > {this.renderLabel()} </Container> </View> ); } } function createStyles() { return StyleSheet.create({ badge: { alignSelf: 'flex-start', borderRadius: BorderRadiuses.br100, backgroundColor: ThemeManager.primaryColor, alignItems: 'center', justifyContent: 'center', }, label: { ...Typography.text90, color: Colors.white, backgroundColor: 'transparent', }, labelSmall: { ...Typography.text100, lineHeight: undefined, }, }); }