# DragSource

```ts
import { DragSource } from 'cx/widgets';
```



`DragSource` wraps elements that can be dragged. The `data` property specifies what data is passed to the drop zone when the element is dropped.

## Example

```tsx
import { DragSource, DropZone } from "cx/widgets";

export default (
  <div class="flex items-center gap-8">
    <DragSource data={{ text: "Drag me!" }}>
      <div class="p-4 border-2 border-dashed rounded bg-blue-50 cursor-move hover:bg-blue-100">
        Drag me!
      </div>
    </DragSource>

    <DragSource data={{ text: "Or me!" }}>
      <div class="p-4 border-2 border-dashed rounded bg-blue-50 cursor-move hover:bg-blue-100">
        Or me!
      </div>
    </DragSource>

    <DropZone
      onDrop={({ source }) => {
        alert(`Dropped: ${source.data.text}`);
      }}
    >
      <div class="p-4 border border-gray-200 rounded bg-green-50 min-w-32 text-center">
        Drop here
      </div>
    </DropZone>
  </div>
);

```

## Dashboard Example

This example demonstrates advanced features:

- `handled` - Only the handle initiates drag, allowing text selection
- `clone` - Custom lightweight widget shown during drag instead of cloning the entire content

```tsx
import { createModel } from "cx/data";
import { Controller } from "cx/ui";
import { DragHandle, DragSource, DropZone, Repeater } from "cx/widgets";

interface Widget {
  id: number;
  title: string;
  color: string;
}

interface PageModel {
  widgets: Widget[];
  $widget: Widget;
  $index: number;
}

const m = createModel<PageModel>();

class PageController extends Controller {
  onInit() {
    this.store.set(m.widgets, [
      { id: 1, title: "Sales", color: "bg-blue-100" },
      { id: 2, title: "Orders", color: "bg-green-100" },
      { id: 3, title: "Users", color: "bg-purple-100" },
    ]);
  }
}

export default (
  <div class="flex flex-col gap-2" controller={PageController}>
    <Repeater
      records={m.widgets}
      recordAlias={m.$widget}
      indexAlias={m.$index}
      keyField="id"
    >
      <DropZone
        mod="block"
        onDrop={({ source }, { store }) => {
          let targetIndex = store.get(m.$index);
          let widgets = store
            .get(m.widgets)
            .filter((w) => w.id !== source.data.id);
          store.set(m.widgets, [
            ...widgets.slice(0, targetIndex),
            source.data,
            ...widgets.slice(targetIndex),
          ]);
        }}
        matchWidth
        matchHeight
        matchMargin
        inflate={100}
      />
      <DragSource
        data={m.$widget}
        handled
        clone={
          <div
            class="p-2 border rounded bg-white shadow-lg opacity-90"
            text={m.$widget.title}
          />
        }
      >
        <div class={["p-4 border rounded", m.$widget.color]}>
          <div class="flex items-center gap-2 mb-2">
            <DragHandle>
              <div class="cursor-move text-gray-400 hover:text-gray-600">
                ⋮⋮
              </div>
            </DragHandle>
            <div class="font-medium" text={m.$widget.title} />
          </div>
          <div class="text-2xl font-bold">1,234</div>
          <div class="text-sm text-gray-500">Sample metric</div>
        </div>
      </DragSource>
    </Repeater>
    <DropZone
      mod="block"
      matchWidth
      matchHeight
      matchMargin
      onDrop={({ source }, { store }) => {
        let widgets = store
          .get(m.widgets)
          .filter((w) => w.id !== source.data.id);
        store.set(m.widgets, [...widgets, source.data]);
      }}
      inflate={100}
    >
      <div class="h-2" />
    </DropZone>
  </div>
);

```

## Configuration

| Property       | Type       | Description                                                                        |
| -------------- | ---------- | ---------------------------------------------------------------------------------- |
| `data`         | `Prop`     | Data passed to the drop zone on drop.                                              |
| `hideOnDrag`   | `boolean`  | Hide the source element while dragging. Defaults to `false`.                       |
| `handled`      | `boolean`  | If `true`, dragging is initiated only through a `DragHandle`. Defaults to `false`. |
| `onDragStart`  | `function` | Callback invoked when drag starts. Return `false` to cancel.                       |
| `onDragEnd`    | `function` | Callback invoked when drag ends.                                                   |
| `clone`        | `Config`   | Custom widget to display during drag instead of cloning the source.                |
| `cloneStyle`   | `Style`    | CSS styles applied to the drag clone.                                              |
| `cloneClass`   | `Class`    | CSS class applied to the drag clone.                                               |
| `draggedStyle` | `Style`    | CSS styles applied to the source element while being dragged.                      |
| `draggedClass` | `Class`    | CSS class applied to the source element while being dragged.                       |
| `id`           | `string`   | ID attribute for the element.                                                      |