Skip to main content

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:

  • T es 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-handler y react-native-reanimated, pero desde el punto de vista de uso solo necesitas preocuparte por los props y la render prop.


Props en detalle

PropTipoDefaultDescripción
dataTObjeto que representa el contenido lógico del draggable. Es lo que se recibe en onDrop y en la render prop.
children(props: DraggableRenderProps<T>) => ReactNodeFunción que recibe el estado de drag (isDragging, data, distance) y retorna el JSX a renderizar.
disablebooleanfalseSi está en true, este elemento no responde a gestos de arrastre.
resetAfterDropbooleanfalseSi está en true, el draggable vuelve visualmente a su posición original después del drop. Útil para “bancos” de opciones reutilizables.
disableIfAllFilledbooleanfalsePensado para usarse junto con MultipleDraggables/droppables: permite deshabilitar el draggable cuando ya no hay lugares válidos donde soltar.
disappearAfterDropbooleanfalseHace que el draggable desaparezca después de un drop válido, consistente con el mode configurado en DNDProvider.
shakeItAnimationbooleanfalseActiva animaciones de sacudida para feedback visual en situaciones de error o rechazo del drop.
blackHoleAnimationbooleanfalseActiva animaciones especiales de desaparición (tipo “black hole”) en combinación con modos de desaparición.
timeMovenumberopcionalTiempo 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 en data.

  • isDragging:

    • true mientras 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 Draggable por cada elemento en data.
  • Calcula el layout (filas/columnas) en función de workspaceDimensions.
  • Usa distance para 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 word se vuelve un draggable.
  • Modo opciones: se usan PadObject<T> personalizados como data.

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).
  • Al iniciar drag:

    • Notifica su id, data y rectángulo al store (startDrag).
  • Mientras se mueve:

    • Actualiza su rectángulo y la distance al droppable más cercano (moveDrag + setDistance).
  • Al soltar:

    • El slice decide el mejor droppable (getBestDroppable) y actualiza:

      • dropped
      • history / legacyHistory
      • dataDroppable
    • Se dispara el callback global onDrop del DNDProvider.

Si se llama a cleanAll() (por ejemplo, con CleanButton):

  • El slice limpia history, dataDroppable, dropped y marca deleteData.
  • Cada Draggable puede 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