app-expo-cli 1.0.5 → 1.0.6
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/package.json +1 -1
- package/src/copy-template.js +19 -1
- package/src/install-deps.js +6 -0
- package/template/assets/fonts/Inter_18pt-Black.ttf +0 -0
- package/template/assets/fonts/Inter_18pt-BlackItalic.ttf +0 -0
- package/template/assets/icon/index.ts +146 -0
- package/template/assets/images/about_us.png +0 -0
- package/template/assets/images/index.ts +6 -0
- package/template/assets/images/logo.png +0 -0
- package/template/assets/images/privacy_policy.png +0 -0
- package/template/assets/images/terms_and_conditions.png +0 -0
- package/template/src/app/_layout.tsx +3 -3
- package/template/src/app/auth/change_pass.tsx +18 -15
- package/template/src/app/auth/change_pass_modal.tsx +32 -51
- package/template/src/app/auth/forgot.tsx +74 -88
- package/template/src/app/auth/index.tsx +161 -192
- package/template/src/app/auth/opt_verify.tsx +93 -103
- package/template/src/app/auth/register.tsx +198 -260
- package/template/src/app/auth/reset_pass.tsx +102 -105
- package/template/src/app/index.tsx +70 -7
- package/template/src/app/modals/confirmation_logout_modal.tsx +0 -5
- package/template/src/app/modals/success_modal.tsx +28 -48
- package/template/src/app/settings/about_us.tsx +1 -1
- package/template/src/app/settings/privacy_policy.tsx +1 -1
- package/template/src/app/settings/terms_and_conditions.tsx +1 -1
- package/template/src/lib/DateTimePicker/DateTimePicker.tsx +63 -0
- package/template/src/lib/Empty/EmptyCard.tsx +67 -0
- package/template/src/lib/Error/GlobalErrorBoundary.tsx +111 -0
- package/template/src/lib/animate/AniImage.tsx +32 -0
- package/template/src/lib/backHeader/BackButton.tsx +62 -0
- package/template/src/lib/backHeader/BackWithCoponent.tsx +112 -0
- package/template/src/lib/backHeader/BackWithHeader.tsx +46 -0
- package/template/src/lib/backHeader/BackWithTitle.tsx +53 -0
- package/template/src/lib/buttons/IButton.tsx +69 -0
- package/template/src/lib/buttons/IwtButton.tsx +199 -0
- package/template/src/lib/buttons/Or.tsx +27 -0
- package/template/src/lib/buttons/SimpleButton.tsx +45 -0
- package/template/src/lib/buttons/TButton.tsx +70 -0
- package/template/src/lib/cards/Card.tsx +175 -0
- package/template/src/lib/cards/OptionSelect.tsx +44 -0
- package/template/src/lib/cards/SearchCard.tsx +35 -0
- package/template/src/lib/editor/TextEditor.tsx +81 -0
- package/template/src/lib/expend/ExpendComponent.tsx +36 -0
- package/template/src/lib/imageViewer/ImageViwer.tsx +332 -0
- package/template/src/lib/imageZoomer/ImageZoomer.tsx +104 -0
- package/template/src/lib/inputs/CheckBox.tsx +86 -0
- package/template/src/lib/inputs/InputText.tsx +232 -0
- package/template/src/lib/loader/GLoading.tsx +26 -0
- package/template/src/lib/loading/MLoading.tsx +14 -0
- package/template/src/lib/loading/SLoading.tsx +14 -0
- package/template/src/lib/modals/ActionModal.tsx +97 -0
- package/template/src/lib/modals/BottomModal.tsx +224 -0
- package/template/src/lib/modals/ConfrimationModal.tsx +116 -0
- package/template/src/lib/modals/DateModal.tsx +152 -0
- package/template/src/lib/modals/NormalModal.tsx +73 -0
- package/template/src/lib/modals/SideModal.tsx +57 -0
- package/template/src/lib/modals/Toaster.tsx +256 -0
- package/template/src/lib/payment/PaymentCardForD.tsx +47 -0
- package/template/src/lib/progressBar/ProgressBar.tsx +64 -0
- package/template/src/lib/tailwind.js +9 -0
- package/template/src/lib/ui/Avatar.tsx +55 -0
- package/template/src/redux/api-config/baseApi.ts +0 -22
- package/template/src/redux/interface/interface.ts +11 -193
- package/template/src/redux/store.ts +2 -2
- package/template/src/app/home/_layout.tsx +0 -29
- package/template/src/app/home/drawer/_layout.tsx +0 -27
- package/template/src/app/home/tabs/_layout.tsx +0 -75
- package/template/src/app/home/tabs/index.tsx +0 -11
- package/template/src/app/modals/payment_modal.tsx +0 -105
- package/template/src/hooks/useCheckLocation.ts +0 -36
- package/template/src/hooks/useDocPicker.ts +0 -83
- package/template/src/hooks/useSuggestionLocation.ts +0 -36
- package/template/src/hooks/useUploadProgress.ts +0 -127
|
@@ -0,0 +1,332 @@
|
|
|
1
|
+
import {
|
|
2
|
+
Alert,
|
|
3
|
+
Dimensions,
|
|
4
|
+
Image,
|
|
5
|
+
Modal,
|
|
6
|
+
PermissionsAndroid,
|
|
7
|
+
Platform,
|
|
8
|
+
Pressable,
|
|
9
|
+
StyleSheet,
|
|
10
|
+
Text,
|
|
11
|
+
TouchableOpacity,
|
|
12
|
+
View,
|
|
13
|
+
} from 'react-native';
|
|
14
|
+
import Animated, {
|
|
15
|
+
interpolate,
|
|
16
|
+
runOnJS,
|
|
17
|
+
useAnimatedStyle,
|
|
18
|
+
useSharedValue,
|
|
19
|
+
withSpring,
|
|
20
|
+
withTiming,
|
|
21
|
+
} from 'react-native-reanimated';
|
|
22
|
+
import {
|
|
23
|
+
Gesture,
|
|
24
|
+
GestureDetector,
|
|
25
|
+
GestureHandlerRootView,
|
|
26
|
+
} from 'react-native-gesture-handler';
|
|
27
|
+
import React, {useRef, useState} from 'react';
|
|
28
|
+
|
|
29
|
+
import RNFS from 'react-native-fs';
|
|
30
|
+
|
|
31
|
+
const {width: SCREEN_WIDTH, height: SCREEN_HEIGHT} = Dimensions.get('window');
|
|
32
|
+
interface ImageViewProps {
|
|
33
|
+
source: {uri: string}; // Image source (required)
|
|
34
|
+
style?: any; // Custom styles for the thumbnail
|
|
35
|
+
modalBackgroundStyle?: any; // Style for the modal background
|
|
36
|
+
headerStyle?: any; // Style for the header container
|
|
37
|
+
closeButtonStyle?: any; // Style for the close button text
|
|
38
|
+
onClose?: () => void; // Optional callback for the close button
|
|
39
|
+
onModalOpen?: () => void; // Optional callback for when the modal opens
|
|
40
|
+
animationConfig?: {
|
|
41
|
+
openDuration?: number;
|
|
42
|
+
closeDuration?: number;
|
|
43
|
+
}; // Customize animation durations
|
|
44
|
+
gesturesEnabled?: boolean; // Toggle gestures
|
|
45
|
+
aspectRatio?: number; // Allow customizable aspect ratio
|
|
46
|
+
doubleTapScale?: number; // Customize double-tap zoom scale
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
const ImageView: React.FC<ImageViewProps> = ({
|
|
50
|
+
source,
|
|
51
|
+
style,
|
|
52
|
+
modalBackgroundStyle,
|
|
53
|
+
headerStyle,
|
|
54
|
+
closeButtonStyle,
|
|
55
|
+
onClose,
|
|
56
|
+
onModalOpen,
|
|
57
|
+
animationConfig = {openDuration: 500, closeDuration: 300},
|
|
58
|
+
gesturesEnabled = true,
|
|
59
|
+
aspectRatio = 1,
|
|
60
|
+
doubleTapScale = 2,
|
|
61
|
+
}) => {
|
|
62
|
+
const [isModalVisible, setIsModalVisible] = useState(false);
|
|
63
|
+
const [origin, setOrigin] = useState({x: 0, y: 0, width: 0, height: 0});
|
|
64
|
+
|
|
65
|
+
const scale = useSharedValue(1);
|
|
66
|
+
const translateX = useSharedValue(0);
|
|
67
|
+
const translateY = useSharedValue(0);
|
|
68
|
+
const animatedOpacity = useSharedValue(1);
|
|
69
|
+
const animatedPosition = useSharedValue(0);
|
|
70
|
+
|
|
71
|
+
const ref = useRef<Image>(null);
|
|
72
|
+
|
|
73
|
+
const cumulativeTranslateX = useSharedValue(0);
|
|
74
|
+
const cumulativeTranslateY = useSharedValue(0);
|
|
75
|
+
|
|
76
|
+
const initialImageWidth = SCREEN_WIDTH;
|
|
77
|
+
const initialImageHeight = SCREEN_WIDTH / aspectRatio;
|
|
78
|
+
|
|
79
|
+
const calculateBounds = (scaleFactor: number) => {
|
|
80
|
+
'worklet';
|
|
81
|
+
const scaledWidth = initialImageWidth * scaleFactor;
|
|
82
|
+
const scaledHeight = initialImageHeight * scaleFactor;
|
|
83
|
+
|
|
84
|
+
const maxTranslateX = Math.max((scaledWidth - SCREEN_WIDTH) / 2, 0);
|
|
85
|
+
const maxTranslateY = Math.max((scaledHeight - SCREEN_HEIGHT) / 2, 0);
|
|
86
|
+
|
|
87
|
+
return {maxTranslateX, maxTranslateY};
|
|
88
|
+
};
|
|
89
|
+
|
|
90
|
+
const clamp = (value: number, min: number, max: number) => {
|
|
91
|
+
'worklet';
|
|
92
|
+
return Math.min(Math.max(value, min), max);
|
|
93
|
+
};
|
|
94
|
+
|
|
95
|
+
const doubleTapGesture = Gesture.Tap()
|
|
96
|
+
.numberOfTaps(2)
|
|
97
|
+
.onEnd(() => {
|
|
98
|
+
if (scale.value > 1) {
|
|
99
|
+
scale.value = withSpring(1);
|
|
100
|
+
translateX.value = withSpring(0);
|
|
101
|
+
translateY.value = withSpring(0);
|
|
102
|
+
cumulativeTranslateX.value = 0;
|
|
103
|
+
cumulativeTranslateY.value = 0;
|
|
104
|
+
} else {
|
|
105
|
+
scale.value = withSpring(doubleTapScale);
|
|
106
|
+
}
|
|
107
|
+
});
|
|
108
|
+
|
|
109
|
+
const pinchGesture = Gesture.Pinch()
|
|
110
|
+
.onUpdate(e => {
|
|
111
|
+
scale.value = Math.max(1, e.scale);
|
|
112
|
+
})
|
|
113
|
+
.onEnd(() => {
|
|
114
|
+
if (scale.value < 1) {
|
|
115
|
+
scale.value = withSpring(1);
|
|
116
|
+
}
|
|
117
|
+
});
|
|
118
|
+
|
|
119
|
+
const panGesture = Gesture.Pan()
|
|
120
|
+
.onUpdate(e => {
|
|
121
|
+
if (scale.value > 1) {
|
|
122
|
+
const {maxTranslateX, maxTranslateY} = calculateBounds(scale.value);
|
|
123
|
+
|
|
124
|
+
translateX.value = clamp(
|
|
125
|
+
e.translationX + cumulativeTranslateX.value,
|
|
126
|
+
-maxTranslateX,
|
|
127
|
+
maxTranslateX,
|
|
128
|
+
);
|
|
129
|
+
translateY.value = clamp(
|
|
130
|
+
e.translationY + cumulativeTranslateY.value,
|
|
131
|
+
-maxTranslateY,
|
|
132
|
+
maxTranslateY,
|
|
133
|
+
);
|
|
134
|
+
}
|
|
135
|
+
})
|
|
136
|
+
.onEnd(() => {
|
|
137
|
+
if (scale.value > 1) {
|
|
138
|
+
cumulativeTranslateX.value = translateX.value;
|
|
139
|
+
cumulativeTranslateY.value = translateY.value;
|
|
140
|
+
}
|
|
141
|
+
});
|
|
142
|
+
|
|
143
|
+
const combinedGesture = gesturesEnabled
|
|
144
|
+
? Gesture.Simultaneous(
|
|
145
|
+
Gesture.Simultaneous(pinchGesture, panGesture),
|
|
146
|
+
doubleTapGesture,
|
|
147
|
+
)
|
|
148
|
+
: undefined;
|
|
149
|
+
|
|
150
|
+
const animatedImageStyle = useAnimatedStyle(() => {
|
|
151
|
+
'worklet';
|
|
152
|
+
return {
|
|
153
|
+
opacity: animatedOpacity.value,
|
|
154
|
+
width: interpolate(
|
|
155
|
+
animatedPosition.value,
|
|
156
|
+
[0, 1],
|
|
157
|
+
[origin.width, initialImageWidth],
|
|
158
|
+
),
|
|
159
|
+
height: interpolate(
|
|
160
|
+
animatedPosition.value,
|
|
161
|
+
[0, 1],
|
|
162
|
+
[origin.height, initialImageHeight],
|
|
163
|
+
),
|
|
164
|
+
transform: [
|
|
165
|
+
{
|
|
166
|
+
translateX: interpolate(
|
|
167
|
+
animatedPosition.value,
|
|
168
|
+
[0, 1],
|
|
169
|
+
[origin.x + origin.width / 2 - SCREEN_WIDTH / 2, translateX.value],
|
|
170
|
+
),
|
|
171
|
+
},
|
|
172
|
+
{
|
|
173
|
+
translateY: interpolate(
|
|
174
|
+
animatedPosition.value,
|
|
175
|
+
[0, 1],
|
|
176
|
+
[
|
|
177
|
+
origin.y + origin.height / 2 - SCREEN_HEIGHT / 2,
|
|
178
|
+
translateY.value,
|
|
179
|
+
],
|
|
180
|
+
),
|
|
181
|
+
},
|
|
182
|
+
{scale: scale.value},
|
|
183
|
+
],
|
|
184
|
+
};
|
|
185
|
+
});
|
|
186
|
+
|
|
187
|
+
const animatedBackgroundStyle = useAnimatedStyle(() => {
|
|
188
|
+
'worklet';
|
|
189
|
+
return {
|
|
190
|
+
opacity: animatedOpacity.value,
|
|
191
|
+
backgroundColor: 'black',
|
|
192
|
+
...modalBackgroundStyle,
|
|
193
|
+
};
|
|
194
|
+
});
|
|
195
|
+
|
|
196
|
+
const handleOpenModal = () => {
|
|
197
|
+
ref.current?.measure((x, y, width, height, pageX, pageY) => {
|
|
198
|
+
setOrigin({x: pageX, y: pageY, width, height});
|
|
199
|
+
animatedPosition.value = 0;
|
|
200
|
+
animatedOpacity.value = 0;
|
|
201
|
+
|
|
202
|
+
setIsModalVisible(true);
|
|
203
|
+
onModalOpen?.();
|
|
204
|
+
|
|
205
|
+
animatedPosition.value = withTiming(1, {
|
|
206
|
+
duration: animationConfig.openDuration,
|
|
207
|
+
});
|
|
208
|
+
animatedOpacity.value = withTiming(1, {
|
|
209
|
+
duration: animationConfig.openDuration,
|
|
210
|
+
});
|
|
211
|
+
});
|
|
212
|
+
};
|
|
213
|
+
|
|
214
|
+
const handleCloseModal = () => {
|
|
215
|
+
'worklet';
|
|
216
|
+
animatedOpacity.value = withTiming(
|
|
217
|
+
0,
|
|
218
|
+
{duration: animationConfig.closeDuration},
|
|
219
|
+
() => {
|
|
220
|
+
// Use runOnJS to call non-worklet functions
|
|
221
|
+
runOnJS(setIsModalVisible)(false);
|
|
222
|
+
if (onClose) {
|
|
223
|
+
runOnJS(onClose)();
|
|
224
|
+
}
|
|
225
|
+
},
|
|
226
|
+
);
|
|
227
|
+
};
|
|
228
|
+
|
|
229
|
+
const handleDownload = async () => {
|
|
230
|
+
if (Platform.OS === 'android') {
|
|
231
|
+
const granted = await PermissionsAndroid.request(
|
|
232
|
+
PermissionsAndroid.PERMISSIONS.WRITE_EXTERNAL_STORAGE,
|
|
233
|
+
{
|
|
234
|
+
title: 'Storage Permission Required',
|
|
235
|
+
message: 'The app needs access to your storage to download images.',
|
|
236
|
+
},
|
|
237
|
+
);
|
|
238
|
+
if (granted !== PermissionsAndroid.RESULTS.GRANTED) {
|
|
239
|
+
Alert.alert(
|
|
240
|
+
'Permission Denied',
|
|
241
|
+
'Cannot download image without storage permission.',
|
|
242
|
+
);
|
|
243
|
+
return;
|
|
244
|
+
}
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
const fileName = source.uri.split('/').pop();
|
|
248
|
+
const path = `${RNFS.DownloadDirectoryPath}/${fileName}`;
|
|
249
|
+
try {
|
|
250
|
+
await RNFS.downloadFile({
|
|
251
|
+
fromUrl: source.uri,
|
|
252
|
+
toFile: path,
|
|
253
|
+
}).promise;
|
|
254
|
+
Alert.alert('Download Successful', 'Image downloaded successfully.');
|
|
255
|
+
} catch (error) {
|
|
256
|
+
Alert.alert(
|
|
257
|
+
'Download Failed',
|
|
258
|
+
'An error occurred while downloading the image.',
|
|
259
|
+
);
|
|
260
|
+
}
|
|
261
|
+
};
|
|
262
|
+
|
|
263
|
+
return (
|
|
264
|
+
<>
|
|
265
|
+
<Pressable onPress={handleOpenModal}>
|
|
266
|
+
<Image ref={ref} source={source} style={[style]} />
|
|
267
|
+
</Pressable>
|
|
268
|
+
|
|
269
|
+
<Modal
|
|
270
|
+
statusBarTranslucent
|
|
271
|
+
animationType="fade"
|
|
272
|
+
transparent
|
|
273
|
+
visible={isModalVisible}
|
|
274
|
+
onRequestClose={handleCloseModal}>
|
|
275
|
+
<GestureHandlerRootView style={styles.modalContainer}>
|
|
276
|
+
<Animated.View
|
|
277
|
+
style={[styles.modalBackground, animatedBackgroundStyle]}>
|
|
278
|
+
<View style={[styles.header, headerStyle]}>
|
|
279
|
+
<TouchableOpacity onPress={handleCloseModal}>
|
|
280
|
+
<Text style={[styles.closeButton, closeButtonStyle]}>
|
|
281
|
+
Close
|
|
282
|
+
</Text>
|
|
283
|
+
</TouchableOpacity>
|
|
284
|
+
<TouchableOpacity onPress={() => handleDownload()}>
|
|
285
|
+
<Text style={styles.downloadButton}>Download</Text>
|
|
286
|
+
</TouchableOpacity>
|
|
287
|
+
</View>
|
|
288
|
+
<GestureDetector gesture={combinedGesture}>
|
|
289
|
+
<Animated.Image
|
|
290
|
+
source={source}
|
|
291
|
+
style={[animatedImageStyle]}
|
|
292
|
+
resizeMode="cover"
|
|
293
|
+
/>
|
|
294
|
+
</GestureDetector>
|
|
295
|
+
</Animated.View>
|
|
296
|
+
</GestureHandlerRootView>
|
|
297
|
+
</Modal>
|
|
298
|
+
</>
|
|
299
|
+
);
|
|
300
|
+
};
|
|
301
|
+
|
|
302
|
+
export default ImageView;
|
|
303
|
+
|
|
304
|
+
const styles = StyleSheet.create({
|
|
305
|
+
modalContainer: {
|
|
306
|
+
flex: 1,
|
|
307
|
+
},
|
|
308
|
+
modalBackground: {
|
|
309
|
+
flex: 1,
|
|
310
|
+
justifyContent: 'center',
|
|
311
|
+
alignItems: 'center',
|
|
312
|
+
},
|
|
313
|
+
header: {
|
|
314
|
+
position: 'absolute',
|
|
315
|
+
top: 50,
|
|
316
|
+
right: 20,
|
|
317
|
+
zIndex: 1,
|
|
318
|
+
flexDirection: 'row-reverse',
|
|
319
|
+
gap: 15,
|
|
320
|
+
},
|
|
321
|
+
closeButton: {
|
|
322
|
+
color: 'white',
|
|
323
|
+
fontSize: 18,
|
|
324
|
+
fontWeight: 'bold',
|
|
325
|
+
},
|
|
326
|
+
downloadButton: {
|
|
327
|
+
color: 'white',
|
|
328
|
+
fontSize: 18,
|
|
329
|
+
fontWeight: 'bold',
|
|
330
|
+
marginLeft: 15,
|
|
331
|
+
},
|
|
332
|
+
});
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
import Animated, {
|
|
2
|
+
clamp,
|
|
3
|
+
useAnimatedStyle,
|
|
4
|
+
useSharedValue,
|
|
5
|
+
withTiming,
|
|
6
|
+
} from "react-native-reanimated";
|
|
7
|
+
import { Gesture, GestureDetector } from "react-native-gesture-handler";
|
|
8
|
+
|
|
9
|
+
import { Dimensions } from "react-native";
|
|
10
|
+
import React from "react";
|
|
11
|
+
|
|
12
|
+
const { width: SCREEN_WIDTH, height: SCREEN_HEIGHT } = Dimensions.get("window");
|
|
13
|
+
|
|
14
|
+
interface ZoomableImageProps {
|
|
15
|
+
uri: string;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
const ZoomableImage: React.FC<ZoomableImageProps> = ({ uri }) => {
|
|
19
|
+
const scale = useSharedValue(1);
|
|
20
|
+
const savedScale = useSharedValue(1);
|
|
21
|
+
|
|
22
|
+
const translateX = useSharedValue(0);
|
|
23
|
+
const translateY = useSharedValue(0);
|
|
24
|
+
const savedTranslateX = useSharedValue(0);
|
|
25
|
+
const savedTranslateY = useSharedValue(0);
|
|
26
|
+
|
|
27
|
+
const imageWidth = SCREEN_WIDTH;
|
|
28
|
+
const imageHeight = SCREEN_HEIGHT * 0.8;
|
|
29
|
+
|
|
30
|
+
const pinchGesture = Gesture.Pinch()
|
|
31
|
+
.onUpdate((event) => {
|
|
32
|
+
scale.value = clamp(savedScale.value * event.scale, 1, 5);
|
|
33
|
+
})
|
|
34
|
+
.onEnd(() => {
|
|
35
|
+
savedScale.value = scale.value;
|
|
36
|
+
});
|
|
37
|
+
|
|
38
|
+
const panGesture = Gesture.Pan()
|
|
39
|
+
.onUpdate((event) => {
|
|
40
|
+
if (scale.value > 1) {
|
|
41
|
+
// Limit pan to image bounds
|
|
42
|
+
const scaledWidth = imageWidth * scale.value;
|
|
43
|
+
const scaledHeight = imageHeight * scale.value;
|
|
44
|
+
|
|
45
|
+
const maxX = (scaledWidth - imageWidth) / 2;
|
|
46
|
+
const maxY = (scaledHeight - imageHeight) / 6;
|
|
47
|
+
|
|
48
|
+
translateX.value = clamp(
|
|
49
|
+
savedTranslateX.value + event.translationX,
|
|
50
|
+
-maxX,
|
|
51
|
+
maxX
|
|
52
|
+
);
|
|
53
|
+
translateY.value = clamp(
|
|
54
|
+
savedTranslateY.value + event.translationY,
|
|
55
|
+
-maxY,
|
|
56
|
+
maxY
|
|
57
|
+
);
|
|
58
|
+
}
|
|
59
|
+
})
|
|
60
|
+
.onEnd(() => {
|
|
61
|
+
savedTranslateX.value = translateX.value;
|
|
62
|
+
savedTranslateY.value = translateY.value;
|
|
63
|
+
});
|
|
64
|
+
|
|
65
|
+
const doubleTapGesture = Gesture.Tap()
|
|
66
|
+
.numberOfTaps(2)
|
|
67
|
+
.onEnd(() => {
|
|
68
|
+
const newScale = scale.value > 1 ? 1 : 2;
|
|
69
|
+
scale.value = withTiming(newScale);
|
|
70
|
+
savedScale.value = newScale;
|
|
71
|
+
|
|
72
|
+
translateX.value = withTiming(0);
|
|
73
|
+
translateY.value = withTiming(0);
|
|
74
|
+
savedTranslateX.value = 0;
|
|
75
|
+
savedTranslateY.value = 0;
|
|
76
|
+
});
|
|
77
|
+
|
|
78
|
+
const composedGesture = Gesture.Simultaneous(
|
|
79
|
+
doubleTapGesture,
|
|
80
|
+
Gesture.Simultaneous(pinchGesture, panGesture)
|
|
81
|
+
);
|
|
82
|
+
|
|
83
|
+
const animatedStyle = useAnimatedStyle(() => {
|
|
84
|
+
return {
|
|
85
|
+
transform: [
|
|
86
|
+
{ translateX: translateX.value },
|
|
87
|
+
{ translateY: translateY.value },
|
|
88
|
+
{ scale: scale.value },
|
|
89
|
+
],
|
|
90
|
+
};
|
|
91
|
+
});
|
|
92
|
+
|
|
93
|
+
return (
|
|
94
|
+
<GestureDetector gesture={composedGesture}>
|
|
95
|
+
<Animated.Image
|
|
96
|
+
source={{ uri }}
|
|
97
|
+
style={[{ width: imageWidth, height: imageHeight }, animatedStyle]}
|
|
98
|
+
resizeMode="contain"
|
|
99
|
+
/>
|
|
100
|
+
</GestureDetector>
|
|
101
|
+
);
|
|
102
|
+
};
|
|
103
|
+
|
|
104
|
+
export default ZoomableImage;
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
import { Text, TouchableOpacity, View } from "react-native";
|
|
2
|
+
|
|
3
|
+
import React from "react";
|
|
4
|
+
import { SvgXml } from "react-native-svg";
|
|
5
|
+
import tw from "../tailwind";
|
|
6
|
+
|
|
7
|
+
interface CheckBoxProps {
|
|
8
|
+
onPress?: (version: boolean) => void;
|
|
9
|
+
color?: string;
|
|
10
|
+
iconColor?: string;
|
|
11
|
+
size?: number;
|
|
12
|
+
style?: any;
|
|
13
|
+
icon?: string;
|
|
14
|
+
title?: string;
|
|
15
|
+
titleStyle?: any;
|
|
16
|
+
containerStyle?: any;
|
|
17
|
+
checked?: boolean;
|
|
18
|
+
titleComponent?: React.ReactNode;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
const defaultIcon = `<svg xmlns="http://www.w3.org/2000/svg" version="1.1" xmlns:xlink="http://www.w3.org/1999/xlink" width="12" height="12" x="0" y="0" viewBox="0 0 520 520" style="enable-background:new 0 0 512 512" xml:space="preserve" class=""><g><path d="M239.987 460.841a10 10 0 0 1-7.343-3.213L34.657 243.463A10 10 0 0 1 42 226.675h95.3a10.006 10.006 0 0 1 7.548 3.439l66.168 76.124c7.151-15.286 20.994-40.738 45.286-71.752 35.912-45.85 102.71-113.281 216.994-174.153a10 10 0 0 1 10.85 16.712c-.436.341-44.5 35.041-95.212 98.6-46.672 58.49-108.714 154.13-139.243 277.6a10 10 0 0 1-9.707 7.6z" data-name="6-Check" fill="white" opacity="1" class=""></path></g></svg>`;
|
|
22
|
+
|
|
23
|
+
const CheckBox = ({
|
|
24
|
+
color,
|
|
25
|
+
iconColor,
|
|
26
|
+
size,
|
|
27
|
+
style,
|
|
28
|
+
icon,
|
|
29
|
+
checked = false,
|
|
30
|
+
onPress,
|
|
31
|
+
title,
|
|
32
|
+
titleStyle,
|
|
33
|
+
containerStyle,
|
|
34
|
+
titleComponent,
|
|
35
|
+
}: CheckBoxProps) => {
|
|
36
|
+
// This component renders a simple checkbox style
|
|
37
|
+
|
|
38
|
+
return (
|
|
39
|
+
<TouchableOpacity
|
|
40
|
+
style={[tw` flex-row items-center gap-2 `, containerStyle]}
|
|
41
|
+
onPress={() => {
|
|
42
|
+
onPress && onPress(!checked);
|
|
43
|
+
}}
|
|
44
|
+
>
|
|
45
|
+
<View
|
|
46
|
+
style={[
|
|
47
|
+
tw`w-5 h-5 border border-white rounded-md`,
|
|
48
|
+
{
|
|
49
|
+
backgroundColor: checked
|
|
50
|
+
? color || tw.color("primary")
|
|
51
|
+
: "transparent",
|
|
52
|
+
justifyContent: "center",
|
|
53
|
+
alignItems: "center",
|
|
54
|
+
},
|
|
55
|
+
style,
|
|
56
|
+
]}
|
|
57
|
+
>
|
|
58
|
+
{checked && icon ? (
|
|
59
|
+
<SvgXml xml={icon} width={size || 12} height={size || 12} />
|
|
60
|
+
) : (
|
|
61
|
+
checked && (
|
|
62
|
+
<SvgXml xml={defaultIcon} width={size || 12} height={size || 12} />
|
|
63
|
+
)
|
|
64
|
+
)}
|
|
65
|
+
</View>
|
|
66
|
+
|
|
67
|
+
{
|
|
68
|
+
// Render title component if provided
|
|
69
|
+
titleComponent ? titleComponent : null
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
{title && !titleComponent ? (
|
|
73
|
+
<Text
|
|
74
|
+
style={[
|
|
75
|
+
tw`text-white text-sm leading-5 font-InterRegular `,
|
|
76
|
+
titleStyle,
|
|
77
|
+
]}
|
|
78
|
+
>
|
|
79
|
+
{title}
|
|
80
|
+
</Text>
|
|
81
|
+
) : null}
|
|
82
|
+
</TouchableOpacity>
|
|
83
|
+
);
|
|
84
|
+
};
|
|
85
|
+
|
|
86
|
+
export default CheckBox;
|