@hoddy-ui/core 1.1.0 → 1.1.2
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 +972 -190
- package/index.ts +7 -3
- package/next/dist/index.d.mts +62 -7
- package/next/dist/index.d.ts +62 -7
- package/next/dist/index.js +225 -200
- package/next/dist/index.js.map +1 -1
- package/next/dist/index.mjs +223 -200
- package/next/dist/index.mjs.map +1 -1
- package/next/index.ts +1 -0
- package/next/package.json +1 -1
- package/package.json +1 -1
- package/src/Components/Animators/Animator.tsx +117 -0
- package/src/Components/Animators/README.md +137 -0
- package/src/Components/Animators/hooks/index.ts +8 -0
- package/src/Components/Animators/hooks/useAppState.ts +37 -0
- package/src/Components/Animators/hooks/useBlinkAnimation.ts +71 -0
- package/src/Components/Animators/hooks/useFadeAnimation.ts +50 -0
- package/src/Components/Animators/hooks/useFloatAnimation.ts +103 -0
- package/src/Components/Animators/hooks/useGrowAnimation.ts +54 -0
- package/src/Components/Animators/hooks/useRollAnimation.ts +86 -0
- package/src/Components/Animators/hooks/useSlideAnimation.ts +88 -0
- package/src/Components/Animators/hooks/useThrownUpAnimation.ts +82 -0
- package/src/Components/Locator.tsx +50 -22
- package/src/Components/Popup.tsx +16 -13
- package/src/Components/TextField.tsx +230 -220
- package/src/Components/Typography.tsx +0 -2
- package/src/config/KeyManager.ts +2 -0
- package/src/config/index.ts +2 -0
- package/src/theme/index.tsx +3 -1
- package/src/types.ts +57 -9
- package/src/Components/Animator.tsx +0 -43
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
import { useEffect, useRef } from "react";
|
|
2
|
+
import { Animated, Dimensions, Easing, Platform } from "react-native";
|
|
3
|
+
import useAppState from "./useAppState";
|
|
4
|
+
|
|
5
|
+
const { width, height } = Dimensions.get("window");
|
|
6
|
+
|
|
7
|
+
interface UseSlideAnimationProps {
|
|
8
|
+
duration?: number;
|
|
9
|
+
delay?: number;
|
|
10
|
+
direction?: "up" | "down" | "left" | "right";
|
|
11
|
+
closeAfter?: number | null;
|
|
12
|
+
initialValue?: number;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
const getInitialPosition = (direction: string) => {
|
|
16
|
+
switch (direction) {
|
|
17
|
+
case "up":
|
|
18
|
+
return height;
|
|
19
|
+
case "down":
|
|
20
|
+
return -height;
|
|
21
|
+
case "left":
|
|
22
|
+
return width;
|
|
23
|
+
case "right":
|
|
24
|
+
return -width;
|
|
25
|
+
default:
|
|
26
|
+
return 0;
|
|
27
|
+
}
|
|
28
|
+
};
|
|
29
|
+
|
|
30
|
+
export const useSlideAnimation = ({
|
|
31
|
+
duration = 1000,
|
|
32
|
+
delay = 0,
|
|
33
|
+
direction = "up",
|
|
34
|
+
closeAfter,
|
|
35
|
+
initialValue,
|
|
36
|
+
}: UseSlideAnimationProps = {}) => {
|
|
37
|
+
const translateValue = useRef(new Animated.Value(0)).current;
|
|
38
|
+
const { isActive } = useAppState();
|
|
39
|
+
|
|
40
|
+
useEffect(() => {
|
|
41
|
+
if (!isActive && Platform.OS === "ios") {
|
|
42
|
+
translateValue.stopAnimation();
|
|
43
|
+
}
|
|
44
|
+
}, [isActive]);
|
|
45
|
+
|
|
46
|
+
useEffect(() => {
|
|
47
|
+
const initialPosition = initialValue || getInitialPosition(direction);
|
|
48
|
+
translateValue.setValue(initialPosition);
|
|
49
|
+
|
|
50
|
+
// Slide-in animation with ease-out effect
|
|
51
|
+
Animated.timing(translateValue, {
|
|
52
|
+
toValue: 0,
|
|
53
|
+
duration,
|
|
54
|
+
delay,
|
|
55
|
+
easing: Easing.out(Easing.ease),
|
|
56
|
+
useNativeDriver: true,
|
|
57
|
+
}).start();
|
|
58
|
+
|
|
59
|
+
if (closeAfter) {
|
|
60
|
+
const timer = setTimeout(() => {
|
|
61
|
+
Animated.timing(translateValue, {
|
|
62
|
+
toValue: initialPosition,
|
|
63
|
+
duration,
|
|
64
|
+
easing: Easing.out(Easing.ease),
|
|
65
|
+
useNativeDriver: true,
|
|
66
|
+
}).start();
|
|
67
|
+
}, closeAfter + duration + delay);
|
|
68
|
+
|
|
69
|
+
return () => {
|
|
70
|
+
translateValue.stopAnimation();
|
|
71
|
+
clearTimeout(timer);
|
|
72
|
+
};
|
|
73
|
+
}
|
|
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
|
+
return {
|
|
86
|
+
animatedStyle: slideStyle,
|
|
87
|
+
};
|
|
88
|
+
};
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
import { useEffect, useRef } from "react";
|
|
2
|
+
import { Animated, Platform } from "react-native";
|
|
3
|
+
import useAppState from "./useAppState";
|
|
4
|
+
|
|
5
|
+
interface UseThrownUpAnimationProps {
|
|
6
|
+
delay?: number;
|
|
7
|
+
closeAfter?: number | null;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
export const useThrownUpAnimation = ({
|
|
11
|
+
delay = 0,
|
|
12
|
+
closeAfter = 3000,
|
|
13
|
+
}: UseThrownUpAnimationProps = {}) => {
|
|
14
|
+
const translateY = useRef(new Animated.Value(600)).current;
|
|
15
|
+
const opacity = useRef(new Animated.Value(0)).current;
|
|
16
|
+
const isUnmounting = useRef(false);
|
|
17
|
+
const { isActive } = useAppState();
|
|
18
|
+
|
|
19
|
+
useEffect(() => {
|
|
20
|
+
if (!isActive && Platform.OS === "ios") {
|
|
21
|
+
translateY.stopAnimation();
|
|
22
|
+
opacity.stopAnimation();
|
|
23
|
+
}
|
|
24
|
+
}, [isActive]);
|
|
25
|
+
|
|
26
|
+
useEffect(() => {
|
|
27
|
+
// Animate up and fade in when component mounts
|
|
28
|
+
Animated.parallel([
|
|
29
|
+
Animated.spring(translateY, {
|
|
30
|
+
toValue: 0,
|
|
31
|
+
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();
|
|
44
|
+
|
|
45
|
+
// Start timer to animate out after duration
|
|
46
|
+
let timer: NodeJS.Timeout | null = null;
|
|
47
|
+
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);
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
return () => {
|
|
69
|
+
if (timer) clearTimeout(timer);
|
|
70
|
+
translateY.stopAnimation();
|
|
71
|
+
opacity.stopAnimation();
|
|
72
|
+
isUnmounting.current = true;
|
|
73
|
+
};
|
|
74
|
+
}, [translateY, opacity, delay, closeAfter]);
|
|
75
|
+
|
|
76
|
+
return {
|
|
77
|
+
animatedStyle: {
|
|
78
|
+
transform: [{ translateY }],
|
|
79
|
+
opacity,
|
|
80
|
+
},
|
|
81
|
+
};
|
|
82
|
+
};
|
|
@@ -12,11 +12,14 @@ import { LocatorProps } from "../types";
|
|
|
12
12
|
import { getConfig } from "../config/KeyManager";
|
|
13
13
|
import Typography from "./Typography";
|
|
14
14
|
|
|
15
|
-
type predictionType = {
|
|
15
|
+
export type predictionType = {
|
|
16
16
|
id: string;
|
|
17
17
|
description: string;
|
|
18
18
|
};
|
|
19
|
-
export const getPredictionsFromCoords = async (coords:
|
|
19
|
+
export const getPredictionsFromCoords = async (coords: {
|
|
20
|
+
latitude: number;
|
|
21
|
+
longitude: number;
|
|
22
|
+
}) => {
|
|
20
23
|
const { GOOGLE_MAP_API_KEY } = getConfig();
|
|
21
24
|
|
|
22
25
|
if (!GOOGLE_MAP_API_KEY)
|
|
@@ -44,6 +47,45 @@ export const getPredictionsFromCoords = async (coords: any) => {
|
|
|
44
47
|
return p;
|
|
45
48
|
};
|
|
46
49
|
|
|
50
|
+
export const getPredictionsFromQuery = async (
|
|
51
|
+
query: string,
|
|
52
|
+
country: string
|
|
53
|
+
) => {
|
|
54
|
+
const { GOOGLE_MAP_API_KEY } = getConfig();
|
|
55
|
+
const endpoint = `https://maps.googleapis.com/maps/api/place/autocomplete/json?input=${query}&components=country:${country}&radius=20000&key=${GOOGLE_MAP_API_KEY}`;
|
|
56
|
+
const res = await (await fetch(endpoint)).json();
|
|
57
|
+
|
|
58
|
+
const p = [];
|
|
59
|
+
for (let key in res.predictions) {
|
|
60
|
+
const { description, place_id } = res.predictions[key];
|
|
61
|
+
p.push({
|
|
62
|
+
description,
|
|
63
|
+
id: place_id,
|
|
64
|
+
});
|
|
65
|
+
}
|
|
66
|
+
return p;
|
|
67
|
+
};
|
|
68
|
+
|
|
69
|
+
export const getLocationFromPlaceId = async (
|
|
70
|
+
place_id: string
|
|
71
|
+
): Promise<{
|
|
72
|
+
formatted_address: string;
|
|
73
|
+
geometry: {
|
|
74
|
+
location: {
|
|
75
|
+
lat: number;
|
|
76
|
+
lng: number;
|
|
77
|
+
};
|
|
78
|
+
};
|
|
79
|
+
}> => {
|
|
80
|
+
const { GOOGLE_MAP_API_KEY } = getConfig();
|
|
81
|
+
const res = await (
|
|
82
|
+
await fetch(
|
|
83
|
+
`https://maps.googleapis.com/maps/api/place/details/json?place_id=${place_id}&fields=formatted_address%2Cgeometry&key=${GOOGLE_MAP_API_KEY}`
|
|
84
|
+
)
|
|
85
|
+
).json();
|
|
86
|
+
return res.result;
|
|
87
|
+
};
|
|
88
|
+
|
|
47
89
|
export const Locator: React.FC<LocatorProps> = ({
|
|
48
90
|
variant = "contained",
|
|
49
91
|
onLocationSelected,
|
|
@@ -84,17 +126,8 @@ export const Locator: React.FC<LocatorProps> = ({
|
|
|
84
126
|
},
|
|
85
127
|
});
|
|
86
128
|
const search = async (query: string) => {
|
|
87
|
-
const
|
|
88
|
-
|
|
89
|
-
const p = [];
|
|
90
|
-
for (let key in res.predictions) {
|
|
91
|
-
const { description, place_id } = res.predictions[key];
|
|
92
|
-
p.push({
|
|
93
|
-
description,
|
|
94
|
-
id: place_id,
|
|
95
|
-
});
|
|
96
|
-
}
|
|
97
|
-
setPrediction(p);
|
|
129
|
+
const predictions = await getPredictionsFromQuery(query, country);
|
|
130
|
+
setPrediction(predictions);
|
|
98
131
|
};
|
|
99
132
|
|
|
100
133
|
const locateMe = () => {
|
|
@@ -135,19 +168,14 @@ export const Locator: React.FC<LocatorProps> = ({
|
|
|
135
168
|
};
|
|
136
169
|
const locationPressed = async (loc: predictionType) => {
|
|
137
170
|
setValue(loc.description);
|
|
138
|
-
const res = await (
|
|
139
|
-
await fetch(
|
|
140
|
-
`https://maps.googleapis.com/maps/api/place/details/json?place_id=${loc.id}&fields=formatted_address%2Cgeometry&key=${GOOGLE_MAP_API_KEY}`
|
|
141
|
-
)
|
|
142
|
-
).json();
|
|
171
|
+
const res = await getLocationFromPlaceId(loc.id);
|
|
143
172
|
onLocationSelected(
|
|
144
173
|
{
|
|
145
|
-
latitude: res.
|
|
146
|
-
longitude: res.
|
|
147
|
-
|
|
174
|
+
latitude: res.geometry.location.lat,
|
|
175
|
+
longitude: res.geometry.location.lng,
|
|
148
176
|
description: loc.description,
|
|
149
177
|
},
|
|
150
|
-
res
|
|
178
|
+
res?.formatted_address
|
|
151
179
|
);
|
|
152
180
|
setChanged(false);
|
|
153
181
|
setPrediction([]);
|
package/src/Components/Popup.tsx
CHANGED
|
@@ -58,15 +58,18 @@ export const Popup: React.FC<PopupProps> = ({
|
|
|
58
58
|
...style,
|
|
59
59
|
},
|
|
60
60
|
content: {
|
|
61
|
-
paddingHorizontal: bare ? undefined : "
|
|
61
|
+
paddingHorizontal: bare ? undefined : "15@ms",
|
|
62
62
|
// flex: 1,
|
|
63
63
|
},
|
|
64
64
|
title: {
|
|
65
65
|
flexDirection: "row",
|
|
66
66
|
alignItems: "center",
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
67
|
+
justifyContent: "center",
|
|
68
|
+
height: "50@ms",
|
|
69
|
+
},
|
|
70
|
+
titleIcon: {
|
|
71
|
+
position: "absolute",
|
|
72
|
+
left: "15@ms",
|
|
70
73
|
},
|
|
71
74
|
backdrop: {
|
|
72
75
|
position: "absolute",
|
|
@@ -131,16 +134,16 @@ export const Popup: React.FC<PopupProps> = ({
|
|
|
131
134
|
<View style={styles.container}>
|
|
132
135
|
{!bare && (
|
|
133
136
|
<View style={styles.title}>
|
|
134
|
-
<
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
<Typography color="textSecondary" align="center">
|
|
141
|
-
{title}
|
|
142
|
-
</Typography>
|
|
137
|
+
<View style={styles.titleIcon}>
|
|
138
|
+
<IconButton
|
|
139
|
+
size={20}
|
|
140
|
+
icon="close"
|
|
141
|
+
onPress={closeAction}
|
|
142
|
+
/>
|
|
143
143
|
</View>
|
|
144
|
+
<Typography align="center" fontWeight={500}>
|
|
145
|
+
{title}
|
|
146
|
+
</Typography>
|
|
144
147
|
</View>
|
|
145
148
|
)}
|
|
146
149
|
|