[RN] Extract AvatarListItem

This commit is contained in:
Bettenbuk Zoltan 2019-02-13 10:39:40 +01:00 committed by Saúl Ibarra Corretgé
parent 555f8b3a99
commit 38b1be1291
6 changed files with 223 additions and 60 deletions

View File

@ -1 +1,3 @@
// @flow
export * from './native';

View File

@ -1,3 +1,5 @@
// @flow
export * from './_';
export { default as AbstractPage } from './AbstractPage';
export { default as NavigateSectionList } from './NavigateSectionList';

View File

@ -0,0 +1,203 @@
// @flow
import React, { Component } from 'react';
import { Text } from 'react-native';
import { Icon } from '../../../font-icons';
import { Avatar } from '../../../participants';
import { StyleType } from '../../../styles';
import { type Item } from '../../Types';
import Container from './Container';
import styles, { AVATAR_SIZE, UNDERLAY_COLOR } from './styles';
type Props = {
/**
* Preferred size of the avatar.
*/
avatarSize?: number,
/**
* External style to be applied to the avatar (icon).
*/
avatarStyle?: StyleType,
/**
* External style to be applied to the avatar (text).
*/
avatarTextStyle?: StyleType,
/**
* Children of the component.
*/
children?: ?React$Element<*>,
/**
* item containing data to be rendered
*/
item: Item,
/**
* External style prop to be applied to the extra lines.
*/
linesStyle?: StyleType,
/**
* Function to invoke on press.
*/
onPress: ?Function,
/**
* External style prop to be applied to the title.
*/
titleStyle?: StyleType
};
/**
* Implements a list item with an avatar rendered for it.
*/
export default class AvatarListItem extends Component<Props> {
/**
* Constructor of the component.
*
* @inheritdoc
*/
constructor(props: Props) {
super(props);
this._renderItemLine = this._renderItemLine.bind(this);
}
/**
* Implements {@code Component#render}.
*
* @inheritdoc
*/
render() {
const {
avatarSize = AVATAR_SIZE,
avatarStyle,
avatarTextStyle
} = this.props;
const { avatar, colorBase, lines, title } = this.props.item;
const avatarStyles = {
...styles.avatar,
...this._getAvatarColor(colorBase),
...avatarStyle,
borderRadius: avatarSize / 2,
height: avatarSize,
width: avatarSize
};
const isAvatarURL = Boolean(avatar && avatar.match(/^http[s]*:\/\//i));
return (
<Container
onClick = { this.props.onPress }
style = { styles.listItem }
underlayColor = { UNDERLAY_COLOR }>
<Container style = { styles.avatarContainer }>
<Container style = { avatarStyles }>
{
isAvatarURL && <Avatar
size = { avatarSize }
uri = { avatar } />
}
{
Boolean(avatar && !isAvatarURL) && <Icon
name = { avatar } />
}
{
!avatar && <Text
style = { [
styles.avatarContent,
avatarTextStyle
] }>
{ title.substr(0, 1).toUpperCase() }
</Text>
}
</Container>
</Container>
<Container style = { styles.listItemDetails }>
<Text
numberOfLines = { 1 }
style = { [
styles.listItemText,
styles.listItemTitle,
this.props.titleStyle
] }>
{ title }
</Text>
{this._renderItemLines(lines)}
</Container>
{ this.props.children }
</Container>
);
}
/**
* Returns a style (color) based on the string that determines the color of
* the avatar.
*
* @param {string} colorBase - The string that is the base of the color.
* @private
* @returns {Object}
*/
_getAvatarColor(colorBase) {
if (!colorBase) {
return null;
}
let nameHash = 0;
for (let i = 0; i < colorBase.length; i++) {
nameHash += colorBase.codePointAt(i);
}
return styles[`avatarColor${(nameHash % 5) + 1}`];
}
_renderItemLine: (string, number) => React$Node;
/**
* Renders a single line from the additional lines.
*
* @param {string} line - The line text.
* @param {number} index - The index of the line.
* @private
* @returns {React$Node}
*/
_renderItemLine(line, index) {
if (!line) {
return null;
}
return (
<Text
key = { index }
numberOfLines = { 1 }
style = { [
styles.listItemText,
this.props.linesStyle
] }>
{line}
</Text>
);
}
_renderItemLines: Array<string> => Array<React$Node>;
/**
* Renders the additional item lines, if any.
*
* @param {Array<string>} lines - The lines to render.
* @private
* @returns {Array<React$Node>}
*/
_renderItemLines(lines) {
return lines && lines.length ? lines.map(this._renderItemLine) : null;
}
}

View File

@ -7,9 +7,11 @@ import { ColorPalette } from '../../../styles';
import Container from './Container';
import Text from './Text';
import styles, { UNDERLAY_COLOR } from './styles';
import styles from './styles';
import type { Item } from '../../Types';
import AvatarListItem from './AvatarListItem';
type Props = {
/**
@ -48,34 +50,11 @@ export default class NavigateSectionListItem extends Component<Props> {
*/
constructor(props: Props) {
super(props);
this._getAvatarColor = this._getAvatarColor.bind(this);
this._renderItemLine = this._renderItemLine.bind(this);
this._renderItemLines = this._renderItemLines.bind(this);
}
_getAvatarColor: string => Object;
/**
* Returns a style (color) based on the string that determines the color of
* the avatar.
*
* @param {string} colorBase - The string that is the base of the color.
* @private
* @returns {Object}
*/
_getAvatarColor(colorBase) {
if (!colorBase) {
return null;
}
let nameHash = 0;
for (let i = 0; i < colorBase.length; i++) {
nameHash += colorBase.codePointAt(i);
}
return styles[`avatarColor${(nameHash % 5) + 1}`];
}
_renderItemLine: (string, number) => React$Node;
/**
@ -138,12 +117,8 @@ export default class NavigateSectionListItem extends Component<Props> {
* @returns {ReactElement}
*/
render() {
const { slideActions } = this.props;
const { id, colorBase, lines, title } = this.props.item;
const avatarStyles = {
...styles.avatar,
...this._getAvatarColor(colorBase)
};
const { item, slideActions } = this.props;
const { id } = item;
let right;
// NOTE: The {@code Swipeout} component has an onPress prop encapsulated
@ -165,31 +140,12 @@ export default class NavigateSectionListItem extends Component<Props> {
<Swipeout
backgroundColor = { ColorPalette.transparent }
right = { right }>
<Container
onClick = { this.props.onPress }
style = { styles.listItem }
underlayColor = { UNDERLAY_COLOR }>
<Container style = { styles.avatarContainer }>
<Container style = { avatarStyles }>
<Text style = { styles.avatarContent }>
{title.substr(0, 1).toUpperCase()}
</Text>
</Container>
</Container>
<Container style = { styles.listItemDetails }>
<Text
numberOfLines = { 1 }
style = { [
styles.listItemText,
styles.listItemTitle
] }>
{title}
</Text>
{this._renderItemLines(lines)}
</Container>
<AvatarListItem
item = { item }
onPress = { this.props.onPress }>
{ this.props.secondaryAction
&& this._renderSecondaryAction() }
</Container>
</AvatarListItem>
</Swipeout>
);
}

View File

@ -1,3 +1,6 @@
// @flow
export { default as AvatarListItem } from './AvatarListItem';
export { default as BackButton } from './BackButton';
export { default as Container } from './Container';
export { default as Header } from './Header';

View File

@ -5,7 +5,6 @@ import { StyleSheet } from 'react-native';
import { BoxModel, ColorPalette, createStyleSheet } from '../../../styles';
const AVATAR_OPACITY = 0.4;
const AVATAR_SIZE = 65;
const HEADER_COLOR = ColorPalette.blue;
// Header height is from Android guidelines. Also, this looks good.
@ -13,6 +12,7 @@ const HEADER_HEIGHT = 56;
const OVERLAY_FONT_COLOR = 'rgba(255, 255, 255, 0.6)';
const SECONDARY_ACTION_BUTTON_SIZE = 30;
export const AVATAR_SIZE = 65;
export const HEADER_PADDING = BoxModel.padding;
export const STATUSBAR_COLOR = ColorPalette.blueHighlight;
export const SIDEBAR_WIDTH = 250;
@ -153,10 +153,7 @@ const SECTION_LIST_STYLES = {
avatar: {
alignItems: 'center',
backgroundColor: `rgba(23, 160, 219, ${AVATAR_OPACITY})`,
borderRadius: AVATAR_SIZE,
height: AVATAR_SIZE,
justifyContent: 'center',
width: AVATAR_SIZE
justifyContent: 'center'
},
/**
@ -200,7 +197,7 @@ const SECTION_LIST_STYLES = {
avatarContent: {
backgroundColor: 'rgba(0, 0, 0, 0)',
color: OVERLAY_FONT_COLOR,
fontSize: 32,
fontSize: Math.floor(AVATAR_SIZE / 2),
fontWeight: '100',
textAlign: 'center'
},