Draggable
id: draggable title: Draggable sidebar_label: Draggable
El componente Draggable es el bloque básico de elementos arrastrables del sistema de DnD.
Envuelve cualquier UI (texto, imagen, botón, etc.) y la conecta con el estado global del DNDProvider.
Se usa como:
<Draggable<T> data={...}>
{({ isDragging, data, distance }) => (
// Tu UI aquí
)}
</Draggable>
Donde:
Tes el tipo de dato que viaja con el draggable (por ejemplo, un número, una letra, un objeto con metadata).- El hijo es una render prop que recibe el estado del drag en tiempo real.
Firma básica
type DraggableRenderProps<T> = {
/** Datos que viajan con el draggable (los mismos que pasas en `data`) */
data: T;
/** Indica si este elemento está siendo arrastrado en ese momento */
isDragging: boolean;
/**
* Distancia al droppable más cercano.
* Se actualiza en tiempo real mientras se arrastra.
*/
distance: number;
};
interface DraggableProps<T> {
/** Datos del draggable. Debe ser serializable y tipado. */
data: T;
/** Render prop con el contenido visual. */
children: (props: DraggableRenderProps<T>) => React.ReactNode;
/** Deshabilita completamente el drag para este elemento. */
disable?: boolean;
/**
* Si es true, el elemento **vuelve a su posición original**
* después de ser soltado (aunque caiga en un droppable válido).
*/
resetAfterDrop?: boolean;
/**
* Marca el draggable para que pueda deshabilitarse cuando
* todos los droppables relevantes estén llenos.
* (Usado junto con `MultipleDraggables` y la lógica del slice).
*/
disableIfAllFilled?: boolean;
/**
* Si es true, el elemento desaparece (se oculta/deshabilita)
* después de ser soltado correctamente, según el `mode` global.
*/
disappearAfterDrop?: boolean;
/**
* Activa una animación de “shake” (sacudida) en ciertos escenarios
* (por ejemplo, drop inválido o rechazo según la lógica del slice).
*/
shakeItAnimation?: boolean;
/**
* Activa una animación estilo “agujero negro” al desaparecer,
* combinada con el modo `disappear` / `multi-disappear`.
*/
blackHoleAnimation?: boolean;
/**
* Tiempo base para animaciones de movimiento/rebote en ms.
* Ejemplo: 0 para movimiento instantáneo, >0 para animaciones suaves.
*/
timeMove?: number;
}
Nota: La implementación interna usa
react-native-gesture-handleryreact-native-reanimated, pero desde el punto de vista de uso solo necesitas preocuparte por los props y la render prop.
Props en detalle
| Prop | Tipo | Default | Descripción |
|---|---|---|---|
data | T | — | Objeto que representa el contenido lógico del draggable. Es lo que se recibe en onDrop y en la render prop. |
children | (props: DraggableRenderProps<T>) => ReactNode | — | Función que recibe el estado de drag (isDragging, data, distance) y retorna el JSX a renderizar. |
disable | boolean | false | Si está en true, este elemento no responde a gestos de arrastre. |
resetAfterDrop | boolean | false | Si está en true, el draggable vuelve visualmente a su posición original después del drop. Útil para “bancos” de opciones reutilizables. |
disableIfAllFilled | boolean | false | Pensado para usarse junto con MultipleDraggables/droppables: permite deshabilitar el draggable cuando ya no hay lugares válidos donde soltar. |
disappearAfterDrop | boolean | false | Hace que el draggable desaparezca después de un drop válido, consistente con el mode configurado en DNDProvider. |
shakeItAnimation | boolean | false | Activa animaciones de sacudida para feedback visual en situaciones de error o rechazo del drop. |
blackHoleAnimation | boolean | false | Activa animaciones especiales de desaparición (tipo “black hole”) en combinación con modos de desaparición. |
timeMove | number | opcional | Tiempo base de animación (ms) para el movimiento / retorno del draggable. |
Parámetros de la render prop
La función hija recibe un objeto con el estado del draggable:
<Draggable<MyData> data={...}>
{({ data, isDragging, distance }) => {
// data: los datos que pasaste en props
// isDragging: true mientras el usuario está arrastrando
// distance: distancia al droppable más cercano
return (
// tu UI
);
}}
</Draggable>
-
data: el mismo objeto que pasaste endata. -
isDragging:truemientras el dedo/mouse está presionando y moviendo el elemento.- Ideal para cambiar estilos (opacidad, escala, sombra, etc.).
-
distance:- Métrica numérica calculada a partir de la posición del draggable y el droppable más cercano.
- Sirve para animaciones que dependen de qué tan cerca está del objetivo (por ejemplo, escalar más cuando se acerca).
Ejemplo mínimo
import { DNDProvider } from "@components/DnDRevolution";
import { Draggable, Droppable } from "@components/DnDRevolution";
type Option = { id: number; text: string };
export const SimpleExample = () => {
const workspaceDimensions = { width: 300, height: 400 };
return (
<DNDProvider<Option>
mode="remplacement-multi"
onDrop={(dataByDroppable, historyByDroppable) => {
// Evaluar respuestas, actualizar estado externo, etc.
console.log(dataByDroppable, historyByDroppable);
}}
>
{/* Área donde se puede soltar */}
<Droppable<Option> id="target">
{({ isOver, data }) => (
<View
style={{
width: 200,
height: 80,
borderWidth: 2,
borderColor: isOver ? "green" : "gray",
alignItems: "center",
justifyContent: "center",
}}
>
<Text>{data?.text ?? "Suelta aquí"}</Text>
</View>
)}
</Droppable>
{/* Elemento arrastrable */}
<Draggable<Option>
data={{ id: 1, text: "Respuesta A" }}
resetAfterDrop
>
{({ data, isDragging }) => (
<View
style={{
marginTop: 24,
paddingHorizontal: 16,
paddingVertical: 8,
borderRadius: 8,
backgroundColor: isDragging ? "#4f46e5" : "#6366f1",
}}
>
<Text style={{ color: "white" }}>{data.text}</Text>
</View>
)}
</Draggable>
</DNDProvider>
);
};
Uso con MultipleDraggables
MultipleDraggables construye muchos Draggable en grilla y les pasa varios flags:
<MultipleDraggables<PadObject>
data={options}
workspaceDimensions={workspaceDimensions}
resetAfterDrop
disappearAfterDrop={false}
disableIfAllFilled
shakeItAnimation
blackHoleAnimation={false}
>
{({ isDragging, data, distance, idx }) => (
// aquí tú decides cómo se ve cada opción
)}
</MultipleDraggables>
Internamente, este componente:
- Crea un
Draggablepor cada elemento endata. - Calcula el layout (filas/columnas) en función de
workspaceDimensions. - Usa
distancepara animaciones responsivas (por ejemplo, escalar al acercarse al droppable).
Uso en PadDND
En PadDND se usa Draggable para construir pads de números, letras u opciones personalizadas:
- Modo números:
0–9(y opcionalmente.) se generan como draggables. - Modo palabra: cada carácter de
wordse vuelve un draggable. - Modo opciones: se usan
PadObject<T>personalizados comodata.
Ejemplo (simplificado):
{Array.from({ length: 10 }, (_, i) => (
<Draggable<PadObject>
key={i}
timeMove={0}
data={{ id: i, text: `${i}` }}
resetAfterDrop
disable={disable}
>
{({ data, isDragging }) => (
// Render del botón con el número
)}
</Draggable>
))}
Interacción con DNDProvider y el store
Cuando utilizas Draggable dentro de un DNDProvider:
-
Al montarse:
- Se registra en el slice (
registerDraggable).
- Se registra en el slice (
-
Al iniciar drag:
- Notifica su
id,datay rectángulo al store (startDrag).
- Notifica su
-
Mientras se mueve:
- Actualiza su rectángulo y la
distanceal droppable más cercano (moveDrag+setDistance).
- Actualiza su rectángulo y la
-
Al soltar:
-
El slice decide el mejor droppable (
getBestDroppable) y actualiza:droppedhistory/legacyHistorydataDroppable
-
Se dispara el callback global
onDropdelDNDProvider.
-
Si se llama a cleanAll() (por ejemplo, con CleanButton):
- El slice limpia
history,dataDroppable,droppedy marcadeleteData. - Cada
Draggablepuede reaccionar a ese flag para resetear su posición/estado visual.
Cuándo usar Draggable directamente
Usa Draggable directo cuando:
-
Quieres control total sobre la UI y el layout.
-
Estás construyendo un patrón nuevo que no encaja con:
Si tu caso es “banco de opciones en grilla” o “pad de números/letras”, es más sencillo partir de esos componentes de alto nivel y solo personalizar la render prop.
Siguiente sección recomendada: 👉 Droppable