@holper/react-native-holper-storybook 0.7.1 → 0.8.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.
Files changed (92) hide show
  1. package/index.js +2 -3
  2. package/lib/components/Button/index.js +104 -0
  3. package/lib/components/Button/{style.ts → style.js} +7 -8
  4. package/lib/components/Card/index.js +49 -0
  5. package/lib/components/Card/{style.ts → style.js} +4 -5
  6. package/lib/components/ConfirmationModal/{index.tsx → index.js} +79 -25
  7. package/lib/components/ConfirmationModal/{style.tsx → style.js} +13 -14
  8. package/lib/components/Container/{index.tsx → index.js} +28 -7
  9. package/lib/components/Container/{style.ts → style.js} +5 -6
  10. package/lib/components/CustomChatView/{index.tsx → index.js} +30 -22
  11. package/lib/components/CustomChatView/{style.ts → style.js} +1 -1
  12. package/lib/components/DeckSwiper/index.js +118 -0
  13. package/lib/components/DeckSwiper/{style.ts → style.js} +12 -13
  14. package/lib/components/FlashMessage/index.js +83 -0
  15. package/lib/components/FloatingContainer/index.js +69 -0
  16. package/lib/components/FloatingContainer/{style.ts → style.js} +6 -7
  17. package/lib/components/Footer/index.js +61 -0
  18. package/lib/components/Footer/{style.ts → style.js} +3 -4
  19. package/lib/components/Header/index.js +45 -0
  20. package/lib/components/Header/{style.ts → style.js} +3 -4
  21. package/lib/components/ImagePicker/{index.tsx → index.js} +12 -3
  22. package/lib/components/ImageResponsive/index.js +39 -0
  23. package/lib/components/ImageResponsive/style.js +7 -0
  24. package/lib/components/ImageViewer/index.js +62 -0
  25. package/lib/components/ImageViewer/{style.ts → style.js} +3 -4
  26. package/lib/components/Input/{index.tsx → index.js} +33 -6
  27. package/lib/components/Input/{style.ts → style.js} +18 -7
  28. package/lib/components/InputPin/{index.tsx → index.js} +13 -6
  29. package/lib/components/InputPin/{style.ts → style.js} +6 -7
  30. package/lib/components/MenuItem/index.js +44 -0
  31. package/lib/components/MenuItem/{style.ts → style.js} +7 -9
  32. package/lib/components/NavigationTitle/{index.tsx → index.js} +30 -9
  33. package/lib/components/NavigationTitle/{style.ts → style.js} +11 -12
  34. package/lib/components/Notification/index.js +80 -0
  35. package/lib/components/Notification/{style.ts → style.js} +11 -13
  36. package/lib/components/PreventDoubleClick/index.js +21 -0
  37. package/lib/components/Select/index.js +89 -0
  38. package/lib/components/Select/style.js +81 -0
  39. package/lib/components/SwipeablePanel/{index.tsx → index.js} +85 -58
  40. package/lib/components/SwipeablePanel/{style.ts → style.js} +14 -15
  41. package/lib/components/Switch/index.js +57 -0
  42. package/lib/components/TakePicture/{confirmPictureModal.tsx → confirmPictureModal.js} +33 -9
  43. package/lib/components/TakePicture/index.js +198 -0
  44. package/lib/components/TakePicture/{style.ts → style.js} +4 -4
  45. package/lib/components/Text/index.js +75 -0
  46. package/lib/components/Text/{style.ts → style.js} +2 -4
  47. package/lib/components/Textarea/{index.tsx → index.js} +24 -5
  48. package/lib/components/Textarea/{style.ts → style.js} +4 -5
  49. package/lib/components/TimeOutButton/index.js +104 -0
  50. package/lib/components/TimeOutButton/{style.ts → style.js} +3 -4
  51. package/lib/components/UploadDocument/index.js +222 -0
  52. package/lib/components/UploadDocument/{style.ts → style.js} +15 -16
  53. package/lib/components/VirtualKeyboard/index.js +86 -0
  54. package/lib/components/VirtualKeyboard/{style.ts → style.js} +8 -9
  55. package/lib/components/index.js +28 -0
  56. package/lib/configs/constants.js +276 -0
  57. package/lib/configs/loadFonts.js +11 -0
  58. package/lib/hooks/index.js +1 -0
  59. package/lib/hooks/{useDebounce.tsx → useDebounce.js} +4 -6
  60. package/lib/index.js +2 -3
  61. package/package.json +58 -72
  62. package/{README.md → readme.md} +20 -19
  63. package/LICENSE +0 -21
  64. package/lib/components/Button/index.tsx +0 -66
  65. package/lib/components/Card/index.tsx +0 -33
  66. package/lib/components/DeckSwiper/index.tsx +0 -90
  67. package/lib/components/DonutCountdown/index.tsx +0 -86
  68. package/lib/components/DonutCountdown/style.ts +0 -8
  69. package/lib/components/FloatingContainer/index.tsx +0 -35
  70. package/lib/components/Footer/index.tsx +0 -35
  71. package/lib/components/Header/index.tsx +0 -21
  72. package/lib/components/ImageResponsive/index.tsx +0 -24
  73. package/lib/components/ImageResponsive/style.ts +0 -9
  74. package/lib/components/ImageViewer/index.tsx +0 -36
  75. package/lib/components/MenuItem/index.tsx +0 -25
  76. package/lib/components/Notification/index.tsx +0 -44
  77. package/lib/components/PreventDoubleClick/index.tsx +0 -28
  78. package/lib/components/Select/index.tsx +0 -51
  79. package/lib/components/Select/style.ts +0 -64
  80. package/lib/components/Switch/index.tsx +0 -30
  81. package/lib/components/TakePicture/index.tsx +0 -148
  82. package/lib/components/Text/index.tsx +0 -33
  83. package/lib/components/TimeOutButton/index.tsx +0 -67
  84. package/lib/components/Toast/index.tsx +0 -34
  85. package/lib/components/Toast/style.ts +0 -12
  86. package/lib/components/UploadDocument/index.tsx +0 -179
  87. package/lib/components/VirtualKeyboard/index.tsx +0 -75
  88. package/lib/components/index.ts +0 -29
  89. package/lib/configs/constants.ts +0 -273
  90. package/lib/configs/types.ts +0 -326
  91. package/lib/hooks/index.ts +0 -2
  92. package/lib/hooks/useLoadFonts.tsx +0 -13
@@ -0,0 +1,81 @@
1
+ import { Dimensions } from 'react-native';
2
+ import { Colors, borderRadius } from '../../configs/constants';
3
+
4
+ const { width } = Dimensions.get('window');
5
+
6
+ export default {
7
+ container: {
8
+ width: width - 60,
9
+ position: 'relative',
10
+ },
11
+ fit: {
12
+ width: '100%',
13
+ position: 'relative',
14
+ },
15
+ selectIcon: {
16
+ marginTop: 20,
17
+ marginRight: 15,
18
+ },
19
+ disabled: {
20
+ opacity: 0.5,
21
+ },
22
+ };
23
+
24
+ const fontStyle = {
25
+ color: Colors.darkblue,
26
+ fontFamily: 'poppins_regular',
27
+ };
28
+
29
+ const basicStyle = {
30
+ height: 50,
31
+ width: '100%',
32
+ borderRadius,
33
+ borderWidth: 1,
34
+ paddingHorizontal: 10,
35
+ backgroundColor: Colors.white,
36
+ marginVertical: 6,
37
+ ...fontStyle,
38
+ };
39
+
40
+ export const includesSelect = {
41
+ default: {
42
+ ...basicStyle,
43
+ borderColor: Colors.midblue,
44
+ },
45
+ completed: {
46
+ ...basicStyle,
47
+ borderColor: Colors.green,
48
+ },
49
+ error: {
50
+ ...basicStyle,
51
+ borderColor: Colors.red,
52
+ color: Colors.red,
53
+ },
54
+ };
55
+
56
+ export const placeholderStyle = {
57
+ fontSize: 16,
58
+ ...fontStyle,
59
+ color: Colors.midblue,
60
+ };
61
+
62
+ export const listItemLabelStyle = {
63
+ ...fontStyle,
64
+ fontSize: 18,
65
+ paddingHorizontal: 20,
66
+ };
67
+
68
+ export const listItemContainerStyle = {
69
+ height: 55,
70
+ borderBottomWidth: 1,
71
+ borderBottomColor: Colors.gray,
72
+ };
73
+
74
+ export const selectedItemLabelStyle = {
75
+ fontFamily: 'poppins_semiBold',
76
+ };
77
+
78
+ export const textStyle = {
79
+ ...fontStyle,
80
+ fontSize: 16,
81
+ };
@@ -1,23 +1,21 @@
1
- import { Ionicons } from '@expo/vector-icons';
2
- import Constants from 'expo-constants';
3
- import { useEffect, useRef, useState } from 'react';
1
+ import React, { useState, useRef, useEffect } from 'react';
2
+ import PropTypes from 'prop-types';
4
3
  import {
4
+ View,
5
+ ScrollView,
5
6
  Animated,
6
7
  Dimensions,
7
8
  PanResponder,
9
+ TouchableWithoutFeedback,
8
10
  Platform,
9
- ScrollView,
10
11
  TouchableOpacity,
11
- TouchableWithoutFeedback,
12
- View,
13
12
  } from 'react-native';
14
-
13
+ import { getStatusBarHeight } from 'react-native-status-bar-height';
14
+ import Ionicons from 'react-native-vector-icons/Ionicons';
15
+ import { CountdownCircleTimer } from 'react-native-countdown-circle-timer';
15
16
  import { Colors } from '../../configs/constants';
16
- import DonutCountdown from '../DonutCountdown';
17
17
  import style from './style';
18
18
 
19
- import type { SwipeablePanelProps } from '../../configs/types';
20
-
21
19
  const { height: screenHeight } = Dimensions.get('window');
22
20
 
23
21
  const STATUS = {
@@ -27,7 +25,7 @@ const STATUS = {
27
25
 
28
26
  const calcMarginTop = () => {
29
27
  if (Platform.OS === 'ios') {
30
- return Constants.statusBarHeight === 44 ? 10 : -50;
28
+ return getStatusBarHeight(true) === 44 ? 10 : -50;
31
29
  } else {
32
30
  return -50;
33
31
  }
@@ -43,43 +41,16 @@ const SwipeablePanel = ({
43
41
  onClose,
44
42
  avoidScroll,
45
43
  children,
46
- }: SwipeablePanelProps) => {
44
+ }) => {
47
45
  const LARGE_PANEL_HEIGHT = screenHeight - offset - calcMarginTop(); // This is the margin top
48
46
  const MEDIUM_PANEL_HEIGHT = 400; // Fixed height
49
47
 
50
48
  const [closeButtonClicked, setCloseButtonClicked] = useState(false);
51
49
  const [status, setStatus] = useState(STATUS.LARGE);
52
50
  const [animatedValueY, setAnimatedValueY] = useState(LARGE_PANEL_HEIGHT);
53
- const pan = useRef(new Animated.ValueXY({ x: 0, y: LARGE_PANEL_HEIGHT })).current;
54
-
55
- const _animateTo = (newStatus = 0, firstAnimate?: boolean) => {
56
- if (!firstAnimate && lockPanel) {
57
- return;
58
- }
59
-
60
- if (status === newStatus) {
61
- return;
62
- }
63
-
64
- let newY = 0;
65
-
66
- if (newStatus === STATUS.MEDIUM) {
67
- newY = MEDIUM_PANEL_HEIGHT;
68
- } else if (newStatus === STATUS.LARGE) {
69
- newY = LARGE_PANEL_HEIGHT;
70
- }
71
-
72
- setStatus(newStatus);
73
-
74
- Animated.spring(pan, {
75
- toValue: { x: 0, y: newY },
76
- tension: 80,
77
- friction: 25,
78
- useNativeDriver: true,
79
- restDisplacementThreshold: 10,
80
- restSpeedThreshold: 10,
81
- }).start();
82
- };
51
+ const pan = useRef(
52
+ new Animated.ValueXY({ x: 0, y: LARGE_PANEL_HEIGHT })
53
+ ).current;
83
54
 
84
55
  useEffect(() => {
85
56
  pan.y.addListener(({ value }) => setAnimatedValueY(value));
@@ -87,7 +58,6 @@ const SwipeablePanel = ({
87
58
  if (autoMaximize) {
88
59
  setTimeout(() => _animateTo(STATUS.MEDIUM), 2000);
89
60
  }
90
- // eslint-disable-next-line react-hooks/exhaustive-deps
91
61
  }, [offset, autoMaximize]);
92
62
 
93
63
  const panResponder = useRef(
@@ -106,8 +76,10 @@ const SwipeablePanel = ({
106
76
  pan.setValue({ x: 0, y: 0 });
107
77
  },
108
78
  onPanResponderMove: (evt, gestureState) => {
109
- // @ts-expect-error omit ._value and ._offset
110
- if (status === STATUS.MEDIUM && Math.abs(pan.y._value) <= pan.y._offset) {
79
+ if (
80
+ status === STATUS.MEDIUM &&
81
+ Math.abs(pan.y._value) <= pan.y._offset
82
+ ) {
111
83
  pan.setValue({
112
84
  x: 0,
113
85
  y: gestureState.dy,
@@ -128,6 +100,35 @@ const SwipeablePanel = ({
128
100
  })
129
101
  ).current;
130
102
 
103
+ const _animateTo = (newStatus = 0, firstAnimate) => {
104
+ if (!firstAnimate && lockPanel) {
105
+ return;
106
+ }
107
+
108
+ if (status === newStatus) {
109
+ return;
110
+ }
111
+
112
+ let newY = 0;
113
+
114
+ if (newStatus === STATUS.MEDIUM) {
115
+ newY = MEDIUM_PANEL_HEIGHT;
116
+ } else if (newStatus === STATUS.LARGE) {
117
+ newY = LARGE_PANEL_HEIGHT;
118
+ }
119
+
120
+ setStatus(newStatus);
121
+
122
+ Animated.spring(pan, {
123
+ toValue: { x: 0, y: newY },
124
+ tension: 80,
125
+ friction: 25,
126
+ useNativeDriver: true,
127
+ restDisplacementThreshold: 10,
128
+ restSpeedThreshold: 10,
129
+ }).start();
130
+ };
131
+
131
132
  const renderContent = () => (
132
133
  <TouchableWithoutFeedback onPress={() => _animateTo(STATUS.MEDIUM)}>
133
134
  <View style={style.contentContainer}>{children}</View>
@@ -136,7 +137,9 @@ const SwipeablePanel = ({
136
137
 
137
138
  return (
138
139
  <Animated.View style={style.background}>
139
- <TouchableWithoutFeedback onPress={() => (maximized ? null : _animateTo(STATUS.LARGE))}>
140
+ <TouchableWithoutFeedback
141
+ onPress={() => (maximized ? null : _animateTo(STATUS.LARGE))}
142
+ >
140
143
  <View style={style.backgroundLayer} />
141
144
  </TouchableWithoutFeedback>
142
145
  <Animated.View
@@ -158,32 +161,34 @@ const SwipeablePanel = ({
158
161
  <TouchableOpacity
159
162
  onPress={() => {
160
163
  setCloseButtonClicked(true);
161
- if (onClose) {
162
- onClose();
163
- }
164
+ onClose();
164
165
  }}
165
166
  style={style.closeButton}
166
167
  >
167
- <Ionicons name="close-outline" size={20} color={Colors.darkblue} />
168
+ <Ionicons name='close-outline' size={20} color={Colors.darkblue} />
168
169
  </TouchableOpacity>
169
170
  )}
170
171
 
171
172
  {closeAfterSeconds > 0 && (
172
173
  <View style={style.autoClose}>
173
- <DonutCountdown
174
+ <CountdownCircleTimer
175
+ isPlaying={true}
174
176
  duration={closeAfterSeconds}
175
- color={Colors.green}
177
+ colors={Colors.green}
176
178
  onComplete={() => {
177
179
  if (!closeButtonClicked) {
178
- if (onClose) {
179
- onClose();
180
- }
180
+ onClose();
181
181
  }
182
182
  }}
183
- radius={16}
183
+ size={40}
184
184
  strokeWidth={4}
185
- textSize="small"
186
- />
185
+ >
186
+ {({ remainingTime, animatedColor }) => (
187
+ <Animated.Text style={{ color: animatedColor, fontSize: 14 }}>
188
+ {remainingTime}
189
+ </Animated.Text>
190
+ )}
191
+ </CountdownCircleTimer>
187
192
  </View>
188
193
  )}
189
194
 
@@ -205,4 +210,26 @@ const SwipeablePanel = ({
205
210
  );
206
211
  };
207
212
 
213
+ SwipeablePanel.defaultProps = {
214
+ offset: 0,
215
+ maximized: false,
216
+ lockPanel: false,
217
+ autoMaximize: false,
218
+ closeButton: false,
219
+ avoidScroll: false,
220
+ closeAfterSeconds: 0,
221
+ onClose: () => {},
222
+ };
223
+
224
+ SwipeablePanel.propTypes = {
225
+ offset: PropTypes.number,
226
+ maximized: PropTypes.bool,
227
+ lockPanel: PropTypes.bool,
228
+ autoMaximize: PropTypes.bool,
229
+ closeAfterSeconds: PropTypes.number,
230
+ closeButton: PropTypes.bool,
231
+ avoidScroll: PropTypes.bool,
232
+ onClose: PropTypes.func,
233
+ };
234
+
208
235
  export default SwipeablePanel;
@@ -1,10 +1,9 @@
1
- import { Dimensions, StyleSheet } from 'react-native';
1
+ import {Dimensions} from 'react-native';
2
+ import {Colors} from '../../configs/constants';
2
3
 
3
- import { Colors } from '../../configs/constants';
4
+ const {width} = Dimensions.get('window');
4
5
 
5
- const { width } = Dimensions.get('window');
6
-
7
- export default StyleSheet.create({
6
+ export default {
8
7
  background: {
9
8
  position: 'relative',
10
9
  width,
@@ -12,11 +11,11 @@ export default StyleSheet.create({
12
11
  flex: 1,
13
12
  justifyContent: 'center',
14
13
  alignItems: 'center',
15
- overflow: 'hidden',
14
+ overflow: 'hidden'
16
15
  },
17
16
  backgroundLayer: {
18
17
  width,
19
- flex: 1,
18
+ flex: 1
20
19
  },
21
20
  panel: {
22
21
  position: 'absolute',
@@ -42,25 +41,25 @@ export default StyleSheet.create({
42
41
  elevation: 1,
43
42
  zIndex: 2,
44
43
  paddingHorizontal: 20,
45
- paddingBottom: 20,
44
+ paddingBottom: 20
46
45
  },
47
46
  barContainer: {
48
47
  height: 50,
49
48
  justifyContent: 'center',
50
- alignItems: 'center',
49
+ alignItems: 'center'
51
50
  },
52
51
  bar: {
53
52
  backgroundColor: Colors.lightblue,
54
53
  height: 5,
55
54
  width: width * 0.12,
56
- borderRadius: 5,
55
+ borderRadius: 5
57
56
  },
58
57
  content: {
59
- paddingHorizontal: 20,
58
+ paddingHorizontal: 20
60
59
  },
61
60
  contentContainer: {
62
61
  width: '100%',
63
- flex: 1,
62
+ flex: 1
64
63
  },
65
64
  closeButton: {
66
65
  position: 'absolute',
@@ -71,11 +70,11 @@ export default StyleSheet.create({
71
70
  borderRadius: 15,
72
71
  backgroundColor: Colors.lightblue,
73
72
  alignItems: 'center',
74
- justifyContent: 'center',
73
+ justifyContent: 'center'
75
74
  },
76
75
  autoClose: {
77
76
  position: 'absolute',
78
77
  left: 10,
79
- top: 10,
78
+ top: 10
80
79
  },
81
- });
80
+ };
@@ -0,0 +1,57 @@
1
+ import React from 'react';
2
+ import PropTypes from 'prop-types';
3
+ import { Switch as RNSwitch, Platform } from 'react-native';
4
+ import { Colors } from '../../configs/constants';
5
+
6
+ const Switch = ({ value, size = 'medium', ...props }) => (
7
+ <RNSwitch
8
+ trackColor={{
9
+ false: Colors.lightblue,
10
+ true: Colors.lightblue,
11
+ }}
12
+ thumbColor={value ? Colors.green : Colors.midblue}
13
+ ios_backgroundColor={Colors.white}
14
+ value={value}
15
+ style={{
16
+ transform: [
17
+ {
18
+ scaleX:
19
+ size === 'small'
20
+ ? Platform.OS === 'ios'
21
+ ? 0.5
22
+ : 0.7
23
+ : Platform.OS === 'ios'
24
+ ? 0.7
25
+ : 0.9,
26
+ },
27
+ {
28
+ scaleY:
29
+ size === 'small'
30
+ ? Platform.OS === 'ios'
31
+ ? 0.5
32
+ : 0.7
33
+ : Platform.OS === 'ios'
34
+ ? 0.7
35
+ : 0.9,
36
+ },
37
+ ],
38
+ }}
39
+ {...props}
40
+ />
41
+ );
42
+
43
+ Switch.defaultProps = {
44
+ onValueChange: () => {},
45
+ value: false,
46
+ disabled: false,
47
+ size: 'medium',
48
+ };
49
+
50
+ Switch.propTypes = {
51
+ onValueChange: PropTypes.func,
52
+ value: PropTypes.bool,
53
+ disabled: PropTypes.bool,
54
+ size: PropTypes.oneOf(['medium', 'small']),
55
+ };
56
+
57
+ export default Switch;
@@ -1,11 +1,10 @@
1
+ import React from 'react';
2
+ import PropTypes from 'prop-types';
1
3
  import { Modal, TouchableOpacity, View } from 'react-native';
2
-
3
- import ImageResponsive from '../ImageResponsive';
4
4
  import Text from '../Text';
5
+ import ImageResponsive from '../ImageResponsive';
5
6
  import style from './style';
6
7
 
7
- import type { ConfirmPictureModalProps } from '../../configs/types';
8
-
9
8
  export const ConfirmPictureModal = ({
10
9
  avatar,
11
10
  visible,
@@ -14,13 +13,18 @@ export const ConfirmPictureModal = ({
14
13
  image,
15
14
  repeatPictureText,
16
15
  usePictureText,
17
- }: ConfirmPictureModalProps) => (
18
- <Modal animationType="slide" transparent={false} visible={visible} onRequestClose={() => {}}>
16
+ }) => (
17
+ <Modal
18
+ animationType='slide'
19
+ transparent={false}
20
+ visible={visible}
21
+ onRequestClose={() => {}}
22
+ >
19
23
  <View style={style.cameraTakenImageContainer}>
20
24
  <ImageResponsive
21
- source={
22
- image && typeof image === 'object' && 'uri' in image ? { uri: image.uri } : image === null ? undefined : image
23
- }
25
+ source={{
26
+ uri: image ? image.uri : null,
27
+ }}
24
28
  style={avatar ? style.cameraTakenImage : style.cameraContainer}
25
29
  />
26
30
 
@@ -35,3 +39,23 @@ export const ConfirmPictureModal = ({
35
39
  </View>
36
40
  </Modal>
37
41
  );
42
+
43
+ ConfirmPictureModal.defaultProps = {
44
+ visible: false,
45
+ avatar: false,
46
+ repeatPictureText: ' ',
47
+ usePictureText: ' ',
48
+ image: null,
49
+ onRepeatPhoto: () => {},
50
+ onUsePhoto: () => {},
51
+ };
52
+
53
+ ConfirmPictureModal.propTypes = {
54
+ visible: PropTypes.bool.isRequired,
55
+ avatar: PropTypes.bool,
56
+ repeatPictureText: PropTypes.string.isRequired,
57
+ usePictureText: PropTypes.string.isRequired,
58
+ image: PropTypes.object,
59
+ onRepeatPhoto: PropTypes.func.isRequired,
60
+ onUsePhoto: PropTypes.func.isRequired,
61
+ };
@@ -0,0 +1,198 @@
1
+ import React, { useState, useRef } from 'react';
2
+ import PropTypes from 'prop-types';
3
+ import { Modal, TouchableOpacity, View, ActivityIndicator } from 'react-native';
4
+ import Ionicons from 'react-native-vector-icons/Ionicons';
5
+ import { CameraView, useCameraPermissions } from 'expo-camera';
6
+ import * as ImageManipulator from 'expo-image-manipulator';
7
+ import { Svg, Defs, Rect, Mask, Circle } from 'react-native-svg';
8
+ import Text from '../Text';
9
+ import Container from '../Container';
10
+ import Button from '../Button';
11
+ import { Colors } from '../../configs/constants';
12
+ import { ConfirmPictureModal } from './confirmPictureModal';
13
+ import style from './style';
14
+
15
+ const DESIRED_RATIO = '16:9';
16
+
17
+ const SvgCircle = () => {
18
+ return (
19
+ <Svg height='100%' width='100%'>
20
+ <Defs>
21
+ <Mask id='mask' x='0' y='0' height='100%' width='100%'>
22
+ <Rect height='100%' width='100%' fill={Colors.white} />
23
+ <Circle r='29%' cx='50%' cy='50%' fill={Colors.darkgray} />
24
+ </Mask>
25
+ </Defs>
26
+ <Rect
27
+ height='100%'
28
+ width='100%'
29
+ fill='rgba(0, 0, 0, 0.8)'
30
+ mask='url(#mask)'
31
+ fill-opacity='0'
32
+ />
33
+ </Svg>
34
+ );
35
+ };
36
+
37
+ const TakePicture = ({
38
+ visible,
39
+ onClose,
40
+ avatar,
41
+ cameraErrorMessage,
42
+ requestPermissionsMessage,
43
+ processingPictureMessage,
44
+ repeatPictureText,
45
+ usePictureText,
46
+ }) => {
47
+ const [permission, requestPermission] = useCameraPermissions();
48
+ const [type, setType] = useState('back');
49
+ const [image, setImage] = useState(null);
50
+ const [takingPicture, setTakingPicture] = useState(false);
51
+ const camera = useRef();
52
+
53
+ const flipCamera = () => {
54
+ setType((current) => (current === 'back' ? 'front' : 'back'));
55
+ };
56
+
57
+ const takePicture = async () => {
58
+ if (camera) {
59
+ setTakingPicture(true);
60
+ const tempImage = await camera.current.takePictureAsync({ quality: 1.0 });
61
+
62
+ if (avatar) {
63
+ const avatarImage = await ImageManipulator.manipulateAsync(
64
+ tempImage.localUri || tempImage.uri,
65
+ [
66
+ {
67
+ crop: {
68
+ originX: 0,
69
+ originY: (tempImage.height - tempImage.width) / 2,
70
+ width: tempImage.width,
71
+ height: tempImage.width,
72
+ },
73
+ },
74
+ ],
75
+ { compress: 1.0, format: ImageManipulator.SaveFormat.PNG }
76
+ );
77
+
78
+ setImage(avatarImage);
79
+ setTakingPicture(false);
80
+ } else {
81
+ setImage(tempImage);
82
+ setTakingPicture(false);
83
+ }
84
+ }
85
+ };
86
+
87
+ const repeatPhoto = () => {
88
+ setImage(null);
89
+ };
90
+
91
+ const usePhoto = () => {
92
+ onClose(typeof image === 'object' ? image.uri : image);
93
+ setImage(null);
94
+ };
95
+
96
+ const closeModal = () => {
97
+ setImage(null);
98
+ onClose();
99
+ };
100
+
101
+ if (!permission?.granted) {
102
+ return (
103
+ <View style={style.cameraPermissionsContainer}>
104
+ <Text color='red'>{cameraErrorMessage}</Text>
105
+ <Button onPress={requestPermission} color='inverted'>
106
+ <Text color='white'>{requestPermissionsMessage}</Text>
107
+ </Button>
108
+ </View>
109
+ );
110
+ }
111
+
112
+ return (
113
+ <Modal
114
+ animationType='slide'
115
+ transparent={false}
116
+ visible={visible}
117
+ onRequestClose={onClose}
118
+ >
119
+ <Container style={style.cameraContainer}>
120
+ <CameraView
121
+ ref={camera}
122
+ style={style.cameraContainer}
123
+ facing={type}
124
+ ratio={DESIRED_RATIO}
125
+ >
126
+ {avatar && <SvgCircle />}
127
+
128
+ <View style={style.cameraFlipContainer}>
129
+ <TouchableOpacity onPress={closeModal} style={style.closeIcon}>
130
+ <Ionicons
131
+ name='close-outline'
132
+ color={Colors.darkblue}
133
+ size={24}
134
+ />
135
+ </TouchableOpacity>
136
+ <TouchableOpacity style={style.cameraFlipBtn} onPress={flipCamera}>
137
+ <Ionicons
138
+ name='camera-reverse-outline'
139
+ style={style.cameraFlipIcon}
140
+ color={Colors.white}
141
+ size={40}
142
+ />
143
+ </TouchableOpacity>
144
+ <TouchableOpacity
145
+ style={style.cameraRecordBtn}
146
+ onPress={takePicture}
147
+ >
148
+ <Ionicons
149
+ name='radio-button-on-outline'
150
+ color={Colors.red}
151
+ size={60}
152
+ />
153
+ </TouchableOpacity>
154
+ </View>
155
+ </CameraView>
156
+
157
+ {takingPicture && (
158
+ <View style={style.cameraTakingPictureOverlay}>
159
+ <ActivityIndicator color={Colors.darkblue} />
160
+ <Text color='white'>{processingPictureMessage} ...</Text>
161
+ </View>
162
+ )}
163
+ <ConfirmPictureModal
164
+ visible={image !== null}
165
+ image={image}
166
+ repeatPictureText={repeatPictureText}
167
+ usePictureText={usePictureText}
168
+ onRepeatPhoto={repeatPhoto}
169
+ onUsePhoto={usePhoto}
170
+ />
171
+ </Container>
172
+ </Modal>
173
+ );
174
+ };
175
+
176
+ TakePicture.defaultProps = {
177
+ visible: false,
178
+ onClose: () => {},
179
+ avatar: false,
180
+ cameraErrorMessage: ' ',
181
+ requestPermissionsMessage: ' ',
182
+ processingPictureMessage: ' ',
183
+ repeatPictureText: ' ',
184
+ usePictureText: ' ',
185
+ };
186
+
187
+ TakePicture.propTypes = {
188
+ visible: PropTypes.bool.isRequired,
189
+ onClose: PropTypes.func.isRequired,
190
+ avatar: PropTypes.bool,
191
+ cameraErrorMessage: PropTypes.string.isRequired,
192
+ requestPermissionsMessage: PropTypes.string.isRequired,
193
+ processingPictureMessage: PropTypes.string.isRequired,
194
+ repeatPictureText: PropTypes.string.isRequired,
195
+ usePictureText: PropTypes.string.isRequired,
196
+ };
197
+
198
+ export default TakePicture;