ikualo-ui-kit-mobile 2.0.4 → 2.0.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/app.json CHANGED
@@ -3,7 +3,7 @@
3
3
  "name": "ikualo-app-2.0",
4
4
  "slug": "ikualo-app-20",
5
5
  "owner": "ikualo",
6
- "version": "2.0.4",
6
+ "version": "2.0.5",
7
7
  "orientation": "portrait",
8
8
  "icon": "./assets/icon.png",
9
9
  "userInterfaceStyle": "automatic",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ikualo-ui-kit-mobile",
3
- "version": "2.0.4",
3
+ "version": "2.0.5",
4
4
  "main": "src/index.ts",
5
5
  "scripts": {
6
6
  "start": "expo start",
@@ -1,101 +1,224 @@
1
- import { Icon, Text } from 'react-native-paper';
2
- import { getStylesDialog } from '../../../assets/styles/elements/dialog';
3
- import { View, TouchableWithoutFeedback, Modal, Keyboard, TouchableHighlight, Animated } from 'react-native';
4
- import { useEffect, useState, useRef } from 'react';
5
- import useStore from '../../store';
6
- import { IDialogDown } from '../../models';
7
-
8
- export const DialogDown = (props: IDialogDown) => {
9
- const theme = useStore().theme;
10
- const stylesDialog = getStylesDialog(theme);
11
- const { isVisible, title, children, onDismiss, image, showCloseButton = true } = props;
12
- const slideAnim = useRef(new Animated.Value(0)).current;
13
-
14
- const handleDismissKeyboard = () => {
15
- Keyboard.dismiss();
16
- };
17
- const [isKeyboardVisible, setIsKeyboardVisible] = useState(false);
18
-
19
- useEffect(() => {
20
- if (isVisible) {
21
- Animated.timing(slideAnim, {
22
- toValue: 1,
23
- duration: 300,
24
- useNativeDriver: true,
25
- }).start();
26
- } else {
27
- slideAnim.setValue(0);
28
- }
29
- }, [isVisible, slideAnim]);
30
-
31
- useEffect(() => {
32
- const keyboardDidShowListener = Keyboard.addListener('keyboardDidShow', () => setIsKeyboardVisible(true));
33
- const keyboardDidHideListener = Keyboard.addListener('keyboardDidHide', () =>
34
- setIsKeyboardVisible(false)
35
- );
36
-
37
- return () => {
38
- keyboardDidShowListener.remove();
39
- keyboardDidHideListener.remove();
40
- };
41
- }, []);
42
-
43
- const handleDismiss = () => {
44
- Animated.timing(slideAnim, {
45
- toValue: 0,
46
- duration: 200,
47
- useNativeDriver: true,
48
- }).start(() => {
49
- onDismiss();
50
- });
51
- };
52
-
53
- return (
54
- <Modal visible={isVisible} transparent={true} animationType="none" onRequestClose={handleDismiss}>
55
- <TouchableWithoutFeedback onPress={handleDismissKeyboard}>
56
- <View style={stylesDialog.modalBackground}>
57
- <Animated.View
58
- style={[
59
- stylesDialog['dialog-down'],
60
- !isKeyboardVisible && stylesDialog['dialog-bottom'],
61
- {
62
- transform: [
63
- {
64
- translateY: slideAnim.interpolate({
65
- inputRange: [0, 1],
66
- outputRange: [300, 0],
67
- }),
68
- },
69
- ],
70
- },
71
- ]}
72
- >
73
- {showCloseButton && (
74
- <TouchableHighlight
75
- onPress={handleDismiss}
76
- underlayColor={theme.colors.background_focus}
77
- style={stylesDialog['dialog-down-close']}
78
- >
79
- <Icon
80
- source="close"
81
- color={stylesDialog['dialog-down-close-icon'].color}
82
- size={stylesDialog['dialog-down-close-icon'].width}
83
- />
84
- </TouchableHighlight>
85
- )}
86
-
87
- {image ? (
88
- <View style={stylesDialog['dialog-img']}>{image}</View>
89
- ) : (
90
- <View style={{ marginTop: image ? 0 : -16, position: 'relative' }}>
91
- <Text style={[stylesDialog['dialog-title']]}>{title}</Text>
92
- </View>
93
- )}
94
-
95
- {children}
96
- </Animated.View>
97
- </View>
98
- </TouchableWithoutFeedback>
99
- </Modal>
100
- );
101
- };
1
+ import { Icon, Text } from 'react-native-paper';
2
+ import { getStylesDialog } from '../../../assets/styles/elements/dialog';
3
+ import { View, TouchableWithoutFeedback, Modal, Keyboard, TouchableHighlight, Animated } from 'react-native';
4
+ import { useEffect, useState, useRef } from 'react';
5
+ import useStore from '../../store';
6
+ import { IDialogDown } from '../../models';
7
+
8
+ // Sistema de cola de diálogos global
9
+ interface DialogQueueItem extends IDialogDown {
10
+ id: string;
11
+ priority: number;
12
+ }
13
+
14
+ class DialogManager {
15
+ private static instance: DialogManager;
16
+ private dialogs: DialogQueueItem[] = [];
17
+ private currentDialog: DialogQueueItem | null = null;
18
+ private listeners: Set<() => void> = new Set();
19
+
20
+ static getInstance(): DialogManager {
21
+ if (!DialogManager.instance) {
22
+ DialogManager.instance = new DialogManager();
23
+ }
24
+ return DialogManager.instance;
25
+ }
26
+
27
+ subscribe(listener: () => void) {
28
+ this.listeners.add(listener);
29
+ return () => this.listeners.delete(listener);
30
+ }
31
+
32
+ private notify() {
33
+ this.listeners.forEach((listener) => listener());
34
+ }
35
+
36
+ addDialog(dialog: Omit<DialogQueueItem, 'id'>) {
37
+ const id = Math.random().toString(36).substr(2, 9);
38
+ const dialogWithId = { ...dialog, id };
39
+
40
+ // Solo cerrar el diálogo actual si el nuevo tiene prioridad explícita (1-5)
41
+ // y es mayor que la prioridad del diálogo actual
42
+ if (this.currentDialog && dialog.priority > 0 && dialog.priority > this.currentDialog.priority) {
43
+ this.closeCurrentDialog();
44
+ }
45
+
46
+ // Agregar a la cola
47
+ this.dialogs.push(dialogWithId);
48
+
49
+ // Si no hay diálogo actual, mostrar el nuevo
50
+ if (!this.currentDialog) {
51
+ this.showNextDialog();
52
+ }
53
+
54
+ this.notify();
55
+ }
56
+
57
+ removeDialog(id: string) {
58
+ this.dialogs = this.dialogs.filter((dialog) => dialog.id !== id);
59
+
60
+ if (this.currentDialog?.id === id) {
61
+ this.currentDialog = null;
62
+ this.showNextDialog();
63
+ }
64
+
65
+ this.notify();
66
+ }
67
+
68
+ private closeCurrentDialog() {
69
+ if (this.currentDialog) {
70
+ this.currentDialog.onDismiss();
71
+ this.currentDialog = null;
72
+ }
73
+ }
74
+
75
+ private showNextDialog() {
76
+ if (this.dialogs.length > 0) {
77
+ // Ordenar por prioridad (mayor prioridad primero)
78
+ this.dialogs.sort((a, b) => b.priority - a.priority);
79
+ this.currentDialog = this.dialogs[0];
80
+ }
81
+ }
82
+
83
+ getCurrentDialog(): DialogQueueItem | null {
84
+ return this.currentDialog;
85
+ }
86
+
87
+ clear() {
88
+ this.dialogs = [];
89
+ this.currentDialog = null;
90
+ this.notify();
91
+ }
92
+ }
93
+
94
+ export const DialogDown = (props: IDialogDown & { priority?: 1 | 2 | 3 | 4 | 5 }) => {
95
+ const theme = useStore().theme;
96
+ const stylesDialog = getStylesDialog(theme);
97
+ const { isVisible, title, children, onDismiss, image, showCloseButton = true, priority } = props;
98
+ const slideAnim = useRef(new Animated.Value(0)).current;
99
+ const [currentDialog, setCurrentDialog] = useState<DialogQueueItem | null>(null);
100
+ const dialogManager = useRef(DialogManager.getInstance());
101
+
102
+ const handleDismissKeyboard = () => {
103
+ Keyboard.dismiss();
104
+ };
105
+ const [isKeyboardVisible, setIsKeyboardVisible] = useState(false);
106
+
107
+ // Suscribirse a cambios en el diálogo actual
108
+ useEffect(() => {
109
+ const unsubscribe = dialogManager.current.subscribe(() => {
110
+ setCurrentDialog(dialogManager.current.getCurrentDialog());
111
+ });
112
+ return () => {
113
+ unsubscribe();
114
+ };
115
+ }, []);
116
+
117
+ // Manejar cuando se debe mostrar/ocultar el diálogo
118
+ useEffect(() => {
119
+ if (isVisible) {
120
+ dialogManager.current.addDialog({
121
+ ...props,
122
+ priority: priority || 0, // Si no se especifica prioridad, usar 0 (sin prioridad)
123
+ onDismiss: () => {
124
+ Animated.timing(slideAnim, {
125
+ toValue: 0,
126
+ duration: 200,
127
+ useNativeDriver: true,
128
+ }).start(() => {
129
+ dialogManager.current.removeDialog(currentDialog?.id || '');
130
+ onDismiss();
131
+ });
132
+ },
133
+ });
134
+ } else {
135
+ if (currentDialog?.id) {
136
+ dialogManager.current.removeDialog(currentDialog.id);
137
+ }
138
+ }
139
+ }, [isVisible, priority]);
140
+
141
+ // Animación basada en si este diálogo es el actual
142
+ useEffect(() => {
143
+ const isCurrentDialog = currentDialog?.id === props.title; // Usar title como identificador único
144
+ if (isCurrentDialog) {
145
+ Animated.timing(slideAnim, {
146
+ toValue: 1,
147
+ duration: 300,
148
+ useNativeDriver: true,
149
+ }).start();
150
+ } else {
151
+ slideAnim.setValue(0);
152
+ }
153
+ }, [currentDialog, slideAnim, props.title]);
154
+
155
+ useEffect(() => {
156
+ const keyboardDidShowListener = Keyboard.addListener('keyboardDidShow', () => setIsKeyboardVisible(true));
157
+ const keyboardDidHideListener = Keyboard.addListener('keyboardDidHide', () =>
158
+ setIsKeyboardVisible(false)
159
+ );
160
+
161
+ return () => {
162
+ keyboardDidShowListener.remove();
163
+ keyboardDidHideListener.remove();
164
+ };
165
+ }, []);
166
+
167
+ const handleDismiss = () => {
168
+ if (currentDialog?.id) {
169
+ dialogManager.current.removeDialog(currentDialog.id);
170
+ }
171
+ };
172
+
173
+ // Solo renderizar si este diálogo es el actual
174
+ const isCurrentDialog = Boolean(currentDialog && currentDialog.title === title);
175
+
176
+ return (
177
+ <Modal visible={isCurrentDialog} transparent={true} animationType="none" onRequestClose={handleDismiss}>
178
+ <TouchableWithoutFeedback onPress={handleDismissKeyboard}>
179
+ <View style={stylesDialog.modalBackground}>
180
+ <Animated.View
181
+ style={[
182
+ stylesDialog['dialog-down'],
183
+ !isKeyboardVisible && stylesDialog['dialog-bottom'],
184
+ {
185
+ transform: [
186
+ {
187
+ translateY: slideAnim.interpolate({
188
+ inputRange: [0, 1],
189
+ outputRange: [300, 0],
190
+ }),
191
+ },
192
+ ],
193
+ },
194
+ ]}
195
+ >
196
+ {showCloseButton && (
197
+ <TouchableHighlight
198
+ onPress={handleDismiss}
199
+ underlayColor={theme.colors.background_focus}
200
+ style={stylesDialog['dialog-down-close']}
201
+ >
202
+ <Icon
203
+ source="close"
204
+ color={stylesDialog['dialog-down-close-icon'].color}
205
+ size={stylesDialog['dialog-down-close-icon'].width}
206
+ />
207
+ </TouchableHighlight>
208
+ )}
209
+
210
+ {image ? (
211
+ <View style={stylesDialog['dialog-img']}>{image}</View>
212
+ ) : (
213
+ <View style={{ marginTop: image ? 0 : -16, position: 'relative' }}>
214
+ <Text style={[stylesDialog['dialog-title']]}>{title}</Text>
215
+ </View>
216
+ )}
217
+
218
+ {children}
219
+ </Animated.View>
220
+ </View>
221
+ </TouchableWithoutFeedback>
222
+ </Modal>
223
+ );
224
+ };
package/src/store.ts CHANGED
@@ -2,13 +2,12 @@ import { create } from 'zustand';
2
2
  import { darkTheme, getGlobalTheme, lightTheme } from './config/paper.config';
3
3
  import { ITheme } from './models';
4
4
 
5
- const useStore = create<
6
- {
7
- theme: ITheme,
8
- setTheme: (newTheme: any) => void
9
- }>((set) => ({
10
- theme: getGlobalTheme(),
11
- setTheme: (newTheme: ITheme) => set((state: any) => ({ theme: newTheme })),
12
- }));
5
+ const useStore = create<{
6
+ theme: ITheme;
7
+ setTheme: (newTheme: any) => void;
8
+ }>((set) => ({
9
+ theme: getGlobalTheme(),
10
+ setTheme: (newTheme: ITheme) => set((state: any) => ({ theme: newTheme })),
11
+ }));
13
12
 
14
13
  export default useStore;