@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
package/next/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@hoddy-ui/next",
3
- "version": "2.0.47",
3
+ "version": "2.0.61",
4
4
  "description": "Core rich react native components written in typescript, with support for expo-router",
5
5
  "main": "./dist/index.js",
6
6
  "module": "./dist/index.mjs",
@@ -19,17 +19,19 @@
19
19
  },
20
20
  "peerDependencies": {
21
21
  "@expo/vector-icons": ">=13.0.0",
22
- "@types/react": "^18.2.6",
23
- "@types/react-native": "^0.72.0",
22
+ "@react-native-async-storage/async-storage": ">=1.18.1",
23
+ "@types/react": ">=18.2.6",
24
+ "@types/react-native": ">=0.72.0",
24
25
  "expo-haptics": ">=12.4.0",
25
26
  "expo-location": ">=15.1.1",
26
27
  "expo-navigation-bar": ">=2.1.1",
27
28
  "expo-router": ">=1.0.0",
28
29
  "expo-system-ui": ">=2.2.1",
29
- "react": "^18.2.0",
30
+ "react": ">=18.2.0",
30
31
  "react-native": ">=0.71.8",
32
+ "react-native-reanimated": ">=3.17.4",
31
33
  "react-native-safe-area-context": ">=4.5.3",
32
- "typescript": "^5.0.4"
34
+ "typescript": ">=5.0.4"
33
35
  },
34
36
  "keywords": [
35
37
  "react-native",
@@ -39,7 +41,6 @@
39
41
  "kinghoddy"
40
42
  ],
41
43
  "dependencies": {
42
- "@react-native-async-storage/async-storage": "^1.18.1",
43
44
  "react-native-size-matters": "^0.4.0"
44
45
  },
45
46
  "devDependencies": {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@hoddy-ui/core",
3
- "version": "1.1.4",
3
+ "version": "2.0.35",
4
4
  "description": "Core rich react native components written in typescript",
5
5
  "main": "index.ts",
6
6
  "repository": {
@@ -13,7 +13,8 @@
13
13
  "private": false,
14
14
  "peerDependencies": {
15
15
  "@expo/vector-icons": ">=13.0.0",
16
- "@react-navigation/native": ">=6.1.7",
16
+ "@react-native-async-storage/async-storage": ">=1.18.1",
17
+ "@react-navigation/native": ">=6.1.6",
17
18
  "@types/react": ">=18.2.6",
18
19
  "@types/react-native": ">=0.72.0",
19
20
  "expo-haptics": ">=12.4.0",
@@ -22,6 +23,7 @@
22
23
  "expo-system-ui": ">=2.2.1",
23
24
  "react": ">=18.2.0",
24
25
  "react-native": ">=0.71.8",
26
+ "react-native-reanimated": ">=3.17.4",
25
27
  "react-native-safe-area-context": ">=4.5.3",
26
28
  "typescript": ">=5.0.4"
27
29
  },
@@ -33,10 +35,10 @@
33
35
  "kinghoddy"
34
36
  ],
35
37
  "dependencies": {
36
- "@react-native-async-storage/async-storage": "^1.18.1",
37
- "react-native-size-matters": "^0.4.0"
38
+ "react-native-size-matters": "^0.4.2"
38
39
  },
39
40
  "publishConfig": {
40
41
  "access": "public"
41
- }
42
+ },
43
+ "devDependencies": {}
42
44
  }
@@ -1,7 +1,7 @@
1
1
  import { MaterialIcons } from "@expo/vector-icons";
2
2
  import React from "react";
3
3
  import { 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 { AlertXProps } from "../types";
7
7
  import Typography from "./Typography";
@@ -16,7 +16,7 @@ const AlertX: React.FC<AlertXProps> = ({
16
16
  }) => {
17
17
  const colors = useColors();
18
18
 
19
- const styles: any = ScaledSheet.create({
19
+ const styles = ScaledSheet.create({
20
20
  container: {
21
21
  padding: 20,
22
22
  paddingTop: 10,
@@ -24,7 +24,7 @@ const AlertX: React.FC<AlertXProps> = ({
24
24
  borderRadius: 8,
25
25
  alignItems: "center",
26
26
  flexDirection: "row",
27
- marginBottom: gutterBottom + "@ms",
27
+ marginBottom: ms(gutterBottom),
28
28
  backgroundColor: colors[type].main + (variant === "contained" ? "" : "3"),
29
29
  },
30
30
  title: {
@@ -35,7 +35,7 @@ const AlertX: React.FC<AlertXProps> = ({
35
35
  },
36
36
  });
37
37
  return (
38
- <View style={{ ...styles.container, ...style }}>
38
+ <View style={[styles.container, style]}>
39
39
  <View style={{ width: "80%" }}>
40
40
  <Typography style={styles.title} gutterBottom={3} fontWeight={700}>
41
41
  {title}
@@ -1,5 +1,5 @@
1
1
  import React, { FC } from "react";
2
- import { Animated } from "react-native";
2
+ import Animated from "react-native-reanimated";
3
3
  import { AnimatorProps } from "../../types";
4
4
  import { useBlinkAnimation } from "./hooks/useBlinkAnimation";
5
5
  import { useFadeAnimation } from "./hooks/useFadeAnimation";
@@ -2,7 +2,7 @@ import { useEffect, useState } from "react";
2
2
  import { AppState, Platform } from "react-native";
3
3
 
4
4
  const useAppState = () => {
5
- const [isActive, setIsActive] = useState(true);
5
+ const [isActive, setIsActive] = useState(AppState.currentState === "active");
6
6
 
7
7
  useEffect(() => {
8
8
  const handleAppStateChange = (nextAppState: string) => {
@@ -10,24 +10,17 @@ const useAppState = () => {
10
10
  };
11
11
 
12
12
  let subscription: any;
13
- let blurSub: any;
14
- let focusSub: any;
15
13
 
16
14
  if (Platform.OS === "android") {
17
- blurSub = AppState.addEventListener("blur", () =>
18
- handleAppStateChange("inactive")
19
- );
20
- focusSub = AppState.addEventListener("focus", () =>
21
- handleAppStateChange("active")
22
- );
15
+ // For Android, use the change event which covers both blur and focus
16
+ subscription = AppState.addEventListener("change", handleAppStateChange);
23
17
  } else {
18
+ // For iOS, use the change event as well
24
19
  subscription = AppState.addEventListener("change", handleAppStateChange);
25
20
  }
26
21
 
27
22
  return () => {
28
23
  subscription?.remove();
29
- blurSub?.remove();
30
- focusSub?.remove();
31
24
  };
32
25
  }, []);
33
26
 
@@ -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
+ useAnimatedStyle,
6
+ useSharedValue,
7
+ withRepeat,
8
+ withSequence,
9
+ withTiming,
10
+ } from "react-native-reanimated";
3
11
  import useAppState from "./useAppState";
4
12
 
5
13
  interface UseBlinkAnimationProps {
@@ -15,57 +23,56 @@ export const useBlinkAnimation = ({
15
23
  minOpacity = 0.5,
16
24
  maxOpacity = 1,
17
25
  }: UseBlinkAnimationProps = {}) => {
18
- const opacity = useRef(new Animated.Value(maxOpacity)).current;
26
+ const opacity = useSharedValue(maxOpacity);
19
27
  const { isActive } = useAppState();
20
- const blinkAnim = useRef<Animated.CompositeAnimation | null>(null);
28
+
29
+ const animatedStyle = useAnimatedStyle(() => {
30
+ return {
31
+ opacity: opacity.value,
32
+ };
33
+ });
21
34
 
22
35
  const startBlinking = () => {
23
- blinkAnim.current = Animated.loop(
24
- Animated.sequence([
25
- Animated.timing(opacity, {
26
- toValue: minOpacity,
36
+ opacity.value = withRepeat(
37
+ withSequence(
38
+ withTiming(minOpacity, {
27
39
  duration: blinkDuration / 2,
28
40
  easing: Easing.inOut(Easing.quad),
29
- useNativeDriver: true,
30
41
  }),
31
- Animated.timing(opacity, {
32
- toValue: maxOpacity,
42
+ withTiming(maxOpacity, {
33
43
  duration: blinkDuration / 2,
34
44
  easing: Easing.inOut(Easing.quad),
35
- useNativeDriver: true,
36
- }),
37
- ])
45
+ })
46
+ ),
47
+ -1,
48
+ false
38
49
  );
39
- blinkAnim.current.start();
40
50
  };
41
51
 
42
52
  useEffect(() => {
43
53
  if (!isActive && Platform.OS === "ios") {
44
- opacity.stopAnimation();
54
+ opacity.value = maxOpacity;
55
+ return;
45
56
  }
46
- }, [isActive]);
47
57
 
48
- useEffect(() => {
49
58
  if (delay > 0) {
50
59
  const timer = setTimeout(() => {
51
60
  startBlinking();
52
61
  }, delay);
53
62
  return () => {
54
63
  clearTimeout(timer);
55
- opacity.stopAnimation();
56
- blinkAnim.current?.stop();
64
+ opacity.value = maxOpacity;
57
65
  };
58
66
  } else {
59
67
  startBlinking();
60
68
  }
61
69
 
62
70
  return () => {
63
- opacity.stopAnimation();
64
- blinkAnim.current?.stop();
71
+ opacity.value = maxOpacity;
65
72
  };
66
- }, [delay, blinkDuration, minOpacity, maxOpacity]);
73
+ }, [delay, blinkDuration, minOpacity, maxOpacity, isActive]);
67
74
 
68
75
  return {
69
- animatedStyle: { opacity },
76
+ animatedStyle,
70
77
  };
71
78
  };
@@ -1,5 +1,11 @@
1
- import { useEffect, useRef } from "react";
2
- import { Animated, Platform } from "react-native";
1
+ import { useEffect } from "react";
2
+ import { Platform } from "react-native";
3
+ import {
4
+ useAnimatedStyle,
5
+ useSharedValue,
6
+ withDelay,
7
+ withTiming,
8
+ } from "react-native-reanimated";
3
9
  import useAppState from "./useAppState";
4
10
 
5
11
  interface UseFadeAnimationProps {
@@ -11,40 +17,36 @@ interface UseFadeAnimationProps {
11
17
  export const useFadeAnimation = ({
12
18
  duration = 1000,
13
19
  delay = 0,
14
- closeAfter = 2000,
20
+ closeAfter = null,
15
21
  }: UseFadeAnimationProps = {}) => {
16
- const opacity = useRef(new Animated.Value(0)).current;
22
+ const opacity = useSharedValue(0);
17
23
  const { isActive } = useAppState();
18
24
 
25
+ const animatedStyle = useAnimatedStyle(() => {
26
+ return {
27
+ opacity: opacity.value,
28
+ };
29
+ });
30
+
19
31
  useEffect(() => {
20
32
  if (!isActive && Platform.OS === "ios") {
21
- opacity.stopAnimation();
33
+ opacity.value = 0;
34
+ return;
22
35
  }
23
- }, [isActive]);
24
36
 
25
- useEffect(() => {
26
37
  // Fade-in animation
27
- Animated.timing(opacity, {
28
- toValue: 1,
29
- duration,
38
+ opacity.value = withDelay(
30
39
  delay,
31
- useNativeDriver: true,
32
- }).start(() => {
33
- if (closeAfter) {
34
- setTimeout(() => {
35
- Animated.timing(opacity, {
36
- toValue: 0,
37
- duration,
38
- useNativeDriver: true,
39
- }).start();
40
- }, closeAfter);
41
- }
42
- });
43
-
44
- return () => opacity.stopAnimation();
45
- }, [opacity, duration, delay, closeAfter]);
40
+ withTiming(1, { duration }, () => {
41
+ if (closeAfter) {
42
+ // Schedule fade-out after closeAfter duration
43
+ opacity.value = withDelay(closeAfter, withTiming(0, { duration }));
44
+ }
45
+ })
46
+ );
47
+ }, [opacity, duration, delay, closeAfter, isActive]);
46
48
 
47
49
  return {
48
- animatedStyle: { opacity },
50
+ animatedStyle,
49
51
  };
50
52
  };
@@ -1,5 +1,14 @@
1
1
  import { useEffect, useRef } from "react";
2
- import { Animated, Easing, Platform } from "react-native";
2
+ import { Platform } from "react-native";
3
+ import {
4
+ Easing,
5
+ useAnimatedStyle,
6
+ useSharedValue,
7
+ withDelay,
8
+ withRepeat,
9
+ withSequence,
10
+ withTiming,
11
+ } from "react-native-reanimated";
3
12
  import useAppState from "./useAppState";
4
13
 
5
14
  interface UseFloatAnimationProps {
@@ -14,76 +23,79 @@ interface UseFloatAnimationProps {
14
23
  export const useFloatAnimation = ({
15
24
  duration = 800,
16
25
  delay = 0,
17
- closeAfter = 2000,
26
+ closeAfter = null,
18
27
  closeDuration = 600,
19
28
  floatDistance = 10,
20
29
  floatDuration = 1200,
21
30
  }: UseFloatAnimationProps = {}) => {
22
- const opacity = useRef(new Animated.Value(0)).current;
23
- const translateY = useRef(new Animated.Value(0)).current;
31
+ const opacity = useSharedValue(0);
32
+ const translateY = useSharedValue(0);
24
33
  const { isActive } = useAppState();
25
- const floatAnim = useRef<Animated.CompositeAnimation | null>(null);
34
+ const isFloating = useRef(false);
35
+
36
+ const animatedStyle = useAnimatedStyle(() => {
37
+ return {
38
+ opacity: opacity.value,
39
+ transform: [{ translateY: translateY.value }],
40
+ };
41
+ });
26
42
 
27
43
  const startFloating = () => {
28
- floatAnim.current = Animated.loop(
29
- Animated.sequence([
30
- Animated.timing(translateY, {
31
- toValue: -floatDistance,
32
- duration: floatDuration / 2,
33
- easing: Easing.inOut(Easing.quad),
34
- useNativeDriver: true,
35
- }),
36
- Animated.timing(translateY, {
37
- toValue: floatDistance,
38
- duration: floatDuration,
39
- easing: Easing.inOut(Easing.quad),
40
- useNativeDriver: true,
41
- }),
42
- Animated.timing(translateY, {
43
- toValue: 0,
44
- duration: floatDuration / 2,
45
- easing: Easing.inOut(Easing.quad),
46
- useNativeDriver: true,
47
- }),
48
- ])
49
- );
50
- floatAnim.current.start();
44
+ if (!isFloating.current) {
45
+ isFloating.current = true;
46
+ translateY.value = withRepeat(
47
+ withSequence(
48
+ withTiming(-floatDistance, {
49
+ duration: floatDuration / 2,
50
+ easing: Easing.inOut(Easing.quad),
51
+ }),
52
+ withTiming(floatDistance, {
53
+ duration: floatDuration,
54
+ easing: Easing.inOut(Easing.quad),
55
+ }),
56
+ withTiming(0, {
57
+ duration: floatDuration / 2,
58
+ easing: Easing.inOut(Easing.quad),
59
+ })
60
+ ),
61
+ -1,
62
+ false
63
+ );
64
+ }
51
65
  };
52
66
 
53
67
  useEffect(() => {
54
68
  if (!isActive && Platform.OS === "ios") {
55
- opacity.stopAnimation();
56
- translateY.stopAnimation();
69
+ opacity.value = 0;
70
+ translateY.value = 0;
71
+ isFloating.current = false;
72
+ return;
57
73
  }
58
- }, [isActive]);
59
74
 
60
- useEffect(() => {
61
75
  // Fade-in
62
- Animated.timing(opacity, {
63
- toValue: 1,
64
- duration,
76
+ opacity.value = withDelay(
65
77
  delay,
66
- useNativeDriver: true,
67
- }).start(() => {
68
- startFloating();
69
-
70
- if (closeAfter) {
71
- setTimeout(() => {
72
- floatAnim.current?.stop();
78
+ withTiming(1, { duration }, () => {
79
+ startFloating();
73
80
 
74
- Animated.timing(opacity, {
75
- toValue: 0,
76
- duration: closeDuration,
77
- useNativeDriver: true,
78
- }).start();
79
- }, closeAfter);
80
- }
81
- });
81
+ if (closeAfter) {
82
+ opacity.value = withDelay(
83
+ closeAfter,
84
+ withTiming(0, { duration: closeDuration })
85
+ );
86
+ translateY.value = withDelay(
87
+ closeAfter,
88
+ withTiming(0, { duration: closeDuration })
89
+ );
90
+ isFloating.current = false;
91
+ }
92
+ })
93
+ );
82
94
 
83
95
  return () => {
84
- opacity.stopAnimation();
85
- translateY.stopAnimation();
86
- floatAnim.current?.stop();
96
+ opacity.value = 0;
97
+ translateY.value = 0;
98
+ isFloating.current = false;
87
99
  };
88
100
  }, [
89
101
  duration,
@@ -92,12 +104,10 @@ export const useFloatAnimation = ({
92
104
  closeDuration,
93
105
  floatDistance,
94
106
  floatDuration,
107
+ isActive,
95
108
  ]);
96
109
 
97
110
  return {
98
- animatedStyle: {
99
- opacity,
100
- transform: [{ translateY }],
101
- },
111
+ animatedStyle,
102
112
  };
103
113
  };
@@ -1,5 +1,12 @@
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
+ useAnimatedStyle,
6
+ useSharedValue,
7
+ withDelay,
8
+ withTiming,
9
+ } from "react-native-reanimated";
3
10
  import useAppState from "./useAppState";
4
11
 
5
12
  interface UseGrowAnimationProps {
@@ -12,43 +19,49 @@ interface UseGrowAnimationProps {
12
19
  export const useGrowAnimation = ({
13
20
  duration = 500,
14
21
  delay = 0,
15
- closeAfter = 2000,
22
+ closeAfter = null,
16
23
  initialScale = 0,
17
24
  }: UseGrowAnimationProps = {}) => {
18
- const scale = useRef(new Animated.Value(initialScale)).current;
25
+ const scale = useSharedValue(initialScale);
19
26
  const { isActive } = useAppState();
20
27
 
28
+ const animatedStyle = useAnimatedStyle(() => {
29
+ return {
30
+ transform: [{ scale: scale.value }],
31
+ };
32
+ });
33
+
21
34
  useEffect(() => {
22
35
  if (!isActive && Platform.OS === "ios") {
23
- scale.stopAnimation();
36
+ scale.value = initialScale;
37
+ return;
24
38
  }
25
- }, [isActive]);
26
39
 
27
- useEffect(() => {
28
40
  // Start grow-in animation with easing
29
- Animated.timing(scale, {
30
- toValue: 1,
31
- duration,
41
+ scale.value = withDelay(
32
42
  delay,
33
- easing: Easing.out(Easing.ease),
34
- useNativeDriver: true,
35
- }).start(() => {
36
- if (closeAfter) {
37
- setTimeout(() => {
38
- Animated.timing(scale, {
39
- toValue: initialScale,
40
- duration,
41
- easing: Easing.out(Easing.ease),
42
- useNativeDriver: true,
43
- }).start();
44
- }, closeAfter);
45
- }
46
- });
47
-
48
- return () => scale.stopAnimation();
49
- }, [scale, duration, delay, closeAfter, initialScale]);
43
+ withTiming(
44
+ 1,
45
+ {
46
+ duration,
47
+ easing: Easing.out(Easing.ease),
48
+ },
49
+ () => {
50
+ if (closeAfter) {
51
+ scale.value = withDelay(
52
+ closeAfter,
53
+ withTiming(initialScale, {
54
+ duration,
55
+ easing: Easing.out(Easing.ease),
56
+ })
57
+ );
58
+ }
59
+ }
60
+ )
61
+ );
62
+ }, [scale, duration, delay, closeAfter, initialScale, isActive]);
50
63
 
51
64
  return {
52
- animatedStyle: { transform: [{ scale }] },
65
+ animatedStyle,
53
66
  };
54
67
  };