mobile-design 0.1.1 → 0.1.3
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.
- package/COMPONENTS.md +3053 -1625
- package/README.md +115 -265
- package/examples/navigation-stack.tsx +132 -0
- package/package.json +8 -2
- package/src/components/fab.tsx +9 -10
- package/src/components/text-field.tsx +16 -5
package/README.md
CHANGED
|
@@ -4,9 +4,9 @@
|
|
|
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) · [
|
|
7
|
+
[Instalación](#instalación-desde-npm) · [Dependencias](#para-qué-sirve-cada-dependencia) · [Primer componente](#primer-componente) · [Proveedor](#configurar-el-proveedor) · [Temas y colores](#temas-y-colores) · [Stack](#tema-del-stack) · [Tipografía e iconos](#tipografía-e-iconos) · [Índice de componentes](#índice-de-componentes)
|
|
8
8
|
|
|
9
|
-
La referencia detallada de propiedades
|
|
9
|
+
La referencia detallada de propiedades y ejemplos viaja con el paquete. Puedes abrir `node_modules/mobile-design/COMPONENTS.md` para consultar la versión instalada. [Referencia en este código fuente](COMPONENTS.md) · [Referencia de la versión publicada](https://cdn.jsdelivr.net/npm/mobile-design@latest/COMPONENTS.md)
|
|
10
10
|
|
|
11
11
|
## Instalación desde npm
|
|
12
12
|
|
|
@@ -31,6 +31,34 @@ npx expo start --web
|
|
|
31
31
|
|
|
32
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.
|
|
33
33
|
|
|
34
|
+
### Para qué sirve cada dependencia
|
|
35
|
+
|
|
36
|
+
Los *peers* forman parte de la configuración de la app. Los comandos anteriores fijan las versiones probadas. En una app Expo, el SDK ya determina las versiones de `react` y `react-native`.
|
|
37
|
+
|
|
38
|
+
| Paquete | Dónde se necesita | Qué aporta |
|
|
39
|
+
| --- | --- | --- |
|
|
40
|
+
| `mobile-design` | Android, iOS y web | Componentes, proveedor MD3, Roboto y los símbolos incluidos. |
|
|
41
|
+
| `@mobile-design/tokens` | Se instala con `mobile-design` | Paletas clara y oscura, tamaños, tipografía y movimiento. Se puede instalar solo si tu app consume únicamente los valores. |
|
|
42
|
+
| `expo` | App Expo o proyecto React Native con módulos Expo configurados | Integra los módulos Expo como fuentes e imágenes. Metro procesa el TypeScript que distribuye el paquete. |
|
|
43
|
+
| `react` | Peer común | Estado, hooks y composición de componentes. |
|
|
44
|
+
| `react-native` | Peer común | Vistas, estilos, accesibilidad y eventos. En web, Metro resuelve los hosts mediante React Native Web. |
|
|
45
|
+
| `react-native-paper` | Peer común | Tema base, iconos por nombre, dirección de lectura, portal y primitivas como `Avatar` y `TouchableRipple`. |
|
|
46
|
+
| `expo-font` | Peer común | Registra Roboto y la fuente de símbolos. También carga las familias que aporta tu app. |
|
|
47
|
+
| `expo-image` | Peer común | Renderiza las imágenes de `Carousel` y `FullScreenCarousel`. |
|
|
48
|
+
| `react-native-gesture-handler` | Peer común | Gestos de arrastre y desplazamiento en hojas, sliders, listas y otros controles. Monta `GestureHandlerRootView` en la raíz. |
|
|
49
|
+
| `react-native-reanimated` | Peer común | Valores compartidos, transiciones de estado y animaciones de los componentes. |
|
|
50
|
+
| `react-native-worklets` | Peer común | Ejecuta callbacks de gestos y animación en UI y comunica resultados al estado de React. |
|
|
51
|
+
| `react-native-safe-area-context` | Peer común | Proporciona los bordes seguros para barras y superficies superpuestas. Monta `SafeAreaProvider` en la raíz. |
|
|
52
|
+
| `react-native-svg` | Peer común | Dibuja indicadores de progreso y las rutas SVG de símbolos disponibles antes de cargar la fuente. |
|
|
53
|
+
| `@gorhom/bottom-sheet` | Peer común | Motor de `BottomSheet` y sus listas, scroll y campos de texto coordinados con el teclado. |
|
|
54
|
+
| `react-dom` | Peer de web | Monta el árbol de React en el DOM. |
|
|
55
|
+
| `react-native-web` | Peer de web | Traduce las primitivas React Native a elementos y eventos del navegador. |
|
|
56
|
+
| `@expo/metro-runtime` | App Expo con Metro en web | Runtime para los bundles web de Expo. Se instala en la app. |
|
|
57
|
+
| `@mobile-design/icons` | Opcional en las tres plataformas | Catálogo completo de Material Symbols Rounded. Instala la versión que corresponda a `mobile-design`. |
|
|
58
|
+
| `@expo/vector-icons` | Opcional en las tres plataformas | Proveedor alternativo si tu app elige, por ejemplo, Ionicons. |
|
|
59
|
+
|
|
60
|
+
### Elegir la entrada del paquete
|
|
61
|
+
|
|
34
62
|
| Entrada | Cuándo usarla |
|
|
35
63
|
| --- | --- |
|
|
36
64
|
| `mobile-design/react-native` | Android e iOS. |
|
|
@@ -89,18 +117,18 @@ export default function App() {
|
|
|
89
117
|
|
|
90
118
|
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
119
|
|
|
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. |
|
|
120
|
+
| Propiedad | Valores y comportamiento | Ejemplo |
|
|
121
|
+
| --- | --- | --- |
|
|
122
|
+
| `children` | Pantallas y componentes que consumen el tema y el host de `Portal`. | `<MobileDesignProvider><Button>Guardar</Button></MobileDesignProvider>` |
|
|
123
|
+
| `colorScheme` | `'light'` o `'dark'`. Si se omite, el proveedor sigue el esquema del sistema y usa claro cuando el sistema no declara modo oscuro. | `colorScheme="dark"` para una vista previa fija. |
|
|
124
|
+
| `theme` | Tema parcial de Paper. Fusiona por rol objetos como `colors`, `fonts`, `shapes`, `elevation` y `motion`. `theme.dark` explícito prevalece sobre `colorScheme`. | `theme={{ fonts: { titleLarge: { fontWeight: '700' } } }}` para enfatizar los títulos grandes. |
|
|
125
|
+
| `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`. | `fontFamily="Inter"` después de registrar Inter. |
|
|
126
|
+
| `languageHeight` | `'small'`, `'medium'`, `'large'` o `'extraLarge'`. El valor inicial es `'small'`. Cambia el interlineado de los estilos MD3 base y enfatizados. | `languageHeight="large"` para texto que requiera más altura de línea. |
|
|
127
|
+
| `motion` | `'expressive'` o `'standard'`. El valor inicial es `'expressive'`. Selecciona el perfil de los resortes espaciales. | `motion="standard"` para usar el perfil espacial estándar. |
|
|
128
|
+
| `reduceMotion` | `'auto'`, `'on'` u `'off'`. El valor inicial es `'auto'` y sigue la preferencia del sistema. | `reduceMotion="on"` para habilitar la reducción en una vista previa. |
|
|
129
|
+
| `direction` | `'ltr'` o `'rtl'`. Si se omite, Paper usa la dirección del entorno. | `direction="rtl"` para una interfaz en árabe. |
|
|
130
|
+
| `settings.icon` | Renderizador para iconos pedidos por nombre. Por defecto usa los Material Symbols del paquete base. | `settings={{ icon: renderMaterialIcon }}` con el renderizador de la colección opcional. |
|
|
131
|
+
| `settings.rippleEffectEnabled` | Habilita el ripple en los controles que consultan este ajuste. Paper lo habilita de forma predeterminada. | `settings={{ rippleEffectEnabled: false }}` para desactivar el efecto en esos controles. |
|
|
104
132
|
|
|
105
133
|
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
134
|
|
|
@@ -223,39 +251,76 @@ export function createBrandTheme(mode: 'light' | 'dark') {
|
|
|
223
251
|
|
|
224
252
|
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
253
|
|
|
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
|
|
254
|
+
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 y sus tipos dentro de @mobile-design/tokens. `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.
|
|
255
|
+
|
|
256
|
+
### Tema del stack
|
|
257
|
+
|
|
258
|
+
```ts
|
|
259
|
+
import { DarkTheme, DefaultTheme } from '@react-navigation/native';
|
|
260
|
+
import { createMobileDesignTheme } from 'mobile-design/react-native';
|
|
261
|
+
import { adaptNavigationTheme } from 'react-native-paper';
|
|
262
|
+
|
|
263
|
+
export const { LightTheme: navLight, DarkTheme: navDark } = adaptNavigationTheme({
|
|
264
|
+
reactNavigationLight: DefaultTheme,
|
|
265
|
+
reactNavigationDark: DarkTheme,
|
|
266
|
+
materialLight: createMobileDesignTheme({ colorScheme: 'light' }),
|
|
267
|
+
materialDark: createMobileDesignTheme({ colorScheme: 'dark' })
|
|
268
|
+
});
|
|
269
|
+
```
|
|
270
|
+
|
|
271
|
+
Dentro de `MobileDesignProvider`, usa `useMobileTheme().dark` para elegir el tema de `NavigationContainer`. Con `headerShown: false`, tu `TopAppBar` usa los colores y Roboto del proveedor. [Stack completo](examples/navigation-stack.tsx) con tres pantallas y un `ScreenLayout` reutilizable. Usa `<ScreenLayout title="Perfil">…</ScreenLayout>` para componer otra pantalla.
|
|
227
272
|
|
|
228
273
|
## Tipografía e iconos
|
|
229
274
|
|
|
230
|
-
Roboto
|
|
275
|
+
Para **Roboto y los símbolos incluidos**, basta `MobileDesignProvider`. El proveedor carga esas fuentes cuando se usan.
|
|
276
|
+
|
|
277
|
+
### Incrustar fuentes con Expo
|
|
278
|
+
|
|
279
|
+
Usa el plugin si necesitas una fuente incrustada en Android o iOS, disponible desde el arranque. Añade la entrada a `expo.plugins` en `app.json` y recompila. Las rutas parten de la raíz de la app.
|
|
280
|
+
|
|
281
|
+
```json
|
|
282
|
+
{
|
|
283
|
+
"expo": {
|
|
284
|
+
"plugins": [["expo-font", {
|
|
285
|
+
"android": { "fonts": [{
|
|
286
|
+
"fontFamily": "Inter",
|
|
287
|
+
"fontDefinitions": [
|
|
288
|
+
{ "path": "./assets/fonts/Inter-Regular.ttf", "weight": 400 },
|
|
289
|
+
{ "path": "./assets/fonts/Inter-Bold.ttf", "weight": 700 }
|
|
290
|
+
]
|
|
291
|
+
}] },
|
|
292
|
+
"ios": { "fonts": ["./assets/fonts/Inter-Regular.ttf", "./assets/fonts/Inter-Bold.ttf"] }
|
|
293
|
+
}]]
|
|
294
|
+
}
|
|
295
|
+
}
|
|
296
|
+
```
|
|
297
|
+
|
|
298
|
+
Selecciona `fontFamily="Inter"` en el proveedor. En iOS usa el nombre interno de la familia. Incluye los pesos y estilos que necesites.
|
|
299
|
+
|
|
300
|
+
### Cargar una fuente propia al iniciar
|
|
231
301
|
|
|
232
|
-
|
|
302
|
+
Usa `useFonts` para cargar la familia en ejecución, también en web.
|
|
233
303
|
|
|
234
304
|
```tsx
|
|
235
305
|
import { useFonts } from 'expo-font';
|
|
236
306
|
import { MobileDesignProvider, Text } from 'mobile-design/react-native';
|
|
237
307
|
|
|
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
308
|
export function CustomTypeface() {
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
309
|
+
const [ready, error] = useFonts([{
|
|
310
|
+
fontFamily: 'Inter',
|
|
311
|
+
fontDefinitions: [
|
|
312
|
+
{ path: require('./assets/fonts/Inter-Regular.ttf'), weight: 400 },
|
|
313
|
+
{ path: require('./assets/fonts/Inter-Bold.ttf'), weight: 700 },
|
|
314
|
+
],
|
|
315
|
+
}]);
|
|
316
|
+
if (error) throw error;
|
|
317
|
+
if (!ready) return null;
|
|
318
|
+
return (
|
|
319
|
+
<MobileDesignProvider fontFamily="Inter">
|
|
320
|
+
<Text variant="bodyLarge">Contenido</Text>
|
|
321
|
+
<Text variant="titleLarge" style={{ fontWeight: '700' }}>Encabezado</Text>
|
|
322
|
+
</MobileDesignProvider>
|
|
323
|
+
);
|
|
259
324
|
}
|
|
260
325
|
```
|
|
261
326
|
|
|
@@ -292,12 +357,16 @@ Los ejemplos se montan bajo el proveedor de la instalación. Importan desde `mob
|
|
|
292
357
|
|
|
293
358
|
El archivo `node_modules/mobile-design/COMPONENTS.md` amplía la tabla de cada familia, las piezas relacionadas y los ejemplos completos.
|
|
294
359
|
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
360
|
+
## Índice de componentes
|
|
361
|
+
|
|
362
|
+
| Familia | Fichas |
|
|
363
|
+
| --- | --- |
|
|
364
|
+
| [Acciones y botones](#acciones-y-botones) | [`Button`](#button), [`IconButton`](#iconbutton), [`ButtonGroup`](#buttongroup), [`SegmentedButtons`](#segmentedbuttons), [`SplitButton`](#splitbutton), [`FAB`](#fab), [`FAB.Extended`](#fabextended), [`FAB.Menu`](#fabmenu) |
|
|
365
|
+
| [Campos y selección](#campos-y-selección) | [`TextField`](#textfield), [`Select`](#select), [`Autocomplete`](#autocomplete), [`Checkbox`](#checkbox), [`RadioButton`](#radiobutton), [`Switch`](#switch), [`Slider`](#slider-y-rangeslider), [`RangeSlider`](#slider-y-rangeslider), [`DatePicker`](#datepicker-y-dockeddatepicker), [`DockedDatePicker`](#datepicker-y-dockeddatepicker), [`TimePicker`](#timepicker) |
|
|
366
|
+
| [Navegación y búsqueda](#navegación-y-búsqueda) | [`TopAppBar`](#topappbar), [`NavigationBar`](#navigationbar), [`NavigationRail`](#navigationrail), [`NavigationDrawer`](#navigationdrawer), [`Tabs`](#tabs-y-tabspager), [`TabsPager`](#tabs-y-tabspager), [`Searchbar`](#searchbar-y-searchview), [`SearchView`](#searchbar-y-searchview), [`Toolbar`](#toolbar) |
|
|
367
|
+
| [Contenido y tipografía](#contenido-y-tipografía) | [`Text`](#text), [`Badge`](#badge-y-badgedbox), [`BadgedBox`](#badge-y-badgedbox), [`Card`](#card), [`Carousel`](#carousel), [`Chip`](#chip), [`Divider`](#divider), [`List`](#list) |
|
|
368
|
+
| [Superficies y ayuda](#superficies-y-ayuda) | [`Menu`](#menu), [`Dialog`](#dialog-y-fullscreendialog), [`FullScreenDialog`](#dialog-y-fullscreendialog), [`BottomSheet`](#bottomsheet), [`SideSheet`](#sidesheet), [`Snackbar`](#snackbar), [`Tooltip`](#tooltip-y-richtooltip), [`RichTooltip`](#tooltip-y-richtooltip) |
|
|
369
|
+
| [Carga y progreso](#carga-y-progreso) | [`LoadingIndicator`](#loadingindicator-y-pulltorefresh), [`PullToRefresh`](#loadingindicator-y-pulltorefresh), [`ProgressIndicator`](#progressindicator) |
|
|
301
370
|
|
|
302
371
|
### Acciones y botones
|
|
303
372
|
|
|
@@ -361,7 +430,7 @@ El componente usa `mode` y `sizeClass`. `mode="contained"` aplica Filled, `conta
|
|
|
361
430
|
|
|
362
431
|
Reúne acciones relacionadas o alternativas de selección. Standard redistribuye el espacio al pulsar. Connected mantiene el grupo unido.
|
|
363
432
|
|
|
364
|
-
Cada ítem puede tener
|
|
433
|
+
Cada ítem puede tener `icon`. Con `showSelectedIcon`, el ítem seleccionado muestra una marca de verificación. `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
434
|
|
|
366
435
|
| Propiedad | Tipo | Inicial | Uso |
|
|
367
436
|
| --- | --- | --- | --- |
|
|
@@ -461,58 +530,6 @@ Controla expanded y ciérralo desde onDismiss. Trigger define el FAB y su nombre
|
|
|
461
530
|
| `expanded` · requerida | `boolean` | — | Estado controlado de expansión. |
|
|
462
531
|
| `onDismiss` · requerida | `() => void` | — | Solicita cerrar la superficie. |
|
|
463
532
|
|
|
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
533
|
### Campos y selección
|
|
517
534
|
|
|
518
535
|
#### TextField
|
|
@@ -525,6 +542,7 @@ El contador muestra la longitud. `maxLength` establece el límite. El ref `TextF
|
|
|
525
542
|
| --- | --- | --- | --- |
|
|
526
543
|
| `value` · requerida | `string` | — | Valor actual controlado por la pantalla. |
|
|
527
544
|
| `onChangeText` · requerida | `(value: string) => void` | — | Comunica el nuevo texto para actualizar el valor controlado. |
|
|
545
|
+
| `size` | `"standard" \| "compact"` | `'standard'` | Altura base de una línea de 56 dp con `standard` o 48 dp con `compact`, en Filled y Outlined. Respeta el escalado del texto. |
|
|
528
546
|
| `variant` | `"filled" \| "outlined"` | — | Tratamiento visual o modalidad del componente. |
|
|
529
547
|
| `error` | `boolean` | — | Activa el tratamiento visual y accesible de error. |
|
|
530
548
|
|
|
@@ -539,6 +557,7 @@ export default function Example() {
|
|
|
539
557
|
|
|
540
558
|
return (
|
|
541
559
|
<TextField
|
|
560
|
+
size="compact"
|
|
542
561
|
variant="outlined"
|
|
543
562
|
label="Nombre de la colección"
|
|
544
563
|
value={name}
|
|
@@ -844,61 +863,6 @@ Para deslizar entre paneles comparte `value`, `position` y la solicitud de selec
|
|
|
844
863
|
| `onValueChange` · requerida | `(key: string) => void` | — | Entrega el siguiente valor para actualizar el estado del consumidor. |
|
|
845
864
|
| `variant` | `"primary" \| "secondary"` | `'primary'` | Tratamiento visual o modalidad del componente. |
|
|
846
865
|
|
|
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
866
|
#### Searchbar y SearchView
|
|
903
867
|
|
|
904
868
|
Conecta una barra de búsqueda con una vista que presenta consulta, sugerencias y resultados.
|
|
@@ -1065,37 +1029,6 @@ Usa `List.Accordion` con `title`, `expanded` y `onPress`. Configura las acciones
|
|
|
1065
1029
|
| `variant` | `"expressive" \| "baseline"` | `'expressive'` | Tratamiento visual o modalidad del componente. |
|
|
1066
1030
|
| `selectionMode` | `"single" \| "multiple"` | — | Define si la selección es inexistente, única o múltiple. |
|
|
1067
1031
|
|
|
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
1032
|
### Superficies y ayuda
|
|
1100
1033
|
|
|
1101
1034
|
#### Menu
|
|
@@ -1113,48 +1046,6 @@ Controla visible y onDismiss y proporciona un anchor que abra el menú.
|
|
|
1113
1046
|
| `onDismiss` | `(() => void)` | — | Solicita cerrar la superficie. |
|
|
1114
1047
|
| `onClosed` | `(() => void)` | — | Avisa cuando concluye la salida de la superficie. |
|
|
1115
1048
|
|
|
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
1049
|
#### Dialog y FullScreenDialog
|
|
1159
1050
|
|
|
1160
1051
|
Presenta información o una tarea que necesita atención antes de volver al contenido anterior.
|
|
@@ -1183,47 +1074,6 @@ Monta `GestureHandlerRootView` como se muestra en el ejemplo de instalación. Us
|
|
|
1183
1074
|
| `variant` | `"standard" \| "modal"` | `'modal'` | `standard` comparte el layout y `modal` presenta una hoja superpuesta. |
|
|
1184
1075
|
| `snapPoints` | `(string \| number)[]` | — | Posiciones de reposo de la hoja. Admite puntos en dp o porcentajes según el tipo. |
|
|
1185
1076
|
|
|
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
1077
|
#### SideSheet
|
|
1228
1078
|
|
|
1229
1079
|
Muestra una tarea complementaria desde un borde lateral, como parte del layout o en una superficie modal.
|
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
import { mobileDesignTokens } from '@mobile-design/tokens';
|
|
2
|
+
import { DarkTheme, DefaultTheme, NavigationContainer } from '@react-navigation/native';
|
|
3
|
+
import { createNativeStackNavigator, type NativeStackScreenProps } from '@react-navigation/native-stack';
|
|
4
|
+
import type { PropsWithChildren } from 'react';
|
|
5
|
+
import { ScrollView, StatusBar, StyleSheet, View } from 'react-native';
|
|
6
|
+
import { GestureHandlerRootView } from 'react-native-gesture-handler';
|
|
7
|
+
import { adaptNavigationTheme } from 'react-native-paper';
|
|
8
|
+
import { SafeAreaProvider, SafeAreaView } from 'react-native-safe-area-context';
|
|
9
|
+
import { Button, MobileDesignProvider, Text, TopAppBar, createMobileDesignTheme, useMobileTheme } from 'mobile-design/react-native';
|
|
10
|
+
|
|
11
|
+
type RootStackParamList = {
|
|
12
|
+
SignIn: undefined;
|
|
13
|
+
SignUp: undefined;
|
|
14
|
+
ForgotPassword: undefined;
|
|
15
|
+
};
|
|
16
|
+
|
|
17
|
+
type ScreenProps<Route extends keyof RootStackParamList> = NativeStackScreenProps<RootStackParamList, Route>;
|
|
18
|
+
|
|
19
|
+
const Stack = createNativeStackNavigator<RootStackParamList>();
|
|
20
|
+
|
|
21
|
+
const { LightTheme: navigationLightTheme, DarkTheme: navigationDarkTheme } = adaptNavigationTheme({
|
|
22
|
+
reactNavigationLight: DefaultTheme,
|
|
23
|
+
reactNavigationDark: DarkTheme,
|
|
24
|
+
materialLight: createMobileDesignTheme({ colorScheme: 'light', fontFamily: 'Roboto' }),
|
|
25
|
+
materialDark: createMobileDesignTheme({ colorScheme: 'dark', fontFamily: 'Roboto' }),
|
|
26
|
+
});
|
|
27
|
+
|
|
28
|
+
type ScreenLayoutProps = PropsWithChildren<{
|
|
29
|
+
title: string;
|
|
30
|
+
heading?: string;
|
|
31
|
+
description?: string;
|
|
32
|
+
onBack?: () => void;
|
|
33
|
+
scroll?: boolean;
|
|
34
|
+
}>;
|
|
35
|
+
|
|
36
|
+
export function ScreenLayout({ title, heading, description, onBack, scroll = true, children }: ScreenLayoutProps) {
|
|
37
|
+
const { colors } = useMobileTheme();
|
|
38
|
+
const content = (
|
|
39
|
+
<>
|
|
40
|
+
{heading ? <Text variant="headlineSmall" accessibilityRole="header">{heading}</Text> : null}
|
|
41
|
+
{description ? <Text variant="bodyMedium" style={{ color: colors.onSurfaceVariant }}>{description}</Text> : null}
|
|
42
|
+
{children}
|
|
43
|
+
</>
|
|
44
|
+
);
|
|
45
|
+
|
|
46
|
+
return (
|
|
47
|
+
<View style={[styles.fill, { backgroundColor: colors.surface }]}>
|
|
48
|
+
<TopAppBar
|
|
49
|
+
title={title}
|
|
50
|
+
navigationIcon={onBack ? 'arrow_back' : undefined}
|
|
51
|
+
navigationLabel="Volver"
|
|
52
|
+
onNavigationPress={onBack}
|
|
53
|
+
/>
|
|
54
|
+
<SafeAreaView style={styles.fill} edges={['left', 'right', 'bottom']}>
|
|
55
|
+
{scroll ? <ScrollView contentContainerStyle={styles.content}>{content}</ScrollView> : content}
|
|
56
|
+
</SafeAreaView>
|
|
57
|
+
</View>
|
|
58
|
+
);
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
function SignInScreen({ navigation }: ScreenProps<'SignIn'>) {
|
|
62
|
+
return (
|
|
63
|
+
<ScreenLayout title="Iniciar sesión" heading="Bienvenido" description="Elige cómo continuar con tu cuenta.">
|
|
64
|
+
<Button variant="filled" onPress={() => navigation.navigate('SignUp')}>Crear cuenta</Button>
|
|
65
|
+
<Button variant="text" onPress={() => navigation.navigate('ForgotPassword')}>Olvidé mi contraseña</Button>
|
|
66
|
+
</ScreenLayout>
|
|
67
|
+
);
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
function SignUpScreen({ navigation }: ScreenProps<'SignUp'>) {
|
|
71
|
+
return (
|
|
72
|
+
<ScreenLayout
|
|
73
|
+
title="Crear cuenta"
|
|
74
|
+
description="Aquí puedes integrar los pasos de registro de tu app."
|
|
75
|
+
onBack={() => navigation.goBack()}
|
|
76
|
+
/>
|
|
77
|
+
);
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
function ForgotPasswordScreen({ navigation }: ScreenProps<'ForgotPassword'>) {
|
|
81
|
+
return (
|
|
82
|
+
<ScreenLayout
|
|
83
|
+
title="Recuperar contraseña"
|
|
84
|
+
description="Aquí puedes integrar la solicitud del código y el cambio de contraseña."
|
|
85
|
+
onBack={() => navigation.goBack()}
|
|
86
|
+
/>
|
|
87
|
+
);
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
function RootNavigator() {
|
|
91
|
+
const { animation, colors, dark } = useMobileTheme();
|
|
92
|
+
const navigationTheme = dark ? navigationDarkTheme : navigationLightTheme;
|
|
93
|
+
|
|
94
|
+
return (
|
|
95
|
+
<NavigationContainer theme={navigationTheme}>
|
|
96
|
+
<StatusBar barStyle={dark ? 'light-content' : 'dark-content'} />
|
|
97
|
+
<Stack.Navigator
|
|
98
|
+
initialRouteName="SignIn"
|
|
99
|
+
screenOptions={{
|
|
100
|
+
headerShown: false,
|
|
101
|
+
animation: animation.scale === 0 ? 'none' : 'default',
|
|
102
|
+
contentStyle: { backgroundColor: colors.surface },
|
|
103
|
+
}}
|
|
104
|
+
>
|
|
105
|
+
<Stack.Screen name="SignIn" component={SignInScreen} />
|
|
106
|
+
<Stack.Screen name="SignUp" component={SignUpScreen} />
|
|
107
|
+
<Stack.Screen name="ForgotPassword" component={ForgotPasswordScreen} />
|
|
108
|
+
</Stack.Navigator>
|
|
109
|
+
</NavigationContainer>
|
|
110
|
+
);
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
export default function App() {
|
|
114
|
+
return (
|
|
115
|
+
<GestureHandlerRootView style={styles.fill}>
|
|
116
|
+
<SafeAreaProvider>
|
|
117
|
+
<MobileDesignProvider fontFamily="Roboto">
|
|
118
|
+
<RootNavigator />
|
|
119
|
+
</MobileDesignProvider>
|
|
120
|
+
</SafeAreaProvider>
|
|
121
|
+
</GestureHandlerRootView>
|
|
122
|
+
);
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
const styles = StyleSheet.create({
|
|
126
|
+
fill: { flex: 1 },
|
|
127
|
+
content: {
|
|
128
|
+
gap: mobileDesignTokens.spacing.small,
|
|
129
|
+
paddingHorizontal: mobileDesignTokens.spacing.medium,
|
|
130
|
+
paddingVertical: mobileDesignTokens.spacing.large,
|
|
131
|
+
},
|
|
132
|
+
});
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "mobile-design",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.3",
|
|
4
4
|
"description": "Material Design 3 components for React Native and React Native Web, built on React Native Paper",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"material-design",
|
|
@@ -32,13 +32,14 @@
|
|
|
32
32
|
"assets/logo.svg",
|
|
33
33
|
"README.md",
|
|
34
34
|
"COMPONENTS.md",
|
|
35
|
+
"examples/navigation-stack.tsx",
|
|
35
36
|
"LICENSE",
|
|
36
37
|
"NOTICE",
|
|
37
38
|
"LICENSE-Apache-2.0.txt"
|
|
38
39
|
],
|
|
39
40
|
"sideEffects": false,
|
|
40
41
|
"dependencies": {
|
|
41
|
-
"@mobile-design/tokens": "0.1.
|
|
42
|
+
"@mobile-design/tokens": "0.1.3"
|
|
42
43
|
},
|
|
43
44
|
"peerDependencies": {
|
|
44
45
|
"react": ">=19.2.0",
|
|
@@ -62,5 +63,10 @@
|
|
|
62
63
|
"react-native-web": {
|
|
63
64
|
"optional": true
|
|
64
65
|
}
|
|
66
|
+
},
|
|
67
|
+
"repository": {
|
|
68
|
+
"type": "git",
|
|
69
|
+
"url": "https://github.com/nicolas-rm/mobile-design.git",
|
|
70
|
+
"directory": "library/packages/mobile-design"
|
|
65
71
|
}
|
|
66
72
|
}
|