ota-components-module 1.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (36) hide show
  1. package/README.md +179 -0
  2. package/assets/images/ic_camera.svg +3 -0
  3. package/assets/images/ic_close.svg +8 -0
  4. package/assets/images/ic_folder.svg +3 -0
  5. package/assets/images/placeholder.png +0 -0
  6. package/expo-env.d.ts +7 -0
  7. package/mri-manifest.json +10 -0
  8. package/package.json +28 -0
  9. package/src/button/ThemedButton.tsx +120 -0
  10. package/src/feedback/ActivityLoader.tsx +84 -0
  11. package/src/feedback/CustomAlert.tsx +143 -0
  12. package/src/feedback/DeleteImageConfirmationDialog.tsx +58 -0
  13. package/src/feedback/ProgressBar.tsx +58 -0
  14. package/src/image/ImagePickerBottomSheet.tsx +61 -0
  15. package/src/image/ImagePickerView.tsx +103 -0
  16. package/src/image/MultipleImagePreview.tsx +424 -0
  17. package/src/image/StackedImage.tsx +155 -0
  18. package/src/index.ts +68 -0
  19. package/src/input/CustomDropdown.tsx +142 -0
  20. package/src/input/CustomInput.tsx +101 -0
  21. package/src/input/FormField.tsx +358 -0
  22. package/src/input/KeyboardScrollView.tsx +131 -0
  23. package/src/input/SearchViewInput.tsx +183 -0
  24. package/src/layout/BottomSheetDialog.tsx +208 -0
  25. package/src/layout/BottomTwoButtonLayoutComponent.tsx +153 -0
  26. package/src/layout/CardView.tsx +101 -0
  27. package/src/layout/PropertyHeaderComponent.tsx +110 -0
  28. package/src/list/SearchableList.tsx +273 -0
  29. package/src/models/PropertyImage.ts +20 -0
  30. package/src/typography/Label.tsx +225 -0
  31. package/src/utils/BaseStyle.ts +46 -0
  32. package/src/utils/Strings.ts +1 -0
  33. package/src/utils/TextConstants.ts +24 -0
  34. package/src/utils/Utils.ts +11 -0
  35. package/src/webbaseview/WebBaseView.tsx +26 -0
  36. package/tsconfig.json +9 -0
package/README.md ADDED
@@ -0,0 +1,179 @@
1
+ # OTA Components Module
2
+
3
+ A reusable UI components library for React Native applications.
4
+
5
+ ## Features
6
+
7
+ - TypeScript support
8
+ - React Native components
9
+ - Cross-platform compatibility (iOS, Android, Web)
10
+ - Easy to integrate
11
+ - Consistent styling
12
+
13
+ ## Installation
14
+
15
+ ```bash
16
+ # Using npm
17
+ npm install ota-components-module
18
+
19
+ # Using yarn
20
+ yarn add ota-components-module
21
+ ```
22
+
23
+ ## Local Development
24
+
25
+ You can use this module locally by adding it to your package.json:
26
+
27
+ ```json
28
+ "dependencies": {
29
+ "ota-components-module": "file:app-modules/ota-components-module"
30
+ }
31
+ ```
32
+
33
+ Then run:
34
+
35
+ ```bash
36
+ npm install
37
+ ```
38
+
39
+ ## Available Components
40
+
41
+ ### Button Components
42
+ - `ThemedButton`: A customizable button with primary and secondary styles
43
+
44
+ ### Image Components
45
+ - `MultipleImagePreview`: A gallery component for viewing multiple images with pagination
46
+ - `ImagePickerView`: A component for selecting images from camera or gallery
47
+ - `ImagePickerBottomSheet`: A bottom sheet implementation of the image picker
48
+ - `StackedImage`: A component for displaying stacked images
49
+
50
+ ### Input Components
51
+ - `CustomInput`: A basic text input component
52
+ - `FormField`: A form field component with label and validation
53
+ - `CustomDropdown`: A dropdown selection component
54
+ - `SearchViewInput`: A search input with clear functionality
55
+ - `KeyboardScrollView`: A scrollview that adjusts for keyboard
56
+
57
+ ### Layout Components
58
+ - `BottomTwoButtonLayoutComponent`: A layout with two buttons at the bottom
59
+ - `PropertyHeaderComponent`: A header component for property screens
60
+ - `BottomSheetDialog`: A customizable bottom sheet dialog
61
+
62
+ ### Feedback Components
63
+ - `ActivityLoader`: A loading indicator
64
+ - `ProgressBar`: A progress bar component
65
+ - `CustomAlert`: A custom alert dialog
66
+ - `Dialog`: A general purpose dialog component
67
+ - `DeleteImageConfirmationDialog`: A confirmation dialog for deleting images
68
+
69
+ ### Typography Components
70
+ - `Label`: A text component with various styles and variants
71
+
72
+ ### List Components
73
+ - `SearchableList`: A searchable list component
74
+
75
+ ## Component Structure
76
+
77
+ ```
78
+ app-modules/ota-components-module/
79
+ ├── src/
80
+ │ ├── button/
81
+ │ │ └── ThemedButton.tsx
82
+ │ ├── feedback/
83
+ │ │ ├── ActivityLoader.tsx
84
+ │ │ ├── CustomAlert.tsx
85
+ │ │ ├── DeleteImageConfirmationDialog.tsx
86
+ │ │ ├── Dialog.tsx
87
+ │ │ └── ProgressBar.tsx
88
+ │ ├── image/
89
+ │ │ ├── ImagePickerBottomSheet.tsx
90
+ │ │ ├── ImagePickerView.tsx
91
+ │ │ ├── MultipleImagePreview.tsx
92
+ │ │ └── StackedImage.tsx
93
+ │ ├── input/
94
+ │ │ ├── CustomDropdown.tsx
95
+ │ │ ├── CustomInput.tsx
96
+ │ │ ├── FormField.tsx
97
+ │ │ ├── KeyboardScrollView.tsx
98
+ │ │ └── SearchViewInput.tsx
99
+ │ ├── layout/
100
+ │ │ ├── BottomSheetDialog.tsx
101
+ │ │ ├── BottomTwoButtonLayoutComponent.tsx
102
+ │ │ └── PropertyHeaderComponent.tsx
103
+ │ ├── list/
104
+ │ │ └── SearchableList.tsx
105
+ │ ├── models/
106
+ │ │ └── PropertyImage.ts
107
+ │ ├── typography/
108
+ │ │ └── Label.tsx
109
+ │ ├── utils/
110
+ │ │ ├── BaseStyle.ts
111
+ │ │ └── TextConstants.ts
112
+ │ └── index.ts
113
+ ```
114
+
115
+ ## Usage Examples
116
+
117
+ ### ThemedButton
118
+
119
+ ```jsx
120
+ import { ThemedButton } from 'ota-components-module';
121
+
122
+ const MyComponent = () => {
123
+ return (
124
+ <ThemedButton
125
+ title="Press Me"
126
+ onPress={() => console.log('Button pressed')}
127
+ type="primary" // or "secondary"
128
+ size="regular" // or "small"
129
+ />
130
+ );
131
+ };
132
+ ```
133
+
134
+ ### MultipleImagePreview
135
+
136
+ ```jsx
137
+ import { MultipleImagePreview } from 'ota-components-module';
138
+
139
+ const MyComponent = () => {
140
+ const images = [
141
+ { id: '1', imageBlobUrl: 'https://example.com/image1.jpg' },
142
+ { id: '2', imageBlobUrl: 'https://example.com/image2.jpg' },
143
+ ];
144
+
145
+ return (
146
+ <MultipleImagePreview
147
+ images={images}
148
+ initialIndex={0}
149
+ onClose={() => console.log('Closed')}
150
+ onDelete={(index) => console.log(`Delete image at index ${index}`)}
151
+ />
152
+ );
153
+ };
154
+ ```
155
+
156
+ ### Label
157
+
158
+ ```jsx
159
+ import { Label, TextSize, TextWeight } from 'ota-components-module';
160
+
161
+ const MyComponent = () => {
162
+ return (
163
+ <Label
164
+ text="Hello World"
165
+ size={TextSize.LARGE}
166
+ weight={TextWeight.BOLD}
167
+ textColorType="primary"
168
+ />
169
+ );
170
+ };
171
+ ```
172
+
173
+ ## Contributing
174
+
175
+ 1. Fork the repository
176
+ 2. Create your feature branch (`git checkout -b feature/amazing-feature`)
177
+ 3. Commit your changes (`git commit -m 'Add some amazing feature'`)
178
+ 4. Push to the branch (`git push origin feature/amazing-feature`)
179
+ 5. Open a Pull Request
@@ -0,0 +1,3 @@
1
+ <svg width="20" height="20" viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg">
2
+ <path d="M10.0001 8.16667C11.4751 8.16667 12.6667 9.35833 12.6667 10.8333C12.6667 12.3083 11.4751 13.5 10.0001 13.5C8.52508 13.5 7.33342 12.3083 7.33342 10.8333C7.33342 9.35833 8.52508 8.16667 10.0001 8.16667ZM16.6667 4.16667H8.33342V3.33333C8.33342 2.875 7.95842 2.5 7.50008 2.5H5.00008C4.54175 2.5 4.16675 2.875 4.16675 3.33333V4.16667H3.33341C2.41675 4.16667 1.66675 4.91667 1.66675 5.83333V15.8333C1.66675 16.75 2.41675 17.5 3.33341 17.5H16.6667C17.5834 17.5 18.3334 16.75 18.3334 15.8333V5.83333C18.3334 4.91667 17.5834 4.16667 16.6667 4.16667ZM10.0001 15C7.70008 15 5.83342 13.1333 5.83342 10.8333C5.83342 8.53333 7.70008 6.66667 10.0001 6.66667C12.3001 6.66667 14.1667 8.53333 14.1667 10.8333C14.1667 13.1333 12.3001 15 10.0001 15ZM15.8334 7.5C15.3751 7.5 15.0001 7.125 15.0001 6.66667C15.0001 6.20833 15.3751 5.83333 15.8334 5.83333C16.2917 5.83333 16.6667 6.20833 16.6667 6.66667C16.6667 7.125 16.2917 7.5 15.8334 7.5Z" fill="#607184"/>
3
+ </svg>
@@ -0,0 +1,8 @@
1
+ <svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
2
+ <mask id="mask0_2777_19678" style="mask-type:alpha" maskUnits="userSpaceOnUse" x="0" y="0" width="24" height="24">
3
+ <rect width="24" height="24" fill="#D9D9D9"/>
4
+ </mask>
5
+ <g mask="url(#mask0_2777_19678)">
6
+ <path d="M11.9969 13.4008L7.09688 18.3008C6.91354 18.4841 6.68021 18.5758 6.39688 18.5758C6.11354 18.5758 5.88021 18.4841 5.69688 18.3008C5.51354 18.1174 5.42188 17.8841 5.42188 17.6008C5.42188 17.3174 5.51354 17.0841 5.69688 16.9008L10.5969 12.0008L5.69688 7.10078C5.51354 6.91745 5.42188 6.68411 5.42188 6.40078C5.42188 6.11745 5.51354 5.88411 5.69688 5.70078C5.88021 5.51745 6.11354 5.42578 6.39688 5.42578C6.68021 5.42578 6.91354 5.51745 7.09688 5.70078L11.9969 10.6008L16.8969 5.70078C17.0802 5.51745 17.3135 5.42578 17.5969 5.42578C17.8802 5.42578 18.1135 5.51745 18.2969 5.70078C18.4802 5.88411 18.5719 6.11745 18.5719 6.40078C18.5719 6.68411 18.4802 6.91745 18.2969 7.10078L13.3969 12.0008L18.2969 16.9008C18.4802 17.0841 18.5719 17.3174 18.5719 17.6008C18.5719 17.8841 18.4802 18.1174 18.2969 18.3008C18.1135 18.4841 17.8802 18.5758 17.5969 18.5758C17.3135 18.5758 17.0802 18.4841 16.8969 18.3008L11.9969 13.4008Z" fill="#6C7278"/>
7
+ </g>
8
+ </svg>
@@ -0,0 +1,3 @@
1
+ <svg width="20" height="20" viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg">
2
+ <path d="M3.33341 16.6668C2.89939 16.6668 2.47976 16.5684 2.12248 16.2111C1.76519 15.8538 1.66675 15.4342 1.66675 15.0002V5.00016C1.66675 4.56613 1.76519 4.14651 2.12248 3.78923C2.47976 3.43194 2.89939 3.3335 3.33341 3.3335H7.56597C7.79304 3.3335 8.01335 3.41078 8.19066 3.55263L9.72617 4.78103C9.90348 4.92288 10.1238 5.00016 10.3509 5.00016H16.6667C17.1008 5.00016 17.5204 5.0986 17.8777 5.45589C18.235 5.81318 18.3334 6.2328 18.3334 6.66683V15.0002C18.3334 15.4342 18.235 15.8538 17.8777 16.2111C17.5204 16.5684 17.1008 16.6668 16.6667 16.6668H3.33341ZM3.33341 14.5002C3.33341 14.7763 3.55727 15.0002 3.83341 15.0002H16.1667C16.4429 15.0002 16.6667 14.7763 16.6667 14.5002V7.16683C16.6667 6.89069 16.4429 6.66683 16.1667 6.66683H3.33341V14.5002Z" fill="#607184"/>
3
+ </svg>
Binary file
package/expo-env.d.ts ADDED
@@ -0,0 +1,7 @@
1
+ /// <reference types="expo/types" />
2
+
3
+ declare module '*.svg' {
4
+ import * as React from 'react';
5
+ const ReactComponent: React.FC<React.SVGProps<SVGSVGElement>>;
6
+ export default ReactComponent;
7
+ }
@@ -0,0 +1,10 @@
1
+ {
2
+ "version": 1,
3
+ "swaggerVersion": 1,
4
+ "baseUrl": "http://example.com/api/v1.0",
5
+ "apiKey": "x-api-key",
6
+ "property:details": {
7
+ "mobileComponent": "",
8
+ "webComponent": ""
9
+ }
10
+ }
package/package.json ADDED
@@ -0,0 +1,28 @@
1
+ {
2
+ "name": "ota-components-module",
3
+ "version": "1.3.0",
4
+ "description": "Reusable UI components for OTA applications",
5
+ "main": "src/index.ts",
6
+ "scripts": {
7
+ "test": "echo \"Error: no test specified\" && exit 1"
8
+ },
9
+ "author": "",
10
+ "license": "ISC",
11
+ "peerDependencies": {
12
+ "@expo/vector-icons": "*",
13
+ "@types/react": "*",
14
+ "expo-checkbox": "*",
15
+ "expo-image": "*",
16
+ "react": "*",
17
+ "react-native": "*",
18
+ "react-native-safe-area-context": "*"
19
+ },
20
+ "dependencies": {
21
+ "react-native-element-dropdown": "^2.12.4"
22
+ },
23
+ "devDependencies": {
24
+ "@types/node": "^24.0.8",
25
+ "mri-cli-app-dev": "^1.0.5",
26
+ "typescript": "^5.9.2"
27
+ }
28
+ }
@@ -0,0 +1,120 @@
1
+ import React from 'react';
2
+ import { TouchableOpacity, ViewStyle, TextStyle } from 'react-native';
3
+ import { Colors } from '../utils/BaseStyle';
4
+ import Label from '../typography/Label';
5
+ import { TextSize, TextWeight } from '../utils/TextConstants';
6
+
7
+
8
+ export type ButtonType = 'primary' | 'secondary';
9
+ export type ButtonSize = 'small' | 'regular';
10
+
11
+ export type ButtonProps = {
12
+ testID?: string;
13
+ title: string;
14
+ onPress: () => void;
15
+ type?: ButtonType;
16
+ size?: ButtonSize;
17
+ disabled?: boolean;
18
+ style?: ViewStyle;
19
+ textstyle?: TextStyle;
20
+ primaryColor?: string;
21
+ backgroundColor?: string;
22
+ disabledColor?: string;
23
+ };
24
+
25
+ const ThemedButton: React.FC<ButtonProps> = ({
26
+ testID,
27
+ title,
28
+ onPress,
29
+ type = 'primary',
30
+ size = 'regular',
31
+ disabled = false,
32
+ style,
33
+ textstyle,
34
+ primaryColor = Colors.lightThemePrimaryColor,
35
+ backgroundColor = '#FFFFFF',
36
+ disabledColor = '#B8D8E8'
37
+ }) => {
38
+
39
+ const getButtonStyle = (): ViewStyle => {
40
+ const baseStyle: ViewStyle = {
41
+ borderRadius: 30, // Rounded corners as shown in the image
42
+ alignItems: 'center',
43
+ justifyContent: 'center',
44
+ borderWidth: type === 'secondary' ? 2 : 0,
45
+ borderColor: primaryColor,
46
+ height: 50
47
+ };
48
+
49
+ // Size variations
50
+ const sizeStyles: Record<ButtonSize, ViewStyle> = {
51
+ small: {
52
+ paddingVertical: 6,
53
+ paddingHorizontal: 16,
54
+ minWidth: 80,
55
+ },
56
+ regular: {
57
+ paddingVertical: 12,
58
+ paddingHorizontal: 24,
59
+ minWidth: 120,
60
+ },
61
+ };
62
+ // Type variations
63
+ const typeStyles: Record<ButtonType, ViewStyle> = {
64
+ primary: {
65
+ backgroundColor: disabled ? disabledColor : primaryColor, // Lighter blue when disabled
66
+ },
67
+ secondary: {
68
+ backgroundColor: 'transparent',
69
+ },
70
+ };
71
+
72
+ return {
73
+ ...baseStyle,
74
+ ...sizeStyles[size],
75
+ ...typeStyles[type],
76
+ ...style,
77
+ };
78
+ };
79
+
80
+ const getTextStyle = (): TextStyle => {
81
+ const baseStyle: TextStyle = {
82
+ textAlign: 'center',
83
+ };
84
+
85
+ // Type variations
86
+ const typeStyles: Record<ButtonType, TextStyle> = {
87
+ primary: {
88
+ color: backgroundColor,
89
+ },
90
+ secondary: {
91
+ color: primaryColor,
92
+ },
93
+ };
94
+
95
+ return {
96
+ ...baseStyle,
97
+ ...typeStyles[type],
98
+ ...textstyle
99
+ };
100
+ };
101
+
102
+ return (
103
+ <TouchableOpacity
104
+ testID={testID}
105
+ style={getButtonStyle()}
106
+ onPress={onPress}
107
+ disabled={disabled}
108
+ activeOpacity={0.7}
109
+ >
110
+ <Label
111
+ text={title}
112
+ size={size === 'small' ? TextSize.SMALL : TextSize.NORMAL}
113
+ weight={TextWeight.SEMI_BOLD}
114
+ customStyle={getTextStyle()}
115
+ />
116
+ </TouchableOpacity>
117
+ );
118
+ };
119
+
120
+ export default ThemedButton;
@@ -0,0 +1,84 @@
1
+ import {ActivityIndicator, Platform, StyleSheet, Text, View} from 'react-native';
2
+ import { Colors } from '../utils/BaseStyle';
3
+ import Label from "../typography/Label";
4
+ import { TextSize, TextWeight } from "../utils/TextConstants";
5
+
6
+ interface ActivityLoaderProps {
7
+ loading?: boolean;
8
+ message?: string;
9
+ backgroundColor?: string;
10
+ textColor?: string;
11
+ shadowColor?: string;
12
+ }
13
+
14
+ /**
15
+ * A loading indicator component that displays a spinner and optional message
16
+ */
17
+ const ActivityLoader: React.FC<ActivityLoaderProps> = ({
18
+ loading = true,
19
+ message = "Loading...",
20
+ backgroundColor = Colors.whiteColor,
21
+ textColor = Colors.textBlack,
22
+ shadowColor = Colors.shadowColor,
23
+ }) => {
24
+ if (!loading) {
25
+ return null;
26
+ }
27
+
28
+ const styles = getStyles(backgroundColor, textColor, shadowColor);
29
+
30
+ return (
31
+ <View style={[styles.parent]}>
32
+ <View style={[styles.activityContainer, styles.shadowProp]}>
33
+ <ActivityIndicator
34
+ size="large"
35
+ color={textColor}
36
+ style={{marginTop: 20}}
37
+ />
38
+ <Label text={message} size={TextSize.NORMAL} weight={TextWeight.NORMAL} color={textColor} customStyle={styles.text} />
39
+ </View>
40
+ </View>
41
+ );
42
+ };
43
+
44
+ const getStyles = (backgroundColor: string, textColor: string, shadowColor: string) => StyleSheet.create({
45
+ shadowProp: {
46
+ shadowColor: shadowColor,
47
+ shadowOffset: {width: -2, height: 2},
48
+ shadowOpacity: 0.2,
49
+ shadowRadius: 5,
50
+ elevation: Platform.OS === 'android' ? 5 : 0,
51
+ // On Android, add a subtle border to enhance the shadow appearance
52
+ ...(Platform.OS === 'android'
53
+ ? {
54
+ borderWidth: 0.5,
55
+ borderColor: 'rgba(0,0,0,0.05)',
56
+ }
57
+ : {}),
58
+ },
59
+ parent: {
60
+ flex: 1,
61
+ justifyContent: 'center',
62
+ alignItems: 'center',
63
+ position: 'absolute',
64
+ top: 0,
65
+ left: 0,
66
+ right: 0,
67
+ bottom: 0,
68
+ backgroundColor: 'rgba(0, 0, 0, 0.5)',
69
+ },
70
+ activityContainer: {
71
+ width: 230,
72
+ height: 100,
73
+ borderRadius: 10,
74
+ backgroundColor: backgroundColor,
75
+ },
76
+ text: {
77
+ alignSelf: 'center',
78
+ marginTop: 4,
79
+ fontSize: 16,
80
+ color: textColor,
81
+ },
82
+ });
83
+
84
+ export default ActivityLoader;
@@ -0,0 +1,143 @@
1
+ import React from "react";
2
+ import {
3
+ View,
4
+ Text,
5
+ StyleSheet,
6
+ Modal,
7
+ GestureResponderEvent,
8
+ TouchableOpacity,
9
+ Platform,
10
+ ViewStyle,
11
+ } from "react-native";
12
+ import { Colors } from "../utils/BaseStyle";
13
+ import Label from "../typography/Label";
14
+ import { TextSize, TextWeight } from "../utils/TextConstants";
15
+
16
+ interface CustomAlertProps {
17
+ visible: boolean;
18
+ title: string;
19
+ message: string | null;
20
+ positiveButtonText?: string;
21
+ negativeButtonText?: string;
22
+ backgroundColor?: string;
23
+ textColor?: string;
24
+ shadowColor?: string;
25
+ onPositivePress: (event: GestureResponderEvent) => void;
26
+ onNegativePress?: (event: GestureResponderEvent) => void;
27
+ viewStyle?: ViewStyle | ViewStyle[];
28
+ }
29
+
30
+ /**
31
+ * A custom alert dialog component with configurable buttons
32
+ */
33
+ const CustomAlert: React.FC<CustomAlertProps> = ({
34
+ visible,
35
+ title,
36
+ message,
37
+ positiveButtonText = "OK",
38
+ negativeButtonText = "Cancel",
39
+ onPositivePress,
40
+ onNegativePress,
41
+ backgroundColor = Colors.whiteColor,
42
+ textColor = Colors.textBlack,
43
+ shadowColor = Colors.shadowColor,
44
+ viewStyle
45
+ }) => {
46
+ const styles = getStyles(backgroundColor, textColor, shadowColor);
47
+ return (
48
+ <Modal
49
+ animationType="fade"
50
+ transparent={true}
51
+ visible={visible}
52
+ statusBarTranslucent={true}
53
+ onRequestClose={onNegativePress || onPositivePress}
54
+ >
55
+ <View style={styles.centeredView}>
56
+ <View style={[styles.modalView, viewStyle]}>
57
+ {title && <Label text={title} size={TextSize.LARGE} weight={TextWeight.BOLD} color={textColor} customStyle={styles.title} />}
58
+
59
+ <Label text={message} size={TextSize.NORMAL} weight={TextWeight.NORMAL} color={textColor} customStyle={styles.message} />
60
+
61
+ <View style={styles.buttonContainer}>
62
+ <TouchableOpacity style={styles.textButton} testID="btnPositiveAction" onPress={onPositivePress}>
63
+ <Label text={positiveButtonText} size={TextSize.NORMAL} weight={TextWeight.BOLD} color={textColor} customStyle={styles.text} />
64
+ </TouchableOpacity>
65
+
66
+ {onNegativePress && (
67
+ <View style={styles.buttonWrapper}>
68
+ <TouchableOpacity style={styles.textButton} testID="btnNegativeAction" onPress={onNegativePress}>
69
+ <Label text={negativeButtonText} size={TextSize.NORMAL} weight={TextWeight.BOLD} color={textColor} customStyle={styles.text} />
70
+ </TouchableOpacity>
71
+ </View>
72
+ )}
73
+ </View>
74
+ </View>
75
+ </View>
76
+ </Modal>
77
+ );
78
+ };
79
+
80
+ const getStyles = (backgroundColor: string, textColor: string, shadowColor: string) => StyleSheet.create({
81
+ centeredView: {
82
+ flex: 1,
83
+ justifyContent: "center",
84
+ alignItems: "center",
85
+ paddingHorizontal: 20,
86
+ backgroundColor: "rgba(0, 0, 0, 0.5)",
87
+ },
88
+ modalView: {
89
+ width: "100%",
90
+ backgroundColor: backgroundColor,
91
+ borderRadius: 20,
92
+ padding: 20,
93
+ alignItems: "center",
94
+ shadowColor: shadowColor,
95
+ shadowOffset: { width: 0, height: 2 },
96
+ shadowOpacity: 0.25,
97
+ shadowRadius: 4,
98
+ elevation: Platform.OS === 'android' ? 5 : 0,
99
+ // On Android, add a subtle border to enhance the shadow appearance
100
+ ...(Platform.OS === 'android'
101
+ ? {
102
+ borderWidth: 0.5,
103
+ borderColor: 'rgba(0,0,0,0.05)',
104
+ }
105
+ : {}),
106
+ },
107
+ title: {
108
+ width: "100%",
109
+ textAlign: "left",
110
+ marginBottom: 10,
111
+ },
112
+ message: {
113
+ width: "100%",
114
+ textAlign: "left",
115
+ marginBottom: 20,
116
+ lineHeight: 26
117
+ },
118
+ buttonContainer: {
119
+ flexDirection: "row",
120
+ justifyContent: "flex-end",
121
+ width: "100%",
122
+ marginTop: 20,
123
+ paddingHorizontal: 10,
124
+ },
125
+ singleButtonContainer: {
126
+ justifyContent: "center",
127
+ },
128
+ text: {
129
+ fontSize: 16,
130
+ fontWeight: "bold",
131
+ color: textColor,
132
+ },
133
+ buttonWrapper: {
134
+ // Ensures both buttons take equal width
135
+ marginStart: 35, // Adds spacing between buttons
136
+ },
137
+ textButton: {
138
+ paddingHorizontal: 10,
139
+ paddingVertical: 5
140
+ }
141
+ });
142
+
143
+ export default CustomAlert;
@@ -0,0 +1,58 @@
1
+ import { View } from 'react-native'
2
+ import React from 'react'
3
+ import Label from '../typography/Label'
4
+ import { TextSize, TextWeight } from '../utils/TextConstants'
5
+ import BottomTwoButtonLayoutComponent from '../layout/BottomTwoButtonLayoutComponent'
6
+
7
+ interface DeleteImageConfirmationDialogProps {
8
+ message: string;
9
+ onConfirm: () => void;
10
+ onCancel: () => void;
11
+ primaryButtonText?: string;
12
+ secondaryButtonText?: string;
13
+ primaryColor?: string;
14
+ backgroundColor?: string;
15
+ textColor?: string;
16
+ }
17
+
18
+ /**
19
+ * A confirmation dialog for deleting images
20
+ */
21
+ const DeleteImageConfirmationDialog: React.FC<DeleteImageConfirmationDialogProps> = ({
22
+ message,
23
+ primaryButtonText = "Delete",
24
+ secondaryButtonText = "Cancel",
25
+ onConfirm,
26
+ onCancel,
27
+ primaryColor,
28
+ backgroundColor,
29
+ textColor
30
+ }) => {
31
+ return (
32
+ <View style={{flex: 1}}>
33
+ <Label
34
+ text={message}
35
+ size={TextSize.NORMAL}
36
+ weight={TextWeight.NORMAL}
37
+ textColorType="primary"
38
+ customStyle={{alignSelf: 'center', textAlign: 'center', paddingHorizontal: 15, color: textColor}}
39
+ />
40
+
41
+ <BottomTwoButtonLayoutComponent
42
+ primaryButtonTestID="btnDelete"
43
+ secondaryButtonTestID="btnCancel"
44
+ primaryButtonText={primaryButtonText}
45
+ secondaryButtonText={secondaryButtonText}
46
+ onPrimaryButtonPress={onConfirm}
47
+ onSecondaryButtonPress={onCancel}
48
+ isPrimaryButtonDisabled={false}
49
+ isSecondaryButtonDisabled={false}
50
+ containerStyle={{elevation: 0, shadowOpacity: 0, borderTopWidth: 0}}
51
+ primaryColor={primaryColor}
52
+ backgroundColor={backgroundColor}
53
+ />
54
+ </View>
55
+ )
56
+ }
57
+
58
+ export default DeleteImageConfirmationDialog