Code Monkey home page Code Monkey logo

react-draggable's Introduction

React-Grid-Layout

travis build CDNJS npm package npm downloads

React-Grid-Layout is a grid layout system much like Packery or Gridster, for React.

Unlike those systems, it is responsive and supports breakpoints. Breakpoint layouts can be provided by the user or autogenerated.

RGL is React-only and does not require jQuery.

BitMEX UI

GIF from production usage on BitMEX.com

[Demo | Changelog | CodeSandbox Editable demo]

Table of Contents

Demos

  1. Showcase
  2. Basic
  3. No Dragging/Resizing (Layout Only)
  4. Messy Layout Autocorrect
  5. Layout Defined on Children
  6. Static Elements
  7. Adding/Removing Elements
  8. Saving Layout to LocalStorage
  9. Saving a Responsive Layout to LocalStorage
  10. Minimum and Maximum Width/Height
  11. Dynamic Minimum and Maximum Width/Height
  12. No Vertical Compacting (Free Movement)
  13. Prevent Collision
  14. Error Case
  15. Toolbox
  16. Drag From Outside
  17. Bounded Layout
  18. Responsive Bootstrap-style Layout
  19. Scaled Containers
  20. Allow Overlap
  21. All Resizable Handles
  22. Single Row Horizontal

Projects Using React-Grid-Layout

Know of others? Create a PR to let me know!

Features

  • 100% React - no jQuery
  • Compatible with server-rendered apps
  • Draggable widgets
  • Resizable widgets
  • Static widgets
  • Configurable packing: horizontal, vertical, or off
  • Bounds checking for dragging and resizing
  • Widgets may be added or removed without rebuilding grid
  • Layout can be serialized and restored
  • Responsive breakpoints
  • Separate layouts per responsive breakpoint
  • Grid Items placed using CSS Transforms
    • Performance with CSS Transforms: on / off, note paint (green) as % of time
  • Compatibility with <React.StrictMode>
Version Compatibility
>= 0.17.0 React 16 & 17
>= 0.11.3 React 0.14 & 15
>= 0.10.0 React 0.14
0.8. - 0.9.2 React 0.13
< 0.8 React 0.12

Installation

Install the React-Grid-Layout package using npm:

npm install react-grid-layout

Include the following stylesheets in your application:

/node_modules/react-grid-layout/css/styles.css
/node_modules/react-resizable/css/styles.css

Usage

Use ReactGridLayout like any other component. The following example below will produce a grid with three items where:

  • users will not be able to drag or resize item a
  • item b will be restricted to a minimum width of 2 grid blocks and a maximum width of 4 grid blocks
  • users will be able to freely drag and resize item c
import GridLayout from "react-grid-layout";

class MyFirstGrid extends React.Component {
  render() {
    // layout is an array of objects, see the demo for more complete usage
    const layout = [
      { i: "a", x: 0, y: 0, w: 1, h: 2, static: true },
      { i: "b", x: 1, y: 0, w: 3, h: 2, minW: 2, maxW: 4 },
      { i: "c", x: 4, y: 0, w: 1, h: 2 }
    ];
    return (
      <GridLayout
        className="layout"
        layout={layout}
        cols={12}
        rowHeight={30}
        width={1200}
      >
        <div key="a">a</div>
        <div key="b">b</div>
        <div key="c">c</div>
      </GridLayout>
    );
  }
}

You may also choose to set layout properties directly on the children:

import GridLayout from "react-grid-layout";

class MyFirstGrid extends React.Component {
  render() {
    return (
      <GridLayout className="layout" cols={12} rowHeight={30} width={1200}>
        <div key="a" data-grid={{ x: 0, y: 0, w: 1, h: 2, static: true }}>
          a
        </div>
        <div key="b" data-grid={{ x: 1, y: 0, w: 3, h: 2, minW: 2, maxW: 4 }}>
          b
        </div>
        <div key="c" data-grid={{ x: 4, y: 0, w: 1, h: 2 }}>
          c
        </div>
      </GridLayout>
    );
  }
}

Usage without Browserify/Webpack

A module usable in a <script> tag is included here. It uses a UMD shim and excludes React, so it must be otherwise available in your application, either via RequireJS or on window.React.

Responsive Usage

To make RGL responsive, use the <ResponsiveReactGridLayout> element:

import { Responsive as ResponsiveGridLayout } from "react-grid-layout";

class MyResponsiveGrid extends React.Component {
  render() {
    // {lg: layout1, md: layout2, ...}
    const layouts = getLayoutsFromSomewhere();
    return (
      <ResponsiveGridLayout
        className="layout"
        layouts={layouts}
        breakpoints={{ lg: 1200, md: 996, sm: 768, xs: 480, xxs: 0 }}
        cols={{ lg: 12, md: 10, sm: 6, xs: 4, xxs: 2 }}
      >
        <div key="1">1</div>
        <div key="2">2</div>
        <div key="3">3</div>
      </ResponsiveGridLayout>
    );
  }
}

When in responsive mode, you should supply at least one breakpoint via the layouts property.

When using layouts, it is best to supply as many breakpoints as possible, especially the largest one. If the largest is provided, RGL will attempt to interpolate the rest.

You will also need to provide a width, when using <ResponsiveReactGridLayout> it is suggested you use the HOC WidthProvider as per the instructions below.

It is possible to supply default mappings via the data-grid property on individual items, so that they would be taken into account within layout interpolation.

Providing Grid Width

Both <ResponsiveReactGridLayout> and <ReactGridLayout> take width to calculate positions on drag events. In simple cases a HOC WidthProvider can be used to automatically determine width upon initialization and window resize events.

import { Responsive, WidthProvider } from "react-grid-layout";

const ResponsiveGridLayout = WidthProvider(Responsive);

class MyResponsiveGrid extends React.Component {
  render() {
    // {lg: layout1, md: layout2, ...}
    var layouts = getLayoutsFromSomewhere();
    return (
      <ResponsiveGridLayout
        className="layout"
        layouts={layouts}
        breakpoints={{ lg: 1200, md: 996, sm: 768, xs: 480, xxs: 0 }}
        cols={{ lg: 12, md: 10, sm: 6, xs: 4, xxs: 2 }}
      >
        <div key="1">1</div>
        <div key="2">2</div>
        <div key="3">3</div>
      </ResponsiveGridLayout>
    );
  }
}

This allows you to easily replace WidthProvider with your own Provider HOC if you need more sophisticated logic.

WidthProvider accepts a single prop, measureBeforeMount. If true, WidthProvider will measure the container's width before mounting children. Use this if you'd like to completely eliminate any resizing animation on application/component mount.

Have a more complicated layout? WidthProvider is very simple and only listens to window 'resize' events. If you need more power and flexibility, try the SizeMe React HOC as an alternative to WidthProvider.

Grid Layout Props

RGL supports the following properties (see the source for the final word on this):

//
// Basic props
//

// This allows setting the initial width on the server side.
// This is required unless using the HOC <WidthProvider> or similar
width: number,

// If true, the container height swells and contracts to fit contents
autoSize: ?boolean = true,

// Number of columns in this layout.
cols: ?number = 12,

// A CSS selector for tags that will not be draggable.
// For example: draggableCancel:'.MyNonDraggableAreaClassName'
// If you forget the leading . it will not work.
// .react-resizable-handle" is always prepended to this value.
draggableCancel: ?string = '',

// A CSS selector for tags that will act as the draggable handle.
// For example: draggableHandle:'.MyDragHandleClassName'
// If you forget the leading . it will not work.
draggableHandle: ?string = '',

// Compaction type.
compactType: ?('vertical' | 'horizontal' | null) = 'vertical';

// Layout is an array of objects with the format:
// The index into the layout must match the key used on each item component.
// If you choose to use custom keys, you can specify that key in the layout
// array objects using the `i` prop.
layout: ?Array<{i?: string, x: number, y: number, w: number, h: number}> = null, // If not provided, use data-grid props on children

// Margin between items [x, y] in px.
margin: ?[number, number] = [10, 10],

// Padding inside the container [x, y] in px
containerPadding: ?[number, number] = margin,

// Rows have a static height, but you can change this based on breakpoints
// if you like.
rowHeight: ?number = 150,

// Configuration of a dropping element. Dropping element is a "virtual" element
// which appears when you drag over some element from outside.
// It can be changed by passing specific parameters:
//  i - id of an element
//  w - width of an element
//  h - height of an element
droppingItem?: { i: string, w: number, h: number }

//
// Flags
//
isDraggable: ?boolean = true,
isResizable: ?boolean = true,
isBounded: ?boolean = false,
// Uses CSS3 translate() instead of position top/left.
// This makes about 6x faster paint performance
useCSSTransforms: ?boolean = true,
// If parent DOM node of ResponsiveReactGridLayout or ReactGridLayout has "transform: scale(n)" css property,
// we should set scale coefficient to avoid render artefacts while dragging.
transformScale: ?number = 1,

// If true, grid can be placed one over the other.
// If set, implies `preventCollision`.
allowOverlap: ?boolean = false,

// If true, grid items won't change position when being
// dragged over. If `allowOverlap` is still false,
// this simply won't allow one to drop on an existing object.
preventCollision: ?boolean = false,

// If true, droppable elements (with `draggable={true}` attribute)
// can be dropped on the grid. It triggers "onDrop" callback
// with position and event object as parameters.
// It can be useful for dropping an element in a specific position
//
// NOTE: In case of using Firefox you should add
// `onDragStart={e => e.dataTransfer.setData('text/plain', '')}` attribute
// along with `draggable={true}` otherwise this feature will work incorrect.
// onDragStart attribute is required for Firefox for a dragging initialization
// @see https://bugzilla.mozilla.org/show_bug.cgi?id=568313
isDroppable: ?boolean = false,
// Defines which resize handles should be rendered.
// Allows for any combination of:
// 's' - South handle (bottom-center)
// 'w' - West handle (left-center)
// 'e' - East handle (right-center)
// 'n' - North handle (top-center)
// 'sw' - Southwest handle (bottom-left)
// 'nw' - Northwest handle (top-left)
// 'se' - Southeast handle (bottom-right)
// 'ne' - Northeast handle (top-right)
//
// Note that changing this property dynamically does not work due to a restriction in react-resizable.
resizeHandles: ?Array<'s' | 'w' | 'e' | 'n' | 'sw' | 'nw' | 'se' | 'ne'> = ['se'],
// Custom component for resize handles
// See `handle` as used in https://github.com/react-grid-layout/react-resizable#resize-handle
// Your component should have the class `.react-resizable-handle`, or you should add your custom
// class to the `draggableCancel` prop.
resizeHandle?: ReactElement<any> | ((resizeHandleAxis: ResizeHandleAxis, ref: ReactRef<HTMLElement>) => ReactElement<any>),

//
// Callbacks
//

// Callback so you can save the layout.
// Calls back with (currentLayout) after every drag or resize stop.
onLayoutChange: (layout: Layout) => void,

//
// All callbacks below have signature (layout, oldItem, newItem, placeholder, e, element).
// 'start' and 'stop' callbacks pass `undefined` for 'placeholder'.
//
type ItemCallback = (layout: Layout, oldItem: LayoutItem, newItem: LayoutItem,
                     placeholder: LayoutItem, e: MouseEvent, element: HTMLElement) => void,

// Calls when drag starts.
onDragStart: ItemCallback,
// Calls on each drag movement.
onDrag: ItemCallback,
// Calls when drag is complete.
onDragStop: ItemCallback,
// Calls when resize starts.
onResizeStart: ItemCallback,
// Calls when resize movement happens.
onResize: ItemCallback,
// Calls when resize is complete.
onResizeStop: ItemCallback,

//
// Dropover functionality
//

// Calls when an element has been dropped into the grid from outside.
onDrop: (layout: Layout, item: ?LayoutItem, e: Event) => void,
// Calls when an element is being dragged over the grid from outside as above.
// This callback should return an object to dynamically change the droppingItem size
// Return false to short-circuit the dragover
onDropDragOver: (e: DragOverEvent) => ?({|w?: number, h?: number|} | false),

// Ref for getting a reference for the grid's wrapping div.
// You can use this instead of a regular ref and the deprecated `ReactDOM.findDOMNode()`` function.
// Note that this type is React.Ref<HTMLDivElement> in TypeScript, Flow has a bug here
// https://github.com/facebook/flow/issues/8671#issuecomment-862634865
innerRef: {current: null | HTMLDivElement},

Responsive Grid Layout Props

The responsive grid layout can be used instead. It supports all of the props above, excepting layout. The new properties and changes are:

// {name: pxVal}, e.g. {lg: 1200, md: 996, sm: 768, xs: 480}
// Breakpoint names are arbitrary but must match in the cols and layouts objects.
breakpoints: ?Object = {lg: 1200, md: 996, sm: 768, xs: 480, xxs: 0},

// # of cols. This is a breakpoint -> cols map, e.g. {lg: 12, md: 10, ...}
cols: ?Object = {lg: 12, md: 10, sm: 6, xs: 4, xxs: 2},


// margin (in pixels). Can be specified either as horizontal and vertical margin, e.g. `[10, 10]` or as a breakpoint -> margin map, e.g. `{lg: [10, 10], md: [10, 10], ...}.
margin: [number, number] | {[breakpoint: $Keys<breakpoints>]: [number, number]},


// containerPadding (in pixels). Can be specified either as horizontal and vertical padding, e.g. `[10, 10]` or as a breakpoint -> containerPadding map, e.g. `{lg: [10, 10], md: [10, 10], ...}.
containerPadding: [number, number] | {[breakpoint: $Keys<breakpoints>]: [number, number]},


// layouts is an object mapping breakpoints to layouts.
// e.g. {lg: Layout, md: Layout, ...}
layouts: {[key: $Keys<breakpoints>]: Layout},

//
// Callbacks
//

// Calls back with breakpoint and new # cols
onBreakpointChange: (newBreakpoint: string, newCols: number) => void,

// Callback so you can save the layout.
// AllLayouts are keyed by breakpoint.
onLayoutChange: (currentLayout: Layout, allLayouts: {[key: $Keys<breakpoints>]: Layout}) => void,

// Callback when the width changes, so you can modify the layout as needed.
onWidthChange: (containerWidth: number, margin: [number, number], cols: number, containerPadding: [number, number]) => void;

Grid Item Props

RGL supports the following properties on grid items or layout items. When initializing a grid, build a layout array (as in the first example above), or attach this object as the data-grid property to each of your child elements (as in the second example).

If data-grid is provided on an item, it will take precedence over an item in the layout with the same key (i).

Note that if a grid item is provided but incomplete (missing one of x, y, w, or h), an error will be thrown so you can correct your layout.

If no properties are provided for a grid item, one will be generated with a width and height of 1.

You can set minimums and maximums for each dimension. This is for resizing; it of course has no effect if resizing is disabled. Errors will be thrown if your mins and maxes overlap incorrectly, or your initial dimensions are out of range.

Any <GridItem> properties defined directly will take precedence over globally-set options. For example, if the layout has the property isDraggable: false, but the grid item has the prop isDraggable: true, the item will be draggable, even if the item is marked static: true.

{

  // A string corresponding to the component key
  i: string,

  // These are all in grid units, not pixels
  x: number,
  y: number,
  w: number,
  h: number,
  minW: ?number = 0,
  maxW: ?number = Infinity,
  minH: ?number = 0,
  maxH: ?number = Infinity,

  // If true, equal to `isDraggable: false, isResizable: false`.
  static: ?boolean = false,
  // If false, will not be draggable. Overrides `static`.
  isDraggable: ?boolean = true,
  // If false, will not be resizable. Overrides `static`.
  isResizable: ?boolean = true,
  // By default, a handle is only shown on the bottom-right (southeast) corner.
  // As of RGL >= 1.4.0, resizing on any corner works just fine!
  resizeHandles?: ?Array<'s' | 'w' | 'e' | 'n' | 'sw' | 'nw' | 'se' | 'ne'> = ['se']
  // If true and draggable, item will be moved only within grid.
  isBounded: ?boolean = false
}

Grid Item Heights and Widths

Grid item widths are based on container and number of columns. The size of a grid unit's height is based on rowHeight.

Note that an item that has h=2 is not exactly twice as tall as one with h=1 unless you have no margin!

In order for the grid to not be ragged, when an item spans grid units, it must also span margins. So you must add the height or width or the margin you are spanning for each unit. So actual pixel height is (rowHeight * h) + (marginH * (h - 1).

For example, with rowHeight=30, margin=[10,10] and a unit with height 4, the calculation is (30 * 4) + (10 * 3)

margin

If this is a problem for you, set margin=[0,0] and handle visual spacing between your elements inside the elements' content.

Performance

<ReactGridLayout> has an optimized shouldComponentUpdate implementation, but it relies on the user memoizing the children array:

// lib/ReactGridLayout.jsx
// ...
shouldComponentUpdate(nextProps: Props, nextState: State) {
  return (
    // NOTE: this is almost always unequal. Therefore the only way to get better performance
    // from SCU is if the user intentionally memoizes children. If they do, and they can
    // handle changes properly, performance will increase.
    this.props.children !== nextProps.children ||
    !fastRGLPropsEqual(this.props, nextProps, isEqual) ||
    !isEqual(this.state.activeDrag, nextState.activeDrag)
  );
}
// ...

If you memoize your children, you can take advantage of this, and reap faster rerenders. For example:

function MyGrid(props) {
  const children = React.useMemo(() => {
    return new Array(props.count).fill(undefined).map((val, idx) => {
      return <div key={idx} data-grid={{ x: idx, y: 1, w: 1, h: 1 }} />;
    });
  }, [props.count]);
  return <ReactGridLayout cols={12}>{children}</ReactGridLayout>;
}

Because the children prop doesn't change between rerenders, updates to <MyGrid> won't result in new renders, improving performance.

React Hooks Performance

Using hooks to save your layout state on change will cause the layouts to re-render as the ResponsiveGridLayout will change it's value on every render. To avoid this you should wrap your WidthProvider in a useMemo:

const ResponsiveReactGridLayout = useMemo(() => WidthProvider(Responsive), []);

Custom Child Components and Draggable Handles

If you use React Components as grid children, they need to do a few things:

  1. Forward refs to an underlying DOM node, and
  2. Forward style,className, onMouseDown, onMouseUp and onTouchEnd to that same DOM node.

For example:

const CustomGridItemComponent = React.forwardRef(({style, className, onMouseDown, onMouseUp, onTouchEnd, children, ...props}, ref) => {
  return (
    <div style={{ /* styles */, ...style}} className={className} ref={ref} onMouseDown={onMouseDown} onMouseUp={onMouseUp} onTouchEnd={onTouchEnd}>
      {/* Some other content */}
      {children} {/* Make sure to include children to add resizable handle */}
    </div>
  );
})

The same is true of custom elements as draggable handles using the draggableHandle prop. This is so that the underlying react-draggable library can get a reference to the DOM node underneath, manipulate positioning via style, and set classes.

Contribute

If you have a feature request, please add it as an issue or make a pull request.

If you have a bug to report, please reproduce the bug in CodeSandbox to help us easily isolate it.

TODO List

  • Basic grid layout
  • Fluid grid layout
  • Grid packing
  • Draggable grid items
  • Live grid packing while dragging
  • Resizable grid items
  • Layouts per responsive breakpoint
  • Define grid attributes on children themselves (data-grid key)
  • Static elements
  • Persistent id per item for predictable localstorage restores, even when # items changes
  • Min/max w/h per item
  • Resizable handles on other corners
  • Configurable w/h per breakpoint

react-draggable's People

Contributors

acusti avatar andrewraycode avatar billcz avatar brianfay avatar cjblomqvist avatar davidstubbs avatar davidwells avatar dependabot[bot] avatar erfangc avatar gargroh avatar ipiranhaa avatar jmuerle avatar latentflip avatar liseki avatar martinross avatar matiss avatar mzabriskie avatar ndelangen avatar nikolas avatar panzarino avatar prayagverma avatar quolpr avatar sebastiaannijland avatar strml avatar tnrich avatar trysound avatar varad25 avatar victor-homyakov avatar williamstein avatar wojtekmaj avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

react-draggable's Issues

It Doesn't Work With Images

var Floorplan = React.createClass({

  render: function() {
    var allBoothKeys = Object.keys(this.props.booths)
      , self = this
      , booths

    if (allBoothKeys.length < 1) return null

    booths = allBoothKeys.map(function(key) {
      return (<Booth key={key} booth={self.props.booths[key]} />)
    })

    return (
      <Draggable
        start={{ x: 0, y: 0 }}
      >
        <div>
          {booths}
        </div>
      </Draggable>
    )
  }
})

Works. But if, inside div, I add

<img id="layout" src="img/layout.png" />

Does something weird: the drag starts on mouse up and I have to click again to stop the drag.

Does not work in react .13

In 0.13 I receive the error. Likely due to depreciation of renderComponent

"Cannot read property '__reactAutoBindMap' of null

onDrag reports previous position

onDrag is called before updating the state. It therefore reports positions which correspond to the previous mouse event. I am trying to update the position of a sibling in the onDrag handler. This behavior causes the sibling to "lag" behind.

Even worse, since handleDragEnd resets the state before calling onStop, the last position is never reported. This could lead to persisting different a position than last displayed.

I could think of three possible solutions:

  • Set state before invoking onDrag. This would result in an extra render even if dragging is canceled. Not sure if this could have unwanted side effects.
  • Explicitly pass the new position to createUIEvent and make it fall back to state if non is passed.
  • Add a new event callback (onDragged?) to keep everything 100% backwards compatible. I can't help but feel that would be ugly, though.

I'd be happy to provide a PR.

Programmatically trigger a drag start?

Is it possible to make a draggable component start following the mouse on, say, a button click? I'm imagining that a click, which would obviously contain a mouseup event, would be how the item got placed in this case.

Options for alternate drag offset strategies

This is similar to #81. I'm using draggable in an SVG context, and using translate() actually gets in the way, using x and y attrs would be better. In my specific case I can pan and zoom my canvas, as well as drag the elements on it. Getting element positions is now more complicated because there's the transform matrix applied to the top level node, and a second transform applied to the dragged item itself. This makes it harder to reason about the application and harder to do math to find local vs world coords, etc. If I could drag using x and y which works in SVG (or specify any other offset strategy) I would have a much easier time

onStart should be when dragging starts

For Example

You have a contentEditable div that you can edit and drag. When dragging starts, you change contentEditable to false, when dragging stops you change contentEditable to true. However, at the moment onStart triggers on a single click, which means you can never click into a contentEditable div to edit it after you have dragged it.

onStart should trigger when the user has started to drag the element, in my opinion. Or a new function like onDragStart.

Multi-touch support

Try dragging two elements at once on a touch screen device. It doesn't work right. The second element starts moving, but its movement is correlated with the first element instead of moving on its own with the second finger.

Not working on React component

Hi !
I am trying to use react-draggable on dynamic children, ie:

var MyElem = require('MyElem.react')

this.state.elem.map(function(elem, i){
  return (
    <Draggable key=i> 
      <MyElem myModel={elem} />   
    </Draggable>
  )
}

the node is properly rendered but neither the events nor the the class "react-draggable" are added.

It works with static children like :

<Draggable>
    <p>drag me </p>
</Draggable>

I am using react with addons v0.13.3

Kind regards

Have build in repo

As a windows user, there are complications getting the repo to build, which is really unfortunate because I really just wanted to get the compiled webpack file. It would be lovely if the repo consisted (at least with every release) had a build dir with the webpack (and any other files builded).

Should be very easy to achieve. Looks like a great component for React btw!

Not Draggable when using in webpack starter

I haven't been able to figure out what the issue is. It could definitely be user related. The example works fine, but when I add react-draggable to package.json and install it, a non-draggable element is the result. It looks just like the example, but the dragging events don't occur. I tried this in another starter that had a build system and saw the same problem. I'm new with using webpack, so I assume there is something going on with the build that is messing things up.

This is the webpack starter I was using: https://github.com/webpack/react-starter

I changed index.jsx to look like the following:

/** @jsx React.DOM */

var React = require("react"),
    Draggable = require("react-draggable");

var Display = require("./Display");
var Control = require("./Control");

var Example = React.createClass({

    handleDrag: function(el, ui) {
        console.log(ui.position);
    },

    render: function() {
        require("./style.less");
        return (
            <div>
        <div className="module-example">
            <Display />
            <div>
                <Control action="increment" label="+1" />
                <Control action="decrement" label="-1" />
                <Control action="reset" label="Reset" />
            </div>
        </div>
          <Draggable grid={[25,25]} zIndex={100} onDrag={this.handleDrag}>
            <div className="box">I can be dragged anywhere</div>
          </Draggable>
        </div>
        );
    }
});
module.exports = Example;

Here is what the debug window looks like:
image

Please, use translate3d instead of translate

I'm using this on a mobile app, translate is very slow as it don't make use of the GPU, you can just make a 3d option available, I'm forking it just for this. This is a great plugin, it will fit my needs with this option.

Inaccuracy within a svg

I'm not sure if this is within the scope of this project but I'm finding that if you make an element within a SVG draggable, the numbers are a bit inaccurate. This causes for quirky behavior when using a bound.

For example, try to move this rect quickly from left to right. You'll find it difficult to get it to line up with the dotted line it starts out on. It only works when you move slowly and smoothly. Initially, I was thinking it had something to do with pointer-events, but after playing around with it I'm not so sure.

http://jsfiddle.net/dzrgnjgf/15/

screen shot 2015-05-18 at 11 07 40 am

Update:

This seems to be webkit related as it's only happening within safari and chrome. Not firefox.

dragging on ios

When dragging on ios the browser continues to scroll. This behavior is visible on the demo page. Is there a way to disable scroll on touch start for mobile?

Ractify missing from dependencies

First of all, thanks for your work on the component.
When using the component with browserify it complains about reactify not being installed. Adding it to the package.json dependencies should fix that?

Move element from javascript

Hi! Is there anyway to move the draggable element without touch them? I'm trying to move to position I click.

Thanks!

Still drags if mouse let go away from draggable

If I mousedown on the horizontal or vertical draggables and move the mouse off the box then mouseup, the draggable behaves as though I still have mousedown until I mouseup on the box itself. Probably not desired behavior. Perhaps putting an event listener on the body for mouseups after the mousedown would be a fix? Can't wait until I actually know react and can PR for you...

Dragging SVG elements in IE (any version)

At the moment I don't have a jsfiddle setup, but it appears that the dragging of SVG elements has no effect in any version of Internet Explorer. I can't think of any obvious reason why this would be because translate works just fine in IE on SVG. It seems to have something to do with the onDrag event listeners.

Parent boundaries are offset when element is added after load

I just want to say that react-draggable is very useful and easy to use. However, I have an issue when I use bounds="parent" and then add an element in after page load: the border for the parent is offset to the very top left corner of the page.

Demonstration: Dragging problem

var root is undefined

var root = typeof window !== 'undefined' ? window : this; in draggable.js line 7

For some reason this is returning undefined for my setup. Things seem to work if I assign a new object {} instead. Is there any reason this has to be used?

component not updating on props change

I'm having issues with Draggable not re-rendering/repositioning on the page when the start data changes.

//... 
render: function() {
    var x = this.props.x;
    if (!x) x = 200;
    return this.transferPropsTo(
      <Draggable
        grid={[100, zoom/2]}
        start={{x: x, y: this.props.start * zoom}}
        onDrag={this.handleDrag}
        onStop={this.handleStop}
        zIndex={10000 + this.props.index * 10}
        handle=".start"
      >
      <section className="segment no-cursor" key={this.props.key} >....</section>
  </Draggable>

when this.props.x or this.props.start changes, the Draggable wont move to the new coordinates; Is there a way to force re-render or to tell it things change as desired position?

demo not working?

The demo is not working for me on chrome?

Uncaught TypeError: Cannot read property '0' of undefined react-draggable.js:146 getControlPosition react-draggable.js:146 module.exports.React.createClass.handleDragStart react-draggable.js:417 boundMethod 10540963_267651446772964_1656155574_n.js:7071 ....

Invariant Violation: exports.render(): A valid ReactComponent must be returned.

Hey,

when I use <Draggable>...</Draggable>, I get the following error:

Error: Invariant Violation: exports.render(): A valid ReactComponent must be returned. 
You may have returned undefined, an array or some other invalid object.

My code looks like this:

var React     = require('react'),
    Draggable = require('react-draggable');

module.exports = React.createClass({
  handleStart: function() {
    console.log('Dragging started');
  },

  handleDrag: function() {
    console.log('Dragged');
  },

  handleStop: function() {
    cosole.log('Draggins stopped');
  },

  render: function() {
    return (
      <Draggable
        onStart={this.handleStart}
        onDrag={this.handleDrag}
        onStop={this.handleStop}>
        <div className="foo">Bar</div>
      </Draggable>
    );
  }
));

When I remove the Draggable-stuff, the error disappears.

I logged the output of render() and got back a valid object.

ie11 support

Hi, is IE11 supported with react-draggable? I couldn't get the demo page to work when using IE11 on a windows 7 vm so I'm not sure if the demo page is just pointing to old code or if this is a general issue with react-draggable.

When dragging I saw "SCRIPT5007: Unable to get property '0' of undefined or null reference File: react-draggable.min.js, Line: 1, Column: 1805"

Does not play well with touch and mouse events.

If you are using your mouse on a touch screen laptop the mouse events will not work. I've fixed this locally but want to wait until my other PR is pulled in before I put in another pull request.

Add threshold

I would like to be able to not trigger onStart unless the user has dragged more than 20 pixels (this should be configurable).

Draggable Iframes

Hi just wondering what is the best way to make an Iframe draggable?

Make react-draggable agnostic of the HTML structure position

This is related to #56

Let's have a look at the ReactBootstrap and Bootstrap Modals side by side. I have updated the repository to include a Bootstrap example also https://github.com/cosminnicula/ReactBootstrapModalDraggable

1.Bootstrap -> this works

<Draggable handle=".modal-header">
    <div className="modal">
        <div className="modal-dialog">
            <div className="modal-content">
                <div className="modal-header">
                ...
                </div>
            </div>
        </div>
</Draggable>

2.ReactBootstrap -> this does not work

<Draggable handle=".modal-header">
    <Modal>
        ...
    </Modal>
</Draggable>

Under the hood, <Modal /> generates the same HTML mark-up as 1.

Questions:
a) Since 2. is equivalent to 1. in terms of HTML mark-up, why <Dragglable /> would not work with 2. ? (not asking technical details, but rather as a client of the library)
b) If <Draggable /> wants to cover more use cases, it has to be more flexible in terms where in the HTML structure needs to be included. E.g. if ReactBootstrap, or any other component decides not to expose a renderer function or a custom component, then <Draggable /> cannot be used. A solution would be for <Draggable /> to be agnostic of the HTML structure position and rely only on the handle property. Is this make sense?

Would ❤️ to see these two questions addressed!

Detecting click vs drag

I have a use-case in which the draggable element can either be dragged around or clicked to begin editing. Is there a way to detect a single click versus a drag motion?

Percentage based drag system

I'm trying to create resizable split panes. If a draggable component (the handle between panes) is set to a % offset from its parent, it's much more convenient. I'm not sure I can fanangle it with a transform: translate setup

Make react-draggable work with React Bootstrap Modal

Is there a way to make react-draggable work React Bootstrap Modal?

I'm thinking at something like this (btw..this doesn't work):

<Draggable handle=".modal-header">
    <Modal className="myClass" {...this.props} bsSize='small' title='Modal heading' animation={false}>
        <div className='modal-body'>
            <span>This is a test!</span>
        </div>
        <div className='modal-footer'>
            <Button>Close</Button>
        </div>
    </Modal>
</Draggable>

Multitouch support?

This may be a bit of a pipedream, but it would be great to support multi-touch devices by allowing multiple elements to be dragged simultaneously.

Right now, when you try to drag two elements at the same time, one touchmove event seems to take over for both elements.

From what I can tell, this happens because event listeners for move events are attached to the document, rather than a specific element. This means that "handleDrag" is going to fire on any component that is currently dragging.

I tried hacking together a solution with this in handleDrag:
if(e.target != React.findDOMNode(this)){console.log(e.target); return;}

I also had to change getControlPosition to look for "targetTouches" instead of "touches"
var position = (e.targetTouches && e.targetTouches[0]) || e;

This does allow elements to be moved independently via touch, but unfortunately causes another issue. If you drag too quickly, you're out of the bounds of the element, so the move event doesn't fire and control of the element is lost.

So I guess the event listeners need to stay on the document. Maybe an alternative solution would be keeping track of which touch in the e.touches array corresponds to which component. That seems tricky, but if I can come up with a clean way to do it I'll put in a pull request.

Feature Request: "disabled" prop

Sometimes you want to wrap elements in a <Draggable/> definition but not have them draggable until some condition is met. Currently you have to do this via:

if (shouldBeDraggable) {
    return (
        <Draggable>
            <div />
        </Draggable>
    );
} else {
    return (
        <div/>
    );
}

This is highly verbose and requires handling of different children prop values. If would be nice if react-draggable supported a disabled prop which would allow me to do something like this:


return (
    <Draggable disabled={!shouldBeDraggable}>
        <div />
    </Draggable>
);

Use the library without npm install.

Hi.
First: Thanks for your work, this is a great library.

My question: I enhanced my local fork (see #9) and would like to try it on my project. Because of my changes I can’t use the npm-install version of react-draggable. I am new to React and friends and am stuck getting draggable into my project. I tried to include the draggable.js and the main.js the traditional way. The actual error message is Uncaught ReferenceError: require is not defined caused by this line var React = require('react/addons');.
Any idea on how I can proceed?

Request for enhancement, initial position.

I'm able to set the initial Draggable child component's position using a style absolute positioning. The Draggable component will then make translate transforms based off this initial position. The issue with this approach is that I'm unable to store the updated position directly.

Would it be possible to add a start prop to the Draggable component to set the initial position? I've attempted to set the child transform style on first render, but the Draggable component is not aware of this.

Here's a link to how I'm currently fleshing out a window system using your component.
https://github.com/nirrius/nirrius/blob/master/app/common/pane/index.jsx#L55-L69
http://nirri.us/~teffen

Thank you.

moveOnStartChange breaks drag position

Check out the associated PR and open example/index.html. All it does is add moveOnStartChange to one drag element.

broken

You can see when you start dragging it the element jumps about 400 pixels down the page, not at the mouse coords, which I think is related to improperly handling/reading clientX and clientY. I was trying to implement a custom drag strategy to deal with my flux flow but it seems it's currently impossible because moveOnStartChange breaks dragging behavior.

moveOnStartChange causes unnecessary state updates

I'm using react-draggable in my application to scroll an item in a viewport (i.e. dragging the background scrolls the view), and also for custom scrollbars around the viewport. Needless to say, I needed to use the moveOnStartChange attribute to keep these two manners of scrolling the same viewport consistent. I found, however, that there was some really buggy behavior in react-draggable, and it stemmed from the fact that componentWillReceiveProps triggers too often.

If you declare your component like this:

<Draggable start={{x: 0, y: 0}}>...</Draggable>

then every time the render() method is called, the componentWillReceiveProps method is called because, technically, a new object is being passed as the 'start' property.

The solution I have come up with is to change lines 471-473 in the componentWillReceiveProps method to:

if (newProps.moveOnStartChange && newProps.start &&
    (newProps.start.x !== this.props.start.x || newProps.start.y !== this.props.start.y))
{
    this.setState(this.getInitialState(newProps));
}

Thoughts?

Have draggable start at default

Is there any way to have the drag start at the div's original value? for instance my css has the div's bottom : 0px. But once I wrap it in a draggable it ends up centered vertically.

Thanks!

Drag happens in webkit on drop down select

When a draggable component contains a drop down and a drop down option is changed with mouse, the components is moved when it should not be as opening selecting a new option and closing a drop down should not move the components.

droppable example

I am wondering how this draggable component actually can be used. I would think that usual use case is to drag & drop. But I struggle to see how I could use it for drag&drop.

Is anyone here using it for drag&drop? It would be very helpful to see example.

Thank you!

draggable within another draggable

I have a use case where one draggable can be within another draggable and so on. I have positioned the elements as 'absolute'. When i move a child draggable element, the parents left+top are also changed and it moves too. I have also tried attaching handles for each element (by class name), but to no affect. Is it possible to move the child element independently?

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.