Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

React DND typescript support

I read some users mentioning that they are using this library with Typescript support, but I cannot find any documentation anywhere nor I cannot seem to make it work on my own.

I am using typescript 2 and I can't manage to create a really simple working example that simply allows me to drag an existing component. I tried several possibilities but I always get stuck into some problems with typings either when calling DragSource (as a decorator or function) or when rendering the resulting component.

In short I would like an example that shows the usage of react-dnd in typescript that allows me how to make an existing component draggable, possibly without modifiying the component itself (it shouldn't be aware that it is draggable)

Thank you for any help!

like image 330
mastrolindus Avatar asked Oct 18 '16 14:10

mastrolindus


1 Answers

I've got it working with the DT types package on 2.1. It doesn't compile with strictNullChecks and I haven't been able to track down the reason why. (When you decorate your components with @DragSource and @DropTarget, you somehow change the return type of the render function from Element to Element | null, but I can't see how.)

The other niggle is that all of the props that are inserted by the collecting function are undefined when you first instantiate your components in a render method, so your options are to pass a bunch of {undefined as any} or else declare your collector-injected props as optionals and typeguard them out every where you look at them. Overall the type declaration file is not bad and I found the typing to be more helpful than harmful when getting to know the library.

import { 
    ConnectDragSource,
    DragDropContext, 
    DragSource, 
    DragSourceSpec, 
    DragSourceCollector, 
    DragSourceConnector, 
    DragSourceMonitor, 
    DragElementWrapper,
    ConnectDropTarget,
    DropTarget,
    DropTargetConnector,
    DropTargetMonitor,
    ClientOffset,
    DropTargetSpec } from 'react-dnd';
let HTML5Backend = require('react-dnd-html5-backend');
import { AdjacencyMatrixGraph } from "Geometry/Graph";

import * as React from "react";
import * as Sauce from "Sauce";
import * as ReactDOM from "react-dom";
import * as $ from "jquery";
import { Position2 } from "Geometry";
import * as Rx from "rxjs";
import * as Util from "Util";
require("./GraphBuilder.scss");

interface NodeProps {
  label?: string;
  position: ClientOffset;
}

/* New node from the node well */
export interface NodeSourceProps {
   isDragging : boolean;
   connectDragSource: ConnectDragSource;
}
export interface NodeSourceState {
}

// Spec: drag events to handle.
let nodeSourceSpec: DragSourceSpec<NodeSourceProps> = {
    beginDrag: (props: NodeSourceProps) => ({}),
};

// Collect: Put drag state into props
let nodeSourceCollector = (connect: DragSourceConnector, monitor: DragSourceMonitor) => {
    return {
      connectDragSource: connect.dragSource(),
      isDragging: monitor.isDragging()
  }
};

@DragSource("new-node", nodeSourceSpec, nodeSourceCollector)
export class NodeSource extends React.Component<NodeSourceProps, NodeSourceState> {
  constructor(props: NodeSourceProps) {
    super(props);
  }
  render() {
    const { connectDragSource, isDragging } = this.props;
    return connectDragSource(<span className="node-source">{'\u2683'}</span>);
  }
}

/* main graph area */
interface GraphCanvasProps {
    connectDropTarget: ConnectDropTarget,
    isOver: boolean,
    graph: AdjacencyMatrixGraph<NodeProps>;
}

interface GraphCanvasState {}
const canvasDropTargetSpecification: DropTargetSpec<GraphCanvasProps> = {
  drop(props: GraphCanvasProps, monitor: DropTargetMonitor, component: React.Component<GraphCanvasProps, any>) {
    // console.log("Handling drop", print_monitor(monitor));
    let pos = monitor.getSourceClientOffset();
    if (monitor.getItemType() === "main-node-move") {
      let node = (monitor.getItem() as any);
      graph.setData(node.id, { position: pos });
    }
    else if (monitor.getItemType() === "new-node") {
      graph.addNode("node-" + graph.order(), { position: pos });
    }
  },
};

function canvasDropTargetCollectingFunction(connect: DropTargetConnector, monitor: DropTargetMonitor) {
  let rv = {
    connectDropTarget: connect.dropTarget(),
    isOver: monitor.isOver(),
  };

  return rv;
}
/* ... here's  a DropTarget ... */

@DropTarget(["main-node-move", "new-node"], canvasDropTargetSpecification, canvasDropTargetCollectingFunction)
export class GraphCanvas extends React.Component<GraphCanvasProps, GraphCanvasState> {
  constructor(props: GraphCanvasProps) {
    super(props);
  }

  render(): JSX.Element | null {
    const { connectDropTarget, graph } = this.props;
    let nodes = graph.nodes();
    let nodeEls = Object.keys(nodes).map(k => {
      let node = nodes[k];
      return <CanvasNode 
      key={k} 
      id={k} 
      node={node} 
      graph={graph} 
      connectNodeDrop={null as any} 
      connectMoveNodeDragger={(null)}/>
    });
    return connectDropTarget(<div className="graph-canvas">
      {nodeEls}
    </div>);
  }
}

/* ... Here's a the DragContext decorator ... */

@DragDropContext(HTML5Backend)
class Markov extends React.Component<MarkovProps, MarkovState>  {
like image 60
masonk Avatar answered Oct 29 '22 20:10

masonk