CxJS

DragSource

import { DragSource } from 'cx/widgets'; Copied

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

<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>
Drag me!
Or me!
Drop here

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
<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>
⋮⋮
Sales
1,234
Sample metric
⋮⋮
Orders
1,234
Sample metric
⋮⋮
Users
1,234
Sample metric

Configuration

PropertyTypeDescription
dataPropData passed to the drop zone on drop.
hideOnDragbooleanHide the source element while dragging. Defaults to false.
handledbooleanIf true, dragging is initiated only through a DragHandle. Defaults to false.
onDragStartfunctionCallback invoked when drag starts. Return false to cancel.
onDragEndfunctionCallback invoked when drag ends.
cloneConfigCustom widget to display during drag instead of cloning the source.
cloneStyleStyleCSS styles applied to the drag clone.
cloneClassClassCSS class applied to the drag clone.
draggedStyleStyleCSS styles applied to the source element while being dragged.
draggedClassClassCSS class applied to the source element while being dragged.
idstringID attribute for the element.