Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

#35712 selection list refactor #37000

Merged
Merged
Show file tree
Hide file tree
Changes from 20 commits
Commits
Show all changes
21 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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,
luacmartins marked this conversation as resolved.
Show resolved Hide resolved
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,
burczu marked this conversation as resolved.
Show resolved Hide resolved
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,
burczu marked this conversation as resolved.
Show resolved Hide resolved
]}
/>
)}
</>
)}
<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]}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

style had strikethrough style from OfflineWithFeedback.
Removing this caused regression of strikethrough not applied on native when delete item offline

/>
{!!item.alternateText && (
<TextWithTooltip
shouldShowTooltip={showTooltip}
text={item.alternateText}
textStyles={[alternateTextStyles, style]}
/>
)}
</View>
{!!item.rightElement && item.rightElement}
</>
</BaseListItem>
);
}

Expand Down
Loading
Loading