@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.
Files changed (322) hide show
  1. package/README.md +147 -2
  2. package/assets/fonts/Inter_400Regular.ttf +0 -0
  3. package/assets/fonts/Inter_500Medium.ttf +0 -0
  4. package/assets/fonts/Inter_600SemiBold.ttf +0 -0
  5. package/assets/fonts/Inter_700Bold.ttf +0 -0
  6. package/assets/fonts/Inter_800ExtraBold.ttf +0 -0
  7. package/assets/fonts/Inter_900Black.ttf +0 -0
  8. package/assets/fonts/OFL.txt +93 -0
  9. package/lib/module/camara/CampoDeFotos.js +124 -0
  10. package/lib/module/camara/CampoDeFotos.js.map +1 -0
  11. package/lib/module/camara/elegir.js +104 -0
  12. package/lib/module/camara/elegir.js.map +1 -0
  13. package/lib/module/camara/index.js +10 -0
  14. package/lib/module/camara/index.js.map +1 -0
  15. package/lib/module/camara/reducir.js +93 -0
  16. package/lib/module/camara/reducir.js.map +1 -0
  17. package/lib/module/componentes/AccionesDelEncabezado.js +212 -0
  18. package/lib/module/componentes/AccionesDelEncabezado.js.map +1 -0
  19. package/lib/module/componentes/BotonDeAvatar.js +55 -0
  20. package/lib/module/componentes/BotonDeAvatar.js.map +1 -0
  21. package/lib/module/componentes/BotonGrande.js +105 -0
  22. package/lib/module/componentes/BotonGrande.js.map +1 -0
  23. package/lib/module/componentes/CabeceraEmpujada.js +101 -0
  24. package/lib/module/componentes/CabeceraEmpujada.js.map +1 -0
  25. package/lib/module/componentes/Chip.js +77 -0
  26. package/lib/module/componentes/Chip.js.map +1 -0
  27. package/lib/module/componentes/ContenidoDePantalla.js +56 -0
  28. package/lib/module/componentes/ContenidoDePantalla.js.map +1 -0
  29. package/lib/module/componentes/EstadoVacio.js +65 -0
  30. package/lib/module/componentes/EstadoVacio.js.map +1 -0
  31. package/lib/module/componentes/Foto.js +106 -0
  32. package/lib/module/componentes/Foto.js.map +1 -0
  33. package/lib/module/componentes/IconoDeRenglon.js +44 -0
  34. package/lib/module/componentes/IconoDeRenglon.js.map +1 -0
  35. package/lib/module/componentes/Logo.js +44 -0
  36. package/lib/module/componentes/Logo.js.map +1 -0
  37. package/lib/module/componentes/Nota.js +51 -0
  38. package/lib/module/componentes/Nota.js.map +1 -0
  39. package/lib/module/componentes/OpcionesExcluyentes.js +69 -0
  40. package/lib/module/componentes/OpcionesExcluyentes.js.map +1 -0
  41. package/lib/module/componentes/PantallaDeError.js +84 -0
  42. package/lib/module/componentes/PantallaDeError.js.map +1 -0
  43. package/lib/module/componentes/Pestanas.js +87 -0
  44. package/lib/module/componentes/Pestanas.js.map +1 -0
  45. package/lib/module/componentes/Rotulo.js +28 -0
  46. package/lib/module/componentes/Rotulo.js.map +1 -0
  47. package/lib/module/componentes/RotuloDeSeccion.js +87 -0
  48. package/lib/module/componentes/RotuloDeSeccion.js.map +1 -0
  49. package/lib/module/componentes/Tarjeta.js +63 -0
  50. package/lib/module/componentes/Tarjeta.js.map +1 -0
  51. package/lib/module/componentes/TarjetaDeDatos.js +79 -0
  52. package/lib/module/componentes/TarjetaDeDatos.js.map +1 -0
  53. package/lib/module/componentes/TiraDeFotos.js +184 -0
  54. package/lib/module/componentes/TiraDeFotos.js.map +1 -0
  55. package/lib/module/componentes/VisorDeFoto.js +171 -0
  56. package/lib/module/componentes/VisorDeFoto.js.map +1 -0
  57. package/lib/module/componentes/logos/bola-blanca.png +0 -0
  58. package/lib/module/componentes/logos/bola.png +0 -0
  59. package/lib/module/componentes/logos/logo-blanco.png +0 -0
  60. package/lib/module/componentes/logos/logo.png +0 -0
  61. package/lib/module/dialogos/HostDeDialogos.js +170 -0
  62. package/lib/module/dialogos/HostDeDialogos.js.map +1 -0
  63. package/lib/module/dialogos/dialogos.js +112 -0
  64. package/lib/module/dialogos/dialogos.js.map +1 -0
  65. package/lib/module/formularios/CampoDeSeleccion.js +72 -0
  66. package/lib/module/formularios/CampoDeSeleccion.js.map +1 -0
  67. package/lib/module/formularios/SelectorDeOpcion.js +406 -0
  68. package/lib/module/formularios/SelectorDeOpcion.js.map +1 -0
  69. package/lib/module/formularios/TextoDeAccesoControlado.js +73 -0
  70. package/lib/module/formularios/TextoDeAccesoControlado.js.map +1 -0
  71. package/lib/module/formularios/campos.js +501 -0
  72. package/lib/module/formularios/campos.js.map +1 -0
  73. package/lib/module/formularios/controlados.js +199 -0
  74. package/lib/module/formularios/controlados.js.map +1 -0
  75. package/lib/module/formularios/errores.js +91 -0
  76. package/lib/module/formularios/errores.js.map +1 -0
  77. package/lib/module/formularios/guardado.js +78 -0
  78. package/lib/module/formularios/guardado.js.map +1 -0
  79. package/lib/module/formularios/useBusqueda.js +22 -0
  80. package/lib/module/formularios/useBusqueda.js.map +1 -0
  81. package/lib/module/fotos/elegir.js +105 -0
  82. package/lib/module/fotos/elegir.js.map +1 -0
  83. package/lib/module/fotos/index.js +9 -0
  84. package/lib/module/fotos/index.js.map +1 -0
  85. package/lib/module/hoja/HojaDeLista.js +211 -0
  86. package/lib/module/hoja/HojaDeLista.js.map +1 -0
  87. package/lib/module/hoja/HojaInferior.js +112 -0
  88. package/lib/module/hoja/HojaInferior.js.map +1 -0
  89. package/lib/module/hoja/index.js +8 -0
  90. package/lib/module/hoja/index.js.map +1 -0
  91. package/lib/module/hoja/useMasDeLaFicha.js +48 -0
  92. package/lib/module/hoja/useMasDeLaFicha.js.map +1 -0
  93. package/lib/module/index.js +67 -0
  94. package/lib/module/index.js.map +1 -0
  95. package/lib/module/listas/HojaDeFiltros.js +270 -0
  96. package/lib/module/listas/HojaDeFiltros.js.map +1 -0
  97. package/lib/module/listas/ListaPaginada.js +103 -0
  98. package/lib/module/listas/ListaPaginada.js.map +1 -0
  99. package/lib/module/listas/TarjetaDeslizable.js +118 -0
  100. package/lib/module/listas/TarjetaDeslizable.js.map +1 -0
  101. package/lib/module/listas/paginado.js +11 -0
  102. package/lib/module/listas/paginado.js.map +1 -0
  103. package/lib/module/listas/piezas.js +666 -0
  104. package/lib/module/listas/piezas.js.map +1 -0
  105. package/lib/module/navegacion/BarraDePestanas.js +162 -0
  106. package/lib/module/navegacion/BarraDePestanas.js.map +1 -0
  107. package/lib/module/navegacion/BotonDeBarra.js +58 -0
  108. package/lib/module/navegacion/BotonDeBarra.js.map +1 -0
  109. package/lib/module/navegacion/encabezado.js +103 -0
  110. package/lib/module/navegacion/encabezado.js.map +1 -0
  111. package/lib/module/pantallas/PantallaDeAcceso.js +195 -0
  112. package/lib/module/pantallas/PantallaDeAcceso.js.map +1 -0
  113. package/lib/module/pantallas/PantallaDeEdicion.js +121 -0
  114. package/lib/module/pantallas/PantallaDeEdicion.js.map +1 -0
  115. package/lib/module/pantallas/PantallaDeFicha.js +94 -0
  116. package/lib/module/pantallas/PantallaDeFicha.js.map +1 -0
  117. package/lib/module/pantallas/PantallaDeHistorial.js +328 -0
  118. package/lib/module/pantallas/PantallaDeHistorial.js.map +1 -0
  119. package/lib/module/pantallas/PantallaDePerfil.js +137 -0
  120. package/lib/module/pantallas/PantallaDePerfil.js.map +1 -0
  121. package/lib/module/tema/ProveedorDeTema.js +122 -0
  122. package/lib/module/tema/ProveedorDeTema.js.map +1 -0
  123. package/lib/module/tema/estilos.js +54 -0
  124. package/lib/module/tema/estilos.js.map +1 -0
  125. package/lib/module/tema/fuentes.js +28 -0
  126. package/lib/module/tema/fuentes.js.map +1 -0
  127. package/lib/module/tema/index.js +8 -0
  128. package/lib/module/tema/index.js.map +1 -0
  129. package/lib/module/tema/paleta.js +107 -0
  130. package/lib/module/tema/paleta.js.map +1 -0
  131. package/lib/module/tema/tipografia.js +32 -0
  132. package/lib/module/tema/tipografia.js.map +1 -0
  133. package/lib/module/texto.js +48 -0
  134. package/lib/module/texto.js.map +1 -0
  135. package/lib/typescript/src/camara/CampoDeFotos.d.ts +30 -0
  136. package/lib/typescript/src/camara/CampoDeFotos.d.ts.map +1 -0
  137. package/lib/typescript/src/camara/elegir.d.ts +47 -0
  138. package/lib/typescript/src/camara/elegir.d.ts.map +1 -0
  139. package/lib/typescript/src/camara/index.d.ts +4 -0
  140. package/lib/typescript/src/camara/index.d.ts.map +1 -0
  141. package/lib/typescript/src/camara/reducir.d.ts +32 -0
  142. package/lib/typescript/src/camara/reducir.d.ts.map +1 -0
  143. package/lib/typescript/src/componentes/AccionesDelEncabezado.d.ts +34 -0
  144. package/lib/typescript/src/componentes/AccionesDelEncabezado.d.ts.map +1 -0
  145. package/lib/typescript/src/componentes/BotonDeAvatar.d.ts +17 -0
  146. package/lib/typescript/src/componentes/BotonDeAvatar.d.ts.map +1 -0
  147. package/lib/typescript/src/componentes/BotonGrande.d.ts +26 -0
  148. package/lib/typescript/src/componentes/BotonGrande.d.ts.map +1 -0
  149. package/lib/typescript/src/componentes/CabeceraEmpujada.d.ts +18 -0
  150. package/lib/typescript/src/componentes/CabeceraEmpujada.d.ts.map +1 -0
  151. package/lib/typescript/src/componentes/Chip.d.ts +14 -0
  152. package/lib/typescript/src/componentes/Chip.d.ts.map +1 -0
  153. package/lib/typescript/src/componentes/ContenidoDePantalla.d.ts +17 -0
  154. package/lib/typescript/src/componentes/ContenidoDePantalla.d.ts.map +1 -0
  155. package/lib/typescript/src/componentes/EstadoVacio.d.ts +17 -0
  156. package/lib/typescript/src/componentes/EstadoVacio.d.ts.map +1 -0
  157. package/lib/typescript/src/componentes/Foto.d.ts +41 -0
  158. package/lib/typescript/src/componentes/Foto.d.ts.map +1 -0
  159. package/lib/typescript/src/componentes/IconoDeRenglon.d.ts +25 -0
  160. package/lib/typescript/src/componentes/IconoDeRenglon.d.ts.map +1 -0
  161. package/lib/typescript/src/componentes/Logo.d.ts +13 -0
  162. package/lib/typescript/src/componentes/Logo.d.ts.map +1 -0
  163. package/lib/typescript/src/componentes/Nota.d.ts +7 -0
  164. package/lib/typescript/src/componentes/Nota.d.ts.map +1 -0
  165. package/lib/typescript/src/componentes/OpcionesExcluyentes.d.ts +14 -0
  166. package/lib/typescript/src/componentes/OpcionesExcluyentes.d.ts.map +1 -0
  167. package/lib/typescript/src/componentes/PantallaDeError.d.ts +23 -0
  168. package/lib/typescript/src/componentes/PantallaDeError.d.ts.map +1 -0
  169. package/lib/typescript/src/componentes/Pestanas.d.ts +28 -0
  170. package/lib/typescript/src/componentes/Pestanas.d.ts.map +1 -0
  171. package/lib/typescript/src/componentes/Rotulo.d.ts +6 -0
  172. package/lib/typescript/src/componentes/Rotulo.d.ts.map +1 -0
  173. package/lib/typescript/src/componentes/RotuloDeSeccion.d.ts +20 -0
  174. package/lib/typescript/src/componentes/RotuloDeSeccion.d.ts.map +1 -0
  175. package/lib/typescript/src/componentes/Tarjeta.d.ts +25 -0
  176. package/lib/typescript/src/componentes/Tarjeta.d.ts.map +1 -0
  177. package/lib/typescript/src/componentes/TarjetaDeDatos.d.ts +13 -0
  178. package/lib/typescript/src/componentes/TarjetaDeDatos.d.ts.map +1 -0
  179. package/lib/typescript/src/componentes/TiraDeFotos.d.ts +36 -0
  180. package/lib/typescript/src/componentes/TiraDeFotos.d.ts.map +1 -0
  181. package/lib/typescript/src/componentes/VisorDeFoto.d.ts +23 -0
  182. package/lib/typescript/src/componentes/VisorDeFoto.d.ts.map +1 -0
  183. package/lib/typescript/src/dialogos/HostDeDialogos.d.ts +14 -0
  184. package/lib/typescript/src/dialogos/HostDeDialogos.d.ts.map +1 -0
  185. package/lib/typescript/src/dialogos/dialogos.d.ts +65 -0
  186. package/lib/typescript/src/dialogos/dialogos.d.ts.map +1 -0
  187. package/lib/typescript/src/formularios/CampoDeSeleccion.d.ts +24 -0
  188. package/lib/typescript/src/formularios/CampoDeSeleccion.d.ts.map +1 -0
  189. package/lib/typescript/src/formularios/SelectorDeOpcion.d.ts +59 -0
  190. package/lib/typescript/src/formularios/SelectorDeOpcion.d.ts.map +1 -0
  191. package/lib/typescript/src/formularios/TextoDeAccesoControlado.d.ts +18 -0
  192. package/lib/typescript/src/formularios/TextoDeAccesoControlado.d.ts.map +1 -0
  193. package/lib/typescript/src/formularios/campos.d.ts +113 -0
  194. package/lib/typescript/src/formularios/campos.d.ts.map +1 -0
  195. package/lib/typescript/src/formularios/controlados.d.ts +29 -0
  196. package/lib/typescript/src/formularios/controlados.d.ts.map +1 -0
  197. package/lib/typescript/src/formularios/errores.d.ts +36 -0
  198. package/lib/typescript/src/formularios/errores.d.ts.map +1 -0
  199. package/lib/typescript/src/formularios/guardado.d.ts +36 -0
  200. package/lib/typescript/src/formularios/guardado.d.ts.map +1 -0
  201. package/lib/typescript/src/formularios/useBusqueda.d.ts +7 -0
  202. package/lib/typescript/src/formularios/useBusqueda.d.ts.map +1 -0
  203. package/lib/typescript/src/fotos/elegir.d.ts +28 -0
  204. package/lib/typescript/src/fotos/elegir.d.ts.map +1 -0
  205. package/lib/typescript/src/fotos/index.d.ts +3 -0
  206. package/lib/typescript/src/fotos/index.d.ts.map +1 -0
  207. package/lib/typescript/src/hoja/HojaDeLista.d.ts +50 -0
  208. package/lib/typescript/src/hoja/HojaDeLista.d.ts.map +1 -0
  209. package/lib/typescript/src/hoja/HojaInferior.d.ts +21 -0
  210. package/lib/typescript/src/hoja/HojaInferior.d.ts.map +1 -0
  211. package/lib/typescript/src/hoja/index.d.ts +4 -0
  212. package/lib/typescript/src/hoja/index.d.ts.map +1 -0
  213. package/lib/typescript/src/hoja/useMasDeLaFicha.d.ts +27 -0
  214. package/lib/typescript/src/hoja/useMasDeLaFicha.d.ts.map +1 -0
  215. package/lib/typescript/src/index.d.ts +46 -0
  216. package/lib/typescript/src/index.d.ts.map +1 -0
  217. package/lib/typescript/src/listas/HojaDeFiltros.d.ts +76 -0
  218. package/lib/typescript/src/listas/HojaDeFiltros.d.ts.map +1 -0
  219. package/lib/typescript/src/listas/ListaPaginada.d.ts +39 -0
  220. package/lib/typescript/src/listas/ListaPaginada.d.ts.map +1 -0
  221. package/lib/typescript/src/listas/TarjetaDeslizable.d.ts +33 -0
  222. package/lib/typescript/src/listas/TarjetaDeslizable.d.ts.map +1 -0
  223. package/lib/typescript/src/listas/paginado.d.ts +16 -0
  224. package/lib/typescript/src/listas/paginado.d.ts.map +1 -0
  225. package/lib/typescript/src/listas/piezas.d.ts +157 -0
  226. package/lib/typescript/src/listas/piezas.d.ts.map +1 -0
  227. package/lib/typescript/src/navegacion/BarraDePestanas.d.ts +57 -0
  228. package/lib/typescript/src/navegacion/BarraDePestanas.d.ts.map +1 -0
  229. package/lib/typescript/src/navegacion/BotonDeBarra.d.ts +15 -0
  230. package/lib/typescript/src/navegacion/BotonDeBarra.d.ts.map +1 -0
  231. package/lib/typescript/src/navegacion/encabezado.d.ts +44 -0
  232. package/lib/typescript/src/navegacion/encabezado.d.ts.map +1 -0
  233. package/lib/typescript/src/pantallas/PantallaDeAcceso.d.ts +32 -0
  234. package/lib/typescript/src/pantallas/PantallaDeAcceso.d.ts.map +1 -0
  235. package/lib/typescript/src/pantallas/PantallaDeEdicion.d.ts +38 -0
  236. package/lib/typescript/src/pantallas/PantallaDeEdicion.d.ts.map +1 -0
  237. package/lib/typescript/src/pantallas/PantallaDeFicha.d.ts +35 -0
  238. package/lib/typescript/src/pantallas/PantallaDeFicha.d.ts.map +1 -0
  239. package/lib/typescript/src/pantallas/PantallaDeHistorial.d.ts +65 -0
  240. package/lib/typescript/src/pantallas/PantallaDeHistorial.d.ts.map +1 -0
  241. package/lib/typescript/src/pantallas/PantallaDePerfil.d.ts +19 -0
  242. package/lib/typescript/src/pantallas/PantallaDePerfil.d.ts.map +1 -0
  243. package/lib/typescript/src/tema/ProveedorDeTema.d.ts +32 -0
  244. package/lib/typescript/src/tema/ProveedorDeTema.d.ts.map +1 -0
  245. package/lib/typescript/src/tema/estilos.d.ts +31 -0
  246. package/lib/typescript/src/tema/estilos.d.ts.map +1 -0
  247. package/lib/typescript/src/tema/fuentes.d.ts +20 -0
  248. package/lib/typescript/src/tema/fuentes.d.ts.map +1 -0
  249. package/lib/typescript/src/tema/index.d.ts +6 -0
  250. package/lib/typescript/src/tema/index.d.ts.map +1 -0
  251. package/lib/typescript/src/tema/paleta.d.ts +86 -0
  252. package/lib/typescript/src/tema/paleta.d.ts.map +1 -0
  253. package/lib/typescript/src/tema/tipografia.d.ts +29 -0
  254. package/lib/typescript/src/tema/tipografia.d.ts.map +1 -0
  255. package/lib/typescript/src/texto.d.ts +16 -0
  256. package/lib/typescript/src/texto.d.ts.map +1 -0
  257. package/package.json +140 -5
  258. package/src/camara/CampoDeFotos.tsx +132 -0
  259. package/src/camara/elegir.ts +113 -0
  260. package/src/camara/index.ts +15 -0
  261. package/src/camara/reducir.ts +87 -0
  262. package/src/componentes/AccionesDelEncabezado.tsx +247 -0
  263. package/src/componentes/BotonDeAvatar.tsx +59 -0
  264. package/src/componentes/BotonGrande.tsx +100 -0
  265. package/src/componentes/CabeceraEmpujada.tsx +69 -0
  266. package/src/componentes/Chip.tsx +60 -0
  267. package/src/componentes/ContenidoDePantalla.tsx +53 -0
  268. package/src/componentes/EstadoVacio.tsx +45 -0
  269. package/src/componentes/Foto.tsx +92 -0
  270. package/src/componentes/IconoDeRenglon.tsx +42 -0
  271. package/src/componentes/Logo.tsx +34 -0
  272. package/src/componentes/Nota.tsx +37 -0
  273. package/src/componentes/OpcionesExcluyentes.tsx +62 -0
  274. package/src/componentes/PantallaDeError.tsx +76 -0
  275. package/src/componentes/Pestanas.tsx +86 -0
  276. package/src/componentes/Rotulo.tsx +21 -0
  277. package/src/componentes/RotuloDeSeccion.tsx +71 -0
  278. package/src/componentes/Tarjeta.tsx +58 -0
  279. package/src/componentes/TarjetaDeDatos.tsx +62 -0
  280. package/src/componentes/TiraDeFotos.tsx +189 -0
  281. package/src/componentes/VisorDeFoto.tsx +181 -0
  282. package/src/componentes/logos/bola-blanca.png +0 -0
  283. package/src/componentes/logos/bola.png +0 -0
  284. package/src/componentes/logos/logo-blanco.png +0 -0
  285. package/src/componentes/logos/logo.png +0 -0
  286. package/src/dialogos/HostDeDialogos.tsx +172 -0
  287. package/src/dialogos/dialogos.ts +148 -0
  288. package/src/formularios/CampoDeSeleccion.tsx +60 -0
  289. package/src/formularios/SelectorDeOpcion.tsx +407 -0
  290. package/src/formularios/TextoDeAccesoControlado.tsx +72 -0
  291. package/src/formularios/campos.tsx +497 -0
  292. package/src/formularios/controlados.tsx +200 -0
  293. package/src/formularios/errores.ts +105 -0
  294. package/src/formularios/guardado.ts +76 -0
  295. package/src/formularios/useBusqueda.ts +19 -0
  296. package/src/fotos/elegir.ts +116 -0
  297. package/src/fotos/index.ts +6 -0
  298. package/src/hoja/HojaDeLista.tsx +214 -0
  299. package/src/hoja/HojaInferior.tsx +103 -0
  300. package/src/hoja/index.ts +5 -0
  301. package/src/hoja/useMasDeLaFicha.tsx +63 -0
  302. package/src/index.ts +151 -0
  303. package/src/listas/HojaDeFiltros.tsx +286 -0
  304. package/src/listas/ListaPaginada.tsx +117 -0
  305. package/src/listas/TarjetaDeslizable.tsx +126 -0
  306. package/src/listas/paginado.ts +15 -0
  307. package/src/listas/piezas.tsx +670 -0
  308. package/src/navegacion/BarraDePestanas.tsx +198 -0
  309. package/src/navegacion/BotonDeBarra.tsx +61 -0
  310. package/src/navegacion/encabezado.tsx +88 -0
  311. package/src/pantallas/PantallaDeAcceso.tsx +184 -0
  312. package/src/pantallas/PantallaDeEdicion.tsx +116 -0
  313. package/src/pantallas/PantallaDeFicha.tsx +115 -0
  314. package/src/pantallas/PantallaDeHistorial.tsx +293 -0
  315. package/src/pantallas/PantallaDePerfil.tsx +126 -0
  316. package/src/tema/ProveedorDeTema.tsx +150 -0
  317. package/src/tema/estilos.ts +51 -0
  318. package/src/tema/fuentes.ts +20 -0
  319. package/src/tema/index.ts +10 -0
  320. package/src/tema/paleta.ts +147 -0
  321. package/src/tema/tipografia.ts +29 -0
  322. package/src/texto.tsx +44 -0
@@ -0,0 +1,69 @@
1
+ import AccionesDelEncabezado, {
2
+ type AccionDelEncabezado,
3
+ } from "../componentes/AccionesDelEncabezado";
4
+ import { Text } from "../texto";
5
+ import { crearEstilos, tipografia, usePaleta } from "../tema";
6
+ import MaterialIcons from "@expo/vector-icons/MaterialIcons";
7
+ import { router } from "expo-router";
8
+ import type React from "react";
9
+ import { TouchableOpacity, View } from "react-native";
10
+ import { useSafeAreaInsets } from "react-native-safe-area-context";
11
+
12
+ /**
13
+ * La cabecera de una pantalla empujada encima de las pestañas: atrás, el rótulo, el título y, si
14
+ * hay, sus acciones a la derecha (`AccionesDelEncabezado`). La usan las fichas, las
15
+ * pantallas de edición y el perfil.
16
+ *
17
+ * `estado` va debajo del título —el estado de la ficha, normalmente un `Chip`— y no a su lado: con
18
+ * acciones a la derecha, un folio largo y su chip en el mismo renglón se peleaban el ancho.
19
+ */
20
+ const CabeceraEmpujada = ({
21
+ eyebrow,
22
+ titulo,
23
+ acciones,
24
+ estado,
25
+ }: {
26
+ eyebrow: string;
27
+ titulo: string;
28
+ acciones?: AccionDelEncabezado[];
29
+ estado?: React.ReactNode;
30
+ }) => {
31
+ const c = usePaleta();
32
+ const styles = useStyles();
33
+ const insets = useSafeAreaInsets();
34
+ return (
35
+ <View style={[styles.hdr, { paddingTop: insets.top + 10 }]}>
36
+ <View style={styles.hdrRow}>
37
+ <TouchableOpacity onPress={() => router.back()} accessibilityLabel="Atrás">
38
+ <MaterialIcons name="arrow-back" size={45} color={c.primary} />
39
+ </TouchableOpacity>
40
+ {/* `flexShrink` y no `flex`: con acciones, el título les deja su hueco. */}
41
+ <View style={acciones?.length ? styles.tituloConAcciones : styles.titulo}>
42
+ <Text style={styles.hdrEyebrow}>{eyebrow}</Text>
43
+ <Text style={styles.hdrTitle}>{titulo}</Text>
44
+ {estado ? <View style={styles.estado}>{estado}</View> : null}
45
+ </View>
46
+ {acciones?.length ? <AccionesDelEncabezado acciones={acciones} /> : null}
47
+ </View>
48
+ </View>
49
+ );
50
+ };
51
+
52
+ const useStyles = crearEstilos((c) => ({
53
+ hdr: { paddingHorizontal: 18, paddingBottom: 13, backgroundColor: c.superficie },
54
+ hdrRow: { flexDirection: "row", alignItems: "flex-end", gap: 8 },
55
+ titulo: { flex: 1, minWidth: 0 },
56
+ tituloConAcciones: { flexShrink: 1, minWidth: 0 },
57
+ hdrEyebrow: {
58
+ fontSize: tipografia.etiqueta,
59
+ color: c.secondaryLight,
60
+ textTransform: "uppercase",
61
+ letterSpacing: 1.6,
62
+ marginBottom: 4,
63
+ fontWeight: "600",
64
+ },
65
+ hdrTitle: { fontSize: tipografia.titulo, fontWeight: "900", color: c.primary },
66
+ estado: { marginTop: 6, flexDirection: "row", flexWrap: "wrap", gap: 6 },
67
+ }));
68
+
69
+ export default CabeceraEmpujada;
@@ -0,0 +1,60 @@
1
+ import { Text } from "../texto";
2
+ import { crearEstilos, type Paleta, renglon, tipografia, usePaleta } from "../tema";
3
+ import { View, type StyleProp, type ViewStyle } from "react-native";
4
+
5
+ /**
6
+ * El tono de un chip, por lo que quiere decir y no por su color: así un estado se ve igual en
7
+ * claro y en oscuro, y dos pantallas no le ponen dos colores a lo mismo.
8
+ */
9
+ export type TonoDeChip = "exito" | "aviso" | "info" | "error" | "morado" | "neutro" | "primario";
10
+
11
+ const colores = (c: Paleta, tono: TonoDeChip) => {
12
+ switch (tono) {
13
+ case "exito":
14
+ return c.exito;
15
+ case "aviso":
16
+ return { fondo: c.aviso.fondo, tinta: c.aviso.tinta };
17
+ case "info":
18
+ return c.info;
19
+ case "error":
20
+ return c.error;
21
+ case "morado":
22
+ return c.morado;
23
+ case "primario":
24
+ return { fondo: c.primary, tinta: c.sobrePrimario };
25
+ default:
26
+ return { fondo: c.superficieTenue, tinta: c.secondary };
27
+ }
28
+ };
29
+
30
+ /** Un estado en una pastilla: «En progreso», «Por vencer», «En viaje». */
31
+ const Chip = ({
32
+ texto,
33
+ tono = "neutro",
34
+ style,
35
+ }: {
36
+ texto: string;
37
+ tono?: TonoDeChip;
38
+ style?: StyleProp<ViewStyle>;
39
+ }) => {
40
+ const c = usePaleta();
41
+ const styles = useStyles();
42
+ const { fondo, tinta } = colores(c, tono);
43
+ return (
44
+ <View style={[styles.chip, { backgroundColor: fondo }, style]}>
45
+ <Text style={[styles.texto, { color: tinta }]}>{texto}</Text>
46
+ </View>
47
+ );
48
+ };
49
+
50
+ const useStyles = crearEstilos(() => ({
51
+ chip: {
52
+ alignSelf: "flex-start",
53
+ paddingHorizontal: 9,
54
+ paddingVertical: 3,
55
+ borderRadius: 999,
56
+ },
57
+ texto: { ...renglon(tipografia.etiqueta), fontWeight: "700" },
58
+ }));
59
+
60
+ export default Chip;
@@ -0,0 +1,53 @@
1
+ import React from "react";
2
+ import { useWindowDimensions, View } from "react-native";
3
+ import { KeyboardAvoidingView } from "react-native-keyboard-controller";
4
+ import { useSafeAreaInsets } from "react-native-safe-area-context";
5
+ import { crearEstilos } from "../tema";
6
+
7
+ interface Props {
8
+ children: React.ReactNode;
9
+ paddingBottom?: number;
10
+ /**
11
+ * Cuando el hijo ya maneja el teclado por su cuenta (p. ej. un `KeyboardAwareScrollView`),
12
+ * `false` quita el `KeyboardAvoidingView` para no encimar dos manejadores del teclado.
13
+ */
14
+ avoidKeyboard?: boolean;
15
+ }
16
+
17
+ const BASE_PADDING = 20;
18
+
19
+ /**
20
+ * El cuerpo de una pantalla debajo de su header: el fondo, el margen a los lados (el doble en una
21
+ * tableta, más lo que pida la muesca) y, por omisión, que esquive el teclado.
22
+ */
23
+ const ContenidoDePantalla = ({ paddingBottom, avoidKeyboard = true, children }: Props) => {
24
+ const styles = useStyles();
25
+ const insets = useSafeAreaInsets();
26
+ const { width } = useWindowDimensions();
27
+ const horizontalPadding = width > 700 ? BASE_PADDING * 2 : BASE_PADDING;
28
+ const containerStyle = [
29
+ styles.container,
30
+ {
31
+ paddingTop: 20,
32
+ paddingLeft: horizontalPadding + insets.left,
33
+ paddingRight: horizontalPadding + insets.right,
34
+ paddingBottom: paddingBottom ?? 0,
35
+ },
36
+ ];
37
+ if (!avoidKeyboard) {
38
+ return <View style={containerStyle}>{children}</View>;
39
+ }
40
+ return (
41
+ // `behavior="padding"` funciona en ambas plataformas con edge-to-edge
42
+ // gracias a react-native-keyboard-controller (RN lo ignora en Android).
43
+ <KeyboardAvoidingView behavior="padding" style={containerStyle}>
44
+ {children}
45
+ </KeyboardAvoidingView>
46
+ );
47
+ };
48
+
49
+ const useStyles = crearEstilos((c) => ({
50
+ container: { flex: 1, backgroundColor: c.fondo },
51
+ }));
52
+
53
+ export default ContenidoDePantalla;
@@ -0,0 +1,45 @@
1
+ import { crearEstilos, renglon, tipografia, usePaleta } from "../tema";
2
+ import { Text } from "../texto";
3
+ import MaterialCommunityIcons from "@expo/vector-icons/MaterialCommunityIcons";
4
+ import React from "react";
5
+ import { View } from "react-native";
6
+
7
+ interface Props {
8
+ icono: React.ComponentProps<typeof MaterialCommunityIcons>["name"];
9
+ titulo: string;
10
+ texto?: string;
11
+ /** Una acción («Reintentar») que va debajo del texto, centrada con él. */
12
+ children?: React.ReactNode;
13
+ }
14
+
15
+ /**
16
+ * Lo que se pinta cuando una lista no tiene nada que enseñar. Ocupa todo el alto y centra lo suyo,
17
+ * así que una acción va como hijo: puesta después, como hermano, quedaba hasta abajo de la
18
+ * pantalla, debajo de la barra de pestañas.
19
+ */
20
+ const EstadoVacio = ({ icono, titulo, texto, children }: Props) => {
21
+ const c = usePaleta();
22
+ const styles = useStyles();
23
+ return (
24
+ <View style={styles.root}>
25
+ <MaterialCommunityIcons name={icono} size={56} color={c.secondaryLight} />
26
+ <Text style={styles.titulo}>{titulo}</Text>
27
+ {texto ? <Text style={styles.texto}>{texto}</Text> : null}
28
+ {children ? <View style={styles.accion}>{children}</View> : null}
29
+ </View>
30
+ );
31
+ };
32
+
33
+ const useStyles = crearEstilos((c) => ({
34
+ root: { flex: 1, alignItems: "center", justifyContent: "center", gap: 10, padding: 24 },
35
+ titulo: {
36
+ ...renglon(tipografia.subtitulo),
37
+ fontWeight: "700",
38
+ color: c.secondaryDark,
39
+ textAlign: "center",
40
+ },
41
+ texto: { fontSize: tipografia.cuerpo, color: c.secondary, textAlign: "center" },
42
+ accion: { alignSelf: "stretch", marginTop: 14 },
43
+ }));
44
+
45
+ export default EstadoVacio;
@@ -0,0 +1,92 @@
1
+ import MaterialCommunityIcons from "@expo/vector-icons/MaterialCommunityIcons";
2
+ import React from "react";
3
+ import { ActivityIndicator, Image, type ImageProps, StyleSheet, View } from "react-native";
4
+ import { usePaleta } from "../tema";
5
+
6
+ /**
7
+ * Una foto: del servidor (`ruta`, la que da el API) o del teléfono (`uri` local, que todavía no
8
+ * sube).
9
+ */
10
+ export type FotoAVer = { ruta: string } | { uri: string };
11
+
12
+ /** Cómo baja la app una foto del servidor: la `uri` local donde quedó. Rechaza si no se pudo. */
13
+ export type BajarFoto = (ruta: string) => Promise<string>;
14
+
15
+ const Bajar = React.createContext<BajarFoto | null>(null);
16
+
17
+ /**
18
+ * Cómo se bajan las fotos del servidor en esta app. Va en el layout raíz, una vez.
19
+ *
20
+ * Es de la app y no de la librería porque las fotos piden la sesión: en Android, con la nueva
21
+ * arquitectura, el `Image` de React Native **no manda las `headers` del `source`**, así que una
22
+ * foto con `Authorization` se baja primero con `fetch` a un archivo (con el refresh del token de
23
+ * cada app) y se pinta el archivo. Sin proveedor, la `ruta` se pinta tal cual, como URL pública.
24
+ */
25
+ export function ProveedorDeFotos({
26
+ bajar,
27
+ children,
28
+ }: {
29
+ bajar: BajarFoto;
30
+ children: React.ReactNode;
31
+ }) {
32
+ return <Bajar.Provider value={bajar}>{children}</Bajar.Provider>;
33
+ }
34
+
35
+ interface Props extends Omit<ImageProps, "source"> {
36
+ foto: FotoAVer;
37
+ }
38
+
39
+ /**
40
+ * Pinta una foto, venga de donde venga: la misma en una tira, el visor o una lista.
41
+ *
42
+ * Las del servidor se bajan primero con el `ProveedorDeFotos` de la app y se pinta el archivo
43
+ * local. Mientras baja, un indicador; si no se pudo, un icono de foto rota en vez de un cuadro gris
44
+ * sin explicación.
45
+ *
46
+ * Una miniatura lleva `resizeMethod="resize"`: en Android, sin él, cada cuadrito de 72 px
47
+ * decodifica la foto entera de 1920 px en memoria, y con varias la lista se arrastra.
48
+ */
49
+ const Foto = ({ foto, style, onError, ...props }: Props) => {
50
+ const c = usePaleta();
51
+ const bajar = React.useContext(Bajar);
52
+ const ruta = "ruta" in foto ? foto.ruta : null;
53
+ // Lo bajado, con la ruta a la que corresponde: si la foto cambia, lo de la anterior no sirve.
54
+ const [bajada, setBajada] = React.useState<{ ruta: string; uri?: string; fallo?: boolean }>();
55
+
56
+ React.useEffect(() => {
57
+ if (!ruta || !bajar) return;
58
+ let vigente = true;
59
+ bajar(ruta)
60
+ .then((uri) => vigente && setBajada({ ruta, uri }))
61
+ .catch(() => vigente && setBajada({ ruta, fallo: true }));
62
+ return () => {
63
+ vigente = false;
64
+ };
65
+ }, [ruta, bajar]);
66
+
67
+ const deEstaRuta = bajada && bajada.ruta === ruta ? bajada : undefined;
68
+ const local = "uri" in foto ? foto.uri : bajar ? (deEstaRuta?.uri ?? null) : ruta;
69
+ const fallo = Boolean(deEstaRuta?.fallo);
70
+
71
+ if (fallo) {
72
+ return (
73
+ <View style={[style, styles.centro]}>
74
+ <MaterialCommunityIcons name="image-broken-variant" size={28} color={c.secondary} />
75
+ </View>
76
+ );
77
+ }
78
+ if (!local) {
79
+ return (
80
+ <View style={[style, styles.centro]}>
81
+ <ActivityIndicator color={c.secondary} />
82
+ </View>
83
+ );
84
+ }
85
+ return <Image {...props} style={style} source={{ uri: local }} onError={onError} />;
86
+ };
87
+
88
+ const styles = StyleSheet.create({
89
+ centro: { alignItems: "center", justifyContent: "center" },
90
+ });
91
+
92
+ export default Foto;
@@ -0,0 +1,42 @@
1
+ import { renglon } from "../tema";
2
+ import MaterialCommunityIcons from "@expo/vector-icons/MaterialCommunityIcons";
3
+ import React from "react";
4
+ import { useWindowDimensions, View, type StyleProp, type ViewStyle } from "react-native";
5
+
6
+ interface Props {
7
+ name: React.ComponentProps<typeof MaterialCommunityIcons>["name"];
8
+ color: string;
9
+ /** El `fontSize` del texto que acompaña. Su estilo tiene que llevar `...renglon(fontSize)`. */
10
+ fontSize: number;
11
+ /** El tamaño del icono a escala 1. Por omisión, el de la letra. */
12
+ size?: number;
13
+ style?: StyleProp<ViewStyle>;
14
+ }
15
+
16
+ /**
17
+ * Un icono que acompaña a un renglón de texto y **se queda centrado en él aunque el operador
18
+ * agrande la letra del teléfono**.
19
+ *
20
+ * El texto crece con la escala del sistema (Ajustes › Tamaño de fuente; hay quien la trae al
21
+ * 180%) y un icono de tamaño fijo no: se quedaba chico y desfasado hacia arriba. Aquí el icono
22
+ * crece con la misma escala y va en una caja del alto del primer renglón, así que queda centrado
23
+ * contra la primera línea aunque el texto parta en varias. Para eso el texto necesita un alto de
24
+ * renglón explícito: el de `renglon()`, que es el mismo que usa esta caja.
25
+ */
26
+ const IconoDeRenglon = ({ name, color, fontSize, size = fontSize, style }: Props) => {
27
+ const { fontScale } = useWindowDimensions();
28
+ const alto = renglon(fontSize).lineHeight * fontScale;
29
+
30
+ return (
31
+ <View
32
+ style={[
33
+ { height: alto, minWidth: alto, alignItems: "center", justifyContent: "center" },
34
+ style,
35
+ ]}
36
+ >
37
+ <MaterialCommunityIcons name={name} size={size * fontScale} color={color} />
38
+ </View>
39
+ );
40
+ };
41
+
42
+ export default IconoDeRenglon;
@@ -0,0 +1,34 @@
1
+ import { Image } from "react-native";
2
+ import { useEsOscuro } from "../tema";
3
+
4
+ /**
5
+ * La bola del header, de los archivos de marca (`O:\Logo ACV`): **a color** en claro
6
+ * (`bola.png`, el «standalone») y **toda en blanco** en oscuro (`bola-blanca.png`, sacada del
7
+ * mismo archivo: lo rojo opaco y los trazos huecos), como el `Logo` del portal en oscuro.
8
+ */
9
+ export const Logo = () => {
10
+ const oscuro = useEsOscuro();
11
+ return (
12
+ <Image
13
+ source={oscuro ? require("./logos/bola-blanca.png") : require("./logos/bola.png")}
14
+ // 42 × 36 conserva el aspecto de los PNG (242 × 210).
15
+ style={{ width: 42, height: 36, resizeMode: "contain" }}
16
+ />
17
+ );
18
+ };
19
+
20
+ /**
21
+ * El logo con las letras: el del login. En oscuro va **todo en blanco**, como el `Logo` del
22
+ * portal: `logo-blanco.png` sale del archivo de marca en blanco sobre negro
23
+ * (`O:\Logo ACV\acv-logo_2012-whitereversed`), con el negro vuelto transparente.
24
+ */
25
+ export const LogoCompleto = () => {
26
+ const oscuro = useEsOscuro();
27
+ return (
28
+ <Image
29
+ source={oscuro ? require("./logos/logo-blanco.png") : require("./logos/logo.png")}
30
+ // 175 × 66 conserva el aspecto del PNG claro (318 × 120); el blanco cabe en la misma caja.
31
+ style={{ resizeMode: "contain", width: 175, height: 66 }}
32
+ />
33
+ );
34
+ };
@@ -0,0 +1,37 @@
1
+ import IconoDeRenglon from "../componentes/IconoDeRenglon";
2
+ import { Text } from "../texto";
3
+ import { crearEstilos, renglon, tipografia, usePaleta } from "../tema";
4
+ import { View } from "react-native";
5
+
6
+ /** Un aviso dentro de la pantalla: azul para informar, ámbar para advertir. */
7
+ const Nota = ({ texto, tono = "info" }: { texto: string; tono?: "info" | "aviso" }) => {
8
+ const c = usePaleta();
9
+ const styles = useStyles();
10
+ const aviso = tono === "aviso";
11
+ return (
12
+ <View style={[styles.nota, aviso && styles.notaAviso]}>
13
+ <IconoDeRenglon
14
+ name={aviso ? "alert-outline" : "information-outline"}
15
+ fontSize={tipografia.cuerpo}
16
+ color={aviso ? c.aviso.tinta : c.info.tinta}
17
+ />
18
+ <Text style={[styles.notaTexto, aviso && styles.notaTextoAviso]}>{texto}</Text>
19
+ </View>
20
+ );
21
+ };
22
+
23
+ const useStyles = crearEstilos((c) => ({
24
+ nota: {
25
+ flexDirection: "row",
26
+ alignItems: "flex-start",
27
+ gap: 8,
28
+ backgroundColor: c.info.fondo,
29
+ borderRadius: 12,
30
+ padding: 14,
31
+ },
32
+ notaAviso: { backgroundColor: c.aviso.fondo },
33
+ notaTexto: { flex: 1, ...renglon(tipografia.cuerpo), color: c.info.tinta },
34
+ notaTextoAviso: { color: c.aviso.tinta },
35
+ }));
36
+
37
+ export default Nota;
@@ -0,0 +1,62 @@
1
+ import IconoDeRenglon from "../componentes/IconoDeRenglon";
2
+ import { Text } from "../texto";
3
+ import { crearEstilos, renglon, tipografia, usePaleta } from "../tema";
4
+ import { Pressable, View } from "react-native";
5
+
6
+ /**
7
+ * Opciones excluyentes como tarjetas, una por renglón: la carga y la razón del cambio de sello en
8
+ * la caseta, la razón de un rechazo en la oficina.
9
+ */
10
+ const OpcionesExcluyentes = <V extends string | boolean>({
11
+ opciones,
12
+ valor,
13
+ onElegir,
14
+ }: {
15
+ opciones: readonly { valor: V; texto: string }[];
16
+ valor: V | null;
17
+ onElegir: (valor: V) => void;
18
+ }) => {
19
+ const c = usePaleta();
20
+ const styles = useStyles();
21
+ return (
22
+ <View style={styles.opciones}>
23
+ {opciones.map((opcion) => {
24
+ const elegida = opcion.valor === valor;
25
+ return (
26
+ <Pressable
27
+ key={String(opcion.valor)}
28
+ onPress={() => onElegir(opcion.valor)}
29
+ accessibilityRole="radio"
30
+ accessibilityState={{ selected: elegida }}
31
+ style={[styles.opcion, elegida && styles.opcionElegida]}
32
+ >
33
+ <IconoDeRenglon
34
+ name={elegida ? "radiobox-marked" : "radiobox-blank"}
35
+ fontSize={tipografia.subtitulo}
36
+ color={elegida ? c.primary : c.secondaryLight}
37
+ />
38
+ <Text style={styles.opcionTexto}>{opcion.texto}</Text>
39
+ </Pressable>
40
+ );
41
+ })}
42
+ </View>
43
+ );
44
+ };
45
+
46
+ const useStyles = crearEstilos((c) => ({
47
+ opciones: { gap: 10 },
48
+ opcion: {
49
+ flexDirection: "row",
50
+ alignItems: "flex-start",
51
+ gap: 8,
52
+ backgroundColor: c.superficie,
53
+ borderRadius: 14,
54
+ borderWidth: 2,
55
+ borderColor: c.secondaryLighter,
56
+ padding: 14,
57
+ },
58
+ opcionElegida: { borderColor: c.primary, backgroundColor: c.primaryWash },
59
+ opcionTexto: { flex: 1, ...renglon(tipografia.subtitulo), fontWeight: "800", color: c.texto },
60
+ }));
61
+
62
+ export default OpcionesExcluyentes;
@@ -0,0 +1,76 @@
1
+ import MaterialCommunityIcons from "@expo/vector-icons/MaterialCommunityIcons";
2
+ import { useEffect } from "react";
3
+ import { ScrollView } from "react-native";
4
+ import { SafeAreaView } from "react-native-safe-area-context";
5
+ import { Text } from "../texto";
6
+ import { crearEstilos, tipografia, usePaleta } from "../tema";
7
+ import BotonGrande from "./BotonGrande";
8
+
9
+ /** Lo que expo-router le pasa al `ErrorBoundary` de un layout. */
10
+ export interface PropsDePantallaDeError {
11
+ error: Error;
12
+ retry: () => Promise<void>;
13
+ }
14
+
15
+ /**
16
+ * Lo que se ve cuando una pantalla truena al pintarse, en vez de la pantalla roja (en desarrollo) o
17
+ * de una app que se cierra sola (en producción). Es el `ErrorBoundary` de expo-router: lo exporta el
18
+ * layout raíz, envuelto para pasarle `onError`.
19
+ *
20
+ * El error se reporta aquí (`onError`, a Sentry): React no lo deja llegar al manejador global una
21
+ * vez que una frontera lo atrapa. «Reintentar» vuelve a montar la pantalla.
22
+ *
23
+ * No usa nada de Paper ni de un proveedor: si lo que tronó es el layout raíz, aquí no hay
24
+ * proveedores.
25
+ */
26
+ const PantallaDeError = ({
27
+ error,
28
+ retry,
29
+ onError,
30
+ mensaje = "Esta pantalla no se pudo abrir. Intenta de nuevo; si vuelve a pasar, avisa a soporte.",
31
+ }: PropsDePantallaDeError & {
32
+ onError?: (error: Error) => void;
33
+ /** Lo que se le dice a quien la ve: qué no se perdió y a quién avisar. */
34
+ mensaje?: string;
35
+ }) => {
36
+ const c = usePaleta();
37
+ const styles = useStyles();
38
+ useEffect(() => {
39
+ onError?.(error);
40
+ // Se reporta una vez por error, aunque quien llama pase un `onError` nuevo en cada render.
41
+ // eslint-disable-next-line react-hooks/exhaustive-deps
42
+ }, [error]);
43
+
44
+ return (
45
+ <SafeAreaView style={styles.root}>
46
+ <ScrollView contentContainerStyle={styles.contenido}>
47
+ <MaterialCommunityIcons name="alert-octagon-outline" size={64} color={c.primary} />
48
+ <Text style={styles.titulo}>Algo salió mal</Text>
49
+ <Text style={styles.texto}>{mensaje}</Text>
50
+ <BotonGrande icono="refresh" onPress={() => void retry()}>
51
+ Reintentar
52
+ </BotonGrande>
53
+ </ScrollView>
54
+ </SafeAreaView>
55
+ );
56
+ };
57
+
58
+ const useStyles = crearEstilos((c) => ({
59
+ root: { flex: 1, backgroundColor: c.fondo },
60
+ contenido: {
61
+ flexGrow: 1,
62
+ justifyContent: "center",
63
+ alignItems: "stretch",
64
+ gap: 16,
65
+ padding: 24,
66
+ },
67
+ titulo: {
68
+ fontSize: tipografia.titulo,
69
+ fontWeight: "800",
70
+ color: c.secondaryDark,
71
+ textAlign: "center",
72
+ },
73
+ texto: { fontSize: tipografia.cuerpo, color: c.secondary, textAlign: "center" },
74
+ }));
75
+
76
+ export default PantallaDeError;
@@ -0,0 +1,86 @@
1
+ import { crearEstilos, renglon, tipografia, usePaleta } from "../tema";
2
+ import { Text } from "../texto";
3
+ import MaterialCommunityIcons from "@expo/vector-icons/MaterialCommunityIcons";
4
+ import React from "react";
5
+ import { Pressable, View } from "react-native";
6
+
7
+ export interface Pestana<K extends string> {
8
+ key: K;
9
+ label: string;
10
+ icono: React.ComponentProps<typeof MaterialCommunityIcons>["name"];
11
+ }
12
+
13
+ interface Props<K extends string> {
14
+ pestanas: readonly Pestana<K>[];
15
+ activa: K;
16
+ onSelect: (key: K) => void;
17
+ }
18
+
19
+ /**
20
+ * La tira de pestañas dentro de una pantalla: la misma de las listas de acv-wms (Cobranza,
21
+ * Importaciones), con su icono, y la elegida en rojo sobre un lavado rojo. Allá se repite en
22
+ * cada pantalla; aquí es un componente para que cada sección nueva la reutilice.
23
+ *
24
+ * Los iconos son de MaterialCommunityIcons, la familia de Paper, y no de MaterialIcons como en
25
+ * wms: es la que tiene `progress-clock`, el de Actual.
26
+ *
27
+ * **El icono va arriba del texto**, no al lado como en wms: con la letra del operador
28
+ * (`tipografia.etiqueta`) tres rótulos en fila no caben en un teléfono, y el botón más alto
29
+ * también se atina mejor con prisa. El rótulo no se encoge por debajo de `etiqueta`, que es el
30
+ * mínimo: con la letra muy grande, parte en otro renglón.
31
+ */
32
+ const Pestanas = <K extends string>({ pestanas, activa, onSelect }: Props<K>) => {
33
+ const c = usePaleta();
34
+ const styles = useStyles();
35
+ return (
36
+ <View style={styles.tabs}>
37
+ {pestanas.map((p) => {
38
+ const on = p.key === activa;
39
+ return (
40
+ <Pressable
41
+ key={p.key}
42
+ onPress={() => onSelect(p.key)}
43
+ accessibilityRole="tab"
44
+ accessibilityState={{ selected: on }}
45
+ style={[styles.tab, on && styles.tabOn]}
46
+ >
47
+ <MaterialCommunityIcons
48
+ name={p.icono}
49
+ size={22}
50
+ color={on ? c.primary : c.secondaryLight}
51
+ />
52
+ <Text style={[styles.tabText, on && styles.tabTextOn]}>{p.label}</Text>
53
+ </Pressable>
54
+ );
55
+ })}
56
+ </View>
57
+ );
58
+ };
59
+
60
+ const useStyles = crearEstilos((c) => ({
61
+ tabs: { flexDirection: "row", gap: 6, marginBottom: 12 },
62
+ tab: {
63
+ flex: 1,
64
+ alignItems: "center",
65
+ justifyContent: "center",
66
+ gap: 2,
67
+ paddingVertical: 8,
68
+ paddingHorizontal: 6,
69
+ borderRadius: 10,
70
+ borderWidth: 1.5,
71
+ borderColor: "transparent",
72
+ backgroundColor: c.superficie,
73
+ },
74
+ tabOn: { borderColor: c.primary, backgroundColor: c.primaryWash },
75
+ tabText: {
76
+ ...renglon(tipografia.etiqueta),
77
+ fontWeight: "500",
78
+ color: c.secondaryLight,
79
+ textAlign: "center",
80
+ },
81
+ // Sólo el color, sin negritas: en negritas «Por iniciar» ya no cabía en un renglón y la pestaña
82
+ // crecía al elegirla. Lo elegido ya lo dicen el borde y el fondo.
83
+ tabTextOn: { color: c.primary },
84
+ }));
85
+
86
+ export default Pestanas;