ota-components-module 1.3.2 → 1.3.10

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 (67) hide show
  1. package/assets/images/back_arrow.svg +3 -0
  2. package/mri-manifest.json +40 -8
  3. package/package.json +2 -2
  4. package/src/feedback/CustomAlert.tsx +4 -2
  5. package/src/image/ImagePickerBottomSheet.tsx +11 -2
  6. package/src/image/ImagePickerView.tsx +14 -9
  7. package/src/image/MultipleImagePreview.tsx +156 -90
  8. package/src/index.ts +14 -0
  9. package/src/input/CustomDropdown.tsx +55 -12
  10. package/src/input/FormField.tsx +168 -73
  11. package/src/layout/PropertyHeaderComponent.tsx +2 -2
  12. package/src/navbar/CustomNavBar.tsx +42 -0
  13. package/src/utils/ValidationUtils.ts +197 -0
  14. package/lib/button/ThemedButton.d.ts +0 -19
  15. package/lib/button/ThemedButton.js +0 -73
  16. package/lib/feedback/ActivityLoader.d.ts +0 -12
  17. package/lib/feedback/ActivityLoader.js +0 -65
  18. package/lib/feedback/CustomAlert.d.ts +0 -20
  19. package/lib/feedback/CustomAlert.js +0 -96
  20. package/lib/feedback/DeleteImageConfirmationDialog.d.ts +0 -16
  21. package/lib/feedback/DeleteImageConfirmationDialog.js +0 -19
  22. package/lib/feedback/ProgressBar.d.ts +0 -15
  23. package/lib/feedback/ProgressBar.js +0 -34
  24. package/lib/image/ImagePickerBottomSheet.d.ts +0 -10
  25. package/lib/image/ImagePickerBottomSheet.js +0 -31
  26. package/lib/image/ImagePickerView.d.ts +0 -11
  27. package/lib/image/ImagePickerView.js +0 -65
  28. package/lib/image/MultipleImagePreview.d.ts +0 -33
  29. package/lib/image/MultipleImagePreview.js +0 -332
  30. package/lib/image/StackedImage.d.ts +0 -11
  31. package/lib/image/StackedImage.js +0 -122
  32. package/lib/index.d.ts +0 -25
  33. package/lib/index.js +0 -56
  34. package/lib/input/CustomDropdown.d.ts +0 -40
  35. package/lib/input/CustomDropdown.js +0 -71
  36. package/lib/input/CustomInput.d.ts +0 -17
  37. package/lib/input/CustomInput.js +0 -47
  38. package/lib/input/FormField.d.ts +0 -31
  39. package/lib/input/FormField.js +0 -250
  40. package/lib/input/KeyboardScrollView.d.ts +0 -7
  41. package/lib/input/KeyboardScrollView.js +0 -96
  42. package/lib/input/SearchViewInput.d.ts +0 -59
  43. package/lib/input/SearchViewInput.js +0 -80
  44. package/lib/layout/BottomSheetDialog.d.ts +0 -5
  45. package/lib/layout/BottomSheetDialog.js +0 -156
  46. package/lib/layout/BottomTwoButtonLayoutComponent.d.ts +0 -26
  47. package/lib/layout/BottomTwoButtonLayoutComponent.js +0 -81
  48. package/lib/layout/CardView.d.ts +0 -24
  49. package/lib/layout/CardView.js +0 -79
  50. package/lib/layout/PropertyHeaderComponent.d.ts +0 -34
  51. package/lib/layout/PropertyHeaderComponent.js +0 -63
  52. package/lib/list/SearchableList.d.ts +0 -50
  53. package/lib/list/SearchableList.js +0 -143
  54. package/lib/models/PropertyImage.d.ts +0 -11
  55. package/lib/models/PropertyImage.js +0 -22
  56. package/lib/typography/Label.d.ts +0 -26
  57. package/lib/typography/Label.js +0 -147
  58. package/lib/utils/BaseStyle.d.ts +0 -31
  59. package/lib/utils/BaseStyle.js +0 -42
  60. package/lib/utils/Strings.d.ts +0 -1
  61. package/lib/utils/Strings.js +0 -4
  62. package/lib/utils/TextConstants.d.ts +0 -23
  63. package/lib/utils/TextConstants.js +0 -28
  64. package/lib/utils/Utils.d.ts +0 -5
  65. package/lib/utils/Utils.js +0 -12
  66. package/lib/webbaseview/WebBaseView.d.ts +0 -8
  67. package/lib/webbaseview/WebBaseView.js +0 -28
@@ -0,0 +1,3 @@
1
+ <svg width="16" height="12" viewBox="0 0 16 12" fill="none" xmlns="http://www.w3.org/2000/svg">
2
+ <path d="M3.84844 7.0005L6.69844 9.8505C6.89844 10.0505 6.99427 10.2838 6.98594 10.5505C6.9776 10.8172 6.88177 11.0505 6.69844 11.2505C6.49844 11.4505 6.26094 11.5547 5.98594 11.563C5.71094 11.5713 5.47344 11.4755 5.27344 11.2755L0.698438 6.7005C0.498438 6.5005 0.398438 6.26717 0.398438 6.0005C0.398438 5.73383 0.498438 5.5005 0.698438 5.3005L5.27344 0.7255C5.47344 0.5255 5.71094 0.429667 5.98594 0.438C6.26094 0.446333 6.49844 0.5505 6.69844 0.7505C6.88177 0.9505 6.9776 1.18383 6.98594 1.4505C6.99427 1.71717 6.89844 1.9505 6.69844 2.1505L3.84844 5.0005H14.9984C15.2818 5.0005 15.5193 5.09633 15.7109 5.288C15.9026 5.47967 15.9984 5.71717 15.9984 6.0005C15.9984 6.28383 15.9026 6.52133 15.7109 6.713C15.5193 6.90467 15.2818 7.0005 14.9984 7.0005H3.84844Z" fill="#607184"/>
3
+ </svg>
package/mri-manifest.json CHANGED
@@ -1,10 +1,42 @@
1
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": ""
2
+ "integration": {
3
+ "authentication": {
4
+ "apiKeyName": "Partner1",
5
+ "type": "api_key"
6
+ },
7
+ "baseUrl": ""
8
+ },
9
+ "integrator": {
10
+ "lightLogo": "https://devxapacptmasterbfy6d.blob.core.windows.net/attachments-da9c8e2f-31cb-4584-b926-6a9c12cf380… partner new 1",
11
+ "mriProviderId": "1234",
12
+ "name": "Partner1",
13
+ "type": "partner"
14
+ },
15
+ "manifestSpecVersion": 2,
16
+ "modules": {
17
+ "property:maintenance:owner": {
18
+ "mobileComponent": "ModuleMiniAppScreen",
19
+ "webComponent": null,
20
+ "activateLink": "PartnerActivationScreen",
21
+ "configurationLink": "PartnerConfigurationScreen",
22
+ "providerActions": [
23
+ "maintenance:show",
24
+ "provider:activated",
25
+ "provider:configured"
26
+ ],
27
+ "description": "maintenance second partner 2"
28
+ },
29
+ "property:maintenance:tenant": {
30
+ "mobileComponent": "ModuleMiniAppScreen",
31
+ "webComponent": null,
32
+ "activateLink": "PartnerActivationScreen",
33
+ "configurationLink": "PartnerConfigurationScreen",
34
+ "providerActions": [
35
+ "maintenance:show",
36
+ "provider:activated",
37
+ "provider:configured"
38
+ ],
39
+ "description": "maintenance second partner 2"
40
+ }
9
41
  }
10
- }
42
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ota-components-module",
3
- "version": "1.3.2",
3
+ "version": "1.3.10",
4
4
  "description": "Reusable UI components for OTA applications",
5
5
  "main": "src/index.ts",
6
6
  "scripts": {
@@ -19,8 +19,8 @@
19
19
  "react-native-safe-area-context": "^5.6.2"
20
20
  },
21
21
  "devDependencies": {
22
+ "mri-cli-app-dev": "^2.0.12",
22
23
  "@types/node": "^24.0.8",
23
- "mri-cli-app-dev": "^1.0.5",
24
24
  "typescript": "^5.9.2",
25
25
  "react": ">=16.8.0",
26
26
  "react-native": ">=0.60.0"
@@ -25,6 +25,7 @@ interface CustomAlertProps {
25
25
  onPositivePress: (event: GestureResponderEvent) => void;
26
26
  onNegativePress?: (event: GestureResponderEvent) => void;
27
27
  viewStyle?: ViewStyle | ViewStyle[];
28
+ onRequestClose?: (event: GestureResponderEvent) => void;
28
29
  }
29
30
 
30
31
  /**
@@ -41,7 +42,8 @@ const CustomAlert: React.FC<CustomAlertProps> = ({
41
42
  backgroundColor = Colors.whiteColor,
42
43
  textColor = Colors.textBlack,
43
44
  shadowColor = Colors.shadowColor,
44
- viewStyle
45
+ viewStyle,
46
+ onRequestClose
45
47
  }) => {
46
48
  const styles = getStyles(backgroundColor, textColor, shadowColor);
47
49
  return (
@@ -50,7 +52,7 @@ const CustomAlert: React.FC<CustomAlertProps> = ({
50
52
  transparent={true}
51
53
  visible={visible}
52
54
  statusBarTranslucent={true}
53
- onRequestClose={onNegativePress || onPositivePress}
55
+ onRequestClose={onRequestClose || onNegativePress || onPositivePress}
54
56
  >
55
57
  <View style={styles.centeredView}>
56
58
  <View style={[styles.modalView, viewStyle]}>
@@ -1,6 +1,7 @@
1
1
  import React, { useRef } from 'react';
2
2
  import ImagePickerView from './ImagePickerView';
3
3
  import BottomSheetDialog from '../layout/BottomSheetDialog';
4
+ import { useSafeAreaInsets } from 'react-native-safe-area-context';
4
5
 
5
6
  interface ImagePickerBottomSheetProps {
6
7
  visible: boolean;
@@ -8,6 +9,8 @@ interface ImagePickerBottomSheetProps {
8
9
  onTakePhoto: () => void;
9
10
  onUploadFile: () => void;
10
11
  title?: string;
12
+ backgroundColor?: string;
13
+ textColor?: string;
11
14
  }
12
15
 
13
16
  const ImagePickerBottomSheet: React.FC<ImagePickerBottomSheetProps> = ({
@@ -16,10 +19,13 @@ const ImagePickerBottomSheet: React.FC<ImagePickerBottomSheetProps> = ({
16
19
  onTakePhoto,
17
20
  onUploadFile,
18
21
  title,
22
+ backgroundColor,
23
+ textColor,
19
24
  }) => {
20
25
 
21
26
  const ref: any = useRef(null);
22
-
27
+ const insets = useSafeAreaInsets();
28
+
23
29
  const TakePhoto = () => {
24
30
  closeSheet();
25
31
 
@@ -45,14 +51,17 @@ const ImagePickerBottomSheet: React.FC<ImagePickerBottomSheetProps> = ({
45
51
  <BottomSheetDialog
46
52
  visible={visible}
47
53
  onClose={onClose}
48
- height={200} // Adjust height as needed
54
+ height={200 + insets.bottom} // Adjust height as needed
49
55
  ref={ref}
56
+ containerStyle={{ backgroundColor: backgroundColor }}
50
57
  >
51
58
  <ImagePickerView
52
59
  title={title}
53
60
  onTakePhoto={TakePhoto}
54
61
  onUploadFile={UploadFile}
55
62
  onClose={closeSheet}
63
+ backgroundColor={backgroundColor}
64
+ textColor={textColor}
56
65
  />
57
66
  </BottomSheetDialog>
58
67
  );
@@ -17,6 +17,8 @@ interface ImagePickerViewProps {
17
17
  onTakePhoto: () => void;
18
18
  onUploadFile: () => void;
19
19
  onClose?: () => void;
20
+ backgroundColor?: string;
21
+ textColor?: string;
20
22
  containerStyle?: ViewStyle;
21
23
  title?: string;
22
24
  }
@@ -26,43 +28,46 @@ const ImagePickerView: React.FC<ImagePickerViewProps> = ({
26
28
  onUploadFile,
27
29
  onClose,
28
30
  containerStyle,
31
+ backgroundColor,
32
+ textColor,
29
33
  title = 'Add an Image',
30
34
  }) => {
31
35
 
32
36
  return (
33
- <View style={[styles.container, containerStyle]}>
34
- <View style={styles.header}>
37
+ <View style={[styles.container, containerStyle, { backgroundColor }]}>
38
+ <View style={[styles.header]}>
35
39
  <Label
36
40
  text={title}
37
41
  size={TextSize.LARGE}
38
42
  weight={TextWeight.BOLD}
39
43
  textColorType="primary"
44
+ color={textColor}
40
45
  />
41
46
  {onClose && (
42
47
  <TouchableOpacity onPress={onClose} style={styles.closeButton}>
43
- <CloseIcon width={24} height={24} />
48
+ <CloseIcon width={24} height={24} color={textColor} />
44
49
  </TouchableOpacity>
45
50
  )}
46
51
  </View>
47
52
 
48
53
  <TouchableOpacity
49
- style={styles.option}
54
+ style={[styles.option]}
50
55
  onPress={onTakePhoto}
51
56
  activeOpacity={0.7}
52
57
  >
53
- <CameraIcon width={24} height={24} style={styles.icon} />
54
- <Label text="Take a photo" size={TextSize.NORMAL} weight={TextWeight.NORMAL} textColorType="primary" />
58
+ <CameraIcon width={24} height={24} style={styles.icon} color={textColor} />
59
+ <Label text="Take a photo" size={TextSize.NORMAL} weight={TextWeight.NORMAL} textColorType="primary" color={textColor} />
55
60
  </TouchableOpacity>
56
61
 
57
62
  <View style={styles.divider} />
58
63
 
59
64
  <TouchableOpacity
60
- style={styles.option}
65
+ style={[styles.option]}
61
66
  onPress={onUploadFile}
62
67
  activeOpacity={0.7}
63
68
  >
64
- <FolderIcon width={24} height={24} style={styles.icon} />
65
- <Label text="Upload a file" size={TextSize.NORMAL} weight={TextWeight.NORMAL} textColorType="primary" />
69
+ <FolderIcon width={24} height={24} style={styles.icon} color={textColor} />
70
+ <Label text="Upload a file" size={TextSize.NORMAL} weight={TextWeight.NORMAL} textColorType="primary" color={textColor} />
66
71
  </TouchableOpacity>
67
72
  </View>
68
73
  );
@@ -10,21 +10,18 @@ import {
10
10
  SafeAreaView,
11
11
  Platform,
12
12
  Text,
13
+ useWindowDimensions,
14
+ NativeSyntheticEvent,
15
+ NativeScrollEvent,
13
16
  } from 'react-native';
14
17
  import { Image } from 'expo-image';
15
- import Checkbox from 'expo-checkbox';
16
18
  import { Colors } from '../utils/BaseStyle';
17
19
  import { useSafeAreaInsets } from "react-native-safe-area-context";
18
20
  import { PropertyImage } from '../models/PropertyImage';
19
21
  import BottomSheetDialog, { BottomSheetDialogRef } from '../layout/BottomSheetDialog';
20
22
  import DeleteImageConfirmationDialog from '../feedback/DeleteImageConfirmationDialog';
21
23
  import { delete_image_confirmation_msg } from '../utils/Strings';
22
-
23
- // Get screen dimensions
24
- const { width: SCREEN_WIDTH, height: SCREEN_HEIGHT } = Dimensions.get('window');
25
-
26
- // Check if we're running on web
27
- const isWeb = Platform.OS === 'web';
24
+ import { isWebDevice } from '@mri-software/ota-components-module/src/utils/Utils';
28
25
 
29
26
  type ImageSource = {
30
27
  uri: PropertyImage;
@@ -40,6 +37,10 @@ type MultipleImagePreviewProps = {
40
37
  * Initial image index to display
41
38
  */
42
39
  initialIndex?: number;
40
+ /**
41
+ * Theme of the preview
42
+ */
43
+ theme: any;
43
44
  /**
44
45
  * Callback when the preview is closed
45
46
  */
@@ -64,28 +65,54 @@ type MultipleImagePreviewProps = {
64
65
  const MultipleImagePreview: React.FC<MultipleImagePreviewProps> = ({
65
66
  images,
66
67
  initialIndex = 0,
68
+ theme,
67
69
  onClose,
68
70
  onDelete,
69
71
  OnMarkDefault,
70
72
  showDeleteButton = true,
71
73
  }) => {
72
74
  const [activeIndex, setActiveIndex] = useState(initialIndex);
75
+ const [deleteIndex, setDeleteIndex] = useState(0);
73
76
  const [visible, setVisible] = useState(true);
77
+ // Initialize with sorted images immediately - default image comes first
78
+ const [sortedImages, setSortedImages] = useState<PropertyImage[]>(() => {
79
+ return [...images].sort((a, b) => {
80
+ if (a.isDefault) return -1;
81
+ if (b.isDefault) return 1;
82
+ return 0;
83
+ });
84
+ });
74
85
  const flatListRef = useRef<FlatList>(null);
75
86
  const bottomSheetRef = useRef<BottomSheetDialogRef>(null);
76
87
  const [showDeleteConfirmation, setShowDeleteConfirmation] = useState(false);
77
88
 
78
89
  const insets = useSafeAreaInsets();
79
-
80
- // Calculate dimensions based on platform
90
+ const { width: windowWidth, height: windowHeight } = useWindowDimensions();
91
+ const isWeb = isWebDevice(windowWidth) ? true : false;
92
+ // Calculate dimensions based on platform - recalculates on window resize
81
93
  const dimensions = useMemo(() => {
82
94
  // For web, we need to ensure each item takes exactly one screen width
95
+ const horizontalPadding = isWeb ? 120 : 20;
96
+ const actualImageWidth = windowWidth - (horizontalPadding * 2);
97
+
83
98
  return {
84
- itemWidth: SCREEN_WIDTH,
85
- imageWidth: isWeb ? SCREEN_WIDTH - 40 : SCREEN_WIDTH - 40,
86
- imageHeight: isWeb ? SCREEN_HEIGHT * 0.6 : SCREEN_HEIGHT * 0.4
99
+ itemWidth: windowWidth,
100
+ imageWidth: actualImageWidth,
101
+ imageHeight: isWeb ? windowHeight * 0.6 : actualImageWidth
87
102
  };
88
- }, []);
103
+ }, [windowWidth, windowHeight]);
104
+
105
+ // Add dialog container style for web
106
+ const dialogContainerStyle = useMemo(() => {
107
+ if (isWeb) {
108
+ return {
109
+ alignSelf: 'center' as const,
110
+ width: dimensions.imageWidth,
111
+ maxWidth: dimensions.imageWidth,
112
+ };
113
+ }
114
+ return {};
115
+ }, [isWeb, dimensions.imageWidth]);
89
116
 
90
117
  const closeSheet = () => {
91
118
  if (bottomSheetRef.current) {
@@ -103,16 +130,16 @@ const MultipleImagePreview: React.FC<MultipleImagePreviewProps> = ({
103
130
  };
104
131
 
105
132
  // Create styles with the calculated dimensions
106
- const styles = previewStyles(insets.top, dimensions);
133
+ const styles = previewStyles(isWeb, insets.top, theme, windowWidth, windowHeight, dimensions);
107
134
 
108
135
  // Process images to handle both local and remote URLs
109
136
  const processedImages = useMemo(() => {
110
- return images.map((image) => {
137
+ return sortedImages.map((image) => {
111
138
  // Check if the image is a local file path or a server URL
112
139
  const isLocalImage = image.imageBlobUrl.startsWith('data:') || image.imageBlobUrl.startsWith('/');
113
140
  return { uri: image, isLocal: isLocalImage };
114
141
  });
115
- }, [images]);
142
+ }, [sortedImages, images]);
116
143
 
117
144
  // Scroll to initial index on mount
118
145
  useEffect(() => {
@@ -134,7 +161,7 @@ const MultipleImagePreview: React.FC<MultipleImagePreviewProps> = ({
134
161
 
135
162
  const handleDelete = () => {
136
163
  if (onDelete) {
137
- onDelete(activeIndex);
164
+ onDelete(deleteIndex);
138
165
  }
139
166
  };
140
167
 
@@ -142,14 +169,27 @@ const MultipleImagePreview: React.FC<MultipleImagePreviewProps> = ({
142
169
  const handleScrollEnd = (event: any) => {
143
170
  const contentOffsetX = event.nativeEvent.contentOffset.x;
144
171
  const newIndex = Math.round(contentOffsetX / dimensions.itemWidth);
145
- if (newIndex !== activeIndex && newIndex >= 0 && newIndex < images.length) {
172
+ if (newIndex !== activeIndex && newIndex >= 0 && newIndex < sortedImages.length) {
146
173
  setActiveIndex(newIndex);
147
174
  }
148
175
  };
149
176
 
177
+ // Handle scroll for web
178
+ const handleScroll = (event: NativeSyntheticEvent<NativeScrollEvent>) => {
179
+ if (Platform.OS === 'web') {
180
+ const contentOffsetX = event.nativeEvent.contentOffset?.x;
181
+ if (contentOffsetX !== undefined) {
182
+ const newIndex = Math.round(contentOffsetX / dimensions.itemWidth);
183
+ if (newIndex !== activeIndex && newIndex >= 0 && newIndex < sortedImages.length) {
184
+ setActiveIndex(newIndex);
185
+ }
186
+ }
187
+ }
188
+ };
189
+
150
190
  // Function to handle manual navigation between images
151
191
  const goToImage = (index: number) => {
152
- if (flatListRef.current && index >= 0 && index < images.length) {
192
+ if (flatListRef.current && index >= 0 && index < sortedImages.length) {
153
193
  flatListRef.current.scrollToIndex({
154
194
  index,
155
195
  animated: true,
@@ -159,47 +199,72 @@ const MultipleImagePreview: React.FC<MultipleImagePreviewProps> = ({
159
199
  };
160
200
 
161
201
  // Render each image item
162
- const renderItem = ({ item }: { item: ImageSource }) => {
202
+ const renderItem = ({ item, index }: { item: ImageSource, index: number }) => {
163
203
  return (
164
204
  <View style={styles.itemContainer}>
165
- {OnMarkDefault && (
166
- <View style={styles.imageDefaultTextSection}>
167
- <Checkbox style={{borderColor: 'white', width: 20, height: 20}} value={item.uri.isDefault} onValueChange={(value: boolean) => {
168
- if(OnMarkDefault) {
169
- OnMarkDefault(activeIndex, value);
170
- }
171
- }} />
172
- <Text style={styles.imageDefaultText}>Mark as Default</Text>
173
- </View>
174
- )}
205
+ {/* Action buttons row above the image */}
206
+ <View style={styles.actionButtonsRow}>
207
+ {/* Favorite button on the left */}
208
+ {OnMarkDefault && (
209
+ <TouchableOpacity
210
+ style={styles.imageDefaultTextSection}
211
+ onPress={() => {
212
+ const newSorted = sortedImages.map((img, idx) => ({
213
+ ...img,
214
+ isDefault: idx === index ? !item.uri.isDefault : (!item.uri.isDefault ? false : img.isDefault)
215
+ }));
216
+ setSortedImages(newSorted);
217
+ if (OnMarkDefault) {
218
+ const originalIndex = images.findIndex(img => img.imageBlobUrl === item.uri.imageBlobUrl);
219
+ OnMarkDefault(originalIndex, !item.uri.isDefault);
220
+ }
221
+ }}
222
+ activeOpacity={0.7}
223
+ >
224
+ <Ionicons
225
+ name={item.uri.isDefault ? "star" : "star-outline"}
226
+ size={24}
227
+ color={theme.colors.primary}
228
+ />
229
+ <Text style={styles.imageDefaultText}>
230
+ {item.uri.isDefault ? "Favorite" : "Set as Favorite"}
231
+ </Text>
232
+ </TouchableOpacity>
233
+ )}
234
+
235
+ {/* Spacer to push delete button to the right */}
236
+ <View style={{ flex: 1 }} />
175
237
 
238
+ {/* Delete button on the right */}
239
+ {showDeleteButton && onDelete && item.uri.isDefault !== true && (
240
+ <TouchableOpacity
241
+ style={styles.imageDeleteButton}
242
+ onPress={() => {
243
+ if (item.isLocal) {
244
+ onDelete(index);
245
+ } else {
246
+ setDeleteIndex(index);
247
+ setShowDeleteConfirmation(true);
248
+ }
249
+ }}
250
+ activeOpacity={0.7}
251
+ >
252
+ <Ionicons name="trash-outline" size={24} color={theme.colors.redColor} />
253
+ </TouchableOpacity>
254
+ )}
255
+ </View>
256
+
257
+ {/* Image below the buttons */}
176
258
  <Image
177
259
  key={item.uri.imageBlobUrl}
178
260
  source={item.uri.imageBlobUrl}
179
261
  style={styles.image}
180
262
  contentFit="cover"
181
- placeholderContentFit='cover'
263
+ placeholderContentFit="cover"
182
264
  transition={500}
183
265
  cachePolicy="disk"
184
266
  placeholder={require("../../assets/images/placeholder.png")}
185
267
  />
186
-
187
- {/* Delete button overlay on the image */}
188
- {showDeleteButton && onDelete && item.uri.isDefault !== true && (
189
- <TouchableOpacity
190
- style={styles.imageDeleteButton}
191
- onPress={() => {
192
- if(item.isLocal) {
193
- handleDelete();
194
- } else {
195
- setShowDeleteConfirmation(true)
196
- }
197
- }}
198
- activeOpacity={0.7}
199
- >
200
- <Ionicons name="trash-outline" size={20} color="red" />
201
- </TouchableOpacity>
202
- )}
203
268
  </View>
204
269
  );
205
270
  };
@@ -213,7 +278,7 @@ const MultipleImagePreview: React.FC<MultipleImagePreviewProps> = ({
213
278
  onPress={handleClose}
214
279
  activeOpacity={0.7}
215
280
  >
216
- <Ionicons name="close-outline" size={30} color={Colors.whiteColor} />
281
+ <Ionicons name="close-outline" size={30} color={theme.colors.whiteColor} />
217
282
  </TouchableOpacity>
218
283
  </View>
219
284
  );
@@ -222,7 +287,7 @@ const MultipleImagePreview: React.FC<MultipleImagePreviewProps> = ({
222
287
  const renderFooter = () => (
223
288
  <View style={styles.footerContainer}>
224
289
  <View style={styles.dotsContainer}>
225
- {images.map((_, index) => (
290
+ {sortedImages.map((_, index) => (
226
291
  <TouchableOpacity
227
292
  key={index}
228
293
  style={[
@@ -264,6 +329,7 @@ const MultipleImagePreview: React.FC<MultipleImagePreviewProps> = ({
264
329
  windowSize={5}
265
330
  keyExtractor={(_, index) => `image-${index}`}
266
331
  onMomentumScrollEnd={handleScrollEnd}
332
+ onScroll={handleScroll}
267
333
  style={styles.flatList}
268
334
  contentContainerStyle={styles.flatListContent}
269
335
  snapToInterval={dimensions.itemWidth}
@@ -288,6 +354,7 @@ const MultipleImagePreview: React.FC<MultipleImagePreviewProps> = ({
288
354
  visible={showDeleteConfirmation}
289
355
  onClose={closeBottomSheet}
290
356
  height={200}
357
+ containerStyle={dialogContainerStyle}
291
358
  >
292
359
  <DeleteImageConfirmationDialog
293
360
  message={delete_image_confirmation_msg}
@@ -303,7 +370,7 @@ const MultipleImagePreview: React.FC<MultipleImagePreviewProps> = ({
303
370
  );
304
371
  };
305
372
 
306
- const previewStyles = (top: number, dimensions?: { itemWidth: number, imageWidth: number, imageHeight: number }) => StyleSheet.create({
373
+ const previewStyles = (isWeb: boolean, top: number, theme: any, windowWidth: number, windowHeight: number, dimensions?: { itemWidth: number, imageWidth: number, imageHeight: number }) => StyleSheet.create({
307
374
  container: {
308
375
  flex: 1,
309
376
  backgroundColor: '#000000CF',
@@ -314,7 +381,7 @@ const previewStyles = (top: number, dimensions?: { itemWidth: number, imageWidth
314
381
  alignItems: 'center',
315
382
  },
316
383
  flatList: {
317
- width: SCREEN_WIDTH,
384
+ width: dimensions?.itemWidth,
318
385
  ...(isWeb && {
319
386
  scrollSnapType: 'x mandatory', // Web-specific for better snapping
320
387
  WebkitOverflowScrolling: 'touch', // Better scrolling on iOS Safari
@@ -328,72 +395,71 @@ const previewStyles = (top: number, dimensions?: { itemWidth: number, imageWidth
328
395
  }),
329
396
  },
330
397
  itemContainer: {
331
- width: dimensions?.itemWidth || SCREEN_WIDTH,
398
+ width: dimensions?.itemWidth || windowWidth,
332
399
  justifyContent: 'center',
333
400
  alignItems: 'center',
334
- paddingHorizontal: 20,
401
+ paddingHorizontal: isWeb ? 120 : 20,
402
+
335
403
  },
336
404
  image: {
337
- width: dimensions?.imageWidth || SCREEN_WIDTH - 40,
338
- height: dimensions?.imageHeight || SCREEN_HEIGHT * 0.4,
405
+ width: '100%',
406
+ height: dimensions?.imageHeight || windowHeight * 0.4,
339
407
  resizeMode: 'cover',
340
408
  borderRadius: 20,
341
- borderColor: Colors.whiteColor,
342
- borderWidth: 1,
343
409
  shadowColor: Colors.shadowColor,
344
410
  shadowOffset: { width: 0, height: 2 },
345
411
  shadowOpacity: 0.1,
346
412
  shadowRadius: 4,
347
413
  elevation: Platform.OS === 'android' ? 4 : 0,
348
414
  },
349
- headerContainer: {
350
- position: 'absolute',
351
- top: top,
352
- right: 0,
353
- left: 0,
354
- height: 60,
355
- paddingHorizontal: 15,
415
+ actionButtonsRow: {
356
416
  flexDirection: 'row',
357
- justifyContent: 'flex-end', // Align to right side
358
- alignItems: 'center',
359
- zIndex: 100,
360
- backgroundColor: "transparent",
361
- },
362
- closeButton: {
363
- justifyContent: 'center',
417
+ width: '100%',
418
+ justifyContent: 'space-between',
364
419
  alignItems: 'center',
420
+ paddingHorizontal: 0,
421
+ marginBottom: 12,
422
+ minHeight: 44, // Ensure touchable area
365
423
  },
366
424
  imageDefaultTextSection: {
367
425
  flexDirection: 'row',
368
- position: 'absolute',
369
- top: 5,
370
- left: 20,
371
- borderRadius: 10,
372
- backgroundColor: 'rgba(0, 0, 0, 0.5)',
373
426
  justifyContent: 'center',
374
427
  alignItems: 'center',
375
- zIndex: 10,
376
- margin: 8,
377
- paddingVertical: 12,
378
- paddingHorizontal: 16
428
+ paddingVertical: 8,
429
+ paddingHorizontal: 12,
379
430
  },
380
431
  imageDefaultText: {
381
- color: 'white',
432
+ color: theme.colors.primary,
382
433
  alignSelf: 'center',
383
434
  textAlign: 'center',
384
- marginStart: 12
435
+ marginStart: 8,
436
+ fontSize: 16,
437
+ fontWeight: '500',
385
438
  },
386
439
  imageDeleteButton: {
440
+ width: 44,
441
+ height: 44,
442
+ justifyContent: 'center',
443
+ alignItems: 'center',
444
+ },
445
+ headerContainer: {
387
446
  position: 'absolute',
388
- top: 15,
389
- right: 30,
447
+ top: top,
448
+ right: 0,
449
+ left: 0,
450
+ height: 60,
451
+ paddingHorizontal: 20,
452
+ flexDirection: 'row',
453
+ justifyContent: 'flex-end',
454
+ alignItems: 'center',
455
+ zIndex: 100,
456
+ backgroundColor: "transparent",
457
+ },
458
+ closeButton: {
390
459
  width: 40,
391
460
  height: 40,
392
- borderRadius: 20,
393
- backgroundColor: 'rgba(0, 0, 0, 0.5)',
394
461
  justifyContent: 'center',
395
462
  alignItems: 'center',
396
- zIndex: 10,
397
463
  },
398
464
  footerContainer: {
399
465
  height: 60,
@@ -410,11 +476,11 @@ const previewStyles = (top: number, dimensions?: { itemWidth: number, imageWidth
410
476
  width: 8,
411
477
  height: 8,
412
478
  borderRadius: 4,
413
- backgroundColor: '#FFFFFF',
479
+ backgroundColor: theme.colors.iconGrayColor,
414
480
  marginHorizontal: 4,
415
481
  },
416
482
  activeDot: {
417
- backgroundColor: '#007AC6',
483
+ backgroundColor: theme.colors.primary,
418
484
  width: 10,
419
485
  height: 10,
420
486
  borderRadius: 5,
package/src/index.ts CHANGED
@@ -40,6 +40,15 @@ import SearchableList from "./list/SearchableList";
40
40
  import * as BaseStyle from "./utils/BaseStyle";
41
41
  import * as TextConstants from "./utils/TextConstants";
42
42
 
43
+ // Navbar Components
44
+ import CustomNavBar from "./navbar/CustomNavBar";
45
+
46
+ // SVG Icons
47
+ import BackArrow from "../assets/images/back_arrow.svg";
48
+ import Camera from "../assets/images/ic_camera.svg";
49
+ import Close from "../assets/images/ic_close.svg";
50
+ import Folder from "../assets/images/ic_folder.svg";
51
+
43
52
  export {
44
53
  ThemedButton,
45
54
  MultipleImagePreview,
@@ -63,6 +72,11 @@ export {
63
72
  BaseStyle,
64
73
  TextConstants,
65
74
  CardView,
75
+ CustomNavBar,
76
+ BackArrow,
77
+ Camera,
78
+ Close,
79
+ Folder,
66
80
  };
67
81
 
68
82
  export type { BottomSheetDialogRef };