@hoddy-ui/core 1.1.4 → 2.0.35

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 (38) hide show
  1. package/README.md +155 -28
  2. package/next/dist/index.d.mts +113 -41
  3. package/next/dist/index.d.ts +113 -41
  4. package/next/dist/index.js +444 -297
  5. package/next/dist/index.js.map +1 -1
  6. package/next/dist/index.mjs +460 -307
  7. package/next/dist/index.mjs.map +1 -1
  8. package/next/package.json +7 -6
  9. package/package.json +7 -5
  10. package/src/Components/AlertX.tsx +4 -4
  11. package/src/Components/Animators/Animator.tsx +1 -1
  12. package/src/Components/Animators/hooks/useAppState.ts +4 -11
  13. package/src/Components/Animators/hooks/useBlinkAnimation.ts +31 -24
  14. package/src/Components/Animators/hooks/useFadeAnimation.ts +28 -26
  15. package/src/Components/Animators/hooks/useFloatAnimation.ts +67 -57
  16. package/src/Components/Animators/hooks/useGrowAnimation.ts +41 -28
  17. package/src/Components/Animators/hooks/useRollAnimation.ts +77 -57
  18. package/src/Components/Animators/hooks/useSlideAnimation.ts +44 -35
  19. package/src/Components/Animators/hooks/useThrownUpAnimation.ts +43 -50
  20. package/src/Components/Avatar.tsx +13 -7
  21. package/src/Components/Button.tsx +13 -12
  22. package/src/Components/FlashMessage.tsx +119 -42
  23. package/src/Components/FormWrapper.tsx +7 -2
  24. package/src/Components/Grid.tsx +5 -5
  25. package/src/Components/Locator.tsx +10 -2
  26. package/src/Components/OTPInput.tsx +0 -4
  27. package/src/Components/Popup.tsx +161 -83
  28. package/src/Components/SafeAreaView.tsx +11 -11
  29. package/src/Components/SelectMenu.tsx +34 -52
  30. package/src/Components/TextField.tsx +16 -6
  31. package/src/Components/Typography.tsx +19 -16
  32. package/src/config/KeyManager.ts +6 -1
  33. package/src/config/index.ts +37 -2
  34. package/src/hooks.ts +21 -14
  35. package/src/theme/index.tsx +14 -6
  36. package/src/types.ts +12 -3
  37. package/src/utility.ts +11 -0
  38. package/src/Components/Animators/README.md +0 -137
@@ -1,5 +1,13 @@
1
- import { useEffect, useRef } from "react";
2
- import { Animated, Easing, Platform } from "react-native";
1
+ import { useEffect } from "react";
2
+ import { Platform } from "react-native";
3
+ import {
4
+ Easing,
5
+ interpolate,
6
+ useAnimatedStyle,
7
+ useSharedValue,
8
+ withDelay,
9
+ withTiming,
10
+ } from "react-native-reanimated";
3
11
  import useAppState from "./useAppState";
4
12
 
5
13
  interface UseRollAnimationProps {
@@ -13,74 +21,86 @@ interface UseRollAnimationProps {
13
21
  export const useRollAnimation = ({
14
22
  duration = 500,
15
23
  delay = 0,
16
- closeAfter = 2000,
24
+ closeAfter = null,
17
25
  initialTranslateY = 100,
18
26
  initialRotate = "0deg",
19
27
  }: UseRollAnimationProps = {}) => {
20
- const translateY = useRef(new Animated.Value(initialTranslateY)).current;
21
- const rotate = useRef(new Animated.Value(0.5)).current;
28
+ const translateY = useSharedValue(initialTranslateY);
29
+ const rotate = useSharedValue(0);
22
30
  const { isActive } = useAppState();
23
31
 
32
+ const animatedStyle = useAnimatedStyle(() => {
33
+ // Interpolate rotation from 0-1 to initial rotation to 360deg
34
+ const rotateInterpolated = interpolate(
35
+ rotate.value,
36
+ [0, 1],
37
+ [parseFloat(initialRotate.replace("deg", "")), 360]
38
+ );
39
+
40
+ return {
41
+ transform: [
42
+ { translateY: translateY.value },
43
+ { rotate: `${rotateInterpolated}deg` },
44
+ ],
45
+ };
46
+ });
47
+
24
48
  useEffect(() => {
25
49
  if (!isActive && Platform.OS === "ios") {
26
- rotate.stopAnimation();
27
- translateY.stopAnimation();
50
+ translateY.value = initialTranslateY;
51
+ rotate.value = 0;
52
+ return;
28
53
  }
29
- }, [isActive]);
30
54
 
31
- useEffect(() => {
32
- // Start roll-in animation with easing
33
- Animated.parallel([
34
- Animated.timing(translateY, {
35
- toValue: 0.5,
55
+ // Start roll-in animation with easing (parallel animations)
56
+ translateY.value = withDelay(
57
+ delay,
58
+ withTiming(0, {
36
59
  duration,
37
- delay,
38
60
  easing: Easing.out(Easing.ease),
39
- useNativeDriver: true,
40
- }),
41
- Animated.timing(rotate, {
42
- toValue: 1,
43
- duration,
44
- delay,
45
- easing: Easing.out(Easing.ease),
46
- useNativeDriver: true,
47
- }),
48
- ]).start(() => {
49
- if (closeAfter) {
50
- setTimeout(() => {
51
- Animated.parallel([
52
- Animated.timing(translateY, {
53
- toValue: initialTranslateY,
54
- duration,
55
- easing: Easing.out(Easing.ease),
56
- useNativeDriver: true,
57
- }),
58
- Animated.timing(rotate, {
59
- toValue: 0,
60
- duration,
61
- easing: Easing.out(Easing.ease),
62
- useNativeDriver: true,
63
- }),
64
- ]).start();
65
- }, closeAfter);
66
- }
67
- });
61
+ })
62
+ );
68
63
 
69
- return () => {
70
- translateY.stopAnimation();
71
- rotate.stopAnimation();
72
- };
73
- }, [translateY, rotate, duration, delay, closeAfter, initialTranslateY]);
74
-
75
- // Interpolate the rotation value to degrees
76
- const rotateInterpolation = rotate.interpolate({
77
- inputRange: [0, 1],
78
- outputRange: [initialRotate, "360deg"],
79
- });
64
+ rotate.value = withDelay(
65
+ delay,
66
+ withTiming(
67
+ 1,
68
+ {
69
+ duration,
70
+ easing: Easing.out(Easing.ease),
71
+ },
72
+ () => {
73
+ if (closeAfter) {
74
+ translateY.value = withDelay(
75
+ closeAfter,
76
+ withTiming(initialTranslateY, {
77
+ duration,
78
+ easing: Easing.out(Easing.ease),
79
+ })
80
+ );
81
+ rotate.value = withDelay(
82
+ closeAfter,
83
+ withTiming(0, {
84
+ duration,
85
+ easing: Easing.out(Easing.ease),
86
+ })
87
+ );
88
+ }
89
+ }
90
+ )
91
+ );
92
+ }, [
93
+ translateY,
94
+ rotate,
95
+ duration,
96
+ delay,
97
+ closeAfter,
98
+ initialTranslateY,
99
+ initialRotate,
100
+ isActive,
101
+ ]);
80
102
 
81
103
  return {
82
- animatedStyle: {
83
- transform: [{ translateY }, { rotate: rotateInterpolation }],
84
- },
104
+ animatedStyle,
85
105
  };
86
106
  };
@@ -1,5 +1,12 @@
1
- import { useEffect, useRef } from "react";
2
- import { Animated, Dimensions, Easing, Platform } from "react-native";
1
+ import { useEffect } from "react";
2
+ import { Dimensions, Platform } from "react-native";
3
+ import {
4
+ Easing,
5
+ useAnimatedStyle,
6
+ useSharedValue,
7
+ withDelay,
8
+ withTiming,
9
+ } from "react-native-reanimated";
3
10
  import useAppState from "./useAppState";
4
11
 
5
12
  const { width, height } = Dimensions.get("window");
@@ -34,55 +41,57 @@ export const useSlideAnimation = ({
34
41
  closeAfter,
35
42
  initialValue,
36
43
  }: UseSlideAnimationProps = {}) => {
37
- const translateValue = useRef(new Animated.Value(0)).current;
44
+ const translateValue = useSharedValue(0);
38
45
  const { isActive } = useAppState();
39
46
 
47
+ const animatedStyle = useAnimatedStyle(() => {
48
+ const slideStyle =
49
+ direction === "up" || direction === "down"
50
+ ? { transform: [{ translateY: translateValue.value }] }
51
+ : { transform: [{ translateX: translateValue.value }] };
52
+
53
+ return slideStyle;
54
+ });
55
+
40
56
  useEffect(() => {
41
57
  if (!isActive && Platform.OS === "ios") {
42
- translateValue.stopAnimation();
58
+ const initialPosition = initialValue || getInitialPosition(direction);
59
+ translateValue.value = initialPosition;
60
+ return;
43
61
  }
44
- }, [isActive]);
45
62
 
46
- useEffect(() => {
47
63
  const initialPosition = initialValue || getInitialPosition(direction);
48
- translateValue.setValue(initialPosition);
64
+ translateValue.value = initialPosition;
49
65
 
50
66
  // Slide-in animation with ease-out effect
51
- Animated.timing(translateValue, {
52
- toValue: 0,
53
- duration,
67
+ translateValue.value = withDelay(
54
68
  delay,
55
- easing: Easing.out(Easing.ease),
56
- useNativeDriver: true,
57
- }).start();
69
+ withTiming(0, {
70
+ duration,
71
+ easing: Easing.out(Easing.ease),
72
+ })
73
+ );
58
74
 
59
75
  if (closeAfter) {
60
- const timer = setTimeout(() => {
61
- Animated.timing(translateValue, {
62
- toValue: initialPosition,
76
+ translateValue.value = withDelay(
77
+ closeAfter + duration + delay,
78
+ withTiming(initialPosition, {
63
79
  duration,
64
80
  easing: Easing.out(Easing.ease),
65
- useNativeDriver: true,
66
- }).start();
67
- }, closeAfter + duration + delay);
68
-
69
- return () => {
70
- translateValue.stopAnimation();
71
- clearTimeout(timer);
72
- };
81
+ })
82
+ );
73
83
  }
74
-
75
- return () => {
76
- translateValue.stopAnimation();
77
- };
78
- }, [translateValue, duration, delay, direction, closeAfter]);
79
-
80
- const slideStyle =
81
- direction === "up" || direction === "down"
82
- ? { transform: [{ translateY: translateValue }] }
83
- : { transform: [{ translateX: translateValue }] };
84
+ }, [
85
+ translateValue,
86
+ duration,
87
+ delay,
88
+ direction,
89
+ closeAfter,
90
+ initialValue,
91
+ isActive,
92
+ ]);
84
93
 
85
94
  return {
86
- animatedStyle: slideStyle,
95
+ animatedStyle,
87
96
  };
88
97
  };
@@ -1,5 +1,12 @@
1
1
  import { useEffect, useRef } from "react";
2
- import { Animated, Platform } from "react-native";
2
+ import { Platform } from "react-native";
3
+ import {
4
+ useAnimatedStyle,
5
+ useSharedValue,
6
+ withDelay,
7
+ withSpring,
8
+ withTiming,
9
+ } from "react-native-reanimated";
3
10
  import useAppState from "./useAppState";
4
11
 
5
12
  interface UseThrownUpAnimationProps {
@@ -9,74 +16,60 @@ interface UseThrownUpAnimationProps {
9
16
 
10
17
  export const useThrownUpAnimation = ({
11
18
  delay = 0,
12
- closeAfter = 3000,
19
+ closeAfter = null,
13
20
  }: UseThrownUpAnimationProps = {}) => {
14
- const translateY = useRef(new Animated.Value(600)).current;
15
- const opacity = useRef(new Animated.Value(0)).current;
21
+ const translateY = useSharedValue(600);
22
+ const opacity = useSharedValue(0);
16
23
  const isUnmounting = useRef(false);
17
24
  const { isActive } = useAppState();
18
25
 
26
+ const animatedStyle = useAnimatedStyle(() => {
27
+ return {
28
+ transform: [{ translateY: translateY.value }],
29
+ opacity: opacity.value,
30
+ };
31
+ });
32
+
19
33
  useEffect(() => {
20
34
  if (!isActive && Platform.OS === "ios") {
21
- translateY.stopAnimation();
22
- opacity.stopAnimation();
35
+ translateY.value = 600;
36
+ opacity.value = 0;
37
+ return;
23
38
  }
24
- }, [isActive]);
25
39
 
26
- useEffect(() => {
27
40
  // Animate up and fade in when component mounts
28
- Animated.parallel([
29
- Animated.spring(translateY, {
30
- toValue: 0,
41
+ translateY.value = withDelay(
42
+ delay,
43
+ withSpring(0, {
31
44
  velocity: 1,
32
- tension: 0.001,
33
- friction: 2,
34
- useNativeDriver: true,
35
- delay,
36
- }),
37
- Animated.timing(opacity, {
38
- toValue: 1,
39
- duration: 500,
40
- useNativeDriver: true,
41
- delay,
42
- }),
43
- ]).start();
45
+ stiffness: 100,
46
+ damping: 15,
47
+ })
48
+ );
49
+
50
+ opacity.value = withDelay(delay, withTiming(1, { duration: 500 }));
44
51
 
45
52
  // Start timer to animate out after duration
46
- let timer: NodeJS.Timeout | null = null;
47
53
  if (closeAfter) {
48
- timer = setTimeout(() => {
49
- if (!isUnmounting.current) {
50
- Animated.parallel([
51
- Animated.spring(translateY, {
52
- toValue: 800,
53
- velocity: 1,
54
- tension: 10,
55
- friction: 7,
56
- useNativeDriver: true,
57
- }),
58
- Animated.timing(opacity, {
59
- toValue: 0,
60
- duration: 500,
61
- useNativeDriver: true,
62
- }),
63
- ]).start();
64
- }
65
- }, closeAfter);
54
+ translateY.value = withDelay(
55
+ closeAfter,
56
+ withSpring(800, {
57
+ velocity: 1,
58
+ stiffness: 200,
59
+ damping: 20,
60
+ })
61
+ );
62
+ opacity.value = withDelay(closeAfter, withTiming(0, { duration: 500 }));
66
63
  }
67
64
 
68
65
  return () => {
69
- if (timer) clearTimeout(timer);
70
- translateY.stopAnimation();
71
- opacity.stopAnimation();
66
+ translateY.value = 600;
67
+ opacity.value = 0;
72
68
  isUnmounting.current = true;
73
69
  };
74
- }, [translateY, opacity, delay, closeAfter]);
70
+ }, [translateY, opacity, delay, closeAfter, isActive]);
75
71
 
76
72
  return {
77
- animatedStyle: {
78
- transform: [{ translateY }],
79
- opacity,
80
- },
73
+ animatedStyle,
81
74
  };
82
75
  };
@@ -1,7 +1,7 @@
1
1
  import { AntDesign } from "@expo/vector-icons";
2
- import React from "react";
2
+ import React, { useState } from "react";
3
3
  import { Image, View } from "react-native";
4
- import { ScaledSheet } from "react-native-size-matters";
4
+ import { ms, ScaledSheet } from "react-native-size-matters";
5
5
  import { useColors } from "../hooks";
6
6
  import { AvatarProps } from "../types";
7
7
  import Typography from "./Typography";
@@ -15,11 +15,12 @@ const Avatar: React.FC<AvatarProps> = ({
15
15
  style = {},
16
16
  }) => {
17
17
  const colors = useColors();
18
+ const [imageError, setImageError] = useState(false);
18
19
  const styles: any = ScaledSheet.create({
19
20
  root: {
20
21
  borderRadius: 150,
21
- height: size + "@ms",
22
- width: size + "@ms",
22
+ height: ms(size),
23
+ width: ms(size),
23
24
  alignItems: "center",
24
25
  justifyContent: "center",
25
26
  overflow: "hidden",
@@ -27,7 +28,7 @@ const Avatar: React.FC<AvatarProps> = ({
27
28
  borderColor: variant === "outlined" ? "#fff" : "#0000",
28
29
  backgroundColor:
29
30
  variant === "outlined"
30
- ? null
31
+ ? undefined
31
32
  : label
32
33
  ? colors[color].main
33
34
  : colors.white[4],
@@ -41,8 +42,13 @@ const Avatar: React.FC<AvatarProps> = ({
41
42
 
42
43
  return (
43
44
  <View style={styles.root}>
44
- {source ? (
45
- <Image resizeMode="cover" style={styles.image} source={source} />
45
+ {source && !imageError ? (
46
+ <Image
47
+ resizeMode="cover"
48
+ style={styles.image}
49
+ source={source}
50
+ onError={() => setImageError(true)}
51
+ />
46
52
  ) : label ? (
47
53
  <Typography style={{ color: colors[color].text }}>
48
54
  {label[0]}
@@ -1,17 +1,18 @@
1
1
  import { Ionicons, MaterialIcons } from "@expo/vector-icons";
2
2
  import React, { forwardRef } from "react";
3
3
  import { ActivityIndicator, Text, TouchableOpacity } from "react-native";
4
- import { ScaledSheet, moderateScale } from "react-native-size-matters";
4
+ import { ScaledSheet, moderateScale, ms } from "react-native-size-matters";
5
5
  import { getConfig } from "../config/KeyManager";
6
6
  import { useColors, useTheme } from "../hooks";
7
7
  import { ButtonProps, IconButtonProps, LinkButtonProps } from "../types";
8
+ import { getFontFamily } from "../utility";
8
9
 
9
10
  export const LinkButton: React.FC<LinkButtonProps> = ({
10
11
  title,
11
12
  style = {},
12
13
  color = "blue",
13
14
  fontSize = 12,
14
- fontWeight = "400",
15
+ fontWeight = 400,
15
16
  disabled,
16
17
  onPress = () => {},
17
18
  }) => {
@@ -20,8 +21,8 @@ export const LinkButton: React.FC<LinkButtonProps> = ({
20
21
  const styles: any = ScaledSheet.create({
21
22
  text: {
22
23
  fontSize: moderateScale(fontSize),
23
- fontWeight: fontWeight,
24
- fontFamily: getConfig().DEFAULT_FONT_FAMILY || "System",
24
+ fontWeight: fontWeight.toString() as any,
25
+ fontFamily: getFontFamily(fontWeight),
25
26
  color: disabled ? "#777" : colors[color].main,
26
27
  },
27
28
  });
@@ -51,7 +52,7 @@ export const IconButton: React.FC<IconButtonProps> = ({
51
52
  container: {
52
53
  alignSelf: "flex-start",
53
54
  flexGrow: 0,
54
- backgroundColor: bg ? bgColor : elevation! > 0 ? bgColor : null,
55
+ backgroundColor: bg ? bgColor : elevation! > 0 ? bgColor : undefined,
55
56
  padding: "5@ms",
56
57
  shadowColor: "#000",
57
58
  shadowOpacity: 0.1,
@@ -59,8 +60,8 @@ export const IconButton: React.FC<IconButtonProps> = ({
59
60
  height: 1,
60
61
  width: 0,
61
62
  },
62
- height: bg ? size + 20 + "@ms" : undefined,
63
- width: bg ? size + 20 + "@ms" : undefined,
63
+ height: bg ? ms(size + 20) : undefined,
64
+ width: bg ? ms(size + 20) : undefined,
64
65
  alignItems: "center",
65
66
  justifyContent: "center",
66
67
  shadowRadius: elevation,
@@ -118,7 +119,7 @@ const Button: React.FC<ButtonProps> = forwardRef(
118
119
  justifyContent: "center",
119
120
  backgroundColor:
120
121
  variant === "text" || variant === "outlined"
121
- ? null
122
+ ? undefined
122
123
  : translucent
123
124
  ? translucent === "dark"
124
125
  ? colors.white[3] + "22"
@@ -131,7 +132,7 @@ const Button: React.FC<ButtonProps> = forwardRef(
131
132
  borderRadius: rounded ? 30 : 10,
132
133
  elevation: variant === "text" ? 0 : elevation,
133
134
  paddingVertical:
134
- size === "small" ? 8 : size === "large" ? "15@ms" : "13@ms",
135
+ size === "small" ? 8 : size === "large" ? "15@mvs" : "13@mvs",
135
136
  paddingHorizontal: size === "small" ? "10@ms" : "18@ms",
136
137
  borderColor: colors[color].main,
137
138
  borderWidth: variant === "outlined" ? 1 : 0,
@@ -143,7 +144,7 @@ const Button: React.FC<ButtonProps> = forwardRef(
143
144
  width: 0,
144
145
  },
145
146
  shadowOpacity: variant === "text" ? 0 : 0.3,
146
- width: fullWidth ? "100%" : null,
147
+ width: fullWidth ? "100%" : undefined,
147
148
  ...style,
148
149
  },
149
150
  text: {
@@ -155,8 +156,8 @@ const Button: React.FC<ButtonProps> = forwardRef(
155
156
  variant === "text" || variant === "outlined" ? "main" : "text"
156
157
  ],
157
158
  fontWeight: variant === "outlined" ? "700" : "500",
158
- fontSize: size === "small" ? "12@ms" : "16@ms",
159
- fontFamily: getConfig().DEFAULT_FONT_FAMILY || "System",
159
+ fontSize: size === "small" ? "12@ms" : "13@ms",
160
+ fontFamily: getFontFamily(variant === "outlined" ? 700 : 500),
160
161
  },
161
162
  });
162
163