# DropZone

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



`DropZone` defines areas where dragged items can be dropped. The `onDrop` callback receives the dropped data and allows you to handle the drop action.

## Example

This example demonstrates `onDropTest` to restrict which items can be dropped in each zone. Fruits and Vegetables zones only accept matching types, while Anything accepts all items.

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

interface Item {
  name: string;
  type: "fruit" | "vegetable";
}

interface PageModel {
  fruits: Item[];
  vegetables: Item[];
  anything: Item[];
  $fruit: Item;
  $vegetable: Item;
  $item: Item;
}

const m = createModel<PageModel>();

function removeItem(store: any, item: Item) {
  store.set(
    m.fruits,
    store.get(m.fruits).filter((x: Item) => x.name !== item.name),
  );
  store.set(
    m.vegetables,
    store.get(m.vegetables).filter((x: Item) => x.name !== item.name),
  );
  store.set(
    m.anything,
    store.get(m.anything).filter((x: Item) => x.name !== item.name),
  );
}

class PageController extends Controller {
  onInit() {
    this.store.set(m.fruits, [
      { name: "Apple", type: "fruit" },
      { name: "Banana", type: "fruit" },
    ]);
    this.store.set(m.vegetables, [
      { name: "Carrot", type: "vegetable" },
      { name: "Broccoli", type: "vegetable" },
    ]);
    this.store.set(m.anything, []);
  }
}

export default (
  <div class="flex gap-4" controller={PageController}>
    <DropZone
      class="flex flex-col gap-2 p-4 border rounded min-w-32 min-h-32 bg-gray-50"
      overClass="!bg-green-50 !border-green-300"
      onDropTest={({ source }) => source.data.type === "fruit"}
      onDrop={({ source }, { store }) => {
        removeItem(store, source.data);
        store.set(m.fruits, [...store.get(m.fruits), source.data]);
      }}
    >
      <div class="text-sm font-medium mb-2">Fruits</div>
      <div class="flex flex-col gap-1 min-h-16">
        <Repeater records={m.fruits} recordAlias={m.$fruit} keyField="name">
          <DragSource data={m.$fruit}>
            <div class="p-2 border rounded bg-white cursor-move">
              <span text={m.$fruit.name} />
            </div>
          </DragSource>
        </Repeater>
      </div>
    </DropZone>

    <DropZone
      class="flex flex-col gap-2 p-4 border rounded min-w-32 min-h-32 bg-gray-50"
      overClass="!bg-orange-50 !border-orange-300"
      onDropTest={({ source }) => source.data.type === "vegetable"}
      onDrop={({ source }, { store }) => {
        removeItem(store, source.data);
        store.set(m.vegetables, [...store.get(m.vegetables), source.data]);
      }}
    >
      <div class="text-sm font-medium mb-2">Vegetables</div>
      <div class="flex flex-col gap-1 min-h-16">
        <Repeater
          records={m.vegetables}
          recordAlias={m.$vegetable}
          keyField="name"
        >
          <DragSource data={m.$vegetable}>
            <div class="p-2 border rounded bg-white cursor-move">
              <span text={m.$vegetable.name} />
            </div>
          </DragSource>
        </Repeater>
      </div>
    </DropZone>

    <DropZone
      class="flex flex-col gap-2 p-4 border rounded min-w-32 min-h-32 bg-gray-50"
      overClass="!bg-blue-50 !border-blue-300"
      onDrop={({ source }, { store }) => {
        removeItem(store, source.data);
        store.set(m.anything, [...store.get(m.anything), source.data]);
      }}
    >
      <div class="text-sm font-medium mb-2">Anything</div>
      <div class="flex flex-col gap-1 min-h-16">
        <Repeater records={m.anything} recordAlias={m.$item} keyField="name">
          <DragSource data={m.$item}>
            <div class="p-2 border rounded bg-white cursor-move">
              <span text={m.$item.name} />
            </div>
          </DragSource>
        </Repeater>
      </div>
    </DropZone>
  </div>
);

```

## Sizing Options

Control how the drop zone matches its content:

```tsx
<DropZone
  matchWidth // Match the width of the child element
  matchHeight // Match the height of the child element
  matchMargin // Include margins when calculating dimensions
  inflate={5} // Expand the drop zone by 5 pixels
>
  <div>Content</div>
</DropZone>
```

## Block Mode

Use `mod="block"` for invisible drop zones used in list reordering. In block mode, the drop zone has a height of 1px and spans the full width. Use `inflate` to expand the hit area:

```tsx
<DropZone mod="block" inflate={50} onDrop={handleDrop} />
```

## Configuration

| Property       | Type       | Description                                                        |
| -------------- | ---------- | ------------------------------------------------------------------ |
| `data`         | `Prop`     | Bindable data related to the DropZone, available in callbacks.     |
| `onDrop`       | `function` | Callback invoked when an item is dropped.                          |
| `onDropTest`   | `function` | Test if dragged source is acceptable. Return `false` to reject.    |
| `onDragStart`  | `function` | Callback invoked when any drag operation begins.                   |
| `onDragEnd`    | `function` | Callback invoked when any drag operation ends.                     |
| `onDragEnter`  | `function` | Callback invoked when source enters the drop zone.                 |
| `onDragLeave`  | `function` | Callback invoked when source leaves the drop zone.                 |
| `onDragOver`   | `function` | Callback invoked continuously while source is over the zone.       |
| `onDragNear`   | `function` | Callback invoked when source gets close to the zone.               |
| `onDragAway`   | `function` | Callback invoked when source moves away from the zone.             |
| `overStyle`    | `Style`    | CSS styles applied when cursor is over the drop zone.              |
| `overClass`    | `Class`    | CSS class applied when cursor is over the drop zone.               |
| `nearStyle`    | `Style`    | CSS styles applied when cursor is near the drop zone.              |
| `nearClass`    | `Class`    | CSS class applied when cursor is near the drop zone.               |
| `farStyle`     | `Style`    | CSS styles applied when drag begins to highlight drop zones.       |
| `farClass`     | `Class`    | CSS class applied when drag begins to highlight drop zones.        |
| `nearDistance` | `number`   | Distance in pixels to consider the cursor "near". Defaults to `0`. |
| `inflate`      | `number`   | Inflate bounding box by this many pixels in all directions.        |
| `hinflate`     | `number`   | Inflate bounding box horizontally.                                 |
| `vinflate`     | `number`   | Inflate bounding box vertically.                                   |
| `matchWidth`   | `boolean`  | Match width of the item being dragged.                             |
| `matchHeight`  | `boolean`  | Match height of the item being dragged.                            |
| `matchMargin`  | `boolean`  | Match margin of the item being dragged.                            |