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,208 @@
|
|
|
1
|
+
import React, {
|
|
2
|
+
useEffect,
|
|
3
|
+
useImperativeHandle,
|
|
4
|
+
useRef,
|
|
5
|
+
useState,
|
|
6
|
+
} from 'react';
|
|
7
|
+
import {
|
|
8
|
+
View,
|
|
9
|
+
StyleSheet,
|
|
10
|
+
Modal,
|
|
11
|
+
TouchableWithoutFeedback,
|
|
12
|
+
Animated,
|
|
13
|
+
Dimensions,
|
|
14
|
+
PanResponder,
|
|
15
|
+
ViewStyle,
|
|
16
|
+
BackHandler,
|
|
17
|
+
} from 'react-native';
|
|
18
|
+
|
|
19
|
+
const screenHeight = Dimensions.get('window').height;
|
|
20
|
+
|
|
21
|
+
type BottomSheetDialogProps = {
|
|
22
|
+
visible: boolean;
|
|
23
|
+
onClose: () => void;
|
|
24
|
+
children: React.ReactNode;
|
|
25
|
+
height?: number | string;
|
|
26
|
+
containerStyle?: ViewStyle;
|
|
27
|
+
contentStyle?: ViewStyle;
|
|
28
|
+
closeOnBackdropPress?: boolean;
|
|
29
|
+
isTranslucent?: boolean;
|
|
30
|
+
closeOnBackButtonPress?: boolean;
|
|
31
|
+
};
|
|
32
|
+
|
|
33
|
+
export type BottomSheetDialogRef = {
|
|
34
|
+
closeBottomSheet: () => void;
|
|
35
|
+
};
|
|
36
|
+
|
|
37
|
+
const BottomSheetDialog = React.forwardRef<BottomSheetDialogRef, BottomSheetDialogProps>(
|
|
38
|
+
(
|
|
39
|
+
{
|
|
40
|
+
visible,
|
|
41
|
+
onClose,
|
|
42
|
+
children,
|
|
43
|
+
height = screenHeight * 0.6,
|
|
44
|
+
containerStyle,
|
|
45
|
+
contentStyle,
|
|
46
|
+
closeOnBackdropPress = true,
|
|
47
|
+
isTranslucent = true,
|
|
48
|
+
closeOnBackButtonPress = true,
|
|
49
|
+
},
|
|
50
|
+
ref
|
|
51
|
+
) => {
|
|
52
|
+
const translateY = useRef(new Animated.Value(screenHeight)).current;
|
|
53
|
+
const opacity = useRef(new Animated.Value(0)).current;
|
|
54
|
+
|
|
55
|
+
const isAnimating = useRef(false);
|
|
56
|
+
const isVisibleRef = useRef(false);
|
|
57
|
+
|
|
58
|
+
useImperativeHandle(ref, () => ({
|
|
59
|
+
closeBottomSheet,
|
|
60
|
+
}));
|
|
61
|
+
|
|
62
|
+
useEffect(() => {
|
|
63
|
+
if (visible) {
|
|
64
|
+
openBottomSheet();
|
|
65
|
+
} else {
|
|
66
|
+
closeBottomSheet(); // Animate close
|
|
67
|
+
}
|
|
68
|
+
}, [visible]);
|
|
69
|
+
|
|
70
|
+
const openBottomSheet = () => {
|
|
71
|
+
if (isAnimating.current || isVisibleRef.current) return;
|
|
72
|
+
isAnimating.current = true;
|
|
73
|
+
|
|
74
|
+
Animated.parallel([
|
|
75
|
+
Animated.timing(translateY, {
|
|
76
|
+
toValue: 0,
|
|
77
|
+
duration: 300,
|
|
78
|
+
useNativeDriver: true,
|
|
79
|
+
}),
|
|
80
|
+
Animated.timing(opacity, {
|
|
81
|
+
toValue: 1,
|
|
82
|
+
duration: 300,
|
|
83
|
+
useNativeDriver: true,
|
|
84
|
+
}),
|
|
85
|
+
]).start(() => {
|
|
86
|
+
isAnimating.current = false;
|
|
87
|
+
isVisibleRef.current = true;
|
|
88
|
+
});
|
|
89
|
+
};
|
|
90
|
+
|
|
91
|
+
const closeBottomSheet = () => {
|
|
92
|
+
if (isAnimating.current || !isVisibleRef.current) return;
|
|
93
|
+
isAnimating.current = true;
|
|
94
|
+
|
|
95
|
+
Animated.parallel([
|
|
96
|
+
Animated.timing(translateY, {
|
|
97
|
+
toValue: screenHeight,
|
|
98
|
+
duration: 300,
|
|
99
|
+
useNativeDriver: true,
|
|
100
|
+
}),
|
|
101
|
+
Animated.timing(opacity, {
|
|
102
|
+
toValue: 0,
|
|
103
|
+
duration: 300,
|
|
104
|
+
useNativeDriver: true,
|
|
105
|
+
}),
|
|
106
|
+
]).start(() => {
|
|
107
|
+
isAnimating.current = false;
|
|
108
|
+
isVisibleRef.current = false;
|
|
109
|
+
onClose?.();
|
|
110
|
+
});
|
|
111
|
+
};
|
|
112
|
+
|
|
113
|
+
const handleBackdropPress = () => {
|
|
114
|
+
if (closeOnBackdropPress) {
|
|
115
|
+
closeBottomSheet();
|
|
116
|
+
}
|
|
117
|
+
};
|
|
118
|
+
|
|
119
|
+
const panResponder = useRef(
|
|
120
|
+
PanResponder.create({
|
|
121
|
+
onStartShouldSetPanResponder: () => true,
|
|
122
|
+
onMoveShouldSetPanResponder: (_, gestureState) => gestureState.dy > 0,
|
|
123
|
+
onPanResponderMove: (_, gestureState) => {
|
|
124
|
+
if (gestureState.dy > 0) {
|
|
125
|
+
translateY.setValue(gestureState.dy);
|
|
126
|
+
}
|
|
127
|
+
},
|
|
128
|
+
onPanResponderRelease: (_, gestureState) => {
|
|
129
|
+
if (gestureState.dy > 100) {
|
|
130
|
+
closeBottomSheet();
|
|
131
|
+
} else {
|
|
132
|
+
Animated.spring(translateY, {
|
|
133
|
+
toValue: 0,
|
|
134
|
+
useNativeDriver: true,
|
|
135
|
+
}).start();
|
|
136
|
+
}
|
|
137
|
+
},
|
|
138
|
+
})
|
|
139
|
+
).current;
|
|
140
|
+
|
|
141
|
+
return (
|
|
142
|
+
<Modal transparent visible={visible} animationType="none" statusBarTranslucent={isTranslucent}
|
|
143
|
+
onRequestClose={() => {
|
|
144
|
+
onClose?.();
|
|
145
|
+
}} >
|
|
146
|
+
<View style={styles.modalContainer}>
|
|
147
|
+
<TouchableWithoutFeedback onPress={handleBackdropPress}>
|
|
148
|
+
<Animated.View style={[styles.backdrop, { opacity }]} />
|
|
149
|
+
</TouchableWithoutFeedback>
|
|
150
|
+
|
|
151
|
+
<Animated.View
|
|
152
|
+
style={[
|
|
153
|
+
styles.bottomSheetContainer,
|
|
154
|
+
containerStyle,
|
|
155
|
+
{
|
|
156
|
+
maxHeight:"100%",
|
|
157
|
+
height,
|
|
158
|
+
transform: [{ translateY }],
|
|
159
|
+
},
|
|
160
|
+
]}
|
|
161
|
+
>
|
|
162
|
+
<View style={styles.dragHandleContainer} {...panResponder.panHandlers}>
|
|
163
|
+
<View style={styles.dragHandle} />
|
|
164
|
+
</View>
|
|
165
|
+
<View style={[styles.contentContainer, contentStyle]}>
|
|
166
|
+
{children}
|
|
167
|
+
</View>
|
|
168
|
+
</Animated.View>
|
|
169
|
+
</View>
|
|
170
|
+
</Modal>
|
|
171
|
+
);
|
|
172
|
+
}
|
|
173
|
+
);
|
|
174
|
+
|
|
175
|
+
const styles = StyleSheet.create({
|
|
176
|
+
modalContainer: {
|
|
177
|
+
flex: 1,
|
|
178
|
+
justifyContent: 'flex-end',
|
|
179
|
+
},
|
|
180
|
+
backdrop: {
|
|
181
|
+
...StyleSheet.absoluteFillObject,
|
|
182
|
+
backgroundColor: 'rgba(0, 0, 0, 0.5)',
|
|
183
|
+
},
|
|
184
|
+
bottomSheetContainer: {
|
|
185
|
+
backgroundColor: 'white',
|
|
186
|
+
borderTopLeftRadius: 20,
|
|
187
|
+
borderTopRightRadius: 20,
|
|
188
|
+
overflow: 'hidden',
|
|
189
|
+
},
|
|
190
|
+
dragHandleContainer: {
|
|
191
|
+
width: '100%',
|
|
192
|
+
height: 24,
|
|
193
|
+
alignItems: 'center',
|
|
194
|
+
justifyContent: 'center',
|
|
195
|
+
},
|
|
196
|
+
dragHandle: {
|
|
197
|
+
width: 40,
|
|
198
|
+
height: 4,
|
|
199
|
+
borderRadius: 2,
|
|
200
|
+
backgroundColor: '#ccc',
|
|
201
|
+
},
|
|
202
|
+
contentContainer: {
|
|
203
|
+
flex: 1,
|
|
204
|
+
padding: 16,
|
|
205
|
+
},
|
|
206
|
+
});
|
|
207
|
+
|
|
208
|
+
export default BottomSheetDialog;
|
|
@@ -0,0 +1,153 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import { View, StyleSheet, ViewStyle, Platform } from 'react-native';
|
|
3
|
+
import ThemedButton from '../button/ThemedButton';
|
|
4
|
+
import ProgressBar from '../feedback/ProgressBar';
|
|
5
|
+
import { Colors } from '../utils/BaseStyle';
|
|
6
|
+
import { useSafeAreaInsets } from 'react-native-safe-area-context';
|
|
7
|
+
import { isAndroid15AndAbove, isIOS } from '../utils/Utils';
|
|
8
|
+
|
|
9
|
+
interface BottomTwoButtonLayoutProps {
|
|
10
|
+
primaryButtonTestID?: string;
|
|
11
|
+
secondaryButtonTestID?: string;
|
|
12
|
+
primaryButtonText: string;
|
|
13
|
+
secondaryButtonText: string;
|
|
14
|
+
onPrimaryButtonPress: () => void;
|
|
15
|
+
onSecondaryButtonPress: () => void;
|
|
16
|
+
isPrimaryButtonDisabled?: boolean;
|
|
17
|
+
isSecondaryButtonDisabled?: boolean;
|
|
18
|
+
containerStyle?: ViewStyle;
|
|
19
|
+
// Progress bar props
|
|
20
|
+
showProgressBar?: boolean;
|
|
21
|
+
showButtons?: boolean;
|
|
22
|
+
progress?: number;
|
|
23
|
+
maxValue?: number;
|
|
24
|
+
minValue?: number;
|
|
25
|
+
primaryColor?: string;
|
|
26
|
+
backgroundColor?: string;
|
|
27
|
+
disabledButtonColor?: string;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* A layout component with two buttons at the bottom and optional progress bar
|
|
32
|
+
*/
|
|
33
|
+
const BottomTwoButtonLayoutComponent: React.FC<BottomTwoButtonLayoutProps> = ({
|
|
34
|
+
primaryButtonTestID,
|
|
35
|
+
secondaryButtonTestID,
|
|
36
|
+
primaryButtonText,
|
|
37
|
+
secondaryButtonText,
|
|
38
|
+
onPrimaryButtonPress,
|
|
39
|
+
onSecondaryButtonPress,
|
|
40
|
+
isPrimaryButtonDisabled = false,
|
|
41
|
+
isSecondaryButtonDisabled = false,
|
|
42
|
+
containerStyle,
|
|
43
|
+
showButtons = true,
|
|
44
|
+
// Progress bar props
|
|
45
|
+
showProgressBar = false,
|
|
46
|
+
progress = 0,
|
|
47
|
+
maxValue = 1,
|
|
48
|
+
minValue = 0,
|
|
49
|
+
primaryColor = Colors.lightThemePrimaryColor,
|
|
50
|
+
backgroundColor = Colors.whiteColor,
|
|
51
|
+
disabledButtonColor = '#B8D8E8'
|
|
52
|
+
}) => {
|
|
53
|
+
|
|
54
|
+
const insets = useSafeAreaInsets();
|
|
55
|
+
// const bottomPadding = ((isIOS || isAndroid15AndAbove) ? Math.max(insets.bottom, 20) : 0) + 20;
|
|
56
|
+
const styles = layoutStyles(showButtons, (isIOS) ? insets.bottom : insets.bottom + 20, backgroundColor);
|
|
57
|
+
|
|
58
|
+
return (
|
|
59
|
+
<View style={[
|
|
60
|
+
styles.buttonContainer,
|
|
61
|
+
containerStyle
|
|
62
|
+
]}>
|
|
63
|
+
{/* Progress Bar */}
|
|
64
|
+
{showProgressBar && (
|
|
65
|
+
<View style={styles.progressBarContainer}>
|
|
66
|
+
<ProgressBar
|
|
67
|
+
progress={progress}
|
|
68
|
+
maxValue={maxValue}
|
|
69
|
+
minValue={minValue}
|
|
70
|
+
height={6}
|
|
71
|
+
backgroundColor={Colors.lightGrayColor || '#E5E5E5'}
|
|
72
|
+
progressColor={primaryColor}
|
|
73
|
+
borderRadius={3}
|
|
74
|
+
/>
|
|
75
|
+
</View>
|
|
76
|
+
)}
|
|
77
|
+
|
|
78
|
+
{/* Buttons */}
|
|
79
|
+
{showButtons && (
|
|
80
|
+
<View style={styles.buttonsRow}>
|
|
81
|
+
<ThemedButton
|
|
82
|
+
testID={secondaryButtonTestID}
|
|
83
|
+
style={styles.secondaryButton}
|
|
84
|
+
title={secondaryButtonText}
|
|
85
|
+
onPress={onSecondaryButtonPress}
|
|
86
|
+
disabled={isSecondaryButtonDisabled}
|
|
87
|
+
type="secondary"
|
|
88
|
+
primaryColor={primaryColor}
|
|
89
|
+
backgroundColor={backgroundColor}
|
|
90
|
+
disabledColor={disabledButtonColor}
|
|
91
|
+
/>
|
|
92
|
+
<ThemedButton
|
|
93
|
+
testID={primaryButtonTestID}
|
|
94
|
+
style={styles.primaryButton}
|
|
95
|
+
title={primaryButtonText}
|
|
96
|
+
onPress={onPrimaryButtonPress}
|
|
97
|
+
disabled={isPrimaryButtonDisabled}
|
|
98
|
+
type="primary"
|
|
99
|
+
primaryColor={primaryColor}
|
|
100
|
+
backgroundColor={backgroundColor}
|
|
101
|
+
disabledColor={disabledButtonColor}
|
|
102
|
+
/>
|
|
103
|
+
</View>
|
|
104
|
+
)}
|
|
105
|
+
</View>
|
|
106
|
+
);
|
|
107
|
+
};
|
|
108
|
+
|
|
109
|
+
const layoutStyles = (showButtons: boolean, paddingBottom: number, backgroundColor: string) => StyleSheet.create({
|
|
110
|
+
buttonContainer: {
|
|
111
|
+
flexDirection: 'column',
|
|
112
|
+
justifyContent: showButtons ? 'space-between' : 'center',
|
|
113
|
+
paddingHorizontal: 30,
|
|
114
|
+
paddingTop: 20,
|
|
115
|
+
paddingBottom: paddingBottom,
|
|
116
|
+
backgroundColor: backgroundColor,
|
|
117
|
+
position: 'absolute',
|
|
118
|
+
bottom: 0,
|
|
119
|
+
left: 0,
|
|
120
|
+
right: 0,
|
|
121
|
+
borderTopWidth: 1,
|
|
122
|
+
borderTopColor: '#eee',
|
|
123
|
+
elevation: 5,
|
|
124
|
+
shadowColor: Colors.shadowColor,
|
|
125
|
+
shadowOffset: { width: 0, height: -3 },
|
|
126
|
+
shadowOpacity: 0.07,
|
|
127
|
+
shadowRadius: 5,
|
|
128
|
+
},
|
|
129
|
+
progressBarContainer: {
|
|
130
|
+
width: '100%',
|
|
131
|
+
marginBottom: 20,
|
|
132
|
+
justifyContent: 'center',
|
|
133
|
+
alignItems: 'center',
|
|
134
|
+
alignContent: 'center',
|
|
135
|
+
},
|
|
136
|
+
buttonsRow: {
|
|
137
|
+
flexDirection: 'row',
|
|
138
|
+
justifyContent: 'space-between',
|
|
139
|
+
width: '100%',
|
|
140
|
+
},
|
|
141
|
+
primaryButton: {
|
|
142
|
+
flex: 1,
|
|
143
|
+
marginLeft: 8,
|
|
144
|
+
height: 60
|
|
145
|
+
},
|
|
146
|
+
secondaryButton: {
|
|
147
|
+
flex: 1,
|
|
148
|
+
marginRight: 8,
|
|
149
|
+
height: 60
|
|
150
|
+
},
|
|
151
|
+
});
|
|
152
|
+
|
|
153
|
+
export default BottomTwoButtonLayoutComponent;
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
import React, { ReactNode } from 'react';
|
|
2
|
+
import { View, StyleSheet, ViewStyle, Platform } from 'react-native';
|
|
3
|
+
import { Colors } from '../utils/BaseStyle';
|
|
4
|
+
|
|
5
|
+
interface CardViewProps {
|
|
6
|
+
children: ReactNode;
|
|
7
|
+
style?: ViewStyle;
|
|
8
|
+
cardPadding?: number;
|
|
9
|
+
borderRadius?: number;
|
|
10
|
+
backgroundColor?: string;
|
|
11
|
+
elevation?: number;
|
|
12
|
+
shadowOpacity?: number;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* CardView - A reusable card component with shadow
|
|
17
|
+
*
|
|
18
|
+
* @param children - The content to display inside the card
|
|
19
|
+
* @param style - Additional styles to apply to the card container
|
|
20
|
+
* @param cardPadding - Padding inside the card (default: 16)
|
|
21
|
+
* @param borderRadius - Border radius of the card (default: 20)
|
|
22
|
+
* @param backgroundColor - Background color of the card (default: white)
|
|
23
|
+
* @param elevation - Shadow elevation for Android (default: 4)
|
|
24
|
+
* @param shadowOpacity - Shadow opacity for iOS (default: 0.2)
|
|
25
|
+
*/
|
|
26
|
+
const CardView: React.FC<CardViewProps> = ({
|
|
27
|
+
children,
|
|
28
|
+
style,
|
|
29
|
+
cardPadding = 16,
|
|
30
|
+
borderRadius = 20,
|
|
31
|
+
backgroundColor = Colors.whiteColor,
|
|
32
|
+
elevation = 4,
|
|
33
|
+
shadowOpacity = 0.2,
|
|
34
|
+
}) => {
|
|
35
|
+
return (
|
|
36
|
+
<View
|
|
37
|
+
style={[
|
|
38
|
+
styles.container,
|
|
39
|
+
{
|
|
40
|
+
padding: cardPadding,
|
|
41
|
+
borderRadius: borderRadius,
|
|
42
|
+
backgroundColor: backgroundColor,
|
|
43
|
+
shadowColor: Colors.shadowColor,
|
|
44
|
+
shadowOffset: { width: 0, height: 2 },
|
|
45
|
+
// Shadow for iOS
|
|
46
|
+
shadowOpacity: shadowOpacity,
|
|
47
|
+
// Shadow for Android
|
|
48
|
+
elevation: Platform.OS === 'android' ? elevation : 0,
|
|
49
|
+
// On Android, add a subtle border to enhance the shadow appearance
|
|
50
|
+
// Cross-platform shadow styling
|
|
51
|
+
...Platform.select({
|
|
52
|
+
ios: {
|
|
53
|
+
shadowColor: Colors.shadowColor,
|
|
54
|
+
shadowOffset: { width: 0, height: 0 }, // Changed to 0,0 for all-around shadow
|
|
55
|
+
shadowOpacity: 0.1,
|
|
56
|
+
shadowRadius: 15, // Increased radius for better top shadow
|
|
57
|
+
borderTopWidth: 0,
|
|
58
|
+
},
|
|
59
|
+
android: {
|
|
60
|
+
elevation: 8,
|
|
61
|
+
shadowColor: Colors.shadowColor,
|
|
62
|
+
shadowOffset: { width: 0, height: 0 }, // All-around shadow
|
|
63
|
+
shadowOpacity: 0.1,
|
|
64
|
+
shadowRadius: 10,
|
|
65
|
+
borderWidth: 0.5,
|
|
66
|
+
borderColor: "rgba(0,0,0,0.1)",
|
|
67
|
+
|
|
68
|
+
},
|
|
69
|
+
web: {
|
|
70
|
+
shadowColor: Colors.shadowColor,
|
|
71
|
+
shadowOffset: { width: 0, height: 0 },
|
|
72
|
+
shadowOpacity: 0.1,
|
|
73
|
+
shadowRadius: 15,
|
|
74
|
+
borderTopWidth: 0,
|
|
75
|
+
boxShadow: '0px 0px 20px rgba(0, 0, 0, 0.1)',
|
|
76
|
+
},
|
|
77
|
+
}),
|
|
78
|
+
},
|
|
79
|
+
style,
|
|
80
|
+
]}
|
|
81
|
+
>
|
|
82
|
+
{children}
|
|
83
|
+
</View>
|
|
84
|
+
);
|
|
85
|
+
};
|
|
86
|
+
|
|
87
|
+
const styles = StyleSheet.create({
|
|
88
|
+
container: {
|
|
89
|
+
// we don't need to set width 100% and align item to start
|
|
90
|
+
// width: '100%',
|
|
91
|
+
// alignItems: 'flex-start',
|
|
92
|
+
backgroundColor: Colors.whiteColor,
|
|
93
|
+
// Shadow properties for iOS
|
|
94
|
+
shadowColor: Colors.shadowColor,
|
|
95
|
+
shadowOffset: { width: 0, height: 2 },
|
|
96
|
+
shadowRadius: 4,
|
|
97
|
+
},
|
|
98
|
+
});
|
|
99
|
+
|
|
100
|
+
export default CardView;
|
|
101
|
+
|
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import { View, TouchableOpacity, StyleSheet, ImageSourcePropType } from 'react-native';
|
|
3
|
+
import { Ionicons } from '@expo/vector-icons';
|
|
4
|
+
import Label from '../typography/Label';
|
|
5
|
+
import { TextSize, TextWeight } from '../utils/TextConstants';
|
|
6
|
+
import { Colors } from '../utils/BaseStyle';
|
|
7
|
+
|
|
8
|
+
interface PropertyHeaderComponentProps {
|
|
9
|
+
/**
|
|
10
|
+
* The logo to display in the header
|
|
11
|
+
*/
|
|
12
|
+
logo?: ImageSourcePropType;
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* The title text to display in the header
|
|
16
|
+
*/
|
|
17
|
+
title: string;
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* Function to call when the back button is pressed
|
|
21
|
+
*/
|
|
22
|
+
onBackPress: () => void;
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* Optional custom style for the header container
|
|
26
|
+
*/
|
|
27
|
+
containerStyle?: object;
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* Optional custom style for the logo container
|
|
31
|
+
*/
|
|
32
|
+
logoContainerStyle?: object;
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* Optional custom style for the title
|
|
36
|
+
*/
|
|
37
|
+
titleStyle?: object;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* A reusable header component for property screens
|
|
42
|
+
* Displays a back button, logo, and title
|
|
43
|
+
*/
|
|
44
|
+
const PropertyHeaderComponent: React.FC<PropertyHeaderComponentProps> = ({
|
|
45
|
+
title,
|
|
46
|
+
onBackPress,
|
|
47
|
+
containerStyle,
|
|
48
|
+
titleStyle,
|
|
49
|
+
}) => {
|
|
50
|
+
|
|
51
|
+
const styles = headerStyles();
|
|
52
|
+
|
|
53
|
+
return (
|
|
54
|
+
<View style={[styles.container, containerStyle]}>
|
|
55
|
+
{/* Back Button */}
|
|
56
|
+
<TouchableOpacity style={styles.backButton} onPress={onBackPress}>
|
|
57
|
+
<Ionicons name="arrow-back" size={24} color={Colors.secondaryTextColor} />
|
|
58
|
+
</TouchableOpacity>
|
|
59
|
+
|
|
60
|
+
<View style={{ justifyContent: "center", alignContent: "center" }}>
|
|
61
|
+
{/* Title */}
|
|
62
|
+
<Label
|
|
63
|
+
text={title}
|
|
64
|
+
size={TextSize.XXXXLARGE}
|
|
65
|
+
weight={TextWeight.THIN}
|
|
66
|
+
textColorType="secondary"
|
|
67
|
+
customStyle={[styles.title, titleStyle]}
|
|
68
|
+
/>
|
|
69
|
+
</View>
|
|
70
|
+
</View>
|
|
71
|
+
);
|
|
72
|
+
};
|
|
73
|
+
|
|
74
|
+
const headerStyles = () => StyleSheet.create({
|
|
75
|
+
container: {
|
|
76
|
+
paddingHorizontal: 24,
|
|
77
|
+
paddingVertical: 16
|
|
78
|
+
},
|
|
79
|
+
backButton: {
|
|
80
|
+
marginBottom: 10
|
|
81
|
+
},
|
|
82
|
+
subcontainer: {
|
|
83
|
+
flexDirection: 'row',
|
|
84
|
+
justifyContent: 'center',
|
|
85
|
+
alignContent: 'center',
|
|
86
|
+
marginTop: 16
|
|
87
|
+
},
|
|
88
|
+
logoContainer: {
|
|
89
|
+
width: 120,
|
|
90
|
+
height: 60,
|
|
91
|
+
borderWidth: 1,
|
|
92
|
+
borderColor: '#ddd',
|
|
93
|
+
borderRadius: 8,
|
|
94
|
+
justifyContent: 'center',
|
|
95
|
+
alignItems: 'center',
|
|
96
|
+
},
|
|
97
|
+
logo: {
|
|
98
|
+
width: '100%',
|
|
99
|
+
height: '100%',
|
|
100
|
+
},
|
|
101
|
+
placeholderText: {
|
|
102
|
+
textAlign: 'center',
|
|
103
|
+
},
|
|
104
|
+
title: {
|
|
105
|
+
textAlign: "right",
|
|
106
|
+
paddingVertical: 8
|
|
107
|
+
},
|
|
108
|
+
});
|
|
109
|
+
|
|
110
|
+
export default PropertyHeaderComponent;
|