Skip to component content
Behavior121 / 155

Draggable

Attach a plain-text payload to a native drag source.

Example
Water the seedlings

Drop the task here.

Drag the task into a compatible drop area.

Source
Draggable.tsxtsx
import { useState } from "react";
import { Button, Card, Draggable, DropDestination, HStack, Picker, Text, VStack } from "@/ohmygui";

type DragEffect = DataTransfer["effectAllowed"];

export default function Example() {
  const [effectAllowed, setEffectAllowed] = useState<DragEffect>("copyMove");
  const [message, setMessage] = useState("Drag the task into a compatible drop area.");

  return (
    <VStack gap="md" align="stretch">
      <HStack gap="md" align="end" wrap>
        <Picker label="Allowed operation" value={effectAllowed} onChange={(event) => setEffectAllowed(event.currentTarget.value as DragEffect)} items={[{ value: "copy", label: "Copy" }, { value: "move", label: "Move" }, { value: "copyMove", label: "Copy or move" }]} />
        <Draggable
          payload="Water the seedlings"
          effectAllowed={effectAllowed}
          tabIndex={0}
          aria-label="Drag task: water the seedlings"
          onDragStart={() => setMessage(`Drag started · ${effectAllowed} allowed`)}
        >
          <Card>
            <Text weight="semibold">Water the seedlings</Text>
            <Text as="p" variant="metadata" color="muted">text/plain payload</Text>
          </Card>
        </Draggable>
      </HStack>
      <DropDestination aria-label="Garden task destination" accept={["text/plain"]} onDropEvent={(event) => setMessage(`Added: ${event.dataTransfer.getData("text/plain")}`)}>
        <Text as="p">Drop the task here.</Text>
      </DropDestination>
      <Button variant="outline" onClick={() => setMessage("Added: Water the seedlings (without dragging)")}>Add without dragging</Button>
      <Text as="p" role="status" aria-live="polite">{message}</Text>
    </VStack>
  );
}