Sistema de Drag & Drop (DnD)
Este módulo implementa un sistema de drag & drop genérico para React Native, usado en Explorax para actividades interactivas (arrastrar opciones, formar palabras, escribir números, etc.).
Se expone como un conjunto de componentes React y un Provider que comparten un mismo estado global basado en Redux.
A grandes rasgos:
DNDProvidercrea y expone el estado global de drag & drop.Draggableregistra elementos que se pueden arrastrar.Droppableregistra áreas que pueden recibir esos elementos.- Componentes de nivel más alto (
MultipleDraggables,PadDND,PadDroppable,CleanButton) construyen patrones listos para usar encima de esos bloques básicos.
¿Qué resuelve este sistema?
- Centraliza toda la lógica de drag & drop (posiciones, colisiones, historia de drops, modos de uso).
- Evita reimplementar gestos, cálculos de áreas y estados en cada pantalla.
- Funciona con datos tipados (
Tgenérico) para que cada draggable pueda llevar la información que tu caso de uso necesite. - Incluye animaciones listas (rebote, desvanecer, “agujero negro”, “latido”) usando
react-native-reanimatedyreact-native-gesture-handler.
Arquitectura general
1. DNDProvider
Envuelve la sección de la UI donde existirá drag & drop:
- Crea internamente un slice de Redux (
createDragDropSlice) con:- El modo de trabajo (
mode). - Un callback global
onDrop(se dispara cuando termina un drag). - Opcionalmente cálculo de centroides (
calcCentroids) para detección más precisa.
- El modo de trabajo (
- Expone hooks:
useDndDispatchuseDndSelectoruseDndActions
Normalmente no necesitas tocar el slice directamente: trabajas con los componentes y, si hace falta, lees algo del estado con
useDndSelector.
2. Draggable
Componente de bajo nivel que:
- Se registra en el store al montarse y se desregistra al desmontarse.
- Mide su posición en la ventana para poder reportar un rectángulo absoluto al store.
- Usa
react-native-gesture-handler(Gesture.Pan) para:- Iniciar el drag (
startDrag). - Reportar movimiento (
moveDrag). - Finalizar el drag (
endDrag) con la posición final.
- Iniciar el drag (
- Usa
react-native-reanimatedpara animar:- Traslación (seguir el dedo).
- Opacidad / escala / rotación (por ejemplo, efecto de “agujero negro” al desaparecer).
- Expone un render prop:
<Draggable<T> data={...}>
{({ isDragging, data, distance }) => (
// Renderizas el contenido según el estado de drag
)}
</Draggable>
distance es una métrica derivada del store que permite efectos como escalar según qué tan lejos se ha arrastrado.
3. Droppable
Área de drop genérica que:
-
Se registra en el store con su rectángulo en pantalla (medido vía
measureInWindow). -
Se re-mide cuando cambian ciertos eventos globales (por ejemplo, cuando algo se mueve).
-
Sabe si hay un draggable encima (
isOver) y qué dato se está arrastrando. -
Mantiene:
data: el último elemento dejado.history: todos los elementos que han caído allí (útil para modos multi-drop).
-
Expone un render prop:
<Droppable<T> onDrop={...}>
{({ isOver, data, history, antiDrop }) => (
// Renderizas según si está encima, qué se ha dejado, etc.
)}
</Droppable>
antiDrop permite “sacar” el último elemento de ese droppable (y sincronizarlo con el store).
Modos de operación (mode)
El DNDProvider recibe un mode que controla el comportamiento global de los draggables después de caer en un droppable:
-
remplacement-multi(default):- El draggable no desaparece.
- El droppable puede acumular historial, pero su “dato actual” se reemplaza.
-
disappear:- El draggable se desvanece y se deshabilita la primera vez que se usa.
-
remplacement-disappear:- Similar a
disappear, pero pensando en escenarios donde el droppable reemplaza siempre el contenido.
- Similar a
-
multi:- Permite multi-drop sin desaparecer el draggable.
-
multi-disappear:- Combina multi-drop con animaciones especiales (por ejemplo, efecto “agujero negro”) antes de restaurar el estado según el caso.
El modo impacta tanto la lógica interna (
statusde cada draggable) como las animaciones que se disparan al realizar un drop.
Componentes de alto nivel
Sobre Draggable y Droppable se construyen componentes más específicos para los casos de uso de Explorax:
-
Draggable: Detalle completo del componente básico. -
Droppable: Detalle del área de drop y sus callbacks. -
- Renderiza varios draggables en grilla.
- Distribuye los items en filas, calcula gaps y escala en función de
workspaceDimensions. - Ideal para bancos de opciones (respuestas, letras, iconos).
-
-
“Pad” genérico para arrastrar:
- Números (
useNumbers). - Caracteres de una palabra (
word). - Opciones personalizadas (
useOptions,options).
- Números (
-
Maneja shuffling, indicadores de orden, drag images y tamaños adaptativos.
-
-
-
Droppable especializado para el pad:
- Usa
FactoryLiteThemedContainer. - Soporta estados de correcto / incorrecto, placeholder y animación de estrellas.
- Usa
-
-
- Botón auxiliar que dispara
cleanAll()en el store. - Limpia estado de drags/drops y permite reiniciar la actividad.
- Botón auxiliar que dispara
Flujo básico de uso
-
Envolver la pantalla Envuelve tu pantalla o sección interactiva con
DNDProvider:<DNDProvider<MyData>
mode="remplacement-multi"
onDrop={(dataByDroppable, historyByDroppable) => {
// Aquí puedes evaluar respuestas o actualizar estado externo
}}
>
{/* Aquí van tus Draggables / Droppables / Pads */}
</DNDProvider> -
Definir los draggables Usa
Draggabledirectamente o un helper comoMultipleDraggables/PadDNDpara generar tus elementos arrastrables. -
Definir las áreas de drop Usa
DroppableoPadDroppableen las posiciones deseadas. Dentro del render prop decides cómo se ve cuando hay algo encima, qué texto mostrar, y qué hacer conantiDrop. -
Opcional: reset Añade
CleanButtono alguna acción equivalente para limpiar todo el estado de la actividad.
Requisitos de entorno
Este sistema asume que el proyecto ya está configurado con:
react-native-gesture-handlerreact-native-reanimated- Redux (el store se crea internamente, pero se usa la API de
react-reduxbajo el hood)
No es necesario integrar el slice de drag & drop al store global de la app: el DNDProvider crea su propio store aislado para esta funcionalidad.
En los siguientes apartados se documenta cada pieza con más detalle: