@holper/react-native-holper-storybook 0.6.101 → 0.7.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 (210) hide show
  1. package/.expo/README.md +8 -0
  2. package/.nvmrc +1 -0
  3. package/.prettierignore +5 -0
  4. package/.storybook/index.jsx +11 -0
  5. package/.storybook/main.js +8 -0
  6. package/.storybook/preview.jsx +51 -0
  7. package/.storybook/storybook.requires.js +43 -0
  8. package/.yarn/releases/yarn-4.5.0.cjs +925 -0
  9. package/.yarnrc.yml +3 -0
  10. package/App.jsx +29 -0
  11. package/LICENSE +21 -0
  12. package/{readme.md → README.md} +18 -19
  13. package/app.config.js +77 -0
  14. package/assets/adaptive-icon.png +0 -0
  15. package/assets/favicon.png +0 -0
  16. package/assets/fonts/Poppins-Bold.ttf +0 -0
  17. package/assets/fonts/Poppins-Regular.ttf +0 -0
  18. package/assets/fonts/Poppins-SemiBold.ttf +0 -0
  19. package/assets/icon.png +0 -0
  20. package/assets/splash.png +0 -0
  21. package/babel.config.js +7 -0
  22. package/build.sh +11 -0
  23. package/components/Button/index.tsx +66 -0
  24. package/{lib/components/Button/style.js → components/Button/style.ts} +8 -7
  25. package/components/Card/index.tsx +33 -0
  26. package/{lib/components/Card/style.js → components/Card/style.ts} +5 -4
  27. package/{lib/components/ConfirmationModal/index.js → components/ConfirmationModal/index.tsx} +25 -79
  28. package/{lib/components/ConfirmationModal/style.js → components/ConfirmationModal/style.tsx} +14 -13
  29. package/{lib/components/Container/index.js → components/Container/index.tsx} +7 -28
  30. package/{lib/components/Container/style.js → components/Container/style.ts} +6 -5
  31. package/{lib/components/CustomChatView/index.js → components/CustomChatView/index.tsx} +22 -30
  32. package/{lib/components/CustomChatView/style.js → components/CustomChatView/style.ts} +1 -1
  33. package/components/DeckSwiper/index.tsx +90 -0
  34. package/{lib/components/DeckSwiper/style.js → components/DeckSwiper/style.ts} +13 -12
  35. package/components/DonutCountdown/index.tsx +86 -0
  36. package/components/DonutCountdown/style.ts +8 -0
  37. package/components/FloatingContainer/index.tsx +35 -0
  38. package/{lib/components/FloatingContainer/style.js → components/FloatingContainer/style.ts} +7 -6
  39. package/components/Footer/index.tsx +35 -0
  40. package/{lib/components/Footer/style.js → components/Footer/style.ts} +4 -3
  41. package/components/Header/index.tsx +21 -0
  42. package/{lib/components/Header/style.js → components/Header/style.ts} +4 -3
  43. package/{lib/components/ImagePicker/index.js → components/ImagePicker/index.tsx} +3 -12
  44. package/components/ImageResponsive/index.tsx +24 -0
  45. package/components/ImageResponsive/style.ts +9 -0
  46. package/components/ImageViewer/index.tsx +36 -0
  47. package/{lib/components/ImageViewer/style.js → components/ImageViewer/style.ts} +11 -10
  48. package/{lib/components/Input/index.js → components/Input/index.tsx} +6 -33
  49. package/{lib/components/Input/style.js → components/Input/style.ts} +7 -18
  50. package/{lib/components/InputPin/index.js → components/InputPin/index.tsx} +6 -13
  51. package/{lib/components/InputPin/style.js → components/InputPin/style.ts} +7 -6
  52. package/components/MenuItem/index.tsx +25 -0
  53. package/{lib/components/MenuItem/style.js → components/MenuItem/style.ts} +9 -7
  54. package/{lib/components/NavigationTitle/index.js → components/NavigationTitle/index.tsx} +9 -30
  55. package/{lib/components/NavigationTitle/style.js → components/NavigationTitle/style.ts} +12 -11
  56. package/components/Notification/index.tsx +44 -0
  57. package/{lib/components/Notification/style.js → components/Notification/style.ts} +13 -11
  58. package/components/PreventDoubleClick/index.tsx +28 -0
  59. package/components/Select/index.tsx +51 -0
  60. package/components/Select/style.ts +64 -0
  61. package/{lib/components/SwipeablePanel/index.js → components/SwipeablePanel/index.tsx} +58 -85
  62. package/{lib/components/SwipeablePanel/style.js → components/SwipeablePanel/style.ts} +15 -14
  63. package/components/Switch/index.tsx +30 -0
  64. package/components/TakePicture/confirmPictureModal.tsx +37 -0
  65. package/components/TakePicture/index.tsx +148 -0
  66. package/{lib/components/TakePicture/style.js → components/TakePicture/style.ts} +4 -4
  67. package/components/Text/index.tsx +33 -0
  68. package/{lib/components/Text/style.js → components/Text/style.ts} +4 -2
  69. package/{lib/components/Textarea/index.js → components/Textarea/index.tsx} +5 -24
  70. package/{lib/components/Textarea/style.js → components/Textarea/style.ts} +5 -4
  71. package/components/TimeOutButton/index.tsx +67 -0
  72. package/{lib/components/TimeOutButton/style.js → components/TimeOutButton/style.ts} +4 -3
  73. package/components/Toast/index.tsx +34 -0
  74. package/components/Toast/style.ts +12 -0
  75. package/{lib/components/UploadDocument/index.js → components/UploadDocument/index.tsx} +49 -105
  76. package/{lib/components/UploadDocument/style.js → components/UploadDocument/style.ts} +16 -15
  77. package/components/VirtualKeyboard/index.tsx +75 -0
  78. package/{lib/components/VirtualKeyboard/style.js → components/VirtualKeyboard/style.ts} +9 -8
  79. package/components/index.ts +29 -0
  80. package/{lib/configs/constants.js → configs/constants.ts} +50 -48
  81. package/configs/types.ts +326 -0
  82. package/eas.json +27 -0
  83. package/eslint.config.mjs +205 -0
  84. package/hooks/index.ts +2 -0
  85. package/{lib/hooks/useDebounce.js → hooks/useDebounce.tsx} +6 -4
  86. package/hooks/useLoadFonts.tsx +13 -0
  87. package/index.js +3 -2
  88. package/lib/components/Button/index.tsx +66 -0
  89. package/lib/components/Button/style.ts +111 -0
  90. package/lib/components/Card/index.tsx +33 -0
  91. package/lib/components/Card/style.ts +34 -0
  92. package/lib/components/ConfirmationModal/index.tsx +104 -0
  93. package/lib/components/ConfirmationModal/style.tsx +53 -0
  94. package/lib/components/Container/index.tsx +33 -0
  95. package/lib/components/Container/style.ts +13 -0
  96. package/lib/components/CustomChatView/index.tsx +65 -0
  97. package/lib/components/CustomChatView/style.ts +10 -0
  98. package/lib/components/DeckSwiper/index.tsx +90 -0
  99. package/lib/components/DeckSwiper/style.ts +59 -0
  100. package/lib/components/DonutCountdown/index.tsx +86 -0
  101. package/lib/components/DonutCountdown/style.ts +8 -0
  102. package/lib/components/FloatingContainer/index.tsx +35 -0
  103. package/lib/components/FloatingContainer/style.ts +25 -0
  104. package/lib/components/Footer/index.tsx +35 -0
  105. package/lib/components/Footer/style.ts +40 -0
  106. package/lib/components/Header/index.tsx +21 -0
  107. package/lib/components/Header/style.ts +34 -0
  108. package/lib/components/ImagePicker/index.tsx +18 -0
  109. package/lib/components/ImageResponsive/index.tsx +24 -0
  110. package/lib/components/ImageResponsive/style.ts +9 -0
  111. package/lib/components/ImageViewer/index.tsx +36 -0
  112. package/lib/components/ImageViewer/style.ts +38 -0
  113. package/lib/components/Input/index.tsx +62 -0
  114. package/lib/components/Input/style.ts +91 -0
  115. package/lib/components/InputPin/index.tsx +21 -0
  116. package/lib/components/InputPin/style.ts +22 -0
  117. package/lib/components/MenuItem/index.tsx +25 -0
  118. package/lib/components/MenuItem/style.ts +44 -0
  119. package/lib/components/NavigationTitle/index.tsx +53 -0
  120. package/lib/components/NavigationTitle/style.ts +49 -0
  121. package/lib/components/Notification/index.tsx +44 -0
  122. package/lib/components/Notification/style.ts +50 -0
  123. package/lib/components/PreventDoubleClick/index.tsx +28 -0
  124. package/lib/components/Select/index.tsx +51 -0
  125. package/lib/components/Select/style.ts +64 -0
  126. package/lib/components/SwipeablePanel/index.tsx +208 -0
  127. package/lib/components/SwipeablePanel/style.ts +81 -0
  128. package/lib/components/Switch/index.tsx +30 -0
  129. package/lib/components/TakePicture/confirmPictureModal.tsx +37 -0
  130. package/lib/components/TakePicture/index.tsx +148 -0
  131. package/lib/components/TakePicture/style.ts +95 -0
  132. package/lib/components/Text/index.tsx +33 -0
  133. package/lib/components/Text/style.ts +101 -0
  134. package/lib/components/Textarea/index.tsx +26 -0
  135. package/lib/components/Textarea/style.ts +38 -0
  136. package/lib/components/TimeOutButton/index.tsx +67 -0
  137. package/lib/components/TimeOutButton/style.ts +42 -0
  138. package/lib/components/Toast/index.tsx +34 -0
  139. package/lib/components/Toast/style.ts +12 -0
  140. package/lib/components/UploadDocument/index.tsx +179 -0
  141. package/lib/components/UploadDocument/style.ts +57 -0
  142. package/lib/components/VirtualKeyboard/index.tsx +75 -0
  143. package/lib/components/VirtualKeyboard/style.ts +25 -0
  144. package/lib/components/index.ts +29 -0
  145. package/lib/configs/constants.ts +273 -0
  146. package/lib/configs/types.ts +326 -0
  147. package/lib/hooks/index.ts +2 -0
  148. package/lib/hooks/useDebounce.tsx +24 -0
  149. package/lib/hooks/useLoadFonts.tsx +13 -0
  150. package/lib/index.js +3 -2
  151. package/metro.config.js +11 -0
  152. package/package.json +72 -63
  153. package/prettier.config.mjs +23 -0
  154. package/stories/Button.stories.tsx +181 -0
  155. package/stories/Card.stories.tsx +22 -0
  156. package/stories/Colors.stories.tsx +57 -0
  157. package/stories/ConfirmationModal.stories.tsx +142 -0
  158. package/stories/Container.stories.tsx +105 -0
  159. package/stories/DeckSwiper.stories.tsx +43 -0
  160. package/stories/DonutCountdown.stories.tsx +134 -0
  161. package/stories/FloatingContainer.stories.tsx +139 -0
  162. package/stories/Footer.stories.tsx +65 -0
  163. package/stories/Header.stories.tsx +37 -0
  164. package/stories/ImagePicker.stories.tsx +14 -0
  165. package/stories/ImageResponsive.stories.tsx +18 -0
  166. package/stories/ImageViewer.stories.tsx +24 -0
  167. package/stories/Input.stories.tsx +119 -0
  168. package/stories/InputPin.stories.tsx +40 -0
  169. package/stories/Menu.stories.tsx +53 -0
  170. package/stories/MenuItem.stories.tsx +30 -0
  171. package/stories/NavigationTitle.stories.tsx +51 -0
  172. package/stories/Notification.stories.tsx +58 -0
  173. package/stories/Select.stories.tsx +270 -0
  174. package/stories/SwipeablePanel.stories.tsx +360 -0
  175. package/stories/Switch.stories.tsx +36 -0
  176. package/stories/TakePicture.stories.tsx +59 -0
  177. package/stories/Text.stories.tsx +61 -0
  178. package/stories/Textarea.stories.tsx +48 -0
  179. package/stories/TimeOutButton.stories.tsx +55 -0
  180. package/stories/Toast.stories.tsx +37 -0
  181. package/stories/UploadDocument.stories.tsx +179 -0
  182. package/stories/VirtualKeyboard.stories.tsx +14 -0
  183. package/tsconfig.json +21 -0
  184. package/utilities/ScrollView.tsx +19 -0
  185. package/utilities/index.ts +2 -0
  186. package/utilities/utils.ts +29 -0
  187. package/lib/components/Button/index.js +0 -104
  188. package/lib/components/Card/index.js +0 -49
  189. package/lib/components/DeckSwiper/index.js +0 -118
  190. package/lib/components/FlashMessage/index.js +0 -81
  191. package/lib/components/FloatingContainer/index.js +0 -69
  192. package/lib/components/Footer/index.js +0 -61
  193. package/lib/components/Header/index.js +0 -45
  194. package/lib/components/ImageResponsive/index.js +0 -39
  195. package/lib/components/ImageResponsive/style.js +0 -7
  196. package/lib/components/ImageViewer/index.js +0 -62
  197. package/lib/components/MenuItem/index.js +0 -44
  198. package/lib/components/Notification/index.js +0 -80
  199. package/lib/components/PreventDoubleClick/index.js +0 -21
  200. package/lib/components/Select/index.js +0 -89
  201. package/lib/components/Select/style.js +0 -81
  202. package/lib/components/Switch/index.js +0 -57
  203. package/lib/components/TakePicture/confirmPictureModal.js +0 -64
  204. package/lib/components/TakePicture/index.js +0 -198
  205. package/lib/components/Text/index.js +0 -75
  206. package/lib/components/TimeOutButton/index.js +0 -104
  207. package/lib/components/VirtualKeyboard/index.js +0 -86
  208. package/lib/components/index.js +0 -28
  209. package/lib/configs/loadFonts.js +0 -11
  210. package/lib/hooks/index.js +0 -1
@@ -1,14 +1,10 @@
1
- import React from 'react';
2
- import PropTypes from 'prop-types';
1
+ import { Keyboard, ScrollView, TouchableWithoutFeedback, View } from 'react-native';
3
2
  import { SafeAreaView } from 'react-native-safe-area-context';
4
- import {
5
- TouchableWithoutFeedback,
6
- Keyboard,
7
- ScrollView,
8
- View,
9
- } from 'react-native';
3
+
10
4
  import style from './style';
11
5
 
6
+ import type { ContainerProps } from '../../configs/types';
7
+
12
8
  const Container = ({
13
9
  fullScreen = true,
14
10
  enableScroll,
@@ -16,16 +12,13 @@ const Container = ({
16
12
  scrollStyle,
17
13
  children,
18
14
  ...props
19
- }) => (
20
- <SafeAreaView
21
- {...props}
22
- style={[fullScreen ? style.fullScreen : {}, customStyle]}
23
- >
15
+ }: ContainerProps) => (
16
+ <SafeAreaView {...props} style={[fullScreen ? style.fullScreen : {}, customStyle]}>
24
17
  {enableScroll ? (
25
18
  <ScrollView
26
19
  contentContainerStyle={scrollStyle}
27
20
  showsVerticalScrollIndicator={false}
28
- keyboardDismissMode='on-drag'
21
+ keyboardDismissMode="on-drag"
29
22
  >
30
23
  {children}
31
24
  </ScrollView>
@@ -37,18 +30,4 @@ const Container = ({
37
30
  </SafeAreaView>
38
31
  );
39
32
 
40
- Container.defaultProps = {
41
- fullScreen: true,
42
- enableScroll: false,
43
- style: {},
44
- scrollStyle: {},
45
- };
46
-
47
- Container.propTypes = {
48
- fullScreen: PropTypes.bool,
49
- enableScroll: PropTypes.bool,
50
- style: PropTypes.any,
51
- scrollStyle: PropTypes.any,
52
- };
53
-
54
33
  export default Container;
@@ -1,12 +1,13 @@
1
- import { Dimensions } from 'react-native';
1
+ import { Dimensions, StyleSheet } from 'react-native';
2
+
2
3
  import { Colors } from '../../configs/constants';
3
4
 
4
5
  const { width } = Dimensions.get('window');
5
6
 
6
- export default {
7
+ export default StyleSheet.create({
7
8
  fullScreen: {
8
9
  flex: 1,
9
10
  width,
10
- backgroundColor: Colors.white
11
- }
12
- };
11
+ backgroundColor: Colors.white,
12
+ },
13
+ });
@@ -1,43 +1,47 @@
1
- import MapView, { PROVIDER_GOOGLE } from "react-native-maps";
2
- import PropTypes from "prop-types";
3
- import React from "react";
4
- import { Platform, TouchableOpacity, Alert, Linking } from "react-native";
5
- import { MapStyle } from "../../configs/constants";
6
- import styles from "./style";
1
+ import { Alert, Linking, Platform, TouchableOpacity } from 'react-native';
2
+ import MapView, { PROVIDER_GOOGLE } from 'react-native-maps';
7
3
 
8
- const CustomChatView = ({ currentMessage, containerStyle, mapViewStyle }) => {
4
+ import { MapStyle } from '../../configs/constants';
5
+ import styles from './style';
6
+
7
+ import type { CustomChatViewProps } from '../../configs/types';
8
+
9
+ const CustomChatView = ({ currentMessage, containerStyle, mapViewStyle }: CustomChatViewProps) => {
9
10
  const openMapAsync = async () => {
10
- const { location = {} } = currentMessage;
11
+ const { location } = currentMessage || {};
12
+
13
+ if (!location) {
14
+ Alert.alert('Location data is missing.');
15
+ return null;
16
+ }
11
17
 
12
18
  const mapScheme = Platform.select({
13
- ios: "maps:0,0?q=",
14
- android: "geo:0,0?q=",
19
+ ios: 'maps:0,0?q=',
20
+ android: 'geo:0,0?q=',
15
21
  });
16
22
  const latLng = `${location.latitude},${location.longitude}`;
17
- const label = `${currentMessage.user.name}`;
23
+ const label = `${currentMessage?.user?.name ?? ''}`;
18
24
 
19
25
  const url = Platform.select({
20
26
  ios: `${mapScheme}${label}@${latLng}`,
21
27
  android: `${mapScheme}${latLng}(${label})`,
22
- });
28
+ }) as string;
23
29
 
24
30
  try {
25
31
  const supported = await Linking.canOpenURL(url);
26
32
  if (supported) {
27
33
  return Linking.openURL(url);
28
34
  }
29
- Alert.alert("Opening the map is not supported.");
35
+ Alert.alert('Opening the map is not supported.');
30
36
  } catch ({ message }) {
31
37
  Alert.alert(message);
32
38
  }
39
+ return null;
33
40
  };
34
41
 
35
- if (currentMessage.location) {
42
+ if (currentMessage?.location) {
36
43
  return (
37
- <TouchableOpacity
38
- style={[styles.container, containerStyle]}
39
- onPress={openMapAsync}
40
- >
44
+ <TouchableOpacity style={[styles.container, containerStyle]} onPress={openMapAsync}>
41
45
  <MapView
42
46
  style={[styles.mapView, mapViewStyle]}
43
47
  region={{
@@ -58,16 +62,4 @@ const CustomChatView = ({ currentMessage, containerStyle, mapViewStyle }) => {
58
62
  return null;
59
63
  };
60
64
 
61
- CustomChatView.propTypes = {
62
- currentMessage: PropTypes.object,
63
- containerStyle: PropTypes.object,
64
- mapViewStyle: PropTypes.object,
65
- };
66
-
67
- CustomChatView.defaultProps = {
68
- currentMessage: {},
69
- containerStyle: {},
70
- mapViewStyle: {},
71
- };
72
-
73
65
  export default CustomChatView;
@@ -1,7 +1,7 @@
1
1
  export default {
2
2
  container: {},
3
3
  mapView: {
4
- flexDirection: "column-reverse",
4
+ flexDirection: 'column-reverse',
5
5
  width: 250,
6
6
  height: 150,
7
7
  borderRadius: 13,
@@ -0,0 +1,90 @@
1
+ import { Entypo } from '@expo/vector-icons';
2
+ import { useRef, useState } from 'react';
3
+ import { Image, View } from 'react-native';
4
+ import Swiper from 'react-native-deck-swiper';
5
+
6
+ import { Colors } from '../../configs/constants';
7
+ import Button from '../Button';
8
+ import Text from '../Text';
9
+ import style from './style';
10
+
11
+ import type { DeckSwiperProps } from '../../configs/types';
12
+
13
+ const DeckSwiper = ({ data = [], inverted, nextText, onChange, onFinish }: DeckSwiperProps) => {
14
+ const swiper = useRef<Swiper<(typeof data)[0]>>(null);
15
+ const [finished, setFinished] = useState(false);
16
+ const [index, setIndex] = useState(0);
17
+
18
+ const renderDots = () =>
19
+ data?.map((d, i) => (
20
+ <Entypo name="dot-single" size={36} key={`dot-${i}`} style={style[index === i ? 'dotSelected' : 'dot']} />
21
+ ));
22
+
23
+ return (
24
+ <>
25
+ <Swiper
26
+ ref={swiper}
27
+ cards={data}
28
+ renderCard={(card) => {
29
+ if (!card) {
30
+ return null;
31
+ }
32
+ return (
33
+ <View style={style.card}>
34
+ <View style={style.imageContainer}>
35
+ <Image
36
+ style={style.imageResponsive}
37
+ source={card.image}
38
+ progressiveRenderingEnabled
39
+ resizeMode="contain"
40
+ />
41
+ </View>
42
+ <View style={style.textContainer}>
43
+ <Text size="extra-large" style={style.title}>
44
+ {card.title}
45
+ </Text>
46
+ <Text size="large" align="center">
47
+ {card.description}
48
+ </Text>
49
+ </View>
50
+ </View>
51
+ );
52
+ }}
53
+ onSwipedAll={() => {
54
+ setFinished(true);
55
+ if (onFinish) {
56
+ onFinish();
57
+ }
58
+ }}
59
+ onSwipedRight={(i) => {
60
+ setIndex(i - 1);
61
+ onChange?.(i - 1);
62
+ }}
63
+ onSwipedLeft={(i) => {
64
+ setIndex(i + 1);
65
+ onChange?.(i + 1);
66
+ }}
67
+ cardIndex={index}
68
+ backgroundColor={Colors.white}
69
+ stackSize={2}
70
+ verticalSwipe={false}
71
+ showSecondCard
72
+ disableRightSwipe={index === 0}
73
+ goBackToPreviousCardOnSwipeRight
74
+ childrenOnTop
75
+ />
76
+ {!finished && (
77
+ <>
78
+ <View style={style.dotsContainer}>{renderDots()}</View>
79
+ <View style={style.container}>
80
+ <Button variant={inverted ? 'inverted' : 'primary'} onPress={() => swiper.current?.swipeLeft()}>
81
+ <Text color="white">{nextText}</Text>
82
+ </Button>
83
+ </View>
84
+ </>
85
+ )}
86
+ </>
87
+ );
88
+ };
89
+
90
+ export default DeckSwiper;
@@ -1,9 +1,10 @@
1
- import {Dimensions} from 'react-native';
2
- import {Colors} from '../../configs/constants';
1
+ import { Dimensions, StyleSheet } from 'react-native';
3
2
 
4
- const {height, width} = Dimensions.get('window');
3
+ import { Colors } from '../../configs/constants';
5
4
 
6
- export default {
5
+ const { height, width } = Dimensions.get('window');
6
+
7
+ export default StyleSheet.create({
7
8
  container: {
8
9
  position: 'absolute',
9
10
  bottom: 0,
@@ -13,7 +14,7 @@ export default {
13
14
  paddingVertical: 30,
14
15
  paddingHorizontal: 15,
15
16
  marginBottom: 10,
16
- width
17
+ width,
17
18
  },
18
19
  card: {
19
20
  flex: 1,
@@ -28,17 +29,17 @@ export default {
28
29
  imageResponsive: {
29
30
  height: undefined,
30
31
  width: undefined,
31
- flex: 1
32
+ flex: 1,
32
33
  },
33
34
  textContainer: {
34
35
  display: 'flex',
35
36
  flexDirection: 'column',
36
37
  justifyContent: 'center',
37
38
  alignItems: 'center',
38
- width: width - 100
39
+ width: width - 100,
39
40
  },
40
41
  title: {
41
- marginVertical: 10
42
+ marginVertical: 10,
42
43
  },
43
44
  dotsContainer: {
44
45
  width,
@@ -50,9 +51,9 @@ export default {
50
51
  bottom: 130,
51
52
  },
52
53
  dot: {
53
- color: Colors.gray
54
+ color: Colors.gray,
54
55
  },
55
56
  dotSelected: {
56
- color: Colors.green
57
- }
58
- };
57
+ color: Colors.green,
58
+ },
59
+ });
@@ -0,0 +1,86 @@
1
+ import React, { useEffect, useRef, useState } from 'react';
2
+ import { Animated, StyleSheet, View } from 'react-native';
3
+ import Svg, { Circle } from 'react-native-svg';
4
+
5
+ import { Colors } from '../../configs/constants';
6
+ import Text from '../Text';
7
+ import styles from './style';
8
+
9
+ import type { DonutCountdownProps } from '../../configs/types';
10
+
11
+ const AnimatedCircle = Animated.createAnimatedComponent(Circle);
12
+
13
+ const DonutCountdown = ({
14
+ bgColor = Colors.dimgray,
15
+ color = Colors.green,
16
+ duration = 10,
17
+ onComplete = () => {},
18
+ radius = 60,
19
+ strokeWidth = 10,
20
+ textColor = Colors.darkgray,
21
+ textSize = 'medium',
22
+ }: DonutCountdownProps) => {
23
+ const circumference = 2 * Math.PI * radius;
24
+ const halfCircle = radius + strokeWidth;
25
+ const animatedValue = useRef(new Animated.Value(0)).current;
26
+ const [timeLeft, setTimeLeft] = useState(duration);
27
+
28
+ useEffect(() => {
29
+ // Animate from 0 to 1
30
+ Animated.timing(animatedValue, {
31
+ toValue: 1,
32
+ duration: duration * 1000,
33
+ useNativeDriver: true,
34
+ }).start(onComplete);
35
+
36
+ // Countdown timer
37
+ const interval = setInterval(() => {
38
+ setTimeLeft((prev) => {
39
+ if (prev <= 1) {
40
+ clearInterval(interval);
41
+ return 0;
42
+ }
43
+ return prev - 1;
44
+ });
45
+ }, 1000);
46
+
47
+ return () => clearInterval(interval);
48
+ // eslint-disable-next-line react-hooks/exhaustive-deps
49
+ }, []);
50
+
51
+ const strokeDashoffset = animatedValue.interpolate({
52
+ inputRange: [0, 1],
53
+ outputRange: [0, circumference],
54
+ });
55
+
56
+ return (
57
+ <View style={{ width: radius * 2, height: radius * 2 }}>
58
+ <Svg width={radius * 2} height={radius * 2} viewBox={`0 0 ${halfCircle * 2} ${halfCircle * 2}`}>
59
+ {/* Background circle */}
60
+ <Circle cx="50%" cy="50%" r={radius} stroke={bgColor} strokeWidth={strokeWidth} fill="none" />
61
+ {/* Animated circle */}
62
+ <AnimatedCircle
63
+ cx="50%"
64
+ cy="50%"
65
+ r={radius}
66
+ stroke={color}
67
+ strokeWidth={strokeWidth}
68
+ fill="none"
69
+ strokeDasharray={`${circumference}, ${circumference}`}
70
+ strokeDashoffset={strokeDashoffset}
71
+ strokeLinecap="round"
72
+ transform={`rotate(-90 ${halfCircle} ${halfCircle})`}
73
+ />
74
+ </Svg>
75
+
76
+ {/* Centered countdown text */}
77
+ <View style={[StyleSheet.absoluteFillObject, styles.center]}>
78
+ <Text weight="semiBold" size={textSize} style={{ color: textColor }}>
79
+ {timeLeft}
80
+ </Text>
81
+ </View>
82
+ </View>
83
+ );
84
+ };
85
+
86
+ export default DonutCountdown;
@@ -0,0 +1,8 @@
1
+ import { StyleSheet } from 'react-native';
2
+
3
+ export default StyleSheet.create({
4
+ center: {
5
+ justifyContent: 'center',
6
+ alignItems: 'center',
7
+ },
8
+ });
@@ -0,0 +1,35 @@
1
+ import { KeyboardAvoidingView, Platform, RefreshControl, ScrollView, View } from 'react-native';
2
+
3
+ import style from './style';
4
+
5
+ import type { FloatingContainerProps } from '../../configs/types';
6
+
7
+ const FloatingContainer = ({
8
+ useRefreshControl,
9
+ onRefresh,
10
+ isRefreshing,
11
+ children,
12
+ floatingComponent,
13
+ centered,
14
+ floatingContainerStyle,
15
+ disableScroll,
16
+ }: FloatingContainerProps) => (
17
+ <KeyboardAvoidingView style={style.floatingContainer} behavior={Platform.OS === 'ios' ? 'padding' : 'height'}>
18
+ <ScrollView
19
+ contentContainerStyle={centered ? style.centered : {}}
20
+ fadingEdgeLength={150}
21
+ keyboardShouldPersistTaps="handled"
22
+ showsVerticalScrollIndicator={false}
23
+ scrollEnabled={!disableScroll}
24
+ keyboardDismissMode="on-drag"
25
+ refreshControl={
26
+ useRefreshControl ? <RefreshControl refreshing={!!isRefreshing} onRefresh={onRefresh} /> : undefined
27
+ }
28
+ >
29
+ {children}
30
+ </ScrollView>
31
+ <View style={[style.container, floatingContainerStyle]}>{floatingComponent}</View>
32
+ </KeyboardAvoidingView>
33
+ );
34
+
35
+ export default FloatingContainer;
@@ -1,11 +1,12 @@
1
- import { Dimensions } from 'react-native';
1
+ import { Dimensions, StyleSheet } from 'react-native';
2
+
2
3
  import { Colors } from '../../configs/constants';
3
4
 
4
5
  const { width } = Dimensions.get('window');
5
6
 
6
- export default {
7
+ export default StyleSheet.create({
7
8
  floatingContainer: {
8
- flex: 1
9
+ flex: 1,
9
10
  },
10
11
  container: {
11
12
  minHeight: 50,
@@ -19,6 +20,6 @@ export default {
19
20
  alignSelf: 'center',
20
21
  },
21
22
  centered: {
22
- alignItems: 'center'
23
- }
24
- };
23
+ alignItems: 'center',
24
+ },
25
+ });
@@ -0,0 +1,35 @@
1
+ import { Ionicons } from '@expo/vector-icons';
2
+ import { TouchableOpacity, View } from 'react-native';
3
+
4
+ import { Colors } from '../../configs/constants';
5
+ import Text from '../Text';
6
+ import style from './style';
7
+
8
+ import type { FooterProps } from '../../configs/types';
9
+
10
+ const Footer = ({ inverted, tabs }: FooterProps) => {
11
+ const renderTab = () =>
12
+ tabs.map((tab, index) => (
13
+ <TouchableOpacity key={`tab-${index}`} style={style.tab} onPress={tab.onPress}>
14
+ <Ionicons
15
+ name={tab.icon as keyof typeof Ionicons.glyphMap}
16
+ size={20}
17
+ color={inverted ? Colors.white : Colors.darkblue}
18
+ />
19
+ <Text color={inverted ? 'white' : 'dark'} size="small">
20
+ {tab.text}
21
+ </Text>
22
+ {tab.badge && tab.badge > 0 && (
23
+ <View style={style.badge}>
24
+ <Text color="white" size="tiny" weight="semiBold" align="center">
25
+ {tab.badge > 9 ? '+9' : tab.badge}
26
+ </Text>
27
+ </View>
28
+ )}
29
+ </TouchableOpacity>
30
+ ));
31
+
32
+ return <View style={[style.footerContainer, style[inverted ? 'inverted' : 'default']]}>{renderTab()}</View>;
33
+ };
34
+
35
+ export default Footer;
@@ -1,9 +1,10 @@
1
- import { Dimensions } from 'react-native';
1
+ import { Dimensions, StyleSheet } from 'react-native';
2
+
2
3
  import { Colors } from '../../configs/constants';
3
4
 
4
5
  const { width } = Dimensions.get('window');
5
6
 
6
- export default {
7
+ export default StyleSheet.create({
7
8
  footerContainer: {
8
9
  width,
9
10
  height: 70,
@@ -36,4 +37,4 @@ export default {
36
37
  borderRadius: 9,
37
38
  justifyContent: 'center',
38
39
  },
39
- };
40
+ });
@@ -0,0 +1,21 @@
1
+ import { Ionicons } from '@expo/vector-icons';
2
+ import { Image, TouchableOpacity, View } from 'react-native';
3
+
4
+ import { Colors } from '../../configs/constants';
5
+ import style from './style';
6
+
7
+ import type { HeaderProps } from '../../configs/types';
8
+
9
+ const Header = ({ inverted, logo, right, onMenuPress }: HeaderProps) => (
10
+ <View style={[style.headerContainer, style[inverted ? 'inverted' : 'default']]}>
11
+ <TouchableOpacity onPress={onMenuPress}>
12
+ <Ionicons name="menu-outline" size={30} color={inverted ? Colors.white : Colors.darkblue} />
13
+ </TouchableOpacity>
14
+ <View style={style.imageContainer}>
15
+ <Image style={style.imageResponsive} source={logo} progressiveRenderingEnabled resizeMode="contain" />
16
+ </View>
17
+ {right}
18
+ </View>
19
+ );
20
+
21
+ export default Header;
@@ -1,9 +1,10 @@
1
- import { Dimensions } from 'react-native';
1
+ import { Dimensions, StyleSheet } from 'react-native';
2
+
2
3
  import { Colors } from '../../configs/constants';
3
4
 
4
5
  const { width } = Dimensions.get('window');
5
6
 
6
- export default {
7
+ export default StyleSheet.create({
7
8
  headerContainer: {
8
9
  width,
9
10
  height: 70,
@@ -30,4 +31,4 @@ export default {
30
31
  width: undefined,
31
32
  flex: 1,
32
33
  },
33
- };
34
+ });
@@ -1,7 +1,6 @@
1
- import PropTypes from 'prop-types';
2
1
  import * as ExpoImagePicker from 'expo-image-picker';
3
2
 
4
- const ImagePicker = async (avatar) => {
3
+ const ImagePicker = async (avatar: boolean = false) => {
5
4
  const image = await ExpoImagePicker.launchImageLibraryAsync({
6
5
  mediaTypes: ['images'],
7
6
  allowsEditing: avatar,
@@ -10,18 +9,10 @@ const ImagePicker = async (avatar) => {
10
9
  });
11
10
 
12
11
  if (image.canceled) {
13
- return;
12
+ return undefined;
14
13
  }
15
14
 
16
- return image.assets[0].uri;
17
- };
18
-
19
- ImagePicker.defaultProps = {
20
- avatar: false,
21
- };
22
-
23
- ImagePicker.propTypes = {
24
- avatar: PropTypes.bool,
15
+ return image.assets && image.assets.length > 0 ? image.assets[0].uri : undefined;
25
16
  };
26
17
 
27
18
  export default ImagePicker;
@@ -0,0 +1,24 @@
1
+ import { useState } from 'react';
2
+ import { Image, View } from 'react-native';
3
+
4
+ import style from './style';
5
+
6
+ import type { ImageResponsiveProps } from '../../configs/types';
7
+
8
+ const ImageResponsive = ({ source, style: customStyle, avatar, ...props }: ImageResponsiveProps) => {
9
+ const [width, setWidth] = useState<number>(100);
10
+
11
+ return (
12
+ <View style={[customStyle, avatar ? { borderRadius: width / 2, overflow: 'hidden' } : {}]}>
13
+ <Image
14
+ style={style.responsiveImage}
15
+ source={source}
16
+ resizeMode={avatar ? 'cover' : 'contain'}
17
+ onLayout={({ nativeEvent }) => setWidth(nativeEvent.layout.width)}
18
+ {...props}
19
+ />
20
+ </View>
21
+ );
22
+ };
23
+
24
+ export default ImageResponsive;
@@ -0,0 +1,9 @@
1
+ import { StyleSheet } from 'react-native';
2
+
3
+ export default StyleSheet.create({
4
+ responsiveImage: {
5
+ height: undefined,
6
+ width: undefined,
7
+ flex: 1,
8
+ },
9
+ });
@@ -0,0 +1,36 @@
1
+ import { Ionicons } from '@expo/vector-icons';
2
+ import { useState } from 'react';
3
+ import { ActivityIndicator, Modal, TouchableOpacity, View } from 'react-native';
4
+
5
+ import { Colors } from '../../configs/constants';
6
+ import ImageResponsive from '../ImageResponsive';
7
+ import style from './style';
8
+
9
+ import type { ImageViewerProps } from '../../configs/types';
10
+
11
+ const ImageViewer = ({ source, style: imageStyle, ...props }: ImageViewerProps) => {
12
+ const [loading, setLoading] = useState<boolean>(true);
13
+ const [visible, setVisible] = useState<boolean>(false);
14
+
15
+ return (
16
+ <>
17
+ <TouchableOpacity onPress={() => setVisible(true)}>
18
+ <ImageResponsive source={source} style={imageStyle} {...props} />
19
+ </TouchableOpacity>
20
+
21
+ <Modal animationType="slide" transparent visible={visible} onRequestClose={() => {}}>
22
+ <View style={style.container}>
23
+ <TouchableOpacity onPress={() => setVisible(false)} style={style.closeIcon}>
24
+ <Ionicons name="close-outline" size={24} color={Colors.darkblue} />
25
+ </TouchableOpacity>
26
+
27
+ <ImageResponsive source={source} style={style.body} onLoadEnd={() => setLoading(false)} />
28
+
29
+ {loading && <ActivityIndicator style={style.activityIndicator} />}
30
+ </View>
31
+ </Modal>
32
+ </>
33
+ );
34
+ };
35
+
36
+ export default ImageViewer;