@acvl/mobile-components 0.0.0-stage → 0.0.2
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 +147 -2
- package/assets/fonts/Inter_400Regular.ttf +0 -0
- package/assets/fonts/Inter_500Medium.ttf +0 -0
- package/assets/fonts/Inter_600SemiBold.ttf +0 -0
- package/assets/fonts/Inter_700Bold.ttf +0 -0
- package/assets/fonts/Inter_800ExtraBold.ttf +0 -0
- package/assets/fonts/Inter_900Black.ttf +0 -0
- package/assets/fonts/OFL.txt +93 -0
- package/lib/module/camara/CampoDeFotos.js +124 -0
- package/lib/module/camara/CampoDeFotos.js.map +1 -0
- package/lib/module/camara/elegir.js +104 -0
- package/lib/module/camara/elegir.js.map +1 -0
- package/lib/module/camara/index.js +10 -0
- package/lib/module/camara/index.js.map +1 -0
- package/lib/module/camara/reducir.js +93 -0
- package/lib/module/camara/reducir.js.map +1 -0
- package/lib/module/componentes/AccionesDelEncabezado.js +212 -0
- package/lib/module/componentes/AccionesDelEncabezado.js.map +1 -0
- package/lib/module/componentes/BotonDeAvatar.js +55 -0
- package/lib/module/componentes/BotonDeAvatar.js.map +1 -0
- package/lib/module/componentes/BotonGrande.js +105 -0
- package/lib/module/componentes/BotonGrande.js.map +1 -0
- package/lib/module/componentes/CabeceraEmpujada.js +101 -0
- package/lib/module/componentes/CabeceraEmpujada.js.map +1 -0
- package/lib/module/componentes/Chip.js +77 -0
- package/lib/module/componentes/Chip.js.map +1 -0
- package/lib/module/componentes/ContenidoDePantalla.js +56 -0
- package/lib/module/componentes/ContenidoDePantalla.js.map +1 -0
- package/lib/module/componentes/EstadoVacio.js +65 -0
- package/lib/module/componentes/EstadoVacio.js.map +1 -0
- package/lib/module/componentes/Foto.js +106 -0
- package/lib/module/componentes/Foto.js.map +1 -0
- package/lib/module/componentes/IconoDeRenglon.js +44 -0
- package/lib/module/componentes/IconoDeRenglon.js.map +1 -0
- package/lib/module/componentes/Logo.js +44 -0
- package/lib/module/componentes/Logo.js.map +1 -0
- package/lib/module/componentes/Nota.js +51 -0
- package/lib/module/componentes/Nota.js.map +1 -0
- package/lib/module/componentes/OpcionesExcluyentes.js +69 -0
- package/lib/module/componentes/OpcionesExcluyentes.js.map +1 -0
- package/lib/module/componentes/PantallaDeError.js +84 -0
- package/lib/module/componentes/PantallaDeError.js.map +1 -0
- package/lib/module/componentes/Pestanas.js +87 -0
- package/lib/module/componentes/Pestanas.js.map +1 -0
- package/lib/module/componentes/Rotulo.js +28 -0
- package/lib/module/componentes/Rotulo.js.map +1 -0
- package/lib/module/componentes/RotuloDeSeccion.js +87 -0
- package/lib/module/componentes/RotuloDeSeccion.js.map +1 -0
- package/lib/module/componentes/Tarjeta.js +63 -0
- package/lib/module/componentes/Tarjeta.js.map +1 -0
- package/lib/module/componentes/TarjetaDeDatos.js +79 -0
- package/lib/module/componentes/TarjetaDeDatos.js.map +1 -0
- package/lib/module/componentes/TiraDeFotos.js +184 -0
- package/lib/module/componentes/TiraDeFotos.js.map +1 -0
- package/lib/module/componentes/VisorDeFoto.js +171 -0
- package/lib/module/componentes/VisorDeFoto.js.map +1 -0
- package/lib/module/componentes/logos/bola-blanca.png +0 -0
- package/lib/module/componentes/logos/bola.png +0 -0
- package/lib/module/componentes/logos/logo-blanco.png +0 -0
- package/lib/module/componentes/logos/logo.png +0 -0
- package/lib/module/dialogos/HostDeDialogos.js +170 -0
- package/lib/module/dialogos/HostDeDialogos.js.map +1 -0
- package/lib/module/dialogos/dialogos.js +112 -0
- package/lib/module/dialogos/dialogos.js.map +1 -0
- package/lib/module/formularios/CampoDeSeleccion.js +72 -0
- package/lib/module/formularios/CampoDeSeleccion.js.map +1 -0
- package/lib/module/formularios/SelectorDeOpcion.js +406 -0
- package/lib/module/formularios/SelectorDeOpcion.js.map +1 -0
- package/lib/module/formularios/TextoDeAccesoControlado.js +73 -0
- package/lib/module/formularios/TextoDeAccesoControlado.js.map +1 -0
- package/lib/module/formularios/campos.js +501 -0
- package/lib/module/formularios/campos.js.map +1 -0
- package/lib/module/formularios/controlados.js +199 -0
- package/lib/module/formularios/controlados.js.map +1 -0
- package/lib/module/formularios/errores.js +91 -0
- package/lib/module/formularios/errores.js.map +1 -0
- package/lib/module/formularios/guardado.js +78 -0
- package/lib/module/formularios/guardado.js.map +1 -0
- package/lib/module/formularios/useBusqueda.js +22 -0
- package/lib/module/formularios/useBusqueda.js.map +1 -0
- package/lib/module/fotos/elegir.js +105 -0
- package/lib/module/fotos/elegir.js.map +1 -0
- package/lib/module/fotos/index.js +9 -0
- package/lib/module/fotos/index.js.map +1 -0
- package/lib/module/hoja/HojaDeLista.js +211 -0
- package/lib/module/hoja/HojaDeLista.js.map +1 -0
- package/lib/module/hoja/HojaInferior.js +112 -0
- package/lib/module/hoja/HojaInferior.js.map +1 -0
- package/lib/module/hoja/index.js +8 -0
- package/lib/module/hoja/index.js.map +1 -0
- package/lib/module/hoja/useMasDeLaFicha.js +48 -0
- package/lib/module/hoja/useMasDeLaFicha.js.map +1 -0
- package/lib/module/index.js +67 -0
- package/lib/module/index.js.map +1 -0
- package/lib/module/listas/HojaDeFiltros.js +270 -0
- package/lib/module/listas/HojaDeFiltros.js.map +1 -0
- package/lib/module/listas/ListaPaginada.js +103 -0
- package/lib/module/listas/ListaPaginada.js.map +1 -0
- package/lib/module/listas/TarjetaDeslizable.js +118 -0
- package/lib/module/listas/TarjetaDeslizable.js.map +1 -0
- package/lib/module/listas/paginado.js +11 -0
- package/lib/module/listas/paginado.js.map +1 -0
- package/lib/module/listas/piezas.js +666 -0
- package/lib/module/listas/piezas.js.map +1 -0
- package/lib/module/navegacion/BarraDePestanas.js +162 -0
- package/lib/module/navegacion/BarraDePestanas.js.map +1 -0
- package/lib/module/navegacion/BotonDeBarra.js +58 -0
- package/lib/module/navegacion/BotonDeBarra.js.map +1 -0
- package/lib/module/navegacion/encabezado.js +103 -0
- package/lib/module/navegacion/encabezado.js.map +1 -0
- package/lib/module/pantallas/PantallaDeAcceso.js +195 -0
- package/lib/module/pantallas/PantallaDeAcceso.js.map +1 -0
- package/lib/module/pantallas/PantallaDeEdicion.js +121 -0
- package/lib/module/pantallas/PantallaDeEdicion.js.map +1 -0
- package/lib/module/pantallas/PantallaDeFicha.js +94 -0
- package/lib/module/pantallas/PantallaDeFicha.js.map +1 -0
- package/lib/module/pantallas/PantallaDeHistorial.js +328 -0
- package/lib/module/pantallas/PantallaDeHistorial.js.map +1 -0
- package/lib/module/pantallas/PantallaDePerfil.js +137 -0
- package/lib/module/pantallas/PantallaDePerfil.js.map +1 -0
- package/lib/module/tema/ProveedorDeTema.js +122 -0
- package/lib/module/tema/ProveedorDeTema.js.map +1 -0
- package/lib/module/tema/estilos.js +54 -0
- package/lib/module/tema/estilos.js.map +1 -0
- package/lib/module/tema/fuentes.js +28 -0
- package/lib/module/tema/fuentes.js.map +1 -0
- package/lib/module/tema/index.js +8 -0
- package/lib/module/tema/index.js.map +1 -0
- package/lib/module/tema/paleta.js +107 -0
- package/lib/module/tema/paleta.js.map +1 -0
- package/lib/module/tema/tipografia.js +32 -0
- package/lib/module/tema/tipografia.js.map +1 -0
- package/lib/module/texto.js +48 -0
- package/lib/module/texto.js.map +1 -0
- package/lib/typescript/src/camara/CampoDeFotos.d.ts +30 -0
- package/lib/typescript/src/camara/CampoDeFotos.d.ts.map +1 -0
- package/lib/typescript/src/camara/elegir.d.ts +47 -0
- package/lib/typescript/src/camara/elegir.d.ts.map +1 -0
- package/lib/typescript/src/camara/index.d.ts +4 -0
- package/lib/typescript/src/camara/index.d.ts.map +1 -0
- package/lib/typescript/src/camara/reducir.d.ts +32 -0
- package/lib/typescript/src/camara/reducir.d.ts.map +1 -0
- package/lib/typescript/src/componentes/AccionesDelEncabezado.d.ts +34 -0
- package/lib/typescript/src/componentes/AccionesDelEncabezado.d.ts.map +1 -0
- package/lib/typescript/src/componentes/BotonDeAvatar.d.ts +17 -0
- package/lib/typescript/src/componentes/BotonDeAvatar.d.ts.map +1 -0
- package/lib/typescript/src/componentes/BotonGrande.d.ts +26 -0
- package/lib/typescript/src/componentes/BotonGrande.d.ts.map +1 -0
- package/lib/typescript/src/componentes/CabeceraEmpujada.d.ts +18 -0
- package/lib/typescript/src/componentes/CabeceraEmpujada.d.ts.map +1 -0
- package/lib/typescript/src/componentes/Chip.d.ts +14 -0
- package/lib/typescript/src/componentes/Chip.d.ts.map +1 -0
- package/lib/typescript/src/componentes/ContenidoDePantalla.d.ts +17 -0
- package/lib/typescript/src/componentes/ContenidoDePantalla.d.ts.map +1 -0
- package/lib/typescript/src/componentes/EstadoVacio.d.ts +17 -0
- package/lib/typescript/src/componentes/EstadoVacio.d.ts.map +1 -0
- package/lib/typescript/src/componentes/Foto.d.ts +41 -0
- package/lib/typescript/src/componentes/Foto.d.ts.map +1 -0
- package/lib/typescript/src/componentes/IconoDeRenglon.d.ts +25 -0
- package/lib/typescript/src/componentes/IconoDeRenglon.d.ts.map +1 -0
- package/lib/typescript/src/componentes/Logo.d.ts +13 -0
- package/lib/typescript/src/componentes/Logo.d.ts.map +1 -0
- package/lib/typescript/src/componentes/Nota.d.ts +7 -0
- package/lib/typescript/src/componentes/Nota.d.ts.map +1 -0
- package/lib/typescript/src/componentes/OpcionesExcluyentes.d.ts +14 -0
- package/lib/typescript/src/componentes/OpcionesExcluyentes.d.ts.map +1 -0
- package/lib/typescript/src/componentes/PantallaDeError.d.ts +23 -0
- package/lib/typescript/src/componentes/PantallaDeError.d.ts.map +1 -0
- package/lib/typescript/src/componentes/Pestanas.d.ts +28 -0
- package/lib/typescript/src/componentes/Pestanas.d.ts.map +1 -0
- package/lib/typescript/src/componentes/Rotulo.d.ts +6 -0
- package/lib/typescript/src/componentes/Rotulo.d.ts.map +1 -0
- package/lib/typescript/src/componentes/RotuloDeSeccion.d.ts +20 -0
- package/lib/typescript/src/componentes/RotuloDeSeccion.d.ts.map +1 -0
- package/lib/typescript/src/componentes/Tarjeta.d.ts +25 -0
- package/lib/typescript/src/componentes/Tarjeta.d.ts.map +1 -0
- package/lib/typescript/src/componentes/TarjetaDeDatos.d.ts +13 -0
- package/lib/typescript/src/componentes/TarjetaDeDatos.d.ts.map +1 -0
- package/lib/typescript/src/componentes/TiraDeFotos.d.ts +36 -0
- package/lib/typescript/src/componentes/TiraDeFotos.d.ts.map +1 -0
- package/lib/typescript/src/componentes/VisorDeFoto.d.ts +23 -0
- package/lib/typescript/src/componentes/VisorDeFoto.d.ts.map +1 -0
- package/lib/typescript/src/dialogos/HostDeDialogos.d.ts +14 -0
- package/lib/typescript/src/dialogos/HostDeDialogos.d.ts.map +1 -0
- package/lib/typescript/src/dialogos/dialogos.d.ts +65 -0
- package/lib/typescript/src/dialogos/dialogos.d.ts.map +1 -0
- package/lib/typescript/src/formularios/CampoDeSeleccion.d.ts +24 -0
- package/lib/typescript/src/formularios/CampoDeSeleccion.d.ts.map +1 -0
- package/lib/typescript/src/formularios/SelectorDeOpcion.d.ts +59 -0
- package/lib/typescript/src/formularios/SelectorDeOpcion.d.ts.map +1 -0
- package/lib/typescript/src/formularios/TextoDeAccesoControlado.d.ts +18 -0
- package/lib/typescript/src/formularios/TextoDeAccesoControlado.d.ts.map +1 -0
- package/lib/typescript/src/formularios/campos.d.ts +113 -0
- package/lib/typescript/src/formularios/campos.d.ts.map +1 -0
- package/lib/typescript/src/formularios/controlados.d.ts +29 -0
- package/lib/typescript/src/formularios/controlados.d.ts.map +1 -0
- package/lib/typescript/src/formularios/errores.d.ts +36 -0
- package/lib/typescript/src/formularios/errores.d.ts.map +1 -0
- package/lib/typescript/src/formularios/guardado.d.ts +36 -0
- package/lib/typescript/src/formularios/guardado.d.ts.map +1 -0
- package/lib/typescript/src/formularios/useBusqueda.d.ts +7 -0
- package/lib/typescript/src/formularios/useBusqueda.d.ts.map +1 -0
- package/lib/typescript/src/fotos/elegir.d.ts +28 -0
- package/lib/typescript/src/fotos/elegir.d.ts.map +1 -0
- package/lib/typescript/src/fotos/index.d.ts +3 -0
- package/lib/typescript/src/fotos/index.d.ts.map +1 -0
- package/lib/typescript/src/hoja/HojaDeLista.d.ts +50 -0
- package/lib/typescript/src/hoja/HojaDeLista.d.ts.map +1 -0
- package/lib/typescript/src/hoja/HojaInferior.d.ts +21 -0
- package/lib/typescript/src/hoja/HojaInferior.d.ts.map +1 -0
- package/lib/typescript/src/hoja/index.d.ts +4 -0
- package/lib/typescript/src/hoja/index.d.ts.map +1 -0
- package/lib/typescript/src/hoja/useMasDeLaFicha.d.ts +27 -0
- package/lib/typescript/src/hoja/useMasDeLaFicha.d.ts.map +1 -0
- package/lib/typescript/src/index.d.ts +46 -0
- package/lib/typescript/src/index.d.ts.map +1 -0
- package/lib/typescript/src/listas/HojaDeFiltros.d.ts +76 -0
- package/lib/typescript/src/listas/HojaDeFiltros.d.ts.map +1 -0
- package/lib/typescript/src/listas/ListaPaginada.d.ts +39 -0
- package/lib/typescript/src/listas/ListaPaginada.d.ts.map +1 -0
- package/lib/typescript/src/listas/TarjetaDeslizable.d.ts +33 -0
- package/lib/typescript/src/listas/TarjetaDeslizable.d.ts.map +1 -0
- package/lib/typescript/src/listas/paginado.d.ts +16 -0
- package/lib/typescript/src/listas/paginado.d.ts.map +1 -0
- package/lib/typescript/src/listas/piezas.d.ts +157 -0
- package/lib/typescript/src/listas/piezas.d.ts.map +1 -0
- package/lib/typescript/src/navegacion/BarraDePestanas.d.ts +57 -0
- package/lib/typescript/src/navegacion/BarraDePestanas.d.ts.map +1 -0
- package/lib/typescript/src/navegacion/BotonDeBarra.d.ts +15 -0
- package/lib/typescript/src/navegacion/BotonDeBarra.d.ts.map +1 -0
- package/lib/typescript/src/navegacion/encabezado.d.ts +44 -0
- package/lib/typescript/src/navegacion/encabezado.d.ts.map +1 -0
- package/lib/typescript/src/pantallas/PantallaDeAcceso.d.ts +32 -0
- package/lib/typescript/src/pantallas/PantallaDeAcceso.d.ts.map +1 -0
- package/lib/typescript/src/pantallas/PantallaDeEdicion.d.ts +38 -0
- package/lib/typescript/src/pantallas/PantallaDeEdicion.d.ts.map +1 -0
- package/lib/typescript/src/pantallas/PantallaDeFicha.d.ts +35 -0
- package/lib/typescript/src/pantallas/PantallaDeFicha.d.ts.map +1 -0
- package/lib/typescript/src/pantallas/PantallaDeHistorial.d.ts +65 -0
- package/lib/typescript/src/pantallas/PantallaDeHistorial.d.ts.map +1 -0
- package/lib/typescript/src/pantallas/PantallaDePerfil.d.ts +19 -0
- package/lib/typescript/src/pantallas/PantallaDePerfil.d.ts.map +1 -0
- package/lib/typescript/src/tema/ProveedorDeTema.d.ts +32 -0
- package/lib/typescript/src/tema/ProveedorDeTema.d.ts.map +1 -0
- package/lib/typescript/src/tema/estilos.d.ts +31 -0
- package/lib/typescript/src/tema/estilos.d.ts.map +1 -0
- package/lib/typescript/src/tema/fuentes.d.ts +20 -0
- package/lib/typescript/src/tema/fuentes.d.ts.map +1 -0
- package/lib/typescript/src/tema/index.d.ts +6 -0
- package/lib/typescript/src/tema/index.d.ts.map +1 -0
- package/lib/typescript/src/tema/paleta.d.ts +86 -0
- package/lib/typescript/src/tema/paleta.d.ts.map +1 -0
- package/lib/typescript/src/tema/tipografia.d.ts +29 -0
- package/lib/typescript/src/tema/tipografia.d.ts.map +1 -0
- package/lib/typescript/src/texto.d.ts +16 -0
- package/lib/typescript/src/texto.d.ts.map +1 -0
- package/package.json +140 -5
- package/src/camara/CampoDeFotos.tsx +132 -0
- package/src/camara/elegir.ts +113 -0
- package/src/camara/index.ts +15 -0
- package/src/camara/reducir.ts +87 -0
- package/src/componentes/AccionesDelEncabezado.tsx +247 -0
- package/src/componentes/BotonDeAvatar.tsx +59 -0
- package/src/componentes/BotonGrande.tsx +100 -0
- package/src/componentes/CabeceraEmpujada.tsx +69 -0
- package/src/componentes/Chip.tsx +60 -0
- package/src/componentes/ContenidoDePantalla.tsx +53 -0
- package/src/componentes/EstadoVacio.tsx +45 -0
- package/src/componentes/Foto.tsx +92 -0
- package/src/componentes/IconoDeRenglon.tsx +42 -0
- package/src/componentes/Logo.tsx +34 -0
- package/src/componentes/Nota.tsx +37 -0
- package/src/componentes/OpcionesExcluyentes.tsx +62 -0
- package/src/componentes/PantallaDeError.tsx +76 -0
- package/src/componentes/Pestanas.tsx +86 -0
- package/src/componentes/Rotulo.tsx +21 -0
- package/src/componentes/RotuloDeSeccion.tsx +71 -0
- package/src/componentes/Tarjeta.tsx +58 -0
- package/src/componentes/TarjetaDeDatos.tsx +62 -0
- package/src/componentes/TiraDeFotos.tsx +189 -0
- package/src/componentes/VisorDeFoto.tsx +181 -0
- package/src/componentes/logos/bola-blanca.png +0 -0
- package/src/componentes/logos/bola.png +0 -0
- package/src/componentes/logos/logo-blanco.png +0 -0
- package/src/componentes/logos/logo.png +0 -0
- package/src/dialogos/HostDeDialogos.tsx +172 -0
- package/src/dialogos/dialogos.ts +148 -0
- package/src/formularios/CampoDeSeleccion.tsx +60 -0
- package/src/formularios/SelectorDeOpcion.tsx +407 -0
- package/src/formularios/TextoDeAccesoControlado.tsx +72 -0
- package/src/formularios/campos.tsx +497 -0
- package/src/formularios/controlados.tsx +200 -0
- package/src/formularios/errores.ts +105 -0
- package/src/formularios/guardado.ts +76 -0
- package/src/formularios/useBusqueda.ts +19 -0
- package/src/fotos/elegir.ts +116 -0
- package/src/fotos/index.ts +6 -0
- package/src/hoja/HojaDeLista.tsx +214 -0
- package/src/hoja/HojaInferior.tsx +103 -0
- package/src/hoja/index.ts +5 -0
- package/src/hoja/useMasDeLaFicha.tsx +63 -0
- package/src/index.ts +151 -0
- package/src/listas/HojaDeFiltros.tsx +286 -0
- package/src/listas/ListaPaginada.tsx +117 -0
- package/src/listas/TarjetaDeslizable.tsx +126 -0
- package/src/listas/paginado.ts +15 -0
- package/src/listas/piezas.tsx +670 -0
- package/src/navegacion/BarraDePestanas.tsx +198 -0
- package/src/navegacion/BotonDeBarra.tsx +61 -0
- package/src/navegacion/encabezado.tsx +88 -0
- package/src/pantallas/PantallaDeAcceso.tsx +184 -0
- package/src/pantallas/PantallaDeEdicion.tsx +116 -0
- package/src/pantallas/PantallaDeFicha.tsx +115 -0
- package/src/pantallas/PantallaDeHistorial.tsx +293 -0
- package/src/pantallas/PantallaDePerfil.tsx +126 -0
- package/src/tema/ProveedorDeTema.tsx +150 -0
- package/src/tema/estilos.ts +51 -0
- package/src/tema/fuentes.ts +20 -0
- package/src/tema/index.ts +10 -0
- package/src/tema/paleta.ts +147 -0
- package/src/tema/tipografia.ts +29 -0
- package/src/texto.tsx +44 -0
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { Text } from "../texto";
|
|
2
|
+
import { crearEstilos, renglon, tipografia } from "../tema";
|
|
3
|
+
|
|
4
|
+
/** El rótulo de una sección de un formulario, en mayúsculas: «Referencias», «Sello». */
|
|
5
|
+
const Rotulo = ({ children }: { children: string }) => {
|
|
6
|
+
const styles = useStyles();
|
|
7
|
+
return <Text style={styles.rotulo}>{children}</Text>;
|
|
8
|
+
};
|
|
9
|
+
|
|
10
|
+
const useStyles = crearEstilos((c) => ({
|
|
11
|
+
rotulo: {
|
|
12
|
+
...renglon(tipografia.etiqueta),
|
|
13
|
+
fontWeight: "800",
|
|
14
|
+
color: c.secondary,
|
|
15
|
+
textTransform: "uppercase",
|
|
16
|
+
letterSpacing: 0.8,
|
|
17
|
+
marginTop: 8,
|
|
18
|
+
},
|
|
19
|
+
}));
|
|
20
|
+
|
|
21
|
+
export default Rotulo;
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
import { crearEstilos, renglon, tipografia, usePaleta } from "../tema";
|
|
2
|
+
import { Text } from "../texto";
|
|
3
|
+
import { Pressable, useWindowDimensions, View } from "react-native";
|
|
4
|
+
|
|
5
|
+
interface Props {
|
|
6
|
+
children: string;
|
|
7
|
+
/** El número de la sección, en un círculo rojo (gris si es cero). Sin él, sólo el rótulo. */
|
|
8
|
+
cuantas?: number;
|
|
9
|
+
/**
|
|
10
|
+
* La puerta a lo que la sección resume, a la derecha: «Editar ›», «Ver todos ›». Va en el rótulo
|
|
11
|
+
* y no como botón aparte para que no le robe un renglón a la lista.
|
|
12
|
+
*/
|
|
13
|
+
accion?: { texto: string; onPress: () => void };
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* El rótulo de una sección de lista —«EN CURSO», «PENDIENTES 3»—, el `SectionLabel` de las
|
|
18
|
+
* revisiones de wms con la letra grande de las apps.
|
|
19
|
+
*/
|
|
20
|
+
const RotuloDeSeccion = ({ children, cuantas, accion }: Props) => {
|
|
21
|
+
const c = usePaleta();
|
|
22
|
+
const styles = useStyles();
|
|
23
|
+
// El círculo crece con la letra del sistema, igual que el número que lleva adentro.
|
|
24
|
+
const { fontScale } = useWindowDimensions();
|
|
25
|
+
const alto = renglon(tipografia.etiqueta).lineHeight * fontScale;
|
|
26
|
+
|
|
27
|
+
return (
|
|
28
|
+
<View style={styles.root}>
|
|
29
|
+
<Text style={styles.texto}>{children}</Text>
|
|
30
|
+
{cuantas != null ? (
|
|
31
|
+
<View
|
|
32
|
+
style={[
|
|
33
|
+
styles.cuenta,
|
|
34
|
+
{ minWidth: alto, height: alto, borderRadius: alto / 2 },
|
|
35
|
+
{ backgroundColor: cuantas > 0 ? c.primary : c.secondaryLight },
|
|
36
|
+
]}
|
|
37
|
+
>
|
|
38
|
+
<Text style={styles.cuentaTexto}>{cuantas}</Text>
|
|
39
|
+
</View>
|
|
40
|
+
) : null}
|
|
41
|
+
{accion ? (
|
|
42
|
+
<Pressable
|
|
43
|
+
onPress={accion.onPress}
|
|
44
|
+
accessibilityRole="button"
|
|
45
|
+
hitSlop={8}
|
|
46
|
+
style={styles.accion}
|
|
47
|
+
>
|
|
48
|
+
<Text style={styles.accionTexto}>{accion.texto} ›</Text>
|
|
49
|
+
</Pressable>
|
|
50
|
+
) : null}
|
|
51
|
+
</View>
|
|
52
|
+
);
|
|
53
|
+
};
|
|
54
|
+
|
|
55
|
+
const useStyles = crearEstilos((c) => ({
|
|
56
|
+
root: { flexDirection: "row", alignItems: "center", gap: 8, flexWrap: "wrap" },
|
|
57
|
+
texto: {
|
|
58
|
+
...renglon(tipografia.etiqueta),
|
|
59
|
+
color: c.texto,
|
|
60
|
+
textTransform: "uppercase",
|
|
61
|
+
letterSpacing: 1.4,
|
|
62
|
+
fontWeight: "800",
|
|
63
|
+
},
|
|
64
|
+
cuenta: { paddingHorizontal: 6, alignItems: "center", justifyContent: "center" },
|
|
65
|
+
cuentaTexto: { ...renglon(tipografia.etiqueta), color: c.sobrePrimario, fontWeight: "800" },
|
|
66
|
+
// `marginLeft: auto` la manda a la orilla derecha sin un separador que mida nada.
|
|
67
|
+
accion: { marginLeft: "auto" },
|
|
68
|
+
accionTexto: { ...renglon(tipografia.etiqueta), color: c.primary, fontWeight: "800" },
|
|
69
|
+
}));
|
|
70
|
+
|
|
71
|
+
export default RotuloDeSeccion;
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
import { Pressable, View, type StyleProp, type ViewStyle } from "react-native";
|
|
3
|
+
import { crearEstilos } from "../tema";
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Si lo de adentro está en una hoja que sube desde abajo (`HojaInferior`, `HojaDeLista`). La hoja
|
|
7
|
+
* es de la superficie, igual que una tarjeta: ahí la tarjeta va del color del fondo, o no se
|
|
8
|
+
* distinguiría un renglón del siguiente. Lo ponen las hojas; las pantallas no lo tocan.
|
|
9
|
+
*/
|
|
10
|
+
export const EnHoja = React.createContext(false);
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* La tarjeta de la casa, sola: la superficie, sus esquinas y su aire, con lo que se le ponga
|
|
14
|
+
* adentro. Es de lo que están hechas `TarjetaDeLista` y las fichas; una pantalla la usa para lo que
|
|
15
|
+
* no es un renglón de lista —un bloque de fotos, el resumen de un cobro, un grupo de campos—.
|
|
16
|
+
*
|
|
17
|
+
* **Sin sombra**: en oscuro casi no se ve, así que lo que separa la tarjeta del fondo es el color
|
|
18
|
+
* de la superficie y, en oscuro, un borde. Con `onPress` se toca y se atenúa al presionarla.
|
|
19
|
+
*
|
|
20
|
+
* Dentro de una hoja (`EnHoja`) va del color del fondo, así las listas de una pantalla se pueden
|
|
21
|
+
* enseñar también en una hoja sin otra versión de su renglón.
|
|
22
|
+
*/
|
|
23
|
+
export default function Tarjeta({
|
|
24
|
+
children,
|
|
25
|
+
onPress,
|
|
26
|
+
style,
|
|
27
|
+
}: {
|
|
28
|
+
children: React.ReactNode;
|
|
29
|
+
onPress?: () => void;
|
|
30
|
+
style?: StyleProp<ViewStyle>;
|
|
31
|
+
}) {
|
|
32
|
+
const styles = useStyles();
|
|
33
|
+
const enHoja = React.useContext(EnHoja);
|
|
34
|
+
const base = [styles.tarjeta, enHoja && styles.enHoja];
|
|
35
|
+
if (!onPress) return <View style={[base, style]}>{children}</View>;
|
|
36
|
+
return (
|
|
37
|
+
<Pressable
|
|
38
|
+
onPress={onPress}
|
|
39
|
+
accessibilityRole="button"
|
|
40
|
+
style={({ pressed }) => [base, style, pressed && styles.presionada]}
|
|
41
|
+
>
|
|
42
|
+
{children}
|
|
43
|
+
</Pressable>
|
|
44
|
+
);
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
const useStyles = crearEstilos((c) => ({
|
|
48
|
+
tarjeta: {
|
|
49
|
+
backgroundColor: c.superficie,
|
|
50
|
+
borderRadius: 14,
|
|
51
|
+
padding: 14,
|
|
52
|
+
gap: 4,
|
|
53
|
+
borderWidth: c.esOscuro ? 1 : 0,
|
|
54
|
+
borderColor: c.secondaryLighter,
|
|
55
|
+
},
|
|
56
|
+
enHoja: { backgroundColor: c.fondo },
|
|
57
|
+
presionada: { opacity: 0.7 },
|
|
58
|
+
}));
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
import { Text } from "../texto";
|
|
2
|
+
import { crearEstilos, renglon, tipografia } from "../tema";
|
|
3
|
+
import { useWindowDimensions, View } from "react-native";
|
|
4
|
+
|
|
5
|
+
/** Desde qué ancho los datos van a dos columnas: una tableta. */
|
|
6
|
+
const ANCHO_A_DOS_COLUMNAS = 700;
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Datos de solo lectura, cada uno con su rótulo arriba: la ficha de una estancia de caseta, la de
|
|
10
|
+
* un viaje o un tracto en la oficina.
|
|
11
|
+
*
|
|
12
|
+
* **En una pantalla ancha van a dos columnas**; en un teléfono, uno debajo del otro. Los vacíos no
|
|
13
|
+
* se pintan. `atenuada` es la de algo que ya no cuenta (un movimiento anulado).
|
|
14
|
+
*/
|
|
15
|
+
const TarjetaDeDatos = ({
|
|
16
|
+
datos,
|
|
17
|
+
atenuada,
|
|
18
|
+
}: {
|
|
19
|
+
datos: [string, string | null | undefined][];
|
|
20
|
+
atenuada?: boolean;
|
|
21
|
+
}) => {
|
|
22
|
+
const styles = useStyles();
|
|
23
|
+
const { width } = useWindowDimensions();
|
|
24
|
+
const dosColumnas = width >= ANCHO_A_DOS_COLUMNAS;
|
|
25
|
+
return (
|
|
26
|
+
<View
|
|
27
|
+
style={[
|
|
28
|
+
styles.tarjeta,
|
|
29
|
+
dosColumnas && styles.tarjetaAncha,
|
|
30
|
+
atenuada && styles.atenuada,
|
|
31
|
+
]}
|
|
32
|
+
>
|
|
33
|
+
{datos
|
|
34
|
+
.filter(([, valor]) => valor)
|
|
35
|
+
.map(([etiqueta, valor]) => (
|
|
36
|
+
<View key={etiqueta} style={[styles.dato, dosColumnas && styles.datoEnColumna]}>
|
|
37
|
+
<Text style={styles.etiquetaDeDato}>{etiqueta}</Text>
|
|
38
|
+
<Text style={styles.valor}>{valor}</Text>
|
|
39
|
+
</View>
|
|
40
|
+
))}
|
|
41
|
+
</View>
|
|
42
|
+
);
|
|
43
|
+
};
|
|
44
|
+
|
|
45
|
+
const useStyles = crearEstilos((c) => ({
|
|
46
|
+
tarjeta: { backgroundColor: c.superficie, borderRadius: 14, padding: 14, gap: 10 },
|
|
47
|
+
tarjetaAncha: { flexDirection: "row", flexWrap: "wrap", columnGap: 24, rowGap: 12 },
|
|
48
|
+
atenuada: { opacity: 0.55 },
|
|
49
|
+
dato: { gap: 2 },
|
|
50
|
+
// Dos por renglón: la mitad del ancho menos la mitad del espacio entre columnas.
|
|
51
|
+
datoEnColumna: { width: "48%", flexGrow: 1 },
|
|
52
|
+
etiquetaDeDato: {
|
|
53
|
+
...renglon(tipografia.etiqueta),
|
|
54
|
+
fontWeight: "800",
|
|
55
|
+
color: c.secondary,
|
|
56
|
+
textTransform: "uppercase",
|
|
57
|
+
letterSpacing: 0.8,
|
|
58
|
+
},
|
|
59
|
+
valor: { ...renglon(tipografia.subtitulo), fontWeight: "800", color: c.texto },
|
|
60
|
+
}));
|
|
61
|
+
|
|
62
|
+
export default TarjetaDeDatos;
|
|
@@ -0,0 +1,189 @@
|
|
|
1
|
+
import MaterialCommunityIcons from "@expo/vector-icons/MaterialCommunityIcons";
|
|
2
|
+
import React from "react";
|
|
3
|
+
import { Pressable, ScrollView, View } from "react-native";
|
|
4
|
+
import { crearEstilos, tipografia, usePaleta } from "../tema";
|
|
5
|
+
import { Text } from "../texto";
|
|
6
|
+
import Foto, { type FotoAVer } from "./Foto";
|
|
7
|
+
import VisorDeFoto from "./VisorDeFoto";
|
|
8
|
+
|
|
9
|
+
/** Una miniatura de la tira: qué se pinta, qué abre el visor y qué se puede hacer con ella. */
|
|
10
|
+
export interface FotoDeLaTira {
|
|
11
|
+
llave: string;
|
|
12
|
+
/** Un archivo que no es foto (un PDF) se ve con su icono y no entra al visor. */
|
|
13
|
+
esFoto: boolean;
|
|
14
|
+
miniatura: FotoAVer;
|
|
15
|
+
/** La foto completa, si la miniatura es una previa chica del servidor. Por omisión, ésa. */
|
|
16
|
+
completa?: FotoAVer;
|
|
17
|
+
/** El nombre del archivo: se ve en un documento que no es foto. */
|
|
18
|
+
nombre?: string;
|
|
19
|
+
/** Una nube en la esquina: verde si ya está en el servidor, ámbar si sigue en el teléfono. */
|
|
20
|
+
marca?: "enviada" | "sinSubir";
|
|
21
|
+
/** Con esto lleva su «×» para quitarla. */
|
|
22
|
+
onQuitar?: () => void;
|
|
23
|
+
/** Con esto, el visor lleva «descargar» en ella. Rechaza si no se pudo. */
|
|
24
|
+
onDescargar?: () => Promise<void>;
|
|
25
|
+
/** Lo que dice el lector de pantalla, si no basta con «Ver la foto». */
|
|
26
|
+
etiqueta?: string;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
interface Props {
|
|
30
|
+
fotos: FotoDeLaTira[];
|
|
31
|
+
/** El lado de cada miniatura. */
|
|
32
|
+
lado: number;
|
|
33
|
+
/** En cuadrícula (una hoja, donde hay lugar) o en un renglón que se desliza a los lados. */
|
|
34
|
+
cuadricula?: boolean;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* Unas fotos en miniatura; tocar una abre el visor en ésa y se desliza entre las demás, **en el
|
|
39
|
+
* mismo orden** que las miniaturas. Quien la usa arma las piezas con lo suyo (lo del servidor
|
|
40
|
+
* primero y lo del teléfono después, por ejemplo) y les pone su marca y sus acciones.
|
|
41
|
+
*/
|
|
42
|
+
const TiraDeFotos = ({ fotos: piezas, lado, cuadricula = false }: Props) => {
|
|
43
|
+
const styles = useStyles();
|
|
44
|
+
const [viendo, setViendo] = React.useState<number | null>(null);
|
|
45
|
+
if (piezas.length === 0) return null;
|
|
46
|
+
|
|
47
|
+
const fotos = piezas.filter((p) => p.esFoto);
|
|
48
|
+
const carrusel = fotos.map((p) => p.completa ?? p.miniatura);
|
|
49
|
+
const descargables = fotos.some((p) => p.onDescargar);
|
|
50
|
+
const tamano = { width: lado, height: lado };
|
|
51
|
+
|
|
52
|
+
const miniaturas = piezas.map((p) => (
|
|
53
|
+
<View key={p.llave}>
|
|
54
|
+
<Pressable
|
|
55
|
+
disabled={!p.esFoto}
|
|
56
|
+
onPress={() => setViendo(fotos.indexOf(p))}
|
|
57
|
+
accessibilityRole={p.esFoto ? "imagebutton" : undefined}
|
|
58
|
+
accessibilityLabel={
|
|
59
|
+
p.etiqueta ?? (p.esFoto ? "Ver la foto" : `Archivo ${p.nombre ?? ""}`.trim())
|
|
60
|
+
}
|
|
61
|
+
>
|
|
62
|
+
{p.esFoto ? (
|
|
63
|
+
<Foto
|
|
64
|
+
foto={p.miniatura}
|
|
65
|
+
style={[styles.miniatura, tamano]}
|
|
66
|
+
resizeMethod="resize"
|
|
67
|
+
/>
|
|
68
|
+
) : (
|
|
69
|
+
<Documento lado={lado} nombre={p.nombre} />
|
|
70
|
+
)}
|
|
71
|
+
{p.marca === "enviada" ? (
|
|
72
|
+
<View style={[styles.marca, styles.marcaEnviada]}>
|
|
73
|
+
<MaterialCommunityIcons name="cloud-check" size={16} color="#fff" />
|
|
74
|
+
</View>
|
|
75
|
+
) : null}
|
|
76
|
+
{p.marca === "sinSubir" ? (
|
|
77
|
+
<View style={[styles.marca, styles.marcaSinSubir]}>
|
|
78
|
+
<MaterialCommunityIcons name="cloud-clock-outline" size={16} color="#fff" />
|
|
79
|
+
</View>
|
|
80
|
+
) : null}
|
|
81
|
+
</Pressable>
|
|
82
|
+
{p.onQuitar ? (
|
|
83
|
+
<Pressable
|
|
84
|
+
onPress={p.onQuitar}
|
|
85
|
+
hitSlop={10}
|
|
86
|
+
accessibilityRole="button"
|
|
87
|
+
accessibilityLabel={p.esFoto ? "Quitar esta foto" : "Quitar este archivo"}
|
|
88
|
+
style={styles.quitar}
|
|
89
|
+
>
|
|
90
|
+
<MaterialCommunityIcons name="close" size={24} color="#fff" />
|
|
91
|
+
</Pressable>
|
|
92
|
+
) : null}
|
|
93
|
+
</View>
|
|
94
|
+
));
|
|
95
|
+
|
|
96
|
+
return (
|
|
97
|
+
<>
|
|
98
|
+
{cuadricula ? (
|
|
99
|
+
<View style={styles.cuadricula}>{miniaturas}</View>
|
|
100
|
+
) : (
|
|
101
|
+
// Un solo renglón que se desliza: con varias fotos no se parte en filas.
|
|
102
|
+
<ScrollView
|
|
103
|
+
horizontal
|
|
104
|
+
showsHorizontalScrollIndicator={false}
|
|
105
|
+
contentContainerStyle={styles.renglon}
|
|
106
|
+
>
|
|
107
|
+
{miniaturas}
|
|
108
|
+
</ScrollView>
|
|
109
|
+
)}
|
|
110
|
+
<VisorDeFoto
|
|
111
|
+
fotos={carrusel}
|
|
112
|
+
inicio={viendo}
|
|
113
|
+
onCerrar={() => setViendo(null)}
|
|
114
|
+
onDescargar={
|
|
115
|
+
descargables
|
|
116
|
+
? async (i) => {
|
|
117
|
+
const descargar = fotos[i]?.onDescargar;
|
|
118
|
+
if (!descargar) throw new Error("Esta foto no se puede descargar.");
|
|
119
|
+
await descargar();
|
|
120
|
+
}
|
|
121
|
+
: undefined
|
|
122
|
+
}
|
|
123
|
+
/>
|
|
124
|
+
</>
|
|
125
|
+
);
|
|
126
|
+
};
|
|
127
|
+
|
|
128
|
+
/** La miniatura de un archivo que no es foto (un PDF): un icono y, si cabe, su nombre. */
|
|
129
|
+
function Documento({ lado, nombre }: { lado: number; nombre?: string }) {
|
|
130
|
+
const c = usePaleta();
|
|
131
|
+
const styles = useStyles();
|
|
132
|
+
const conNombre = Boolean(nombre) && lado >= 100;
|
|
133
|
+
return (
|
|
134
|
+
<View style={[styles.miniatura, styles.documento, { width: lado, height: lado }]}>
|
|
135
|
+
<MaterialCommunityIcons
|
|
136
|
+
name="file-document-outline"
|
|
137
|
+
size={Math.round(lado * 0.3)}
|
|
138
|
+
color={c.secondary}
|
|
139
|
+
/>
|
|
140
|
+
{conNombre ? (
|
|
141
|
+
// Dentro de un cuadro fijo: el nombre largo de un PDF tiene que cortarse.
|
|
142
|
+
<Text numberOfLines={2} style={styles.documentoNombre}>
|
|
143
|
+
{nombre}
|
|
144
|
+
</Text>
|
|
145
|
+
) : null}
|
|
146
|
+
</View>
|
|
147
|
+
);
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
// Las marcas y la «×» van encima de la foto: sus colores son fijos para leerse sobre cualquier foto.
|
|
151
|
+
const useStyles = crearEstilos((c) => ({
|
|
152
|
+
renglon: { gap: 8 },
|
|
153
|
+
cuadricula: { flexDirection: "row", flexWrap: "wrap", gap: 12 },
|
|
154
|
+
miniatura: { borderRadius: 10, backgroundColor: c.secondaryLighter },
|
|
155
|
+
documento: { alignItems: "center", justifyContent: "center", padding: 6, gap: 2 },
|
|
156
|
+
documentoNombre: {
|
|
157
|
+
fontSize: tipografia.etiqueta,
|
|
158
|
+
color: c.secondaryDark,
|
|
159
|
+
textAlign: "center",
|
|
160
|
+
},
|
|
161
|
+
marca: {
|
|
162
|
+
position: "absolute",
|
|
163
|
+
bottom: 4,
|
|
164
|
+
right: 4,
|
|
165
|
+
width: 24,
|
|
166
|
+
height: 24,
|
|
167
|
+
borderRadius: 12,
|
|
168
|
+
alignItems: "center",
|
|
169
|
+
justifyContent: "center",
|
|
170
|
+
},
|
|
171
|
+
marcaEnviada: { backgroundColor: "#2e7d32" },
|
|
172
|
+
// La nube ámbar sobre una foto que sigue en el teléfono sin subir.
|
|
173
|
+
marcaSinSubir: { backgroundColor: "#b26a00" },
|
|
174
|
+
quitar: {
|
|
175
|
+
position: "absolute",
|
|
176
|
+
top: 6,
|
|
177
|
+
right: 6,
|
|
178
|
+
width: 40,
|
|
179
|
+
height: 40,
|
|
180
|
+
borderRadius: 20,
|
|
181
|
+
backgroundColor: c.primaryDark,
|
|
182
|
+
alignItems: "center",
|
|
183
|
+
justifyContent: "center",
|
|
184
|
+
borderWidth: 2,
|
|
185
|
+
borderColor: "#fff",
|
|
186
|
+
},
|
|
187
|
+
}));
|
|
188
|
+
|
|
189
|
+
export default TiraDeFotos;
|
|
@@ -0,0 +1,181 @@
|
|
|
1
|
+
import { renglon, tipografia } from "../tema";
|
|
2
|
+
import { Text } from "../texto";
|
|
3
|
+
import MaterialCommunityIcons from "@expo/vector-icons/MaterialCommunityIcons";
|
|
4
|
+
import React from "react";
|
|
5
|
+
import {
|
|
6
|
+
ActivityIndicator,
|
|
7
|
+
FlatList,
|
|
8
|
+
Modal,
|
|
9
|
+
Pressable,
|
|
10
|
+
StyleSheet,
|
|
11
|
+
useWindowDimensions,
|
|
12
|
+
View,
|
|
13
|
+
} from "react-native";
|
|
14
|
+
import { useSafeAreaInsets } from "react-native-safe-area-context";
|
|
15
|
+
import Foto, { type FotoAVer } from "./Foto";
|
|
16
|
+
|
|
17
|
+
export type { FotoAVer };
|
|
18
|
+
|
|
19
|
+
interface Props {
|
|
20
|
+
/** Las fotos, en el orden de sus miniaturas. Los archivos que no son foto no entran. */
|
|
21
|
+
fotos: FotoAVer[];
|
|
22
|
+
/** La que se tocó; `null` cierra el visor. */
|
|
23
|
+
inicio: number | null;
|
|
24
|
+
onCerrar: () => void;
|
|
25
|
+
/**
|
|
26
|
+
* Con esto, la barra lleva «descargar» para la foto que se ve.
|
|
27
|
+
* Recibe su lugar en `fotos`; rechaza si no se pudo.
|
|
28
|
+
*/
|
|
29
|
+
onDescargar?: (indice: number) => Promise<void>;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
/** Cómo va la descarga de la foto que se ve: el icono de la barra lo dice. */
|
|
33
|
+
type Descarga = "nada" | "bajando" | "lista" | "fallo";
|
|
34
|
+
const ICONO_DE_DESCARGA = {
|
|
35
|
+
nada: "download",
|
|
36
|
+
lista: "check",
|
|
37
|
+
fallo: "alert-circle-outline",
|
|
38
|
+
} as const;
|
|
39
|
+
|
|
40
|
+
const llave = (foto: FotoAVer, i: number) => ("ruta" in foto ? foto.ruta : foto.uri) + i;
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* Unas fotos a pantalla completa, sobre fondo negro: se abre en la que se tocó y se pasa a la
|
|
44
|
+
* siguiente o la anterior deslizando a los lados, con «2 de 4» arriba. Mezcla las subidas y las del
|
|
45
|
+
* teléfono. Se cierra con la X o con atrás. Lo abre `TiraDeFotos`.
|
|
46
|
+
*/
|
|
47
|
+
const VisorDeFoto = ({ fotos, inicio, onCerrar, onDescargar }: Props) => (
|
|
48
|
+
<Modal
|
|
49
|
+
visible={inicio !== null}
|
|
50
|
+
transparent
|
|
51
|
+
animationType="fade"
|
|
52
|
+
statusBarTranslucent
|
|
53
|
+
onRequestClose={onCerrar}
|
|
54
|
+
>
|
|
55
|
+
{/* Con `key`, cada apertura nace en la foto que se tocó. */}
|
|
56
|
+
{inicio !== null ? (
|
|
57
|
+
<Carrusel
|
|
58
|
+
key={inicio}
|
|
59
|
+
fotos={fotos}
|
|
60
|
+
inicio={inicio}
|
|
61
|
+
onCerrar={onCerrar}
|
|
62
|
+
onDescargar={onDescargar}
|
|
63
|
+
/>
|
|
64
|
+
) : null}
|
|
65
|
+
</Modal>
|
|
66
|
+
);
|
|
67
|
+
|
|
68
|
+
function Carrusel({
|
|
69
|
+
fotos,
|
|
70
|
+
inicio,
|
|
71
|
+
onCerrar,
|
|
72
|
+
onDescargar,
|
|
73
|
+
}: {
|
|
74
|
+
fotos: FotoAVer[];
|
|
75
|
+
inicio: number;
|
|
76
|
+
onCerrar: () => void;
|
|
77
|
+
onDescargar?: (indice: number) => Promise<void>;
|
|
78
|
+
}) {
|
|
79
|
+
const insets = useSafeAreaInsets();
|
|
80
|
+
const { width, height } = useWindowDimensions();
|
|
81
|
+
const primera = Math.min(inicio, Math.max(0, fotos.length - 1));
|
|
82
|
+
const [actual, setActual] = React.useState(primera);
|
|
83
|
+
const [descarga, setDescarga] = React.useState<Descarga>("nada");
|
|
84
|
+
|
|
85
|
+
// Lo que dice el icono es de la foto en la que estaba: al pasar a otra, vuelve a «descargar».
|
|
86
|
+
const pasarA = (indice: number) => {
|
|
87
|
+
if (indice !== actual) setDescarga("nada");
|
|
88
|
+
setActual(indice);
|
|
89
|
+
};
|
|
90
|
+
|
|
91
|
+
const descargar = async () => {
|
|
92
|
+
if (!onDescargar || descarga === "bajando") return;
|
|
93
|
+
setDescarga("bajando");
|
|
94
|
+
try {
|
|
95
|
+
await onDescargar(actual);
|
|
96
|
+
setDescarga("lista");
|
|
97
|
+
} catch {
|
|
98
|
+
setDescarga("fallo");
|
|
99
|
+
}
|
|
100
|
+
};
|
|
101
|
+
|
|
102
|
+
return (
|
|
103
|
+
<View style={styles.fondo}>
|
|
104
|
+
<FlatList
|
|
105
|
+
data={fotos}
|
|
106
|
+
keyExtractor={llave}
|
|
107
|
+
horizontal
|
|
108
|
+
pagingEnabled
|
|
109
|
+
showsHorizontalScrollIndicator={false}
|
|
110
|
+
initialScrollIndex={primera}
|
|
111
|
+
getItemLayout={(_, i) => ({ length: width, offset: width * i, index: i })}
|
|
112
|
+
onMomentumScrollEnd={(e) =>
|
|
113
|
+
pasarA(Math.round(e.nativeEvent.contentOffset.x / width))
|
|
114
|
+
}
|
|
115
|
+
renderItem={({ item }) => (
|
|
116
|
+
<View style={{ width, height }}>
|
|
117
|
+
<Foto foto={item} style={styles.foto} resizeMode="contain" />
|
|
118
|
+
</View>
|
|
119
|
+
)}
|
|
120
|
+
/>
|
|
121
|
+
|
|
122
|
+
<View style={[styles.barra, { top: insets.top + 12 }]}>
|
|
123
|
+
{fotos.length > 1 ? (
|
|
124
|
+
<Text style={styles.cuenta}>
|
|
125
|
+
{actual + 1} de {fotos.length}
|
|
126
|
+
</Text>
|
|
127
|
+
) : (
|
|
128
|
+
<View />
|
|
129
|
+
)}
|
|
130
|
+
<View style={styles.botones}>
|
|
131
|
+
{onDescargar ? (
|
|
132
|
+
<Pressable
|
|
133
|
+
onPress={descargar}
|
|
134
|
+
hitSlop={12}
|
|
135
|
+
accessibilityRole="button"
|
|
136
|
+
accessibilityLabel={
|
|
137
|
+
descarga === "lista"
|
|
138
|
+
? "Guardada en la galería"
|
|
139
|
+
: descarga === "fallo"
|
|
140
|
+
? "No se pudo guardar; intentar de nuevo"
|
|
141
|
+
: "Guardar en la galería"
|
|
142
|
+
}
|
|
143
|
+
accessibilityState={{ busy: descarga === "bajando" }}
|
|
144
|
+
>
|
|
145
|
+
{descarga === "bajando" ? (
|
|
146
|
+
<ActivityIndicator size={32} color="#fff" />
|
|
147
|
+
) : (
|
|
148
|
+
<MaterialCommunityIcons
|
|
149
|
+
name={ICONO_DE_DESCARGA[descarga]}
|
|
150
|
+
size={32}
|
|
151
|
+
color="#fff"
|
|
152
|
+
/>
|
|
153
|
+
)}
|
|
154
|
+
</Pressable>
|
|
155
|
+
) : null}
|
|
156
|
+
<Pressable onPress={onCerrar} hitSlop={12} accessibilityLabel="Cerrar">
|
|
157
|
+
<MaterialCommunityIcons name="close" size={32} color="#fff" />
|
|
158
|
+
</Pressable>
|
|
159
|
+
</View>
|
|
160
|
+
</View>
|
|
161
|
+
</View>
|
|
162
|
+
);
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
// Sobre la foto: negro con texto blanco, en claro y en oscuro.
|
|
166
|
+
const styles = StyleSheet.create({
|
|
167
|
+
fondo: { flex: 1, backgroundColor: "rgba(0,0,0,0.95)" },
|
|
168
|
+
foto: { width: "100%", height: "100%" },
|
|
169
|
+
barra: {
|
|
170
|
+
position: "absolute",
|
|
171
|
+
left: 16,
|
|
172
|
+
right: 16,
|
|
173
|
+
flexDirection: "row",
|
|
174
|
+
alignItems: "center",
|
|
175
|
+
justifyContent: "space-between",
|
|
176
|
+
},
|
|
177
|
+
botones: { flexDirection: "row", alignItems: "center", gap: 28 },
|
|
178
|
+
cuenta: { ...renglon(tipografia.cuerpo), color: "#fff", fontWeight: "800" },
|
|
179
|
+
});
|
|
180
|
+
|
|
181
|
+
export default VisorDeFoto;
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|