@hoddy-ui/core 2.5.2 → 2.5.4

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 CHANGED
@@ -48,9 +48,6 @@ yarn add @expo/vector-icons @react-native-async-storage/async-storage @react-nav
48
48
  import { initialize } from "@hoddy-ui/core";
49
49
 
50
50
  initialize({
51
- // Custom font family
52
- fontFamily: "Inter-Regular",
53
-
54
51
  // Google Maps API key for Locator component
55
52
  googleMapApiKey: "your-google-maps-api-key",
56
53
 
@@ -65,6 +62,17 @@ initialize({
65
62
  dark: "#4f46e5",
66
63
  },
67
64
  },
65
+
66
+ // Typography settings
67
+ typography: {
68
+ fontFamily: "Inter-Regular",
69
+ fontWeights: {
70
+ 400: "Inter-Regular",
71
+ 500: "Inter-Medium",
72
+ 600: "Inter-SemiBold",
73
+ 700: "Inter-Bold",
74
+ },
75
+ },
68
76
  });
69
77
  ```
70
78
 
@@ -141,24 +149,64 @@ Use the `initialize` function to configure the library globally:
141
149
  import { initialize } from "@hoddy-ui/core";
142
150
 
143
151
  initialize({
144
- // Font family for all typography components
145
- fontFamily?: string;
146
-
147
- // Google Maps API key for Locator component
152
+ // Google Maps API key for map components
148
153
  googleMapApiKey?: string;
149
154
 
150
- // Edge-to-edge display (skips Android navigation bar styling)
151
- edgeToEdge?: boolean;
152
-
153
- // Custom color overrides
155
+ // Custom color palette overrides
154
156
  colors?: {
155
157
  primary?: { main: string; light?: string; dark?: string };
156
158
  secondary?: { main: string; light?: string; dark?: string };
157
- // ... and more
159
+ // ... and more color options
160
+ };
161
+
162
+ // Enable edge-to-edge display mode
163
+ edgeToEdge?: boolean;
164
+
165
+ // Typography settings
166
+ typography?: {
167
+ // Primary font family
168
+ fontFamily?: string;
169
+
170
+ // Font family mappings for each weight (Android support)
171
+ fontWeights?: {
172
+ 100?: string;
173
+ 200?: string;
174
+ 300?: string;
175
+ 400?: string;
176
+ 500?: string;
177
+ 600?: string;
178
+ 700?: string;
179
+ 800?: string;
180
+ 900?: string;
181
+ };
158
182
  };
159
183
  });
160
184
  ```
161
185
 
186
+ ### Configuration Example
187
+
188
+ ```tsx
189
+ initialize({
190
+ googleMapApiKey: "AIzaSyBxxxxxxxxxxxxxxxxxxxxxx",
191
+ edgeToEdge: true,
192
+ colors: {
193
+ primary: "#007AFF",
194
+ secondary: "#34C759",
195
+ },
196
+ typography: {
197
+ fontFamily: "Inter",
198
+ fontWeights: {
199
+ 400: "Inter-Regular",
200
+ 500: "Inter-Medium",
201
+ 600: "Inter-SemiBold",
202
+ 700: "Inter-Bold",
203
+ },
204
+ },
205
+ });
206
+ ```
207
+
208
+ **Note:** The `fontWeights` property is particularly useful for Android devices where different font weights require separate font family files. This allows you to map each weight (100-900) to its corresponding font family name.
209
+
162
210
  ### Theme Configuration
163
211
 
164
212
  The theme system automatically detects system preferences and can be controlled programmatically:
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@hoddy-ui/core",
3
- "version": "2.5.2",
3
+ "version": "2.5.4",
4
4
  "description": "Core rich react native components written in typescript",
5
5
  "main": "index.ts",
6
6
  "repository": {
@@ -131,7 +131,7 @@ const Button: React.FC<ButtonProps> = forwardRef(
131
131
  borderRadius: rounded ? 30 : 10,
132
132
  elevation: variant === "text" ? 0 : elevation,
133
133
  paddingVertical:
134
- size === "small" ? 8 : size === "large" ? "15@ms" : "13@ms",
134
+ size === "small" ? 8 : size === "large" ? "15@mvs" : "13@mvs",
135
135
  paddingHorizontal: size === "small" ? "10@ms" : "18@ms",
136
136
  borderColor: colors[color].main,
137
137
  borderWidth: variant === "outlined" ? 1 : 0,
@@ -155,7 +155,7 @@ const Button: React.FC<ButtonProps> = forwardRef(
155
155
  variant === "text" || variant === "outlined" ? "main" : "text"
156
156
  ],
157
157
  fontWeight: variant === "outlined" ? "700" : "500",
158
- fontSize: size === "small" ? "12@ms" : "14@ms",
158
+ fontSize: size === "small" ? "12@ms" : "13@ms",
159
159
  fontFamily: getConfig().DEFAULT_FONT_FAMILY || "System",
160
160
  },
161
161
  });
@@ -1,14 +1,14 @@
1
- import React, { useEffect, useState } from "react";
2
- import {
3
- LayoutAnimation,
4
- Touchable,
5
- TouchableOpacity,
6
- View,
7
- } from "react-native";
1
+ import React, { 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
 
@@ -17,31 +17,71 @@ export let showFlashMessage: (msg: FlashMessageProps) => void = () => {};
17
17
  const FlashMessage: React.FC = () => {
18
18
  const { top } = useSafeAreaInsets();
19
19
  const [message, setMessage] = useState<null | FlashMessageProps>(null);
20
- const [show, setShow] = useState(false);
21
20
  const colors = useColors();
22
21
  const type = message?.type || "success";
22
+ const timeoutRef = useRef<NodeJS.Timeout | null>(null);
23
+
24
+ // Animated values
25
+ const translateY = useSharedValue(-200);
26
+ const opacity = useSharedValue(0);
27
+
28
+ const hideMessage = () => {
29
+ setMessage(null);
30
+ };
31
+
32
+ const closeMessage = () => {
33
+ // Clear existing timeout if any
34
+ if (timeoutRef.current) {
35
+ clearTimeout(timeoutRef.current);
36
+ timeoutRef.current = null;
37
+ }
38
+
39
+ // Animate out immediately
40
+ translateY.value = withTiming(-200, { duration: 300 });
41
+ opacity.value = withTiming(0, { duration: 300 }, () => {
42
+ runOnJS(hideMessage)();
43
+ });
44
+ };
23
45
 
24
46
  showFlashMessage = (msg: FlashMessageProps) => {
47
+ // Clear existing timeout if any
48
+ if (timeoutRef.current) {
49
+ clearTimeout(timeoutRef.current);
50
+ timeoutRef.current = null;
51
+ }
52
+
53
+ // Reset position immediately before starting new animation
54
+ translateY.value = -200;
55
+ opacity.value = 0;
56
+
25
57
  setMessage(msg);
26
- setTimeout(() => {
27
- setShow(true);
28
- }, 50);
29
-
30
- setTimeout(() => {
31
- setShow(false);
32
- setTimeout(() => {
33
- setMessage(null);
34
- }, 500);
35
- }, msg.duration || 3000);
58
+
59
+ // Animate in
60
+ translateY.value = withTiming(0, { duration: 300 });
61
+ opacity.value = withTiming(1, { duration: 300 });
62
+
63
+ // Animate out after duration
64
+ const duration = msg.duration || 3000;
65
+ timeoutRef.current = setTimeout(() => {
66
+ translateY.value = withTiming(-200, { duration: 300 });
67
+ opacity.value = withTiming(0, { duration: 300 }, () => {
68
+ runOnJS(hideMessage)();
69
+ });
70
+ timeoutRef.current = null;
71
+ }, duration);
36
72
  };
37
- useEffect(() => {
38
- LayoutAnimation.configureNext(LayoutAnimation.Presets.easeInEaseOut);
39
- }, [show]);
73
+
74
+ const animatedStyle = useAnimatedStyle(() => {
75
+ return {
76
+ transform: [{ translateY: translateY.value }],
77
+ opacity: opacity.value,
78
+ };
79
+ });
40
80
 
41
81
  const styles = ScaledSheet.create({
42
82
  root: {
43
83
  position: "absolute",
44
- top: show ? 0 : -200,
84
+ top: 0,
45
85
  zIndex: 1000,
46
86
  left: 0,
47
87
  paddingTop: top + 10,
@@ -64,33 +104,46 @@ const FlashMessage: React.FC = () => {
64
104
  },
65
105
  });
66
106
 
107
+ if (!message) return null;
108
+
67
109
  return (
68
- <View style={styles.root}>
69
- <View style={{ flexDirection: "row" }}>
70
- <View style={{ flex: 1, marginRight: 10 }}>
71
- {message?.title && (
72
- <Typography
73
- variant="h6"
74
- fontWeight={600}
75
- gutterBottom={3}
76
- style={{ color: "#fff" }}
77
- >
78
- {message?.title}
110
+ <Animated.View style={[styles.root, animatedStyle]}>
111
+ <TouchableOpacity onPress={closeMessage} activeOpacity={0.9}>
112
+ <View style={{ flexDirection: "row" }}>
113
+ <View style={{ flex: 1, marginRight: 10 }}>
114
+ {message?.title && (
115
+ <Typography
116
+ variant="h6"
117
+ fontWeight={600}
118
+ gutterBottom={3}
119
+ style={{ color: "#fff" }}
120
+ >
121
+ {message?.title}
122
+ </Typography>
123
+ )}
124
+ <Typography style={{ color: "#fff" }}>
125
+ {message?.message}
79
126
  </Typography>
80
- )}
81
- <Typography style={{ color: "#fff" }}>{message?.message}</Typography>
127
+ </View>
128
+ {/* <MaterialIcons color="#fff" size={36} name="error-outline" /> */}
82
129
  </View>
83
- {/* <MaterialIcons color="#fff" size={36} name="error-outline" /> */}
84
- </View>
130
+ </TouchableOpacity>
85
131
 
86
132
  {message?.actions?.map((cur, i) => (
87
- <TouchableOpacity key={i} style={styles.action} onPress={cur.onPress}>
133
+ <TouchableOpacity
134
+ key={i}
135
+ style={styles.action}
136
+ onPress={() => {
137
+ cur.onPress?.();
138
+ closeMessage();
139
+ }}
140
+ >
88
141
  <Typography fontWeight={700} style={{ color: "#fff" }}>
89
142
  {cur.title}
90
143
  </Typography>
91
144
  </TouchableOpacity>
92
145
  ))}
93
- </View>
146
+ </Animated.View>
94
147
  );
95
148
  };
96
149
 
@@ -115,7 +115,7 @@ export const Popup: React.FC<PopupProps> = ({
115
115
  backdrop: {
116
116
  position: "absolute",
117
117
  height: "100%",
118
- zIndex: -1,
118
+ zIndex: 1,
119
119
  width: "100%",
120
120
  backgroundColor: "#000b",
121
121
  },
@@ -133,13 +133,13 @@ export const Popup: React.FC<PopupProps> = ({
133
133
  visible={modalVisible}
134
134
  onRequestClose={closeAction}
135
135
  >
136
- <Animated.View style={[styles.backdrop, backdropAnimatedStyle]} />
137
136
  <UIThemeProvider>
138
137
  <TouchableWithoutFeedback onPress={Keyboard.dismiss}>
139
138
  <View style={styles.root}>
139
+ <Animated.View style={[styles.backdrop, backdropAnimatedStyle]} />
140
140
  {open && (
141
141
  <Pressable
142
- style={StyleSheet.absoluteFill}
142
+ style={[StyleSheet.absoluteFill, { zIndex: 2 }]}
143
143
  onPress={closeAction}
144
144
  />
145
145
  )}
@@ -45,7 +45,11 @@ const Typography: React.FC<TypographyProps> = forwardRef(
45
45
  alignItems: "center",
46
46
  textAlign: align,
47
47
  fontWeight: fontWeight,
48
- fontFamily: fontFamily || getConfig().DEFAULT_FONT_FAMILY || undefined, // Use custom font if provided, else default
48
+ fontFamily:
49
+ fontFamily ||
50
+ getConfig().TYPOGRAPHY?.fontWeights?.[fontWeight] ||
51
+ getConfig().TYPOGRAPHY?.fontFamily ||
52
+ undefined, // Use custom font if provided, else default
49
53
  },
50
54
  });
51
55
 
@@ -1,6 +1,11 @@
1
1
  type configTypes = {
2
2
  GOOGLE_MAP_API_KEY?: string;
3
- DEFAULT_FONT_FAMILY?: string;
3
+ TYPOGRAPHY?: {
4
+ fontFamily?: string;
5
+ fontWeights?: {
6
+ [K in 100 | 200 | 300 | 400 | 500 | 600 | 700 | 800 | 900]?: string;
7
+ };
8
+ };
4
9
  EDGE_TO_EDGE?: boolean;
5
10
  };
6
11
 
@@ -3,18 +3,53 @@ import { setExtraColors } from "../theme/colors";
3
3
  import { extraColorTypes } from "../types";
4
4
  import { setConfig } from "./KeyManager";
5
5
 
6
+ /**
7
+ * Configuration options for the Hoddy UI library
8
+ *
9
+ * @example
10
+ * ```typescript
11
+ * initialize({
12
+ * googleMapApiKey: "AIzaSyBxxxxxxxxxxxxxxxxxxxxxx",
13
+ * edgeToEdge: true,
14
+ * colors: {
15
+ * primary: "#007AFF",
16
+ * secondary: "#34C759"
17
+ * },
18
+ * typography: {
19
+ * fontFamily: "Inter",
20
+ * fontWeights: {
21
+ * 400: "Inter-Regular",
22
+ * 500: "Inter-Medium",
23
+ * 600: "Inter-SemiBold",
24
+ * 700: "Inter-Bold"
25
+ * }
26
+ * }
27
+ * });
28
+ * ```
29
+ */
6
30
  type configProps = {
31
+ /** Google Maps API key for map components */
7
32
  googleMapApiKey?: string;
33
+ /** Custom color palette overrides */
8
34
  colors?: extraColorTypes;
9
- fontFamily?: string;
35
+ /** Enable edge-to-edge display mode */
10
36
  edgeToEdge?: boolean;
37
+ /** Typography settings */
38
+ typography?: {
39
+ /** Primary font family */
40
+ fontFamily?: string;
41
+ /** Font family mappings for each weight (Android support) */
42
+ fontWeights?: {
43
+ [K in 100 | 200 | 300 | 400 | 500 | 600 | 700 | 800 | 900]?: string;
44
+ };
45
+ };
11
46
  };
12
47
 
13
48
  export function initialize(config: configProps): void {
14
49
  try {
15
50
  setConfig({
16
51
  GOOGLE_MAP_API_KEY: config.googleMapApiKey,
17
- DEFAULT_FONT_FAMILY: config.fontFamily,
52
+ TYPOGRAPHY: config.typography,
18
53
  EDGE_TO_EDGE: config.edgeToEdge ?? false,
19
54
  });
20
55
  if (config.colors) setExtraColors(config.colors);