@hoddy-ui/core 2.5.3 → 2.5.5
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/package.json +1 -1
- package/src/Components/FlashMessage.tsx +93 -40
- package/src/Components/Popup.tsx +3 -3
- package/src/Components/Typography.tsx +7 -3
- package/src/config/KeyManager.ts +6 -1
- package/src/config/index.ts +37 -2
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
|
-
//
|
|
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
|
-
//
|
|
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,14 +1,14 @@
|
|
|
1
|
-
import React, {
|
|
2
|
-
import {
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
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
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
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
|
-
|
|
38
|
-
|
|
39
|
-
|
|
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:
|
|
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
|
-
<
|
|
70
|
-
<View style={{
|
|
71
|
-
{
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
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
|
-
<
|
|
127
|
+
</View>
|
|
128
|
+
{/* <MaterialIcons color="#fff" size={36} name="error-outline" /> */}
|
|
82
129
|
</View>
|
|
83
|
-
|
|
84
|
-
</View>
|
|
130
|
+
</TouchableOpacity>
|
|
85
131
|
|
|
86
132
|
{message?.actions?.map((cur, i) => (
|
|
87
|
-
<TouchableOpacity
|
|
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
|
|
package/src/Components/Popup.tsx
CHANGED
|
@@ -115,7 +115,7 @@ export const Popup: React.FC<PopupProps> = ({
|
|
|
115
115
|
backdrop: {
|
|
116
116
|
position: "absolute",
|
|
117
117
|
height: "100%",
|
|
118
|
-
zIndex:
|
|
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
|
)}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import React, { forwardRef } from "react";
|
|
2
2
|
import { StyleSheet, Text } from "react-native";
|
|
3
|
-
import { moderateScale, verticalScale } from "react-native-size-matters";
|
|
3
|
+
import { moderateScale, ms, verticalScale } from "react-native-size-matters";
|
|
4
4
|
import { useColors } from "../hooks";
|
|
5
5
|
import { TypographyProps } from "../types";
|
|
6
6
|
import { getConfig } from "../config/KeyManager";
|
|
@@ -39,13 +39,17 @@ const Typography: React.FC<TypographyProps> = forwardRef(
|
|
|
39
39
|
const styles: any = StyleSheet.create({
|
|
40
40
|
text: {
|
|
41
41
|
fontSize: fontSize || _fontSize[variant],
|
|
42
|
-
marginBottom:
|
|
42
|
+
marginBottom: ms(gutterBottom) || 0,
|
|
43
43
|
color: colors[color]?.main || color,
|
|
44
44
|
textTransform: textCase,
|
|
45
45
|
alignItems: "center",
|
|
46
46
|
textAlign: align,
|
|
47
47
|
fontWeight: fontWeight,
|
|
48
|
-
fontFamily:
|
|
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
|
|
package/src/config/KeyManager.ts
CHANGED
|
@@ -1,6 +1,11 @@
|
|
|
1
1
|
type configTypes = {
|
|
2
2
|
GOOGLE_MAP_API_KEY?: string;
|
|
3
|
-
|
|
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
|
|
package/src/config/index.ts
CHANGED
|
@@ -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
|
-
|
|
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
|
-
|
|
52
|
+
TYPOGRAPHY: config.typography,
|
|
18
53
|
EDGE_TO_EDGE: config.edgeToEdge ?? false,
|
|
19
54
|
});
|
|
20
55
|
if (config.colors) setExtraColors(config.colors);
|