@dropi/react-native-design-system
v0.3.32
Published
A React Native package built from scratch
Downloads
1,174
Readme
Dropi - React Native Design System 🎨
El Design System de Dropi para aplicaciones React Native. Este paquete reúne la base visual y funcional de nuestros productos móviles, ofreciendo tokens de diseño, componentes reutilizables y patrones consistentes para mantener la coherencia en la experiencia de usuario. Su objetivo es simplificar el desarrollo, acelerar la implementación de interfaces y garantizar que cada proyecto Dropi mantenga una identidad visual sólida y escalable.
Tabla de contenido
Instalación
Para instalar @dropi/react-native-design-system, sigue estos pasos según tu entorno (Expo o React Native CLI).
1️⃣ Requisitos previos (peerDependencies)
Tu proyecto debe tener instaladas estas dependencias mínimas:
"react": ">=19.0.0",
"react-native": ">=0.79.5",
"dropi-lib-icons": ">=1.3.4",
"expo-image": ">=2.4.0",
"expo-constants": ">=17.1.7",
"lottie-react-native": ">=7.2.2",
"react-native-gesture-handler": ">=2.24.0",
"react-native-reanimated": ">=3.19.1",
"@gorhom/bottom-sheet": ">=5.2.6"📦 Instalación de dependencias base
Instala las dependencias principales:
npm install dropi-lib-icons expo-image expo-constants lottie-react-nativeO con yarn:
yarn add dropi-lib-icons expo-image expo-constants lottie-react-native⚙️ Instalación de dependencias de animación e interacción
Estas dependencias son necesarias para componentes como Bottom Sheet, animaciones Lottie y gestos interactivos:
npm install react-native-reanimated react-native-gesture-handler @gorhom/bottom-sheetO con yarn:
yarn add react-native-reanimated react-native-gesture-handler @gorhom/bottom-sheet🔧 Configuración de react-native-reanimated
Añade el plugin de Reanimated en tu babel.config.js:
module.exports = {
presets: ['module:@react-native/babel-preset'],
plugins: [
'react-native-reanimated/plugin', // Debe ser el último plugin
],
};🔧 Configuración de react-native-gesture-handler
En tu archivo de entrada principal (usualmente App.tsx o index.js), importa gesture-handler al inicio:
import 'react-native-gesture-handler';
import { AppRegistry } from 'react-native';
import App from './App';
// ...2️⃣ Instalación del Design System
Usando npm
npm install @dropi/react-native-design-systemUsando yarn
yarn add @dropi/react-native-design-system3️⃣ Configuración adicional según tu entorno
🔹 Si usas Expo
La mayoría de las dependencias ya están incluidas en Expo. Solo asegúrate de tener las versiones mínimas:
npx expo install expo-image expo-constants lottie-react-native react-native-reanimated react-native-gesture-handler @gorhom/bottom-sheetImportante: Aunque Expo incluye muchas de estas librerías, debes añadir el plugin de Reanimated en tu babel.config.js como se explicó anteriormente.
🔹 Si usas React Native CLI
Debes instalar y configurar todas las dependencias manualmente:
1. Instala las dependencias nativas:
npm install expo-image expo-constants lottie-react-native react-native-reanimated react-native-gesture-handler @gorhom/bottom-sheet2. Para iOS, instala los pods:
cd ios && pod install && cd ..3. Configura react-native-reanimated:
Añade el plugin en tu babel.config.js (debe ser el último plugin):
module.exports = {
presets: ['module:@react-native/babel-preset'],
plugins: ['react-native-reanimated/plugin'],
};4. Configura react-native-gesture-handler:
En tu archivo de entrada (index.js o App.tsx), importa al inicio:
import 'react-native-gesture-handler';5. Limpia y reconstruye:
# Android
npx react-native start --reset-cache
npx react-native run-android
# iOS
npx react-native start --reset-cache
npx react-native run-ios📚 Información adicional sobre las dependencias
| Dependencia | Propósito | Componentes que la usan | | :---------- | :-------- | :---------------------- | | dropi-lib-icons | Sistema de íconos de Dropi | Todos los componentes con íconos | | expo-image | Carga optimizada de imágenes | CustomImage | | expo-constants | Información del dispositivo | Tokens de spacing y sizes | | lottie-react-native | Animaciones Lottie | EmptyState, componentes con animaciones | | react-native-reanimated | Animaciones fluidas de alto rendimiento | BottomSheet, transiciones | | react-native-gesture-handler | Gestos táctiles avanzados | BottomSheet, componentes interactivos | | @gorhom/bottom-sheet | Modales deslizables desde abajo | BottomSheetComponent, Select |
Tokens
Radius
Tokens de radio de borde utilizados en todos los componentes para mantener una coherencia visual en las esquinas redondeadas.
Los tokens radius definen los niveles estándar de redondez de esquinas dentro del sistema de diseño.
Son valores numéricos expresados en píxeles, pensados para ser usados en cualquier componente que soporte propiedades de radio de borde (borderRadius).
Estos tokens aseguran una identidad visual consistente y permiten ajustar globalmente la suavidad de las esquinas con facilidad.
| Token | Valor | Descripción | | :--- | :---: | :--- | | none | 0 | Sin radio de borde (esquinas rectas). | | border-1 | 4 | Redondeo sutil para elementos pequeños como etiquetas o insignias. | | border-2 | 8 | Radio estándar para la mayoría de los componentes. | | border-3 | 12 | Redondeo visible, ideal para tarjetas o modales. | | border-4 | 24 | Esquinas más pronunciadas, usadas en contenedores grandes o interactivos. | | border-5 | 32 | Redondeo máximo estándar, da una apariencia más expresiva. | | circle | 50 | Radio perfecto para formar círculos (por ejemplo, avatares o botones circulares). |
Spacing
Tokens de espaciado que definen los márgenes y rellenos estándar utilizados en todo el sistema de diseño.
Los tokens spacing controlan la separación visual entre elementos (márgenes, paddings, gaps, etc.). Su objetivo es mantener una escala modular de espacios coherente en todas las pantallas, tanto en teléfonos como en tabletas.
El valor de cada token se adapta automáticamente dependiendo del dispositivo:
si es una tableta (isTablet = true), los valores aumentan ligeramente para mantener una proporción visual equilibrada en pantallas más grandes.
| Token | Valor base (px) | En tablet (px) | Descripción | | :-------- | :-------------: | :------------: | :----------- | | size-1 | 4 | 12 | Espaciado mínimo, ideal para pequeños detalles visuales. | | size-2 | 8 | 16 | Margen corto entre textos o íconos. | | size-3 | 12 | 20 | Separación común en layouts compactos. | | size-4 | 16 | 24 | Espaciado estándar para la mayoría de los componentes. | | size-5 | 24 | 32 | Espaciado medio, común entre secciones. | | size-6 | 32 | 40 | Espaciado grande, ideal para pantallas amplias o bloques visuales. | | size-7 | 40 | 48 | Separación generosa entre bloques de contenido. | | size-8 | 48 | 56 | Margen grande para layouts aireados. | | size-9 | 56 | 64 | Espaciado extra grande, usado en vistas principales. | | size-10 | 64 | 72 | Máximo espaciado estándar del sistema. |
Sizes
Tokens de tamaño tipográfico utilizados en el sistema de diseño para mantener una jerarquía visual clara y consistente entre dispositivos.
Los tokens sizes definen una escala modular de tamaños de texto (en píxeles), que se ajusta automáticamente al ancho del dispositivo y a la configuración del usuario (por ejemplo, el escalado de fuente del sistema operativo). De esta forma, el diseño mantiene la proporción visual correcta sin sacrificar accesibilidad.
El cálculo de cada tamaño depende de dos factores:
- ancho de la pantalla
Dimensions.get("window").width - factor de escala de fuente
PixelRatio.getFontScale()Gracias a esto, las fuentes se adaptan suavemente en tabletas o pantallas grandes sin distorsionar el diseño original.
| Token | Valor base (px) | Descripción | | :------ | :-------------: | :---------------------------------------------------- | | xxs | 10 | Texto auxiliar o etiquetas pequeñas. | | xs | 12 | Subtítulos o texto de apoyo en componentes compactos. | | s | 14 | Texto secundario o descripciones. | | m | 16 | Tamaño de texto base, ideal para párrafos. | | l | 18 | Texto destacado o títulos pequeños. | | xl | 20 | Encabezados medianos o botones grandes. | | xxl | 24 | Títulos principales o énfasis visual. | | xxxl | 28 | Secciones destacadas o headers grandes. | | xxxxl | 32 | Títulos hero o pantallas de bienvenida. |
Escalado interno:
const BASE_WIDTH = 440;
const rawScaleFactor = width / BASE_WIDTH;
const multiplier = rawScaleFactor > 1 ? 1 + (rawScaleFactor - 1) * 0.3 : rawScaleFactor;Esto significa que: Si la pantalla es más grande que la base, el texto aumenta gradualmente (hasta un 30% adicional). Si la pantalla es más pequeña, el tamaño se ajusta proporcionalmente.
Weights
Tokens de peso tipográfico utilizados para controlar la jerarquía visual y el énfasis dentro de los textos del sistema.
Los tokens weights establecen los distintos niveles de grosor de las fuentes usados en todos los componentes del sistema. Permiten mantener consistencia tipográfica en botones, títulos, subtítulos y párrafos, evitando el uso arbitrario de valores numéricos. Estos valores siguen la escala estándar de CSS para fontWeight, lo que garantiza compatibilidad con cualquier fuente que soporte pesos variables.
| Token | Valor | Descripción | | :--------- | :---: | :---------------------------------------------------------- | | light | '300' | Ideal para textos secundarios o información complementaria. | | regular | '400' | Peso base para la mayoría de los textos. | | medium | '500' | Ligeramente más grueso, usado en botones o subtítulos. | | semibold | '600' | Para destacar encabezados o valores clave. | | bold | '700' | Peso más alto, usado en títulos o llamadas a la acción. |
Colors
Los tokens de color definen la paleta cromática oficial de Dropi para interfaces móviles. Están diseñados para ofrecer consistencia visual, legibilidad y accesibilidad tanto en modo claro como en modo oscuro.
Cada color está organizado por familias cromáticas (Primary, Secondary, Gray, Success, Error, Info, Warning) y subdividido en niveles tonales del 50 al 900.
Esto permite crear jerarquías visuales precisas —por ejemplo, usar tonos 500 para elementos principales y 100/900 para fondos o bordes.
Cada token incluye dos variantes:
lightdark
De esta forma, el sistema puede alternar entre temas sin perder coherencia cromática ni contraste visual.
| Familia | Propósito principal | | :--------- | :---------------------------------------------------------------------------------------------- | | Primary | Color de marca principal. Se usa en botones primarios, íconos destacados y elementos de acción. | | Secondary | Color de acento o refuerzo visual para elementos secundarios. | | Gray | Escala neutra para fondos, textos y bordes. Define la estructura visual base. | | Success | Representa estados exitosos, confirmaciones o acciones completadas. | | Error | Indica errores, validaciones fallidas o acciones críticas. | | Info | Se usa para mostrar información contextual o mensajes neutrales. | | Warning | Señala advertencias, riesgos o acciones pendientes.
Átomos
Textos
Body
Body es el componente tipográfico principal del sistema de diseño. Está pensado para manejar el texto estándar de la aplicación, incluyendo descripciones, párrafos, mensajes y contenido general.
import { Body } from '@dropi/react-native-design-system';⚙️ Props:
| Prop | Tipo | Descripción | | :------- | :------------------------------------------------------------------------------------------------------------------- | :------------------------------------------------------------- | | type | 'xs-regular' | 'xs-medium' | 's-regular' | 's-medium' | 'm-regular' | 'm-medium' | 'l-regular' | 'l-medium' | Define el tamaño y el peso del texto. | | style | TextStyle | (Opcional) Estilos adicionales que se aplican al texto. | | ...rest | TextProps | Todas las props nativas de Text disponibles en React Native. |
🧩 Ejemplo de uso:
<Body type="m-regular"> Este es un texto de ejemplo utilizando el componente body </Body>Caption
El componente Caption representa textos pequeños de apoyo, utilizados principalmente para mostrar etiquetas, subtítulos, o descripciones breves dentro de la interfaz.
Forma parte de la familia tipográfica del sistema y mantiene proporciones ajustadas para espacios reducidos, con un line-height ligeramente más compacto para optimizar la densidad visual.
📦 Importación:
import { Caption } from '@dropi/react-native-design-system';⚙️ Props:
| Prop | Tipo | Descripción | | :------- | :------------------------------------- | :-------------------------------------------------------------- | | type | 's' | 'm' | 's-light' | 'm-light' | Define el tamaño y el peso del texto. | | style | TextStyle | (Opcional) Estilos adicionales para personalizar la apariencia. | | ...rest | TextProps | Todas las props nativas disponibles en Text de React Native. |
🧩 Ejemplo de uso:
<Caption type="m">Última actualización</Caption>
<Caption type="s-light" style={{ color: '#999' }}>2 horas atrás</Caption>Heading
El componente Heading representa los encabezados tipográficos del sistema, usados para jerarquizar títulos, secciones y bloques de contenido.
Cada nivel (h1 a h5) conserva proporciones equilibradas entre tamaño, peso y altura de línea, asegurando legibilidad sin romper la escala visual general de la aplicación.
📦 Importación:
import { Heading } from '@dropi/react-native-design-system';⚙️ Props:
| Prop | Tipo | Descripción | | :------- | :------------------------------------- | :------------------------------------------------------------ | | type | 'h1' | 'h2' | 'h3' | 'h4' | 'h5' | Define el nivel jerárquico del título. | | style | TextStyle | (Opcional) Permite agregar estilos adicionales al encabezado. | | ...rest | TextProps | Cualquier prop nativa del componente Text de React Native. |
🧩 Ejemplo de uso:
<Heading type="h1">Bienvenido a la experiencia Dropi</Heading>
<Heading type="h4" style={{ color: '#666' }}>Configuración avanzada</Heading>Label
El componente Label se utiliza para mostrar textos cortos de énfasis o identificación, como títulos de campos, categorías, o etiquetas de estado.
Su diseño mantiene un tamaño reducido con alto contraste tipográfico (peso bold), lo que facilita su lectura incluso en elementos pequeños o componentes interactivos.
📦 Importación:
import { Label } from '@dropi/react-native-design-system';⚙️ Props:
| Prop | Tipo | Descripción | | :------- | :----------------- | :----------------------------------------------------------- | | type | 's' | 'm' | 'l' | Define el tamaño de la etiqueta. | | style | TextStyle | (Opcional) Estilos adicionales para personalización. | | ...rest | TextProps | Cualquier prop nativa del componente Text de React Native. |
🧩 Ejemplo de usu:
<Label type="m">Dirección</Label>
<Label type="s" style={{ color: '#999' }}>En proceso</Label>Botones
Default Button
El componente DefaultButton es el botón base del sistema de diseño. Está diseñado para ser consistente, flexible y accesible, permitiendo manejar variaciones visuales (variant), tamaños (size), e iconos opcionales antes o después del texto. Incluye variantes semánticas para comunicar estados (éxito, error, advertencia, información) además de las variantes estándar (primario, secundario, terciario). Integra estados de desactivación y carga sin perder la coherencia visual, y soporta configuración personalizada de colores mediante buttonConfig.
📦 Importación:
import { DefaultButton, type Variant, type ButtonConfig } from '@dropi/react-native-design-system';⚙️ Props:
| Prop | Tipo | Descripción |
| :---------------- | :----------------------------------------------------------------------------------------- | :------------------------------------------------------------------------------------------- |
| label | string (opcional) | Texto visible del botón. |
| variant | Variant | (requerido) Define el estilo y color del botón. Ver tipo Variant abajo. |
| size | 'small' | 'normal' | 'large' | (requerido) Controla el tamaño del botón y del texto. |
| preIcon | IconName (opcional) | Ícono mostrado antes del texto. |
| postIcon | IconName (opcional) | Ícono mostrado después del texto. |
| disabled | boolean (opcional) | Desactiva la interacción y reduce la opacidad. Por defecto: false. |
| isMakingRequest | boolean (opcional) | Muestra un ActivityIndicator en lugar del texto mientras se realiza una acción. |
| buttonConfig | ButtonConfig (opcional) | Configuración personalizada de colores. Ver tipo ButtonConfig abajo. |
| ...rest | TouchableOpacityProps | Props nativas de React Native para eventos o estilos adicionales. |
🔧 Tipos exportados:
Variant - Define los estilos disponibles para el botón:
type Variant =
| "primary" // Botón principal con color de marca
| "secondary" // Botón secundario con borde
| "tertiary" // Botón terciario sin relleno
| "success" // Variante semántica para confirmaciones
| "error" // Variante semántica para acciones peligrosas
| "warning" // Variante semántica para advertencias
| "info" // Variante semántica para informaciónButtonConfig - Configuración personalizada de colores:
type ButtonConfig = {
backgroundColor: string // Color de fondo del botón
labelColor: string // Color del texto
borderColor: string // Color del borde
}🧩 Ejemplos de uso:
// Variantes estándar
<DefaultButton
label="Guardar cambios"
variant="primary"
size="normal"
onPress={handleSave}
/>
<DefaultButton
label="Cancelar"
variant="secondary"
size="small"
preIcon="arrow-left"
onPress={handleCancel}
/>
<DefaultButton
label="Más opciones"
variant="tertiary"
size="large"
postIcon="chevron-down"
/>
// Variantes semánticas
<DefaultButton
label="Confirmar"
variant="success"
size="normal"
preIcon="check"
onPress={handleConfirm}
/>
<DefaultButton
label="Eliminar"
variant="error"
size="normal"
onPress={handleDelete}
/>
<DefaultButton
label="Advertencia"
variant="warning"
size="normal"
/>
<DefaultButton
label="Información"
variant="info"
size="normal"
/>
// Con estado de carga
<DefaultButton
label="Cargando..."
variant="primary"
size="large"
isMakingRequest
/>
// Con deshabilitación
<DefaultButton
label="Completado"
variant="primary"
size="normal"
disabled={true}
/>
// Configuración personalizada (color personalizado)
<DefaultButton
label="Personalizado"
variant="primary"
size="normal"
buttonConfig={{
backgroundColor: '#FF6B00',
labelColor: '#FFFFFF',
borderColor: '#FF6B00'
}}
onPress={handleCustom}
/>Feedback Button
El FeedbackButton es un botón semántico diseñado para comunicar estados del sistema (éxito, error, advertencia, información) a través de una codificación cromática clara y reutilizable. Combina tres variantes visuales (primary, secondary, text) con tres tamaños (small, normal, large), soporte para iconos antes y después del texto, y control de estados (deshabilitado / en proceso). Su propósito es facilitar acciones que requieren retroalimentación inmediata al usuario sin perder la consistencia tipográfica y espacial del sistema; por eso deriva sus colores de los tokens Success/Error/Warning/Info y reutiliza Label, spacing y radius para garantizar uniformidad en toda la interfaz.
📦 Importación:
import { FeedbackButton } from '@dropi/react-native-design-system'⚙️ Props:
| Prop | Tipo | Descripción | | :------------------ | :------------------------------------------------------------------ | :------------------------------------------------------------- | | label | string | Texto visible en el botón. | | feedbackType | 'success' | 'error' | 'warning' | 'info' | Define el tipo de feedback visual (color principal del botón). | | variant | 'primary' | 'secondary' | 'text' | Determina el estilo del botón (relleno, borde o texto plano). | | size | 'small' | 'normal' | 'large' | Ajusta padding, tamaño de ícono y tipografía. | | preIcon | IconName (opcional) | Ícono mostrado antes del texto. | | postIcon | IconName (opcional) | Ícono mostrado después del texto. | | disabled | boolean (opcional) | Desactiva interacción y aplica opacidad reducida. | | isMakingRerequest | boolean (opcional) | Indica que hay una solicitud o acción en curso. |
🧩 Ejemplos de uso:
<FeedbackButton
label="Reintentar"
feedbackType="error"
variant="primary"
size="normal"
preIcon="refresh"
onPress={() => console.log('Reintentando acción...')}
/>Text Button
El TextButton es un botón ligero y no intrusivo, pensado para acciones secundarias donde el énfasis visual debe recaer en el texto más que en el fondo. Puede incluir íconos antes o después del texto y adaptarse a diferentes tamaños (small, normal, large). Su diseño se basa en la simplicidad y flexibilidad: hereda la tipografía desde Label, respeta los espacios del sistema (spacing) y se ajusta automáticamente en tablets gracias a isTablet. Además, permite sobrescribir el color con replaceColor para integrarse fácilmente en contextos personalizados.
📦 Importación:
import { TextButton } from "@dropi/react-native-design-system";⚙️ Props:
| Prop | Tipo | Descripción | | :------------- | :------------------------------- | :------------------------------------------------ | | label | string | Texto visible dentro del botón. | | variant | 'primary' | 'secondary' | Define el color base del texto e íconos. | | size | 'small' | 'normal' | 'large' | Controla el tamaño del texto e íconos. | | preIcon | IconName | Ícono que aparece antes del texto. | | postIcon | IconName | Ícono que aparece después del texto. | | replaceColor | string | Sobrescribe el color del texto y los íconos. | | ...rest | TouchableOpacityProps | Hereda cualquier propiedad de TouchableOpacity. |
🧩 Ejemplos de uso:
<TextButton
label="Ver más"
variant="primary"
size="normal"
preIcon="IconName"
onPress={() => console.log("Ver más presionado")}
/>
<TextButton
label="Eliminar"
variant="secondary"
size="small"
replaceColor="#FF3B30"
postIcon="IconName"
onPress={() => console.log("Eliminar presionado")}
/>
Imágenes
Custom Image
El componente CustomImage es un wrapper optimizado del componente Image de expo-image que agrega soporte automático para imágenes de respaldo (fallback) en caso de error de carga. Está diseñado para manejar elegantemente errores de carga de imágenes, mostrando una imagen alternativa cuando la fuente principal falla o no está disponible. Incluye transiciones suaves, soporte para placeholders, y manejo inteligente de fuentes locales y remotas.
📦 Importación:
import { CustomImage } from '@dropi/react-native-design-system';⚙️ Props:
| Prop | Tipo | Descripción |
| :-------------- | :------------------------ | :------------------------------------------------------------------------------------------- |
| source | string | number | Fuente principal de la imagen. Puede ser una URI (string) o un recurso local (require). |
| fallbackSource | string | number | (Opcional) Imagen de respaldo mostrada si source falla o está vacía. |
| contentFit | ContentFit | (Opcional) Modo de ajuste de la imagen. Por defecto: 'cover'. |
| ...rest | ImageProps (expo-image) | Todas las props nativas del componente Image de expo-image. |
🔧 Características:
- Fallback automático: Si la imagen principal no carga o falla, muestra automáticamente
fallbackSource. - Placeholder inteligente: Usa
fallbackSourcecomo placeholder mientras carga la imagen principal. - Transición suave: Incluye una transición de 200ms entre estados de carga.
- Manejo de errores: Detecta errores de carga mediante el evento
onErrory cambia a la imagen de respaldo. - Normalización de fuentes: Maneja tanto URIs (strings) como recursos locales (require) de forma transparente.
- Renderizado condicional: No renderiza nada si no hay fuente válida disponible.
🧩 Ejemplos de uso:
// Imagen con fallback remoto
<CustomImage
source="https://example.com/product.jpg"
fallbackSource="https://example.com/placeholder.jpg"
style={{ width: 200, height: 200 }}
contentFit="cover"
/>
// Imagen con fallback local
<CustomImage
source={{ uri: profileImageUrl }}
fallbackSource={require('./assets/default-avatar.png')}
style={{ width: 100, height: 100, borderRadius: 50 }}
contentFit="cover"
/>
// Imagen que solo usa fallback si source está vacío
<CustomImage
source={product.imageUrl || ''}
fallbackSource={require('./assets/no-image.png')}
style={{ width: 300, height: 200 }}
contentFit="contain"
/>
// Con todas las props de expo-image
<CustomImage
source="https://api.example.com/image.jpg"
fallbackSource="https://example.com/fallback.jpg"
style={{ width: '100%', height: 400 }}
contentFit="cover"
transition={500}
cachePolicy="memory-disk"
/>Moléculas
Alert
El componente Alert muestra un mensaje contextual acompañado de un ícono y colores que representan su nivel de severidad. Está diseñado para comunicar información relevante al usuario: advertencias, errores, confirmaciones o simples avisos informativos.
Cada variante (info, warning, error, success) aplica automáticamente colores de fondo, borde e ícono usando el sistema de tokens (colors, sizes, radius, spacing). Además, permite incluir un botón de acción secundaria y un botón de cierre opcional.
📦 Importación:
import { Alert } from "@dropi/react-native-design-system";
⚙️ Props:
| Prop | Tipo | Descripción |
| :-------------- | :------------------------------------------- | :---------------------------------------------------------------- |
| message | string | Texto principal que describe la alerta. |
| variant | 'info' | 'warning' | 'error' | 'success' | Define los colores, ícono y estilo visual general. |
| buttonLabel | string | Texto del botón opcional dentro de la alerta. |
| onButtonPress | () => void | Acción ejecutada al presionar el botón opcional. |
| onClosePress | () => void | Acción ejecutada al presionar el botón de cierre (close-small). |
🧩 Ejemplos de uso:
<Alert
message="Tu información ha sido guardada correctamente."
variant="success"
onClosePress={() => console.log("Cerrar alerta")}
/>
<Alert
message="No pudimos procesar tu solicitud."
variant="error"
buttonLabel="Reintentar"
onButtonPress={() => console.log("Reintentar")}
onClosePress={() => console.log("Cerrar")}
/>
Chip
El Chip es un componente compacto utilizado para mostrar etiquetas, identificadores o categorías de manera visual. Combina texto, íconos opcionales y un estilo diferenciado por variantes (primary / tertiary) para adaptarse a distintos contextos. Además, puede configurarse como descartable (isDismissable), mostrando un ícono de cierre que ejecuta una acción personalizada. Es ideal para mostrar información resumida como IDs de producto, estados, tags o filtros activos.
📦 Importación:
import { Chip } from "@dropi/react-native-design-system";⚙️ Props:
| Prop | Tipo | Descripción |
| :------------- | :------------------------- | :--------------------------------------------------------------------------- |
| variant | 'primary' | 'tertiary' | (Opcional) Define el estilo visual. Por defecto es 'tertiary'. |
| preIcon | IconName | (Opcional) Ícono que aparece antes del texto. |
| label | string | Texto que se muestra en el chip. |
| isDismissable | boolean | (Opcional) Muestra un ícono de cierre (cross-circle) a la derecha. |
| onDismiss | () => void | (Opcional) Callback ejecutado al presionar el ícono de cierre. |
🧩 Ejemplos de uso:
<Chip label="ID: 12345" variant="tertiary" />
<Chip
label="VARIABLE"
variant="primary"
/>
<Chip
label="Promo activa"
variant="primary"
preIcon="tag-outline"
/>
<Chip
label="Filtro aplicado"
variant="tertiary"
isDismissable
onDismiss={() => console.log("Filtro eliminado")}
/>Bottom Sheet
El BottomSheetComponent es un modal que se desliza desde la parte inferior de la pantalla, diseñado para mostrar contenido contextual sin interrumpir completamente la experiencia del usuario. Incluye un backdrop semitransparente, un header con título opcional y botón de cierre, soporte para footer personalizado y snap points configurables para controlar las alturas disponibles del sheet. El componente expone métodos imperativos (present, close, dismiss) mediante forwardRef, permitiendo un control programático completo desde el componente padre. Internamente usa @gorhom/bottom-sheet y mantiene la coherencia visual con los tokens del design system.
📦 Importación:
import { BottomSheetComponent, BottomSheetComponentRef } from "@dropi/react-native-design-system";⚙️ Props:
| Prop | Tipo | Descripción |
| :----------- | :------------- | :----------------------------------------------------------------------------- |
| children | ReactNode | Contenido principal que se muestra dentro del bottom sheet. |
| footer | ReactNode | (Opcional) Contenido del footer fijo en la parte inferior del sheet. |
| title | string | (Opcional) Título mostrado en el header. Si no se provee, solo se muestra el botón de cierre. |
| snapPoints | string[] | (Opcional) Array de alturas disponibles (ej: ['50%', '90%']). Por defecto: ['70%', '90%']. |
| onDismiss | () => void | (Opcional) Callback ejecutado cuando el sheet se cierra completamente. |
| ref | BottomSheetComponentRef | Referencia para controlar el sheet imperativamente (present, close, dismiss). |
🔧 Métodos expuestos (via ref):
| Método | Descripción | | :---------- | :----------------------------------------------------------------------------- | | present() | Abre el bottom sheet desde la parte inferior. | | close() | Cierra el sheet de forma animada. | | dismiss() | Descarta el sheet inmediatamente. |
🧩 Ejemplos de uso:
import { useRef } from 'react';
import { Button, View } from 'react-native';
import { BottomSheetComponent, BottomSheetComponentRef, Body, DefaultButton } from '@dropi/react-native-design-system';
const MyScreen = () => {
const bottomSheetRef = useRef<BottomSheetComponentRef>(null);
return (
<View>
<Button
title="Abrir opciones"
onPress={() => bottomSheetRef.current?.present()}
/>
<BottomSheetComponent
ref={bottomSheetRef}
title="Opciones de envío"
snapPoints={['50%', '80%']}
onDismiss={() => console.log('Sheet cerrado')}
>
<View style={{ padding: 20 }}>
<Body type="m-regular">Selecciona tu método de envío preferido</Body>
</View>
</BottomSheetComponent>
</View>
);
};
// Con footer personalizado
<BottomSheetComponent
ref={sheetRef}
title="Confirmar pedido"
footer={
<DefaultButton
label="Confirmar"
variant="primary"
size="normal"
onPress={handleConfirm}
/>
}
>
<Body type="m-regular">Revisa los detalles antes de continuar</Body>
</BottomSheetComponent>
// Sin título (solo botón de cierre)
<BottomSheetComponent
ref={sheetRef}
snapPoints={['40%']}
>
<Body type="m-regular">Contenido simple sin título</Body>
</BottomSheetComponent>Input
Los componentes de Input proporcionan campos de entrada de texto especializados con validación integrada, estados visuales dinámicos y soporte para íconos y ayuda contextual. Cada variante (TextInput, EmailInput, NumberInput, PasswordInput) hereda de BaseInput y añade lógica de validación específica, manejo de bordes según el estado (foco, error, default) y mensajes de error personalizables. Los inputs están diseñados para mantener consistencia visual con el design system, usando colores, espaciado y tipografía del sistema de tokens.
📦 Importación:
import { TextInput, EmailInput, NumberInput, PasswordInput } from "@dropi/react-native-design-system";TextInput
El TextInput es un campo de entrada de texto genérico que permite al usuario ingresar cualquier tipo de contenido textual. Incluye soporte para label, placeholder, helper text, íconos previos, validación de errores y modo lectura. El estado visual cambia dinámicamente según si el campo está enfocado, tiene error o está en estado normal.
⚙️ Props:
| Prop | Tipo | Descripción |
| :------------- | :------------------- | :------------------------------------------------------------------------------- |
| label | string (opcional) | Etiqueta que se muestra encima del input. |
| value | string | Valor actual del input. |
| onChange | (value: string) => void | Callback ejecutado cuando el texto cambia. |
| hasError | boolean | Indica si el input está en estado de error. |
| errorMessage | string (opcional) | Mensaje de error mostrado debajo del input. |
| placeholder | string (opcional) | Texto placeholder del input. |
| preIcon | IconName (opcional)| Ícono mostrado antes del texto dentro del input. |
| helper | string (opcional) | Texto de ayuda mostrado debajo del input (solo si no hay error). |
| tooltip | string (opcional) | Tooltip asociado al input. |
| readOnly | boolean (opcional) | Si es true, el input es de solo lectura. Por defecto: false. |
🧩 Ejemplo de uso:
const [name, setName] = useState("");
const [error, setError] = useState(false);
<TextInput
label="Nombre completo"
value={name}
onChange={setName}
placeholder="Ingresa tu nombre"
preIcon="user"
helper="Tu nombre será visible en tu perfil"
hasError={error}
errorMessage={error ? "El nombre es requerido" : undefined}
onBlur={() => setError(name.trim() === "")}
/>EmailInput
El EmailInput es un campo especializado para direcciones de correo electrónico con validación automática de formato. Cuando el usuario abandona el campo (blur), valida automáticamente que sea un email válido usando una expresión regular. Si la validación falla, muestra un mensaje de error personalizado. El input convierte automáticamente el texto a minúsculas para normalización.
⚙️ Props:
| Prop | Tipo | Descripción |
| :------------- | :------------------- | :------------------------------------------------------------------------------- |
| value | string | Valor actual del input (dirección de email). |
| onChange | (value: string) => void | Callback ejecutado cuando el email cambia. |
| hasError | boolean | Indica si el input está en estado de error. |
| errorMessage | string (opcional) | Mensaje de error personalizado mostrado debajo del input. |
| preIcon | IconName (opcional)| Ícono mostrado antes del texto dentro del input. |
| helper | string (opcional) | Texto de ayuda mostrado debajo del input (solo si no hay error). |
| tooltip | string (opcional) | Tooltip asociado al input. |
| readOnly | boolean | Si es true, el input es de solo lectura. |
🧩 Ejemplo de uso:
const [email, setEmail] = useState("");
const [emailError, setEmailError] = useState(false);
<EmailInput
value={email}
onChange={setEmail}
preIcon="mail"
helper="Usa un correo electrónico válido"
hasError={emailError}
errorMessage={emailError ? "Por favor verifica tu correo" : undefined}
readOnly={false}
/>NumberInput
El NumberInput es un campo especializado para entrada de números con validación de formato numérico, soporte para decimales y límites de rango (mínimo y máximo). Valida automáticamente que el valor ingresado sea un número válido según las opciones configuradas. Utiliza el keyboard decimal-pad para facilitar la entrada numérica en dispositivos móviles.
⚙️ Props:
| Prop | Tipo | Descripción |
| :------------- | :------------------- | :------------------------------------------------------------------------------- |
| label | string (opcional) | Etiqueta que se muestra encima del input. |
| value | string | Valor actual del input (número como string). |
| onChange | (value: string) => void | Callback ejecutado cuando el número cambia. |
| hasError | boolean | Indica si el input está en estado de error. |
| errorMessage | string (opcional) | Mensaje de error mostrado debajo del input. |
| placeholder | string (opcional) | Texto placeholder del input. |
| preIcon | IconName (opcional)| Ícono mostrado antes del texto dentro del input. |
| helper | string (opcional) | Texto de ayuda mostrado debajo del input (solo si no hay error). |
| tooltip | string (opcional) | Tooltip asociado al input. |
| allowDecimals | boolean (opcional) | Si es true, permite números decimales. Por defecto: true. |
| minValue | number (opcional) | Valor mínimo permitido. |
| maxValue | number (opcional) | Valor máximo permitido. |
| readOnly | boolean (opcional) | Si es true, el input es de solo lectura. Por defecto: false. |
🧩 Ejemplo de uso:
const [price, setPrice] = useState("");
const [priceError, setPriceError] = useState(false);
<NumberInput
label="Precio"
value={price}
onChange={setPrice}
placeholder="0.00"
preIcon="currency-dollar"
helper="Ingresa el precio en COP"
hasError={priceError}
errorMessage={priceError ? "Precio inválido" : undefined}
allowDecimals={true}
minValue={0}
maxValue={999999}
/>PasswordInput
El PasswordInput es un campo especializado para contraseñas con validación de longitud mínima (4 caracteres), toggle de visibilidad de contraseña y bordes dinámicos según estado. El usuario puede alternar entre ver y occultar el texto de la contraseña presionando un ícono integrado. Valida automáticamente que la contraseña tenga al menos 4 caracteres cuando el campo pierde el foco.
⚙️ Props:
| Prop | Tipo | Descripción | | :------------- | :------------------- | :------------------------------------------------------------------------------- | | value | string | Valor actual de la contraseña. | | onChangeFunction | (value: string) => void | Callback ejecutado cuando la contraseña cambia. | | hasError | boolean | Indica si el input está en estado de error. | | errorMessage | string (opcional) | Mensaje de error mostrado debajo del input. | | preIcon | IconName (opcional)| Ícono mostrado antes del texto dentro del input. | | helper | string (opcional) | Texto de ayuda mostrado debajo del input (solo si no hay error). | | tooltip | string (opcional) | Tooltip asociado al input. |
🔧 Características especiales:
- Toggle de visibilidad: Ícono dinámico que permite alternar entre mostrar (
eye) y ocultar (eye-slash) la contraseña. - Validación de longitud: Valida automáticamente que la contraseña tenga al menos 4 caracteres.
- Bordes dinámicos: Cambia de color según el estado (normal: gris, enfocado: azul info, error: rojo).
🧩 Ejemplo de uso:
const [password, setPassword] = useState("");
const [passwordError, setPasswordError] = useState(false);
<PasswordInput
value={password}
onChangeFunction={setPassword}
preIcon="lock"
helper="Mínimo 4 caracteres, incluye mayúsculas y números"
hasError={passwordError}
errorMessage={passwordError ? "Contraseña muy corta" : undefined}
/>Select
El Select es un componente de selección que muestra un campo tipo dropdown con un bottom sheet modal para elegir entre múltiples opciones. Incluye soporte para label, placeholder, helper text, estados de error y validación con bordes dinámicos. El usuario puede explorar las opciones en un modal con scroll, seleccionar una de manera temporal (draft) y confirmar el cambio presionando el botón "Guardar" del footer, que solo se habilita si hubo cambios. Internamente usa TitleDescription para renderizar cada opción con imagen opcional y BottomSheetComponent para la interfaz modal.
📦 Importación:
import { Select, SelectOption } from "@dropi/react-native-design-system";⚙️ Props:
| Prop | Tipo | Descripción |
| :------------ | :------------------------------- | :------------------------------------------------------------------------------- |
| options | SelectOption[] | Array de opciones disponibles. Cada una tiene label, value e image opcional. |
| value | SelectOption | null | (Opcional) Opción actualmente seleccionada. |
| onChange | (option: SelectOption) => void | Callback ejecutado cuando el usuario confirma una nueva selección. |
| label | string | (Opcional) Etiqueta que aparece sobre el campo. |
| placeholder | string | (Opcional) Texto mostrado cuando no hay selección. Por defecto: 'Seleccionar'. |
| helper | string | (Opcional) Texto de ayuda bajo el campo (no se muestra si hay error). |
| hasError | boolean | (Opcional) Indica si el campo tiene un error. Cambia el color del borde. |
| errorMessage | string | (Opcional) Mensaje de error mostrado con ícono. |
| title | string | (Opcional) Título del bottom sheet. Si no se provee, usa label o "Seleccionar". |
| disabled | boolean | (Opcional) Desactiva el campo y reduce la opacidad. Por defecto: false. |
| onRestablish | () => void | (Opcional) Callback para un botón de restablecimiento en el footer. |
🔧 Tipo SelectOption:
type SelectOption = {
label: string
value: string | number
image?: string | number // Imagen opcional para la opción
message?: string // Mensaje adicional en la opción
}🧩 Ejemplos de uso:
import { useState } from 'react';
import { Select, SelectOption } from '@dropi/react-native-design-system';
const MyForm = () => {
const [country, setCountry] = useState<SelectOption | null>(null);
const countries: SelectOption[] = [
{ label: 'Colombia', value: 'CO' },
{ label: 'México', value: 'MX' },
{ label: 'Argentina', value: 'AR' },
];
return (
<Select
label="País"
placeholder="Selecciona tu país"
options={countries}
value={country}
onChange={setCountry}
/>
);
};
// Con helper text
<Select
label="Método de pago"
placeholder="Selecciona un método"
options={paymentMethods}
value={selectedMethod}
onChange={setSelectedMethod}
helper="Puedes cambiar esto más tarde"
/>
// Con estado de error
<Select
label="Ciudad"
placeholder="Selecciona tu ciudad"
options={cities}
value={selectedCity}
onChange={setSelectedCity}
hasError={!selectedCity}
errorMessage="Debes seleccionar una ciudad"
/>
// Con imágenes en las opciones
<Select
label="Categoría"
placeholder="Selecciona una categoría"
options={[
{ label: 'Electrónica', value: 'electronics', image: require('./electronics.png') },
{ label: 'Ropa', value: 'clothing', image: require('./clothing.png') },
{ label: 'Hogar', value: 'home', image: require('./home.png') },
]}
value={category}
onChange={setCategory}
/>
// Con botón de restablecimiento
<Select
label="Preferencias"
options={preferences}
value={selectedPreference}
onChange={setSelectedPreference}
onRestablish={() => setSelectedPreference(null)}
/>MultipleSelect
El MultipleSelect es un componente de selección múltiple que permite al usuario elegir una o más opciones de una lista. Presenta un bottom sheet modal con checkboxes para cada opción, búsqueda opcional para filtrar opciones, y control de cambios. El usuario puede seleccionar/deseleccionar opciones de manera temporal (draft) y confirmar presionando "Guardar", que solo se habilita si hay cambios. Usa Checkbox para renderizar las opciones y Search para el filtrado opcional.
📦 Importación:
import { MultipleSelect, MultipleSelectOption } from "@dropi/react-native-design-system";⚙️ Props:
| Prop | Tipo | Descripción |
| :-------------- | :----------------------------------- | :------------------------------------------------------------------------------- |
| options | MultipleSelectOption[] | Array de opciones disponibles. Cada una tiene label, value y description opcional. |
| selectedValues | (string | number)[] | Array de valores actualmente seleccionados. |
| onChange | (options: MultipleSelectOption[]) => void | Callback ejecutado con el array de opciones seleccionadas confirmadas. |
| label | string | (Opcional) Etiqueta que aparece sobre el campo. |
| placeholder | string | (Opcional) Texto mostrado cuando no hay selecciones. Por defecto: 'Seleccionar'. |
| helper | string | (Opcional) Texto de ayuda bajo el campo (no se muestra si hay error). |
| hasError | boolean | (Opcional) Indica si el campo tiene un error. Cambia el color del borde. |
| errorMessage | string | (Opcional) Mensaje de error mostrado con ícono. |
| title | string | (Opcional) Título del bottom sheet. |
| disabled | boolean | (Opcional) Desactiva el campo y reduce la opacidad. Por defecto: false. |
| searchFilter | boolean | (Opcional) Muestra un campo de búsqueda para filtrar opciones. Por defecto: false. |
| onRestablish | () => void | (Opcional) Callback para un botón de restablecimiento en el footer. |
🔧 Tipo MultipleSelectOption:
type MultipleSelectOption = {
label: string
value: string | number
description?: string // Descripción adicional bajo el label
}🧩 Ejemplos de uso:
import { useState } from 'react';
import { MultipleSelect, MultipleSelectOption } from '@dropi/react-native-design-system';
const MyForm = () => {
const [interests, setInterests] = useState<(string | number)[]>([]);
const interestOptions: MultipleSelectOption[] = [
{ label: 'Deportes', value: 'sports', description: 'Fútbol, basquetbol, etc.' },
{ label: 'Música', value: 'music', description: 'Todos los géneros' },
{ label: 'Viajes', value: 'travel', description: 'Explora nuevos destinos' },
{ label: 'Cocina', value: 'cooking', description: 'Recetas y gastronomía' },
];
const handleChange = (selectedOptions: MultipleSelectOption[]) => {
setInterests(selectedOptions.map(opt => opt.value));
};
return (
<MultipleSelect
label="Mis intereses"
placeholder="Selecciona tus intereses"
options={interestOptions}
selectedValues={interests}
onChange={handleChange}
helper="Elige al menos uno para mejorar tu experiencia"
/>
);
};
// Con búsqueda activada
<MultipleSelect
label="Productos favoritos"
placeholder="Busca y selecciona productos"
options={products}
selectedValues={favoriteProductIds}
onChange={(selected) => setFavoriteProductIds(selected.map(p => p.value))}
searchFilter={true}
helper="Filtra por nombre de producto"
/>
// Con estado de error
<MultipleSelect
label="Permisos"
placeholder="Selecciona permisos"
options={permissionOptions}
selectedValues={selectedPermissions}
onChange={handlePermissionChange}
hasError={selectedPermissions.length === 0}
errorMessage="Debe seleccionar al menos un permiso"
/>
// Con restablecimiento
<MultipleSelect
label="Filtros"
placeholder="Selecciona filtros"
options={filterOptions}
selectedValues={activeFilters}
onChange={setActiveFilters}
onRestablish={() => setActiveFilters([])}
/>
// Con búsqueda y restablecimiento
<MultipleSelect
label="Categorías"
placeholder="Busca y selecciona categorías"
options={categories}
selectedValues={selectedCategories}
onChange={handleCategoryChange}
searchFilter={true}
onRestablish={handleResetCategories}
helper="Puedes filtrar por nombre de categoría"
/>Empty State
El EmptyState es un componente visual diseñado para mostrar pantallas vacías en escenarios donde no hay datos disponibles, ocurrió un estado inicial o se requiere una primera acción del usuario. Puede incluir una imagen o un ícono (no ambos), un título opcional, un mensaje descriptivo y un botón configurable. Mantiene una composición centrada y un diseño minimalista, usando automáticamente tamaños distintos para tablet gracias a isTablet.
📦 Importación:
import { EmptyState } from "@dropi/react-native-design-system";⚙️ Props:
| Prop | Tipo | Descripción |
| :------------- | :-------------------- | :------------------------------------------------------------------------------- |
| message | string | Texto principal explicando el estado. (obligatorio) |
| imageSource | string | number | (Opcional) Imagen (URL o require local) mostrada en la parte superior. |
| icon | IconName | (Opcional) Ícono mostrado en un círculo gris (no se puede usar con imageSource). |
| title | string | (Opcional) Título corto que introduce el estado vacío. |
| buttonLabel | string | (Opcional) Texto del botón. Si existe, el botón se muestra. |
| onButtonPress | () => void | (Opcional) Callback del botón. |
| buttonType | Variant | (Opcional) Variante del botón: 'primary' | 'secondary' | 'tertiary'. Por defecto: 'tertiary'. |
🧩 Ejemplos de uso:
// Con imagen
<EmptyState
imageSource={require("../../assets/empty-orders.png")}
title="Sin pedidos todavía"
message="Cuando tengas pedidos activos aparecerán aquí."
/>
// Con ícono
<EmptyState
icon="inbox"
title="Bandeja vacía"
message="No tienes mensajes nuevos"
/>
// Con botón y variante personalizada
<EmptyState
imageSource="https://example.com/empty.png"
message="Aún no has guardado favoritos."
buttonLabel="Explorar productos"
onButtonPress={() => console.log("Ir a explorar")}
buttonType="primary"
/>
// Solo mensaje (minimal)
<EmptyState
message="No hay resultados para tu búsqueda"
/>Dialog Modal
El DialogModal es un componente de diálogo modal semántico diseñado para comunicar estados críticos del sistema (éxito, error, advertencia, información) de manera prominente. Incluye animación Lottie automática según la variante, un ícono opcional con fondo de color, título y descripción, y soporte para uno o dos botones de acción. Soporta modo de marca blanca (isWhiteBrand) para aplicaciones white label que requieren privacidad de marca (sin mostrar información de Dropi). Todos los textos, colores y comportamientos se adaptan automáticamente según el tipo de diálogo.
📦 Importación:
import { DialogModal, DialogModalTypes } from "@dropi/react-native-design-system";⚙️ Props:
| Prop | Tipo | Descripción |
| :---------------- | :-------------------------- | :------------------------------------------------------------------------------- |
| variant | 'success' | 'error' | 'warning' | 'info' | Define el tipo de diálogo, color e ícono/animación. |
| title | string | Título del diálogo. |
| message | string | Mensaje descriptivo principal. |
| setShowAlert | (flag: boolean) => void | Callback para cerrar el diálogo. |
| buttonText | string | (Opcional) Texto del botón principal. Por defecto: 'Aceptar'. |
| secondButtonText | string | (Opcional) Texto del segundo botón. Si se proporciona, se muestra un botón secundario. |
| handlePrimaryBtn | () => void | (Opcional) Callback del botón principal. |
| handleSecundaryBtn| () => void | (Opcional) Callback del segundo botón. |
| isWhiteBrand | boolean | (Opcional) Si es true, desactiva animaciones Lottie y reemplaza con ícono estático en círculo de color para aplicaciones white label/marca blanca. Por defecto: false. |
🔧 Características especiales:
- Animaciones dinámicas: Cada variante tiene una animación Lottie exclusiva (afirmación, negación, alerta, pregunta).
- Colores semánticos: Los colores del ícono y botones cambian según la variante.
- Botón de cierre: Siempre hay un botón
Xen la esquina superior derecha. - Modo White Label: Cuando
isWhiteBrandestrue, reemplaza animaciones Lottie por ícono estático, ideal para aplicaciones marca blanca sin branding de Dropi. - Adaptabilidad: En modo white brand, los botones heredan el color de la variante para mantener consistencia visual.
🧩 Ejemplos de uso:
import { useState } from 'react';
import { DialogModal } from '@dropi/react-native-design-system';
const MyComponent = () => {
const [showDialog, setShowDialog] = useState(false);
const handleConfirm = () => {
console.log('Confirmado');
setShowDialog(false);
};
return (
<>
{showDialog && (
<DialogModal
variant="success"
title="¡Éxito!"
message="Tu pedido ha sido confirmado correctamente."
buttonText="Continuar"
handlePrimaryBtn={handleConfirm}
setShowAlert={setShowDialog}
/>
)}
</>
);
};
// Con dos botones (confirmación)
<DialogModal
variant="warning"
title="Confirmar acción"
message="¿Estás seguro de que deseas eliminar este elemento? Esta acción no se puede deshacer."
