diff --git a/src/components/DatePicker/CalendarPicker/YearPickerModal.tsx b/src/components/DatePicker/CalendarPicker/YearPickerModal.tsx index c88364b7e8f7..c7816b710692 100644 --- a/src/components/DatePicker/CalendarPicker/YearPickerModal.tsx +++ b/src/components/DatePicker/CalendarPicker/YearPickerModal.tsx @@ -65,7 +65,6 @@ function YearPickerModal({isVisible, years, currentYear = new Date().getFullYear onBackButtonPress={onClose} /> ({ pressableStyle, wrapperStyle, containerStyle, - selectMultipleStyle, isDisabled = false, shouldPreventDefaultFocusOnSelectRow = false, canSelectMultiple = false, onSelectRow, - onCheckboxPress, onDismissError = () => {}, rightHandSideComponent, - checkmarkPosition = CONST.DIRECTION.LEFT, keyForList, errors, pendingAction, @@ -34,7 +29,6 @@ function BaseListItem({ }: BaseListItemProps) { const theme = useTheme(); const styles = useThemeStyles(); - const StyleUtils = useStyleUtils(); const {hovered, bind} = useHover(); const rightHandSideComponentRender = () => { @@ -49,14 +43,6 @@ function BaseListItem({ return rightHandSideComponent; }; - const handleCheckboxPress = () => { - if (onCheckboxPress) { - onCheckboxPress(item); - } else { - onSelectRow(item); - } - }; - return ( onDismissError(item)} @@ -80,45 +66,8 @@ function BaseListItem({ style={pressableStyle} > - {canSelectMultiple && checkmarkPosition === CONST.DIRECTION.LEFT && ( - - - {item.isSelected && ( - - )} - - - )} - {typeof children === 'function' ? children(hovered) : children} - {canSelectMultiple && checkmarkPosition === CONST.DIRECTION.RIGHT && ( - - - - )} - {!canSelectMultiple && item.isSelected && !rightHandSideComponent && ( ( shouldShowTooltips = true, shouldUseDynamicMaxToRenderPerBatch = false, rightHandSideComponent, - checkmarkPosition, isLoadingNewOptions = false, onLayout, customListHeader, @@ -335,7 +334,6 @@ function BaseSelectionList( onDismissError={() => onDismissError?.(item)} shouldPreventDefaultFocusOnSelectRow={shouldPreventDefaultFocusOnSelectRow} rightHandSideComponent={rightHandSideComponent} - checkmarkPosition={checkmarkPosition} keyForList={item.keyForList ?? ''} isMultilineSupported={isRowMultilineSupported} /> diff --git a/src/components/SelectionList/InviteMemberListItem.tsx b/src/components/SelectionList/InviteMemberListItem.tsx new file mode 100644 index 000000000000..aabde59ac374 --- /dev/null +++ b/src/components/SelectionList/InviteMemberListItem.tsx @@ -0,0 +1,134 @@ +import Str from 'expensify-common/lib/str'; +import React, {useCallback} from 'react'; +import {View} from 'react-native'; +import MultipleAvatars from '@components/MultipleAvatars'; +import PressableWithFeedback from '@components/Pressable/PressableWithFeedback'; +import SelectCircle from '@components/SelectCircle'; +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 CONST from '@src/CONST'; +import BaseListItem from './BaseListItem'; +import type {InviteMemberListItemProps} from './types'; + +function InviteMemberListItem({ + item, + isFocused, + showTooltip, + isDisabled, + canSelectMultiple, + onSelectRow, + onCheckboxPress, + onDismissError, + shouldPreventDefaultFocusOnSelectRow, + rightHandSideComponent, +}: InviteMemberListItemProps) { + 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; + + const handleCheckboxPress = useCallback(() => { + if (onCheckboxPress) { + onCheckboxPress(item); + } else { + onSelectRow(item); + } + }, [item, onCheckboxPress, onSelectRow]); + + return ( + + {translate('workspace.people.invitedBySecondaryLogin', {secondaryLogin: item.invitedSecondaryLogin})} + + ) : undefined + } + keyForList={item.keyForList} + > + {(hovered?: boolean) => ( + <> + {!!item.icons && + (item.shouldShowSubscript ? ( + + ) : ( + + ))} + + + {!!item.alternateText && ( + + )} + + {!!item.rightElement && item.rightElement} + {canSelectMultiple && ( + + + + )} + + )} + + ); +} + +InviteMemberListItem.displayName = 'InviteMemberListItem'; + +export default InviteMemberListItem; diff --git a/src/components/SelectionList/RadioListItem.tsx b/src/components/SelectionList/RadioListItem.tsx index e7823209cf7e..b6b11c2f273c 100644 --- a/src/components/SelectionList/RadioListItem.tsx +++ b/src/components/SelectionList/RadioListItem.tsx @@ -1,7 +1,6 @@ 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 CONST from '@src/CONST'; import BaseListItem from './BaseListItem'; @@ -12,17 +11,13 @@ function RadioListItem({ isFocused, showTooltip, isDisabled, - canSelectMultiple, onSelectRow, - onCheckboxPress, onDismissError, shouldPreventDefaultFocusOnSelectRow, rightHandSideComponent, - checkmarkPosition, isMultilineSupported = false, }: RadioListItemProps) { const styles = useThemeStyles(); - const StyleUtils = useStyleUtils(); const fullTitle = isMultilineSupported ? item.text?.trimStart() : item.text; const indentsLength = (item.text?.length ?? 0) - (fullTitle?.length ?? 0); const paddingLeft = Math.floor(indentsLength / CONST.INDENTS.length) * styles.ml3.marginLeft; @@ -31,17 +26,13 @@ function RadioListItem({ <> diff --git a/src/components/SelectionList/TableListItem.tsx b/src/components/SelectionList/TableListItem.tsx index b7c3ed549d82..a233f5dd83fd 100644 --- a/src/components/SelectionList/TableListItem.tsx +++ b/src/components/SelectionList/TableListItem.tsx @@ -1,10 +1,14 @@ -import React from 'react'; +import React, {useCallback} from 'react'; import {View} from 'react-native'; +import Icon from '@components/Icon'; +import * as Expensicons from '@components/Icon/Expensicons'; import MultipleAvatars from '@components/MultipleAvatars'; +import PressableWithFeedback from '@components/Pressable/PressableWithFeedback'; import TextWithTooltip from '@components/TextWithTooltip'; import useStyleUtils from '@hooks/useStyleUtils'; import useTheme from '@hooks/useTheme'; import useThemeStyles from '@hooks/useThemeStyles'; +import CONST from '@src/CONST'; import BaseListItem from './BaseListItem'; import type {TableListItemProps} from './types'; @@ -19,7 +23,6 @@ function TableListItem({ onDismissError, shouldPreventDefaultFocusOnSelectRow, rightHandSideComponent, - checkmarkPosition, }: TableListItemProps) { const styles = useThemeStyles(); const theme = useTheme(); @@ -28,29 +31,55 @@ function TableListItem({ const focusedBackgroundColor = styles.sidebarLinkActive.backgroundColor; const hoveredBackgroundColor = styles.sidebarLinkHover?.backgroundColor ? styles.sidebarLinkHover.backgroundColor : theme.sidebar; + const handleCheckboxPress = useCallback(() => { + if (onCheckboxPress) { + onCheckboxPress(item); + } else { + onSelectRow(item); + } + }, [item, onCheckboxPress, onSelectRow]); + return ( {(hovered) => ( <> + {canSelectMultiple && ( + + + {item.isSelected && ( + + )} + + + )} {!!item.icons && ( { + if (onCheckboxPress) { + onCheckboxPress(item); + } else { + onSelectRow(item); + } + }, [item, onCheckboxPress, onSelectRow]); + return ( {(hovered?: boolean) => ( <> + {canSelectMultiple && ( + + + {item.isSelected && ( + + )} + + + )} {!!item.icons && (item.shouldShowSubscript ? ( (props: BaseSelectionListProps, ref: ForwardedRef) { - return ( - - // eslint-disable-next-line react/jsx-props-no-spreading - {...props} - ref={ref} - onScrollBeginDrag={() => Keyboard.dismiss()} - /> - ); -} - -SelectionList.displayName = 'SelectionList'; - -export default forwardRef(SelectionList); diff --git a/src/components/SelectionList/index.android.tsx b/src/components/SelectionList/index.native.tsx similarity index 96% rename from src/components/SelectionList/index.android.tsx rename to src/components/SelectionList/index.native.tsx index f8e54b219f5b..baccdf7c6024 100644 --- a/src/components/SelectionList/index.android.tsx +++ b/src/components/SelectionList/index.native.tsx @@ -10,7 +10,6 @@ function SelectionList(props: BaseSelectionListProps Keyboard.dismiss()} /> ); diff --git a/src/components/SelectionList/selectionListPropTypes.js b/src/components/SelectionList/selectionListPropTypes.js deleted file mode 100644 index eed1966f8222..000000000000 --- a/src/components/SelectionList/selectionListPropTypes.js +++ /dev/null @@ -1,200 +0,0 @@ -import PropTypes from 'prop-types'; -import _ from 'underscore'; -import sourcePropTypes from '@components/Image/sourcePropTypes'; -import CONST from '@src/CONST'; - -const commonListItemPropTypes = { - /** Whether this item is focused (for arrow key controls) */ - isFocused: PropTypes.bool, - - /** Style to be applied to Text */ - textStyles: PropTypes.arrayOf(PropTypes.object), - - /** Style to be applied on the alternate text */ - alternateTextStyles: PropTypes.arrayOf(PropTypes.object), - - /** Whether this item is disabled */ - isDisabled: PropTypes.bool, - - /** Whether this item should show Tooltip */ - showTooltip: PropTypes.bool.isRequired, - - /** Whether to use the Checkbox (multiple selection) instead of the Checkmark (single selection) */ - canSelectMultiple: PropTypes.bool, - - /** Callback to fire when the item is selected */ - onSelectRow: PropTypes.func.isRequired, - - /** Callback to fire when an error is dismissed */ - onDismissError: PropTypes.func, -}; - -const userListItemPropTypes = { - ...commonListItemPropTypes, - - /** The section list item */ - item: PropTypes.shape({ - /** Text to display */ - text: PropTypes.string.isRequired, - - /** Alternate text to display */ - alternateText: PropTypes.string, - - /** Key used internally by React */ - keyForList: PropTypes.string.isRequired, - - /** Whether this option is selected */ - isSelected: PropTypes.bool, - - /** Whether this option is disabled for selection */ - isDisabled: PropTypes.bool, - - /** User accountID */ - accountID: PropTypes.number, - - /** User login */ - login: PropTypes.string, - - /** Element to show on the right side of the item */ - rightElement: PropTypes.element, - - /** Icons for the user (can be multiple if it's a Workspace) */ - icons: PropTypes.arrayOf( - PropTypes.shape({ - source: PropTypes.oneOfType([PropTypes.string, sourcePropTypes]).isRequired, - name: PropTypes.string, - type: PropTypes.string, - }), - ), - - /** Errors that this user may contain */ - errors: PropTypes.objectOf(PropTypes.string), - - /** The type of action that's pending */ - pendingAction: PropTypes.oneOf(_.values(CONST.RED_BRICK_ROAD_PENDING_ACTION)), - }).isRequired, -}; - -const radioListItemPropTypes = { - ...commonListItemPropTypes, - - /** The section list item */ - item: PropTypes.shape({ - /** Text to display */ - text: PropTypes.string.isRequired, - - /** Alternate text to display */ - alternateText: PropTypes.string, - - /** Key used internally by React */ - keyForList: PropTypes.string.isRequired, - - /** Whether this option is selected */ - isSelected: PropTypes.bool, - }).isRequired, -}; - -const baseListItemPropTypes = { - ...commonListItemPropTypes, - item: PropTypes.oneOfType([PropTypes.shape(userListItemPropTypes.item), PropTypes.shape(radioListItemPropTypes.item)]), - shouldPreventDefaultFocusOnSelectRow: PropTypes.bool, -}; - -const propTypes = { - /** Sections for the section list */ - sections: PropTypes.arrayOf( - PropTypes.shape({ - /** Title of the section */ - title: PropTypes.string, - - /** Array of options */ - data: PropTypes.arrayOf(PropTypes.oneOfType([PropTypes.shape(userListItemPropTypes.item), PropTypes.shape(radioListItemPropTypes.item)])), - - /** Whether this section items disabled for selection */ - isDisabled: PropTypes.bool, - }), - ).isRequired, - - /** Whether this is a multi-select list */ - canSelectMultiple: PropTypes.bool, - - /** Callback to fire when a row is pressed */ - onSelectRow: PropTypes.func.isRequired, - - /** Callback to fire when "Select All" checkbox is pressed. Only use along with `canSelectMultiple` */ - onSelectAll: PropTypes.func, - - /** Callback to fire when an error is dismissed */ - onDismissError: PropTypes.func, - - /** Label for the text input */ - textInputLabel: PropTypes.string, - - /** Placeholder for the text input */ - textInputPlaceholder: PropTypes.string, - - /** Value for the text input */ - textInputValue: PropTypes.string, - - /** Max length for the text input */ - textInputMaxLength: PropTypes.number, - - /** Callback to fire when the text input changes */ - onChangeText: PropTypes.func, - - /** Input mode for the text input */ - inputMode: PropTypes.string, - - /** Item `keyForList` to focus initially */ - initiallyFocusedOptionKey: PropTypes.string, - - /** Callback to fire when the list is scrolled */ - onScroll: PropTypes.func, - - /** Callback to fire when the list is scrolled and the user begins dragging */ - onScrollBeginDrag: PropTypes.func, - - /** Message to display at the top of the list */ - headerMessage: PropTypes.string, - - /** Text to display on the confirm button */ - confirmButtonText: PropTypes.string, - - /** Callback to fire when the confirm button is pressed */ - onConfirm: PropTypes.func, - - /** Whether to show the vertical scroll indicator */ - showScrollIndicator: PropTypes.bool, - - /** Whether to show the loading placeholder */ - showLoadingPlaceholder: PropTypes.bool, - - /** Whether to show the default confirm button */ - showConfirmButton: PropTypes.bool, - - /** Whether to stop automatic form submission on pressing enter key or not */ - shouldStopPropagation: PropTypes.bool, - - /** Whether to prevent default focusing of options and focus the textinput when selecting an option */ - shouldPreventDefaultFocusOnSelectRow: PropTypes.bool, - - /** A ref to forward to the TextInput */ - inputRef: PropTypes.oneOfType([PropTypes.object]), - - /** Custom content to display in the header */ - headerContent: PropTypes.oneOfType([PropTypes.func, PropTypes.node]), - - /** Custom content to display in the footer */ - footerContent: PropTypes.oneOfType([PropTypes.func, PropTypes.node]), - - /** Whether to show the tooltip text */ - shouldShowTooltips: PropTypes.bool, - - /** Whether to use dynamic maxToRenderPerBatch depending on the visible number of elements */ - shouldUseDynamicMaxToRenderPerBatch: PropTypes.bool, - - /** Right hand side component to display in the list item. Function has list item passed as the param */ - rightHandSideComponent: PropTypes.oneOfType([PropTypes.func, PropTypes.node]), -}; - -export {propTypes, baseListItemPropTypes, radioListItemPropTypes, userListItemPropTypes}; diff --git a/src/components/SelectionList/types.ts b/src/components/SelectionList/types.ts index dfad7dc8860b..9e7e64896f4f 100644 --- a/src/components/SelectionList/types.ts +++ b/src/components/SelectionList/types.ts @@ -1,11 +1,11 @@ import type {MutableRefObject, ReactElement, ReactNode} from 'react'; import type {GestureResponderEvent, InputModeOptions, LayoutChangeEvent, SectionListData, StyleProp, TextInput, TextStyle, ViewStyle} from 'react-native'; -import type {ValueOf} from 'type-fest'; import type {MaybePhraseKey} from '@libs/Localize'; import type CONST from '@src/CONST'; import type {Errors, Icon, PendingAction} from '@src/types/onyx/OnyxCommon'; import type {ReceiptErrors} from '@src/types/onyx/Transaction'; import type ChildrenProps from '@src/types/utils/ChildrenProps'; +import type InviteMemberListItem from './InviteMemberListItem'; import type RadioListItem from './RadioListItem'; import type TableListItem from './TableListItem'; import type UserListItem from './UserListItem'; @@ -35,9 +35,6 @@ type CommonListItemProps = { /** Component to display on the right side */ rightHandSideComponent?: ((item: TItem) => ReactElement) | ReactElement | null; - /** Direction of checkmark to show */ - checkmarkPosition?: ValueOf; - /** Styles for the pressable component */ pressableStyle?: StyleProp; @@ -153,10 +150,14 @@ type UserListItemProps = ListItemProps & { FooterComponent?: ReactElement; }; +type InviteMemberListItemProps = UserListItemProps; + type RadioListItemProps = ListItemProps; type TableListItemProps = ListItemProps; +type ValidListItem = typeof RadioListItem | typeof UserListItem | typeof TableListItem | typeof InviteMemberListItem; + type Section = { /** Title of the section */ title?: string; @@ -181,7 +182,7 @@ type BaseSelectionListProps = Partial & { sections: Array>> | typeof CONST.EMPTY_ARRAY; /** Default renderer for every item in the list */ - ListItem: typeof RadioListItem | typeof UserListItem | typeof TableListItem; + ListItem: ValidListItem; /** Whether this is a multi-select list */ canSelectMultiple?: boolean; @@ -270,24 +271,15 @@ type BaseSelectionListProps = Partial & { /** Whether keyboard shortcuts should be disabled */ disableKeyboardShortcuts?: boolean; - /** Whether to disable initial styling for focused option */ - disableInitialFocusOptionStyle?: boolean; - /** Styles to apply to SelectionList container */ containerStyle?: ViewStyle; /** Whether keyboard is visible on the screen */ isKeyboardShown?: boolean; - /** Whether focus event should be delayed */ - shouldDelayFocus?: boolean; - /** Component to display on the right side of each child */ rightHandSideComponent?: ((item: ListItem) => ReactElement) | ReactElement | null; - /** Direction of checkmark to show */ - checkmarkPosition?: ValueOf; - /** Whether to show the loading indicator for new options */ isLoadingNewOptions?: boolean; @@ -300,9 +292,6 @@ type BaseSelectionListProps = Partial & { /** Styles for the list header wrapper */ listHeaderWrapperStyle?: StyleProp; - /** Whether to auto focus the Search Input */ - autoFocus?: boolean; - /** Whether to wrap long text up to 2 lines */ isRowMultilineSupported?: boolean; @@ -340,6 +329,7 @@ export type { UserListItemProps, RadioListItemProps, TableListItemProps, + InviteMemberListItemProps, ListItem, ListItemProps, FlattenedSectionsReturn, @@ -347,4 +337,5 @@ export type { ButtonOrCheckBoxRoles, SectionListDataType, SelectionListHandle, + ValidListItem, }; diff --git a/src/pages/SearchPage/index.tsx b/src/pages/SearchPage/index.tsx index d1d7146c6aba..b1555fd1cab8 100644 --- a/src/pages/SearchPage/index.tsx +++ b/src/pages/SearchPage/index.tsx @@ -163,7 +163,6 @@ function SearchPage({betas, reports, isSearchingForReports, navigation}: SearchP headerMessage={headerMessage} headerMessageStyle={headerMessage === translate('common.noResultsFound') ? [themeStyles.ph4, themeStyles.pb5] : undefined} onLayout={setPerformanceTimersEnd} - autoFocus onSelectRow={selectReport} showLoadingPlaceholder={!didScreenTransitionEnd || !isOptionsDataReady} footerContent={SearchPageFooterInstance} diff --git a/src/pages/workspace/WorkspaceInvitePage.tsx b/src/pages/workspace/WorkspaceInvitePage.tsx index a8354cfd3276..014097cd019c 100644 --- a/src/pages/workspace/WorkspaceInvitePage.tsx +++ b/src/pages/workspace/WorkspaceInvitePage.tsx @@ -9,8 +9,8 @@ import FormAlertWithSubmitButton from '@components/FormAlertWithSubmitButton'; import HeaderWithBackButton from '@components/HeaderWithBackButton'; import ScreenWrapper from '@components/ScreenWrapper'; import SelectionList from '@components/SelectionList'; +import InviteMemberListItem from '@components/SelectionList/InviteMemberListItem'; import type {Section} from '@components/SelectionList/types'; -import UserListItem from '@components/SelectionList/UserListItem'; import withNavigationTransitionEnd from '@components/withNavigationTransitionEnd'; import type {WithNavigationTransitionEndProps} from '@components/withNavigationTransitionEnd'; import useLocalize from '@hooks/useLocalize'; @@ -293,7 +293,7 @@ function WorkspaceInvitePage({ { @@ -306,7 +306,6 @@ function WorkspaceInvitePage({ showScrollIndicator showLoadingPlaceholder={!didScreenTransitionEnd || !OptionsListUtils.isPersonalDetailsReady(personalDetailsProp)} shouldPreventDefaultFocusOnSelectRow={!DeviceCapabilities.canUseTouchScreen()} - checkmarkPosition={CONST.DIRECTION.RIGHT} />