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.
- package/README.md +179 -0
- package/assets/images/ic_camera.svg +3 -0
- package/assets/images/ic_close.svg +8 -0
- package/assets/images/ic_folder.svg +3 -0
- package/assets/images/placeholder.png +0 -0
- package/expo-env.d.ts +7 -0
- package/mri-manifest.json +10 -0
- package/package.json +28 -0
- package/src/button/ThemedButton.tsx +120 -0
- package/src/feedback/ActivityLoader.tsx +84 -0
- package/src/feedback/CustomAlert.tsx +143 -0
- package/src/feedback/DeleteImageConfirmationDialog.tsx +58 -0
- package/src/feedback/ProgressBar.tsx +58 -0
- package/src/image/ImagePickerBottomSheet.tsx +61 -0
- package/src/image/ImagePickerView.tsx +103 -0
- package/src/image/MultipleImagePreview.tsx +424 -0
- package/src/image/StackedImage.tsx +155 -0
- package/src/index.ts +68 -0
- package/src/input/CustomDropdown.tsx +142 -0
- package/src/input/CustomInput.tsx +101 -0
- package/src/input/FormField.tsx +358 -0
- package/src/input/KeyboardScrollView.tsx +131 -0
- package/src/input/SearchViewInput.tsx +183 -0
- package/src/layout/BottomSheetDialog.tsx +208 -0
- package/src/layout/BottomTwoButtonLayoutComponent.tsx +153 -0
- package/src/layout/CardView.tsx +101 -0
- package/src/layout/PropertyHeaderComponent.tsx +110 -0
- package/src/list/SearchableList.tsx +273 -0
- package/src/models/PropertyImage.ts +20 -0
- package/src/typography/Label.tsx +225 -0
- package/src/utils/BaseStyle.ts +46 -0
- package/src/utils/Strings.ts +1 -0
- package/src/utils/TextConstants.ts +24 -0
- package/src/utils/Utils.ts +11 -0
- package/src/webbaseview/WebBaseView.tsx +26 -0
- 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
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
|