@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.
@@ -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: any) => {
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 endpoint = `https://maps.googleapis.com/maps/api/place/autocomplete/json?input=${query}&components=country:${country}&radius=20000&key=${GOOGLE_MAP_API_KEY}`;
88
- const res = await (await fetch(endpoint)).json();
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.result?.geometry.location.lat,
146
- longitude: res.result?.geometry.location.lng,
147
-
174
+ latitude: res.geometry.location.lat,
175
+ longitude: res.geometry.location.lng,
148
176
  description: loc.description,
149
177
  },
150
- res.result?.formatted_address
178
+ res?.formatted_address
151
179
  );
152
180
  setChanged(false);
153
181
  setPrediction([]);
@@ -58,15 +58,18 @@ export const Popup: React.FC<PopupProps> = ({
58
58
  ...style,
59
59
  },
60
60
  content: {
61
- paddingHorizontal: bare ? undefined : "10@ms",
61
+ paddingHorizontal: bare ? undefined : "15@ms",
62
62
  // flex: 1,
63
63
  },
64
64
  title: {
65
65
  flexDirection: "row",
66
66
  alignItems: "center",
67
- paddingVertical: "5@ms",
68
- paddingHorizontal: "10@ms",
69
- marginBottom: "10@ms",
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
- <IconButton
135
- size={20}
136
- icon="close"
137
- onPress={closeAction}
138
- />
139
- <View style={{ flex: 1 }}>
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