🚧 VeloxKit is pre-release software. APIs may change before v1.0. Get started →
Documentation
Packages
@velox/drag-drop

@velox/drag-drop

Draggable + DropZone built on native drag events. Drop targets are matched by hit-testing the pointer against each zone's live layout rect at drop time.

Install

npm install @velox/drag-drop

Usage

import { Draggable, DropZone } from '@velox/drag-drop'
 
<Draggable data={card} type="card">
  <Card />
</Draggable>
 
<DropZone accepts="card" onDrop={(card) => moveTo(column, card)}>
  <Column />
</DropZone>

Kanban board example

function KanbanBoard() {
  const [columns, setColumns] = useState({
    todo:  [{ id: 1, title: 'Design mockup' }],
    doing: [{ id: 2, title: 'Build API' }],
    done:  [],
  })
 
  const move = (card, toColumn) => {
    setColumns(prev => {
      const next = { ...prev }
      for (const col of Object.keys(next)) {
        next[col] = next[col].filter(c => c.id !== card.id)
      }
      next[toColumn] = [...next[toColumn], card]
      return next
    })
  }
 
  return (
    <View style={{ flexDirection: 'row', gap: 16 }}>
      {Object.entries(columns).map(([col, cards]) => (
        <DropZone key={col} accepts="card" onDrop={(card) => move(card, col)}
                  style={{ flex: 1, minHeight: 200, padding: 12,
                           backgroundColor: '#1e2235', borderRadius: 8 }}>
          <Text style={{ fontWeight: '600', marginBottom: 8 }}>{col.toUpperCase()}</Text>
          {cards.map(card => (
            <Draggable key={card.id} data={card} type="card">
              <View style={{ padding: 10, backgroundColor: '#262b3f',
                             borderRadius: 6, marginBottom: 8 }}>
                <Text>{card.title}</Text>
              </View>
            </Draggable>
          ))}
        </DropZone>
      ))}
    </View>
  )
}

useDraggable hook

For custom drag interactions that don't fit the Draggable / DropZone pattern, use useDraggable directly from @velox/react:

import { useDraggable } from 'veloxkit'
 
const dragHandlers = useDraggable({
  onDragStart: (x, y) => { /* pointer down + moved past threshold */ },
  onDragMove:  (dx, dy) => { /* called each frame while dragging */ },
  onDragEnd:   (x, y) => { /* pointer released */ },
})
 
// Attach to any View or Pressable:
<View {...dragHandlers}>
  <Text>Drag me</Text>
</View>

Resizable panel example

function ResizablePanel({ children }) {
  const [width, setWidth] = useState(300)
  const startW = useRef(300)
 
  const dragHandlers = useDraggable({
    onDragStart: () => { startW.current = width },
    onDragMove:  (dx) => setWidth(Math.max(120, startW.current + dx)),
  })
 
  return (
    <View style={{ flexDirection: 'row' }}>
      <View style={{ width }}>{children}</View>
      <View {...dragHandlers}
            style={{ width: 4, backgroundColor: '#3c4464', cursor: 'col-resize' }} />
    </View>
  )
}