# DragHandle

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



`DragHandle` provides a specific area within a `DragSource` that initiates dragging. This allows the rest of the content to remain interactive (selectable text, clickable buttons, etc.) while still supporting drag and drop.

## Example

Try selecting the text - only the handle on the left initiates drag:

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

interface Item {
  id: number;
  text: string;
}

interface PageModel {
  items: Item[];
  $record: Item;
  $index: number;
}

const m = createModel<PageModel>();

class PageController extends Controller {
  onInit() {
    this.store.set(m.items, [
      { id: 1, text: "Select this text without triggering drag" },
      { id: 2, text: "Use the handle on the left to drag" },
      { id: 3, text: "Content remains interactive" },
    ]);
  }
}

export default (
  <div class="flex flex-col gap-2" controller={PageController}>
    <Repeater
      records={m.items}
      recordAlias={m.$record}
      indexAlias={m.$index}
      keyField="id"
    >
      <DropZone
        mod="block"
        onDrop={({ source }, { store }) => {
          let targetIndex = store.get(m.$index);
          let items = store
            .get(m.items)
            .filter((item) => item.id !== source.data.id);
          store.set(m.items, [
            ...items.slice(0, targetIndex),
            source.data,
            ...items.slice(targetIndex),
          ]);
        }}
        matchWidth
        matchHeight
        matchMargin
        inflate={50}
      />
      <DragSource data={m.$record} hideOnDrag handled>
        <div class="flex items-center gap-2 p-2 border rounded bg-white">
          <DragHandle class="cursor-move text-gray-400 hover:text-gray-600 px-1">
            ⋮⋮
          </DragHandle>
          <span text={m.$record.text} />
        </div>
      </DragSource>
    </Repeater>
    <DropZone
      mod="block"
      onDrop={({ source }, { store }) => {
        let items = store
          .get(m.items)
          .filter((item) => item.id !== source.data.id);
        store.set(m.items, [...items, source.data]);
      }}
      matchWidth
      matchHeight
      matchMargin
      inflate={50}
    >
      <div class="h-2" />
    </DropZone>
  </div>
);

```

## Usage

To use `DragHandle`, set `handled` on the parent `DragSource`:

```tsx
<DragSource data={m.$record} handled>
  <div class="flex items-center gap-2">
    <DragHandle>
      <div class="cursor-move">⋮⋮</div>
    </DragHandle>
    <span text={m.$record.text} />
  </div>
</DragSource>
```

Without `handled`, the entire `DragSource` content would be draggable, preventing text selection and other interactions.

## Configuration

| Property    | Type     | Description                                                      |
| ----------- | -------- | ---------------------------------------------------------------- |
| `style`     | `Style`  | Inline styles for the handle element.                            |
| `class`     | `Class`  | CSS class for the handle element.                                |
| `baseClass` | `string` | Base CSS class applied to the element. Defaults to `draghandle`. |