npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@acvl/mobile-components

v0.0.9

Published

Librería de componentes de las apps móviles de ACV (Expo / React Native).

Readme

@acvl/mobile-components

La librería de componentes de las apps móviles de ACV (Expo / React Native): tms, wms y suma. Es el equivalente de @acvl/frontend-components para las apps: lo que se ve igual en todas vive aquí, y cada app pone sólo lo suyo (su API, su dominio).

El punto de partida es la app de tms, la más reciente. Su diseño es para quien usa la app de pie y con prisa: letra grande (nada por debajo de tipografia.etiqueta), nada importante recortado a un renglón, y todo listo para la letra del teléfono al 180%.

Qué trae

| Área | Qué | | --------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | Tema | ProveedorDeTema, usePreferenciaDeTema, PALETA_CLARA/PALETA_OSCURA, usePaleta, crearEstilos, useEsOscuro, tipografia, renglon, Text, TextInput | | Componentes | BotonFlotante (con HUECO_DEL_BOTON_FLOTANTE), BotonGrande, Chip (con su tono morado), EstadoVacio, IconoDeRenglon, Nota, OpcionesExcluyentes, Pestanas, Rotulo, RotuloDeSeccion (con su accion), Tarjeta, TarjetaDeDatos, Logo, LogoCompleto, ContenidoDePantalla | | Navegación | useOpcionesDelEncabezado (el header de las pestañas), BarraDePestanas, CabeceraEmpujada, AccionesDelEncabezado (con insignia), BotonDeAvatar | | Diálogos | mostrarDialogo, confirmar, avisar y su HostDeDialogos | | Formularios | CampoDeTexto, CampoDeFecha, CampoDeFechaHora, CampoInterruptor, CampoDeOpciones, CampoDeTelefono, CampoDeSeleccion (del servidor o de una lista fija), CampoDeVarias, SelectorDeOpcion, SelectorDeVarias, Campo (el marco de un campo propio; con accesorio junto al rótulo), CajaDeCampo (la caja con el rótulo adentro que usan todos), resaltarVacio en el texto y los selectores, sus …Controlado de react-hook-form, TextoDeAccesoControlado (el login), useGuardado, soloLoQueCambio, mensajeDeError, setErrors, useBusqueda | | Listas y fichas | ListaPaginada (con Pagina y renglonesDe), CabeceraDeLista, Buscador, TarjetaDeLista, TarjetaDeslizable, HojaDeFiltros y sus opciones (OrdenDeLista nombra los sentidos de cada campo), SeccionDeFicha, RenglonDeLiga, FilaConContador, RenglonDeContacto, RenglonDeDireccion (abre el mapa del teléfono), RenglonDeHoja, EstadoDeHoja | | Idioma | ProveedorDeIdioma (es o en; sin él, español), useTextos, textosActuales, useIdiomaDeLaLibreria | | Fotos | Foto, ProveedorDeFotos, TiraDeFotos, VisorDeFoto | | Pantallas | PantallaDeAcceso (el login), PantallaDeFicha, PantallaDeHistorial (la bitácora de un registro, con TarjetaDeEvento), EsperaDeEdicion/FormularioDeEdicion, PantallaDePerfil, PantallaDeError |

Y tres entradas aparte, para no obligar a cada app a instalar lo que no usa:

| Entrada | Qué | Pide | | ---------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------ | --------------------------------------------------------------------------------------------- | | @acvl/mobile-components/camara | tomarFoto (con alTomar para guardar la original), elegirDeGaleria, pedirPermisoDeCamara, reducirFoto, y CampoDeFotos / FotosControladas, las fotos de un formulario (las nuevas y las ya guardadas) | expo-image-picker, expo-image-manipulator | | @acvl/mobile-components/fotos | Todo lo de camara, más elegirDocumentos y elegirAdjunto (cámara, galería o un documento) | expo-image-picker, expo-image-manipulator, expo-document-picker, expo-file-system | | @acvl/mobile-components/biometria | useBiometriaDeAcceso (entrar con Face ID o huella en PantallaDeAcceso), useBiometriaGuardada y DejarDeUsarBiometria (para el perfil), el servicio biometria | expo-local-authentication, expo-secure-store | | @acvl/mobile-components/hoja | HojaInferior, useMasDeLaFicha (la sección «Más» de una ficha: renglones con contador que abren su hoja), HojaDeLista (una lista con buscador, scroll infinito y sus acciones, que sube sobre la ficha) | @gorhom/bottom-sheet |

Cada componente lleva su documentación en el código: por qué es como es, y qué se probó antes.

Instalar

npm install @acvl/mobile-components

Lo que la librería usa lo pone la app (peerDependencies): react-native-paper, expo-router, react-native-reanimated, react-native-safe-area-context, react-native-keyboard-controller, react-native-gesture-handler, @expo/vector-icons, @react-native-community/datetimepicker, react-hook-form, dayjs, expo-haptics, expo-status-bar y expo-system-ui. Sirve desde el SDK 54 de Expo.

Las fotos del servidor

Si las fotos del servidor piden la sesión, la app dice cómo bajarlas en el layout raíz: en Android el Image no manda las headers, así que se bajan con fetch y se pinta el archivo.

<ProveedorDeFotos bajar={(ruta) => bajarConMiToken(ruta) /* → la uri local */}>

Sin proveedor, la ruta de una foto se pinta tal cual, como URL pública.

La tipografía de Android

Android va con Inter (iOS se queda con San Francisco; ver src/tema/fuentes.ts). Los archivos vienen en el paquete y cada app los registra en su app.json:

["expo-font", {
    "android": {
        "fonts": [{
            "fontFamily": "Inter",
            "fontDefinitions": [
                { "path": "./node_modules/@acvl/mobile-components/assets/fonts/Inter_400Regular.ttf", "weight": 400 },
                { "path": "./node_modules/@acvl/mobile-components/assets/fonts/Inter_500Medium.ttf", "weight": 500 },
                { "path": "./node_modules/@acvl/mobile-components/assets/fonts/Inter_600SemiBold.ttf", "weight": 600 },
                { "path": "./node_modules/@acvl/mobile-components/assets/fonts/Inter_700Bold.ttf", "weight": 700 },
                { "path": "./node_modules/@acvl/mobile-components/assets/fonts/Inter_800ExtraBold.ttf", "weight": 800 },
                { "path": "./node_modules/@acvl/mobile-components/assets/fonts/Inter_900Black.ttf", "weight": 900 }
            ]
        }]
    }
}]

Cambiar esa lista obliga a reconstruir el binario.

Face ID o huella

@acvl/mobile-components/biometria guarda el usuario y la contraseña en el llavero, atados a la biometría del teléfono, y los manda al mismo login de la app (no hay nada en el backend). Pide expo-local-authentication y expo-secure-store, módulos nativos: instalarlos o cambiar sus plugins obliga a un binario nuevo; un update OTA no basta. Los dos plugins llevan el texto del permiso de Face ID en el app.json:

["expo-secure-store", { "faceIDPermission": "Usamos Face ID para que entres a la app sin escribir tu contraseña." }],
["expo-local-authentication", { "faceIDPermission": "Usamos Face ID para que entres a la app sin escribir tu contraseña." }]

En el login, el hook da la prop de la pantalla y la pregunta de guardar. La app la hace tras entrar de verdad (no en una contraseña temporal, ni en una cuenta sin acceso a esta app), y marca su 401 con credencialesRechazadas: con lo guardado, quiere decir que la contraseña cambió, y el hook lo olvida y lo dice.

import { useBiometriaDeAcceso, type ResultadoDeEntrar } from "@acvl/mobile-components/biometria";

const { biometria, ofrecerGuardar } = useBiometriaDeAcceso({ entrar: (datos) => entrar(datos) });

const entrar = async (datos: DatosDeAcceso): Promise<ResultadoDeEntrar> => {
    try {
        await login(datos).unwrap();
        void ofrecerGuardar(datos);
        return null;
    } catch (err: any) {
        if (err?.status === 401) return { mensaje: "Usuario o contraseña incorrectos", credencialesRechazadas: true };
        return { mensaje: "No se pudo iniciar sesión" };
    }
};

<PantallaDeAcceso nombre="TMS" onEntrar={entrar} biometria={biometria} />

En el perfil, «Dejar de usar…» (sólo sale si lo guardado es de quien está en sesión):

<PantallaDePerfil rotulo="Operador" nombre={user.name} onCerrarSesion={salir}>
    <DejarDeUsarBiometria usuario={user.username} />
</PantallaDePerfil>

Donde la contraseña cambie sin pasar por el login (restablecerla), la app llama a biometria.olvidar(): lo guardado tenía la vieja.

Armar una app

El layout raíz monta el tema y los diálogos, y exporta la pantalla de error:

import { HostDeDialogos, PantallaDeError, ProveedorDeTema } from "@acvl/mobile-components";
import * as SecureStore from "expo-secure-store";

const almacenDelTema = {
    leer: () => SecureStore.getItemAsync("TEMA"),
    guardar: (tema) => SecureStore.setItemAsync("TEMA", tema),
};

export const ErrorBoundary = (props) => (
    <PantallaDeError {...props} onError={(error) => Sentry.captureException(error)} />
);

export default function RootLayout() {
    return (
        <ProveedorDeTema almacen={almacenDelTema}>
            <Stack screenOptions={{ headerShown: false }} />
            <HostDeDialogos />
        </ProveedorDeTema>
    );
}

Los textos de la librería (el login, el perfil, las fichas, las listas, los diálogos) salen en español. Para inglés, la app envuelve todo en ProveedorDeIdioma con el idioma ya resuelto (de dónde sale —el teléfono, el token, lo que eligió el usuario— es asunto de la app); avisar y confirmar lo siguen aunque se llamen fuera de un componente:

<ProveedorDeIdioma idioma={idioma}>
    <Stack screenOptions={{ headerShown: false }} />
</ProveedorDeIdioma>

Las fechas de CampoDeFecha usan el locale global de dayjs, que también pone la app.

Las pestañas toman el header y la barra de la casa:

const encabezado = useOpcionesDelEncabezado({
    eyebrow: "Operador",
    derecha: <BotonDeAvatar iniciales="JP" onPress={() => router.push("/perfil")} />,
});

<Tabs screenOptions={encabezado} tabBar={(props) => <BarraDePestanas {...props} iconos={{ index: "assignment" }} />}>

Y una pantalla toma sus colores de la paleta, nunca hexadecimales:

const useStyles = crearEstilos((c) => ({
    tarjeta: { backgroundColor: c.superficie, borderRadius: 14, padding: 14 },
    titulo: { ...renglon(tipografia.subtitulo), fontWeight: "800", color: c.texto },
}));

Desarrollar

npm install
npm run typecheck
npm test          # la lógica pura: diálogos, errores del servidor, biometría
npm run build     # bob: lib/module y lib/typescript
npm run format

Para ver un cambio en una app sin publicar, la app de tms tiene npm run start:lib: Metro sirve src/ de este checkout (../../acv-mobile-components) en lugar del paquete de node_modules. Tiene que estar junto a la app, en la misma carpeta que acv-tms.

Publicar

Igual que la del portal: cada push a main corre el typecheck y las pruebas, sube el patch si la versión ya está en npm, publica y hace commit y tag del bump. Un bump a mano (un minor) se respeta. Necesita el secret NPM_TOKEN en el repo.