diff --git a/app/components/custom-modal/CustomModal.tsx b/app/components/custom-modal/CustomModal.tsx new file mode 100644 index 0000000..59cb41d --- /dev/null +++ b/app/components/custom-modal/CustomModal.tsx @@ -0,0 +1,46 @@ +import React, { type FC, type ReactElement } from 'react'; +import { StyleSheet, View } from 'react-native'; +import Modal, { type ModalProps } from 'react-native-modal'; +import { useTheme } from '../../hooks'; +import { screenHeight } from '../../theme'; +import styleSheet from './CustomModalStyles'; +import type { CustomModalPropsType } from './CustomModalTypes'; + +/** + * The custom modal component. + * @param {CustomModalPropsType} props - the props for the custom modal component. + * @param {StyleProp} [props.modalStyle] - Optional styles for the modal. + * @param {StyleProp} [props.contentContainerStyle] - Optional styles for the content container. + * @param {() => void} [props.onBackdropPress] - Optional callback function invoked when the backdrop is pressed. + * @param {boolean} props.isVisible - Whether the modal is visible or not. + * @returns {React.ReactElement} A React Element. + */ +const CustomModal: FC> = ({ + modalStyle, + contentContainerStyle, + onBackdropPress = () => {}, + isVisible, + children, + ...rest +}): ReactElement => { + const { styles } = useTheme(styleSheet); + + return ( + + + {children} + + + ); +}; + +export default CustomModal; diff --git a/app/components/custom-modal/CustomModalStyles.ts b/app/components/custom-modal/CustomModalStyles.ts new file mode 100644 index 0000000..5128d0e --- /dev/null +++ b/app/components/custom-modal/CustomModalStyles.ts @@ -0,0 +1,22 @@ +import { StyleSheet } from 'react-native'; +import { ApplicationStyles, Colors, scale, type ThemeMode } from '../../theme'; + +/** + * Create a custom style sheet for the given theme. + * @param {StyleSheetOption} theme - The theme mode to use for generating the style sheet. + * @returns A custom style sheet that can be injected into the component. + */ +const styles = (theme: ThemeMode) => + StyleSheet.create({ + ...ApplicationStyles(theme), + contentContainerStyle: { + backgroundColor: Colors[theme]?.white, + borderRadius: scale(16), + padding: scale(16) + }, + modalStyle: { + justifyContent: 'center' + } + }); + +export default styles; diff --git a/app/components/custom-modal/CustomModalTypes.ts b/app/components/custom-modal/CustomModalTypes.ts new file mode 100644 index 0000000..918fbe4 --- /dev/null +++ b/app/components/custom-modal/CustomModalTypes.ts @@ -0,0 +1,10 @@ +import type { StyleProp, ViewStyle } from 'react-native'; +import type { ReactNode } from 'react'; + +export interface CustomModalPropsType { + modalStyle?: StyleProp; + contentContainerStyle?: StyleProp; + onBackdropPress?: () => void; + isVisible: boolean; + children: ReactNode; +} diff --git a/app/components/custom-modal/index.ts b/app/components/custom-modal/index.ts new file mode 100644 index 0000000..14ad840 --- /dev/null +++ b/app/components/custom-modal/index.ts @@ -0,0 +1 @@ +export { default as CustomModal } from './CustomModal'; diff --git a/app/modules/task-listing/TaskListingScreen.tsx b/app/modules/task-listing/TaskListingScreen.tsx index 14a5f1e..5db622c 100644 --- a/app/modules/task-listing/TaskListingScreen.tsx +++ b/app/modules/task-listing/TaskListingScreen.tsx @@ -13,6 +13,7 @@ import { useTaskListing } from './useTaskListing'; * TaskListingScreen component displays a list of tasks. * Uses FlatList for optimal performance and TaskItem component for rendering individual tasks. * @param {TaskListingProps} props - The props for the TaskListing component. + * @param {(task: Task) => void} [props.onTaskPress] - Optional callback function invoked when a task is pressed. * @returns {React.ReactElement} A React element displaying the task list. */ const TaskListingScreen: FC = ({ onTaskPress }): React.ReactElement => { diff --git a/app/theme/Metrics.tsx b/app/theme/Metrics.tsx index 1ced4ef..d681e81 100755 --- a/app/theme/Metrics.tsx +++ b/app/theme/Metrics.tsx @@ -24,9 +24,25 @@ const getDimensions = (): DimensionsType => { return { width, height }; }; +/** + * Get the width and height of the device screen including the status bar and navigation bar. + * @returns {DimensionsType} - the width and height of the device screen. + */ +const getScreenDimensions = (): DimensionsType => { + let { width, height }: ScaledSize = Dimensions.get('screen'); + + if (width > height) { + [width, height] = [height, width]; + } + return { width, height }; +}; + // Get dimensions const { width, height }: DimensionsType = getDimensions(); +// Get screen dimensions including status bar and navigation bar +const { width: screenWidth, height: screenHeight }: DimensionsType = getScreenDimensions(); + //Guideline sizes are based on standard ~5" screen mobile device const guidelineBaseWidth: number = 375; @@ -79,4 +95,4 @@ const globalMetrics: GlobalMetricsType = { isWeb: Platform.OS === 'web' }; -export { globalMetrics, scale, width, height }; +export { globalMetrics, scale, width, height, screenWidth, screenHeight }; diff --git a/package.json b/package.json index 40a29c7..5b147a3 100755 --- a/package.json +++ b/package.json @@ -32,15 +32,15 @@ } }, "dependencies": { + "@react-native/codegen": "0.81.1", "@react-navigation/core": "6.4.17", "@react-navigation/native": "6.1.18", "@react-navigation/native-stack": "6.11.0", "@react-navigation/routers": "6.1.9", - "@react-native/codegen": "0.81.1", "@reduxjs/toolkit": "2.5.0", + "@sentry/react-native": "6.20.0", "apisauce": "3.2.0", "axios": "1.10.0", - "@sentry/react-native": "6.20.0", "formik": "2.4.6", "i18next": "23.12.2", "lodash": "4.17.21", @@ -51,6 +51,7 @@ "react-native-gesture-handler": "2.28.0", "react-native-localize": "3.5.2", "react-native-mmkv": "3.3.3", + "react-native-modal": "^14.0.0-rc.1", "react-native-permissions": "4.1.5", "react-native-safe-area-context": "5.6.1", "react-native-screens": "4.14.0", @@ -70,22 +71,22 @@ "@react-native/metro-config": "0.81.4", "@react-native/typescript-config": "0.81.4", "@react-navigation/devtools": "6.0.27", - "@types/lodash": "4.17.7", "@types/jest": "29.5.13", + "@types/lodash": "4.17.7", "@types/react": "19.1.0", "@types/react-native": "0.73.0", "@types/react-test-renderer": "19.1.0", "@typescript-eslint/eslint-plugin": "8.0.1", "@typescript-eslint/parser": "8.0.1", "babel-jest": "29.7.0", - "cspell": "8.13.1", "babel-plugin-transform-remove-console": "6.9.4", + "cspell": "8.13.1", "eslint": "8.57.0", + "eslint-config-prettier": "9.1.0", "eslint-import-resolver-typescript": "3.6.3", + "eslint-plugin-eslint-comments": "3.2.0", "eslint-plugin-import": "2.29.1", "eslint-plugin-jest": "28.8.0", - "eslint-config-prettier": "9.1.0", - "eslint-plugin-eslint-comments": "3.2.0", "eslint-plugin-prettier": "5.2.1", "eslint-plugin-react-native": "4.1.0", "husky": "8.0.3", @@ -110,4 +111,4 @@ "engines": { "node": ">=20" } -} \ No newline at end of file +}