Skip to content

Commit

Permalink
Merge pull request Expensify#37000 from burczu/refactor/35712-selecti…
Browse files Browse the repository at this point in the history
…on-list-refactor

Expensify#35712 selection list refactor
  • Loading branch information
luacmartins authored Feb 21, 2024
2 parents 5707114 + 45fd85b commit 8889914
Show file tree
Hide file tree
Showing 32 changed files with 245 additions and 110 deletions.
2 changes: 2 additions & 0 deletions src/components/DatePicker/CalendarPicker/YearPickerModal.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import HeaderWithBackButton from '@components/HeaderWithBackButton';
import Modal from '@components/Modal';
import ScreenWrapper from '@components/ScreenWrapper';
import SelectionList from '@components/SelectionList';
import RadioListItem from '@components/SelectionList/RadioListItem';
import useLocalize from '@hooks/useLocalize';
import useThemeStyles from '@hooks/useThemeStyles';
import CONST from '@src/CONST';
Expand Down Expand Up @@ -79,6 +80,7 @@ function YearPickerModal({isVisible, years, currentYear = new Date().getFullYear
showScrollIndicator
shouldStopPropagation
shouldUseDynamicMaxToRenderPerBatch
ListItem={RadioListItem}
/>
</ScreenWrapper>
</Modal>
Expand Down
64 changes: 12 additions & 52 deletions src/components/SelectionList/BaseListItem.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,34 +4,31 @@ import Icon from '@components/Icon';
import * as Expensicons from '@components/Icon/Expensicons';
import OfflineWithFeedback from '@components/OfflineWithFeedback';
import PressableWithFeedback from '@components/Pressable/PressableWithFeedback';
import Text from '@components/Text';
import useLocalize from '@hooks/useLocalize';
import useStyleUtils from '@hooks/useStyleUtils';
import useTheme from '@hooks/useTheme';
import useThemeStyles from '@hooks/useThemeStyles';
import CONST from '@src/CONST';
import RadioListItem from './RadioListItem';
import type {BaseListItemProps, ListItem} from './types';
import UserListItem from './UserListItem';

function BaseListItem<TItem extends ListItem>({
item,
isFocused = false,
wrapperStyle,
selectMultipleStyle,
isDisabled = false,
showTooltip,
shouldPreventDefaultFocusOnSelectRow = false,
canSelectMultiple = false,
onSelectRow,
onDismissError = () => {},
rightHandSideComponent,
keyForList,
errors,
pendingAction,
FooterComponent,
children,
}: BaseListItemProps<TItem>) {
const theme = useTheme();
const styles = useThemeStyles();
const StyleUtils = useStyleUtils();
const {translate} = useLocalize();
const isUserItem = 'icons' in item && item?.icons?.length && item.icons.length > 0;
const ListItem = isUserItem ? UserListItem : RadioListItem;

const rightHandSideComponentRender = () => {
if (canSelectMultiple || !rightHandSideComponent) {
Expand All @@ -48,8 +45,8 @@ function BaseListItem<TItem extends ListItem>({
return (
<OfflineWithFeedback
onClose={() => onDismissError(item)}
pendingAction={isUserItem ? item.pendingAction : undefined}
errors={isUserItem ? item.errors : undefined}
pendingAction={pendingAction}
errors={errors}
errorRowStyles={styles.ph5}
>
<PressableWithFeedback
Expand All @@ -65,31 +62,13 @@ function BaseListItem<TItem extends ListItem>({
>
{({hovered}) => (
<>
<View
style={[
styles.flex1,
styles.justifyContentBetween,
styles.sidebarLinkInner,
styles.userSelectNone,
isUserItem ? styles.peopleRow : styles.optionRow,
isFocused && styles.sidebarLinkActive,
]}
>
<View style={wrapperStyle}>
{canSelectMultiple && (
<View
role={CONST.ACCESSIBILITY_ROLE.BUTTON}
style={StyleUtils.getCheckboxPressableStyle()}
>
<View
style={[
StyleUtils.getCheckboxContainerStyle(20),
styles.mr3,
item.isSelected && styles.checkedContainer,
item.isSelected && styles.borderColorFocus,
item.isDisabled && styles.cursorDisabled,
item.isDisabled && styles.buttonOpacityDisabled,
]}
>
<View style={selectMultipleStyle}>
{item.isSelected && (
<Icon
src={Expensicons.Checkmark}
Expand All @@ -102,22 +81,7 @@ function BaseListItem<TItem extends ListItem>({
</View>
)}

<ListItem
item={item}
textStyles={[
styles.optionDisplayName,
isFocused ? styles.sidebarLinkActiveText : styles.sidebarLinkText,
styles.sidebarLinkTextBold,
styles.pre,
item.alternateText ? styles.mb1 : null,
]}
alternateTextStyles={[styles.textLabelSupporting, styles.lh16, styles.pre]}
isDisabled={isDisabled}
onSelectRow={() => onSelectRow(item)}
showTooltip={showTooltip}
isFocused={isFocused}
isHovered={hovered}
/>
{typeof children === 'function' ? children(hovered) : children}

{!canSelectMultiple && item.isSelected && !rightHandSideComponent && (
<View
Expand All @@ -134,11 +98,7 @@ function BaseListItem<TItem extends ListItem>({
)}
{rightHandSideComponentRender()}
</View>
{isUserItem && item.invitedSecondaryLogin && (
<Text style={[styles.ml9, styles.ph5, styles.pb3, styles.textLabelSupporting]}>
{translate('workspace.people.invitedBySecondaryLogin', {secondaryLogin: item.invitedSecondaryLogin})}
</Text>
)}
{FooterComponent}
</>
)}
</PressableWithFeedback>
Expand Down
4 changes: 2 additions & 2 deletions src/components/SelectionList/BaseSelectionList.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -22,12 +22,12 @@ import Log from '@libs/Log';
import variables from '@styles/variables';
import CONST from '@src/CONST';
import {isEmptyObject} from '@src/types/utils/EmptyObject';
import BaseListItem from './BaseListItem';
import type {BaseSelectionListProps, ButtonOrCheckBoxRoles, FlattenedSectionsReturn, ListItem, Section, SectionListDataType} from './types';

function BaseSelectionList<TItem extends ListItem>(
{
sections,
ListItem,
canSelectMultiple = false,
onSelectRow,
onSelectAll,
Expand Down Expand Up @@ -280,7 +280,7 @@ function BaseSelectionList<TItem extends ListItem>(
const showTooltip = shouldShowTooltips && normalizedIndex < 10;

return (
<BaseListItem
<ListItem
item={item}
isFocused={isItemFocused}
isDisabled={isDisabled}
Expand Down
62 changes: 48 additions & 14 deletions src/components/SelectionList/RadioListItem.tsx
Original file line number Diff line number Diff line change
@@ -1,28 +1,62 @@
import React from 'react';
import {View} from 'react-native';
import TextWithTooltip from '@components/TextWithTooltip';
import useStyleUtils from '@hooks/useStyleUtils';
import useThemeStyles from '@hooks/useThemeStyles';
import type {ListItemProps} from './types';
import BaseListItem from './BaseListItem';
import type {RadioListItemProps} from './types';

function RadioListItem({item, showTooltip, textStyles, alternateTextStyles}: ListItemProps) {
function RadioListItem({
item,
isFocused,
showTooltip,
isDisabled,
canSelectMultiple,
onSelectRow,
onDismissError,
shouldPreventDefaultFocusOnSelectRow,
rightHandSideComponent,
}: RadioListItemProps) {
const styles = useThemeStyles();
const StyleUtils = useStyleUtils();

return (
<View style={[styles.flex1, styles.alignItemsStart]}>
<TextWithTooltip
shouldShowTooltip={showTooltip}
text={item.text}
textStyles={textStyles}
/>

{!!item.alternateText && (
<BaseListItem
item={item}
wrapperStyle={[styles.flex1, styles.justifyContentBetween, styles.sidebarLinkInner, styles.userSelectNone, styles.optionRow, isFocused && styles.sidebarLinkActive]}
selectMultipleStyle={[StyleUtils.getCheckboxContainerStyle(20), StyleUtils.getMultiselectListStyles(!!item.isSelected, !!item.isDisabled)]}
isFocused={isFocused}
isDisabled={isDisabled}
showTooltip={showTooltip}
canSelectMultiple={canSelectMultiple}
onSelectRow={onSelectRow}
onDismissError={onDismissError}
shouldPreventDefaultFocusOnSelectRow={shouldPreventDefaultFocusOnSelectRow}
rightHandSideComponent={rightHandSideComponent}
keyForList={item.keyForList}
>
<View style={[styles.flex1, styles.alignItemsStart]}>
<TextWithTooltip
shouldShowTooltip={showTooltip}
text={item.alternateText}
textStyles={alternateTextStyles}
text={item.text}
textStyles={[
styles.optionDisplayName,
isFocused ? styles.sidebarLinkActiveText : styles.sidebarLinkText,
styles.sidebarLinkTextBold,
styles.pre,
item.alternateText ? styles.mb1 : null,
]}
/>
)}
</View>

{!!item.alternateText && (
<TextWithTooltip
shouldShowTooltip={showTooltip}
text={item.alternateText}
textStyles={[styles.textLabelSupporting, styles.lh16, styles.pre]}
/>
)}
</View>
</BaseListItem>
);
}

Expand Down
116 changes: 81 additions & 35 deletions src/components/SelectionList/UserListItem.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,61 +2,107 @@ import React from 'react';
import {View} from 'react-native';
import MultipleAvatars from '@components/MultipleAvatars';
import SubscriptAvatar from '@components/SubscriptAvatar';
import Text from '@components/Text';
import TextWithTooltip from '@components/TextWithTooltip';
import useLocalize from '@hooks/useLocalize';
import useStyleUtils from '@hooks/useStyleUtils';
import useTheme from '@hooks/useTheme';
import useThemeStyles from '@hooks/useThemeStyles';
import type {ListItemProps} from './types';
import BaseListItem from './BaseListItem';
import type {UserListItemProps} from './types';

function UserListItem({item, textStyles, alternateTextStyles, showTooltip, style, isFocused, isHovered}: ListItemProps) {
function UserListItem({
item,
isFocused,
showTooltip,
isDisabled,
canSelectMultiple,
onSelectRow,
onDismissError,
shouldPreventDefaultFocusOnSelectRow,
rightHandSideComponent,
}: UserListItemProps) {
const styles = useThemeStyles();
const theme = useTheme();
const StyleUtils = useStyleUtils();
const {translate} = useLocalize();

const focusedBackgroundColor = styles.sidebarLinkActive.backgroundColor;
const subscriptAvatarBorderColor = isFocused ? focusedBackgroundColor : theme.sidebar;
const hoveredBackgroundColor = !!styles.sidebarLinkHover && 'backgroundColor' in styles.sidebarLinkHover ? styles.sidebarLinkHover.backgroundColor : theme.sidebar;

return (
<>
{!!item.icons && (
<BaseListItem
item={item}
wrapperStyle={[styles.flex1, styles.justifyContentBetween, styles.sidebarLinkInner, styles.userSelectNone, styles.peopleRow, isFocused && styles.sidebarLinkActive]}
selectMultipleStyle={[StyleUtils.getCheckboxContainerStyle(20), StyleUtils.getMultiselectListStyles(!!item.isSelected, !!item.isDisabled)]}
isFocused={isFocused}
isDisabled={isDisabled}
showTooltip={showTooltip}
canSelectMultiple={canSelectMultiple}
onSelectRow={onSelectRow}
onDismissError={onDismissError}
shouldPreventDefaultFocusOnSelectRow={shouldPreventDefaultFocusOnSelectRow}
rightHandSideComponent={rightHandSideComponent}
errors={item.errors}
pendingAction={item.pendingAction}
FooterComponent={
item.invitedSecondaryLogin ? (
<Text style={[styles.ml9, styles.ph5, styles.pb3, styles.textLabelSupporting]}>
{translate('workspace.people.invitedBySecondaryLogin', {secondaryLogin: item.invitedSecondaryLogin})}
</Text>
) : undefined
}
keyForList={item.keyForList}
>
{(hovered) => (
<>
{item.shouldShowSubscript ? (
<SubscriptAvatar
mainAvatar={item.icons[0]}
secondaryAvatar={item.icons[1]}
showTooltip={showTooltip}
backgroundColor={isHovered && !isFocused ? hoveredBackgroundColor : subscriptAvatarBorderColor}
/>
) : (
<MultipleAvatars
icons={item.icons ?? []}
{!!item.icons && (
<>
{item.shouldShowSubscript ? (
<SubscriptAvatar
mainAvatar={item.icons[0]}
secondaryAvatar={item.icons[1]}
showTooltip={showTooltip}
backgroundColor={hovered && !isFocused ? hoveredBackgroundColor : subscriptAvatarBorderColor}
/>
) : (
<MultipleAvatars
icons={item.icons ?? []}
shouldShowTooltip={showTooltip}
secondAvatarStyle={[
StyleUtils.getBackgroundAndBorderStyle(theme.sidebar),
isFocused ? StyleUtils.getBackgroundAndBorderStyle(focusedBackgroundColor) : undefined,
hovered && !isFocused ? StyleUtils.getBackgroundAndBorderStyle(hoveredBackgroundColor) : undefined,
]}
/>
)}
</>
)}
<View style={[styles.flex1, styles.flexColumn, styles.justifyContentCenter, styles.alignItemsStretch, styles.optionRow]}>
<TextWithTooltip
shouldShowTooltip={showTooltip}
secondAvatarStyle={[
StyleUtils.getBackgroundAndBorderStyle(theme.sidebar),
isFocused ? StyleUtils.getBackgroundAndBorderStyle(focusedBackgroundColor) : undefined,
isHovered && !isFocused ? StyleUtils.getBackgroundAndBorderStyle(hoveredBackgroundColor) : undefined,
text={item.text}
textStyles={[
styles.optionDisplayName,
isFocused ? styles.sidebarLinkActiveText : styles.sidebarLinkText,
styles.sidebarLinkTextBold,
styles.pre,
item.alternateText ? styles.mb1 : null,
]}
/>
)}
{!!item.alternateText && (
<TextWithTooltip
shouldShowTooltip={showTooltip}
text={item.alternateText}
textStyles={[styles.textLabelSupporting, styles.lh16, styles.pre]}
/>
)}
</View>
{!!item.rightElement && item.rightElement}
</>
)}
<View style={[styles.flex1, styles.flexColumn, styles.justifyContentCenter, styles.alignItemsStretch, styles.optionRow]}>
<TextWithTooltip
shouldShowTooltip={showTooltip}
text={item.text}
textStyles={[textStyles, style]}
/>
{!!item.alternateText && (
<TextWithTooltip
shouldShowTooltip={showTooltip}
text={item.alternateText}
textStyles={[alternateTextStyles, style]}
/>
)}
</View>
{!!item.rightElement && item.rightElement}
</>
</BaseListItem>
);
}

Expand Down
Loading

0 comments on commit 8889914

Please sign in to comment.