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.
- package/assets/images/back_arrow.svg +3 -0
- package/mri-manifest.json +40 -8
- package/package.json +2 -2
- package/src/feedback/CustomAlert.tsx +4 -2
- package/src/image/ImagePickerBottomSheet.tsx +11 -2
- package/src/image/ImagePickerView.tsx +14 -9
- package/src/image/MultipleImagePreview.tsx +156 -90
- package/src/index.ts +14 -0
- package/src/input/CustomDropdown.tsx +55 -12
- package/src/input/FormField.tsx +168 -73
- package/src/layout/PropertyHeaderComponent.tsx +2 -2
- package/src/navbar/CustomNavBar.tsx +42 -0
- package/src/utils/ValidationUtils.ts +197 -0
- package/lib/button/ThemedButton.d.ts +0 -19
- package/lib/button/ThemedButton.js +0 -73
- package/lib/feedback/ActivityLoader.d.ts +0 -12
- package/lib/feedback/ActivityLoader.js +0 -65
- package/lib/feedback/CustomAlert.d.ts +0 -20
- package/lib/feedback/CustomAlert.js +0 -96
- package/lib/feedback/DeleteImageConfirmationDialog.d.ts +0 -16
- package/lib/feedback/DeleteImageConfirmationDialog.js +0 -19
- package/lib/feedback/ProgressBar.d.ts +0 -15
- package/lib/feedback/ProgressBar.js +0 -34
- package/lib/image/ImagePickerBottomSheet.d.ts +0 -10
- package/lib/image/ImagePickerBottomSheet.js +0 -31
- package/lib/image/ImagePickerView.d.ts +0 -11
- package/lib/image/ImagePickerView.js +0 -65
- package/lib/image/MultipleImagePreview.d.ts +0 -33
- package/lib/image/MultipleImagePreview.js +0 -332
- package/lib/image/StackedImage.d.ts +0 -11
- package/lib/image/StackedImage.js +0 -122
- package/lib/index.d.ts +0 -25
- package/lib/index.js +0 -56
- package/lib/input/CustomDropdown.d.ts +0 -40
- package/lib/input/CustomDropdown.js +0 -71
- package/lib/input/CustomInput.d.ts +0 -17
- package/lib/input/CustomInput.js +0 -47
- package/lib/input/FormField.d.ts +0 -31
- package/lib/input/FormField.js +0 -250
- package/lib/input/KeyboardScrollView.d.ts +0 -7
- package/lib/input/KeyboardScrollView.js +0 -96
- package/lib/input/SearchViewInput.d.ts +0 -59
- package/lib/input/SearchViewInput.js +0 -80
- package/lib/layout/BottomSheetDialog.d.ts +0 -5
- package/lib/layout/BottomSheetDialog.js +0 -156
- package/lib/layout/BottomTwoButtonLayoutComponent.d.ts +0 -26
- package/lib/layout/BottomTwoButtonLayoutComponent.js +0 -81
- package/lib/layout/CardView.d.ts +0 -24
- package/lib/layout/CardView.js +0 -79
- package/lib/layout/PropertyHeaderComponent.d.ts +0 -34
- package/lib/layout/PropertyHeaderComponent.js +0 -63
- package/lib/list/SearchableList.d.ts +0 -50
- package/lib/list/SearchableList.js +0 -143
- package/lib/models/PropertyImage.d.ts +0 -11
- package/lib/models/PropertyImage.js +0 -22
- package/lib/typography/Label.d.ts +0 -26
- package/lib/typography/Label.js +0 -147
- package/lib/utils/BaseStyle.d.ts +0 -31
- package/lib/utils/BaseStyle.js +0 -42
- package/lib/utils/Strings.d.ts +0 -1
- package/lib/utils/Strings.js +0 -4
- package/lib/utils/TextConstants.d.ts +0 -23
- package/lib/utils/TextConstants.js +0 -28
- package/lib/utils/Utils.d.ts +0 -5
- package/lib/utils/Utils.js +0 -12
- package/lib/webbaseview/WebBaseView.d.ts +0 -8
- 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
|
-
"
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
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.
|
|
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
|
-
|
|
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:
|
|
85
|
-
imageWidth:
|
|
86
|
-
imageHeight: isWeb ?
|
|
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
|
|
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(
|
|
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 <
|
|
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 <
|
|
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
|
-
{
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
}
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
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=
|
|
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={
|
|
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
|
-
{
|
|
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:
|
|
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 ||
|
|
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:
|
|
338
|
-
height: dimensions?.imageHeight ||
|
|
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
|
-
|
|
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
|
-
|
|
358
|
-
|
|
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
|
-
|
|
376
|
-
|
|
377
|
-
paddingVertical: 12,
|
|
378
|
-
paddingHorizontal: 16
|
|
428
|
+
paddingVertical: 8,
|
|
429
|
+
paddingHorizontal: 12,
|
|
379
430
|
},
|
|
380
431
|
imageDefaultText: {
|
|
381
|
-
color:
|
|
432
|
+
color: theme.colors.primary,
|
|
382
433
|
alignSelf: 'center',
|
|
383
434
|
textAlign: 'center',
|
|
384
|
-
marginStart:
|
|
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:
|
|
389
|
-
right:
|
|
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:
|
|
479
|
+
backgroundColor: theme.colors.iconGrayColor,
|
|
414
480
|
marginHorizontal: 4,
|
|
415
481
|
},
|
|
416
482
|
activeDot: {
|
|
417
|
-
backgroundColor:
|
|
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 };
|