@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.
Files changed (297) hide show
  1. package/README.md +146 -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/componentes/AccionesDelEncabezado.js +212 -0
  10. package/lib/module/componentes/AccionesDelEncabezado.js.map +1 -0
  11. package/lib/module/componentes/BotonDeAvatar.js +55 -0
  12. package/lib/module/componentes/BotonDeAvatar.js.map +1 -0
  13. package/lib/module/componentes/BotonGrande.js +105 -0
  14. package/lib/module/componentes/BotonGrande.js.map +1 -0
  15. package/lib/module/componentes/CabeceraEmpujada.js +101 -0
  16. package/lib/module/componentes/CabeceraEmpujada.js.map +1 -0
  17. package/lib/module/componentes/Chip.js +77 -0
  18. package/lib/module/componentes/Chip.js.map +1 -0
  19. package/lib/module/componentes/ContenidoDePantalla.js +56 -0
  20. package/lib/module/componentes/ContenidoDePantalla.js.map +1 -0
  21. package/lib/module/componentes/EstadoVacio.js +65 -0
  22. package/lib/module/componentes/EstadoVacio.js.map +1 -0
  23. package/lib/module/componentes/Foto.js +106 -0
  24. package/lib/module/componentes/Foto.js.map +1 -0
  25. package/lib/module/componentes/IconoDeRenglon.js +44 -0
  26. package/lib/module/componentes/IconoDeRenglon.js.map +1 -0
  27. package/lib/module/componentes/Logo.js +44 -0
  28. package/lib/module/componentes/Logo.js.map +1 -0
  29. package/lib/module/componentes/Nota.js +51 -0
  30. package/lib/module/componentes/Nota.js.map +1 -0
  31. package/lib/module/componentes/OpcionesExcluyentes.js +69 -0
  32. package/lib/module/componentes/OpcionesExcluyentes.js.map +1 -0
  33. package/lib/module/componentes/PantallaDeError.js +84 -0
  34. package/lib/module/componentes/PantallaDeError.js.map +1 -0
  35. package/lib/module/componentes/Pestanas.js +87 -0
  36. package/lib/module/componentes/Pestanas.js.map +1 -0
  37. package/lib/module/componentes/Rotulo.js +28 -0
  38. package/lib/module/componentes/Rotulo.js.map +1 -0
  39. package/lib/module/componentes/RotuloDeSeccion.js +87 -0
  40. package/lib/module/componentes/RotuloDeSeccion.js.map +1 -0
  41. package/lib/module/componentes/Tarjeta.js +47 -0
  42. package/lib/module/componentes/Tarjeta.js.map +1 -0
  43. package/lib/module/componentes/TarjetaDeDatos.js +79 -0
  44. package/lib/module/componentes/TarjetaDeDatos.js.map +1 -0
  45. package/lib/module/componentes/TiraDeFotos.js +184 -0
  46. package/lib/module/componentes/TiraDeFotos.js.map +1 -0
  47. package/lib/module/componentes/VisorDeFoto.js +171 -0
  48. package/lib/module/componentes/VisorDeFoto.js.map +1 -0
  49. package/lib/module/componentes/logos/bola-blanca.png +0 -0
  50. package/lib/module/componentes/logos/bola.png +0 -0
  51. package/lib/module/componentes/logos/logo-blanco.png +0 -0
  52. package/lib/module/componentes/logos/logo.png +0 -0
  53. package/lib/module/dialogos/HostDeDialogos.js +170 -0
  54. package/lib/module/dialogos/HostDeDialogos.js.map +1 -0
  55. package/lib/module/dialogos/dialogos.js +112 -0
  56. package/lib/module/dialogos/dialogos.js.map +1 -0
  57. package/lib/module/formularios/CampoDeSeleccion.js +47 -0
  58. package/lib/module/formularios/CampoDeSeleccion.js.map +1 -0
  59. package/lib/module/formularios/SelectorDeOpcion.js +406 -0
  60. package/lib/module/formularios/SelectorDeOpcion.js.map +1 -0
  61. package/lib/module/formularios/TextoDeAccesoControlado.js +73 -0
  62. package/lib/module/formularios/TextoDeAccesoControlado.js.map +1 -0
  63. package/lib/module/formularios/campos.js +493 -0
  64. package/lib/module/formularios/campos.js.map +1 -0
  65. package/lib/module/formularios/controlados.js +197 -0
  66. package/lib/module/formularios/controlados.js.map +1 -0
  67. package/lib/module/formularios/errores.js +91 -0
  68. package/lib/module/formularios/errores.js.map +1 -0
  69. package/lib/module/formularios/guardado.js +78 -0
  70. package/lib/module/formularios/guardado.js.map +1 -0
  71. package/lib/module/formularios/useBusqueda.js +22 -0
  72. package/lib/module/formularios/useBusqueda.js.map +1 -0
  73. package/lib/module/fotos/elegir.js +178 -0
  74. package/lib/module/fotos/elegir.js.map +1 -0
  75. package/lib/module/fotos/index.js +8 -0
  76. package/lib/module/fotos/index.js.map +1 -0
  77. package/lib/module/fotos/reducir.js +93 -0
  78. package/lib/module/fotos/reducir.js.map +1 -0
  79. package/lib/module/hoja/HojaInferior.js +108 -0
  80. package/lib/module/hoja/HojaInferior.js.map +1 -0
  81. package/lib/module/hoja/index.js +7 -0
  82. package/lib/module/hoja/index.js.map +1 -0
  83. package/lib/module/hoja/useMasDeLaFicha.js +48 -0
  84. package/lib/module/hoja/useMasDeLaFicha.js.map +1 -0
  85. package/lib/module/index.js +66 -0
  86. package/lib/module/index.js.map +1 -0
  87. package/lib/module/listas/HojaDeFiltros.js +260 -0
  88. package/lib/module/listas/HojaDeFiltros.js.map +1 -0
  89. package/lib/module/listas/ListaPaginada.js +103 -0
  90. package/lib/module/listas/ListaPaginada.js.map +1 -0
  91. package/lib/module/listas/TarjetaDeslizable.js +118 -0
  92. package/lib/module/listas/TarjetaDeslizable.js.map +1 -0
  93. package/lib/module/listas/paginado.js +11 -0
  94. package/lib/module/listas/paginado.js.map +1 -0
  95. package/lib/module/listas/piezas.js +603 -0
  96. package/lib/module/listas/piezas.js.map +1 -0
  97. package/lib/module/navegacion/BarraDePestanas.js +162 -0
  98. package/lib/module/navegacion/BarraDePestanas.js.map +1 -0
  99. package/lib/module/navegacion/BotonDeBarra.js +58 -0
  100. package/lib/module/navegacion/BotonDeBarra.js.map +1 -0
  101. package/lib/module/navegacion/encabezado.js +103 -0
  102. package/lib/module/navegacion/encabezado.js.map +1 -0
  103. package/lib/module/pantallas/PantallaDeAcceso.js +195 -0
  104. package/lib/module/pantallas/PantallaDeAcceso.js.map +1 -0
  105. package/lib/module/pantallas/PantallaDeEdicion.js +121 -0
  106. package/lib/module/pantallas/PantallaDeEdicion.js.map +1 -0
  107. package/lib/module/pantallas/PantallaDeFicha.js +94 -0
  108. package/lib/module/pantallas/PantallaDeFicha.js.map +1 -0
  109. package/lib/module/pantallas/PantallaDePerfil.js +137 -0
  110. package/lib/module/pantallas/PantallaDePerfil.js.map +1 -0
  111. package/lib/module/tema/ProveedorDeTema.js +122 -0
  112. package/lib/module/tema/ProveedorDeTema.js.map +1 -0
  113. package/lib/module/tema/estilos.js +54 -0
  114. package/lib/module/tema/estilos.js.map +1 -0
  115. package/lib/module/tema/fuentes.js +28 -0
  116. package/lib/module/tema/fuentes.js.map +1 -0
  117. package/lib/module/tema/index.js +8 -0
  118. package/lib/module/tema/index.js.map +1 -0
  119. package/lib/module/tema/paleta.js +107 -0
  120. package/lib/module/tema/paleta.js.map +1 -0
  121. package/lib/module/tema/tipografia.js +32 -0
  122. package/lib/module/tema/tipografia.js.map +1 -0
  123. package/lib/module/texto.js +48 -0
  124. package/lib/module/texto.js.map +1 -0
  125. package/lib/typescript/src/componentes/AccionesDelEncabezado.d.ts +34 -0
  126. package/lib/typescript/src/componentes/AccionesDelEncabezado.d.ts.map +1 -0
  127. package/lib/typescript/src/componentes/BotonDeAvatar.d.ts +17 -0
  128. package/lib/typescript/src/componentes/BotonDeAvatar.d.ts.map +1 -0
  129. package/lib/typescript/src/componentes/BotonGrande.d.ts +26 -0
  130. package/lib/typescript/src/componentes/BotonGrande.d.ts.map +1 -0
  131. package/lib/typescript/src/componentes/CabeceraEmpujada.d.ts +18 -0
  132. package/lib/typescript/src/componentes/CabeceraEmpujada.d.ts.map +1 -0
  133. package/lib/typescript/src/componentes/Chip.d.ts +14 -0
  134. package/lib/typescript/src/componentes/Chip.d.ts.map +1 -0
  135. package/lib/typescript/src/componentes/ContenidoDePantalla.d.ts +17 -0
  136. package/lib/typescript/src/componentes/ContenidoDePantalla.d.ts.map +1 -0
  137. package/lib/typescript/src/componentes/EstadoVacio.d.ts +17 -0
  138. package/lib/typescript/src/componentes/EstadoVacio.d.ts.map +1 -0
  139. package/lib/typescript/src/componentes/Foto.d.ts +41 -0
  140. package/lib/typescript/src/componentes/Foto.d.ts.map +1 -0
  141. package/lib/typescript/src/componentes/IconoDeRenglon.d.ts +25 -0
  142. package/lib/typescript/src/componentes/IconoDeRenglon.d.ts.map +1 -0
  143. package/lib/typescript/src/componentes/Logo.d.ts +13 -0
  144. package/lib/typescript/src/componentes/Logo.d.ts.map +1 -0
  145. package/lib/typescript/src/componentes/Nota.d.ts +7 -0
  146. package/lib/typescript/src/componentes/Nota.d.ts.map +1 -0
  147. package/lib/typescript/src/componentes/OpcionesExcluyentes.d.ts +14 -0
  148. package/lib/typescript/src/componentes/OpcionesExcluyentes.d.ts.map +1 -0
  149. package/lib/typescript/src/componentes/PantallaDeError.d.ts +23 -0
  150. package/lib/typescript/src/componentes/PantallaDeError.d.ts.map +1 -0
  151. package/lib/typescript/src/componentes/Pestanas.d.ts +28 -0
  152. package/lib/typescript/src/componentes/Pestanas.d.ts.map +1 -0
  153. package/lib/typescript/src/componentes/Rotulo.d.ts +6 -0
  154. package/lib/typescript/src/componentes/Rotulo.d.ts.map +1 -0
  155. package/lib/typescript/src/componentes/RotuloDeSeccion.d.ts +20 -0
  156. package/lib/typescript/src/componentes/RotuloDeSeccion.d.ts.map +1 -0
  157. package/lib/typescript/src/componentes/Tarjeta.d.ts +15 -0
  158. package/lib/typescript/src/componentes/Tarjeta.d.ts.map +1 -0
  159. package/lib/typescript/src/componentes/TarjetaDeDatos.d.ts +13 -0
  160. package/lib/typescript/src/componentes/TarjetaDeDatos.d.ts.map +1 -0
  161. package/lib/typescript/src/componentes/TiraDeFotos.d.ts +36 -0
  162. package/lib/typescript/src/componentes/TiraDeFotos.d.ts.map +1 -0
  163. package/lib/typescript/src/componentes/VisorDeFoto.d.ts +23 -0
  164. package/lib/typescript/src/componentes/VisorDeFoto.d.ts.map +1 -0
  165. package/lib/typescript/src/dialogos/HostDeDialogos.d.ts +14 -0
  166. package/lib/typescript/src/dialogos/HostDeDialogos.d.ts.map +1 -0
  167. package/lib/typescript/src/dialogos/dialogos.d.ts +65 -0
  168. package/lib/typescript/src/dialogos/dialogos.d.ts.map +1 -0
  169. package/lib/typescript/src/formularios/CampoDeSeleccion.d.ts +22 -0
  170. package/lib/typescript/src/formularios/CampoDeSeleccion.d.ts.map +1 -0
  171. package/lib/typescript/src/formularios/SelectorDeOpcion.d.ts +59 -0
  172. package/lib/typescript/src/formularios/SelectorDeOpcion.d.ts.map +1 -0
  173. package/lib/typescript/src/formularios/TextoDeAccesoControlado.d.ts +18 -0
  174. package/lib/typescript/src/formularios/TextoDeAccesoControlado.d.ts.map +1 -0
  175. package/lib/typescript/src/formularios/campos.d.ts +107 -0
  176. package/lib/typescript/src/formularios/campos.d.ts.map +1 -0
  177. package/lib/typescript/src/formularios/controlados.d.ts +29 -0
  178. package/lib/typescript/src/formularios/controlados.d.ts.map +1 -0
  179. package/lib/typescript/src/formularios/errores.d.ts +36 -0
  180. package/lib/typescript/src/formularios/errores.d.ts.map +1 -0
  181. package/lib/typescript/src/formularios/guardado.d.ts +36 -0
  182. package/lib/typescript/src/formularios/guardado.d.ts.map +1 -0
  183. package/lib/typescript/src/formularios/useBusqueda.d.ts +7 -0
  184. package/lib/typescript/src/formularios/useBusqueda.d.ts.map +1 -0
  185. package/lib/typescript/src/fotos/elegir.d.ts +58 -0
  186. package/lib/typescript/src/fotos/elegir.d.ts.map +1 -0
  187. package/lib/typescript/src/fotos/index.d.ts +3 -0
  188. package/lib/typescript/src/fotos/index.d.ts.map +1 -0
  189. package/lib/typescript/src/fotos/reducir.d.ts +32 -0
  190. package/lib/typescript/src/fotos/reducir.d.ts.map +1 -0
  191. package/lib/typescript/src/hoja/HojaInferior.d.ts +21 -0
  192. package/lib/typescript/src/hoja/HojaInferior.d.ts.map +1 -0
  193. package/lib/typescript/src/hoja/index.d.ts +3 -0
  194. package/lib/typescript/src/hoja/index.d.ts.map +1 -0
  195. package/lib/typescript/src/hoja/useMasDeLaFicha.d.ts +27 -0
  196. package/lib/typescript/src/hoja/useMasDeLaFicha.d.ts.map +1 -0
  197. package/lib/typescript/src/index.d.ts +45 -0
  198. package/lib/typescript/src/index.d.ts.map +1 -0
  199. package/lib/typescript/src/listas/HojaDeFiltros.d.ts +60 -0
  200. package/lib/typescript/src/listas/HojaDeFiltros.d.ts.map +1 -0
  201. package/lib/typescript/src/listas/ListaPaginada.d.ts +39 -0
  202. package/lib/typescript/src/listas/ListaPaginada.d.ts.map +1 -0
  203. package/lib/typescript/src/listas/TarjetaDeslizable.d.ts +33 -0
  204. package/lib/typescript/src/listas/TarjetaDeslizable.d.ts.map +1 -0
  205. package/lib/typescript/src/listas/paginado.d.ts +16 -0
  206. package/lib/typescript/src/listas/paginado.d.ts.map +1 -0
  207. package/lib/typescript/src/listas/piezas.d.ts +135 -0
  208. package/lib/typescript/src/listas/piezas.d.ts.map +1 -0
  209. package/lib/typescript/src/navegacion/BarraDePestanas.d.ts +57 -0
  210. package/lib/typescript/src/navegacion/BarraDePestanas.d.ts.map +1 -0
  211. package/lib/typescript/src/navegacion/BotonDeBarra.d.ts +15 -0
  212. package/lib/typescript/src/navegacion/BotonDeBarra.d.ts.map +1 -0
  213. package/lib/typescript/src/navegacion/encabezado.d.ts +44 -0
  214. package/lib/typescript/src/navegacion/encabezado.d.ts.map +1 -0
  215. package/lib/typescript/src/pantallas/PantallaDeAcceso.d.ts +32 -0
  216. package/lib/typescript/src/pantallas/PantallaDeAcceso.d.ts.map +1 -0
  217. package/lib/typescript/src/pantallas/PantallaDeEdicion.d.ts +38 -0
  218. package/lib/typescript/src/pantallas/PantallaDeEdicion.d.ts.map +1 -0
  219. package/lib/typescript/src/pantallas/PantallaDeFicha.d.ts +35 -0
  220. package/lib/typescript/src/pantallas/PantallaDeFicha.d.ts.map +1 -0
  221. package/lib/typescript/src/pantallas/PantallaDePerfil.d.ts +19 -0
  222. package/lib/typescript/src/pantallas/PantallaDePerfil.d.ts.map +1 -0
  223. package/lib/typescript/src/tema/ProveedorDeTema.d.ts +32 -0
  224. package/lib/typescript/src/tema/ProveedorDeTema.d.ts.map +1 -0
  225. package/lib/typescript/src/tema/estilos.d.ts +31 -0
  226. package/lib/typescript/src/tema/estilos.d.ts.map +1 -0
  227. package/lib/typescript/src/tema/fuentes.d.ts +20 -0
  228. package/lib/typescript/src/tema/fuentes.d.ts.map +1 -0
  229. package/lib/typescript/src/tema/index.d.ts +6 -0
  230. package/lib/typescript/src/tema/index.d.ts.map +1 -0
  231. package/lib/typescript/src/tema/paleta.d.ts +86 -0
  232. package/lib/typescript/src/tema/paleta.d.ts.map +1 -0
  233. package/lib/typescript/src/tema/tipografia.d.ts +29 -0
  234. package/lib/typescript/src/tema/tipografia.d.ts.map +1 -0
  235. package/lib/typescript/src/texto.d.ts +16 -0
  236. package/lib/typescript/src/texto.d.ts.map +1 -0
  237. package/package.json +135 -5
  238. package/src/componentes/AccionesDelEncabezado.tsx +247 -0
  239. package/src/componentes/BotonDeAvatar.tsx +59 -0
  240. package/src/componentes/BotonGrande.tsx +100 -0
  241. package/src/componentes/CabeceraEmpujada.tsx +69 -0
  242. package/src/componentes/Chip.tsx +60 -0
  243. package/src/componentes/ContenidoDePantalla.tsx +53 -0
  244. package/src/componentes/EstadoVacio.tsx +45 -0
  245. package/src/componentes/Foto.tsx +92 -0
  246. package/src/componentes/IconoDeRenglon.tsx +42 -0
  247. package/src/componentes/Logo.tsx +34 -0
  248. package/src/componentes/Nota.tsx +37 -0
  249. package/src/componentes/OpcionesExcluyentes.tsx +62 -0
  250. package/src/componentes/PantallaDeError.tsx +76 -0
  251. package/src/componentes/Pestanas.tsx +86 -0
  252. package/src/componentes/Rotulo.tsx +21 -0
  253. package/src/componentes/RotuloDeSeccion.tsx +71 -0
  254. package/src/componentes/Tarjeta.tsx +44 -0
  255. package/src/componentes/TarjetaDeDatos.tsx +62 -0
  256. package/src/componentes/TiraDeFotos.tsx +189 -0
  257. package/src/componentes/VisorDeFoto.tsx +181 -0
  258. package/src/componentes/logos/bola-blanca.png +0 -0
  259. package/src/componentes/logos/bola.png +0 -0
  260. package/src/componentes/logos/logo-blanco.png +0 -0
  261. package/src/componentes/logos/logo.png +0 -0
  262. package/src/dialogos/HostDeDialogos.tsx +172 -0
  263. package/src/dialogos/dialogos.ts +148 -0
  264. package/src/formularios/CampoDeSeleccion.tsx +46 -0
  265. package/src/formularios/SelectorDeOpcion.tsx +407 -0
  266. package/src/formularios/TextoDeAccesoControlado.tsx +72 -0
  267. package/src/formularios/campos.tsx +483 -0
  268. package/src/formularios/controlados.tsx +198 -0
  269. package/src/formularios/errores.ts +105 -0
  270. package/src/formularios/guardado.ts +76 -0
  271. package/src/formularios/useBusqueda.ts +19 -0
  272. package/src/fotos/elegir.ts +194 -0
  273. package/src/fotos/index.ts +15 -0
  274. package/src/fotos/reducir.ts +87 -0
  275. package/src/hoja/HojaInferior.tsx +102 -0
  276. package/src/hoja/index.ts +4 -0
  277. package/src/hoja/useMasDeLaFicha.tsx +63 -0
  278. package/src/index.ts +143 -0
  279. package/src/listas/HojaDeFiltros.tsx +261 -0
  280. package/src/listas/ListaPaginada.tsx +117 -0
  281. package/src/listas/TarjetaDeslizable.tsx +126 -0
  282. package/src/listas/paginado.ts +15 -0
  283. package/src/listas/piezas.tsx +594 -0
  284. package/src/navegacion/BarraDePestanas.tsx +198 -0
  285. package/src/navegacion/BotonDeBarra.tsx +61 -0
  286. package/src/navegacion/encabezado.tsx +88 -0
  287. package/src/pantallas/PantallaDeAcceso.tsx +184 -0
  288. package/src/pantallas/PantallaDeEdicion.tsx +116 -0
  289. package/src/pantallas/PantallaDeFicha.tsx +115 -0
  290. package/src/pantallas/PantallaDePerfil.tsx +126 -0
  291. package/src/tema/ProveedorDeTema.tsx +150 -0
  292. package/src/tema/estilos.ts +51 -0
  293. package/src/tema/fuentes.ts +20 -0
  294. package/src/tema/index.ts +10 -0
  295. package/src/tema/paleta.ts +147 -0
  296. package/src/tema/tipografia.ts +29 -0
  297. package/src/texto.tsx +44 -0
@@ -0,0 +1,483 @@
1
+ import DateTimePicker from "@react-native-community/datetimepicker";
2
+ import dayjs from "dayjs";
3
+ import React from "react";
4
+ import { Pressable, Switch, View, type KeyboardTypeOptions } from "react-native";
5
+ import IconoDeRenglon from "../componentes/IconoDeRenglon";
6
+ import OpcionesExcluyentes from "../componentes/OpcionesExcluyentes";
7
+ import { crearEstilos, renglon, tipografia, usePaleta } from "../tema";
8
+ import { Text, TextInput } from "../texto";
9
+
10
+ /**
11
+ * Los campos de un formulario (un viaje, un tracto, un operador). Cada uno trae su rótulo arriba y
12
+ * su error abajo, para que la pantalla sólo los apile. Son controlados: atados a react-hook-form,
13
+ * van por `controlados.tsx`.
14
+ */
15
+
16
+ /** El error de un campo, en rojo y debajo de él. */
17
+ export function ErrorDelCampo({ error }: { error?: string }) {
18
+ const styles = useStyles();
19
+ return error ? <Text style={styles.error}>{error}</Text> : null;
20
+ }
21
+
22
+ /**
23
+ * El marco de un campo: el rótulo arriba, lo que se captura en medio, y debajo la ayuda y el error.
24
+ * Lo usan todos los campos de aquí, y una app lo usa para lo que no es un campo de la casa —una
25
+ * tira de fotos, el renglón de un escáner— sin que se vea distinto de los demás.
26
+ *
27
+ * Sin `etiqueta` no pinta rótulo: el interruptor lleva el suyo adentro.
28
+ */
29
+ export function Campo({
30
+ etiqueta,
31
+ ayuda,
32
+ error,
33
+ children,
34
+ }: {
35
+ etiqueta?: string;
36
+ ayuda?: string;
37
+ error?: string;
38
+ children: React.ReactNode;
39
+ }) {
40
+ const styles = useStyles();
41
+ return (
42
+ <View style={styles.campo}>
43
+ {etiqueta ? <Text style={styles.etiqueta}>{etiqueta}</Text> : null}
44
+ {children}
45
+ {ayuda ? <Text style={styles.ayuda}>{ayuda}</Text> : null}
46
+ <ErrorDelCampo error={error} />
47
+ </View>
48
+ );
49
+ }
50
+
51
+ /**
52
+ * Un texto libre: una referencia (tope de 100), unas instrucciones (varios renglones), un VIN (en
53
+ * mayúsculas) o un año (`teclado="number-pad"`).
54
+ */
55
+ export function CampoDeTexto({
56
+ etiqueta,
57
+ valor,
58
+ onCambiar,
59
+ error,
60
+ multilinea,
61
+ maximo,
62
+ placeholder,
63
+ ayuda,
64
+ disabled,
65
+ teclado,
66
+ mayusculas,
67
+ }: {
68
+ etiqueta: string;
69
+ valor: string;
70
+ onCambiar: (valor: string) => void;
71
+ error?: string;
72
+ multilinea?: boolean;
73
+ maximo?: number;
74
+ placeholder?: string;
75
+ ayuda?: string;
76
+ disabled?: boolean;
77
+ teclado?: KeyboardTypeOptions;
78
+ /** Cómo pone mayúsculas el teclado; un VIN o una placa, `characters`. */
79
+ mayusculas?: "none" | "sentences" | "words" | "characters";
80
+ }) {
81
+ const styles = useStyles();
82
+ return (
83
+ <Campo etiqueta={etiqueta} ayuda={ayuda} error={error}>
84
+ <TextInput
85
+ value={valor}
86
+ onChangeText={onCambiar}
87
+ keyboardType={teclado}
88
+ autoCapitalize={mayusculas}
89
+ autoCorrect={mayusculas === "characters" ? false : undefined}
90
+ multiline={multilinea}
91
+ maxLength={maximo}
92
+ placeholder={placeholder}
93
+ editable={!disabled}
94
+ style={[
95
+ styles.entrada,
96
+ multilinea && styles.multilinea,
97
+ !!error && styles.entradaError,
98
+ disabled && styles.inactivo,
99
+ ]}
100
+ />
101
+ </Campo>
102
+ );
103
+ }
104
+
105
+ /**
106
+ * Una fecha con su hora: dos botones, el día y la hora, cada uno con el selector del sistema. **La
107
+ * hora es el dato** (contra ella se mide si un viaje va tarde o un equipo está comprometido), así
108
+ * que nunca se manda sólo el día.
109
+ *
110
+ * Vacío, un solo botón «Elegir»: abre el día y, al elegirlo, la hora. Si es `opcional`, «Quitar»
111
+ * lo deja vacío otra vez.
112
+ */
113
+ export function CampoDeFechaHora({
114
+ etiqueta,
115
+ valor,
116
+ onCambiar,
117
+ error,
118
+ opcional,
119
+ ayuda,
120
+ }: {
121
+ etiqueta: string;
122
+ valor: Date | null;
123
+ onCambiar: (valor: Date | null) => void;
124
+ error?: string;
125
+ opcional?: boolean;
126
+ ayuda?: string;
127
+ }) {
128
+ const c = usePaleta();
129
+ const styles = useStyles();
130
+ const [eligiendo, setEligiendo] = React.useState<"date" | "time" | null>(null);
131
+ // Vacío, el selector arranca en la próxima hora en punto: la hora de ahora no le sirve a nadie.
132
+ const base = valor ?? dayjs().add(1, "hour").startOf("hour").toDate();
133
+
134
+ return (
135
+ <Campo etiqueta={etiqueta} ayuda={ayuda} error={error}>
136
+ {valor ? (
137
+ <View style={styles.fechaHora}>
138
+ <Pressable
139
+ onPress={() => setEligiendo("date")}
140
+ accessibilityRole="button"
141
+ style={[styles.entrada, styles.mitad, !!error && styles.entradaError]}
142
+ >
143
+ <Text style={styles.textoFecha}>{dayjs(valor).format("D MMM YYYY")}</Text>
144
+ </Pressable>
145
+ <Pressable
146
+ onPress={() => setEligiendo("time")}
147
+ accessibilityRole="button"
148
+ style={[styles.entrada, styles.mitad, !!error && styles.entradaError]}
149
+ >
150
+ <Text style={styles.textoFecha}>{dayjs(valor).format("HH:mm")}</Text>
151
+ </Pressable>
152
+ </View>
153
+ ) : (
154
+ <Pressable
155
+ onPress={() => setEligiendo("date")}
156
+ accessibilityRole="button"
157
+ style={[styles.entrada, !!error && styles.entradaError]}
158
+ >
159
+ <View style={styles.renglon}>
160
+ <IconoDeRenglon
161
+ name="calendar-clock"
162
+ fontSize={tipografia.subtitulo}
163
+ color={c.secondaryLight}
164
+ />
165
+ <Text style={[styles.textoFecha, styles.sinValor]}>Elegir…</Text>
166
+ </View>
167
+ </Pressable>
168
+ )}
169
+ {opcional && valor ? (
170
+ <Pressable
171
+ onPress={() => onCambiar(null)}
172
+ accessibilityRole="button"
173
+ style={styles.quitar}
174
+ >
175
+ <IconoDeRenglon
176
+ name="close-circle-outline"
177
+ fontSize={tipografia.cuerpo}
178
+ color={c.secondary}
179
+ />
180
+ <Text style={styles.quitarTexto}>Quitar</Text>
181
+ </Pressable>
182
+ ) : null}
183
+ {eligiendo ? (
184
+ <DateTimePicker
185
+ value={base}
186
+ mode={eligiendo}
187
+ is24Hour
188
+ onChange={(evento, elegida) => {
189
+ const paso = eligiendo;
190
+ setEligiendo(null);
191
+ if (evento.type !== "set" || !elegida) return;
192
+ const nueva =
193
+ paso === "date"
194
+ ? dayjs(base)
195
+ .year(elegida.getFullYear())
196
+ .month(elegida.getMonth())
197
+ .date(elegida.getDate())
198
+ : dayjs(base)
199
+ .hour(elegida.getHours())
200
+ .minute(elegida.getMinutes())
201
+ .second(0)
202
+ .millisecond(0);
203
+ onCambiar(nueva.toDate());
204
+ // Vacío, tras el día va la hora: sin ella el campo no está completo.
205
+ if (paso === "date" && !valor) setEligiendo("time");
206
+ }}
207
+ />
208
+ ) : null}
209
+ </Campo>
210
+ );
211
+ }
212
+
213
+ /** Un interruptor con su texto y, si hace falta, por qué está como está. */
214
+ export function CampoInterruptor({
215
+ texto,
216
+ valor,
217
+ onCambiar,
218
+ ayuda,
219
+ error,
220
+ disabled,
221
+ }: {
222
+ texto: string;
223
+ valor: boolean;
224
+ onCambiar: (valor: boolean) => void;
225
+ ayuda?: string;
226
+ error?: string;
227
+ disabled?: boolean;
228
+ }) {
229
+ const c = usePaleta();
230
+ const styles = useStyles();
231
+ return (
232
+ <Campo ayuda={ayuda} error={error}>
233
+ <Pressable
234
+ onPress={() => onCambiar(!valor)}
235
+ disabled={disabled}
236
+ accessibilityRole="switch"
237
+ accessibilityState={{ checked: valor, disabled: !!disabled }}
238
+ style={[styles.interruptor, disabled && styles.inactivo]}
239
+ >
240
+ <Text style={styles.interruptorTexto}>{texto}</Text>
241
+ <Switch
242
+ value={valor}
243
+ onValueChange={onCambiar}
244
+ disabled={disabled}
245
+ trackColor={{ true: c.primaryLight }}
246
+ thumbColor={valor ? c.primary : undefined}
247
+ />
248
+ </Pressable>
249
+ </Campo>
250
+ );
251
+ }
252
+
253
+ /**
254
+ * Una fecha sin hora (un nacimiento, el fin de una renta): un botón con el selector del sistema.
255
+ * El valor es el de la API, `YYYY-MM-DD`, para no pasar por una zona horaria que lo corra un día.
256
+ */
257
+ export function CampoDeFecha({
258
+ etiqueta,
259
+ valor,
260
+ onCambiar,
261
+ error,
262
+ opcional,
263
+ ayuda,
264
+ }: {
265
+ etiqueta: string;
266
+ valor: string | null;
267
+ onCambiar: (valor: string | null) => void;
268
+ error?: string;
269
+ opcional?: boolean;
270
+ ayuda?: string;
271
+ }) {
272
+ const c = usePaleta();
273
+ const styles = useStyles();
274
+ const [eligiendo, setEligiendo] = React.useState(false);
275
+ const fecha = valor ? dayjs(valor) : null;
276
+
277
+ return (
278
+ <Campo etiqueta={etiqueta} ayuda={ayuda} error={error}>
279
+ <Pressable
280
+ onPress={() => setEligiendo(true)}
281
+ accessibilityRole="button"
282
+ style={[styles.entrada, !!error && styles.entradaError]}
283
+ >
284
+ <View style={styles.renglon}>
285
+ <IconoDeRenglon
286
+ name="calendar"
287
+ fontSize={tipografia.subtitulo}
288
+ color={fecha ? c.secondary : c.secondaryLight}
289
+ />
290
+ <Text style={[styles.textoFecha, !fecha && styles.sinValor]}>
291
+ {fecha ? fecha.format("D MMM YYYY") : "Elegir…"}
292
+ </Text>
293
+ </View>
294
+ </Pressable>
295
+ {opcional && valor ? (
296
+ <Pressable
297
+ onPress={() => onCambiar(null)}
298
+ accessibilityRole="button"
299
+ style={styles.quitar}
300
+ >
301
+ <IconoDeRenglon
302
+ name="close-circle-outline"
303
+ fontSize={tipografia.cuerpo}
304
+ color={c.secondary}
305
+ />
306
+ <Text style={styles.quitarTexto}>Quitar</Text>
307
+ </Pressable>
308
+ ) : null}
309
+ {eligiendo ? (
310
+ <DateTimePicker
311
+ value={fecha?.toDate() ?? new Date()}
312
+ mode="date"
313
+ onChange={(evento, elegida) => {
314
+ setEligiendo(false);
315
+ if (evento.type === "set" && elegida) {
316
+ onCambiar(dayjs(elegida).format("YYYY-MM-DD"));
317
+ }
318
+ }}
319
+ />
320
+ ) : null}
321
+ </Campo>
322
+ );
323
+ }
324
+
325
+ /**
326
+ * Una de pocas opciones fijas (la cabina, la propiedad, la longitud): todas a la vista, una por
327
+ * renglón, en vez de un menú que con la letra al 180% deja ver dos.
328
+ */
329
+ export function CampoDeOpciones<V extends string>({
330
+ etiqueta,
331
+ opciones,
332
+ valor,
333
+ onElegir,
334
+ error,
335
+ ayuda,
336
+ }: {
337
+ etiqueta: string;
338
+ opciones: readonly { value: V; label: string }[];
339
+ valor: V | null;
340
+ onElegir: (valor: V) => void;
341
+ error?: string;
342
+ ayuda?: string;
343
+ }) {
344
+ return (
345
+ <Campo etiqueta={etiqueta} ayuda={ayuda} error={error}>
346
+ <OpcionesExcluyentes
347
+ opciones={opciones.map((o) => ({ valor: o.value, texto: o.label }))}
348
+ valor={valor}
349
+ onElegir={onElegir}
350
+ />
351
+ </Campo>
352
+ );
353
+ }
354
+
355
+ /** Los países de un teléfono: lo que la operación marca, de un lado y del otro. */
356
+ const LADAS = [
357
+ { pais: "MX", lada: "+52", texto: "MX +52" },
358
+ { pais: "US", lada: "+1", texto: "US +1" },
359
+ ] as const;
360
+
361
+ /** Un teléfono guardado (`+526641234567`) partido en su lada y sus dígitos. */
362
+ const partirTelefono = (telefono: string | null) => {
363
+ const limpio = (telefono ?? "").replace(/[^\d+]/g, "");
364
+ const lada = LADAS.find((l) => limpio.startsWith(l.lada)) ?? LADAS[0];
365
+ return {
366
+ lada: lada.lada,
367
+ digitos: limpio.startsWith(lada.lada) ? limpio.slice(lada.lada.length) : limpio,
368
+ };
369
+ };
370
+
371
+ /**
372
+ * Un teléfono, como el `ControlledPhoneNumber` del portal: la lada (México o Estados Unidos) y los
373
+ * diez dígitos. El valor es el del servidor (`PhoneNumberField`), en E.164: `+526641234567`; vacío
374
+ * es `null`.
375
+ */
376
+ export function CampoDeTelefono({
377
+ etiqueta,
378
+ valor,
379
+ onCambiar,
380
+ error,
381
+ ayuda,
382
+ }: {
383
+ etiqueta: string;
384
+ valor: string | null;
385
+ onCambiar: (valor: string | null) => void;
386
+ error?: string;
387
+ ayuda?: string;
388
+ }) {
389
+ const c = usePaleta();
390
+ const styles = useStyles();
391
+ const { lada, digitos } = partirTelefono(valor);
392
+ const armar = (nuevaLada: string, nuevosDigitos: string) => {
393
+ const solo = nuevosDigitos.replace(/\D/g, "");
394
+ onCambiar(solo ? `${nuevaLada}${solo}` : null);
395
+ };
396
+ return (
397
+ <Campo etiqueta={etiqueta} ayuda={ayuda} error={error}>
398
+ <View style={styles.telefono}>
399
+ {LADAS.map((l) => (
400
+ <Pressable
401
+ key={l.pais}
402
+ onPress={() => armar(l.lada, digitos)}
403
+ accessibilityRole="radio"
404
+ accessibilityState={{ selected: lada === l.lada }}
405
+ style={[styles.lada, lada === l.lada && styles.ladaElegida]}
406
+ >
407
+ <Text style={[styles.ladaTexto, lada === l.lada && { color: c.primary }]}>
408
+ {l.texto}
409
+ </Text>
410
+ </Pressable>
411
+ ))}
412
+ </View>
413
+ <TextInput
414
+ value={digitos}
415
+ onChangeText={(texto) => armar(lada, texto)}
416
+ keyboardType="phone-pad"
417
+ maxLength={10}
418
+ placeholder="10 dígitos"
419
+ style={[styles.entrada, !!error && styles.entradaError]}
420
+ />
421
+ </Campo>
422
+ );
423
+ }
424
+
425
+ const useStyles = crearEstilos((c) => ({
426
+ campo: { gap: 6 },
427
+ etiqueta: {
428
+ ...renglon(tipografia.etiqueta),
429
+ fontWeight: "800",
430
+ color: c.secondary,
431
+ textTransform: "uppercase",
432
+ letterSpacing: 0.8,
433
+ },
434
+ entrada: {
435
+ borderWidth: 1.5,
436
+ borderColor: c.secondaryLight,
437
+ borderRadius: 12,
438
+ paddingHorizontal: 14,
439
+ paddingVertical: 12,
440
+ backgroundColor: c.superficie,
441
+ fontSize: tipografia.subtitulo,
442
+ color: c.texto,
443
+ },
444
+ multilinea: { minHeight: 100, textAlignVertical: "top", fontSize: tipografia.cuerpo },
445
+ entradaError: { borderColor: c.primary },
446
+ inactivo: { opacity: 0.5 },
447
+ fechaHora: { flexDirection: "row", gap: 12 },
448
+ mitad: { flex: 1, justifyContent: "center" },
449
+ renglon: { flexDirection: "row", alignItems: "flex-start", gap: 8 },
450
+ textoFecha: { ...renglon(tipografia.cuerpo), fontWeight: "700", color: c.texto },
451
+ sinValor: { color: c.secondaryLight, fontWeight: "600" },
452
+ quitar: { flexDirection: "row", alignItems: "flex-start", gap: 6, alignSelf: "flex-start" },
453
+ quitarTexto: { ...renglon(tipografia.cuerpo), fontWeight: "700", color: c.secondary },
454
+ ayuda: { ...renglon(tipografia.secundario), color: c.secondary },
455
+ error: { ...renglon(tipografia.secundario), color: c.primary },
456
+ telefono: { flexDirection: "row", gap: 10 },
457
+ lada: {
458
+ flex: 1,
459
+ alignItems: "center",
460
+ borderWidth: 2,
461
+ borderColor: c.secondaryLighter,
462
+ borderRadius: 12,
463
+ paddingVertical: 10,
464
+ backgroundColor: c.superficie,
465
+ },
466
+ ladaElegida: { borderColor: c.primary, backgroundColor: c.primaryWash },
467
+ ladaTexto: { ...renglon(tipografia.cuerpo), fontWeight: "800", color: c.secondary },
468
+ interruptor: {
469
+ flexDirection: "row",
470
+ alignItems: "center",
471
+ gap: 10,
472
+ backgroundColor: c.superficie,
473
+ borderRadius: 14,
474
+ paddingHorizontal: 14,
475
+ paddingVertical: 10,
476
+ },
477
+ interruptorTexto: {
478
+ flex: 1,
479
+ ...renglon(tipografia.cuerpo),
480
+ fontWeight: "700",
481
+ color: c.texto,
482
+ },
483
+ }));
@@ -0,0 +1,198 @@
1
+ import React from "react";
2
+ import { Controller, type Control, type FieldValues, type Path } from "react-hook-form";
3
+ import CampoDeSeleccion, { CampoDeVarias } from "./CampoDeSeleccion";
4
+ import {
5
+ CampoDeFecha,
6
+ CampoDeFechaHora,
7
+ CampoDeOpciones,
8
+ CampoDeTelefono,
9
+ CampoDeTexto,
10
+ CampoInterruptor,
11
+ } from "./campos";
12
+ import type { Opcion } from "./SelectorDeOpcion";
13
+
14
+ /**
15
+ * Los campos de `campos.tsx` ya atados a un formulario de react-hook-form: reciben `control` y
16
+ * `name` y se cuelgan su valor y su error. Así un formulario largo (un tracto, una caja, un
17
+ * operador) es una lista de campos y no una de `Controller`.
18
+ *
19
+ * Un campo propio de una app (un catálogo de su API, una dirección) se ata igual, con `CampoAtado`
20
+ * y `SinValor`.
21
+ */
22
+
23
+ /** Lo que un campo controlado recibe para atarse al formulario. */
24
+ export interface CampoAtado<T extends FieldValues> {
25
+ control: Control<T>;
26
+ name: Path<T>;
27
+ }
28
+
29
+ /** Las props de un campo sin lo que pone el `Controller`. */
30
+ export type SinValor<P> = Omit<P, "valor" | "onCambiar" | "onElegir" | "error">;
31
+
32
+ export function TextoControlado<T extends FieldValues>({
33
+ control,
34
+ name,
35
+ ...props
36
+ }: CampoAtado<T> & SinValor<React.ComponentProps<typeof CampoDeTexto>>) {
37
+ return (
38
+ <Controller
39
+ name={name}
40
+ control={control}
41
+ render={({ field: { value, onChange }, fieldState }) => (
42
+ <CampoDeTexto
43
+ {...props}
44
+ valor={value ?? ""}
45
+ onCambiar={onChange}
46
+ error={fieldState.error?.message}
47
+ />
48
+ )}
49
+ />
50
+ );
51
+ }
52
+
53
+ export function InterruptorControlado<T extends FieldValues>({
54
+ control,
55
+ name,
56
+ ...props
57
+ }: CampoAtado<T> & SinValor<React.ComponentProps<typeof CampoInterruptor>>) {
58
+ return (
59
+ <Controller
60
+ name={name}
61
+ control={control}
62
+ render={({ field: { value, onChange }, fieldState }) => (
63
+ <CampoInterruptor
64
+ {...props}
65
+ valor={Boolean(value)}
66
+ onCambiar={onChange}
67
+ error={fieldState.error?.message}
68
+ />
69
+ )}
70
+ />
71
+ );
72
+ }
73
+
74
+ export function FechaControlada<T extends FieldValues>({
75
+ control,
76
+ name,
77
+ ...props
78
+ }: CampoAtado<T> & SinValor<React.ComponentProps<typeof CampoDeFecha>>) {
79
+ return (
80
+ <Controller
81
+ name={name}
82
+ control={control}
83
+ render={({ field: { value, onChange }, fieldState }) => (
84
+ <CampoDeFecha
85
+ {...props}
86
+ valor={value ?? null}
87
+ onCambiar={onChange}
88
+ error={fieldState.error?.message}
89
+ />
90
+ )}
91
+ />
92
+ );
93
+ }
94
+
95
+ export function FechaHoraControlada<T extends FieldValues>({
96
+ control,
97
+ name,
98
+ ...props
99
+ }: CampoAtado<T> & SinValor<React.ComponentProps<typeof CampoDeFechaHora>>) {
100
+ return (
101
+ <Controller
102
+ name={name}
103
+ control={control}
104
+ render={({ field: { value, onChange }, fieldState }) => (
105
+ <CampoDeFechaHora
106
+ {...props}
107
+ valor={value ?? null}
108
+ onCambiar={onChange}
109
+ error={fieldState.error?.message}
110
+ />
111
+ )}
112
+ />
113
+ );
114
+ }
115
+
116
+ export function OpcionesControladas<T extends FieldValues, V extends string>({
117
+ control,
118
+ name,
119
+ ...props
120
+ }: CampoAtado<T> & SinValor<React.ComponentProps<typeof CampoDeOpciones<V>>>) {
121
+ return (
122
+ <Controller
123
+ name={name}
124
+ control={control}
125
+ render={({ field: { value, onChange }, fieldState }) => (
126
+ <CampoDeOpciones<V>
127
+ {...props}
128
+ valor={value ?? null}
129
+ onElegir={onChange}
130
+ error={fieldState.error?.message}
131
+ />
132
+ )}
133
+ />
134
+ );
135
+ }
136
+
137
+ export function SeleccionControlada<T extends FieldValues, O extends Opcion>({
138
+ control,
139
+ name,
140
+ ...props
141
+ }: CampoAtado<T> & SinValor<React.ComponentProps<typeof CampoDeSeleccion<O>>>) {
142
+ return (
143
+ <Controller
144
+ name={name}
145
+ control={control}
146
+ render={({ field: { value, onChange }, fieldState }) => (
147
+ <CampoDeSeleccion<O>
148
+ {...props}
149
+ valor={(value as O | null) ?? null}
150
+ onElegir={onChange}
151
+ error={fieldState.error?.message}
152
+ />
153
+ )}
154
+ />
155
+ );
156
+ }
157
+
158
+ export function TelefonoControlado<T extends FieldValues>({
159
+ control,
160
+ name,
161
+ ...props
162
+ }: CampoAtado<T> & SinValor<React.ComponentProps<typeof CampoDeTelefono>>) {
163
+ return (
164
+ <Controller
165
+ name={name}
166
+ control={control}
167
+ render={({ field: { value, onChange }, fieldState }) => (
168
+ <CampoDeTelefono
169
+ {...props}
170
+ valor={value ?? null}
171
+ onCambiar={onChange}
172
+ error={fieldState.error?.message}
173
+ />
174
+ )}
175
+ />
176
+ );
177
+ }
178
+
179
+ export function VariasControladas<T extends FieldValues, O extends Opcion>({
180
+ control,
181
+ name,
182
+ ...props
183
+ }: CampoAtado<T> & SinValor<React.ComponentProps<typeof CampoDeVarias<O>>>) {
184
+ return (
185
+ <Controller
186
+ name={name}
187
+ control={control}
188
+ render={({ field: { value, onChange }, fieldState }) => (
189
+ <CampoDeVarias<O>
190
+ {...props}
191
+ valor={(value as O[] | null) ?? []}
192
+ onElegir={onChange}
193
+ error={fieldState.error?.message}
194
+ />
195
+ )}
196
+ />
197
+ );
198
+ }