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
|
@@ -0,0 +1,155 @@
|
|
|
1
|
+
import {StyleSheet, Text, TouchableOpacity, View} from 'react-native';
|
|
2
|
+
import React, {ReactElement} from 'react';
|
|
3
|
+
import { Colors } from '../utils/BaseStyle';
|
|
4
|
+
import { PropertyImage } from '../models/PropertyImage';
|
|
5
|
+
import { Image } from 'expo-image';
|
|
6
|
+
|
|
7
|
+
type StackedImageProps = {
|
|
8
|
+
photos: PropertyImage[];
|
|
9
|
+
onPress?: (photos: any) => void;
|
|
10
|
+
};
|
|
11
|
+
|
|
12
|
+
type Size = {
|
|
13
|
+
width: number;
|
|
14
|
+
height: number;
|
|
15
|
+
};
|
|
16
|
+
|
|
17
|
+
const MAX_COUNT = 3;
|
|
18
|
+
|
|
19
|
+
// Spacing constants
|
|
20
|
+
const Spacing = {
|
|
21
|
+
mini: 4,
|
|
22
|
+
sm: 8,
|
|
23
|
+
smd: 12,
|
|
24
|
+
md: 16,
|
|
25
|
+
lg: 24,
|
|
26
|
+
xl: 32,
|
|
27
|
+
xxl: 40,
|
|
28
|
+
xxxl: 80,
|
|
29
|
+
};
|
|
30
|
+
|
|
31
|
+
// Border constants
|
|
32
|
+
const Border = {
|
|
33
|
+
br_mini: 15,
|
|
34
|
+
br_mini2: 5,
|
|
35
|
+
border_10: 10,
|
|
36
|
+
border_20: 20,
|
|
37
|
+
border_25: 25,
|
|
38
|
+
border_40: 40,
|
|
39
|
+
};
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* A component for displaying stacked images
|
|
43
|
+
*/
|
|
44
|
+
const StackedImage: React.FC<StackedImageProps> = ({
|
|
45
|
+
photos,
|
|
46
|
+
onPress,
|
|
47
|
+
}): ReactElement => {
|
|
48
|
+
const photoCount = photos?.length > MAX_COUNT ? MAX_COUNT : photos.length;
|
|
49
|
+
const style = StackPhotoStyle(photoCount);
|
|
50
|
+
|
|
51
|
+
const getHeight = (index: number) => {
|
|
52
|
+
if ((photoCount - 1) == index) {
|
|
53
|
+
return {width: 120, height: 120};
|
|
54
|
+
} else {
|
|
55
|
+
return {width: 100 + (index * 8), height: 100 + (index * 8)};
|
|
56
|
+
}
|
|
57
|
+
};
|
|
58
|
+
|
|
59
|
+
const getRight = (index: number) => {
|
|
60
|
+
if (photoCount == 1) {
|
|
61
|
+
return undefined;
|
|
62
|
+
} else {
|
|
63
|
+
return index * Spacing.smd;
|
|
64
|
+
}
|
|
65
|
+
};
|
|
66
|
+
|
|
67
|
+
const getLeft = (index: number) => {
|
|
68
|
+
if (photoCount == 1) {
|
|
69
|
+
return 0;
|
|
70
|
+
} else {
|
|
71
|
+
return undefined;
|
|
72
|
+
}
|
|
73
|
+
};
|
|
74
|
+
|
|
75
|
+
const onPressStack = () => {
|
|
76
|
+
if (onPress) {
|
|
77
|
+
onPress(photos);
|
|
78
|
+
}
|
|
79
|
+
};
|
|
80
|
+
|
|
81
|
+
return (
|
|
82
|
+
<TouchableOpacity onPress={onPressStack}>
|
|
83
|
+
<View style={style.imageStack}>
|
|
84
|
+
{photos
|
|
85
|
+
.slice(0, photoCount)
|
|
86
|
+
.reverse()
|
|
87
|
+
.map((image: PropertyImage, index: number) => (
|
|
88
|
+
<Image
|
|
89
|
+
key={index}
|
|
90
|
+
source={image.imageBlobUrl}
|
|
91
|
+
contentFit="cover"
|
|
92
|
+
placeholderContentFit='cover'
|
|
93
|
+
transition={500}
|
|
94
|
+
cachePolicy="disk"
|
|
95
|
+
placeholder={require("../../assets/images/placeholder.png")}
|
|
96
|
+
style={[
|
|
97
|
+
style.image,
|
|
98
|
+
{
|
|
99
|
+
width: getHeight(index).width,
|
|
100
|
+
height: getHeight(index).height,
|
|
101
|
+
borderColor: Colors.whiteColor,
|
|
102
|
+
borderWidth: 1,
|
|
103
|
+
shadowColor: Colors.shadowColor,
|
|
104
|
+
shadowOffset: { width: 0, height: 2 },
|
|
105
|
+
shadowOpacity: 0.1,
|
|
106
|
+
shadowRadius: 4,
|
|
107
|
+
bottom: 0,
|
|
108
|
+
left: getLeft(index),
|
|
109
|
+
right: getRight(index),
|
|
110
|
+
},
|
|
111
|
+
]}
|
|
112
|
+
/>
|
|
113
|
+
))}
|
|
114
|
+
</View>
|
|
115
|
+
|
|
116
|
+
{photos.length >= 1 && (
|
|
117
|
+
<View style={style.countOverlay}>
|
|
118
|
+
<Text style={style.countText}>{photos.length}</Text>
|
|
119
|
+
</View>
|
|
120
|
+
)}
|
|
121
|
+
</TouchableOpacity>
|
|
122
|
+
);
|
|
123
|
+
};
|
|
124
|
+
|
|
125
|
+
const StackPhotoStyle = (photoCount: number) =>
|
|
126
|
+
StyleSheet.create({
|
|
127
|
+
imageStack: {
|
|
128
|
+
position: 'relative',
|
|
129
|
+
width: Spacing.xxxl + 60,
|
|
130
|
+
height: Spacing.xxxl + 60,
|
|
131
|
+
},
|
|
132
|
+
image: {
|
|
133
|
+
position: 'absolute',
|
|
134
|
+
borderRadius: Border.border_10,
|
|
135
|
+
},
|
|
136
|
+
countOverlay: {
|
|
137
|
+
position: 'absolute',
|
|
138
|
+
borderColor: Colors.whiteColor,
|
|
139
|
+
borderWidth: 1,
|
|
140
|
+
backgroundColor: Colors.lightThemePrimaryColor,
|
|
141
|
+
borderRadius: Border.br_mini,
|
|
142
|
+
width: Spacing.lg,
|
|
143
|
+
height: Spacing.lg,
|
|
144
|
+
alignItems: 'center',
|
|
145
|
+
bottom: 10,
|
|
146
|
+
left: (photoCount > 0 && photoCount % 2 == 0) ? 18 : 8,
|
|
147
|
+
},
|
|
148
|
+
countText: {
|
|
149
|
+
color: Colors.whiteColor,
|
|
150
|
+
fontFamily: 'OpenSans-Regular',
|
|
151
|
+
includeFontPadding: false,
|
|
152
|
+
},
|
|
153
|
+
});
|
|
154
|
+
|
|
155
|
+
export default StackedImage;
|
package/src/index.ts
ADDED
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
// import all components from their respective folders
|
|
2
|
+
|
|
3
|
+
// Button Components
|
|
4
|
+
import ThemedButton from "./button/ThemedButton";
|
|
5
|
+
|
|
6
|
+
// Image Components
|
|
7
|
+
import MultipleImagePreview from "./image/MultipleImagePreview";
|
|
8
|
+
import ImagePickerView from "./image/ImagePickerView";
|
|
9
|
+
import ImagePickerBottomSheet from "./image/ImagePickerBottomSheet";
|
|
10
|
+
import StackedImage from "./image/StackedImage";
|
|
11
|
+
|
|
12
|
+
// Input Components
|
|
13
|
+
import CustomInput from "./input/CustomInput";
|
|
14
|
+
import FormField from "./input/FormField";
|
|
15
|
+
import CustomDropdown from "./input/CustomDropdown";
|
|
16
|
+
import SearchViewInput from "./input/SearchViewInput";
|
|
17
|
+
import KeyboardScrollView from "./input/KeyboardScrollView";
|
|
18
|
+
|
|
19
|
+
// Layout Components
|
|
20
|
+
import BottomTwoButtonLayoutComponent from "./layout/BottomTwoButtonLayoutComponent";
|
|
21
|
+
import PropertyHeaderComponent from "./layout/PropertyHeaderComponent";
|
|
22
|
+
import BottomSheetDialog from "./layout/BottomSheetDialog";
|
|
23
|
+
import { BottomSheetDialogRef } from "./layout/BottomSheetDialog";
|
|
24
|
+
import CardView from "./layout/CardView";
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
// Feedback Components
|
|
28
|
+
import ActivityLoader from "./feedback/ActivityLoader";
|
|
29
|
+
import ProgressBar from "./feedback/ProgressBar";
|
|
30
|
+
import CustomAlert from "./feedback/CustomAlert";
|
|
31
|
+
import DeleteImageConfirmationDialog from "./feedback/DeleteImageConfirmationDialog";
|
|
32
|
+
|
|
33
|
+
// Typography Components
|
|
34
|
+
import Label from "./typography/Label";
|
|
35
|
+
|
|
36
|
+
// List Components
|
|
37
|
+
import SearchableList from "./list/SearchableList";
|
|
38
|
+
|
|
39
|
+
// Common utilities and types
|
|
40
|
+
import * as BaseStyle from "./utils/BaseStyle";
|
|
41
|
+
import * as TextConstants from "./utils/TextConstants";
|
|
42
|
+
|
|
43
|
+
export {
|
|
44
|
+
ThemedButton,
|
|
45
|
+
MultipleImagePreview,
|
|
46
|
+
ImagePickerView,
|
|
47
|
+
ImagePickerBottomSheet,
|
|
48
|
+
StackedImage,
|
|
49
|
+
CustomInput,
|
|
50
|
+
FormField,
|
|
51
|
+
CustomDropdown,
|
|
52
|
+
SearchViewInput,
|
|
53
|
+
KeyboardScrollView,
|
|
54
|
+
BottomTwoButtonLayoutComponent,
|
|
55
|
+
PropertyHeaderComponent,
|
|
56
|
+
BottomSheetDialog,
|
|
57
|
+
ActivityLoader,
|
|
58
|
+
ProgressBar,
|
|
59
|
+
CustomAlert,
|
|
60
|
+
DeleteImageConfirmationDialog,
|
|
61
|
+
Label,
|
|
62
|
+
SearchableList,
|
|
63
|
+
BaseStyle,
|
|
64
|
+
TextConstants,
|
|
65
|
+
CardView,
|
|
66
|
+
};
|
|
67
|
+
|
|
68
|
+
export type { BottomSheetDialogRef };
|
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import { View, StyleSheet } from 'react-native';
|
|
3
|
+
import { Dropdown } from 'react-native-element-dropdown';
|
|
4
|
+
import { Colors } from '../utils/BaseStyle';
|
|
5
|
+
|
|
6
|
+
type DropdownItem = {
|
|
7
|
+
label?: string;
|
|
8
|
+
value: string;
|
|
9
|
+
};
|
|
10
|
+
|
|
11
|
+
interface CustomDropdownProps {
|
|
12
|
+
/**
|
|
13
|
+
* Test ID for the component
|
|
14
|
+
*/
|
|
15
|
+
testID?: string;
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* Array of items to display in the dropdown
|
|
19
|
+
*/
|
|
20
|
+
items: DropdownItem[];
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* Currently selected value
|
|
24
|
+
*/
|
|
25
|
+
value: string;
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* Z-index for the dropdown (important for positioning)
|
|
29
|
+
*/
|
|
30
|
+
zIndex: number;
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* Function to call when selection changes
|
|
34
|
+
*/
|
|
35
|
+
onChange: (item: DropdownItem) => void;
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* Placeholder text to display when no item is selected
|
|
39
|
+
*/
|
|
40
|
+
placeHolder: string;
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* Whether the dropdown is disabled
|
|
44
|
+
*/
|
|
45
|
+
disabled?: boolean;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* A customizable dropdown component
|
|
50
|
+
*/
|
|
51
|
+
const CustomDropdown: React.FC<CustomDropdownProps> = ({
|
|
52
|
+
testID,
|
|
53
|
+
items,
|
|
54
|
+
value,
|
|
55
|
+
onChange,
|
|
56
|
+
zIndex,
|
|
57
|
+
placeHolder,
|
|
58
|
+
disabled = false
|
|
59
|
+
}) => {
|
|
60
|
+
// Find the selected item in the items array
|
|
61
|
+
const findSelectedItem = () => {
|
|
62
|
+
if (!value || !items || items.length === 0) return null;
|
|
63
|
+
|
|
64
|
+
// Try to find an item where the 'value' property matches the provided value
|
|
65
|
+
return items.find(item => item.value === value) || null;
|
|
66
|
+
};
|
|
67
|
+
|
|
68
|
+
const selectedItem = findSelectedItem();
|
|
69
|
+
|
|
70
|
+
// Create styles for disabled state
|
|
71
|
+
const dropdownStyle = [
|
|
72
|
+
styles.dropdown,
|
|
73
|
+
disabled && styles.disabledDropdown
|
|
74
|
+
];
|
|
75
|
+
|
|
76
|
+
return (
|
|
77
|
+
<View style={[styles.container, { zIndex }]}>
|
|
78
|
+
<Dropdown
|
|
79
|
+
testID={testID}
|
|
80
|
+
style={dropdownStyle}
|
|
81
|
+
containerStyle={styles.dropdownContainer}
|
|
82
|
+
data={items}
|
|
83
|
+
labelField="value"
|
|
84
|
+
valueField="value"
|
|
85
|
+
value={selectedItem?.value || ''}
|
|
86
|
+
onChange={(item: DropdownItem) => !disabled ? onChange(item) : null}
|
|
87
|
+
placeholder={placeHolder}
|
|
88
|
+
autoScroll={true}
|
|
89
|
+
dropdownPosition='auto'
|
|
90
|
+
itemTextStyle={styles.itemText}
|
|
91
|
+
disable={disabled}
|
|
92
|
+
placeholderStyle={styles.placeholder}
|
|
93
|
+
selectedTextStyle={styles.selectedText}
|
|
94
|
+
/>
|
|
95
|
+
</View>
|
|
96
|
+
);
|
|
97
|
+
};
|
|
98
|
+
|
|
99
|
+
const styles = StyleSheet.create({
|
|
100
|
+
container: {
|
|
101
|
+
position: 'relative',
|
|
102
|
+
overflow: 'visible',
|
|
103
|
+
},
|
|
104
|
+
dropdown: {
|
|
105
|
+
paddingHorizontal: 0,
|
|
106
|
+
paddingVertical: 10,
|
|
107
|
+
paddingBottom: 10,
|
|
108
|
+
borderBottomWidth: 1.0,
|
|
109
|
+
borderBottomColor: Colors.borderColor,
|
|
110
|
+
},
|
|
111
|
+
disabledDropdown: {
|
|
112
|
+
opacity: 0.5,
|
|
113
|
+
},
|
|
114
|
+
dropdownContainer: {
|
|
115
|
+
zIndex: 100,
|
|
116
|
+
borderRadius: 8,
|
|
117
|
+
borderWidth: 1,
|
|
118
|
+
borderColor: Colors.borderColor,
|
|
119
|
+
shadowColor: Colors.shadowColor,
|
|
120
|
+
shadowOffset: { width: 0, height: 2 },
|
|
121
|
+
shadowOpacity: 0.1,
|
|
122
|
+
shadowRadius: 4,
|
|
123
|
+
elevation: 3,
|
|
124
|
+
},
|
|
125
|
+
itemText: {
|
|
126
|
+
padding: 0,
|
|
127
|
+
fontSize: 16,
|
|
128
|
+
fontWeight: '500',
|
|
129
|
+
color: Colors.primaryTextColor,
|
|
130
|
+
},
|
|
131
|
+
placeholder: {
|
|
132
|
+
color: Colors.secondaryTextColor,
|
|
133
|
+
fontSize: 16,
|
|
134
|
+
},
|
|
135
|
+
selectedText: {
|
|
136
|
+
color: Colors.primaryTextColor,
|
|
137
|
+
fontSize: 16,
|
|
138
|
+
fontWeight: '500',
|
|
139
|
+
},
|
|
140
|
+
});
|
|
141
|
+
|
|
142
|
+
export default CustomDropdown;
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
import {
|
|
2
|
+
TextInput,
|
|
3
|
+
KeyboardType,
|
|
4
|
+
StyleSheet,
|
|
5
|
+
StyleProp,
|
|
6
|
+
TextStyle,
|
|
7
|
+
View,
|
|
8
|
+
Text,
|
|
9
|
+
ViewStyle,
|
|
10
|
+
} from "react-native";
|
|
11
|
+
import React, { useState } from "react";
|
|
12
|
+
import { Colors } from "../utils/BaseStyle";
|
|
13
|
+
|
|
14
|
+
const CustomInput = ({
|
|
15
|
+
onChangeText,
|
|
16
|
+
placeholder,
|
|
17
|
+
keyBoardType = "default",
|
|
18
|
+
textStyle,
|
|
19
|
+
customStyle,
|
|
20
|
+
placeholderTextColor,
|
|
21
|
+
text,
|
|
22
|
+
isEditable,
|
|
23
|
+
inputType,
|
|
24
|
+
validationPattern,
|
|
25
|
+
errorMessage,
|
|
26
|
+
onSubmitEditing,
|
|
27
|
+
returnKeyType
|
|
28
|
+
}: {
|
|
29
|
+
onChangeText: (text: string) => void;
|
|
30
|
+
placeholder: string;
|
|
31
|
+
keyBoardType?: KeyboardType;
|
|
32
|
+
customStyle?: StyleProp<ViewStyle>;
|
|
33
|
+
textStyle?: StyleProp<TextStyle>;
|
|
34
|
+
placeholderTextColor?: string;
|
|
35
|
+
text?: string;
|
|
36
|
+
isEditable?: boolean;
|
|
37
|
+
inputType?: 'email' | 'text' | 'number';
|
|
38
|
+
validationPattern?: RegExp;
|
|
39
|
+
errorMessage?: string;
|
|
40
|
+
onSubmitEditing?: () => void;
|
|
41
|
+
returnKeyType?: 'default' | 'done' | 'go' | 'next' | 'search' | 'send';
|
|
42
|
+
}) => {
|
|
43
|
+
const [isValid, setIsValid] = useState(true);
|
|
44
|
+
|
|
45
|
+
const validateInput = (text: string) => {
|
|
46
|
+
if (inputType === 'email') {
|
|
47
|
+
const emailRegex = validationPattern || /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
|
|
48
|
+
setIsValid(emailRegex.test(text));
|
|
49
|
+
} else if (validationPattern) {
|
|
50
|
+
setIsValid(validationPattern.test(text));
|
|
51
|
+
} else {
|
|
52
|
+
setIsValid(true);
|
|
53
|
+
}
|
|
54
|
+
};
|
|
55
|
+
|
|
56
|
+
const onChange = (text: string) => {
|
|
57
|
+
onChangeText(text);
|
|
58
|
+
validateInput(text);
|
|
59
|
+
};
|
|
60
|
+
|
|
61
|
+
const styles = inputStyles(isEditable!!, !isValid);
|
|
62
|
+
|
|
63
|
+
return (
|
|
64
|
+
<View style={[styles.inputcontainer, customStyle]}>
|
|
65
|
+
<TextInput
|
|
66
|
+
placeholder={placeholder}
|
|
67
|
+
placeholderTextColor={placeholderTextColor}
|
|
68
|
+
style={[styles.input, textStyle]}
|
|
69
|
+
value={text}
|
|
70
|
+
onChangeText={onChange}
|
|
71
|
+
keyboardType={keyBoardType}
|
|
72
|
+
editable={isEditable}
|
|
73
|
+
onSubmitEditing={onSubmitEditing}
|
|
74
|
+
returnKeyType={returnKeyType}
|
|
75
|
+
/>
|
|
76
|
+
{!isValid && isEditable && errorMessage && (
|
|
77
|
+
<Text style={styles.errorText}>{errorMessage}</Text>
|
|
78
|
+
)}
|
|
79
|
+
</View>
|
|
80
|
+
);
|
|
81
|
+
};
|
|
82
|
+
|
|
83
|
+
const inputStyles = (isEditable: boolean, isError: boolean) =>
|
|
84
|
+
StyleSheet.create({
|
|
85
|
+
inputcontainer: {
|
|
86
|
+
|
|
87
|
+
},
|
|
88
|
+
input: {
|
|
89
|
+
borderBottomWidth: isEditable ? 1 : 0,
|
|
90
|
+
borderBottomColor: isError ? "red" : Colors.darkGrayColor,
|
|
91
|
+
outlineColor: "transparent",
|
|
92
|
+
paddingVertical: 10
|
|
93
|
+
},
|
|
94
|
+
errorText: {
|
|
95
|
+
color: 'red',
|
|
96
|
+
fontSize: 12,
|
|
97
|
+
marginTop: 5,
|
|
98
|
+
},
|
|
99
|
+
});
|
|
100
|
+
|
|
101
|
+
export default CustomInput;
|