@hoddy-ui/core 2.0.0 → 2.0.36
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +60 -12
- package/next/dist/index.d.mts +113 -41
- package/next/dist/index.d.ts +113 -41
- package/next/dist/index.js +444 -297
- package/next/dist/index.js.map +1 -1
- package/next/dist/index.mjs +460 -307
- package/next/dist/index.mjs.map +1 -1
- package/next/package.json +4 -3
- package/package.json +2 -2
- package/src/Components/AlertX.tsx +4 -4
- package/src/Components/Animators/hooks/useFadeAnimation.ts +1 -3
- package/src/Components/Animators/hooks/useFloatAnimation.ts +9 -9
- package/src/Components/Animators/hooks/useGrowAnimation.ts +5 -4
- package/src/Components/Animators/hooks/useRollAnimation.ts +10 -6
- package/src/Components/Animators/hooks/useSlideAnimation.ts +5 -8
- package/src/Components/Animators/hooks/useThrownUpAnimation.ts +9 -12
- package/src/Components/Avatar.tsx +13 -7
- package/src/Components/Button.tsx +13 -12
- package/src/Components/FlashMessage.tsx +119 -42
- package/src/Components/FormWrapper.tsx +7 -2
- package/src/Components/Grid.tsx +5 -5
- package/src/Components/Locator.tsx +10 -2
- package/src/Components/Popup.tsx +95 -34
- package/src/Components/SafeAreaView.tsx +11 -11
- package/src/Components/SelectMenu.tsx +34 -52
- package/src/Components/TextField.tsx +16 -6
- package/src/Components/Typography.tsx +19 -16
- package/src/config/KeyManager.ts +6 -1
- package/src/config/index.ts +37 -2
- package/src/hooks.ts +21 -14
- package/src/theme/index.tsx +14 -6
- package/src/types.ts +10 -4
- package/src/utility.ts +11 -0
package/next/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@hoddy-ui/next",
|
|
3
|
-
"version": "2.0.
|
|
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,8 +19,9 @@
|
|
|
19
19
|
},
|
|
20
20
|
"peerDependencies": {
|
|
21
21
|
"@expo/vector-icons": ">=13.0.0",
|
|
22
|
+
"@react-native-async-storage/async-storage": ">=1.18.1",
|
|
22
23
|
"@types/react": ">=18.2.6",
|
|
23
|
-
"@types/react-native": "
|
|
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",
|
|
@@ -28,6 +29,7 @@
|
|
|
28
29
|
"expo-system-ui": ">=2.2.1",
|
|
29
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
34
|
"typescript": ">=5.0.4"
|
|
33
35
|
},
|
|
@@ -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": "2.0.
|
|
3
|
+
"version": "2.0.36",
|
|
4
4
|
"description": "Core rich react native components written in typescript",
|
|
5
5
|
"main": "index.ts",
|
|
6
6
|
"repository": {
|
|
@@ -13,6 +13,7 @@
|
|
|
13
13
|
"private": false,
|
|
14
14
|
"peerDependencies": {
|
|
15
15
|
"@expo/vector-icons": ">=13.0.0",
|
|
16
|
+
"@react-native-async-storage/async-storage": ">=1.18.1",
|
|
16
17
|
"@react-navigation/native": ">=6.1.6",
|
|
17
18
|
"@types/react": ">=18.2.6",
|
|
18
19
|
"@types/react-native": ">=0.72.0",
|
|
@@ -34,7 +35,6 @@
|
|
|
34
35
|
"kinghoddy"
|
|
35
36
|
],
|
|
36
37
|
"dependencies": {
|
|
37
|
-
"@react-native-async-storage/async-storage": "^1.18.1",
|
|
38
38
|
"react-native-size-matters": "^0.4.2"
|
|
39
39
|
},
|
|
40
40
|
"publishConfig": {
|
|
@@ -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
|
|
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
|
|
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={
|
|
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}
|
|
@@ -40,9 +40,7 @@ export const useFadeAnimation = ({
|
|
|
40
40
|
withTiming(1, { duration }, () => {
|
|
41
41
|
if (closeAfter) {
|
|
42
42
|
// Schedule fade-out after closeAfter duration
|
|
43
|
-
|
|
44
|
-
opacity.value = withTiming(0, { duration });
|
|
45
|
-
}, closeAfter);
|
|
43
|
+
opacity.value = withDelay(closeAfter, withTiming(0, { duration }));
|
|
46
44
|
}
|
|
47
45
|
})
|
|
48
46
|
);
|
|
@@ -64,11 +64,6 @@ export const useFloatAnimation = ({
|
|
|
64
64
|
}
|
|
65
65
|
};
|
|
66
66
|
|
|
67
|
-
const stopFloating = () => {
|
|
68
|
-
isFloating.current = false;
|
|
69
|
-
translateY.value = withTiming(0, { duration: 200 });
|
|
70
|
-
};
|
|
71
|
-
|
|
72
67
|
useEffect(() => {
|
|
73
68
|
if (!isActive && Platform.OS === "ios") {
|
|
74
69
|
opacity.value = 0;
|
|
@@ -84,10 +79,15 @@ export const useFloatAnimation = ({
|
|
|
84
79
|
startFloating();
|
|
85
80
|
|
|
86
81
|
if (closeAfter) {
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
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
91
|
}
|
|
92
92
|
})
|
|
93
93
|
);
|
|
@@ -48,12 +48,13 @@ export const useGrowAnimation = ({
|
|
|
48
48
|
},
|
|
49
49
|
() => {
|
|
50
50
|
if (closeAfter) {
|
|
51
|
-
|
|
52
|
-
|
|
51
|
+
scale.value = withDelay(
|
|
52
|
+
closeAfter,
|
|
53
|
+
withTiming(initialScale, {
|
|
53
54
|
duration,
|
|
54
55
|
easing: Easing.out(Easing.ease),
|
|
55
|
-
})
|
|
56
|
-
|
|
56
|
+
})
|
|
57
|
+
);
|
|
57
58
|
}
|
|
58
59
|
}
|
|
59
60
|
)
|
|
@@ -71,16 +71,20 @@ export const useRollAnimation = ({
|
|
|
71
71
|
},
|
|
72
72
|
() => {
|
|
73
73
|
if (closeAfter) {
|
|
74
|
-
|
|
75
|
-
|
|
74
|
+
translateY.value = withDelay(
|
|
75
|
+
closeAfter,
|
|
76
|
+
withTiming(initialTranslateY, {
|
|
76
77
|
duration,
|
|
77
78
|
easing: Easing.out(Easing.ease),
|
|
78
|
-
})
|
|
79
|
-
|
|
79
|
+
})
|
|
80
|
+
);
|
|
81
|
+
rotate.value = withDelay(
|
|
82
|
+
closeAfter,
|
|
83
|
+
withTiming(0, {
|
|
80
84
|
duration,
|
|
81
85
|
easing: Easing.out(Easing.ease),
|
|
82
|
-
})
|
|
83
|
-
|
|
86
|
+
})
|
|
87
|
+
);
|
|
84
88
|
}
|
|
85
89
|
}
|
|
86
90
|
)
|
|
@@ -73,16 +73,13 @@ export const useSlideAnimation = ({
|
|
|
73
73
|
);
|
|
74
74
|
|
|
75
75
|
if (closeAfter) {
|
|
76
|
-
|
|
77
|
-
|
|
76
|
+
translateValue.value = withDelay(
|
|
77
|
+
closeAfter + duration + delay,
|
|
78
|
+
withTiming(initialPosition, {
|
|
78
79
|
duration,
|
|
79
80
|
easing: Easing.out(Easing.ease),
|
|
80
|
-
})
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
return () => {
|
|
84
|
-
clearTimeout(timer);
|
|
85
|
-
};
|
|
81
|
+
})
|
|
82
|
+
);
|
|
86
83
|
}
|
|
87
84
|
}, [
|
|
88
85
|
translateValue,
|
|
@@ -50,22 +50,19 @@ export const useThrownUpAnimation = ({
|
|
|
50
50
|
opacity.value = withDelay(delay, withTiming(1, { duration: 500 }));
|
|
51
51
|
|
|
52
52
|
// Start timer to animate out after duration
|
|
53
|
-
let timer: NodeJS.Timeout | null = null;
|
|
54
53
|
if (closeAfter) {
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
}, 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 }));
|
|
65
63
|
}
|
|
66
64
|
|
|
67
65
|
return () => {
|
|
68
|
-
if (timer) clearTimeout(timer);
|
|
69
66
|
translateY.value = 600;
|
|
70
67
|
opacity.value = 0;
|
|
71
68
|
isUnmounting.current = true;
|
|
@@ -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
|
|
22
|
-
width: size
|
|
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
|
-
?
|
|
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
|
|
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 =
|
|
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:
|
|
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 :
|
|
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
|
|
63
|
-
width: bg ? size + 20
|
|
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
|
-
?
|
|
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@
|
|
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%" :
|
|
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" : "
|
|
159
|
-
fontFamily:
|
|
159
|
+
fontSize: size === "small" ? "12@ms" : "13@ms",
|
|
160
|
+
fontFamily: getFontFamily(variant === "outlined" ? 700 : 500),
|
|
160
161
|
},
|
|
161
162
|
});
|
|
162
163
|
|
|
@@ -1,47 +1,111 @@
|
|
|
1
|
-
import React, { useEffect, useState } from "react";
|
|
2
|
-
import {
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
1
|
+
import React, { useEffect, useRef, useState } from "react";
|
|
2
|
+
import { TouchableOpacity, View } from "react-native";
|
|
3
|
+
import Animated, {
|
|
4
|
+
runOnJS,
|
|
5
|
+
useAnimatedStyle,
|
|
6
|
+
useSharedValue,
|
|
7
|
+
withTiming,
|
|
8
|
+
} from "react-native-reanimated";
|
|
8
9
|
import { useSafeAreaInsets } from "react-native-safe-area-context";
|
|
9
10
|
import { ScaledSheet } from "react-native-size-matters";
|
|
10
11
|
import { useColors } from "../hooks";
|
|
11
|
-
import { MaterialIcons } from "@expo/vector-icons";
|
|
12
12
|
import { FlashMessageProps } from "../types";
|
|
13
13
|
import Typography from "./Typography";
|
|
14
14
|
|
|
15
|
-
|
|
15
|
+
// Event-based API to decouple the trigger from the component instance
|
|
16
|
+
type FlashListener = (msg: FlashMessageProps) => void;
|
|
17
|
+
const flashListeners = new Set<FlashListener>();
|
|
18
|
+
|
|
19
|
+
export const showFlashMessage = (msg: FlashMessageProps) => {
|
|
20
|
+
flashListeners.forEach((listener) => listener(msg));
|
|
21
|
+
};
|
|
22
|
+
|
|
23
|
+
const subscribeToFlashMessages = (listener: FlashListener) => {
|
|
24
|
+
flashListeners.add(listener);
|
|
25
|
+
return () => {
|
|
26
|
+
flashListeners.delete(listener);
|
|
27
|
+
};
|
|
28
|
+
};
|
|
16
29
|
|
|
17
30
|
const FlashMessage: React.FC = () => {
|
|
18
31
|
const { top } = useSafeAreaInsets();
|
|
19
32
|
const [message, setMessage] = useState<null | FlashMessageProps>(null);
|
|
20
|
-
const [show, setShow] = useState(false);
|
|
21
33
|
const colors = useColors();
|
|
22
34
|
const type = message?.type || "success";
|
|
35
|
+
const timeoutRef = useRef<NodeJS.Timeout | null>(null);
|
|
36
|
+
|
|
37
|
+
// Animated values
|
|
38
|
+
const translateY = useSharedValue(-200);
|
|
39
|
+
const opacity = useSharedValue(0);
|
|
40
|
+
|
|
41
|
+
const hideMessage = () => {
|
|
42
|
+
setMessage(null);
|
|
43
|
+
};
|
|
44
|
+
|
|
45
|
+
const closeMessage = () => {
|
|
46
|
+
// Clear existing timeout if any
|
|
47
|
+
if (timeoutRef.current) {
|
|
48
|
+
clearTimeout(timeoutRef.current);
|
|
49
|
+
timeoutRef.current = null;
|
|
50
|
+
}
|
|
23
51
|
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
setTimeout(() => {
|
|
31
|
-
setShow(false);
|
|
32
|
-
setTimeout(() => {
|
|
33
|
-
setMessage(null);
|
|
34
|
-
}, 500);
|
|
35
|
-
}, msg.duration || 3000);
|
|
52
|
+
// Animate out immediately
|
|
53
|
+
translateY.value = withTiming(-200, { duration: 300 });
|
|
54
|
+
opacity.value = withTiming(0, { duration: 300 }, () => {
|
|
55
|
+
runOnJS(hideMessage)();
|
|
56
|
+
});
|
|
36
57
|
};
|
|
58
|
+
|
|
37
59
|
useEffect(() => {
|
|
38
|
-
|
|
39
|
-
|
|
60
|
+
const listener: FlashListener = (msg) => {
|
|
61
|
+
// Clear existing timeout if any
|
|
62
|
+
if (timeoutRef.current) {
|
|
63
|
+
clearTimeout(timeoutRef.current);
|
|
64
|
+
timeoutRef.current = null;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
// Reset position immediately before starting new animation
|
|
68
|
+
translateY.value = -200;
|
|
69
|
+
opacity.value = 0;
|
|
70
|
+
|
|
71
|
+
setMessage(msg);
|
|
72
|
+
|
|
73
|
+
// Animate in
|
|
74
|
+
translateY.value = withTiming(0, { duration: 300 });
|
|
75
|
+
opacity.value = withTiming(1, { duration: 300 });
|
|
76
|
+
|
|
77
|
+
// Animate out after duration
|
|
78
|
+
const duration = msg.duration || 3000;
|
|
79
|
+
timeoutRef.current = setTimeout(() => {
|
|
80
|
+
translateY.value = withTiming(-200, { duration: 300 });
|
|
81
|
+
opacity.value = withTiming(0, { duration: 300 }, () => {
|
|
82
|
+
runOnJS(hideMessage)();
|
|
83
|
+
});
|
|
84
|
+
timeoutRef.current = null;
|
|
85
|
+
}, duration);
|
|
86
|
+
};
|
|
87
|
+
|
|
88
|
+
const unsubscribe = subscribeToFlashMessages(listener);
|
|
89
|
+
return () => {
|
|
90
|
+
if (timeoutRef.current) {
|
|
91
|
+
clearTimeout(timeoutRef.current);
|
|
92
|
+
timeoutRef.current = null;
|
|
93
|
+
}
|
|
94
|
+
unsubscribe();
|
|
95
|
+
};
|
|
96
|
+
}, []);
|
|
97
|
+
|
|
98
|
+
const animatedStyle = useAnimatedStyle(() => {
|
|
99
|
+
return {
|
|
100
|
+
transform: [{ translateY: translateY.value }],
|
|
101
|
+
opacity: opacity.value,
|
|
102
|
+
};
|
|
103
|
+
});
|
|
40
104
|
|
|
41
105
|
const styles = ScaledSheet.create({
|
|
42
106
|
root: {
|
|
43
107
|
position: "absolute",
|
|
44
|
-
top:
|
|
108
|
+
top: 0,
|
|
45
109
|
zIndex: 1000,
|
|
46
110
|
left: 0,
|
|
47
111
|
paddingTop: top + 10,
|
|
@@ -64,33 +128,46 @@ const FlashMessage: React.FC = () => {
|
|
|
64
128
|
},
|
|
65
129
|
});
|
|
66
130
|
|
|
131
|
+
if (!message) return null;
|
|
132
|
+
|
|
67
133
|
return (
|
|
68
|
-
<View style={styles.root}>
|
|
69
|
-
<
|
|
70
|
-
<View style={{
|
|
71
|
-
{
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
134
|
+
<Animated.View style={[styles.root, animatedStyle]}>
|
|
135
|
+
<TouchableOpacity onPress={closeMessage} activeOpacity={0.9}>
|
|
136
|
+
<View style={{ flexDirection: "row" }}>
|
|
137
|
+
<View style={{ flex: 1, marginRight: 10 }}>
|
|
138
|
+
{message?.title && (
|
|
139
|
+
<Typography
|
|
140
|
+
variant="h6"
|
|
141
|
+
fontWeight={600}
|
|
142
|
+
gutterBottom={3}
|
|
143
|
+
style={{ color: "#fff" }}
|
|
144
|
+
>
|
|
145
|
+
{message?.title}
|
|
146
|
+
</Typography>
|
|
147
|
+
)}
|
|
148
|
+
<Typography style={{ color: "#fff" }}>
|
|
149
|
+
{message?.message}
|
|
79
150
|
</Typography>
|
|
80
|
-
|
|
81
|
-
<
|
|
151
|
+
</View>
|
|
152
|
+
{/* <MaterialIcons color="#fff" size={36} name="error-outline" /> */}
|
|
82
153
|
</View>
|
|
83
|
-
|
|
84
|
-
</View>
|
|
154
|
+
</TouchableOpacity>
|
|
85
155
|
|
|
86
156
|
{message?.actions?.map((cur, i) => (
|
|
87
|
-
<TouchableOpacity
|
|
157
|
+
<TouchableOpacity
|
|
158
|
+
key={i}
|
|
159
|
+
style={styles.action}
|
|
160
|
+
onPress={() => {
|
|
161
|
+
cur.onPress?.();
|
|
162
|
+
closeMessage();
|
|
163
|
+
}}
|
|
164
|
+
>
|
|
88
165
|
<Typography fontWeight={700} style={{ color: "#fff" }}>
|
|
89
166
|
{cur.title}
|
|
90
167
|
</Typography>
|
|
91
168
|
</TouchableOpacity>
|
|
92
169
|
))}
|
|
93
|
-
</View>
|
|
170
|
+
</Animated.View>
|
|
94
171
|
);
|
|
95
172
|
};
|
|
96
173
|
|
|
@@ -8,6 +8,7 @@ import {
|
|
|
8
8
|
} from "react-native";
|
|
9
9
|
import { ScaledSheet } from "react-native-size-matters";
|
|
10
10
|
import { FormWrapperProps } from "../types";
|
|
11
|
+
import { useSafeAreaInsets } from "react-native-safe-area-context";
|
|
11
12
|
export const FormWrapper: React.FC<FormWrapperProps> = ({
|
|
12
13
|
children,
|
|
13
14
|
behavior = Platform.OS === "ios" ? "padding" : "height",
|
|
@@ -17,6 +18,9 @@ export const FormWrapper: React.FC<FormWrapperProps> = ({
|
|
|
17
18
|
style = {},
|
|
18
19
|
onScroll,
|
|
19
20
|
}) => {
|
|
21
|
+
const { bottom } = useSafeAreaInsets();
|
|
22
|
+
|
|
23
|
+
const defaultOffset = Platform.OS === "ios" ? -bottom : -bottom * 2;
|
|
20
24
|
const styles = ScaledSheet.create({
|
|
21
25
|
root: {
|
|
22
26
|
width: "100%",
|
|
@@ -24,13 +28,14 @@ export const FormWrapper: React.FC<FormWrapperProps> = ({
|
|
|
24
28
|
...style,
|
|
25
29
|
},
|
|
26
30
|
});
|
|
31
|
+
|
|
27
32
|
return mode === "static" ? (
|
|
28
33
|
<TouchableWithoutFeedback onPress={Keyboard.dismiss} accessible={false}>
|
|
29
34
|
<KeyboardAvoidingView
|
|
30
35
|
style={styles.root}
|
|
31
36
|
behavior={behavior}
|
|
32
37
|
contentContainerStyle={styles.root}
|
|
33
|
-
keyboardVerticalOffset={keyboardVerticalOffset}
|
|
38
|
+
keyboardVerticalOffset={keyboardVerticalOffset || defaultOffset}
|
|
34
39
|
>
|
|
35
40
|
{children}
|
|
36
41
|
</KeyboardAvoidingView>
|
|
@@ -39,7 +44,7 @@ export const FormWrapper: React.FC<FormWrapperProps> = ({
|
|
|
39
44
|
<KeyboardAvoidingView
|
|
40
45
|
behavior={behavior}
|
|
41
46
|
style={styles.root}
|
|
42
|
-
keyboardVerticalOffset={keyboardVerticalOffset}
|
|
47
|
+
keyboardVerticalOffset={keyboardVerticalOffset || defaultOffset}
|
|
43
48
|
>
|
|
44
49
|
<ScrollView
|
|
45
50
|
onScroll={onScroll}
|
package/src/Components/Grid.tsx
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import { View, Text } from "react-native";
|
|
2
1
|
import React from "react";
|
|
3
|
-
import {
|
|
2
|
+
import { View } from "react-native";
|
|
3
|
+
import { ms, ScaledSheet } from "react-native-size-matters";
|
|
4
4
|
import { GridItemProps, GridProps } from "../types";
|
|
5
5
|
|
|
6
6
|
export const GridItem: React.FC<GridItemProps> = ({
|
|
@@ -12,8 +12,8 @@ export const GridItem: React.FC<GridItemProps> = ({
|
|
|
12
12
|
}) => {
|
|
13
13
|
const styles = ScaledSheet.create({
|
|
14
14
|
gridItem: {
|
|
15
|
-
width: 100 / col
|
|
16
|
-
padding: spacing * 10
|
|
15
|
+
width: `${100 / col}%`,
|
|
16
|
+
padding: ms(spacing * 10),
|
|
17
17
|
alignItems: alignItems,
|
|
18
18
|
},
|
|
19
19
|
});
|
|
@@ -27,7 +27,7 @@ export const Grid: React.FC<GridProps> = ({
|
|
|
27
27
|
const styles = ScaledSheet.create({
|
|
28
28
|
grid: {
|
|
29
29
|
flexWrap: "wrap",
|
|
30
|
-
margin: -spacing * 10
|
|
30
|
+
margin: `${-spacing * 10}@ms`,
|
|
31
31
|
flexDirection: "row",
|
|
32
32
|
},
|
|
33
33
|
});
|