@hoddy-ui/core 2.5.25 → 2.5.27

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@hoddy-ui/core",
3
- "version": "2.5.25",
3
+ "version": "2.5.27",
4
4
  "description": "Core rich react native components written in typescript",
5
5
  "main": "index.ts",
6
6
  "repository": {
@@ -1,4 +1,4 @@
1
- import React, { useRef, useState } from "react";
1
+ import React, { useEffect, useRef, useState } from "react";
2
2
  import { TouchableOpacity, View } from "react-native";
3
3
  import Animated, {
4
4
  runOnJS,
@@ -12,7 +12,20 @@ import { useColors } from "../hooks";
12
12
  import { FlashMessageProps } from "../types";
13
13
  import Typography from "./Typography";
14
14
 
15
- export let showFlashMessage: (msg: FlashMessageProps) => void = () => {};
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();
@@ -43,33 +56,44 @@ const FlashMessage: React.FC = () => {
43
56
  });
44
57
  };
45
58
 
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
-
57
- setMessage(msg);
58
-
59
- // Animate in
60
- translateY.value = withTiming(0, { duration: 300 });
61
- opacity.value = withTiming(1, { duration: 300 });
59
+ useEffect(() => {
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
+ };
62
87
 
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);
72
- };
88
+ const unsubscribe = subscribeToFlashMessages(listener);
89
+ return () => {
90
+ if (timeoutRef.current) {
91
+ clearTimeout(timeoutRef.current);
92
+ timeoutRef.current = null;
93
+ }
94
+ unsubscribe();
95
+ };
96
+ }, [message, opacity, translateY]);
73
97
 
74
98
  const animatedStyle = useAnimatedStyle(() => {
75
99
  return {
@@ -104,7 +128,7 @@ const FlashMessage: React.FC = () => {
104
128
  },
105
129
  });
106
130
 
107
- if (!message) return null;
131
+ // if (!message) return null;
108
132
 
109
133
  return (
110
134
  <Animated.View style={[styles.root, animatedStyle]}>
@@ -42,6 +42,7 @@ export const Popup: React.FC<PopupProps> = ({
42
42
  const theme = useTheme();
43
43
  const colors = useColors();
44
44
  const [modalVisible, setModalVisible] = useState(false);
45
+ const [modalOpen, setModalOpen] = useState(false);
45
46
  const [keyboardVisible, setKeyboardVisible] = useState(false);
46
47
  const { bottom } = useSafeAreaInsets();
47
48
 
@@ -74,25 +75,31 @@ export const Popup: React.FC<PopupProps> = ({
74
75
  };
75
76
  }, []);
76
77
 
78
+ const _onModalShow = () => {
79
+ setModalVisible(true);
80
+ onModalShow?.();
81
+ };
82
+ const _onModalHide = () => {
83
+ onModalHide?.();
84
+ setModalOpen(false);
85
+ };
86
+
77
87
  // Trigger animations when open prop changes
78
88
  useEffect(() => {
79
89
  if (open) {
80
- setModalVisible(true);
90
+ setModalOpen(true);
81
91
  // Opening animation
82
92
  backdropOpacity.value = withTiming(1, { duration: 300 });
83
93
  contentTranslateY.value = withTiming(0, { duration: 300 }, () => {
84
- if (onModalShow) {
85
- runOnJS(onModalShow)();
86
- }
94
+ runOnJS(_onModalShow)();
87
95
  });
88
96
  } else {
89
97
  // Closing animation
98
+ setModalVisible(false);
99
+
90
100
  backdropOpacity.value = withTiming(0, { duration: 200 });
91
101
  contentTranslateY.value = withTiming(1000, { duration: 200 }, () => {
92
- runOnJS(setModalVisible)(false);
93
- if (onModalHide) {
94
- runOnJS(onModalHide)();
95
- }
102
+ runOnJS(_onModalHide)();
96
103
  });
97
104
  }
98
105
  }, [open]);
@@ -167,7 +174,7 @@ export const Popup: React.FC<PopupProps> = ({
167
174
  transparent
168
175
  animationType="none"
169
176
  statusBarTranslucent
170
- visible={modalVisible}
177
+ visible={modalOpen}
171
178
  onRequestClose={closeAction}
172
179
  >
173
180
  <UIThemeProvider>
@@ -181,7 +188,7 @@ export const Popup: React.FC<PopupProps> = ({
181
188
  >
182
189
  <TouchableWithoutFeedback onPress={Keyboard.dismiss}>
183
190
  <View style={styles.root}>
184
- {open && (
191
+ {modalOpen && (
185
192
  <Pressable
186
193
  style={[StyleSheet.absoluteFill, { zIndex: 1 }]}
187
194
  onPress={closeAction}
@@ -190,10 +197,14 @@ export const Popup: React.FC<PopupProps> = ({
190
197
 
191
198
  <Animated.View
192
199
  style={[styles.avoidingView, contentAnimatedStyle]}
193
- layout={LinearTransition.springify()
194
- .stiffness(200)
195
- .mass(0.5)
196
- .damping(100)}
200
+ layout={
201
+ modalVisible
202
+ ? LinearTransition.springify()
203
+ .stiffness(200)
204
+ .mass(0.5)
205
+ .damping(100)
206
+ : undefined
207
+ }
197
208
  >
198
209
  <View style={styles.container}>
199
210
  {!bare && (