@agroshine/ags-mobile-ui-kit
v1.0.1
Published
AgroShine · librería de componentes React Native reutilizables (NativeWind + rn-primitives + lucide-react-native).
Downloads
254
Readme
@agroshine/ags-mobile-ui-kit
Librería de componentes React Native reutilizables de AgroShine. Es el
espejo móvil de @agroshine/ags-web-ui-kit: misma
paleta, mismos tokens semánticos, misma filosofía AgroShine — adaptada al
stack RN.
Tabla de contenidos
- Stack
- Instalación
- Setup en una app Expo / React Native
- Setup en Storybook (web)
- Estructura del paquete
- API pública
- Sistema de iconos
- Toasts (Toaster)
- Tokens y paleta
- Scripts
- Compatibilidad
- Publicación a npm
- Contribuir
- Licencia
Stack
- NativeWind 4 (sintaxis Tailwind v3) — clases utilitarias en React Native.
- React Native Reusables (
@rn-primitives/*) — primitives accesibles (Radix-style) para RN. - lucide-react-native — iconografía vectorial.
- jotai — estado del
Toaster. - react-native-builder-bob 0.40 — empaqueta a CJS, ESM y
.d.ts. - TypeScript estricto, ESLint flat config, Prettier.
Instalación
# pnpm
pnpm add @agroshine/ags-mobile-ui-kit
# yarn
yarn add @agroshine/ags-mobile-ui-kit
# npm
npm install @agroshine/ags-mobile-ui-kitPeer dependencies
El kit declara como peers (no las trae él mismo) las libs que tu app ya debería estar usando. Instala las que falten:
pnpm add nativewind tailwindcss@^3 tailwind-merge clsx \
class-variance-authority lucide-react-native \
react-native-gesture-handler react-native-reanimated \
react-native-safe-area-context react-native-svg \
@rn-primitives/slot@rn-primitives/* adicionales (accordion, dialog, popover, select,
tabs, tooltip, etc.) son opcionales — solo instala los que use el
componente que vayas a consumir.
jotai es opcional pero requerido si vas a usar el Toaster o el
toastStore del kit.
Setup en una app Expo / React Native
1. tailwind.config.js
Asegúrate de incluir el dist del kit en content y aplicar tanto el preset
de NativeWind como el del kit:
module.exports = {
content: [
'./App.{js,jsx,ts,tsx}',
'./index.{js,jsx,ts,tsx}',
'./app/**/*.{js,jsx,ts,tsx}',
'./node_modules/@agroshine/ags-mobile-ui-kit/**/*.{js,ts,jsx,tsx}',
],
presets: [
require('nativewind/preset'),
require('@agroshine/ags-mobile-ui-kit/tailwind.preset'),
],
};2. metro.config.js
const { getDefaultConfig } = require('expo/metro-config');
const { withNativeWind } = require('nativewind/metro');
const config = getDefaultConfig(__dirname);
module.exports = withNativeWind(config, {
input: './global.css', // tu propio CSS app
});3. global.css de la app
Re-exporta los tokens del kit (o cópialos) para que las variables HSL estén disponibles:
@import '@agroshine/ags-mobile-ui-kit/styles/global.css';
/* Y/o agrega tus propios overrides debajo. */4. babel.config.js
module.exports = function (api) {
api.cache(true);
return {
presets: [
['babel-preset-expo', { jsxImportSource: 'nativewind' }],
'nativewind/babel',
],
};
};5. Provider raíz (opcional pero recomendado)
Si vas a usar Toaster, monta SafeAreaProvider en la raíz:
import { SafeAreaProvider } from 'react-native-safe-area-context';
import { Toaster } from '@agroshine/ags-mobile-ui-kit';
export default function App() {
return (
<SafeAreaProvider>
{/* tu app */}
<Toaster position="bottom" />
</SafeAreaProvider>
);
}Setup en Storybook (web)
El kit incluye configuración de Storybook 8 (React + Vite) en
.storybook/ que renderiza componentes RN en el browser vía
react-native-web. Para arrancarlo localmente sobre el repo del kit:
pnpm storybook # http://localhost:6006
pnpm build-storybook # static buildDetalles del setup (alias react-native → react-native-web, stub para
react-native-safe-area-context, NativeWind via PostCSS) viven en
.storybook/main.ts y .storybook/preview.tsx. Si añades organisms que
toquen APIs nativas adicionales (TurboModules, codegen), recuerda
stubearlas en .storybook/mocks/ y excluirlas de optimizeDeps.
Estructura del paquete
src/
├── atoms/ Componentes opinionados AgroShine (Button, Input, …)
├── molecules/ Composiciones (FormField, Select, EntityCard, …)
├── organisms/ Composiciones de alto nivel (Layout, Drawer, Toaster, …)
├── ui/ Primitives rn-primitives-style (button, switch, …)
├── hooks/ Hooks compartidos (placeholder — en desarrollo)
├── lib/ Utilidades (cn, tailwindColors, toastStore)
├── foundation/ Catálogo visual (vacío — en desarrollo)
└── styles/
└── global.css Directivas Tailwind + tokens semánticos HSLSubpath exports
| Import path | Contenido |
| --------------------------------------------------- | ------------------------------- |
| @agroshine/ags-mobile-ui-kit | Barrel raíz (atoms + molecules + organisms + lib + hooks) |
| @agroshine/ags-mobile-ui-kit/atoms | Solo atoms |
| @agroshine/ags-mobile-ui-kit/molecules | Solo molecules |
| @agroshine/ags-mobile-ui-kit/organisms | Solo organisms |
| @agroshine/ags-mobile-ui-kit/ui | Primitives rn-primitives-style |
| @agroshine/ags-mobile-ui-kit/hooks | Hooks |
| @agroshine/ags-mobile-ui-kit/lib | cn, tailwindColors, toast atoms |
| @agroshine/ags-mobile-ui-kit/tailwind.preset | Preset de Tailwind |
| @agroshine/ags-mobile-ui-kit/styles/global.css | Stylesheet base + tokens HSL |
Los primitives bajo
/uino se re-exportan desde el barrel raíz para evitar colisión con sus wrappers opinionados.
API pública
Atoms
| Componente | Props clave |
| ------------ | --------------------------------------------------------------------------- |
| Button | variant (primary · secondary · outline · ghost · danger · success · link), size (sm/md/lg), loading, disabled, fullWidth |
| IconButton | Igual que Button + icon: IconName |
| Input | label, helperText, error, containerClassName, etc. (extiende TextInputProps) |
| TextArea | Igual que Input + multi-línea |
| Badge | tone (default · secondary · success · warning · danger · outline · muted) |
| Card | Wrapper + CardHeader, CardContent, CardFooter, CardTitle, CardDescription |
| Divider | orientation (horizontal/vertical) |
| Icon | name: IconName, size, className (color por Tailwind) |
| Snackbar | variant (default · success · info · warning · error), message, actionLabel, onActionPress |
| Spinner | Wrapper de ActivityIndicator |
| Text | variant (default · h1..h4 · p · blockquote · code · lead · large · small · muted) |
import { Button, Input, Badge } from '@agroshine/ags-mobile-ui-kit';
<Button variant="primary" loading={isSubmitting} onPress={save}>
Guardar
</Button>
<Input label="Correo" error={!!errors.email} helperText={errors.email} />
<Badge tone="success">Sincronizado</Badge>Molecules
| Componente | Descripción |
| ---------------------- | ------------------------------------------------------------ |
| ActionSheet | Bottom sheet modal con acciones |
| ConfirmActionSheet | ActionSheet con CTA destructiva + cancelar |
| CardButton | Card táctil con icono + label |
| EmptyState | Estado vacío (icono + título + descripción + CTA) |
| EntityCard | Card para listas (avatar/icon + título + subtítulo + actions) |
| FormField | Wrapper para inputs (label, required, error, helperText) |
| ObservationCard | Card de observación con estado (pending/synced/error) |
| SearchInput | Input con icono de búsqueda + clear |
| Select | Dropdown con búsqueda opcional (options, searchable) |
| SyncStatusBadge | Badge con estado de sync |
| TechnicalSheetButton | Botón que abre el TechnicalSheet organism |
import { FormField, Select } from '@agroshine/ags-mobile-ui-kit';
<FormField label="País" required error={errors.country}>
<Select
value={country}
options={[
{ label: 'Chile', value: 'CL' },
{ label: 'Perú', value: 'PE' },
]}
onSelect={setCountry}
searchable
/>
</FormField>Organisms
| Componente | Descripción |
| ----------------- | ---------------------------------------------------------- |
| Layout | Contenedor de pantalla (safe area + scroll opcional) |
| ScreenHeader | Header con back + título + acciones |
| Breadcrumbs | Migas de pan |
| BottomMenu | Menú inferior con acciones (observación, ficha, crear, etc.) |
| Drawer | Drawer lateral / modal sheet |
| TechnicalSheet | Ficha técnica de Block/Ship (datos de cultivo) |
| Toaster | Stack de toasts global (consume toastStore) |
| QueryState | Renderiza loading / error / empty / data para queries |
UI primitives (rn-primitives-style)
Wrappers shadcn-style, sin opinión de marca. Útiles para construir componentes nuevos:
badge · button · card · icon · input · separator · skeleton
· text · textarea
import { Button, buttonVariants } from '@agroshine/ags-mobile-ui-kit/ui';
// con variants para construir tus propios componentes
<Pressable className={buttonVariants({ variant: 'outline', size: 'lg' })}>
<Text>Custom</Text>
</Pressable>Lib utils
import { cn, addToastAtom, dismissToastAtom, toastsAtom }
from '@agroshine/ags-mobile-ui-kit/lib';
// cn: clsx + tailwind-merge
<View className={cn('p-4', isActive && 'bg-primary-50', extraClass)} />Sistema de iconos
El atom Icon mapea nombres semánticos a iconos de lucide-react-native.
Las pantallas y componentes propios deben usar Icon y nunca importar
lucide-react-native directamente (esto centraliza el catálogo y permite
sustituir el set de iconos en el futuro).
import { Icon } from '@agroshine/ags-mobile-ui-kit';
<Icon name="checkCircle" size={20} className="text-success" />Nombres disponibles (extracto):
alert · archive · arrowLeft · arrowRight · check · checkCircle ·
checkSquare · chevronDown · chevronLeft · chevronRight ·
chevronUp · circle · add · edit · eye · eyeOff · fileText ·
filter · home · info · list · listChecks · logout · menu ·
more · pencil · plus · refresh · save · search · send ·
settings · square · trash · upload · close
El catálogo completo está en src/atoms/Icon/icons.ts. Si pasas un nombre
desconocido y __DEV__ está activo verás un warning en consola; el render
cae a menu como fallback seguro.
Toasts (Toaster)
El Toaster es un organism global respaldado por átomos de Jotai
(toastStore). Mismo enfoque shadcn/sonner pero RN-native.
// App.tsx
import { Toaster } from '@agroshine/ags-mobile-ui-kit';
<Toaster position="bottom" visibleToasts={3} />// Cualquier pantalla
import { useSetAtom } from 'jotai';
import { addToastAtom } from '@agroshine/ags-mobile-ui-kit/lib';
const showToast = useSetAtom(addToastAtom);
showToast({
message: 'Observación guardada',
variant: 'success',
duration: 4000, // opcional, default 4000
actionLabel: 'Deshacer', // opcional
onActionPress: () => undo(),
});Variantes (heredadas de Snackbar): default · success · info ·
warning · error.
Tokens y paleta
La paleta de marca (default · primary · secondary · success ·
warning · danger · grey, escala completa 50→900 + DEFAULT +
foreground, además de content1-content4, focus, overlay) es
idéntica a la del web kit (ags-web-ui-kit/src/styles/tokens.css).
Vive en tailwind.preset.cjs con sintaxis Tailwind v3 porque NativeWind 4
aún no soporta el @theme de v4.
Los tokens semánticos shadcn-compatibles (--background, --border,
--ring, --muted, --accent, --popover, --card, --destructive,
--input, --radius) viven en src/styles/global.css como variables HSL.
El soporte de .dark:root está activo — para alternarlo, agrega la clase
dark a la raíz desde tu app.
Acceso programático desde JS (útil para style={} o componentes que no
aceptan className):
import { tailwindColors } from '@agroshine/ags-mobile-ui-kit/lib';
<ActivityIndicator color={tailwindColors.primary.DEFAULT} />Scripts
| Comando | Descripción |
| ---------------------- | ---------------------------------------------------------- |
| pnpm build | Compila con react-native-builder-bob (CJS + ESM + d.ts) |
| pnpm clean | Borra lib/ |
| pnpm typecheck | tsc --noEmit |
| pnpm lint | ESLint sobre src/ |
| pnpm lint:fix | ESLint con --fix |
| pnpm format | Prettier sobre src/ |
| pnpm format:check | Prettier en modo check |
| pnpm storybook | Levanta Storybook web (vite + RN-Web) en :6006 |
| pnpm build-storybook | Build estático de Storybook |
| pnpm commit | Commitizen interactivo (cz-customizable) |
| pnpm release | semantic-release |
Compatibilidad
- Node ≥ 20
- pnpm ≥ 10 (es el package manager del kit; las apps consumidoras pueden usar yarn/npm)
- React 18 o 19
- React Native ≥ 0.73 (probado con 0.81.x)
- Expo SDK 50+ (NativeWind 4 requiere Reanimated 3+)
- NativeWind 4.x
- Tailwind CSS 3.4+
⚠️ El kit se construye y publica con
react-native-builder-bob 0.40. Los tipos viven enlib/typescript/{commonjs,module}/src/**. Si forkeas el kit y cambiasbob, verifica que el campoexports.<sub>.import.typesy.require.typessiga apuntando a archivos existentes (correpnpm build && npm pack --dry-run).
Publicación a npm
El paquete está bajo el scope público @agroshine. Para publicar:
pnpm typecheck # debe pasar
pnpm lint # debe pasar
pnpm build # genera lib/
npm pack --dry-run # revisa contenido del tarball
# bump de versión (semantic-release lo hace automático en CI):
npm version patch | minor | major
npm publish --access publicLa automatización vía pnpm release usa semantic-release con changelog,
git tag, npm publish y GitHub release.
Contribuir
- Crea una rama desde
main. pnpm install(Node ≥ 20, pnpm ≥ 10).- Añade tu componente bajo el nivel correcto (
atoms/molecules/organisms) con suindex.ts,Component.tsx,Component.types.tsyComponent.stories.tsx. - Re-exporta desde el barrel del nivel.
pnpm typecheck && pnpm lint && pnpm build.pnpm commitpara un commit Conventional via Commitizen.- Abre PR.
Convenciones:
- Nunca importes
lucide-react-nativedirecto desde un componente del kit — usa el atomIcony agrega el icono al catálogo si hace falta. - Nunca importes desde pantallas de las apps — el kit es self-contained.
- Estilos solo vía
className(NativeWind). Evitastyle={{ ... }}salvo para valores no expresables en Tailwind. - Props con
Propssuffix en sus types files (ButtonProps). - Si tu componente necesita un primitive nuevo, agrégalo a
src/ui/primero y luego envuélvelo en el atom/molecule/organism.
Licencia
MIT © AgroShine
