mobile-design 0.1.0 → 0.1.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 (3) hide show
  1. package/COMPONENTS.md +3776 -0
  2. package/README.md +1257 -6
  3. package/package.json +3 -2
package/README.md CHANGED
@@ -4,6 +4,10 @@
4
4
 
5
5
  Componentes Material Design 3 para React Native y React Native Web. El paquete integra el tema, las fuentes y los componentes sobre React Native Paper.
6
6
 
7
+ [Instalación](#instalación-desde-npm) · [Primer componente](#primer-componente) · [Proveedor](#configurar-el-proveedor) · [Temas y colores](#temas-y-colores) · [Iconos](#tipografía-e-iconos) · [Componentes](#referencia-de-componentes)
8
+
9
+ La referencia detallada de propiedades, configuraciones y ejemplos se incluye como `COMPONENTS.md` en el paquete. [Referencia completa de componentes en la versión 0.1.1](https://cdn.jsdelivr.net/npm/mobile-design@0.1.1/COMPONENTS.md)
10
+
7
11
  ## Instalación desde npm
8
12
 
9
13
  En una app Expo, usa Metro y las versiones de React y React Native de tu SDK. El paquete distribuye TypeScript y Metro resuelve los archivos específicos de cada plataforma. La configuración verificada usa Expo `58.0.17`, React Native `0.88.0`, React `19.3.0` y la versión preliminar `6.0.0-alpha.0` de React Native Paper.
@@ -15,10 +19,10 @@ npx expo install expo-font@58.0.10 expo-image@58.0.16 \
15
19
  react-native-gesture-handler@3.3.0 react-native-reanimated@4.7.1 \
16
20
  react-native-worklets@0.13.0 react-native-safe-area-context@5.10.1 \
17
21
  react-native-svg@15.16.0
18
- npm install mobile-design@0.1.0 react-native-paper@6.0.0-alpha.0 @gorhom/bottom-sheet@5.2.14
22
+ npm install mobile-design react-native-paper@6.0.0-alpha.0 @gorhom/bottom-sheet@5.2.14
19
23
  ```
20
24
 
21
- Para React Native Web, usa Metro e instala las dependencias del navegador antes de iniciar Expo.
25
+ Para React Native Web, configura `expo.web.bundler` como `"metro"` en `app.json` o su equivalente en la configuración dinámica. Instala las dependencias del navegador antes de iniciar Expo.
22
26
 
23
27
  ```sh
24
28
  npx expo install react-dom@19.3.0 react-native-web@0.21.4 @expo/metro-runtime@58.0.17
@@ -27,7 +31,31 @@ npx expo start --web
27
31
 
28
32
  `mobile-design` instala `@mobile-design/tokens` como dependencia e incluye Roboto y los símbolos MD3 que necesitan los componentes. Con estas versiones se verificaron TypeScript, la exportación de Metro en Android, iOS y web, y el renderizado de fuentes y símbolos en Chromium con dependencias públicas.
29
33
 
30
- ## Uso
34
+ | Entrada | Cuándo usarla |
35
+ | --- | --- |
36
+ | `mobile-design/react-native` | Android e iOS. |
37
+ | `mobile-design/react-native-web` | React Native Web. |
38
+ | `@mobile-design/tokens` | Medidas, interlineado, estados y resortes en código propio. |
39
+ | `@mobile-design/tokens/colors` | Roles de color claros y oscuros ya generados. |
40
+ | `@mobile-design/icons/react-native` y `@mobile-design/icons/react-native-web` | Colección completa de iconos, instalable por separado. |
41
+
42
+ Las dos entradas de componentes ofrecen la misma API. En un proyecto multiplataforma puedes elegir la entrada con un módulo `.ts` para nativo y otro `.web.ts` para navegador. La guía de cada paquete profundiza en sus datos y opciones. [Tokens](https://www.npmjs.com/package/@mobile-design/tokens) · [Catálogo de iconos](https://www.npmjs.com/package/@mobile-design/icons)
43
+
44
+ Si mantienes una sola pantalla para las dos plataformas, crea `ui.ts` junto a `ui.web.ts` y desde la pantalla importa los componentes desde `./ui`. Exporta solo lo que necesite tu app.
45
+
46
+ `ui.ts`
47
+
48
+ ```ts
49
+ export { Button, MobileDesignProvider, TextField } from 'mobile-design/react-native';
50
+ ```
51
+
52
+ `ui.web.ts`
53
+
54
+ ```ts
55
+ export { Button, MobileDesignProvider, TextField } from 'mobile-design/react-native-web';
56
+ ```
57
+
58
+ ## Primer componente
31
59
 
32
60
  Usa `mobile-design/react-native` en Android e iOS y `mobile-design/react-native-web` en el navegador. Monta la app dentro de `GestureHandlerRootView`, `SafeAreaProvider` y `MobileDesignProvider`.
33
61
 
@@ -55,23 +83,193 @@ export default function App() {
55
83
  }
56
84
  ```
57
85
 
58
- `MobileDesignProvider` sigue el esquema claro u oscuro del sistema. Puedes fijarlo con `colorScheme`.
86
+ `TextField` es controlado. El estado `name` es la fuente del valor, `onChangeText` recibe el nuevo texto y `Button.onPress` lo limpia. Puedes pegar este ejemplo en `App.tsx` y cambiar el import a `mobile-design/react-native-web` para el navegador.
87
+
88
+ ## Configurar el proveedor
89
+
90
+ Monta un `MobileDesignProvider` en la raíz para compartir el tema, la dirección de lectura y los portales. `GestureHandlerRootView` da soporte a los gestos y `SafeAreaProvider` permite a las barras y hojas respetar los bordes de la pantalla. Mantén ambos por fuera del proveedor.
91
+
92
+ | Propiedad | Valores y comportamiento |
93
+ | --- | --- |
94
+ | `children` | Pantallas y componentes que consumen el tema y el host de `Portal`. |
95
+ | `colorScheme` | `'light'` o `'dark'`. Si se omite, el proveedor sigue el esquema del sistema y usa claro cuando el sistema no declara modo oscuro. |
96
+ | `theme` | Tema parcial de Paper. Fusiona por rol objetos como `colors`, `fonts`, `shapes`, `elevation` y `motion`. `theme.dark` explícito prevalece sobre `colorScheme`. |
97
+ | `fontFamily` | Nombre de una familia registrada. Por defecto usa `'Roboto'`. Aplica la familia a la escala tipográfica y puedes ajustar un estilo concreto con `theme.fonts`. |
98
+ | `languageHeight` | `'small'`, `'medium'`, `'large'` o `'extraLarge'`. El valor inicial es `'small'`. Cambia el interlineado de los estilos MD3 base y enfatizados. |
99
+ | `motion` | `'expressive'` o `'standard'`. El valor inicial es `'expressive'`. Selecciona el perfil de los resortes espaciales. |
100
+ | `reduceMotion` | `'auto'`, `'on'` u `'off'`. El valor inicial es `'auto'` y sigue la preferencia del sistema. |
101
+ | `direction` | `'ltr'` o `'rtl'`. Si se omite, Paper usa la dirección del entorno. |
102
+ | `settings.icon` | Renderizador para iconos pedidos por nombre. Por defecto usa los Material Symbols del paquete base. |
103
+ | `settings.rippleEffectEnabled` | Habilita el ripple en los controles que consultan este ajuste. Paper lo habilita de forma predeterminada. |
104
+
105
+ El proveedor carga las fuentes predeterminadas que necesita el tema o el renderizador elegido. Antes de la primera carga completa puede mostrar el árbol vacío. Registra las caras y pesos de una fuente personalizada en tu app con `expo-font` antes de pasar su `fontFamily`.
106
+
107
+ `createMobileDesignTheme(options)` permite construir el mismo tema fuera de un componente. Sus opciones son `colorScheme`, `theme`, `fontFamily`, `motion` y `languageHeight`. La función directa usa claro si omites `colorScheme`. `useMobileTheme()` lee el tema de la pantalla actual. `useMobileTheme(overrides)` permite fusionar ajustes para el componente que llama al hook.
108
+
109
+ ## Temas y colores
110
+
111
+ ### Elegir roles para la interfaz
112
+
113
+ La paleta instalada es MD3 Baseline. El preset `md3-baseline` incluye 58 roles por modo y conserva `#6750A4` como referencia de origen. Cambiar a oscuro selecciona otro conjunto completo de roles. Por ejemplo, `primary` es `#6750A4` en claro y `#D0BCFF` en oscuro.
114
+
115
+ | Roles | Uso |
116
+ | --- | --- |
117
+ | `primary`, `onPrimary` | Fondo de acción principal y color de texto o icono encima. |
118
+ | `primaryContainer`, `onPrimaryContainer` | Superficie de énfasis de la familia principal y su contenido. |
119
+ | `secondary*`, `tertiary*` | Acentos secundarios y terciarios, con sus respectivos pares `on…`. |
120
+ | `surface`, `onSurface`, `onSurfaceVariant` | Fondo de pantalla y contenidos de énfasis alto o secundario. |
121
+ | `surfaceContainerLowest` a `surfaceContainerHighest` | Niveles de superficie para diferenciar tarjetas, paneles y regiones. |
122
+ | `outline`, `outlineVariant` | Bordes y separadores con dos niveles de énfasis. |
123
+ | `error`, `onError`, `errorContainer`, `onErrorContainer` | Estados de error y sus colores de contenido. |
124
+ | `inverseSurface`, `inverseOnSurface`, `inversePrimary` | Superficie, contenido y acento de regiones con contraste invertido. |
125
+ | `elevation.level0` a `elevation.level5` | Roles anidados de elevación en el tema de los componentes. |
126
+
127
+ Los roles `on…` son colores para el contenido que va encima de su fondo correspondiente. Usa el par previsto y revisa su contraste en claro y oscuro. [Roles de color de Material Design](https://m3.material.io/styles/color/the-color-system/)
128
+
129
+ ### Personalizar un tema claro y oscuro
130
+
131
+ Pasa los roles que cambian mediante `theme.colors`. El proveedor conserva los demás roles del modo elegido. Cambiar `primary` por sí solo no calcula `onPrimary`, los contenedores ni el modo oscuro.
132
+
133
+ ```tsx
134
+ import { useColorScheme, View } from 'react-native';
135
+ import {
136
+ Button,
137
+ MobileDesignProvider,
138
+ Text,
139
+ useMobileTheme,
140
+ type MobileDesignTheme,
141
+ } from 'mobile-design/react-native';
142
+
143
+ const brandColors = {
144
+ light: {
145
+ primary: '#005AC1',
146
+ onPrimary: '#FFFFFF',
147
+ primaryContainer: '#D8E2FF',
148
+ onPrimaryContainer: '#001A41',
149
+ },
150
+ dark: {
151
+ primary: '#ADC6FF',
152
+ onPrimary: '#002E69',
153
+ primaryContainer: '#004494',
154
+ onPrimaryContainer: '#D8E2FF',
155
+ },
156
+ } satisfies Record<'light' | 'dark', Partial<MobileDesignTheme['colors']>>;
157
+
158
+ function Content() {
159
+ const { colors } = useMobileTheme();
160
+ return (
161
+ <View style={{ flex: 1, backgroundColor: colors.surface, padding: 16, gap: 12 }}>
162
+ <Text variant="titleLarge" style={{ color: colors.onSurface }}>
163
+ Preferencias
164
+ </Text>
165
+ <Button variant="filled" onPress={() => {}}>Guardar</Button>
166
+ </View>
167
+ );
168
+ }
169
+
170
+ export default function BrandTheme() {
171
+ const mode = useColorScheme() === 'dark' ? 'dark' : 'light';
172
+ return (
173
+ <MobileDesignProvider
174
+ colorScheme={mode}
175
+ theme={{ colors: brandColors[mode], fonts: { titleLarge: { fontWeight: '700' } } }}
176
+ motion="standard"
177
+ >
178
+ <Content />
179
+ </MobileDesignProvider>
180
+ );
181
+ }
182
+ ```
183
+
184
+ Usa `BrandTheme` en el lugar que ocupa `MobileDesignProvider` en el primer ejemplo, dentro de los dos proveedores raíz. Para fijar un modo en una pantalla de configuración, puedes sustituir `useColorScheme()` por la preferencia guardada por tu app.
185
+
186
+ ### Aplicar una paleta creada desde un color semilla
187
+
188
+ El paquete npm trae las paletas ya calculadas. Su API aplica roles y combina temas. La generación de una paleta a partir de un hex, una foto o el fondo de pantalla se realiza fuera de esa API.
189
+
190
+ 1. Elige un color de origen en [Material Theme Builder](https://material-foundation.github.io/material-theme-builder/) y obtén los esquemas claro y oscuro.
191
+ 2. Guarda los roles de cada modo en la app con nombres como `primary`, `onPrimary` y `surfaceContainer`. Completa los roles adicionales con la base de `@mobile-design/tokens/colors`.
192
+ 3. Si partes del formato plano de tokens, llama a `createMobileDesignColors(roles)` antes de pasar los colores al tema. El adaptador reúne los seis roles `elevationLevel0` a `elevationLevel5` bajo `colors.elevation`. Si ya tienes los roles con la estructura de `theme.colors`, pásalos directamente.
193
+
194
+ El ejemplo toma los cuatro roles primarios por modo que devuelve el generador del workspace para la semilla `#246B57` y conserva la base para los demás roles. Para aplicar una marca a toda la paleta, sustituye los mapas completos `light` y `dark` por los generados para esa marca.
195
+
196
+ ```ts
197
+ import materialColors from '@mobile-design/tokens/colors';
198
+ import { createMobileDesignColors, createMobileDesignTheme } from 'mobile-design/react-native';
199
+
200
+ const brandPrimaryRoles = {
201
+ light: {
202
+ primary: '#006B54',
203
+ onPrimary: '#FFFFFF',
204
+ primaryContainer: '#80F8D2',
205
+ onPrimaryContainer: '#002118',
206
+ },
207
+ dark: {
208
+ primary: '#61DBB7',
209
+ onPrimary: '#00382B',
210
+ primaryContainer: '#00513F',
211
+ onPrimaryContainer: '#80F8D2',
212
+ },
213
+ };
214
+
215
+ export function createBrandTheme(mode: 'light' | 'dark') {
216
+ const roles = { ...materialColors.appColorTokens[mode], ...brandPrimaryRoles[mode] };
217
+ return createMobileDesignTheme({
218
+ colorScheme: mode,
219
+ theme: { colors: createMobileDesignColors(roles) },
220
+ });
221
+ }
222
+ ```
223
+
224
+ Pasa `theme={createBrandTheme(mode)}` al proveedor, donde `mode` viene del sistema o de la preferencia guardada por tu app. Para ofrecer varios temas de marca, conserva un mapa de roles por marca y modo y usa la marca activa al construir el tema. `createMobileDesignColors` adapta roles existentes y espera un registro completo de tokens. En particular, `surfaceDisabled`, `onSurfaceDisabled` y `backdrop` son roles planos de compatibilidad que el adaptador de Paper no incorpora.
225
+
226
+ En el workspace de desarrollo existe el comando `pnpm theme:generate '#246B57'`. Genera las dos paletas mediante Material Color Utilities y escribe el módulo de colores, sus tipos y el bloque CSS generado del workspace. `pnpm theme:generate md3-baseline` restaura la paleta MD3 publicada y `pnpm theme:generate neutral` genera el preset neutro. Estos comandos corresponden al código fuente del workspace. En una app que instala el paquete usa los roles de tokens, el exportador externo y las APIs de tema anteriores.
59
227
 
60
228
  ## Tipografía e iconos
61
229
 
62
230
  Roboto es la tipografía predeterminada. El proveedor usa un subconjunto de Material Symbols Rounded que cubre los controles y ejemplos de integración. El proveedor carga las fuentes predeterminadas que utiliza el tema o el renderizador activo. Para elegir otra tipografía, registra su familia y los pesos que utilices en tu app y pasa, por ejemplo, `fontFamily="Inter"`.
63
231
 
232
+ Agrupa las caras de una misma familia para que Expo pueda elegir el peso correcto. Coloca los archivos de tu fuente en la app y adapta las rutas del ejemplo. Monta `CustomTypeface` en el lugar que ocupa el proveedor en el primer ejemplo.
233
+
234
+ ```tsx
235
+ import { useFonts } from 'expo-font';
236
+ import { MobileDesignProvider, Text } from 'mobile-design/react-native';
237
+
238
+ const fonts = [
239
+ {
240
+ fontFamily: 'Inter',
241
+ fontDefinitions: [
242
+ { path: require('./assets/fonts/Inter-Regular.ttf'), weight: 400 },
243
+ { path: require('./assets/fonts/Inter-Bold.ttf'), weight: 700 },
244
+ ],
245
+ },
246
+ ] satisfies Parameters<typeof useFonts>[0];
247
+
248
+ export function CustomTypeface() {
249
+ const [ready, error] = useFonts(fonts);
250
+ if (error) throw error;
251
+ if (!ready) return null;
252
+
253
+ return (
254
+ <MobileDesignProvider fontFamily="Inter">
255
+ <Text variant="bodyLarge">Contenido</Text>
256
+ <Text variant="titleLarge" style={{ fontWeight: '700' }}>Encabezado</Text>
257
+ </MobileDesignProvider>
258
+ );
259
+ }
260
+ ```
261
+
64
262
  Para cambiar los iconos por nombre en toda la app, pasa `settings={{ icon: renderIcon }}` al proveedor. Tu renderizador recibe `name`, `size`, `color`, `direction` y `testID`. Traduce los nombres que usan los componentes a los de tu conjunto. Por ejemplo, Paper envía `magnify` y `check`, que en Ionicons se llaman `search` y `checkmark`. La app instala el conjunto que elija.
65
263
 
66
264
  Si quieres usar la colección completa, instala `@mobile-design/icons` en la misma versión que `mobile-design`.
67
265
 
68
266
  ```sh
69
- npm install @mobile-design/icons@0.1.0
267
+ npm install @mobile-design/icons
70
268
  ```
71
269
 
72
270
  Importa `MaterialSymbol` o `renderMaterialIcon` desde `@mobile-design/icons/react-native` en Android e iOS, o desde `@mobile-design/icons/react-native-web` en el navegador. Pasa el renderizador de esa entrada a `settings.icon` para aplicar la colección completa a los iconos por nombre. La fuente se carga al renderizar un símbolo. También puedes precargarla con `useFonts([mobileDesignSymbolsFont])` de `expo-font`.
73
271
 
74
- Para cambiar el icono de un solo control, pasa una función al prop `icon`. Este ejemplo usa Ionicons. Instala `@expo/vector-icons` en la app para usarlo. [Catálogo de Expo](https://icons.expo.fyi/Index)
272
+ Para cambiar el icono de un solo control, pasa una función al prop `icon`. Este ejemplo usa Ionicons. Instala su dependencia opcional con `npx expo install @expo/vector-icons`. [Catálogo de Expo](https://icons.expo.fyi/Index)
75
273
 
76
274
  ```tsx
77
275
  import Ionicons from '@expo/vector-icons/Ionicons';
@@ -88,6 +286,1059 @@ export function CloseButton({ onClose }: { onClose: () => void }) {
88
286
  }
89
287
  ```
90
288
 
289
+ ## Referencia de componentes
290
+
291
+ Los ejemplos se montan bajo el proveedor de la instalación. Importan desde `mobile-design/react-native`. En un archivo específico del navegador, usa `mobile-design/react-native-web`.
292
+
293
+ El archivo `node_modules/mobile-design/COMPONENTS.md` amplía la tabla de cada familia, las piezas relacionadas y los ejemplos completos.
294
+
295
+ - [Acciones y botones](#acciones-y-botones). Button, IconButton, ButtonGroup, SegmentedButtons, SplitButton, FAB, FAB.Extended, FAB.Menu.
296
+ - [Campos y selección](#campos-y-selección). TextField, Select, Autocomplete, Checkbox, RadioButton, Switch, Slider y RangeSlider, DatePicker y DockedDatePicker, TimePicker.
297
+ - [Navegación y búsqueda](#navegación-y-búsqueda). TopAppBar, NavigationBar, NavigationRail, NavigationDrawer, Tabs y TabsPager, Searchbar y SearchView, Toolbar.
298
+ - [Contenido y tipografía](#contenido-y-tipografía). Text, Badge y BadgedBox, Card, Carousel, Chip, Divider, List.
299
+ - [Superficies y ayuda](#superficies-y-ayuda). Menu, Dialog y FullScreenDialog, BottomSheet, SideSheet, Snackbar, Tooltip y RichTooltip.
300
+ - [Carga y progreso](#carga-y-progreso). LoadingIndicator y PullToRefresh, ProgressIndicator.
301
+
302
+ ### Acciones y botones
303
+
304
+ #### Button
305
+
306
+ Ayuda a completar tareas con acciones claras. Default ejecuta una acción. Toggle representa un estado seleccionado controlado.
307
+
308
+ El estado controlado se distingue por la presencia de `selected`. Conserva `pressed` para reflejar una superficie anclada que sigue abierta. En web puedes proporcionar `href` y `hrefAttrs` para obtener un enlace. En Android e iOS ejecuta la navegación en `onPress`.
309
+
310
+ | Propiedad | Tipo | Inicial | Uso |
311
+ | --- | --- | --- | --- |
312
+ | `variant` | `"filled" \| "tonal" \| "elevated" \| "outlined" \| "text"` | `'filled'` | Tratamiento visual o modalidad del componente. |
313
+ | `size` | `"extra-small" \| "small" \| "medium" \| "large" \| "extra-large"` | `'small'` | Tamaño visual o escala semántica del control. |
314
+ | `shape` | `"round" \| "square"` | `'round'` | Forma visual de la superficie. |
315
+ | `selected` | `boolean` | — | Selección persistente controlada por el consumidor. |
316
+
317
+ **Ejemplo**
318
+
319
+ ```tsx
320
+ import { useState } from 'react';
321
+ import { Button, MaterialSymbol } from 'mobile-design/react-native';
322
+
323
+ export default function Example() {
324
+ const [saved, setSaved] = useState(false);
325
+
326
+ return (
327
+ <Button
328
+ variant="filled"
329
+ size="medium"
330
+ shape="round"
331
+ selected={saved}
332
+ icon={(props) => (
333
+ <MaterialSymbol
334
+ {...props}
335
+ name="bookmark"
336
+ filled={saved}
337
+ />
338
+ )}
339
+ onPress={() => setSaved((value) => !value)}
340
+ >
341
+ {saved ? 'Guardado' : 'Guardar'}
342
+ </Button>
343
+ );
344
+ }
345
+ ```
346
+
347
+ #### IconButton
348
+
349
+ Representa acciones reconocibles mediante iconos. Puede ejecutar un comando o alternar un estado seleccionado.
350
+
351
+ El componente usa `mode` y `sizeClass`. `mode="contained"` aplica Filled, `contained-tonal` aplica Tonal, `outlined` aplica el contorno y omitirlo aplica Standard. El objetivo táctil no se deduce del tamaño del glifo.
352
+
353
+ | Propiedad | Tipo | Inicial | Uso |
354
+ | --- | --- | --- | --- |
355
+ | `icon` · requerida | `IconSource` | — | Nombre de icono o elemento de representación admitido por la propiedad. |
356
+ | `mode` | `IconButtonMode` | — | Modo de uso admitido por este componente. |
357
+ | `sizeClass` | `ButtonSize` | `'small'` | Escala del botón, incluido el contenedor y el objetivo de interacción. |
358
+ | `widthVariant` | `"narrow" \| "default" \| "wide"` | `'default'` | Proporción del botón de icono. |
359
+
360
+ #### ButtonGroup
361
+
362
+ Reúne acciones relacionadas o alternativas de selección. Standard redistribuye el espacio al pulsar. Connected mantiene el grupo unido.
363
+
364
+ Cada ítem puede tener icono y `selectedIcon`. `maxVisibleItems` limita cuántos se muestran y el resto aparece en el menú. Con selección múltiple el valor es un arreglo de claves.
365
+
366
+ | Propiedad | Tipo | Inicial | Uso |
367
+ | --- | --- | --- | --- |
368
+ | `items` · requerida | `readonly ButtonGroupItem[]` | — | Elementos del conjunto. Usa identificadores únicos y estables. |
369
+ | `value` | `string \| readonly string[]` | — | Valor actual controlado por la pantalla. |
370
+ | `onValueChange` | `((value: string \| string[]) => void)` | — | Entrega el siguiente valor para actualizar el estado del consumidor. |
371
+ | `variant` | `"standard" \| "connected"` | `'standard'` | Tratamiento visual o modalidad del componente. |
372
+
373
+ **Ejemplo**
374
+
375
+ ```tsx
376
+ import { useState } from 'react';
377
+ import { ButtonGroup } from 'mobile-design/react-native';
378
+
379
+ export default function Example() {
380
+ const [value, setValue] = useState<string | string[]>('week');
381
+
382
+ return (
383
+ <ButtonGroup
384
+ variant="connected"
385
+ selectionMode="single"
386
+ accessibilityLabel="Intervalo del calendario"
387
+ value={value}
388
+ onValueChange={setValue}
389
+ items={[
390
+ { value: 'day', label: 'Día' },
391
+ { value: 'week', label: 'Semana' },
392
+ { value: 'month', label: 'Mes' },
393
+ ]}
394
+ />
395
+ );
396
+ }
397
+ ```
398
+
399
+ #### SegmentedButtons
400
+
401
+ Permite elegir una o varias alternativas de un conjunto pequeño y relacionado.
402
+
403
+ Usa opciones con valores únicos y el mismo tratamiento visual para etiquetas e iconos.
404
+
405
+ | Propiedad | Tipo | Inicial | Uso |
406
+ | --- | --- | --- | --- |
407
+ | `buttons` · requerida | `SegmentedButtonOption<T>[]` | — | Opciones de segmentación con claves únicas. |
408
+ | `value` · requerida | `T \| T[]` | — | Valor actual controlado por la pantalla. |
409
+ | `onValueChange` · requerida | `((value: T) => void) \| ((value: T[]) => void)` | — | Entrega el siguiente valor para actualizar el estado del consumidor. |
410
+ | `multiSelect` | `boolean` | — | Permite seleccionar varios valores y usar un arreglo de claves. |
411
+
412
+ #### SplitButton
413
+
414
+ Combina una acción directa con un menú de opciones relacionadas. Cada región conserva su función independiente.
415
+
416
+ Define label, icon opcional y onPress para el comando principal. Las actions contienen alternativas relacionadas con key, label y onPress.
417
+
418
+ | Propiedad | Tipo | Inicial | Uso |
419
+ | --- | --- | --- | --- |
420
+ | `label` · requerida | `string` | — | Etiqueta visible que describe la acción u opción. |
421
+ | `actions` · requerida | `readonly SplitButtonAction[]` | — | Acciones relacionadas de la composición. |
422
+ | `onPress` · requerida | `() => void` | — | Ejecuta la acción al activar el control. |
423
+ | `expanded` | `boolean` | — | Estado controlado de expansión. |
424
+
425
+ #### FAB
426
+
427
+ Da presencia a la acción principal de la pantalla mediante una superficie flotante y un único icono.
428
+
429
+ Define icon, accessibilityLabel y onPress para la acción principal. MaterialSymbol permite elegir el relleno del glifo.
430
+
431
+ | Propiedad | Tipo | Inicial | Uso |
432
+ | --- | --- | --- | --- |
433
+ | `icon` · requerida | `any` | — | Nombre de icono o elemento de representación admitido por la propiedad. |
434
+ | `size` | `FABSize` | `'default'` | Tamaño visual o escala semántica del control. |
435
+ | `variant` | `"primary" \| "secondary" \| "tertiary" \| "tonalPrimary" \| "tonalSecondary" \| "tonalTertiary"` | `'tonalPrimary'` | Tratamiento visual o modalidad del componente. |
436
+ | `visible` | `boolean` | — | Controla si la superficie o el indicador se presenta. |
437
+
438
+ #### FAB.Extended
439
+
440
+ Destaca la acción principal de una pantalla con una etiqueta que permite entenderla antes de tocar.
441
+
442
+ Usa FAB.Extended o ExtendedFAB con label e icon opcional. Size default corresponde al tamaño Small.
443
+
444
+ | Propiedad | Tipo | Inicial | Uso |
445
+ | --- | --- | --- | --- |
446
+ | `label` · requerida | `string` | — | Etiqueta visible que describe la acción u opción. |
447
+ | `icon` | `any` | — | Nombre de icono o elemento de representación admitido por la propiedad. |
448
+ | `size` | `FABSize` | `'default'` | Tamaño visual o escala semántica del control. |
449
+ | `variant` | `"primary" \| "secondary" \| "tertiary" \| "tonalPrimary" \| "tonalSecondary" \| "tonalTertiary"` | `'tonalPrimary'` | Tratamiento visual o modalidad del componente. |
450
+
451
+ #### FAB.Menu
452
+
453
+ Expande el FAB para mostrar un conjunto de acciones relacionadas con la tarea principal de la pantalla.
454
+
455
+ Controla expanded y ciérralo desde onDismiss. Trigger define el FAB y su nombre accesible.
456
+
457
+ | Propiedad | Tipo | Inicial | Uso |
458
+ | --- | --- | --- | --- |
459
+ | `trigger` · requerida | `FABMenuTriggerProps` | — | Configuración de la acción que abre el menú flotante. |
460
+ | `items` · requerida | `TwoToSix<FABMenuItemProps> \| readonly FABMenuItemProps[]` | — | Elementos del conjunto. Usa identificadores únicos y estables. |
461
+ | `expanded` · requerida | `boolean` | — | Estado controlado de expansión. |
462
+ | `onDismiss` · requerida | `() => void` | — | Solicita cerrar la superficie. |
463
+
464
+ **Ejemplo**
465
+
466
+ ```tsx
467
+ import { useState } from 'react';
468
+ import { View } from 'react-native';
469
+ import { FAB, Text } from 'mobile-design/react-native';
470
+
471
+ export default function Example() {
472
+ const [feedback, setFeedback] = useState('');
473
+ const [expanded, setExpanded] = useState(false);
474
+
475
+ return (
476
+ <View style={{ gap: 16 }}>
477
+ <View
478
+ style={{
479
+ minHeight: 280,
480
+ justifyContent: 'flex-end',
481
+ alignItems: 'flex-end',
482
+ }}
483
+ >
484
+ <FAB.Menu
485
+ presentation="expressive"
486
+ expanded={expanded}
487
+ onDismiss={() => setExpanded(false)}
488
+ trigger={{
489
+ icon: 'plus',
490
+ size: 'medium',
491
+ accessibilityLabel: 'Crear',
492
+ onPress: () => setExpanded(true),
493
+ }}
494
+ items={[
495
+ {
496
+ key: 'note',
497
+ label: 'Nota',
498
+ icon: 'pencil-outline',
499
+ onPress: () => setFeedback('Nueva nota'),
500
+ },
501
+ {
502
+ key: 'folder',
503
+ label: 'Carpeta',
504
+ icon: 'folder-outline',
505
+ onPress: () => setFeedback('Nueva carpeta'),
506
+ },
507
+ ]}
508
+ />
509
+ </View>
510
+ <Text accessibilityLiveRegion="polite">{feedback}</Text>
511
+ </View>
512
+ );
513
+ }
514
+ ```
515
+
516
+ ### Campos y selección
517
+
518
+ #### TextField
519
+
520
+ Recoge información mediante campos Filled u Outlined, con etiquetas, ayuda y errores asociados.
521
+
522
+ El contador muestra la longitud. `maxLength` establece el límite. El ref `TextFieldHandle` ofrece `focus`, `blur`, `clear` e `isFocused`. Para un área de texto de altura fija combina `multiline`, `autoGrow={false}` e `inputStyle` con una altura.
523
+
524
+ | Propiedad | Tipo | Inicial | Uso |
525
+ | --- | --- | --- | --- |
526
+ | `value` · requerida | `string` | — | Valor actual controlado por la pantalla. |
527
+ | `onChangeText` · requerida | `(value: string) => void` | — | Comunica el nuevo texto para actualizar el valor controlado. |
528
+ | `variant` | `"filled" \| "outlined"` | — | Tratamiento visual o modalidad del componente. |
529
+ | `error` | `boolean` | — | Activa el tratamiento visual y accesible de error. |
530
+
531
+ **Ejemplo**
532
+
533
+ ```tsx
534
+ import { useState } from 'react';
535
+ import { TextField } from 'mobile-design/react-native';
536
+
537
+ export default function Example() {
538
+ const [name, setName] = useState('');
539
+
540
+ return (
541
+ <TextField
542
+ variant="outlined"
543
+ label="Nombre de la colección"
544
+ value={name}
545
+ onChangeText={setName}
546
+ counter={40}
547
+ maxLength={40}
548
+ supportingText="Elige un nombre fácil de reconocer"
549
+ />
550
+ );
551
+ }
552
+ ```
553
+
554
+ #### Select
555
+
556
+ Select es una extensión de Mobile Design para elegir una opción fija en un formulario.
557
+
558
+ Las opciones tienen `value` único y `label`. Puedes añadir `description`, `icon` y `disabled`. `Select` devuelve el identificador elegido, conserva el valor al cancelar y no abre un teclado de texto.
559
+
560
+ | Propiedad | Tipo | Inicial | Uso |
561
+ | --- | --- | --- | --- |
562
+ | `label` · requerida | `string` | — | Etiqueta visible que describe la acción u opción. |
563
+ | `value` · requerida | `string` | — | Identificador de la opción seleccionada o cadena vacía mientras no haya elección. |
564
+ | `options` · requerida | `readonly ChoiceOption[]` | — | Opciones con identificador, etiqueta y disponibilidad. |
565
+ | `onValueChange` · requerida | `(value: string) => void` | — | Entrega el identificador `value` de la opción confirmada. |
566
+
567
+ #### Autocomplete
568
+
569
+ Autocomplete es una extensión de Mobile Design que muestra sugerencias a medida que se escribe.
570
+
571
+ El campo admite texto libre. Tras confirmar una sugerencia, `onValueChange` recibe su etiqueta y `onSelect` la opción completa. Guarda por separado su `value` si el formulario necesita el identificador.
572
+
573
+ | Propiedad | Tipo | Inicial | Uso |
574
+ | --- | --- | --- | --- |
575
+ | `label` · requerida | `string` | — | Etiqueta visible que describe la acción u opción. |
576
+ | `value` · requerida | `string` | — | Texto controlado del campo, que puede contener una etiqueta o una consulta libre. |
577
+ | `options` · requerida | `readonly ChoiceOption[]` | — | Opciones con identificador, etiqueta y disponibilidad. |
578
+ | `onValueChange` · requerida | `(value: string) => void` | — | Entrega lo que se escribe y la etiqueta al seleccionar una sugerencia. |
579
+
580
+ **Ejemplo**
581
+
582
+ ```tsx
583
+ import { useState } from 'react';
584
+ import { Autocomplete } from 'mobile-design/react-native';
585
+
586
+ export default function Example() {
587
+ const [query, setQuery] = useState('');
588
+
589
+ return (
590
+ <Autocomplete
591
+ label="Ciudad"
592
+ value={query}
593
+ onValueChange={setQuery}
594
+ leadingIcon="search"
595
+ options={[
596
+ { value: 'mty', label: 'Monterrey', description: 'Nuevo León' },
597
+ { value: 'mid', label: 'Mérida', description: 'Yucatán' },
598
+ { value: 'qro', label: 'Querétaro', description: 'Querétaro' },
599
+ ]}
600
+ />
601
+ );
602
+ }
603
+ ```
604
+
605
+ #### Checkbox
606
+
607
+ Permite seleccionar varias opciones o representar una selección parcial en un conjunto.
608
+
609
+ Controla status con checked, unchecked o indeterminate.
610
+
611
+ En `Checkbox.Item`, pasa `label`, `status` y `onPress` para hacer interactiva toda la fila. Configura `position` para colocar el indicador a la izquierda o la derecha.
612
+
613
+ | Propiedad | Tipo | Inicial | Uso |
614
+ | --- | --- | --- | --- |
615
+ | `status` · requerida | `"checked" \| "unchecked" \| "indeterminate"` | — | Estado actual del control. |
616
+ | `onPress` | `(((event: GestureResponderEvent) => unknown) & ((e: GestureResponderEvent) => void) & ((e: GestureResponderEvent) => void))` | — | Ejecuta la acción al activar el control. |
617
+ | `color` | `ColorValue` | — | Color del elemento o trazo. |
618
+ | `disabled` | `boolean` | — | Bloquea la interacción del control. |
619
+
620
+ #### RadioButton
621
+
622
+ Permite seleccionar una opción de un conjunto. Elegir otra reemplaza la selección anterior.
623
+
624
+ `RadioButton.Group` recibe `value` y `onValueChange`. Cada `RadioButton.Item` aporta un `value` único, `label` y `disabled` si corresponde.
625
+
626
+ | Propiedad | Tipo | Inicial | Uso |
627
+ | --- | --- | --- | --- |
628
+ | `value` · requerida | `string` | — | Clave de la opción dentro de su grupo. |
629
+ | `status` | `"checked" \| "unchecked"` | — | Estado actual del control. |
630
+ | `onPress` | `((e: GestureResponderEvent) => void)` | — | Ejecuta la acción al activar el control. |
631
+ | `disabled` | `boolean` | — | Bloquea la interacción del control. |
632
+
633
+ #### Switch
634
+
635
+ Activa o desactiva una configuración binaria mediante un track y un handle que expresan el estado.
636
+
637
+ Controla value y actualízalo desde onValueChange. Aplica el ajuste al activarlo. No añadas Guardar ni una confirmación diferida.
638
+
639
+ | Propiedad | Tipo | Inicial | Uso |
640
+ | --- | --- | --- | --- |
641
+ | `value` | `boolean` | `false` | Estado booleano de la configuración. |
642
+ | `onValueChange` | `((value: boolean) => void)` | — | Entrega el siguiente valor para actualizar el estado del consumidor. |
643
+ | `checkedIcon` | `any` | — | Icono de un interruptor activado. |
644
+ | `uncheckedIcon` | `any` | — | Icono de un interruptor desactivado. |
645
+
646
+ #### Slider y RangeSlider
647
+
648
+ Permite recorrer un intervalo con control continuo o discreto, en una o dos posiciones.
649
+
650
+ Publica una vista previa con `onValueChange` y confirma en `onSlidingComplete`. `onSlidingCancel` permite descartar el borrador. En el rango, usa una etiqueta diferente por cada extremo.
651
+
652
+ `RangeSlider` recibe la tupla `[mínimo, máximo]`. Usa `minimumRange` para impedir que ambos extremos se junten y `startAccessibilityLabel` y `endAccessibilityLabel` para distinguirlos.
653
+
654
+ | Propiedad | Tipo | Inicial | Uso |
655
+ | --- | --- | --- | --- |
656
+ | `value` · requerida | `number` | — | Posición actual del control dentro de los límites configurados. |
657
+ | `onValueChange` · requerida | `(value: number) => void` | — | Entrega el siguiente valor para actualizar el estado del consumidor. |
658
+ | `onSlidingComplete` | `((value: number) => void)` | — | Recibe el valor confirmado al terminar el gesto. |
659
+ | `minimumValue` | `number` | — | Extremo inferior del intervalo. |
660
+
661
+ **Ejemplo**
662
+
663
+ ```tsx
664
+ import { useState } from 'react';
665
+ import { Slider } from 'mobile-design/react-native';
666
+
667
+ export default function Example() {
668
+ const [value, setValue] = useState(40);
669
+
670
+ return (
671
+ <Slider
672
+ value={value}
673
+ onValueChange={setValue}
674
+ minimumValue={0}
675
+ maximumValue={100}
676
+ step={5}
677
+ accessibilityLabel="Volumen"
678
+ valueSuffix="%"
679
+ />
680
+ );
681
+ }
682
+ ```
683
+
684
+ #### DatePicker y DockedDatePicker
685
+
686
+ Permite elegir días mediante calendario, entrada manual o un selector anclado al campo.
687
+
688
+ En modo simple el valor confirmado es un `Date`. En rango usa un objeto `DateRange` con `start` y `end`. Conviene tratar los días como fechas del calendario local y decidir explícitamente cómo se guardan. El callback de cambio recibe el resultado confirmado.
689
+
690
+ `DockedDatePicker` usa un campo anclado. Define su `label` y conserva el valor elegido con `value` y `onChange`.
691
+
692
+ | Propiedad | Tipo | Inicial | Uso |
693
+ | --- | --- | --- | --- |
694
+ | `value` | `Date \| DateRange` | — | Valor actual controlado por la pantalla. |
695
+ | `mode` | `"single" \| "range"` | — | Elige entre `single` y `range`. |
696
+ | `onChange` · requerida | `((date: Date) => void) \| ((range: DateRange) => void)` | — | Entrega el día o el rango confirmado, según el modo. |
697
+ | `onDismiss` · requerida | `() => void` | — | Solicita cerrar la superficie. |
698
+
699
+ **Ejemplo**
700
+
701
+ ```tsx
702
+ import { useState } from 'react';
703
+ import { Button, DatePicker } from 'mobile-design/react-native';
704
+
705
+ export default function Example() {
706
+ const [visible, setVisible] = useState(false);
707
+ const [date, setDate] = useState(new Date(2026, 10, 3));
708
+
709
+ return (
710
+ <>
711
+ <Button onPress={() => setVisible(true)}>Elegir fecha</Button>
712
+ <DatePicker
713
+ mode="single"
714
+ visible={visible}
715
+ value={date}
716
+ locale="es-MX"
717
+ onDismiss={() => setVisible(false)}
718
+ onChange={(next) => {
719
+ setDate(next);
720
+ setVisible(false);
721
+ }}
722
+ />
723
+ </>
724
+ );
725
+ }
726
+ ```
727
+
728
+ #### TimePicker
729
+
730
+ Selecciona hora y minutos en formato de 12 o 24 horas, con un borrador que se confirma explícitamente.
731
+
732
+ El objeto `TimeValue` siempre usa `hours` de 0 a 23 y `minutes` de 0 a 59. `is24Hour` modifica la presentación. Cierra o confirma desde los callbacks y guarda el valor confirmado en el estado de la pantalla.
733
+
734
+ | Propiedad | Tipo | Inicial | Uso |
735
+ | --- | --- | --- | --- |
736
+ | `value` · requerida | `TimeValue` | — | Valor actual controlado por la pantalla. |
737
+ | `visible` · requerida | `boolean` | — | Controla si la superficie o el indicador se presenta. |
738
+ | `onConfirm` · requerida | `(value: TimeValue) => void` | — | Entrega el borrador validado al confirmar. |
739
+ | `onDismiss` · requerida | `() => void` | — | Solicita cerrar la superficie. |
740
+
741
+ ### Navegación y búsqueda
742
+
743
+ #### TopAppBar
744
+
745
+ Ofrece contexto, navegación y acciones relacionadas con la pantalla. Las barras flexibles responden al desplazamiento del contenido.
746
+
747
+ Elige variant y proporciona title. Subtitle, navigationIcon y actions completan la anatomía.
748
+
749
+ | Propiedad | Tipo | Inicial | Uso |
750
+ | --- | --- | --- | --- |
751
+ | `title` · requerida | `ReactNode` | — | Título visible o contenido principal. |
752
+ | `variant` | `"small" \| "medium-flexible" \| "large-flexible"` | `'small'` | Tratamiento visual o modalidad del componente. |
753
+ | `actions` | `readonly TopAppBarAction[]` | `[]` | Acciones relacionadas de la composición. |
754
+ | `navigationIcon` | `any` | — | Icono de navegación al inicio de la barra. |
755
+
756
+ #### NavigationBar
757
+
758
+ Permite cambiar entre destinos principales de una interfaz compacta con iconos, etiquetas e indicadores.
759
+
760
+ La barra reporta claves de destino. La pantalla decide qué ruta abrir, qué destino está activo y dónde anclar el conjunto. Si la pantalla ya incorpora el inset inferior, configura `safeArea={false}`.
761
+
762
+ | Propiedad | Tipo | Inicial | Uso |
763
+ | --- | --- | --- | --- |
764
+ | `items` · requerida | `readonly NavigationDestination[]` | — | Elementos del conjunto. Usa identificadores únicos y estables. |
765
+ | `value` · requerida | `string` | — | Valor actual controlado por la pantalla. |
766
+ | `onValueChange` · requerida | `(key: string) => void` | — | Entrega el siguiente valor para actualizar el estado del consumidor. |
767
+ | `variant` | `"standard" \| "short"` | `'short'` | Tratamiento visual o modalidad del componente. |
768
+
769
+ **Ejemplo**
770
+
771
+ ```tsx
772
+ import { useState } from 'react';
773
+ import { NavigationBar } from 'mobile-design/react-native';
774
+
775
+ export default function Example() {
776
+ const [value, setValue] = useState('home');
777
+
778
+ return (
779
+ <NavigationBar
780
+ value={value}
781
+ onValueChange={setValue}
782
+ items={[
783
+ {
784
+ key: 'home',
785
+ label: 'Inicio',
786
+ icon: 'home-outline',
787
+ selectedIcon: 'home',
788
+ },
789
+ {
790
+ key: 'library',
791
+ label: 'Biblioteca',
792
+ icon: 'folder-outline',
793
+ badge: 3,
794
+ },
795
+ { key: 'saved', label: 'Guardado', icon: 'bookmark-outline' },
796
+ ]}
797
+ />
798
+ );
799
+ }
800
+ ```
801
+
802
+ #### NavigationRail
803
+
804
+ Mantiene destinos principales en una columna que puede expandirse para dar contexto adicional.
805
+
806
+ Controla `expanded` para cambiar de ancho sin reiniciar el destino. La pantalla decide el breakpoint. El modal puede compartir los mismos `items` y `value` con la variante persistente.
807
+
808
+ `NavigationRailModal` recibe `open`, `onOpenChange`, `items`, `value` y `onValueChange`.
809
+
810
+ | Propiedad | Tipo | Inicial | Uso |
811
+ | --- | --- | --- | --- |
812
+ | `items` · requerida | `readonly (NavigationDestination & { href?: string; })[]` | — | Elementos del conjunto. Usa identificadores únicos y estables. |
813
+ | `value` · requerida | `string` | — | Valor actual controlado por la pantalla. |
814
+ | `onValueChange` · requerida | `(key: string) => void` | — | Entrega el siguiente valor para actualizar el estado del consumidor. |
815
+ | `expanded` | `boolean` | `false` | Estado controlado de expansión. |
816
+
817
+ #### NavigationDrawer
818
+
819
+ Organiza destinos y secciones en un panel lateral estándar o modal.
820
+
821
+ La vista estándar comparte espacio con el contenido. La modal superpone el panel y cierra al seleccionar por defecto. Usa el callback de cierre completo si el nuevo destino requiere recibir foco.
822
+
823
+ `NavigationDrawerModal` controla su apertura con `open` y `onOpenChange`. `NavigationDrawerItem` recibe un objeto `item` y permite controlar su selección.
824
+
825
+ | Propiedad | Tipo | Inicial | Uso |
826
+ | --- | --- | --- | --- |
827
+ | `items` · requerida | `readonly NavigationDrawerDestination[]` | — | Elementos del conjunto. Usa identificadores únicos y estables. |
828
+ | `value` · requerida | `string` | — | Valor actual controlado por la pantalla. |
829
+ | `onValueChange` · requerida | `(key: string) => void` | — | Entrega el siguiente valor para actualizar el estado del consumidor. |
830
+ | `modal` | `boolean` | `false` | Activa la composición modal del panel. |
831
+
832
+ #### Tabs y TabsPager
833
+
834
+ Organiza contenido del mismo nivel mediante pestañas Primary o Secondary y un indicador de selección.
835
+
836
+ Para deslizar entre paneles comparte `value`, `position` y la solicitud de selección entre `Tabs` y `TabsPager`. Si gestionas los paneles por tu cuenta, enlaza el `nativeID` de la pestaña y el `panelID` del panel.
837
+
838
+ `TabsPager` monta cada vista con `renderPage` y permite deslizar con `swipeEnabled`. Conserva las mismas claves y orden de `items` que en `Tabs`.
839
+
840
+ | Propiedad | Tipo | Inicial | Uso |
841
+ | --- | --- | --- | --- |
842
+ | `items` · requerida | `readonly TabItem[]` | — | Elementos del conjunto. Usa identificadores únicos y estables. |
843
+ | `value` · requerida | `string` | — | Valor actual controlado por la pantalla. |
844
+ | `onValueChange` · requerida | `(key: string) => void` | — | Entrega el siguiente valor para actualizar el estado del consumidor. |
845
+ | `variant` | `"primary" \| "secondary"` | `'primary'` | Tratamiento visual o modalidad del componente. |
846
+
847
+ **Ejemplo**
848
+
849
+ ```tsx
850
+ import { useState } from 'react';
851
+ import { View } from 'react-native';
852
+ import { useSharedValue } from 'react-native-reanimated';
853
+ import { Tabs, TabsPager, Text } from 'mobile-design/react-native';
854
+
855
+ export default function Example() {
856
+ const [value, setValue] = useState('files');
857
+ const [request, setRequest] = useState(0);
858
+ const position = useSharedValue(0);
859
+ const items = [
860
+ {
861
+ key: 'files',
862
+ label: 'Archivos',
863
+ icon: 'folder',
864
+ nativeID: 'library-files-tab',
865
+ panelID: 'library-files-panel',
866
+ },
867
+ {
868
+ key: 'recent',
869
+ label: 'Recientes',
870
+ icon: 'schedule',
871
+ nativeID: 'library-recent-tab',
872
+ panelID: 'library-recent-panel',
873
+ },
874
+ ];
875
+
876
+ return (
877
+ <View style={{ flex: 1 }}>
878
+ <Tabs
879
+ items={items}
880
+ value={value}
881
+ onValueChange={setValue}
882
+ position={position}
883
+ onTabPress={() => setRequest((value) => value + 1)}
884
+ />
885
+ <TabsPager
886
+ items={items}
887
+ value={value}
888
+ onValueChange={setValue}
889
+ position={position}
890
+ selectionRequest={request}
891
+ renderPage={(item) => (
892
+ <View style={{ padding: 24 }}>
893
+ <Text>{item.label}</Text>
894
+ </View>
895
+ )}
896
+ />
897
+ </View>
898
+ );
899
+ }
900
+ ```
901
+
902
+ #### Searchbar y SearchView
903
+
904
+ Conecta una barra de búsqueda con una vista que presenta consulta, sugerencias y resultados.
905
+
906
+ El filtrado y la obtención de resultados pertenecen al consumidor. Si las sugerencias llegan de un servicio, descarta respuestas de consultas anteriores. `SearchTrigger` y `SearchIconButton` ayudan a construir el punto de entrada y sus acciones.
907
+
908
+ `SearchView` recibe `open`, `onOpenChange`, `value`, `onChangeText` y `onSearch`. Pasa `results` y usa `onResultPress` para abrir un resultado.
909
+
910
+ | Propiedad | Tipo | Inicial | Uso |
911
+ | --- | --- | --- | --- |
912
+ | `value` · requerida | `string` | — | Valor actual controlado por la pantalla. |
913
+ | `onChangeText` | `(((text: string) => unknown) & ((query: string) => void))` | — | Comunica el nuevo texto para actualizar el valor controlado. |
914
+ | `onIconPress` | `((e: GestureResponderEvent) => void)` | — | Acción del icono independiente. |
915
+ | `right` | `((props: { color: ColorValue; style: Style; testID: string; }) => React.ReactNode)` | — | Contenido del lado final. |
916
+
917
+ **Ejemplo**
918
+
919
+ ```tsx
920
+ import { useState } from 'react';
921
+ import { Searchbar } from 'mobile-design/react-native';
922
+
923
+ export default function Example() {
924
+ const [query, setQuery] = useState('');
925
+
926
+ return (
927
+ <Searchbar
928
+ placeholder="Buscar documentos"
929
+ value={query}
930
+ onChangeText={setQuery}
931
+ accessibilityLabel="Buscar documentos"
932
+ />
933
+ );
934
+ }
935
+ ```
936
+
937
+ #### Toolbar
938
+
939
+ Agrupa acciones de edición o contexto en barras Docked o Floating, horizontales o verticales.
940
+
941
+ Cada acción usa `key`, `icon`, `label` y `onPress`. Usa `selected` para una selección persistente.
942
+
943
+ | Propiedad | Tipo | Inicial | Uso |
944
+ | --- | --- | --- | --- |
945
+ | `actions` · requerida | `readonly ToolbarAction[]` | — | Acciones relacionadas de la composición. |
946
+ | `variant` | `"docked" \| "floating"` | `'docked'` | Tratamiento visual o modalidad del componente. |
947
+ | `orientation` | `"horizontal" \| "vertical"` | `'horizontal'` | Eje de la composición. |
948
+ | `expanded` | `boolean` | `true` | Estado controlado de expansión. |
949
+
950
+ ### Contenido y tipografía
951
+
952
+ #### Text
953
+
954
+ Aplica la escala tipográfica del tema a contenido, etiquetas y títulos. Los roles de texto conservan sus métricas al cambiar la familia en el proveedor.
955
+
956
+ Combina `display`, `headline`, `title`, `body` o `label` con `Large`, `Medium` o `Small`. Agrega `Emphasized` para enfatizar el mismo rol.
957
+
958
+ | Propiedad | Tipo | Inicial | Uso |
959
+ | --- | --- | --- | --- |
960
+ | `variant` | `TextVariant` | — | Rol tipográfico del tema. |
961
+ | `numberOfLines` | `number` | — | Truncate text with an ellipsis after this many lines. `0` means no restriction. |
962
+ | `selectable` | `boolean` | — | Lets the user select text for native copy and paste. |
963
+ | `style` | `((false \| void \| "" \| Readonly<Partial<TextStyle>> \| readonly StyleProp<Readonly<Partial<TextStyle>>>[]) & (false \| void \| "" \| TextStyle \| readonly StyleProp<TextStyle>[])) \| null` | — | Estilo de la superficie exterior. |
964
+
965
+ **Ejemplo**
966
+
967
+ ```tsx
968
+ import { View } from 'react-native';
969
+ import { Text } from 'mobile-design/react-native';
970
+
971
+ export default function Example() {
972
+ return (
973
+ <View style={{ gap: 8 }}>
974
+ <Text variant="headlineMedium" accessibilityRole="header">Documentos</Text>
975
+ <Text variant="bodyLarge" selectable>
976
+ Elige un archivo. <Text variant="bodyLargeEmphasized">Guardamos tus cambios.</Text>
977
+ </Text>
978
+ <Text variant="labelSmall">Actualizado hoy</Text>
979
+ </View>
980
+ );
981
+ }
982
+ ```
983
+
984
+ #### Badge y BadgedBox
985
+
986
+ Comunica novedades o cantidades junto a un destino, un icono o una etiqueta, sin interrumpir la tarea.
987
+
988
+ `BadgedBox` ancla `badge` al elemento hijo. Usa `true` para un punto, un número para un contador, o texto para una etiqueta breve. Ajusta la posición con `badgeContainerStyle`.
989
+
990
+ | Propiedad | Tipo | Inicial | Uso |
991
+ | --- | --- | --- | --- |
992
+ | `children` | `ReactNode` | — | Contenido breve de una insignia de tipo `large`. |
993
+ | `variant` | `"large" \| "dot"` | — | Tratamiento visual o modalidad del componente. |
994
+ | `size` | `number` | — | Tamaño visual o escala semántica del control. |
995
+ | `maximum` | `number` | `999` | A partir de este límite un número se muestra con sufijo `+`. |
996
+
997
+ #### Card
998
+
999
+ Agrupa contenido y acciones que pertenecen a una misma entidad, con tratamientos Elevated, Filled u Outlined.
1000
+
1001
+ Compón el contenido con `Card.Title`, `Card.Content`, `Card.Cover` y `Card.Actions`. Si toda la tarjeta abre un destino, reserva sus hijos para contenido de presentación. Si tiene varias acciones, coloca los controles dentro de `Card.Actions` y deja la superficie como contenedor.
1002
+
1003
+ | Propiedad | Tipo | Inicial | Uso |
1004
+ | --- | --- | --- | --- |
1005
+ | `variant` | `"filled" \| "elevated" \| "outlined"` | — | Tratamiento visual o modalidad del componente. |
1006
+ | `mode` | `"elevated" \| "outlined" \| "contained"` | `'elevated'` | Modo de uso admitido por este componente. |
1007
+ | `onPress` | `((e: GestureResponderEvent) => void)` | — | Ejecuta la acción al activar el control. |
1008
+ | `disabled` | `boolean` | `false` | Bloquea la interacción del control. |
1009
+
1010
+ #### Carousel
1011
+
1012
+ Muestra colecciones visuales en las variantes de la referencia y en Vertical, una extensión propia de Mobile Design para galerías de distintas proporciones.
1013
+
1014
+ Cada elemento usa un `id` estable. `aspectRatio` es ancho dividido entre alto. `onIndexChange` observa el índice aceptado, mientras `initialIndex` fija la posición de arranque.
1015
+
1016
+ `FullScreenCarousel` usa un elemento por página vertical. Pásale `items`, `initialIndex` y `onIndexChange` igual que a la composición principal.
1017
+
1018
+ | Propiedad | Tipo | Inicial | Uso |
1019
+ | --- | --- | --- | --- |
1020
+ | `items` · requerida | `readonly CarouselItem[]` | — | Elementos del conjunto. Usa identificadores únicos y estables. |
1021
+ | `variant` | `"multi-browse" \| "hero" \| "center-aligned-hero" \| "uncontained" \| "uncontained-multi-aspect" \| "full-screen" \| "vertical"` | — | Tratamiento visual o modalidad del componente. |
1022
+ | `initialIndex` | `number` | — | Índice inicial al montar la colección. |
1023
+ | `onIndexChange` | `((index: number) => void)` | — | Avisa del cambio de índice aceptado. |
1024
+
1025
+ #### Chip
1026
+
1027
+ Representa acciones auxiliares, filtros, datos introducidos y sugerencias en superficies compactas.
1028
+
1029
+ Elige variant según Assist, Filter, Input o Suggestion. Children aporta la etiqueta.
1030
+
1031
+ `Chip.Group` distribuye chips en filas con salto automático y coordina el foco por teclado en web. Los estados seleccionados los conserva la pantalla.
1032
+
1033
+ | Propiedad | Tipo | Inicial | Uso |
1034
+ | --- | --- | --- | --- |
1035
+ | `variant` | `"filter" \| "input" \| "assist" \| "suggestion"` | `'assist'` | Tratamiento visual o modalidad del componente. |
1036
+ | `selected` | `boolean` | `false` | Selección persistente controlada por el consumidor. |
1037
+ | `showSelectedCheck` | `boolean` | — | Whether to display default check icon on selected chip. Note. Check will not be shown if `icon` is specified. If specified, `icon` will be shown regardless of `selected`. |
1038
+ | `onPress` | `((e: GestureResponderEvent) => void)` | — | Ejecuta la acción al activar el control. |
1039
+
1040
+ #### Divider
1041
+
1042
+ Ayuda a distinguir contenido o regiones de una composición mediante una línea sutil.
1043
+
1044
+ Coloca Divider entre regiones. Orientation cambia el eje y thickness e insets ajustan la línea.
1045
+
1046
+ | Propiedad | Tipo | Inicial | Uso |
1047
+ | --- | --- | --- | --- |
1048
+ | `orientation` | `"horizontal" \| "vertical"` | `'horizontal'` | Eje de la composición. |
1049
+ | `bold` | `boolean` | `true` | Usa el tratamiento más marcado del divisor. |
1050
+ | `thickness` | `number` | — | Grosor del trazo en dp. |
1051
+ | `leftInset` | `boolean` | — | Reserva un margen al inicio lógico de la línea horizontal. |
1052
+
1053
+ #### List
1054
+
1055
+ Presenta elementos con título, descripción y contenido de apoyo en listas Standard o Segmented.
1056
+
1057
+ Usa `List.Collection` para contenido agrupado y `List.Group` para filas coordinadas. `List.Icon`, `List.Image`, `List.Section` y `List.Subheader` son piezas de composición. Para una colección grande monta las filas en una lista virtualizada del consumidor.
1058
+
1059
+ Usa `List.Accordion` con `title`, `expanded` y `onPress`. Configura las acciones laterales con `SwipeListItem` y su grupo, y el orden editable con `ListReorderGroup`, `items`, `keyExtractor` y `onReorder`.
1060
+
1061
+ | Propiedad | Tipo | Inicial | Uso |
1062
+ | --- | --- | --- | --- |
1063
+ | `title` · requerida | `ReactNode` | — | Título visible o contenido principal. |
1064
+ | `description` | `ReactNode` | — | Texto secundario de apoyo. |
1065
+ | `variant` | `"expressive" \| "baseline"` | `'expressive'` | Tratamiento visual o modalidad del componente. |
1066
+ | `selectionMode` | `"single" \| "multiple"` | — | Define si la selección es inexistente, única o múltiple. |
1067
+
1068
+ **Ejemplo**
1069
+
1070
+ ```tsx
1071
+ import { useState } from 'react';
1072
+ import { View } from 'react-native';
1073
+ import { List, IconButton, Text } from 'mobile-design/react-native';
1074
+
1075
+ export default function Example() {
1076
+ const [feedback, setFeedback] = useState('');
1077
+
1078
+ return (
1079
+ <View style={{ gap: 16 }}>
1080
+ <List.Item
1081
+ title="Informe trimestral"
1082
+ description="Documento compartido"
1083
+ lines={2}
1084
+ onPress={() => setFeedback('Detalle del documento')}
1085
+ trailing={
1086
+ <IconButton
1087
+ icon="bookmark-outline"
1088
+ accessibilityLabel="Guardar Informe trimestral"
1089
+ onPress={() => setFeedback('Guardado Informe trimestral')}
1090
+ />
1091
+ }
1092
+ />
1093
+ <Text accessibilityLiveRegion="polite">{feedback}</Text>
1094
+ </View>
1095
+ );
1096
+ }
1097
+ ```
1098
+
1099
+ ### Superficies y ayuda
1100
+
1101
+ #### Menu
1102
+
1103
+ Muestra acciones o alternativas en una superficie anclada, con grupos, selección y submenús cuando hacen falta.
1104
+
1105
+ Controla visible y onDismiss y proporciona un anchor que abra el menú.
1106
+
1107
+ `Menu.Item` recibe `title`, `onPress` e iconos inicial y final. `Menu.Group` crea una sección y `Menu.Submenu` anida opciones.
1108
+
1109
+ | Propiedad | Tipo | Inicial | Uso |
1110
+ | --- | --- | --- | --- |
1111
+ | `anchor` · requerida | `React.ReactNode \| { x: number; y: number; }` | — | Control o coordenadas que sirven como origen de la superficie. |
1112
+ | `visible` · requerida | `boolean` | — | Controla si la superficie o el indicador se presenta. |
1113
+ | `onDismiss` | `(() => void)` | — | Solicita cerrar la superficie. |
1114
+ | `onClosed` | `(() => void)` | — | Avisa cuando concluye la salida de la superficie. |
1115
+
1116
+ **Ejemplo**
1117
+
1118
+ ```tsx
1119
+ import { useState } from 'react';
1120
+ import { View } from 'react-native';
1121
+ import { Menu, Button, Text } from 'mobile-design/react-native';
1122
+
1123
+ export default function Example() {
1124
+ const [feedback, setFeedback] = useState('');
1125
+ const [visible, setVisible] = useState(false);
1126
+
1127
+ return (
1128
+ <View style={{ gap: 16 }}>
1129
+ <Menu
1130
+ visible={visible}
1131
+ onDismiss={() => setVisible(false)}
1132
+ anchor={
1133
+ <Button
1134
+ variant="tonal"
1135
+ onPress={() => setVisible(true)}
1136
+ >
1137
+ Opciones
1138
+ </Button>
1139
+ }
1140
+ >
1141
+ <Menu.Item
1142
+ title="Guardar"
1143
+ leadingIcon="bookmark-outline"
1144
+ onPress={() => setFeedback('Guardado')}
1145
+ />
1146
+ <Menu.Item
1147
+ title="Enviar"
1148
+ leadingIcon="send-outline"
1149
+ onPress={() => setFeedback('Enviar')}
1150
+ />
1151
+ </Menu>
1152
+ <Text accessibilityLiveRegion="polite">{feedback}</Text>
1153
+ </View>
1154
+ );
1155
+ }
1156
+ ```
1157
+
1158
+ #### Dialog y FullScreenDialog
1159
+
1160
+ Presenta información o una tarea que necesita atención antes de volver al contenido anterior.
1161
+
1162
+ Compón `Dialog.Title`, `Dialog.Icon`, `Dialog.Content`, `Dialog.ScrollArea` y `Dialog.Actions` según la tarea. `Dialog` ya controla su portal. Para medir un origen compartido para la animación de entrada, pasa `transitionOrigin` en las coordenadas de ventana que espera la superficie.
1163
+
1164
+ `FullScreenDialog` recibe `title`, `visible`, `onDismiss` y permite configurar la confirmación con `confirmLabel`, `onConfirm` y `confirmDisabled`.
1165
+
1166
+ | Propiedad | Tipo | Inicial | Uso |
1167
+ | --- | --- | --- | --- |
1168
+ | `visible` · requerida | `boolean` | — | Controla si la superficie o el indicador se presenta. |
1169
+ | `onDismiss` | `(() => void)` | — | Solicita cerrar la superficie. |
1170
+ | `onClosed` | `(() => void)` | — | Avisa cuando concluye la salida de la superficie. |
1171
+ | `dismissable` | `boolean` | `true` | Determines whether clicking outside the dialog dismiss it. |
1172
+
1173
+ #### BottomSheet
1174
+
1175
+ Presenta tareas o información de apoyo con una hoja estándar o modal y posiciones de expansión.
1176
+
1177
+ Monta `GestureHandlerRootView` como se muestra en el ejemplo de instalación. Usa `BottomSheetTextField` o `BottomSheetTextInput` al editar dentro de la hoja. Mantén los hijos durante la salida si su contenido debe permanecer visible hasta `onClosed`.
1178
+
1179
+ | Propiedad | Tipo | Inicial | Uso |
1180
+ | --- | --- | --- | --- |
1181
+ | `open` · requerida | `boolean` | — | Controla si la superficie está abierta. |
1182
+ | `onOpenChange` · requerida | `(open: boolean) => void` | — | Solicita abrir o cerrar. Actualiza `open` en la pantalla. |
1183
+ | `variant` | `"standard" \| "modal"` | `'modal'` | `standard` comparte el layout y `modal` presenta una hoja superpuesta. |
1184
+ | `snapPoints` | `(string \| number)[]` | — | Posiciones de reposo de la hoja. Admite puntos en dp o porcentajes según el tipo. |
1185
+
1186
+ **Ejemplo**
1187
+
1188
+ ```tsx
1189
+ import { useState } from 'react';
1190
+ import {
1191
+ BottomSheet,
1192
+ BottomSheetScrollView,
1193
+ Text,
1194
+ Button,
1195
+ } from 'mobile-design/react-native';
1196
+
1197
+ export default function Example() {
1198
+ const [open, setOpen] = useState(false);
1199
+
1200
+ return (
1201
+ <>
1202
+ <Button onPress={() => setOpen(true)}>Ver detalles</Button>
1203
+ <BottomSheet
1204
+ open={open}
1205
+ onOpenChange={setOpen}
1206
+ variant="modal"
1207
+ scrollable
1208
+ snapPoints={['50%', '85%']}
1209
+ accessibilityLabel="Detalles de la colección"
1210
+ >
1211
+ <BottomSheetScrollView contentContainerStyle={{ padding: 24 }}>
1212
+ <Text variant="headlineSmall">Tu colección</Text>
1213
+ <Text>Información adicional de la colección guardada.</Text>
1214
+ <Button
1215
+ variant="text"
1216
+ onPress={() => setOpen(false)}
1217
+ >
1218
+ Cerrar
1219
+ </Button>
1220
+ </BottomSheetScrollView>
1221
+ </BottomSheet>
1222
+ </>
1223
+ );
1224
+ }
1225
+ ```
1226
+
1227
+ #### SideSheet
1228
+
1229
+ Muestra una tarea complementaria desde un borde lateral, como parte del layout o en una superficie modal.
1230
+
1231
+ En pantallas estrechas abre la hoja modal. En una región amplia puedes montar la estándar como parte del layout. Mantén la selección o los cambios del formulario en el padre para conservarlos al cambiar de presentación.
1232
+
1233
+ | Propiedad | Tipo | Inicial | Uso |
1234
+ | --- | --- | --- | --- |
1235
+ | `open` · requerida | `boolean` | — | Controla si la superficie está abierta. |
1236
+ | `onOpenChange` · requerida | `(open: boolean) => void` | — | Solicita abrir o cerrar. Actualiza `open` en la pantalla. |
1237
+ | `variant` | `"standard" \| "modal"` | `'modal'` | `standard` acompaña al contenido y `modal` lo superpone. |
1238
+ | `edge` | `"end" \| "start"` | `'end'` | Borde lógico desde el que aparece la hoja. |
1239
+
1240
+ #### Snackbar
1241
+
1242
+ Confirma el resultado de una acción mediante un mensaje temporal y una acción opcional.
1243
+
1244
+ Monta un solo `Snackbar` en `Portal` bajo `MobileDesignProvider`. `messageKey` permite volver a mostrar un mensaje idéntico. Usa `bottomOffset` para reservar el espacio de un FAB o una barra inferior.
1245
+
1246
+ | Propiedad | Tipo | Inicial | Uso |
1247
+ | --- | --- | --- | --- |
1248
+ | `children` · requerida | `ReactNode` | — | Contenido que se compone dentro del componente. |
1249
+ | `visible` · requerida | `boolean` | — | Controla si la superficie o el indicador se presenta. |
1250
+ | `onDismiss` · requerida | `() => void` | — | Solicita cerrar la superficie. |
1251
+ | `action` | `(Omit<ButtonProps, "children" \| "variant"> & { label: string; })` | — | Configuración de la acción de texto. |
1252
+
1253
+ #### Tooltip y RichTooltip
1254
+
1255
+ Aclara el propósito de un control mediante una ayuda Plain o aporta contenido y acciones con Rich tooltip.
1256
+
1257
+ Usa `Tooltip` para explicar un control y `RichTooltip` para un contenido que incluye título o acciones. `persistent` cambia la forma de apertura de la ayuda rica. Conserva una etiqueta accesible en el control aunque tenga tooltip.
1258
+
1259
+ `Tooltip` necesita `title` y un elemento hijo que sirve de ancla. Usa `RichTooltip` cuando debas incluir contenido o acciones.
1260
+
1261
+ | Propiedad | Tipo | Inicial | Uso |
1262
+ | --- | --- | --- | --- |
1263
+ | `anchor` · requerida | `ReactNode` | — | Control o coordenadas que sirven como origen de la superficie. |
1264
+ | `visible` | `boolean` | — | Controla si la superficie o el indicador se presenta. |
1265
+ | `children` · requerida | `ReactNode` | — | Contenido que se compone dentro del componente. |
1266
+ | `title` | `string` | — | Título visible o contenido principal. |
1267
+
1268
+ ### Carga y progreso
1269
+
1270
+ #### LoadingIndicator y PullToRefresh
1271
+
1272
+ Comunica trabajo indeterminado mediante una secuencia de formas que cambia continuamente.
1273
+
1274
+ Centra el indicador dentro de la región que está cargando. `animating={false}` pausa el movimiento y `visible={false}` oculta la figura. Usa un indicador de progreso si la tarea informa un porcentaje.
1275
+
1276
+ `PullToRefresh` envuelve el contenido desplazable. Controla `refreshing`, ejecuta la actualización en `onRefresh` y configura `threshold` cuando necesites ajustar la distancia de arrastre.
1277
+
1278
+ | Propiedad | Tipo | Inicial | Uso |
1279
+ | --- | --- | --- | --- |
1280
+ | `variant` | `"contained" \| "uncontained"` | `'uncontained'` | Tratamiento visual o modalidad del componente. |
1281
+ | `visible` | `boolean` | `true` | Controla si la superficie o el indicador se presenta. |
1282
+ | `animating` | `boolean` | `true` | Controla si el indicador cambia de forma o gira. |
1283
+ | `size` | `number` | `48` | Tamaño visual o escala semántica del control. |
1284
+
1285
+ #### ProgressIndicator
1286
+
1287
+ Comunica progreso determinado o indeterminado con indicadores lineales y circulares, planos u ondulados.
1288
+
1289
+ Elige `progress` de 0 a 1 cuando conoces el avance. Si usas `progressSource` para animarlo en UI, publica también hitos accesibles con `progress`. Si no entregas ninguno, el indicador es indeterminado.
1290
+
1291
+ `CircularProgressStatus` comunica los estados `progress` y `success`. `ProgressBar` ofrece avance lineal, y `ActivityIndicator` una indicación circular de actividad.
1292
+
1293
+ | Propiedad | Tipo | Inicial | Uso |
1294
+ | --- | --- | --- | --- |
1295
+ | `variant` | `"circular" \| "linear"` | `'linear'` | Tratamiento visual o modalidad del componente. |
1296
+ | `progress` | `number` | — | Fracción de avance determinada de 0 a 1. |
1297
+ | `progressSource` | `SharedValue<number>` | — | Valor compartido que conduce el avance visual en el hilo UI. |
1298
+ | `appearance` | `"flat" \| "wavy"` | `'flat'` | Tratamiento plano u ondulado del indicador. |
1299
+
1300
+ **Ejemplo**
1301
+
1302
+ ```tsx
1303
+ import { useState } from 'react';
1304
+ import { View } from 'react-native';
1305
+ import { ProgressIndicator, Text, Button } from 'mobile-design/react-native';
1306
+
1307
+ export default function Example() {
1308
+ const [progress, setProgress] = useState(0.4);
1309
+
1310
+ return (
1311
+ <View style={{ gap: 24 }}>
1312
+ <Text>Descarga: {Math.round(progress * 100)} %</Text>
1313
+ <ProgressIndicator
1314
+ variant="linear"
1315
+ appearance="wavy"
1316
+ progress={progress}
1317
+ accessibilityLabel="Descarga de la colección"
1318
+ />
1319
+ <Button
1320
+ variant="tonal"
1321
+ onPress={() => setProgress((value) => Math.min(1, value + 0.2))}
1322
+ >
1323
+ Avanzar demostración
1324
+ </Button>
1325
+ </View>
1326
+ );
1327
+ }
1328
+ ```
1329
+
1330
+ ### Primitivas de composición
1331
+
1332
+ | Exportación | Uso |
1333
+ | --- | --- |
1334
+ | `Icon` y `MaterialSymbol` | Renderiza iconos por nombre o solicita explícitamente un glifo del conjunto incluido. El catálogo completo se instala por separado. |
1335
+ | `TouchableRipple` | Compone una superficie interactiva con respuesta de pulsación. Pasa una acción y un nombre accesible cuando el contenido no tenga etiqueta. |
1336
+ | `Avatar.Icon`, `Avatar.Image`, `Avatar.Text` y `Avatar.Button` | Usa `icon`, `source` o `label` para el avatar visual. Envuelve el avatar en `Avatar.Button` con `onPress` y `accessibilityLabel` para abrir un perfil o menú. |
1337
+ | `Portal` | Presenta contenido por encima del layout del padre dentro del mismo proveedor. Las superficies que ya crean su propia ventana o portal no necesitan envolverse para repetir ese comportamiento. |
1338
+ | `ContainerTransformSurface` | Superficie para las composiciones que conservan un origen medido durante su transición. |
1339
+ | `ThemeProvider`, `LocaleProvider`, `useLocale` y `customText` | Primitivas de integración de React Native Paper para tema local, dirección de lectura y variantes de texto personalizadas. |
1340
+ | `mobileDesignCatalog`, `mobileDesignCatalogGroups` y `getMobileDesignComponent` | Metadatos consultables de las familias y grupos. La aplicación decide cómo mostrarlos. |
1341
+
91
1342
  ## Desarrollo en el workspace
92
1343
 
93
1344
  Declara `"mobile-design": "workspace:*"` en la app del workspace. Si usas el catálogo completo, declara también `"@mobile-design/icons": "workspace:*"`. Ejecuta `pnpm install` y `pnpm verify:mobile-design` desde la raíz.