@acvl/mobile-components 0.0.4 → 0.0.6
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 +15 -1
- package/lib/module/camara/CampoDeFotos.js +4 -2
- package/lib/module/camara/CampoDeFotos.js.map +1 -1
- package/lib/module/camara/elegir.js +4 -3
- package/lib/module/camara/elegir.js.map +1 -1
- package/lib/module/componentes/AccionesDelEncabezado.js +3 -1
- package/lib/module/componentes/AccionesDelEncabezado.js.map +1 -1
- package/lib/module/componentes/BotonDeAvatar.js +24 -20
- package/lib/module/componentes/BotonDeAvatar.js.map +1 -1
- package/lib/module/componentes/BotonFlotante.js +71 -0
- package/lib/module/componentes/BotonFlotante.js.map +1 -0
- package/lib/module/componentes/CabeceraEmpujada.js +3 -1
- package/lib/module/componentes/CabeceraEmpujada.js.map +1 -1
- package/lib/module/componentes/PantallaDeError.js +6 -4
- package/lib/module/componentes/PantallaDeError.js.map +1 -1
- package/lib/module/componentes/TiraDeFotos.js +4 -2
- package/lib/module/componentes/TiraDeFotos.js.map +1 -1
- package/lib/module/componentes/VisorDeFoto.js +6 -4
- package/lib/module/componentes/VisorDeFoto.js.map +1 -1
- package/lib/module/dialogos/HostDeDialogos.js +3 -1
- package/lib/module/dialogos/HostDeDialogos.js.map +1 -1
- package/lib/module/dialogos/dialogos.js +6 -4
- package/lib/module/dialogos/dialogos.js.map +1 -1
- package/lib/module/formularios/SelectorDeOpcion.js +13 -7
- package/lib/module/formularios/SelectorDeOpcion.js.map +1 -1
- package/lib/module/formularios/campos.js +9 -4
- package/lib/module/formularios/campos.js.map +1 -1
- package/lib/module/formularios/guardado.js +4 -2
- package/lib/module/formularios/guardado.js.map +1 -1
- package/lib/module/fotos/elegir.js +10 -8
- package/lib/module/fotos/elegir.js.map +1 -1
- package/lib/module/hoja/HojaDeLista.js +5 -3
- package/lib/module/hoja/HojaDeLista.js.map +1 -1
- package/lib/module/hoja/useMasDeLaFicha.js +3 -1
- package/lib/module/hoja/useMasDeLaFicha.js.map +1 -1
- package/lib/module/idioma/ProveedorDeIdioma.js +54 -0
- package/lib/module/idioma/ProveedorDeIdioma.js.map +1 -0
- package/lib/module/idioma/index.js +4 -0
- package/lib/module/idioma/index.js.map +1 -0
- package/lib/module/idioma/textos.js +237 -0
- package/lib/module/idioma/textos.js.map +1 -0
- package/lib/module/index.js +5 -0
- package/lib/module/index.js.map +1 -1
- package/lib/module/listas/HojaDeFiltros.js +11 -8
- package/lib/module/listas/HojaDeFiltros.js.map +1 -1
- package/lib/module/listas/ListaPaginada.js +6 -4
- package/lib/module/listas/ListaPaginada.js.map +1 -1
- package/lib/module/listas/piezas.js +13 -7
- package/lib/module/listas/piezas.js.map +1 -1
- package/lib/module/pantallas/PantallaDeAcceso.js +6 -4
- package/lib/module/pantallas/PantallaDeAcceso.js.map +1 -1
- package/lib/module/pantallas/PantallaDeEdicion.js +9 -6
- package/lib/module/pantallas/PantallaDeEdicion.js.map +1 -1
- package/lib/module/pantallas/PantallaDeFicha.js +7 -5
- package/lib/module/pantallas/PantallaDeFicha.js.map +1 -1
- package/lib/module/pantallas/PantallaDeHistorial.js +18 -14
- package/lib/module/pantallas/PantallaDeHistorial.js.map +1 -1
- package/lib/module/pantallas/PantallaDePerfil.js +12 -10
- package/lib/module/pantallas/PantallaDePerfil.js.map +1 -1
- package/lib/typescript/src/camara/CampoDeFotos.d.ts.map +1 -1
- package/lib/typescript/src/camara/elegir.d.ts.map +1 -1
- package/lib/typescript/src/componentes/AccionesDelEncabezado.d.ts.map +1 -1
- package/lib/typescript/src/componentes/BotonDeAvatar.d.ts.map +1 -1
- package/lib/typescript/src/componentes/BotonFlotante.d.ts +21 -0
- package/lib/typescript/src/componentes/BotonFlotante.d.ts.map +1 -0
- package/lib/typescript/src/componentes/CabeceraEmpujada.d.ts.map +1 -1
- package/lib/typescript/src/componentes/PantallaDeError.d.ts.map +1 -1
- package/lib/typescript/src/componentes/TiraDeFotos.d.ts.map +1 -1
- package/lib/typescript/src/componentes/VisorDeFoto.d.ts.map +1 -1
- package/lib/typescript/src/dialogos/HostDeDialogos.d.ts.map +1 -1
- package/lib/typescript/src/dialogos/dialogos.d.ts.map +1 -1
- package/lib/typescript/src/formularios/SelectorDeOpcion.d.ts.map +1 -1
- package/lib/typescript/src/formularios/campos.d.ts.map +1 -1
- package/lib/typescript/src/formularios/guardado.d.ts.map +1 -1
- package/lib/typescript/src/fotos/elegir.d.ts.map +1 -1
- package/lib/typescript/src/hoja/HojaDeLista.d.ts.map +1 -1
- package/lib/typescript/src/hoja/useMasDeLaFicha.d.ts.map +1 -1
- package/lib/typescript/src/idioma/ProveedorDeIdioma.d.ts +16 -0
- package/lib/typescript/src/idioma/ProveedorDeIdioma.d.ts.map +1 -0
- package/lib/typescript/src/idioma/index.d.ts +3 -0
- package/lib/typescript/src/idioma/index.d.ts.map +1 -0
- package/lib/typescript/src/idioma/textos.d.ts +124 -0
- package/lib/typescript/src/idioma/textos.d.ts.map +1 -0
- package/lib/typescript/src/index.d.ts +2 -0
- package/lib/typescript/src/index.d.ts.map +1 -1
- package/lib/typescript/src/listas/HojaDeFiltros.d.ts.map +1 -1
- package/lib/typescript/src/listas/ListaPaginada.d.ts.map +1 -1
- package/lib/typescript/src/listas/piezas.d.ts.map +1 -1
- package/lib/typescript/src/pantallas/PantallaDeAcceso.d.ts.map +1 -1
- package/lib/typescript/src/pantallas/PantallaDeEdicion.d.ts.map +1 -1
- package/lib/typescript/src/pantallas/PantallaDeFicha.d.ts.map +1 -1
- package/lib/typescript/src/pantallas/PantallaDeHistorial.d.ts.map +1 -1
- package/lib/typescript/src/pantallas/PantallaDePerfil.d.ts.map +1 -1
- package/package.json +1 -1
- package/src/camara/CampoDeFotos.tsx +4 -2
- package/src/camara/elegir.ts +4 -3
- package/src/componentes/AccionesDelEncabezado.tsx +3 -1
- package/src/componentes/BotonDeAvatar.tsx +22 -18
- package/src/componentes/BotonFlotante.tsx +65 -0
- package/src/componentes/CabeceraEmpujada.tsx +3 -1
- package/src/componentes/PantallaDeError.tsx +6 -4
- package/src/componentes/TiraDeFotos.tsx +4 -2
- package/src/componentes/VisorDeFoto.tsx +7 -7
- package/src/dialogos/HostDeDialogos.tsx +3 -1
- package/src/dialogos/dialogos.ts +15 -4
- package/src/formularios/SelectorDeOpcion.tsx +22 -8
- package/src/formularios/campos.tsx +9 -4
- package/src/formularios/guardado.ts +4 -2
- package/src/fotos/elegir.ts +14 -10
- package/src/hoja/HojaDeLista.tsx +5 -5
- package/src/hoja/useMasDeLaFicha.tsx +3 -1
- package/src/idioma/ProveedorDeIdioma.tsx +51 -0
- package/src/idioma/index.ts +7 -0
- package/src/idioma/textos.ts +246 -0
- package/src/index.ts +12 -0
- package/src/listas/HojaDeFiltros.tsx +10 -9
- package/src/listas/ListaPaginada.tsx +6 -6
- package/src/listas/piezas.tsx +17 -10
- package/src/pantallas/PantallaDeAcceso.tsx +6 -4
- package/src/pantallas/PantallaDeEdicion.tsx +13 -6
- package/src/pantallas/PantallaDeFicha.tsx +7 -5
- package/src/pantallas/PantallaDeHistorial.tsx +22 -16
- package/src/pantallas/PantallaDePerfil.tsx +12 -10
|
@@ -15,6 +15,7 @@ import IconoDeRenglon from "../componentes/IconoDeRenglon";
|
|
|
15
15
|
import { crearEstilos, renglon, tipografia, usePaleta } from "../tema";
|
|
16
16
|
import { Text, TextInput } from "../texto";
|
|
17
17
|
import { CajaDeCampo } from "./campos";
|
|
18
|
+
import { useTextos } from "../idioma";
|
|
18
19
|
|
|
19
20
|
/** Una opción de una lista: lo que se guarda (`value`) y lo que se lee (`label`). */
|
|
20
21
|
export interface Opcion {
|
|
@@ -111,7 +112,7 @@ function HojaDeOpciones<T extends Opcion>({
|
|
|
111
112
|
onTocar,
|
|
112
113
|
detalle,
|
|
113
114
|
grupo,
|
|
114
|
-
vacio
|
|
115
|
+
vacio,
|
|
115
116
|
encabezado,
|
|
116
117
|
pie,
|
|
117
118
|
}: {
|
|
@@ -133,12 +134,13 @@ function HojaDeOpciones<T extends Opcion>({
|
|
|
133
134
|
const c = usePaleta();
|
|
134
135
|
const styles = useStyles();
|
|
135
136
|
const insets = useSafeAreaInsets();
|
|
137
|
+
const t = useTextos();
|
|
136
138
|
|
|
137
139
|
return (
|
|
138
140
|
<Modal visible={abierto} animationType="slide" onRequestClose={onCerrar}>
|
|
139
141
|
<View style={[styles.hoja, { paddingTop: insets.top + 10 }]}>
|
|
140
142
|
<View style={styles.cabecera}>
|
|
141
|
-
<TouchableOpacity onPress={onCerrar}>
|
|
143
|
+
<TouchableOpacity onPress={onCerrar} accessibilityLabel={t.comun.atras}>
|
|
142
144
|
<MaterialIcons name="arrow-back" size={45} color={c.primary} />
|
|
143
145
|
</TouchableOpacity>
|
|
144
146
|
<Text style={styles.titulo}>{etiqueta}</Text>
|
|
@@ -146,7 +148,7 @@ function HojaDeOpciones<T extends Opcion>({
|
|
|
146
148
|
<TextInput
|
|
147
149
|
value={texto}
|
|
148
150
|
onChangeText={onBuscar}
|
|
149
|
-
placeholder=
|
|
151
|
+
placeholder={t.comun.buscar}
|
|
150
152
|
placeholderTextColor={c.secondaryLight}
|
|
151
153
|
autoCorrect={false}
|
|
152
154
|
autoCapitalize="characters"
|
|
@@ -163,7 +165,12 @@ function HojaDeOpciones<T extends Opcion>({
|
|
|
163
165
|
}}
|
|
164
166
|
ListHeaderComponent={encabezado}
|
|
165
167
|
ListEmptyComponent={
|
|
166
|
-
cargando ? null :
|
|
168
|
+
cargando ? null : (
|
|
169
|
+
<EstadoVacio
|
|
170
|
+
icono="magnify"
|
|
171
|
+
titulo={vacio ?? t.listas.nadaQueCoincida}
|
|
172
|
+
/>
|
|
173
|
+
)
|
|
167
174
|
}
|
|
168
175
|
renderItem={({ item, index }) => {
|
|
169
176
|
const rotulo = grupo?.(item);
|
|
@@ -217,6 +224,7 @@ function RenglonDelSelector({
|
|
|
217
224
|
}) {
|
|
218
225
|
const c = usePaleta();
|
|
219
226
|
const styles = useStyles();
|
|
227
|
+
const t = useTextos();
|
|
220
228
|
return (
|
|
221
229
|
<CajaDeCampo
|
|
222
230
|
etiqueta={etiqueta}
|
|
@@ -227,7 +235,9 @@ function RenglonDelSelector({
|
|
|
227
235
|
pendiente={pendiente}
|
|
228
236
|
>
|
|
229
237
|
<View style={styles.renglon}>
|
|
230
|
-
<Text style={[styles.valor, !texto && styles.sinValor]}>
|
|
238
|
+
<Text style={[styles.valor, !texto && styles.sinValor]}>
|
|
239
|
+
{texto ?? t.comun.elegir}
|
|
240
|
+
</Text>
|
|
231
241
|
<IconoDeRenglon
|
|
232
242
|
name="chevron-down"
|
|
233
243
|
fontSize={tipografia.subtitulo}
|
|
@@ -253,13 +263,14 @@ export default function SelectorDeOpcion<T extends Opcion>(props: PropsDeSelecto
|
|
|
253
263
|
detalle,
|
|
254
264
|
grupo,
|
|
255
265
|
opcional,
|
|
256
|
-
textoDeNinguno
|
|
266
|
+
textoDeNinguno,
|
|
257
267
|
vacio,
|
|
258
268
|
disabled,
|
|
259
269
|
accesorio,
|
|
260
270
|
resaltarVacio,
|
|
261
271
|
} = props;
|
|
262
272
|
const styles = useStyles();
|
|
273
|
+
const t = useTextos();
|
|
263
274
|
const [abierto, setAbierto] = React.useState(false);
|
|
264
275
|
const { texto, buscar, visibles } = useBuscador(props);
|
|
265
276
|
|
|
@@ -296,7 +307,9 @@ export default function SelectorDeOpcion<T extends Opcion>(props: PropsDeSelecto
|
|
|
296
307
|
encabezado={
|
|
297
308
|
opcional ? (
|
|
298
309
|
<Pressable onPress={() => elegir(null)} style={styles.opcion}>
|
|
299
|
-
<Text style={styles.opcionNombre}>
|
|
310
|
+
<Text style={styles.opcionNombre}>
|
|
311
|
+
{textoDeNinguno ?? t.comun.ninguno}
|
|
312
|
+
</Text>
|
|
300
313
|
</Pressable>
|
|
301
314
|
) : null
|
|
302
315
|
}
|
|
@@ -329,6 +342,7 @@ export function SelectorDeVarias<T extends Opcion>(props: PropsDeSelectorDeVaria
|
|
|
329
342
|
} = props;
|
|
330
343
|
const [abierto, setAbierto] = React.useState(false);
|
|
331
344
|
const [marcadas, setMarcadas] = React.useState<T[]>(valor);
|
|
345
|
+
const t = useTextos();
|
|
332
346
|
const { texto, buscar, visibles } = useBuscador(props);
|
|
333
347
|
|
|
334
348
|
const abrir = () => {
|
|
@@ -377,7 +391,7 @@ export function SelectorDeVarias<T extends Opcion>(props: PropsDeSelectorDeVaria
|
|
|
377
391
|
cerrar();
|
|
378
392
|
}}
|
|
379
393
|
>
|
|
380
|
-
{marcadas.length ?
|
|
394
|
+
{marcadas.length ? t.listas.listoCon(marcadas.length) : t.comun.listo}
|
|
381
395
|
</BotonGrande>
|
|
382
396
|
}
|
|
383
397
|
/>
|
|
@@ -12,6 +12,7 @@ import IconoDeRenglon from "../componentes/IconoDeRenglon";
|
|
|
12
12
|
import OpcionesExcluyentes from "../componentes/OpcionesExcluyentes";
|
|
13
13
|
import { crearEstilos, renglon, tipografia, usePaleta } from "../tema";
|
|
14
14
|
import { Text, TextInput } from "../texto";
|
|
15
|
+
import { useTextos } from "../idioma";
|
|
15
16
|
|
|
16
17
|
/**
|
|
17
18
|
* Los campos de un formulario (un viaje, un tracto, un operador). Cada uno es una caja con su
|
|
@@ -245,6 +246,7 @@ export function CampoDeFechaHora({
|
|
|
245
246
|
const c = usePaleta();
|
|
246
247
|
const styles = useStyles();
|
|
247
248
|
const [eligiendo, setEligiendo] = React.useState<"date" | "time" | null>(null);
|
|
249
|
+
const t = useTextos();
|
|
248
250
|
// Vacío, el selector arranca en la próxima hora en punto: la hora de ahora no le sirve a nadie.
|
|
249
251
|
const base = valor ?? dayjs().add(1, "hour").startOf("hour").toDate();
|
|
250
252
|
|
|
@@ -296,7 +298,7 @@ export function CampoDeFechaHora({
|
|
|
296
298
|
fontSize={tipografia.subtitulo}
|
|
297
299
|
color={c.secondaryLight}
|
|
298
300
|
/>
|
|
299
|
-
<Text style={[styles.textoFecha, styles.sinValor]}>
|
|
301
|
+
<Text style={[styles.textoFecha, styles.sinValor]}>{t.comun.elegir}</Text>
|
|
300
302
|
</View>
|
|
301
303
|
)}
|
|
302
304
|
</CajaDeCampo>
|
|
@@ -335,12 +337,13 @@ export function CampoDeFechaHora({
|
|
|
335
337
|
/** El «Quitar» de una fecha opcional: la ✕ junto al rótulo, dentro de la caja. */
|
|
336
338
|
function BotonDeQuitar({ onPress }: { onPress: () => void }) {
|
|
337
339
|
const c = usePaleta();
|
|
340
|
+
const t = useTextos();
|
|
338
341
|
return (
|
|
339
342
|
<Pressable
|
|
340
343
|
onPress={onPress}
|
|
341
344
|
hitSlop={10}
|
|
342
345
|
accessibilityRole="button"
|
|
343
|
-
accessibilityLabel=
|
|
346
|
+
accessibilityLabel={t.comun.quitar}
|
|
344
347
|
>
|
|
345
348
|
<IconoDeRenglon
|
|
346
349
|
name="close-circle"
|
|
@@ -418,6 +421,7 @@ export function CampoDeFecha({
|
|
|
418
421
|
const c = usePaleta();
|
|
419
422
|
const styles = useStyles();
|
|
420
423
|
const [eligiendo, setEligiendo] = React.useState(false);
|
|
424
|
+
const t = useTextos();
|
|
421
425
|
const fecha = valor ? dayjs(valor) : null;
|
|
422
426
|
|
|
423
427
|
return (
|
|
@@ -438,7 +442,7 @@ export function CampoDeFecha({
|
|
|
438
442
|
color={fecha ? c.secondary : c.secondaryLight}
|
|
439
443
|
/>
|
|
440
444
|
<Text style={[styles.textoFecha, !fecha && styles.sinValor]}>
|
|
441
|
-
{fecha ? fecha.format("D MMM YYYY") :
|
|
445
|
+
{fecha ? fecha.format("D MMM YYYY") : t.comun.elegir}
|
|
442
446
|
</Text>
|
|
443
447
|
</View>
|
|
444
448
|
</CajaDeCampo>
|
|
@@ -528,6 +532,7 @@ export function CampoDeTelefono({
|
|
|
528
532
|
const styles = useStyles();
|
|
529
533
|
const { entrada, enfocada, enfocar, alEnfocar, alSalir } = useEntradaEnCaja();
|
|
530
534
|
const { lada, digitos } = partirTelefono(valor);
|
|
535
|
+
const t = useTextos();
|
|
531
536
|
const armar = (nuevaLada: string, nuevosDigitos: string) => {
|
|
532
537
|
const solo = nuevosDigitos.replace(/\D/g, "");
|
|
533
538
|
onCambiar(solo ? `${nuevaLada}${solo}` : null);
|
|
@@ -569,7 +574,7 @@ export function CampoDeTelefono({
|
|
|
569
574
|
onBlur={alSalir}
|
|
570
575
|
keyboardType="phone-pad"
|
|
571
576
|
maxLength={10}
|
|
572
|
-
placeholder=
|
|
577
|
+
placeholder={t.campos.diezDigitos}
|
|
573
578
|
accessibilityLabel={etiqueta}
|
|
574
579
|
style={styles.texto}
|
|
575
580
|
/>
|
|
@@ -3,6 +3,7 @@ import React from "react";
|
|
|
3
3
|
import type { FieldValues, UseFormReturn } from "react-hook-form";
|
|
4
4
|
import { avisar } from "../dialogos/dialogos";
|
|
5
5
|
import { mensajeDeError, setErrors } from "./errores";
|
|
6
|
+
import { textosActuales } from "../idioma";
|
|
6
7
|
|
|
7
8
|
/**
|
|
8
9
|
* Lo que comparten los formularios de edición: cómo se compara lo capturado contra lo que vino,
|
|
@@ -54,9 +55,10 @@ export function useGuardado<T extends FieldValues>(form: UseFormReturn<T>) {
|
|
|
54
55
|
router.back();
|
|
55
56
|
} catch (falla) {
|
|
56
57
|
volcarErrores(form, falla);
|
|
57
|
-
const
|
|
58
|
+
const t = textosActuales();
|
|
59
|
+
const mensaje = mensajeDeError(falla, t.comun.revisaLaSenal);
|
|
58
60
|
setError(mensaje);
|
|
59
|
-
await avisar({ titulo:
|
|
61
|
+
await avisar({ titulo: t.edicion.noSeGuardo, mensaje, icono: "alert-circle-outline" });
|
|
60
62
|
}
|
|
61
63
|
};
|
|
62
64
|
return { error, guardar };
|
package/src/fotos/elegir.ts
CHANGED
|
@@ -11,6 +11,7 @@ import {
|
|
|
11
11
|
} from "../camara/elegir";
|
|
12
12
|
import { reducirImagenAdjunta } from "../camara/reducir";
|
|
13
13
|
import { avisar, mostrarDialogo } from "../dialogos/dialogos";
|
|
14
|
+
import { textosActuales } from "../idioma";
|
|
14
15
|
|
|
15
16
|
/** El tope de un archivo en el servidor (`ConArchivoSubido.TAMANO_MAXIMO`): 25 MB. */
|
|
16
17
|
export const TAMANO_MAXIMO_BYTES = 25 * 1024 * 1024;
|
|
@@ -48,10 +49,12 @@ export async function elegirDocumentos({
|
|
|
48
49
|
const pesa = doc.size ?? new File(doc.uri).size;
|
|
49
50
|
if (pesa > tamanoMaximo) {
|
|
50
51
|
await avisar({
|
|
51
|
-
titulo:
|
|
52
|
-
mensaje:
|
|
53
|
-
|
|
54
|
-
|
|
52
|
+
titulo: textosActuales().fotos.demasiadoPesado,
|
|
53
|
+
mensaje: textosActuales().fotos.pesaDeMas(
|
|
54
|
+
doc.name,
|
|
55
|
+
(pesa / (1024 * 1024)).toFixed(1),
|
|
56
|
+
tamanoMaximo / (1024 * 1024),
|
|
57
|
+
),
|
|
55
58
|
icono: "file-alert-outline",
|
|
56
59
|
});
|
|
57
60
|
continue;
|
|
@@ -71,7 +74,7 @@ export async function elegirDocumentos({
|
|
|
71
74
|
* cualquier paso o el archivo no se puede subir (y ya se dijo por qué).
|
|
72
75
|
*/
|
|
73
76
|
export async function elegirAdjunto({
|
|
74
|
-
titulo
|
|
77
|
+
titulo,
|
|
75
78
|
permiso,
|
|
76
79
|
tipos,
|
|
77
80
|
}: {
|
|
@@ -79,29 +82,30 @@ export async function elegirAdjunto({
|
|
|
79
82
|
permiso: PermisoDeCamara;
|
|
80
83
|
tipos?: string[];
|
|
81
84
|
}): Promise<ArchivoElegido | null> {
|
|
85
|
+
const t = textosActuales();
|
|
82
86
|
const origen = await mostrarDialogo<"camara" | "galeria" | "documento" | null>({
|
|
83
|
-
titulo,
|
|
87
|
+
titulo: titulo ?? t.fotos.subirArchivo,
|
|
84
88
|
icono: "paperclip",
|
|
85
89
|
botones: [
|
|
86
90
|
{
|
|
87
|
-
texto:
|
|
91
|
+
texto: t.fotos.tomarUnaFoto,
|
|
88
92
|
icono: "camera-outline",
|
|
89
93
|
estilo: "principal",
|
|
90
94
|
valor: "camara",
|
|
91
95
|
},
|
|
92
96
|
{
|
|
93
|
-
texto:
|
|
97
|
+
texto: t.fotos.deLaGaleria,
|
|
94
98
|
icono: "image-outline",
|
|
95
99
|
estilo: "secundario",
|
|
96
100
|
valor: "galeria",
|
|
97
101
|
},
|
|
98
102
|
{
|
|
99
|
-
texto:
|
|
103
|
+
texto: t.fotos.unDocumento,
|
|
100
104
|
icono: "file-document-outline",
|
|
101
105
|
estilo: "secundario",
|
|
102
106
|
valor: "documento",
|
|
103
107
|
},
|
|
104
|
-
{ texto:
|
|
108
|
+
{ texto: t.comun.cancelar, estilo: "cancelar", valor: null },
|
|
105
109
|
],
|
|
106
110
|
});
|
|
107
111
|
const elegidos =
|
package/src/hoja/HojaDeLista.tsx
CHANGED
|
@@ -13,6 +13,7 @@ import { EnHoja } from "../componentes/Tarjeta";
|
|
|
13
13
|
import { Buscador } from "../listas/piezas";
|
|
14
14
|
import { crearEstilos, renglon, tipografia, usePaleta } from "../tema";
|
|
15
15
|
import { Text } from "../texto";
|
|
16
|
+
import { useTextos } from "../idioma";
|
|
16
17
|
|
|
17
18
|
/** Lo alto de la hoja: arriba se sigue viendo un poco de la ficha, que dice dónde se está. */
|
|
18
19
|
const ALTO = "85%";
|
|
@@ -76,6 +77,7 @@ export default function HojaDeLista<T>({
|
|
|
76
77
|
}) {
|
|
77
78
|
const c = usePaleta();
|
|
78
79
|
const styles = useStyles();
|
|
80
|
+
const t = useTextos();
|
|
79
81
|
const hoja = useRef<BottomSheet>(null);
|
|
80
82
|
const insets = useSafeAreaInsets();
|
|
81
83
|
|
|
@@ -123,9 +125,7 @@ export default function HojaDeLista<T>({
|
|
|
123
125
|
) : null}
|
|
124
126
|
{acciones}
|
|
125
127
|
{nombres && total != null && renglones.length ? (
|
|
126
|
-
<Text style={styles.cuenta}>
|
|
127
|
-
{total === 1 ? `1 ${nombres[0]}` : `${total} ${nombres[1]}`}
|
|
128
|
-
</Text>
|
|
128
|
+
<Text style={styles.cuenta}>{t.listas.cuantos(total, nombres)}</Text>
|
|
129
129
|
) : null}
|
|
130
130
|
</View>
|
|
131
131
|
);
|
|
@@ -134,9 +134,9 @@ export default function HojaDeLista<T>({
|
|
|
134
134
|
<ActivityIndicator color={c.primary} style={styles.espera} />
|
|
135
135
|
) : lista.fallo ? (
|
|
136
136
|
<View style={styles.vacio}>
|
|
137
|
-
<Text style={styles.linea}>
|
|
137
|
+
<Text style={styles.linea}>{t.ficha.hojaNoCargo}</Text>
|
|
138
138
|
<BotonGrande icono="refresh" modo="contorno" onPress={lista.onReintentar}>
|
|
139
|
-
|
|
139
|
+
{t.comun.reintentar}
|
|
140
140
|
</BotonGrande>
|
|
141
141
|
</View>
|
|
142
142
|
) : (
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import HojaInferior from "./HojaInferior";
|
|
2
2
|
import MaterialCommunityIcons from "@expo/vector-icons/MaterialCommunityIcons";
|
|
3
3
|
import React from "react";
|
|
4
|
+
import { useTextos } from "../idioma";
|
|
4
5
|
import { FilaConContador, SeccionDeFicha } from "../listas/piezas";
|
|
5
6
|
|
|
6
7
|
/** Una lista de «Más»: su renglón con contador y lo que abre en la hoja. */
|
|
@@ -26,13 +27,14 @@ export interface ListaDeLaFicha {
|
|
|
26
27
|
*/
|
|
27
28
|
export function useMasDeLaFicha(listas: ListaDeLaFicha[], subtitulo?: string) {
|
|
28
29
|
// Lo que enseña se queda al cerrarla, para que su título no se vacíe mientras baja.
|
|
30
|
+
const t = useTextos();
|
|
29
31
|
const [clave, setClave] = React.useState<string | null>(null);
|
|
30
32
|
const [abierta, setAbierta] = React.useState(false);
|
|
31
33
|
const cerrar = React.useCallback(() => setAbierta(false), []);
|
|
32
34
|
const elegida = listas.find((l) => l.clave === clave);
|
|
33
35
|
|
|
34
36
|
const seccion = listas.length ? (
|
|
35
|
-
<SeccionDeFicha titulo=
|
|
37
|
+
<SeccionDeFicha titulo={t.ficha.mas}>
|
|
36
38
|
{listas.map((l) => (
|
|
37
39
|
<FilaConContador
|
|
38
40
|
key={l.clave}
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import React, { createContext, useContext } from "react";
|
|
2
|
+
import TEXTOS, { type Idioma, type Textos } from "./textos";
|
|
3
|
+
|
|
4
|
+
// ==============================|| IDIOMA ||============================== //
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* El idioma en el que la librería dibuja lo suyo. La librería **no traduce**: recibe el idioma ya
|
|
8
|
+
* resuelto y elige diccionario. De dónde sale (el teléfono, un claim del token, lo que el usuario
|
|
9
|
+
* eligió) y dónde se cambia es asunto de la app. Es el mismo trato que `LocaleProvider` en
|
|
10
|
+
* `@acvl/frontend-components`.
|
|
11
|
+
*
|
|
12
|
+
* Sin proveedor el idioma es español, que es como se comportaba antes de que esto existiera: una
|
|
13
|
+
* app que no lo monta no cambia en nada.
|
|
14
|
+
*
|
|
15
|
+
* @example
|
|
16
|
+
* <ProveedorDeIdioma idioma={idioma}>
|
|
17
|
+
* <Stack />
|
|
18
|
+
* </ProveedorDeIdioma>
|
|
19
|
+
*/
|
|
20
|
+
const ContextoDeIdioma = createContext<Idioma>("es");
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* El idioma vigente para lo que corre fuera de un componente (`avisar`, `confirmar`,
|
|
24
|
+
* `elegirAdjunto`): no pueden leer un contexto. Lo pone el proveedor al pintarse.
|
|
25
|
+
*/
|
|
26
|
+
let idiomaVigente: Idioma = "es";
|
|
27
|
+
|
|
28
|
+
export function ProveedorDeIdioma({
|
|
29
|
+
idioma,
|
|
30
|
+
children,
|
|
31
|
+
}: {
|
|
32
|
+
idioma: Idioma;
|
|
33
|
+
children: React.ReactNode;
|
|
34
|
+
}) {
|
|
35
|
+
// En el render y no en un efecto: un `avisar` que salga del primer render de los hijos ya
|
|
36
|
+
// tiene que hablar el idioma nuevo. Es idempotente, así que StrictMode no lo descompone.
|
|
37
|
+
idiomaVigente = idioma;
|
|
38
|
+
return <ContextoDeIdioma.Provider value={idioma}>{children}</ContextoDeIdioma.Provider>;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
/** El idioma activo de la librería. */
|
|
42
|
+
export const useIdiomaDeLaLibreria = (): Idioma => useContext(ContextoDeIdioma);
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* Los textos de la librería en el idioma activo. Devuelve el diccionario entero y no una función:
|
|
46
|
+
* `t.comun.cerrar` se autocompleta y una llave mal escrita no compila.
|
|
47
|
+
*/
|
|
48
|
+
export const useTextos = (): Textos => TEXTOS[useContext(ContextoDeIdioma)] ?? TEXTOS.es;
|
|
49
|
+
|
|
50
|
+
/** Los textos fuera de un componente; dentro de uno, `useTextos`. */
|
|
51
|
+
export const textosActuales = (): Textos => TEXTOS[idiomaVigente] ?? TEXTOS.es;
|
|
@@ -0,0 +1,246 @@
|
|
|
1
|
+
// ==============================|| TEXTOS DE LA LIBRERÍA ||============================== //
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Los textos que la librería dibuja por su cuenta, en los dos idiomas. Es el espejo del de
|
|
5
|
+
* `@acvl/frontend-components`: un diccionario tipado y no un runtime de i18n, para que cada app
|
|
6
|
+
* traiga el suyo (o ninguno) sin que la librería le imponga `i18next`. A `en` le falta una llave y
|
|
7
|
+
* no compila.
|
|
8
|
+
*
|
|
9
|
+
* El idioma lo pone la app con `ProveedorDeIdioma`; se lee con `useTextos()` en un componente y con
|
|
10
|
+
* `textosActuales()` fuera de uno (`avisar`, `elegirAdjunto`).
|
|
11
|
+
*
|
|
12
|
+
* Las plantillas van como funciones: el orden de las palabras cambia entre idiomas y una
|
|
13
|
+
* concatenación en el punto de uso lo fija.
|
|
14
|
+
*/
|
|
15
|
+
|
|
16
|
+
/** Idiomas que entiende la librería. */
|
|
17
|
+
export type Idioma = "es" | "en";
|
|
18
|
+
|
|
19
|
+
const es = {
|
|
20
|
+
comun: {
|
|
21
|
+
cancelar: "Cancelar",
|
|
22
|
+
cerrar: "Cerrar",
|
|
23
|
+
entendido: "Entendido",
|
|
24
|
+
reintentar: "Reintentar",
|
|
25
|
+
listo: "Listo",
|
|
26
|
+
quitar: "Quitar",
|
|
27
|
+
elegir: "Elegir…",
|
|
28
|
+
buscar: "Buscar",
|
|
29
|
+
ninguno: "Ninguno",
|
|
30
|
+
noDisponible: "N/A",
|
|
31
|
+
si: "Sí",
|
|
32
|
+
no: "No",
|
|
33
|
+
atras: "Atrás",
|
|
34
|
+
revisaLaSenal: "Revisa tu señal e intenta de nuevo.",
|
|
35
|
+
noSePudoCargar: "No se pudo cargar",
|
|
36
|
+
},
|
|
37
|
+
acceso: {
|
|
38
|
+
usuario: "Usuario",
|
|
39
|
+
contrasena: "Contraseña",
|
|
40
|
+
entrar: "Iniciar Sesión",
|
|
41
|
+
noSePudo: "No se pudo iniciar sesión. Intente de nuevo.",
|
|
42
|
+
},
|
|
43
|
+
perfil: {
|
|
44
|
+
titulo: "Perfil",
|
|
45
|
+
apariencia: "Apariencia",
|
|
46
|
+
telefono: "Teléfono",
|
|
47
|
+
claro: "Claro",
|
|
48
|
+
oscuro: "Oscuro",
|
|
49
|
+
cerrarSesion: "Cerrar sesión",
|
|
50
|
+
},
|
|
51
|
+
error: {
|
|
52
|
+
titulo: "Algo salió mal",
|
|
53
|
+
mensaje:
|
|
54
|
+
"Esta pantalla no se pudo abrir. Intenta de nuevo; si vuelve a pasar, avisa a soporte.",
|
|
55
|
+
},
|
|
56
|
+
ficha: {
|
|
57
|
+
noExiste: "No existe",
|
|
58
|
+
noExisteTexto: "Puede que lo hayan borrado, o que no tengas permiso de verlo.",
|
|
59
|
+
mas: "Más",
|
|
60
|
+
hojaNoCargo: "No se pudo cargar. Revisa tu señal.",
|
|
61
|
+
seccionNoCargo: "No se pudo cargar. Jala la ficha para releer.",
|
|
62
|
+
},
|
|
63
|
+
edicion: {
|
|
64
|
+
sinPermiso: "No tienes permiso",
|
|
65
|
+
noSePudoCargar: (queEs: string) => `No se pudo cargar ${queEs}`,
|
|
66
|
+
guardar: "Guardar",
|
|
67
|
+
guardando: "Guardando…",
|
|
68
|
+
noSeGuardo: "No se guardó",
|
|
69
|
+
},
|
|
70
|
+
listas: {
|
|
71
|
+
cuantos: (total: number, [uno, varios]: readonly [string, string]) =>
|
|
72
|
+
total === 1 ? `1 ${uno}` : `${total} ${varios}`,
|
|
73
|
+
borrarBusqueda: "Borrar búsqueda",
|
|
74
|
+
filtrosPuestos: (cuenta: number) => `Filtros, ${cuenta} puestos`,
|
|
75
|
+
filtrosYOrden: "Filtros y orden",
|
|
76
|
+
filtros: "Filtros",
|
|
77
|
+
ver: (total: number, [uno, varios]: readonly [string, string]) =>
|
|
78
|
+
`Ver ${total} ${total === 1 ? uno : varios}`,
|
|
79
|
+
ordenarPor: "Ordenar por",
|
|
80
|
+
sentido: "Sentido",
|
|
81
|
+
ascendente: "Ascendente",
|
|
82
|
+
descendente: "Descendente",
|
|
83
|
+
nadaQueCoincida: "Nada que coincida.",
|
|
84
|
+
listoCon: (n: number) => `Listo (${n})`,
|
|
85
|
+
masAcciones: "Más acciones",
|
|
86
|
+
},
|
|
87
|
+
contacto: {
|
|
88
|
+
llamar: (nombre: string) => `Llamar a ${nombre}`,
|
|
89
|
+
whatsapp: (nombre: string) => `WhatsApp a ${nombre}`,
|
|
90
|
+
correo: (nombre: string) => `Correo a ${nombre}`,
|
|
91
|
+
direccion: "Dirección",
|
|
92
|
+
abrirEnElMapa: (donde: string) => `Abrir en el mapa: ${donde}`,
|
|
93
|
+
},
|
|
94
|
+
campos: {
|
|
95
|
+
diezDigitos: "10 dígitos",
|
|
96
|
+
},
|
|
97
|
+
fotos: {
|
|
98
|
+
verLaFoto: "Ver la foto",
|
|
99
|
+
archivo: (nombre: string) => `Archivo ${nombre}`.trim(),
|
|
100
|
+
quitarFoto: "Quitar esta foto",
|
|
101
|
+
quitarArchivo: "Quitar este archivo",
|
|
102
|
+
guardada: "Guardada en la galería",
|
|
103
|
+
noSeGuardo: "No se pudo guardar; intentar de nuevo",
|
|
104
|
+
guardarEnGaleria: "Guardar en la galería",
|
|
105
|
+
deTotal: (n: number, total: number) => `${n} de ${total}`,
|
|
106
|
+
tomarFoto: "Tomar foto",
|
|
107
|
+
tomarUnaFoto: "Tomar una foto",
|
|
108
|
+
deLaGaleria: "De la galería",
|
|
109
|
+
unDocumento: "Un documento",
|
|
110
|
+
subirArchivo: "Subir un archivo",
|
|
111
|
+
sinPermisoDeCamara: "Sin permiso de cámara",
|
|
112
|
+
activaLaCamara: (para: string) =>
|
|
113
|
+
`Para ${para}, activa la cámara para la app en los ajustes del teléfono.`,
|
|
114
|
+
laCamara: "La cámara",
|
|
115
|
+
demasiadoPesado: "Archivo demasiado pesado",
|
|
116
|
+
pesaDeMas: (nombre: string, pesaMb: string, topeMb: number) =>
|
|
117
|
+
`«${nombre}» pesa ${pesaMb} MB y el tope son ${topeMb} MB. Elige uno más ligero o tómale una foto.`,
|
|
118
|
+
},
|
|
119
|
+
historial: {
|
|
120
|
+
titulo: "Historial",
|
|
121
|
+
sistema: "Sistema",
|
|
122
|
+
sinCambios: "Sin cambios en los campos.",
|
|
123
|
+
ocultarCampos: "Ocultar campos",
|
|
124
|
+
verTodos: "Ver todos los campos",
|
|
125
|
+
verCampos: "Ver los campos",
|
|
126
|
+
noCargo: "No se pudo cargar el historial",
|
|
127
|
+
sinMovimientos: "Sin movimientos registrados",
|
|
128
|
+
},
|
|
129
|
+
};
|
|
130
|
+
|
|
131
|
+
export type Textos = typeof es;
|
|
132
|
+
|
|
133
|
+
const en: Textos = {
|
|
134
|
+
comun: {
|
|
135
|
+
cancelar: "Cancel",
|
|
136
|
+
cerrar: "Close",
|
|
137
|
+
entendido: "Got it",
|
|
138
|
+
reintentar: "Try again",
|
|
139
|
+
listo: "Done",
|
|
140
|
+
quitar: "Clear",
|
|
141
|
+
elegir: "Choose…",
|
|
142
|
+
buscar: "Search",
|
|
143
|
+
ninguno: "None",
|
|
144
|
+
noDisponible: "N/A",
|
|
145
|
+
si: "Yes",
|
|
146
|
+
no: "No",
|
|
147
|
+
atras: "Back",
|
|
148
|
+
revisaLaSenal: "Check your connection and try again.",
|
|
149
|
+
noSePudoCargar: "Couldn't load",
|
|
150
|
+
},
|
|
151
|
+
acceso: {
|
|
152
|
+
usuario: "Username",
|
|
153
|
+
contrasena: "Password",
|
|
154
|
+
entrar: "Sign in",
|
|
155
|
+
noSePudo: "Couldn't sign in. Please try again.",
|
|
156
|
+
},
|
|
157
|
+
perfil: {
|
|
158
|
+
titulo: "Profile",
|
|
159
|
+
apariencia: "Appearance",
|
|
160
|
+
telefono: "Phone",
|
|
161
|
+
claro: "Light",
|
|
162
|
+
oscuro: "Dark",
|
|
163
|
+
cerrarSesion: "Sign out",
|
|
164
|
+
},
|
|
165
|
+
error: {
|
|
166
|
+
titulo: "Something went wrong",
|
|
167
|
+
mensaje: "This screen couldn't open. Try again; if it keeps happening, contact support.",
|
|
168
|
+
},
|
|
169
|
+
ficha: {
|
|
170
|
+
noExiste: "Not found",
|
|
171
|
+
noExisteTexto: "It may have been deleted, or you may not have permission to see it.",
|
|
172
|
+
mas: "More",
|
|
173
|
+
hojaNoCargo: "Couldn't load. Check your connection.",
|
|
174
|
+
seccionNoCargo: "Couldn't load. Pull down to refresh.",
|
|
175
|
+
},
|
|
176
|
+
edicion: {
|
|
177
|
+
sinPermiso: "You don't have permission",
|
|
178
|
+
noSePudoCargar: (queEs: string) => `Couldn't load ${queEs}`,
|
|
179
|
+
guardar: "Save",
|
|
180
|
+
guardando: "Saving…",
|
|
181
|
+
noSeGuardo: "Not saved",
|
|
182
|
+
},
|
|
183
|
+
listas: {
|
|
184
|
+
cuantos: (total: number, [uno, varios]: readonly [string, string]) =>
|
|
185
|
+
total === 1 ? `1 ${uno}` : `${total} ${varios}`,
|
|
186
|
+
borrarBusqueda: "Clear search",
|
|
187
|
+
filtrosPuestos: (cuenta: number) => `Filters, ${cuenta} applied`,
|
|
188
|
+
filtrosYOrden: "Filters and sorting",
|
|
189
|
+
filtros: "Filters",
|
|
190
|
+
ver: (total: number, [uno, varios]: readonly [string, string]) =>
|
|
191
|
+
`Show ${total} ${total === 1 ? uno : varios}`,
|
|
192
|
+
ordenarPor: "Sort by",
|
|
193
|
+
sentido: "Direction",
|
|
194
|
+
ascendente: "Ascending",
|
|
195
|
+
descendente: "Descending",
|
|
196
|
+
nadaQueCoincida: "No matches.",
|
|
197
|
+
listoCon: (n: number) => `Done (${n})`,
|
|
198
|
+
masAcciones: "More actions",
|
|
199
|
+
},
|
|
200
|
+
contacto: {
|
|
201
|
+
llamar: (nombre: string) => `Call ${nombre}`,
|
|
202
|
+
whatsapp: (nombre: string) => `WhatsApp ${nombre}`,
|
|
203
|
+
correo: (nombre: string) => `Email ${nombre}`,
|
|
204
|
+
direccion: "Address",
|
|
205
|
+
abrirEnElMapa: (donde: string) => `Open in maps: ${donde}`,
|
|
206
|
+
},
|
|
207
|
+
campos: {
|
|
208
|
+
diezDigitos: "10 digits",
|
|
209
|
+
},
|
|
210
|
+
fotos: {
|
|
211
|
+
verLaFoto: "View photo",
|
|
212
|
+
archivo: (nombre: string) => `File ${nombre}`.trim(),
|
|
213
|
+
quitarFoto: "Remove this photo",
|
|
214
|
+
quitarArchivo: "Remove this file",
|
|
215
|
+
guardada: "Saved to gallery",
|
|
216
|
+
noSeGuardo: "Couldn't save; try again",
|
|
217
|
+
guardarEnGaleria: "Save to gallery",
|
|
218
|
+
deTotal: (n: number, total: number) => `${n} of ${total}`,
|
|
219
|
+
tomarFoto: "Take photo",
|
|
220
|
+
tomarUnaFoto: "Take a photo",
|
|
221
|
+
deLaGaleria: "From gallery",
|
|
222
|
+
unDocumento: "A document",
|
|
223
|
+
subirArchivo: "Upload a file",
|
|
224
|
+
sinPermisoDeCamara: "No camera permission",
|
|
225
|
+
activaLaCamara: (para: string) =>
|
|
226
|
+
`To ${para}, turn on camera access for the app in your phone's settings.`,
|
|
227
|
+
laCamara: "The camera",
|
|
228
|
+
demasiadoPesado: "File too large",
|
|
229
|
+
pesaDeMas: (nombre: string, pesaMb: string, topeMb: number) =>
|
|
230
|
+
`"${nombre}" is ${pesaMb} MB and the limit is ${topeMb} MB. Choose a smaller one or take a photo of it.`,
|
|
231
|
+
},
|
|
232
|
+
historial: {
|
|
233
|
+
titulo: "History",
|
|
234
|
+
sistema: "System",
|
|
235
|
+
sinCambios: "No field changes.",
|
|
236
|
+
ocultarCampos: "Hide fields",
|
|
237
|
+
verTodos: "Show all fields",
|
|
238
|
+
verCampos: "Show fields",
|
|
239
|
+
noCargo: "Couldn't load the history",
|
|
240
|
+
sinMovimientos: "No recorded activity",
|
|
241
|
+
},
|
|
242
|
+
};
|
|
243
|
+
|
|
244
|
+
const TEXTOS: Record<Idioma, Textos> = { es, en };
|
|
245
|
+
|
|
246
|
+
export default TEXTOS;
|
package/src/index.ts
CHANGED
|
@@ -10,6 +10,7 @@ export {
|
|
|
10
10
|
type AccionDelEncabezado,
|
|
11
11
|
} from "./componentes/AccionesDelEncabezado";
|
|
12
12
|
export { default as BotonDeAvatar, inicialesDe } from "./componentes/BotonDeAvatar";
|
|
13
|
+
export { default as BotonFlotante, HUECO_DEL_BOTON_FLOTANTE } from "./componentes/BotonFlotante";
|
|
13
14
|
export { default as BotonGrande } from "./componentes/BotonGrande";
|
|
14
15
|
export { default as CabeceraEmpujada } from "./componentes/CabeceraEmpujada";
|
|
15
16
|
export { default as Chip, type TonoDeChip } from "./componentes/Chip";
|
|
@@ -150,3 +151,14 @@ export {
|
|
|
150
151
|
type Historial,
|
|
151
152
|
} from "./pantallas/PantallaDeHistorial";
|
|
152
153
|
export { default as PantallaDePerfil } from "./pantallas/PantallaDePerfil";
|
|
154
|
+
|
|
155
|
+
// ==============================|| IDIOMA ||============================== //
|
|
156
|
+
|
|
157
|
+
export {
|
|
158
|
+
ProveedorDeIdioma,
|
|
159
|
+
textosActuales,
|
|
160
|
+
useIdiomaDeLaLibreria,
|
|
161
|
+
useTextos,
|
|
162
|
+
type Idioma,
|
|
163
|
+
type Textos,
|
|
164
|
+
} from "./idioma";
|