@acvl/mobile-components 0.0.0-stage → 0.0.1
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 +146 -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/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 +47 -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 +47 -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 +493 -0
- package/lib/module/formularios/campos.js.map +1 -0
- package/lib/module/formularios/controlados.js +197 -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 +178 -0
- package/lib/module/fotos/elegir.js.map +1 -0
- package/lib/module/fotos/index.js +8 -0
- package/lib/module/fotos/index.js.map +1 -0
- package/lib/module/fotos/reducir.js +93 -0
- package/lib/module/fotos/reducir.js.map +1 -0
- package/lib/module/hoja/HojaInferior.js +108 -0
- package/lib/module/hoja/HojaInferior.js.map +1 -0
- package/lib/module/hoja/index.js +7 -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 +66 -0
- package/lib/module/index.js.map +1 -0
- package/lib/module/listas/HojaDeFiltros.js +260 -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 +603 -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/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/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 +15 -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 +22 -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 +107 -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 +58 -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/fotos/reducir.d.ts +32 -0
- package/lib/typescript/src/fotos/reducir.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 +3 -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 +45 -0
- package/lib/typescript/src/index.d.ts.map +1 -0
- package/lib/typescript/src/listas/HojaDeFiltros.d.ts +60 -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 +135 -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/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 +135 -5
- 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 +44 -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 +46 -0
- package/src/formularios/SelectorDeOpcion.tsx +407 -0
- package/src/formularios/TextoDeAccesoControlado.tsx +72 -0
- package/src/formularios/campos.tsx +483 -0
- package/src/formularios/controlados.tsx +198 -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 +194 -0
- package/src/fotos/index.ts +15 -0
- package/src/fotos/reducir.ts +87 -0
- package/src/hoja/HojaInferior.tsx +102 -0
- package/src/hoja/index.ts +4 -0
- package/src/hoja/useMasDeLaFicha.tsx +63 -0
- package/src/index.ts +143 -0
- package/src/listas/HojaDeFiltros.tsx +261 -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 +594 -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/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,46 @@
|
|
|
1
|
+
import { View } from "react-native";
|
|
2
|
+
import { ErrorDelCampo } from "./campos";
|
|
3
|
+
import SelectorDeOpcion, {
|
|
4
|
+
SelectorDeVarias,
|
|
5
|
+
type Opcion,
|
|
6
|
+
type PropsDeSelectorDeOpcion,
|
|
7
|
+
type PropsDeSelectorDeVarias,
|
|
8
|
+
} from "./SelectorDeOpcion";
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Un campo que se elige de una lista larga —un catálogo del servidor, uno guardado en el
|
|
12
|
+
* teléfono—: `SelectorDeOpcion` con el error del formulario debajo. Es el `ControlledAutocomplete`
|
|
13
|
+
* del portal.
|
|
14
|
+
*
|
|
15
|
+
* Quien lo usa trae las opciones y, si son del servidor, la búsqueda: en una app es su consulta del
|
|
16
|
+
* catálogo (`useBusqueda` para no pedir una vez por letra). Una lista fija se pasa sin `onBuscar`
|
|
17
|
+
* y se filtra sola.
|
|
18
|
+
*/
|
|
19
|
+
export default function CampoDeSeleccion<T extends Opcion>({
|
|
20
|
+
error,
|
|
21
|
+
...props
|
|
22
|
+
}: PropsDeSelectorDeOpcion<T> & { error?: string }) {
|
|
23
|
+
return (
|
|
24
|
+
<View>
|
|
25
|
+
<SelectorDeOpcion<T> {...props} />
|
|
26
|
+
<ErrorDelCampo error={error} />
|
|
27
|
+
</View>
|
|
28
|
+
);
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* Lo mismo, pero se eligen varias (los recibos de un concepto, las piezas sobre las que se cobra
|
|
33
|
+
* un porcentaje): `SelectorDeVarias` con el error debajo. El valor es la lista de opciones
|
|
34
|
+
* enteras, igual que el de una es la opción entera.
|
|
35
|
+
*/
|
|
36
|
+
export function CampoDeVarias<T extends Opcion>({
|
|
37
|
+
error,
|
|
38
|
+
...props
|
|
39
|
+
}: PropsDeSelectorDeVarias<T> & { error?: string }) {
|
|
40
|
+
return (
|
|
41
|
+
<View>
|
|
42
|
+
<SelectorDeVarias<T> {...props} />
|
|
43
|
+
<ErrorDelCampo error={error} />
|
|
44
|
+
</View>
|
|
45
|
+
);
|
|
46
|
+
}
|
|
@@ -0,0 +1,407 @@
|
|
|
1
|
+
import MaterialIcons from "@expo/vector-icons/MaterialIcons";
|
|
2
|
+
import React from "react";
|
|
3
|
+
import {
|
|
4
|
+
ActivityIndicator,
|
|
5
|
+
FlatList,
|
|
6
|
+
Modal,
|
|
7
|
+
Pressable,
|
|
8
|
+
TouchableOpacity,
|
|
9
|
+
View,
|
|
10
|
+
} from "react-native";
|
|
11
|
+
import { useSafeAreaInsets } from "react-native-safe-area-context";
|
|
12
|
+
import BotonGrande from "../componentes/BotonGrande";
|
|
13
|
+
import EstadoVacio from "../componentes/EstadoVacio";
|
|
14
|
+
import IconoDeRenglon from "../componentes/IconoDeRenglon";
|
|
15
|
+
import { crearEstilos, renglon, tipografia, usePaleta } from "../tema";
|
|
16
|
+
import { Text, TextInput } from "../texto";
|
|
17
|
+
|
|
18
|
+
/** Una opción de una lista: lo que se guarda (`value`) y lo que se lee (`label`). */
|
|
19
|
+
export interface Opcion {
|
|
20
|
+
value: number | string;
|
|
21
|
+
label: string;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
/** Lo que tienen en común el selector de una y el de varias: de dónde salen las opciones. */
|
|
25
|
+
interface FuenteDeOpciones<T extends Opcion> {
|
|
26
|
+
etiqueta: string;
|
|
27
|
+
opciones: T[];
|
|
28
|
+
/**
|
|
29
|
+
* Si las opciones las trae el servidor, mientras llegan. Una lista fija (las modalidades, los
|
|
30
|
+
* estados) no lo pasa.
|
|
31
|
+
*/
|
|
32
|
+
cargando?: boolean;
|
|
33
|
+
/**
|
|
34
|
+
* El texto de la búsqueda, para que quien llama pida al servidor. **Sin `onBuscar` la lista es
|
|
35
|
+
* fija** y se filtra aquí, por lo que se lee: así una lista de cinco opciones no necesita una
|
|
36
|
+
* consulta ni un estado en la pantalla.
|
|
37
|
+
*/
|
|
38
|
+
texto?: string;
|
|
39
|
+
onBuscar?: (texto: string) => void;
|
|
40
|
+
/** Lo que va debajo del nombre de cada opción (un viaje: sus unidades). */
|
|
41
|
+
detalle?: (opcion: T) => string | null;
|
|
42
|
+
/** Para agrupar las opciones con un rótulo (los viajes esperados primero). */
|
|
43
|
+
grupo?: (opcion: T) => string;
|
|
44
|
+
vacio?: string;
|
|
45
|
+
disabled?: boolean;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
export interface PropsDeSelectorDeOpcion<T extends Opcion> extends FuenteDeOpciones<T> {
|
|
49
|
+
valor: T | null;
|
|
50
|
+
onElegir: (opcion: T | null) => void;
|
|
51
|
+
/** Si se puede dejar vacío: agrega «Ninguno» arriba de la lista. */
|
|
52
|
+
opcional?: boolean;
|
|
53
|
+
/** Cómo se dice el vacío de `opcional`: «Todos» en un filtro. */
|
|
54
|
+
textoDeNinguno?: string;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
export interface PropsDeSelectorDeVarias<T extends Opcion> extends FuenteDeOpciones<T> {
|
|
58
|
+
valor: T[];
|
|
59
|
+
onElegir: (opciones: T[]) => void;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
/** Para comparar lo escrito con una opción sin que estorben los acentos ni las mayúsculas. */
|
|
63
|
+
const normalizar = (texto: string) =>
|
|
64
|
+
texto.normalize("NFD").replace(/[̀-ͯ]/g, "").toLowerCase().trim();
|
|
65
|
+
|
|
66
|
+
/**
|
|
67
|
+
* La búsqueda de un selector: la de quien llama si trae `onBuscar`, o una propia que filtra la
|
|
68
|
+
* lista fija aquí mismo.
|
|
69
|
+
*/
|
|
70
|
+
function useBuscador<T extends Opcion>({ opciones, texto, onBuscar }: FuenteDeOpciones<T>) {
|
|
71
|
+
const [propio, setPropio] = React.useState("");
|
|
72
|
+
const fija = !onBuscar;
|
|
73
|
+
const actual = fija ? propio : (texto ?? "");
|
|
74
|
+
const visibles = React.useMemo(() => {
|
|
75
|
+
if (!fija || !propio.trim()) return opciones;
|
|
76
|
+
const buscado = normalizar(propio);
|
|
77
|
+
return opciones.filter((opcion) => normalizar(opcion.label).includes(buscado));
|
|
78
|
+
}, [fija, propio, opciones]);
|
|
79
|
+
return {
|
|
80
|
+
texto: actual,
|
|
81
|
+
buscar: fija ? setPropio : onBuscar,
|
|
82
|
+
visibles,
|
|
83
|
+
};
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
/**
|
|
87
|
+
* La lista a pantalla completa de los dos selectores: la cabecera con la flecha, el buscador, y
|
|
88
|
+
* las opciones como tarjetas. Lo que pasa al tocar una —elegirla y cerrar, o marcarla— y lo que
|
|
89
|
+
* va al pie es de cada selector.
|
|
90
|
+
*/
|
|
91
|
+
function HojaDeOpciones<T extends Opcion>({
|
|
92
|
+
etiqueta,
|
|
93
|
+
abierto,
|
|
94
|
+
onCerrar,
|
|
95
|
+
texto,
|
|
96
|
+
onBuscar,
|
|
97
|
+
cargando,
|
|
98
|
+
opciones,
|
|
99
|
+
marcada,
|
|
100
|
+
onTocar,
|
|
101
|
+
detalle,
|
|
102
|
+
grupo,
|
|
103
|
+
vacio = "Nada que coincida.",
|
|
104
|
+
encabezado,
|
|
105
|
+
pie,
|
|
106
|
+
}: {
|
|
107
|
+
etiqueta: string;
|
|
108
|
+
abierto: boolean;
|
|
109
|
+
onCerrar: () => void;
|
|
110
|
+
texto: string;
|
|
111
|
+
onBuscar: (texto: string) => void;
|
|
112
|
+
cargando?: boolean;
|
|
113
|
+
opciones: T[];
|
|
114
|
+
marcada: (opcion: T) => boolean;
|
|
115
|
+
onTocar: (opcion: T) => void;
|
|
116
|
+
detalle?: (opcion: T) => string | null;
|
|
117
|
+
grupo?: (opcion: T) => string;
|
|
118
|
+
vacio?: string;
|
|
119
|
+
encabezado?: React.ReactElement | null;
|
|
120
|
+
pie?: React.ReactNode;
|
|
121
|
+
}) {
|
|
122
|
+
const c = usePaleta();
|
|
123
|
+
const styles = useStyles();
|
|
124
|
+
const insets = useSafeAreaInsets();
|
|
125
|
+
|
|
126
|
+
return (
|
|
127
|
+
<Modal visible={abierto} animationType="slide" onRequestClose={onCerrar}>
|
|
128
|
+
<View style={[styles.hoja, { paddingTop: insets.top + 10 }]}>
|
|
129
|
+
<View style={styles.cabecera}>
|
|
130
|
+
<TouchableOpacity onPress={onCerrar}>
|
|
131
|
+
<MaterialIcons name="arrow-back" size={45} color={c.primary} />
|
|
132
|
+
</TouchableOpacity>
|
|
133
|
+
<Text style={styles.titulo}>{etiqueta}</Text>
|
|
134
|
+
</View>
|
|
135
|
+
<TextInput
|
|
136
|
+
value={texto}
|
|
137
|
+
onChangeText={onBuscar}
|
|
138
|
+
placeholder="Buscar"
|
|
139
|
+
placeholderTextColor={c.secondaryLight}
|
|
140
|
+
autoCorrect={false}
|
|
141
|
+
autoCapitalize="characters"
|
|
142
|
+
style={styles.buscador}
|
|
143
|
+
/>
|
|
144
|
+
{cargando ? <ActivityIndicator color={c.primary} /> : null}
|
|
145
|
+
<FlatList
|
|
146
|
+
data={opciones}
|
|
147
|
+
keyExtractor={(opcion) => String(opcion.value)}
|
|
148
|
+
keyboardShouldPersistTaps="handled"
|
|
149
|
+
contentContainerStyle={{
|
|
150
|
+
gap: 10,
|
|
151
|
+
paddingBottom: pie ? 24 : insets.bottom + 24,
|
|
152
|
+
}}
|
|
153
|
+
ListHeaderComponent={encabezado}
|
|
154
|
+
ListEmptyComponent={
|
|
155
|
+
cargando ? null : <EstadoVacio icono="magnify" titulo={vacio} />
|
|
156
|
+
}
|
|
157
|
+
renderItem={({ item, index }) => {
|
|
158
|
+
const rotulo = grupo?.(item);
|
|
159
|
+
const nuevoGrupo =
|
|
160
|
+
rotulo !== undefined &&
|
|
161
|
+
(index === 0 || grupo?.(opciones[index - 1]) !== rotulo);
|
|
162
|
+
const elegida = marcada(item);
|
|
163
|
+
return (
|
|
164
|
+
<>
|
|
165
|
+
{nuevoGrupo ? <Text style={styles.grupo}>{rotulo}</Text> : null}
|
|
166
|
+
<Pressable
|
|
167
|
+
onPress={() => onTocar(item)}
|
|
168
|
+
accessibilityState={{ selected: elegida }}
|
|
169
|
+
style={[styles.opcion, elegida && styles.opcionElegida]}
|
|
170
|
+
>
|
|
171
|
+
<Text style={styles.opcionNombre}>{item.label}</Text>
|
|
172
|
+
{detalle?.(item) ? (
|
|
173
|
+
<Text style={styles.detalle}>{detalle(item)}</Text>
|
|
174
|
+
) : null}
|
|
175
|
+
</Pressable>
|
|
176
|
+
</>
|
|
177
|
+
);
|
|
178
|
+
}}
|
|
179
|
+
/>
|
|
180
|
+
{pie ? (
|
|
181
|
+
<View style={[styles.pie, { paddingBottom: insets.bottom + 16 }]}>{pie}</View>
|
|
182
|
+
) : null}
|
|
183
|
+
</View>
|
|
184
|
+
</Modal>
|
|
185
|
+
);
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
/** El renglón cerrado de un selector: el rótulo, lo elegido y la flecha. */
|
|
189
|
+
function RenglonDelSelector({
|
|
190
|
+
etiqueta,
|
|
191
|
+
texto,
|
|
192
|
+
detalle,
|
|
193
|
+
disabled,
|
|
194
|
+
onPress,
|
|
195
|
+
}: {
|
|
196
|
+
etiqueta: string;
|
|
197
|
+
texto: string | null;
|
|
198
|
+
detalle?: string | null;
|
|
199
|
+
disabled?: boolean;
|
|
200
|
+
onPress: () => void;
|
|
201
|
+
}) {
|
|
202
|
+
const c = usePaleta();
|
|
203
|
+
const styles = useStyles();
|
|
204
|
+
return (
|
|
205
|
+
<Pressable
|
|
206
|
+
onPress={onPress}
|
|
207
|
+
disabled={disabled}
|
|
208
|
+
accessibilityRole="button"
|
|
209
|
+
style={[styles.campo, disabled && styles.campoInactivo]}
|
|
210
|
+
>
|
|
211
|
+
<Text style={styles.etiqueta}>{etiqueta}</Text>
|
|
212
|
+
<View style={styles.renglon}>
|
|
213
|
+
<Text style={[styles.valor, !texto && styles.sinValor]}>{texto ?? "Elegir…"}</Text>
|
|
214
|
+
<IconoDeRenglon
|
|
215
|
+
name="chevron-down"
|
|
216
|
+
fontSize={tipografia.subtitulo}
|
|
217
|
+
color={c.secondary}
|
|
218
|
+
/>
|
|
219
|
+
</View>
|
|
220
|
+
{detalle ? <Text style={styles.detalle}>{detalle}</Text> : null}
|
|
221
|
+
</Pressable>
|
|
222
|
+
);
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
/**
|
|
226
|
+
* Un campo que se elige de una lista: el renglón con lo elegido y, al tocarlo, la lista a pantalla
|
|
227
|
+
* completa con su buscador. **A pantalla completa y no un menú**: con la letra al 180% un menú
|
|
228
|
+
* desplegable deja ver dos opciones, y quien la usa elige de pie.
|
|
229
|
+
*/
|
|
230
|
+
export default function SelectorDeOpcion<T extends Opcion>(props: PropsDeSelectorDeOpcion<T>) {
|
|
231
|
+
const {
|
|
232
|
+
etiqueta,
|
|
233
|
+
valor,
|
|
234
|
+
onElegir,
|
|
235
|
+
cargando,
|
|
236
|
+
detalle,
|
|
237
|
+
grupo,
|
|
238
|
+
opcional,
|
|
239
|
+
textoDeNinguno = "Ninguno",
|
|
240
|
+
vacio,
|
|
241
|
+
disabled,
|
|
242
|
+
} = props;
|
|
243
|
+
const styles = useStyles();
|
|
244
|
+
const [abierto, setAbierto] = React.useState(false);
|
|
245
|
+
const { texto, buscar, visibles } = useBuscador(props);
|
|
246
|
+
|
|
247
|
+
const elegir = (opcion: T | null) => {
|
|
248
|
+
onElegir(opcion);
|
|
249
|
+
setAbierto(false);
|
|
250
|
+
buscar("");
|
|
251
|
+
};
|
|
252
|
+
|
|
253
|
+
return (
|
|
254
|
+
<>
|
|
255
|
+
<RenglonDelSelector
|
|
256
|
+
etiqueta={etiqueta}
|
|
257
|
+
texto={valor ? valor.label : null}
|
|
258
|
+
detalle={valor ? detalle?.(valor) : null}
|
|
259
|
+
disabled={disabled}
|
|
260
|
+
onPress={() => setAbierto(true)}
|
|
261
|
+
/>
|
|
262
|
+
<HojaDeOpciones
|
|
263
|
+
etiqueta={etiqueta}
|
|
264
|
+
abierto={abierto}
|
|
265
|
+
onCerrar={() => setAbierto(false)}
|
|
266
|
+
texto={texto}
|
|
267
|
+
onBuscar={buscar}
|
|
268
|
+
cargando={cargando}
|
|
269
|
+
opciones={visibles}
|
|
270
|
+
marcada={(opcion) => valor?.value === opcion.value}
|
|
271
|
+
onTocar={elegir}
|
|
272
|
+
detalle={detalle}
|
|
273
|
+
grupo={grupo}
|
|
274
|
+
vacio={vacio}
|
|
275
|
+
encabezado={
|
|
276
|
+
opcional ? (
|
|
277
|
+
<Pressable onPress={() => elegir(null)} style={styles.opcion}>
|
|
278
|
+
<Text style={styles.opcionNombre}>{textoDeNinguno}</Text>
|
|
279
|
+
</Pressable>
|
|
280
|
+
) : null
|
|
281
|
+
}
|
|
282
|
+
/>
|
|
283
|
+
</>
|
|
284
|
+
);
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
/**
|
|
288
|
+
* Lo mismo que `SelectorDeOpcion`, pero se marcan varias: tocar una la pone o la quita, y la
|
|
289
|
+
* elección se confirma con el botón del pie. Se confirma y no se guarda a cada toque para que
|
|
290
|
+
* salir con la flecha deje lo que había —igual que en el selector de una, donde salir no cambia
|
|
291
|
+
* nada—.
|
|
292
|
+
*
|
|
293
|
+
* Cerrado dice lo elegido separado por comas, que con la letra grande parte en los renglones que
|
|
294
|
+
* haga falta en vez de recortarse.
|
|
295
|
+
*/
|
|
296
|
+
export function SelectorDeVarias<T extends Opcion>(props: PropsDeSelectorDeVarias<T>) {
|
|
297
|
+
const { etiqueta, valor, onElegir, cargando, detalle, grupo, vacio, disabled } = props;
|
|
298
|
+
const [abierto, setAbierto] = React.useState(false);
|
|
299
|
+
const [marcadas, setMarcadas] = React.useState<T[]>(valor);
|
|
300
|
+
const { texto, buscar, visibles } = useBuscador(props);
|
|
301
|
+
|
|
302
|
+
const abrir = () => {
|
|
303
|
+
setMarcadas(valor);
|
|
304
|
+
setAbierto(true);
|
|
305
|
+
};
|
|
306
|
+
const cerrar = () => {
|
|
307
|
+
setAbierto(false);
|
|
308
|
+
buscar("");
|
|
309
|
+
};
|
|
310
|
+
const tocar = (opcion: T) =>
|
|
311
|
+
setMarcadas((previas) =>
|
|
312
|
+
previas.some((p) => p.value === opcion.value)
|
|
313
|
+
? previas.filter((p) => p.value !== opcion.value)
|
|
314
|
+
: [...previas, opcion],
|
|
315
|
+
);
|
|
316
|
+
|
|
317
|
+
return (
|
|
318
|
+
<>
|
|
319
|
+
<RenglonDelSelector
|
|
320
|
+
etiqueta={etiqueta}
|
|
321
|
+
texto={valor.length ? valor.map((o) => o.label).join(", ") : null}
|
|
322
|
+
disabled={disabled}
|
|
323
|
+
onPress={abrir}
|
|
324
|
+
/>
|
|
325
|
+
<HojaDeOpciones
|
|
326
|
+
etiqueta={etiqueta}
|
|
327
|
+
abierto={abierto}
|
|
328
|
+
onCerrar={cerrar}
|
|
329
|
+
texto={texto}
|
|
330
|
+
onBuscar={buscar}
|
|
331
|
+
cargando={cargando}
|
|
332
|
+
opciones={visibles}
|
|
333
|
+
marcada={(opcion) => marcadas.some((m) => m.value === opcion.value)}
|
|
334
|
+
onTocar={tocar}
|
|
335
|
+
detalle={detalle}
|
|
336
|
+
grupo={grupo}
|
|
337
|
+
vacio={vacio}
|
|
338
|
+
pie={
|
|
339
|
+
<BotonGrande
|
|
340
|
+
icono="check"
|
|
341
|
+
onPress={() => {
|
|
342
|
+
onElegir(marcadas);
|
|
343
|
+
cerrar();
|
|
344
|
+
}}
|
|
345
|
+
>
|
|
346
|
+
{marcadas.length ? `Listo (${marcadas.length})` : "Listo"}
|
|
347
|
+
</BotonGrande>
|
|
348
|
+
}
|
|
349
|
+
/>
|
|
350
|
+
</>
|
|
351
|
+
);
|
|
352
|
+
}
|
|
353
|
+
|
|
354
|
+
const useStyles = crearEstilos((c) => ({
|
|
355
|
+
campo: {
|
|
356
|
+
backgroundColor: c.superficie,
|
|
357
|
+
borderRadius: 14,
|
|
358
|
+
borderWidth: 2,
|
|
359
|
+
borderColor: c.secondaryLighter,
|
|
360
|
+
padding: 14,
|
|
361
|
+
gap: 4,
|
|
362
|
+
},
|
|
363
|
+
campoInactivo: { opacity: 0.5 },
|
|
364
|
+
etiqueta: {
|
|
365
|
+
...renglon(tipografia.etiqueta),
|
|
366
|
+
fontWeight: "800",
|
|
367
|
+
color: c.secondary,
|
|
368
|
+
textTransform: "uppercase",
|
|
369
|
+
letterSpacing: 0.8,
|
|
370
|
+
},
|
|
371
|
+
renglon: { flexDirection: "row", alignItems: "flex-start", gap: 8 },
|
|
372
|
+
valor: { flex: 1, ...renglon(tipografia.subtitulo), fontWeight: "800", color: c.texto },
|
|
373
|
+
sinValor: { color: c.secondaryLight, fontWeight: "600" },
|
|
374
|
+
detalle: { ...renglon(tipografia.secundario), color: c.secondary },
|
|
375
|
+
hoja: { flex: 1, backgroundColor: c.fondo, paddingHorizontal: 18, gap: 12 },
|
|
376
|
+
cabecera: { flexDirection: "row", alignItems: "center", gap: 8 },
|
|
377
|
+
titulo: { flex: 1, fontSize: tipografia.titulo, fontWeight: "900", color: c.primary },
|
|
378
|
+
buscador: {
|
|
379
|
+
borderWidth: 1.5,
|
|
380
|
+
borderColor: c.secondaryLight,
|
|
381
|
+
borderRadius: 12,
|
|
382
|
+
paddingHorizontal: 14,
|
|
383
|
+
paddingVertical: 12,
|
|
384
|
+
backgroundColor: c.superficie,
|
|
385
|
+
fontSize: tipografia.subtitulo,
|
|
386
|
+
color: c.texto,
|
|
387
|
+
},
|
|
388
|
+
grupo: {
|
|
389
|
+
...renglon(tipografia.etiqueta),
|
|
390
|
+
fontWeight: "800",
|
|
391
|
+
color: c.secondary,
|
|
392
|
+
textTransform: "uppercase",
|
|
393
|
+
letterSpacing: 0.8,
|
|
394
|
+
marginTop: 8,
|
|
395
|
+
},
|
|
396
|
+
opcion: {
|
|
397
|
+
backgroundColor: c.superficie,
|
|
398
|
+
borderRadius: 14,
|
|
399
|
+
borderWidth: 2,
|
|
400
|
+
borderColor: c.secondaryLighter,
|
|
401
|
+
padding: 14,
|
|
402
|
+
gap: 4,
|
|
403
|
+
},
|
|
404
|
+
opcionElegida: { borderColor: c.primary, backgroundColor: c.primaryWash },
|
|
405
|
+
opcionNombre: { ...renglon(tipografia.subtitulo), fontWeight: "800", color: c.texto },
|
|
406
|
+
pie: { paddingTop: 12 },
|
|
407
|
+
}));
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
import { useState } from "react";
|
|
2
|
+
import { Controller, type FieldValues } from "react-hook-form";
|
|
3
|
+
import { View } from "react-native";
|
|
4
|
+
import { HelperText, TextInput, type TextInputProps } from "react-native-paper";
|
|
5
|
+
import { crearEstilos } from "../tema";
|
|
6
|
+
import type { CampoAtado } from "./controlados";
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Un campo del login (el usuario, la contraseña), atado a react-hook-form: el `TextInput` de Paper
|
|
10
|
+
* con contorno y la etiqueta que sube al escribir. Con `contrasena`, oculta lo escrito y lleva el
|
|
11
|
+
* ojo para verlo.
|
|
12
|
+
*
|
|
13
|
+
* Es de Paper y no `TextoControlado`, que lleva su rótulo arriba, porque así es la pantalla de
|
|
14
|
+
* entrada de las apps.
|
|
15
|
+
*/
|
|
16
|
+
export default function TextoDeAccesoControlado<T extends FieldValues>({
|
|
17
|
+
control,
|
|
18
|
+
name,
|
|
19
|
+
etiqueta,
|
|
20
|
+
contrasena = false,
|
|
21
|
+
entrada,
|
|
22
|
+
}: CampoAtado<T> & {
|
|
23
|
+
etiqueta: string;
|
|
24
|
+
contrasena?: boolean;
|
|
25
|
+
/** Lo demás del `TextInput` de Paper (`autoCapitalize`, `keyboardType`…). */
|
|
26
|
+
entrada?: Partial<TextInputProps>;
|
|
27
|
+
}) {
|
|
28
|
+
const styles = useStyles();
|
|
29
|
+
const [verla, setVerla] = useState(false);
|
|
30
|
+
|
|
31
|
+
return (
|
|
32
|
+
<Controller
|
|
33
|
+
name={name}
|
|
34
|
+
control={control}
|
|
35
|
+
render={({ field: { onChange, value }, fieldState: { error } }) => (
|
|
36
|
+
<View>
|
|
37
|
+
<TextInput
|
|
38
|
+
label={etiqueta}
|
|
39
|
+
value={value}
|
|
40
|
+
onChangeText={onChange}
|
|
41
|
+
mode={"outlined"}
|
|
42
|
+
error={!!error}
|
|
43
|
+
secureTextEntry={contrasena && !verla}
|
|
44
|
+
right={
|
|
45
|
+
contrasena ? (
|
|
46
|
+
<TextInput.Icon
|
|
47
|
+
icon={verla ? "eye-off" : "eye"}
|
|
48
|
+
onPress={() => setVerla(!verla)}
|
|
49
|
+
/>
|
|
50
|
+
) : undefined
|
|
51
|
+
}
|
|
52
|
+
outlineStyle={styles.contorno}
|
|
53
|
+
style={styles.caja}
|
|
54
|
+
contentStyle={styles.contenido}
|
|
55
|
+
{...entrada}
|
|
56
|
+
/>
|
|
57
|
+
{error ? (
|
|
58
|
+
<HelperText type={"error"} visible>
|
|
59
|
+
{error.message}
|
|
60
|
+
</HelperText>
|
|
61
|
+
) : null}
|
|
62
|
+
</View>
|
|
63
|
+
)}
|
|
64
|
+
/>
|
|
65
|
+
);
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
const useStyles = crearEstilos((c) => ({
|
|
69
|
+
contorno: { backgroundColor: c.superficie, borderRadius: 12 },
|
|
70
|
+
caja: { height: 64 },
|
|
71
|
+
contenido: { fontSize: 18 },
|
|
72
|
+
}));
|