diff --git a/src/CONST/index.ts b/src/CONST/index.ts index f2d66c6656c65..ee92b516d996e 100644 --- a/src/CONST/index.ts +++ b/src/CONST/index.ts @@ -245,6 +245,8 @@ const CONST = { POPOVER_DROPDOWN_WIDTH: 334, POPOVER_DROPDOWN_MIN_HEIGHT: 0, POPOVER_DROPDOWN_MAX_HEIGHT: 416, + POPOVER_CATEGORY_PICKER_WIDTH: 350, + POPOVER_CATEGORY_PICKER_MAX_HEIGHT: 450, POPOVER_MENU_MAX_HEIGHT: 496, POPOVER_MENU_MAX_HEIGHT_MOBILE: 432, POPOVER_DATE_WIDTH: 338, @@ -7501,6 +7503,7 @@ const CONST = { COMPLETED: 'completed', }, }, + INLINE_EDITABLE_EXPENSE_STATUSES: new Set(['', 'unreported', 'drafts', 'outstanding']), GROUP_COLUMN_PREFIX: 'group', TABLE_COLUMNS: { RECEIPT: 'receipt', @@ -8569,6 +8572,9 @@ const CONST = { GROUP_SELECT_ALL_CHECKBOX: 'Search-GroupSelectAllCheckbox', SORTABLE_HEADER: 'Search-SortableHeader', }, + TABLE: { + EDITABLE_CELL: 'Table-EditableCell', + }, REPORT: { FLOATING_MESSAGE_COUNTER: 'Report-FloatingMessageCounter', LIST_BOUNDARY_LOADER_RETRY: 'Report-ListBoundaryLoaderRetry', diff --git a/src/components/CategoryPicker/CategoryPickerModal.tsx b/src/components/CategoryPicker/CategoryPickerModal.tsx new file mode 100644 index 0000000000000..7545562acbb39 --- /dev/null +++ b/src/components/CategoryPicker/CategoryPickerModal.tsx @@ -0,0 +1,104 @@ +import React, {useRef, useState} from 'react'; +import {View} from 'react-native'; +import ConfirmCancelButtonRow from '@components/ConfirmCancelButtonRow'; +import PopoverWithMeasuredContent from '@components/PopoverWithMeasuredContent'; +import type PopoverWithMeasuredContentProps from '@components/PopoverWithMeasuredContent/types'; +import type {ListItem} from '@components/SelectionList/types'; +import useResponsiveLayout from '@hooks/useResponsiveLayout'; +import useStyleUtils from '@hooks/useStyleUtils'; +import useThemeStyles from '@hooks/useThemeStyles'; +import CONST from '@src/CONST'; +import CategoryPicker from '.'; + +const DEFAULT_ANCHOR_ALIGNMENT = { + horizontal: CONST.MODAL.ANCHOR_ORIGIN_HORIZONTAL.LEFT, + vertical: CONST.MODAL.ANCHOR_ORIGIN_VERTICAL.TOP, +}; + +const popoverDimensions = { + width: CONST.POPOVER_DROPDOWN_WIDTH, + height: CONST.POPOVER_DROPDOWN_MAX_HEIGHT, +}; + +type CategoryPickerModalProps = { + /** Callback to close the modal */ + onClose: () => void; + + /** The policy whose categories should be shown */ + policyID: string | undefined; + + /** Currently selected category */ + selectedCategory?: string; + + /** Called when the user confirms a category selection */ + onSelected?: (item: ListItem) => void; +} & Omit; + +function CategoryPickerModal({ + isVisible, + onClose, + anchorPosition, + policyID, + selectedCategory, + onSelected, + anchorAlignment = DEFAULT_ANCHOR_ALIGNMENT, + shouldMeasureAnchorPositionFromTop = false, +}: CategoryPickerModalProps) { + const styles = useThemeStyles(); + const StyleUtils = useStyleUtils(); + + // We need to use isSmallScreenWidth instead of shouldUseNarrowLayout to distinguish RHL and narrow layout + // eslint-disable-next-line rulesdir/prefer-shouldUseNarrowLayout-instead-of-isSmallScreenWidth + const {isSmallScreenWidth} = useResponsiveLayout(); + const anchorRef = useRef(null); + + const [pendingItem, setPendingItem] = useState(undefined); + + const handleConfirm = () => { + if (pendingItem) { + onSelected?.(pendingItem); + } + setPendingItem(undefined); + onClose(); + }; + + const handleCancel = () => { + setPendingItem(undefined); + onClose(); + }; + + return ( + + + + + + + + + ); +} + +export default CategoryPickerModal; diff --git a/src/components/CategoryPicker.tsx b/src/components/CategoryPicker/index.tsx similarity index 93% rename from src/components/CategoryPicker.tsx rename to src/components/CategoryPicker/index.tsx index 57101183022e2..45c219f3cac18 100644 --- a/src/components/CategoryPicker.tsx +++ b/src/components/CategoryPicker/index.tsx @@ -1,4 +1,7 @@ import React from 'react'; +import RadioListItem from '@components/SelectionList/ListItem/RadioListItem'; +import SelectionListWithSections from '@components/SelectionList/SelectionListWithSections'; +import type {ListItem} from '@components/SelectionList/types'; import useDebouncedState from '@hooks/useDebouncedState'; import useLocalize from '@hooks/useLocalize'; import useNetwork from '@hooks/useNetwork'; @@ -11,9 +14,6 @@ import {getHeaderMessageForNonUserList} from '@libs/OptionsListUtils'; import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; import {isEmptyObject} from '@src/types/utils/EmptyObject'; -import RadioListItem from './SelectionList/ListItem/RadioListItem'; -import SelectionListWithSections from './SelectionList/SelectionListWithSections'; -import type {ListItem} from './SelectionList/types'; type CategoryPickerProps = { policyID: string | undefined; diff --git a/src/components/ConfirmCancelButtonRow.tsx b/src/components/ConfirmCancelButtonRow.tsx new file mode 100644 index 0000000000000..695d7f4946c80 --- /dev/null +++ b/src/components/ConfirmCancelButtonRow.tsx @@ -0,0 +1,40 @@ +import React from 'react'; +import {View} from 'react-native'; +import useLocalize from '@hooks/useLocalize'; +import useThemeStyles from '@hooks/useThemeStyles'; +import Button from './Button'; + +type ConfirmCancelButtonRowProps = { + /** Called when the user presses Apply */ + onConfirm: () => void; + + /** Called when the user presses Cancel */ + onCancel: () => void; + + /** Whether the Apply button is disabled */ + isConfirmDisabled?: boolean; +}; + +function ConfirmCancelButtonRow({onConfirm, onCancel, isConfirmDisabled = false}: ConfirmCancelButtonRowProps) { + const styles = useThemeStyles(); + const {translate} = useLocalize(); + + return ( + +