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 +1 -1
- package/package.json +1 -1
- package/src/elements/dialogs/DialogDown.tsx +224 -101
- package/src/store.ts +7 -8
package/app.json
CHANGED
package/package.json
CHANGED
|
@@ -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
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
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
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
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;
|