diff --git a/src/components/Avatar.js b/src/components/Avatar.js index 15923a5289ba..8def4f64508c 100644 --- a/src/components/Avatar.js +++ b/src/components/Avatar.js @@ -1,3 +1,4 @@ +import _ from 'underscore'; import React, {PureComponent} from 'react'; import {Image} from 'react-native'; import PropTypes from 'prop-types'; @@ -9,11 +10,15 @@ const propTypes = { // Extra styles to pass style: PropTypes.arrayOf(PropTypes.any), + + // Set the size of Avatar + size: PropTypes.oneOf(['default', 'small']), }; const defaultProps = { source: '', style: [], + size: 'default', }; class Avatar extends PureComponent { @@ -26,10 +31,10 @@ class Avatar extends PureComponent { this.image = el} source={{uri: this.props.source}} - style={[ - styles.avatarNormal, - ...this.props.style, - ]} + style={_.union([ + this.props.size === 'small' ? styles.avatarSmall : styles.avatarNormal, + this.props.style, + ])} /> ); } diff --git a/src/components/MultipleAvatars.js b/src/components/MultipleAvatars.js index 6c1b636f1819..b446942563ae 100644 --- a/src/components/MultipleAvatars.js +++ b/src/components/MultipleAvatars.js @@ -1,7 +1,7 @@ import React, {memo} from 'react'; import PropTypes from 'prop-types'; import {Image, Text, View} from 'react-native'; -import styles from '../styles/styles'; +import globalStyles from '../styles/styles'; import Avatar from './Avatar'; const propTypes = { @@ -10,58 +10,86 @@ const propTypes = { // Whether this option is currently in focus so we can modify its style optionIsFocused: PropTypes.bool, + + // Set the sie of avatars + size: PropTypes.oneOf(['default', 'small']), + + // Styles to override the basic component styles + styles: PropTypes.shape({ + // Style for First Avatar on Multiple Avatars + // eslint-disable-next-line react/forbid-prop-types + singleAvatar: PropTypes.object, + + // Style for Second Avatar on Multiple Avatars + // eslint-disable-next-line react/forbid-prop-types + secondAvatar: PropTypes.object, + + // Style for avatar Container + // eslint-disable-next-line react/forbid-prop-types + emptyAvatar: PropTypes.object, + }), }; const defaultProps = { avatarImageURLs: [], optionIsFocused: false, + size: 'default', + styles: {}, }; -const MultipleAvatars = ({avatarImageURLs, optionIsFocused}) => { +const MultipleAvatars = ({ + avatarImageURLs, optionIsFocused, size, styles, +}) => { + const avatarContainerStyles = [ + size === 'small' ? globalStyles.emptyAvatarSmall : globalStyles.emptyAvatar, styles.emptyAvatar, + ]; + const singleAvatarStyles = [ + size === 'small' ? globalStyles.singleAvatarSmall : globalStyles.singleAvatar, styles.singleAvatar, + ]; + const secondAvatarStyles = [ + size === 'small' ? globalStyles.secondAvatarSmall : globalStyles.secondAvatar, + optionIsFocused ? globalStyles.focusedAvatar : globalStyles.avatar, + styles.secondAvatar, + ]; + if (!avatarImageURLs.length) { return null; } if (avatarImageURLs.length === 1) { return ( - - + + ); } return ( - + {avatarImageURLs.length === 2 ? ( ) : ( - - + - {avatarImageURLs.length - 1} + + {`+${avatarImageURLs.length - 1}`} )} diff --git a/src/components/OptionsList.js b/src/components/OptionsList.js index b783cca19866..5add5fa4c95a 100644 --- a/src/components/OptionsList.js +++ b/src/components/OptionsList.js @@ -64,6 +64,9 @@ const propTypes = { // Whether to show the title tooltip showTitleTooltip: PropTypes.bool, + + // Toggle between compact and default view of the option + optionMode: PropTypes.oneOf(['compact', 'default']), }; const defaultProps = { @@ -81,6 +84,7 @@ const defaultProps = { headerMessage: '', innerRef: null, showTitleTooltip: false, + optionMode: undefined, }; class OptionsList extends Component { @@ -146,6 +150,7 @@ class OptionsList extends Component { return ( { const textStyle = optionIsFocused ? styles.sidebarLinkActiveText : styles.sidebarLinkText; const textUnreadStyle = (option.isUnread || forceTextUnreadStyle) ? [textStyle, styles.sidebarLinkTextUnread] : [textStyle]; + const displayNameStyle = mode === 'compact' + ? [styles.optionDisplayName, textUnreadStyle, styles.optionDisplayNameCompact, styles.mr2] + : [styles.optionDisplayName, textUnreadStyle]; + const alternateTextStyle = mode === 'compact' + ? [textStyle, styles.optionAlternateText, styles.optionAlternateTextCompact] + : [textStyle, styles.optionAlternateText, styles.mt1]; + const contentContainerStyles = mode === 'compact' + ? [styles.flex1, styles.flexRow, styles.overflowHidden, styles.alignItemsCenter] + : [styles.flex1]; + const sidebarInnerRowStyle = StyleSheet.flatten(mode === 'compact' ? [ + styles.chatLinkRowPressable, + styles.flexGrow1, + styles.optionItemAvatarNameWrapper, + styles.sidebarInnerRowSmall, + styles.justifyContentCenter, + ] : [ + styles.chatLinkRowPressable, + styles.flexGrow1, + styles.optionItemAvatarNameWrapper, + styles.sidebarInnerRow, + styles.justifyContentCenter, + ]); + return ( {hovered => ( @@ -87,14 +115,7 @@ const OptionRow = ({ hovered && !optionIsFocused ? hoverStyle : null, ]} > - + ) } - + {option.alternateText ? ( {option.alternateText} @@ -170,6 +198,10 @@ export default memo(OptionRow, (prevProps, nextProps) => { return false; } + if (prevProps.mode !== nextProps.mode) { + return false; + } + if (prevProps.option.isUnread !== nextProps.option.isUnread) { return false; } diff --git a/src/pages/home/sidebar/SidebarLinks.js b/src/pages/home/sidebar/SidebarLinks.js index 0a9a0d9fdac3..593af80cc973 100644 --- a/src/pages/home/sidebar/SidebarLinks.js +++ b/src/pages/home/sidebar/SidebarLinks.js @@ -148,6 +148,7 @@ class SidebarLinks extends React.Component { hideSectionHeaders showTitleTooltip disableFocusOptions={this.props.isSmallScreenWidth} + optionMode={this.props.priorityMode === CONST.PRIORITY_MODE.GSD ? 'compact' : 'default'} /> diff --git a/src/styles/styles.js b/src/styles/styles.js index 2f2d808aca7c..e320a53b262a 100644 --- a/src/styles/styles.js +++ b/src/styles/styles.js @@ -539,7 +539,13 @@ const styles = { sidebarInnerRow: { height: 64, paddingTop: 12, - paddingRight: 20, + paddingBottom: 12, + paddingLeft: 20, + }, + + sidebarInnerRowSmall: { + height: 52, + paddingTop: 12, paddingBottom: 12, paddingLeft: 20, }, @@ -584,6 +590,13 @@ const styles = { ...whiteSpace.noWrap, }, + optionDisplayNameCompact: { + minWidth: 'auto', + flexBasis: 'auto', + flexGrow: 0, + flexShrink: 0, + }, + optionDisplayNameTooltipWrapper: { position: 'relative', }, @@ -603,6 +616,12 @@ const styles = { lineHeight: 16, }, + optionAlternateTextCompact: { + flexShrink: 1, + flexGrow: 1, + flexBasis: 'auto', + }, + // App Content Wrapper styles appContentWrapper: { backgroundColor: themeColors.appBG, @@ -827,12 +846,29 @@ const styles = { borderRadius: 24, }, + singleAvatarSmall: { + height: 18, + width: 18, + backgroundColor: themeColors.icon, + borderRadius: 18, + }, + secondAvatar: { + position: 'absolute', + right: -18, + bottom: -18, + borderWidth: 3, + borderRadius: 30, + borderColor: 'transparent', + }, + + secondAvatarSmall: { position: 'absolute', right: -13, - bottom: -14, + bottom: -13, + borderWidth: 3, + borderRadius: 18, borderColor: 'transparent', - borderWidth: 2, }, avatarNormal: { @@ -842,11 +878,11 @@ const styles = { borderRadius: variables.componentSizeNormal, }, - avatarText: { + avatarSmall: { + height: variables.avatarSizeSmall, + width: variables.avatarSizeSmall, backgroundColor: themeColors.icon, - borderRadius: 24, - height: 24, - width: 24, + borderRadius: variables.avatarSizeSmall, }, avatarInnerText: { @@ -857,6 +893,14 @@ const styles = { textAlign: 'center', }, + avatarInnerTextSmall: { + color: themeColors.textReversed, + fontSize: variables.fontSizeExtraSmall, + lineHeight: 18, + marginLeft: -2, + textAlign: 'center', + }, + avatarSpace: { top: 3, left: 3, @@ -864,16 +908,24 @@ const styles = { avatar: { backgroundColor: themeColors.sidebar, + borderColor: themeColors.sidebar, }, focusedAvatar: { - backgroundColor: themeColors.border, + backgroundColor: themeColors.pillBG, + borderColor: themeColors.pillBG, }, emptyAvatar: { marginRight: variables.componentSizeNormal - 24, - height: 40, - width: 40, + height: variables.avatarSizeNormal, + width: variables.avatarSizeNormal, + }, + + emptyAvatarSmall: { + marginRight: variables.componentSizeNormal - 28, + height: variables.avatarSizeSmall, + width: variables.avatarSizeSmall, }, modalViewContainer: { diff --git a/src/styles/variables.js b/src/styles/variables.js index 6463935aa836..2bd1de1681a9 100644 --- a/src/styles/variables.js +++ b/src/styles/variables.js @@ -5,7 +5,10 @@ export default { componentSizeLarge: 52, componentBorderRadiusSmall: 4, componentBorderRadiusNormal: 8, + avatarSizeNormal: 40, + avatarSizeSmall: 28, fontSizeSmall: 11, + fontSizeExtraSmall: 9, fontSizeLabel: 13, fontSizeNormal: 15, fontSizeLarge: 17,