react-crud-mobile 1.3.557 → 1.3.560
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/dist/index.js +14 -37
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +15 -45
- package/dist/index.mjs.map +1 -1
- package/package.json +1 -1
- package/src/elements/core/UIButton.tsx +1 -1
- package/src/elements/core/UIModal.tsx +1 -1
- package/src/elements/core/UIOrder.tsx +235 -271
package/package.json
CHANGED
|
@@ -69,7 +69,7 @@ export default function UIModal(props: UIModalType) {
|
|
|
69
69
|
);
|
|
70
70
|
};
|
|
71
71
|
return (
|
|
72
|
-
<Modal key={key} animationType="slide"
|
|
72
|
+
<Modal key={key} animationType="slide" visible={true} onRequestClose={onClose}>
|
|
73
73
|
<ModalInner />
|
|
74
74
|
</Modal>
|
|
75
75
|
);
|
|
@@ -1,286 +1,250 @@
|
|
|
1
1
|
import React, { useRef, useState, useEffect } from 'react';
|
|
2
2
|
import { ChildType, OptionType, Utils } from 'react-crud-utils';
|
|
3
|
-
import {
|
|
4
|
-
View,
|
|
5
|
-
Text,
|
|
6
|
-
StyleSheet,
|
|
7
|
-
Animated,
|
|
8
|
-
PanResponder,
|
|
9
|
-
TouchableOpacity,
|
|
10
|
-
} from 'react-native';
|
|
3
|
+
import { View, Text, StyleSheet, Animated, PanResponder, TouchableOpacity } from 'react-native';
|
|
11
4
|
|
|
12
5
|
const ITEM_HEIGHT = 70;
|
|
13
6
|
|
|
14
7
|
export default function UIOrder(props: ChildType) {
|
|
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
|
-
positions[it.value] = new Animated.Value(idx * ITEM_HEIGHT);
|
|
48
|
-
|
|
49
|
-
Animated.spring(positions[it.value], {
|
|
50
|
-
toValue: idx * ITEM_HEIGHT,
|
|
51
|
-
useNativeDriver: false,
|
|
52
|
-
}).start();
|
|
53
|
-
});
|
|
54
|
-
}, [items]);
|
|
55
|
-
|
|
56
|
-
const onChange = (updated: any[]) => {
|
|
57
|
-
const array: any[] = [];
|
|
58
|
-
const items = scope.getItems();
|
|
59
|
-
|
|
60
|
-
items.splice(0);
|
|
61
|
-
|
|
62
|
-
Utils.each(updated, o => {
|
|
63
|
-
const v = o.object;
|
|
64
|
-
|
|
65
|
-
array.push(v);
|
|
66
|
-
items.push(v);
|
|
67
|
-
});
|
|
68
|
-
|
|
69
|
-
scope.changeValue(array);
|
|
70
|
-
};
|
|
71
|
-
|
|
72
|
-
const createPanResponder = (item: OptionType) =>
|
|
73
|
-
PanResponder.create({
|
|
74
|
-
onStartShouldSetPanResponder: () => true,
|
|
75
|
-
onPanResponderGrant: () => {
|
|
76
|
-
// Use itemsRef.current para obter a lista mais recente no momento do arrasto.
|
|
77
|
-
const currentItems = itemsRef.current;
|
|
78
|
-
if (
|
|
79
|
-
!positions[item.value] ||
|
|
80
|
-
!currentItems.find(it => it.value === item.value)
|
|
81
|
-
)
|
|
82
|
-
return;
|
|
83
|
-
|
|
84
|
-
activeItem.current = item;
|
|
85
|
-
activeIndex.current = currentItems.findIndex(
|
|
86
|
-
it => it.value === item.value
|
|
87
|
-
);
|
|
88
|
-
// CORREÇÃO: Usa a propriedade interna _value para acessar o valor atual
|
|
89
|
-
offsetY.current = (positions[item.value] as any)._value; // 👈 CORREÇÃO APLICADA
|
|
90
|
-
setDraggingId(item.value);
|
|
91
|
-
},
|
|
92
|
-
onPanResponderMove: (_, gesture) => {
|
|
93
|
-
const currentItems = itemsRef.current;
|
|
94
|
-
if (
|
|
95
|
-
!positions[item.value] ||
|
|
96
|
-
!currentItems.find(it => it.value === item.value)
|
|
97
|
-
)
|
|
98
|
-
return;
|
|
99
|
-
|
|
100
|
-
const y = offsetY.current + gesture.dy;
|
|
101
|
-
positions[item.value].setValue(y);
|
|
102
|
-
|
|
103
|
-
const targetIndex = Math.max(
|
|
104
|
-
0,
|
|
105
|
-
Math.min(
|
|
106
|
-
currentItems.length - 1,
|
|
107
|
-
Math.floor((y + ITEM_HEIGHT / 2) / ITEM_HEIGHT)
|
|
108
|
-
)
|
|
109
|
-
);
|
|
110
|
-
|
|
111
|
-
currentItems.forEach((other, idx) => {
|
|
112
|
-
if (other.value === item.value || !positions[other.value]) return;
|
|
113
|
-
let targetY = idx * ITEM_HEIGHT;
|
|
114
|
-
if (idx > activeIndex.current && idx <= targetIndex)
|
|
115
|
-
targetY = (idx - 1) * ITEM_HEIGHT;
|
|
116
|
-
else if (idx < activeIndex.current && idx >= targetIndex)
|
|
117
|
-
targetY = (idx + 1) * ITEM_HEIGHT;
|
|
118
|
-
|
|
119
|
-
Animated.timing(positions[other.value], {
|
|
120
|
-
toValue: targetY,
|
|
121
|
-
duration: 120,
|
|
122
|
-
useNativeDriver: false,
|
|
123
|
-
}).start();
|
|
124
|
-
});
|
|
125
|
-
},
|
|
126
|
-
onPanResponderRelease: () => {
|
|
127
|
-
const moved = activeItem.current;
|
|
128
|
-
const currentItems = itemsRef.current;
|
|
129
|
-
|
|
130
|
-
if (
|
|
131
|
-
!moved ||
|
|
132
|
-
!positions[moved.value] ||
|
|
133
|
-
!currentItems.find(it => it.value === moved.value)
|
|
134
|
-
)
|
|
135
|
-
return;
|
|
136
|
-
|
|
137
|
-
// CORREÇÃO: Usa a propriedade interna _value para obter a posição final
|
|
138
|
-
const finalY = (positions[moved.value] as any)._value; // 👈 CORREÇÃO APLICADA
|
|
139
|
-
const newIndex = Math.max(
|
|
140
|
-
0,
|
|
141
|
-
Math.min(
|
|
142
|
-
currentItems.length - 1,
|
|
143
|
-
Math.floor((finalY + ITEM_HEIGHT / 2) / ITEM_HEIGHT)
|
|
144
|
-
)
|
|
145
|
-
);
|
|
146
|
-
|
|
147
|
-
const oldIndex = activeIndex.current;
|
|
148
|
-
const updated = [...currentItems];
|
|
149
|
-
const [removed] = updated.splice(oldIndex, 1);
|
|
150
|
-
updated.splice(newIndex, 0, removed);
|
|
151
|
-
|
|
152
|
-
// Atualiza o state com a nova ordem.
|
|
153
|
-
setItems(updated);
|
|
154
|
-
onChange(updated);
|
|
155
|
-
|
|
156
|
-
// A animação final agora está no useEffect, mas fazemos a limpeza das refs.
|
|
157
|
-
setDraggingId(null);
|
|
158
|
-
activeItem.current = null;
|
|
159
|
-
activeIndex.current = -1;
|
|
160
|
-
},
|
|
161
|
-
});
|
|
162
|
-
|
|
163
|
-
// Cria/Reusa os PanResponders
|
|
164
|
-
items.forEach(it => {
|
|
165
|
-
if (!panRespondersRef.current[it.value]) {
|
|
166
|
-
panRespondersRef.current[it.value] = createPanResponder(it);
|
|
167
|
-
}
|
|
168
|
-
});
|
|
169
|
-
|
|
170
|
-
const removeItem = (id: string) => {
|
|
171
|
-
if (draggingId === id) {
|
|
172
|
-
setDraggingId(null);
|
|
173
|
-
activeItem.current = null;
|
|
174
|
-
activeIndex.current = -1;
|
|
175
|
-
}
|
|
176
|
-
|
|
177
|
-
// CORREÇÃO: Delete a referência Animated.Value *e* a referência PanResponder
|
|
178
|
-
if (positions[id]) {
|
|
179
|
-
delete positions[id];
|
|
180
|
-
}
|
|
181
|
-
if (panRespondersRef.current[id]) {
|
|
182
|
-
delete panRespondersRef.current[id];
|
|
183
|
-
}
|
|
184
|
-
|
|
185
|
-
setItems(prev => {
|
|
186
|
-
const updated = prev.filter(it => it.value !== id);
|
|
187
|
-
|
|
188
|
-
// Anima os itens restantes para seus novos índices
|
|
189
|
-
updated.forEach((it, idx) => {
|
|
190
|
-
if (positions[it.value])
|
|
191
|
-
Animated.spring(positions[it.value], {
|
|
8
|
+
const scope = props.scope;
|
|
9
|
+
const element = scope.original;
|
|
10
|
+
const initial: OptionType[] = scope.getOptions();
|
|
11
|
+
|
|
12
|
+
const [items, setItems] = useState<OptionType[]>(initial);
|
|
13
|
+
const [draggingId, setDraggingId] = useState<string | null>(null);
|
|
14
|
+
|
|
15
|
+
// O tipo Animated.Value em um ambiente TypeScript puro não tem o método ._value,
|
|
16
|
+
// mas é a propriedade interna usada para acessar o valor diretamente.
|
|
17
|
+
// Usamos 'any' no acesso para suprimir o erro TS.
|
|
18
|
+
const positions = useRef<Record<string, Animated.Value>>(
|
|
19
|
+
Object.fromEntries(initial.map((it, i) => [it.value, new Animated.Value(i * ITEM_HEIGHT)])),
|
|
20
|
+
).current;
|
|
21
|
+
|
|
22
|
+
const panRespondersRef = useRef<Record<string, any>>({});
|
|
23
|
+
|
|
24
|
+
const activeItem = useRef<OptionType | null>(null);
|
|
25
|
+
const activeIndex = useRef<number>(-1);
|
|
26
|
+
const offsetY = useRef(0);
|
|
27
|
+
|
|
28
|
+
// Armazena a lista mais recente em uma ref para uso em funções de PanResponder
|
|
29
|
+
const itemsRef = useRef(items);
|
|
30
|
+
useEffect(() => {
|
|
31
|
+
itemsRef.current = items;
|
|
32
|
+
}, [items]);
|
|
33
|
+
|
|
34
|
+
// 1. Atualiza posições apenas para itens existentes
|
|
35
|
+
useEffect(() => {
|
|
36
|
+
items.forEach((it, idx) => {
|
|
37
|
+
if (!positions[it.value]) positions[it.value] = new Animated.Value(idx * ITEM_HEIGHT);
|
|
38
|
+
|
|
39
|
+
Animated.spring(positions[it.value], {
|
|
192
40
|
toValue: idx * ITEM_HEIGHT,
|
|
193
41
|
useNativeDriver: false,
|
|
194
|
-
|
|
42
|
+
}).start();
|
|
195
43
|
});
|
|
44
|
+
}, [items]);
|
|
45
|
+
|
|
46
|
+
const onChange = (updated: any[]) => {
|
|
47
|
+
const array: any[] = [];
|
|
48
|
+
const items = scope.getItems();
|
|
49
|
+
|
|
50
|
+
items.splice(0);
|
|
196
51
|
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
52
|
+
Utils.each(updated, (o) => {
|
|
53
|
+
const v = o.object;
|
|
54
|
+
|
|
55
|
+
array.push(v);
|
|
56
|
+
items.push(v);
|
|
57
|
+
});
|
|
58
|
+
|
|
59
|
+
scope.changeValue(array);
|
|
60
|
+
};
|
|
61
|
+
|
|
62
|
+
const createPanResponder = (item: OptionType) =>
|
|
63
|
+
PanResponder.create({
|
|
64
|
+
onStartShouldSetPanResponder: () => true,
|
|
65
|
+
onPanResponderGrant: () => {
|
|
66
|
+
// Use itemsRef.current para obter a lista mais recente no momento do arrasto.
|
|
67
|
+
const currentItems = itemsRef.current;
|
|
68
|
+
if (!positions[item.value] || !currentItems.find((it) => it.value === item.value)) return;
|
|
69
|
+
|
|
70
|
+
activeItem.current = item;
|
|
71
|
+
activeIndex.current = currentItems.findIndex((it) => it.value === item.value);
|
|
72
|
+
// CORREÇÃO: Usa a propriedade interna _value para acessar o valor atual
|
|
73
|
+
offsetY.current = (positions[item.value] as any)._value; // 👈 CORREÇÃO APLICADA
|
|
74
|
+
setDraggingId(item.value);
|
|
75
|
+
},
|
|
76
|
+
onPanResponderMove: (_, gesture) => {
|
|
77
|
+
const currentItems = itemsRef.current;
|
|
78
|
+
if (!positions[item.value] || !currentItems.find((it) => it.value === item.value)) return;
|
|
79
|
+
|
|
80
|
+
const y = offsetY.current + gesture.dy;
|
|
81
|
+
positions[item.value].setValue(y);
|
|
82
|
+
|
|
83
|
+
const targetIndex = Math.max(
|
|
84
|
+
0,
|
|
85
|
+
Math.min(currentItems.length - 1, Math.floor((y + ITEM_HEIGHT / 2) / ITEM_HEIGHT)),
|
|
86
|
+
);
|
|
87
|
+
|
|
88
|
+
currentItems.forEach((other, idx) => {
|
|
89
|
+
if (other.value === item.value || !positions[other.value]) return;
|
|
90
|
+
let targetY = idx * ITEM_HEIGHT;
|
|
91
|
+
if (idx > activeIndex.current && idx <= targetIndex) targetY = (idx - 1) * ITEM_HEIGHT;
|
|
92
|
+
else if (idx < activeIndex.current && idx >= targetIndex) targetY = (idx + 1) * ITEM_HEIGHT;
|
|
93
|
+
|
|
94
|
+
Animated.timing(positions[other.value], {
|
|
95
|
+
toValue: targetY,
|
|
96
|
+
duration: 120,
|
|
97
|
+
useNativeDriver: false,
|
|
98
|
+
}).start();
|
|
99
|
+
});
|
|
100
|
+
},
|
|
101
|
+
onPanResponderRelease: () => {
|
|
102
|
+
const moved = activeItem.current;
|
|
103
|
+
const currentItems = itemsRef.current;
|
|
104
|
+
|
|
105
|
+
if (!moved || !positions[moved.value] || !currentItems.find((it) => it.value === moved.value)) return;
|
|
106
|
+
|
|
107
|
+
// CORREÇÃO: Usa a propriedade interna _value para obter a posição final
|
|
108
|
+
const finalY = (positions[moved.value] as any)._value; // 👈 CORREÇÃO APLICADA
|
|
109
|
+
const newIndex = Math.max(
|
|
110
|
+
0,
|
|
111
|
+
Math.min(currentItems.length - 1, Math.floor((finalY + ITEM_HEIGHT / 2) / ITEM_HEIGHT)),
|
|
112
|
+
);
|
|
113
|
+
|
|
114
|
+
const oldIndex = activeIndex.current;
|
|
115
|
+
const updated = [...currentItems];
|
|
116
|
+
const [removed] = updated.splice(oldIndex, 1);
|
|
117
|
+
updated.splice(newIndex, 0, removed);
|
|
118
|
+
|
|
119
|
+
// Atualiza o state com a nova ordem.
|
|
120
|
+
setItems(updated);
|
|
121
|
+
onChange(updated);
|
|
122
|
+
|
|
123
|
+
// A animação final agora está no useEffect, mas fazemos a limpeza das refs.
|
|
124
|
+
setDraggingId(null);
|
|
125
|
+
activeItem.current = null;
|
|
126
|
+
activeIndex.current = -1;
|
|
127
|
+
},
|
|
128
|
+
});
|
|
129
|
+
|
|
130
|
+
// Cria/Reusa os PanResponders
|
|
131
|
+
items.forEach((it) => {
|
|
132
|
+
if (!panRespondersRef.current[it.value]) {
|
|
133
|
+
panRespondersRef.current[it.value] = createPanResponder(it);
|
|
134
|
+
}
|
|
135
|
+
});
|
|
136
|
+
|
|
137
|
+
const removeItem = (id: string) => {
|
|
138
|
+
if (draggingId === id) {
|
|
139
|
+
setDraggingId(null);
|
|
140
|
+
activeItem.current = null;
|
|
141
|
+
activeIndex.current = -1;
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
// CORREÇÃO: Delete a referência Animated.Value *e* a referência PanResponder
|
|
145
|
+
if (positions[id]) {
|
|
146
|
+
delete positions[id];
|
|
147
|
+
}
|
|
148
|
+
if (panRespondersRef.current[id]) {
|
|
149
|
+
delete panRespondersRef.current[id];
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
setItems((prev) => {
|
|
153
|
+
const updated = prev.filter((it) => it.value !== id);
|
|
154
|
+
|
|
155
|
+
// Anima os itens restantes para seus novos índices
|
|
156
|
+
updated.forEach((it, idx) => {
|
|
157
|
+
if (positions[it.value])
|
|
158
|
+
Animated.spring(positions[it.value], {
|
|
159
|
+
toValue: idx * ITEM_HEIGHT,
|
|
160
|
+
useNativeDriver: false,
|
|
161
|
+
}).start();
|
|
162
|
+
});
|
|
163
|
+
|
|
164
|
+
onChange(updated);
|
|
165
|
+
return updated;
|
|
166
|
+
});
|
|
167
|
+
};
|
|
168
|
+
|
|
169
|
+
const Empty = () => {
|
|
170
|
+
if (Utils.isEmpty(items)) {
|
|
171
|
+
let empty = scope.part('empty', 'Sem registro');
|
|
172
|
+
|
|
173
|
+
if (empty !== false) return <>{empty}</>;
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
return <></>;
|
|
177
|
+
};
|
|
178
|
+
|
|
179
|
+
return (
|
|
180
|
+
<View style={styles.container}>
|
|
181
|
+
<Empty />
|
|
182
|
+
<View style={{ height: items.length * ITEM_HEIGHT, width: '100%', ...scope.getStyle('container') }}>
|
|
183
|
+
{items.map((item) => {
|
|
184
|
+
const y = positions[item.value];
|
|
185
|
+
if (!y) return null;
|
|
186
|
+
|
|
187
|
+
const isDragging = draggingId === item.value;
|
|
188
|
+
const itemStyle = scope.getStyle('row', { ...styles.item });
|
|
189
|
+
|
|
190
|
+
return (
|
|
191
|
+
<Animated.View
|
|
192
|
+
key={item.value}
|
|
193
|
+
{...(panRespondersRef.current[item.value]?.panHandlers ?? {})}
|
|
194
|
+
style={[
|
|
195
|
+
itemStyle,
|
|
196
|
+
{
|
|
197
|
+
position: 'absolute',
|
|
198
|
+
left: 0,
|
|
199
|
+
right: 0,
|
|
200
|
+
height: ITEM_HEIGHT - 8,
|
|
201
|
+
transform: [{ translateY: y }],
|
|
202
|
+
zIndex: isDragging ? 999 : 0,
|
|
203
|
+
elevation: isDragging ? 999 : 0,
|
|
204
|
+
},
|
|
205
|
+
]}
|
|
206
|
+
>
|
|
207
|
+
<View style={styles.handle}>
|
|
208
|
+
<Text style={styles.handleText}>≡</Text>
|
|
209
|
+
</View>
|
|
210
|
+
<Text style={styles.itemText}>{item.label}</Text>
|
|
211
|
+
{element.remove && (
|
|
212
|
+
<TouchableOpacity onPress={() => removeItem(item.value)} style={styles.del}>
|
|
213
|
+
<Text style={{ color: '#fff' }}>X</Text>
|
|
214
|
+
</TouchableOpacity>
|
|
215
|
+
)}
|
|
216
|
+
</Animated.View>
|
|
217
|
+
);
|
|
218
|
+
})}
|
|
219
|
+
</View>
|
|
255
220
|
</View>
|
|
256
|
-
|
|
257
|
-
);
|
|
221
|
+
);
|
|
258
222
|
}
|
|
259
223
|
|
|
260
224
|
const styles = StyleSheet.create({
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
225
|
+
container: { flex: 1 },
|
|
226
|
+
title: { fontSize: 18, fontWeight: '600', marginBottom: 12 },
|
|
227
|
+
item: {
|
|
228
|
+
backgroundColor: '#fff',
|
|
229
|
+
borderRadius: 10,
|
|
230
|
+
marginVertical: 4,
|
|
231
|
+
paddingHorizontal: 12,
|
|
232
|
+
alignItems: 'center',
|
|
233
|
+
flexDirection: 'row',
|
|
234
|
+
elevation: 3,
|
|
235
|
+
shadowColor: '#000',
|
|
236
|
+
shadowOpacity: 0.05,
|
|
237
|
+
shadowRadius: 6,
|
|
238
|
+
},
|
|
239
|
+
handle: { width: 40, alignItems: 'center', justifyContent: 'center' },
|
|
240
|
+
handleText: { fontSize: 22, color: '#666' },
|
|
241
|
+
itemText: { fontSize: 16, flex: 1 },
|
|
242
|
+
del: {
|
|
243
|
+
backgroundColor: '#e74c3c',
|
|
244
|
+
height: 34,
|
|
245
|
+
width: 34,
|
|
246
|
+
borderRadius: 6,
|
|
247
|
+
alignItems: 'center',
|
|
248
|
+
justifyContent: 'center',
|
|
249
|
+
},
|
|
286
250
|
});
|