Giter Site home page Giter Site logo

clauderic / react-sortable-hoc Goto Github PK

View Code? Open in Web Editor NEW
10.7K 82.0 977.0 21.83 MB

A set of higher-order components to turn any list into an animated, accessible and touch-friendly sortable list✌️

Home Page: https://clauderic.github.io/react-sortable-hoc/

License: MIT License

JavaScript 85.29% HTML 7.82% SCSS 6.90%
react sortable drag-and-drop sorting javascript front-end dragging grid higher-order-component

react-sortable-hoc's Introduction

Warning

This library is no longer actively maintained. It will continue to receive critical security updates, but there are no new features planned. In future versions of React, the findDOMNode method will be deprecated. This method is a critical piece of the architecture of react-sortable-hoc, and the library will stop working in the future when that method is removed from react-dom.

All development efforts have been redirected towards @dnd-kit. It provides feature parity, built with a modern and extensible architecture, supports complex use-cases and has accessibility features built-in. New consumers are strongly encouraged to adopt @dnd-kit instead of adopting react-sortable-hoc.

Visit @dnd-kit github repository

React Sortable HOC

A set of higher-order components to turn any list into an animated, accessible and touch-friendly sortable list

npm version npm downloads license Gitter gzip size

Features

  • Higher Order Components – Integrates with your existing components
  • Drag handle, auto-scrolling, locked axis, events, and more!
  • Suuuper smooth animations – Chasing the 60FPS dream 🌈
  • Works with virtualization libraries: react-virtualized, react-tiny-virtual-list, react-infinite, etc.
  • Horizontal lists, vertical lists, or a grid ↔ ↕ ⤡
  • Touch support 👌
  • Accessible: supports keyboard sorting

Installation

Using npm:

$ npm install react-sortable-hoc --save

Then, using a module bundler that supports either CommonJS or ES2015 modules, such as webpack:

// Using an ES6 transpiler like Babel
import {SortableContainer, SortableElement} from 'react-sortable-hoc';

// Not using an ES6 transpiler
var Sortable = require('react-sortable-hoc');
var SortableContainer = Sortable.SortableContainer;
var SortableElement = Sortable.SortableElement;

Alternatively, an UMD build is also available:

<script src="react-sortable-hoc/dist/react-sortable-hoc.umd.js"></script>

Usage

Basic Example

import React, {Component} from 'react';
import {render} from 'react-dom';
import {SortableContainer, SortableElement} from 'react-sortable-hoc';
import arrayMove from 'array-move';

const SortableItem = SortableElement(({value}) => <li>{value}</li>);

const SortableList = SortableContainer(({items}) => {
  return (
    <ul>
      {items.map((value, index) => (
        <SortableItem key={`item-${value}`} index={index} value={value} />
      ))}
    </ul>
  );
});

class SortableComponent extends Component {
  state = {
    items: ['Item 1', 'Item 2', 'Item 3', 'Item 4', 'Item 5', 'Item 6'],
  };
  onSortEnd = ({oldIndex, newIndex}) => {
    this.setState(({items}) => ({
      items: arrayMove(items, oldIndex, newIndex),
    }));
  };
  render() {
    return <SortableList items={this.state.items} onSortEnd={this.onSortEnd} />;
  }
}

render(<SortableComponent />, document.getElementById('root'));

That's it! React Sortable does not come with any styles by default, since it's meant to enhance your existing components.

More code examples are available here.

Why should I use this?

There are already a number of great Drag & Drop libraries out there (for instance, react-dnd is fantastic). If those libraries fit your needs, you should definitely give them a try first. However, most of those libraries rely on the HTML5 Drag & Drop API, which has some severe limitations. For instance, things rapidly become tricky if you need to support touch devices, if you need to lock dragging to an axis, or want to animate the nodes as they're being sorted. React Sortable HOC aims to provide a simple set of higher-order components to fill those gaps. If you're looking for a dead-simple, mobile-friendly way to add sortable functionality to your lists, then you're in the right place.

Prop Types

SortableContainer HOC

Property Type Default Description
axis String y Items can be sorted horizontally, vertically or in a grid. Possible values: x, y or xy
lockAxis String If you'd like, you can lock movement to an axis while sorting. This is not something that is possible with HTML5 Drag & Drop. Possible values: x or y.
helperClass String You can provide a class you'd like to add to the sortable helper to add some styles to it
transitionDuration Number 300 The duration of the transition when elements shift positions. Set this to 0 if you'd like to disable transitions
keyboardSortingTransitionDuration Number transitionDuration The duration of the transition when the helper is shifted during keyboard sorting. Set this to 0 if you'd like to disable transitions for the keyboard sorting helper. Defaults to the value set for transitionDuration if undefined
keyCodes Array {
  lift: [32],
  drop: [32],
  cancel: [27],
  up: [38, 37],
  down: [40, 39]
}
An object containing an array of keycodes for each keyboard-accessible action.
pressDelay Number 0 If you'd like elements to only become sortable after being pressed for a certain time, change this property. A good sensible default value for mobile is 200. Cannot be used in conjunction with the distance prop.
pressThreshold Number 5 Number of pixels of movement to tolerate before ignoring a press event.
distance Number 0 If you'd like elements to only become sortable after being dragged a certain number of pixels. Cannot be used in conjunction with the pressDelay prop.
shouldCancelStart Function Function This function is invoked before sorting begins, and can be used to programatically cancel sorting before it begins. By default, it will cancel sorting if the event target is either an input, textarea, select, option, or button.
updateBeforeSortStart Function This function is invoked before sorting begins. It can return a promise, allowing you to run asynchronous updates (such as setState) before sorting begins. function({node, index, collection, isKeySorting}, event)
onSortStart Function Callback that is invoked when sorting begins. function({node, index, collection, isKeySorting}, event)
onSortMove Function Callback that is invoked during sorting as the cursor moves. function(event)
onSortOver Function Callback that is invoked when moving over an item. function({index, oldIndex, newIndex, collection, isKeySorting}, e)
onSortEnd Function Callback that is invoked when sorting ends. function({oldIndex, newIndex, collection, isKeySorting}, e)
useDragHandle Boolean false If you're using the SortableHandle HOC, set this to true
useWindowAsScrollContainer Boolean false If you want, you can set the window as the scrolling container
hideSortableGhost Boolean true Whether to auto-hide the ghost element. By default, as a convenience, React Sortable List will automatically hide the element that is currently being sorted. Set this to false if you would like to apply your own styling.
lockToContainerEdges Boolean false You can lock movement of the sortable element to it's parent SortableContainer
lockOffset OffsetValue* | [OffsetValue*, OffsetValue*] "50%" WhenlockToContainerEdgesis set totrue, this controls the offset distance between the sortable helper and the top/bottom edges of it's parentSortableContainer. Percentage values are relative to the height of the item currently being sorted. If you wish to specify different behaviours for locking to the top of the container vs the bottom, you may also pass in anarray(For example:["0%", "100%"]).
getContainer Function Optional function to return the scrollable container element. This property defaults to the SortableContainer element itself or (if useWindowAsScrollContainer is true) the window. Use this function to specify a custom container object (eg this is useful for integrating with certain 3rd party components such as FlexTable). This function is passed a single parameter (the wrappedInstance React element) and it is expected to return a DOM element.
getHelperDimensions Function Function Optional function({node, index, collection}) that should return the computed dimensions of the SortableHelper. See default implementation for more details
helperContainer HTMLElement | Function document.body By default, the cloned sortable helper is appended to the document body. Use this prop to specify a different container for the sortable clone to be appended to. Accepts an HTMLElement or a function returning an HTMLElement that will be invoked before right before sorting begins
disableAutoscroll Boolean false Disables autoscrolling while dragging

* OffsetValue can either be a finite Number or a String made up of a number and a unit (px or %). Examples: 10 (which is the same as "10px"), "50%"

SortableElement HOC

Property Type Default Required? Description
index Number This is the element's sortableIndex within it's collection. This prop is required.
collection Number or String 0 The collection the element is part of. This is useful if you have multiple groups of sortable elements within the same SortableContainer. Example
disabled Boolean false Whether the element should be sortable or not

FAQ

Running Examples

In root folder, run the following commands to launch React Storybook:

$ npm install
$ npm start

Accessibility

React Sortable HOC supports keyboard sorting out of the box. To enable it, make sure your SortableElement or SortableHandle is focusable. This can be done by setting tabIndex={0} on the outermost HTML node rendered by the component you're enhancing with SortableElement or SortableHandle.

Once an item is focused/tabbed to, pressing SPACE picks it up, ArrowUp or ArrowLeft moves it one place backward in the list, ArrowDown or ArrowRight moves items one place forward in the list, pressing SPACE again drops the item in its new position. Pressing ESC before the item is dropped will cancel the sort operations.

Grid support

Need to sort items in a grid? We've got you covered! Just set the axis prop to xy. Grid support is currently limited to a setup where all the cells in the grid have the same width and height, though we're working hard to get variable width support in the near future.

Item disappearing when sorting / CSS issues

Upon sorting, react-sortable-hoc creates a clone of the element you are sorting (the sortable-helper) and appends it to the end of the <body> tag. The original element will still be in-place to preserve its position in the DOM until the end of the drag (with inline-styling to make it invisible). If the sortable-helper gets messed up from a CSS standpoint, consider that maybe your selectors to the draggable item are dependent on a parent element which isn't present anymore (again, since the sortable-helper is at the end of the <body>). This can also be a z-index issue, for example, when using react-sortable-hoc within a Bootstrap modal, you'll need to increase the z-index of the SortableHelper so it is displayed on top of the modal (see #87 for more details).

Click events being swallowed

By default, react-sortable-hoc is triggered immediately on mousedown. If you'd like to prevent this behaviour, there are a number of strategies readily available. You can use the distance prop to set a minimum distance (in pixels) to be dragged before sorting is enabled. You can also use the pressDelay prop to add a delay before sorting is enabled. Alternatively, you can also use the SortableHandle HOC.

Wrapper props not passed down to wrapped Component

All props for SortableContainer and SortableElement listed above are intentionally consumed by the wrapper component and are not passed down to the wrapped component. To make them available pass down the desired prop again with a different name. E.g.:

const SortableItem = SortableElement(({value, sortIndex}) => (
  <li>
    {value} - #{sortIndex}
  </li>
));

const SortableList = SortableContainer(({items}) => {
  return (
    <ul>
      {items.map((value, index) => (
        <SortableItem
          key={`item-${index}`}
          index={index}
          sortIndex={index}
          value={value}
        />
      ))}
    </ul>
  );
});

Dependencies

React Sortable HOC only depends on invariant. It has the following peerDependencies: react, react-dom

Reporting Issues

If believe you've found an issue, please report it along with any relevant details to reproduce it. The easiest way to do so is to fork the react-sortable-hoc basic setup sandbox on CodeSandbox:

Edit on CodeSandbox

Asking for help

Please do not use the issue tracker for personal support requests. Instead, use Gitter or StackOverflow.

Contributions

Yes please! Feature requests / pull requests are welcome.

react-sortable-hoc's People

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  avatar  avatar

react-sortable-hoc's Issues

Nested sortable elements cause conflict

I have a list of grouped components. I can sort them just fine with the plugin.

But inside my components I have some lists that also use the same plugin. So a sortable inside a sortable. This conflicts with the parent groups. For example, if I have 3 sortable groups, the first 3 list items inside each group conflict with the parents because they share the same index. Once I get to four or five in any list, they sort fine.

I tested setting the index in the nested sortable list to index+10 and it works without conflict, but of course causes other problems because is the wrong index.

Will work on an example showing the problem.

Drag-handle problems cross-browser

A few things I noticed:

Firefox:
The drag handle in your demo page does not show up, but it works.
In some projects (not your demo), as you drag to sort elements, it selects the text on the page (also in IE11)
firefox-ie11-drag-selects-text

Safari: when using an image as a drag-handle (not background-image), set the image attribute draggable='false' or else it will try to drag the image as if dragging a copy to the desktop.
safari-drag-image

Chrome: does not work in the responsive simulator
chrome-touch-simulator-dnd

Feature Request: Drag and drop in a Grid

Hi @clauderic! I thought I'll create a standing issue incase anyone else is interested in working on this or has thoughts as well.

The feature request: Be able to drag and drop across axis, i.e., in a grid of any sort. Currently, the library only works one axis at a time.

Based on conversations with @clauderic, I looked into the issue a little bit more and it seems like the changes are only limited to SortableContainer and that too, mostly around calculating offset and managing the translate3d function. I am still trying to familiarize myself with how they really work.

I also imagine we'll need to also add a new prop or modify 'axis' prop to reflect the three choices - x, y or xy.

Clickable links inside items

What is the best way to support links inside sortable items?

Here's a JSFiddle showing the issue: https://jsfiddle.net/lambdazen/6r7r2cva/7/ -- you can try clicking the links once the page has loaded. It doesn't work. However, a right-click followed by "Open in a new tab" works as expected.

My current workaround is to attach onMouseDown to a call to window.open. Is there a better way?

Thanks in advance! Love your library.

Firefox dirtying

When the component is moved out of the context, In Firefox, it dirty the page.

Se the images
dirtying

dirtyin2

dirtying3

Firefox version: 48.0

Width of items with padding/border changes

Because react-sortable-hoc uses offsetWidth (see

this.width = node.offsetWidth;
), which includes borders and paddings (see https://developer.mozilla.org/de/docs/Web/API/HTMLElement/offsetWidth), as the width of the helper, it is too wide. This results in visual "jump" when dragging items.

Possible solutions:

  • always set "box-sizing: border-box" on the helper
  • use clientWidth instead.

Workaround:

  • set "box-sizing: border-box" on your SortableElements manually.

Request: HOC to prevent drag from starting on given element

The use case would be a non-draggable button nested inside a draggable list item.

Right now, the workarounds are to either attach native click events to the button (requiring a ref and extra lifecycle handlers), or to add a delay and hope the users click the button faster than the delay (but that reduces drag responsiveness). Otherwise, the dragging event takes precedence and blocks clicks to any clickable thing inside the SortableElement.

SortableHandle could also be a workaround, but that comes at the expense of being able to drag the SortableElement by dragging its background.

Drag helper in wrong styling context

Since the "drag helper" (the new DOM node added to the end of the <body> which is the actual element being dragged) is not within the same parent element as the source being dragged, styles have a strong potential of getting messed up on the helper (which may rely on parent context). In other words if I have:

<body>
  <div class="user-list">
    <div class="user">Draggable User</div>
    <div class="user">Draggable User</div>
  </div>
</body>

I might depend (in CSS) on a .user-list > .user relationship for styling the <div class="user">. But when the drag starts, we essentially get...

<body>
  <div class="user-list">
    <div class="user" style="visibility-hidden">Draggable User</div>
    <div class="user">Draggable User</div>
  </div>

  <!-- Helper (the thing being dragged)-->
  <div class="user" style=" ... ">Draggable User</div>
</body>

This can be fixed if it is possible to put the drag-helper node as a sibling to the original thing being dragged instead of at the end of the body. Thoughts?

Custom events inside sortables

Hi there!
I need to use custom events on elements inside SelectableElement. But Selectable blocks onClick events (I just can bind onMouseDown / onMouseMove and etc, but not onMouseUp or onClick).
Is there any easy way to solve it?

Thanks!

Request: eagerOnSortEnd

It would be great to have a prop which is similar to onSortEnd but gets called whilst you are actually dragging (but only when oldIndex and newIndex have different values from when eagerOnSortEnd was last called).

This way, for example, the "Item 1" and "Item 2" labels of existing list elements can change 'live' whilst I'm dragging to "Item 0", "Item 2" and "Item 0", "Item 1" as I drag the original "Item 0" down the list.

TypeError: undefined is not an object (evaluating 'array.length')

Unfortunately, I keep getting the following error when trying to sort an array of custom objects:

TypeError: undefined is not an object (evaluating 'array.length')

This error happens after successfully dragging and dropping 1 time. After that, the error appears and I am unable to move any of the sortableItems.

I know the problem lies in the chunk of code:

screenshot 2016-08-25 06 10 11

I have also included the code for my sortable component:

screenshot 2016-08-25 06 10 23

Request: Nested Sortable

Fantastic library, thank you. An idea for a potential feature:

A page shows multiple sets of Todos:

TodoList Category A:

  • TodoItem A.1
  • TodoItem A.2

TodoList Category B:

  • TodoItem B.1
  • TodoItem B.2

I'd like to be able to:

  1. Move a TodoItem from Category A to B
  2. Move the entire TodoList Category B above TodoList Category A

Update: Looks like item 2 is almost working perfectly already. I am only running into a bug where a dragHandle on a TodoItem which is disabled is causing the parent TodoList to get dragged.

Request: Don't start dragging unless cursor been dragged x pixels.

I ran into an issue where I have items that expand when clicked. When making them sortable they no longer respond to a click, instead they become dragged immediately. I can get around this by setting the "pressDelay" but then you have to wait for the delay before it's draggable which I noticed that people tend not to do.
I'd like a way to not start the sorting unless you have actually dragged the cursor for say 5px.

Basic styles?

Nice library. Loving it.

It would be nice if you could include in the repo some basic styles to give developers (especially those not good at making things look great) a starting point to then further customize.

ReactCSSTransitionGroup when removing/adding items to sortable not working

I tried using the ReactCSSTransitionGroup element with your great sortable and can make the sortable animate on initial mount (although looks jerky for some reason). But was unable to make it able to animate the adding/removing of individual items to a sortable. Similar code does work for normal object lists where you remove/add to them. Do you have any examples or ideas why it isn't working with a Sortable? I forked another fiddle to show what I mean https://jsfiddle.net/r2LpL7be/

And here is an example of a standard array with splice + animation: http://codepen.io/agrewell/pen/ZYdGOJ

Re-order multiple items (multiple selection)

Hi,

It would be very cool if it was possible to move a list of several items (whatever their positions in the list) at a given position of the list (at sort end, items would be ordered depending on their previous relative positions in the list).

I started something but I think you could do much better.

Thank you :)

import React, {Component} from 'react';
import {SortableContainer, SortableElement} from 'react-sortable-hoc';


const SortableItem = SortableElement(
  class SortableItemAnonymous extends Component {
    onMouseDownCallback( event ){
      return this.props.onMouseDownCallback( this.props.index, event )
    }
    render(){
      var id = this.props.uniqueIdToken + "SortableItem" + this.props.index
      var className = this.props.checked ? "helper checked-sortable-item" : ""
      return (
        <li key={"li-sortable-item-"+id}
            data-sortableId={id}
            style={this.props.style}
            onMouseDown={this.onMouseDownCallback.bind(this)}
            className={className}>
          {this.props.value}
        </li>
      )
    }
  }
)
const SortableList = SortableContainer(
  class SortableListAnonymous extends Component {
    render() {
      var self = this
      return (
        <ul>
          {this.props.items.map((value, index) =>
            {
              var style = {}
              style.visibility = value.visibility ? value.visibility : ''
              value.height = typeof(value.height)!='undefined' ? value.height : value.defaultHeight
              style.height = typeof( value.height ) == 'string' ? value.height : value.height+'px'
              var checked = self.props.selection ? self.props.selection.indexOf(index) > -1 : 0
              return (
                <SortableItem key={`sortable-item-${index}`}
                              style={style}
                              checked={checked}
                              uniqueIdToken={self.props.uniqueIdToken}
                              index={index} value={value.value}
                              onMouseDownCallback={self.props.onMouseDownCallback} />
              )
            }
          )}
        </ul>
      )
    }
  }
)

export class SortableComponent extends Component {
  constructor(props){
    super(props)
    this.state = {
      selected: null,
      selection: [],
      moving: false,
      movingstarted: false,
      items: props.items
    }
  }
  componentWillReceiveProps(nextProps){
    this.setState({
      selected: null,
      selection: [],
      moving: false,
      movingstarted: false,
      items: nextProps.items
    })
  }
  onMouseDownCallback = (index, event) => {
    var newSelection = this.state.selection
    var testIndex = newSelection.indexOf(index)
    if( event.ctrlKey || event.metaKey || this.state.selection.length==0 ) {
      if(newSelection && testIndex != -1 ){
        newSelection.splice(testIndex, 1)
      }else {
        newSelection = newSelection.concat([index])
      }
    }else{
      // si on clique sur un item sans faire CTRL, et quil nest pas encore dans la selection,
      // on met a jour la selection courante juste avec cet item
      if( testIndex == -1 ){
        newSelection = [index]
      }
    }
    this.setState({
      selected: index,
      selection: newSelection.sort((a, b)=>{return a-b})
    })
    event.preventDefault()
    return false
  }
  onSortStart = ({node, index, collection}, event) => {
    this.setState({
      movingstarted: true
    })
  };
  onSortMove = (event) => {

    if( !this.state.moving && this.state.movingstarted ) {
      var selection = this.state.selection
      var selected = this.state.selected
      var items = this.state.items


      var indexSelected = selected
      for (var i = selection.length - 1; i >= 0; i--) {
        var j = selection[i]
        if (j != selected) {
          if (j < indexSelected) indexSelected--
          items[j].height = 0
          items[j].visibility = 'hidden'
        }else{
          items[j].height = items[j].defaultHeight * selection.length
        }
      }

      // DOM MANAGEMENT
      if( selection.length > 1 ) {
        let helpers = document.getElementsByClassName('helper')
        let hl = helpers.length - 1
        /* helpers[hl].innerHTML = ''
         for (let i = 0; i < selection.length; i++ ) {
         let selindex = selection[i]
         let value = this.props.uniqueIdToken+"SortableItem"+selindex
         helpers[hl].innerHTML += ''+document.querySelector('[data-sortableId="' + value + '"]').outerHTML+'';
         }*/
        helpers[hl].innerHTML = selection.length + ' ' + this.props.multipleSelectionLabel
      }
      // END DOM MANAGEMENT

      this.setState({
        items: items,
        moving: true
      })
    }

  };
  onSortEnd = ({oldIndex, newIndex}) => {
    if( this.state.moving && this.state.movingstarted ) {
      if (this.state.selection.length > 0) {

        var newOrder = []
        // new order of index (array of values where values are old indexes)
        // it depends if we've "upped" the list (newIndex < oldIndex) or "downed" it
        var toPushInNewOrderLater = []
        for( var idx = 0; idx < this.state.items.length; idx++ ){
          if( this.state.selection.indexOf(idx) == -1 ) {
            if( newIndex>oldIndex ) {
              if (idx <= newIndex) {
                newOrder.push(idx)
              } else if (idx > newIndex) {
                toPushInNewOrderLater.push(idx)
              }
            }else{
              if (idx < newIndex) {
                newOrder.push(idx)
              } else if (idx >= newIndex) {
                toPushInNewOrderLater.push(idx)
              }
            }
          }
        }
        newOrder = newOrder.concat(this.state.selection).concat(toPushInNewOrderLater)


        var newitems = this.state.items
        var newselection = this.state.selection
        var newselected = this.state.selected

        // Pour determiner la nouvelle liste ditems, on commence par supprimer tous les index de la selection
        // Quand on supprime un item dont lindex est avant le newIndex, on decremente le newIndex
        var selectionToPush = []
        for (var i = this.state.selection.length - 1; i >= 0; i--) {
          var index = this.state.selection[i]
          if (index < newIndex && index != this.state.selected) newIndex--
          selectionToPush.unshift(newitems[index])
          newitems.splice(index, 1)
        }
        // a present, on insere au niveau de newIndex, la liste ordonnée de la selection
        // pour chacun on remet la hauteur et la visibilité par defaut
        var k = 0
        for (var i = 0; i < selectionToPush.length; i++) {
          selectionToPush[i].height = selectionToPush[i].defaultHeight
          selectionToPush[i].visibility = 'visible'
          newitems.splice(newIndex + k, 0, selectionToPush[i])
          k++
        }
        // sil y a eu changement de tri, ou qu'on a selectionné plusieurs items
        if (oldIndex != newIndex || (oldIndex == newIndex && this.state.selection.length > 1)) {
          // on clear la selection
          newselection = []
          newselected = null
        }

        // mise a jour du state local
        this.setState({
          items: newitems,
          selected: newselected,
          selection: newselection,
          moving: false,
          movingstarted: false
        });

        this.props.callbackNewOrder( newOrder )
      }
    }
  };
  render() {
    return (
      <SortableList uniqueIdToken={this.props.uniqueIdToken}
                    items={this.state.items}
                    selection={this.state.selection}
                    selected={this.state.selected}
                    helperClass="helper"
                    onMouseDownCallback={this.onMouseDownCallback}
                    onSortEnd={this.onSortEnd}
                    onSortStart={this.onSortStart}
                    onSortMove={this.onSortMove}
                    useDragHandle={false}
                    distance={10} />
    )
  }
}

USAGE :

let items = [
{value:"item 1", defaultHeight:10},
{value:"item 2", defaultHeight:10},
{value:"item 3", defaultHeight:10}
]

<SortableComponent items={items}
                             uniqueIdToken="test"
                             multipleSelectionLabel=" items selected"
                             callbackNewOrder={(oldIndexesWithNewOrder) => { console.log(oldIndexesWithNewOrder) }} />

Sometimes doesn't work in Chrome

When first loaded in a new Chrome window, dragging works fine, then for some reason it just stops working in every tab on that window. If I open a new window it works again. Both my code and the examples stop working with no indication to a problem in the console.

Dealing with dynamically added items (fiddle inside)

Hey Clauderic,

Just played around with this, and it's a great little component! Great job :)

One stumble-block I ran into though was when dealing with dynamically added items. Take a peek at this fiddle:

https://jsfiddle.net/t9va06g5/1/

If you click the add more button, then drag an existing item of the list, it's only then that the newly added item is rendered. Do you know if there is any way to re-render the list when new items are added like this?

Thanks again.

Request: Ignore right clicks

Right now, right clicking will initiate a drap&drop, even if the intent is actually to open the context menu.

Request: prevent dragging on custom handle

I need to implement table with column sorting and in addition to that - table headers should be sortable horizontally

For horizontal headers sorting I've used your great library
Now I need to handle column sorting by clicking arrows (you know, up|down arrows) inside every header row to sort ASC/DESC specific column.

To do this I need somehow cancel event propogation by clicking arrows so that react-sortable-hoc doesn't begin dragging. I've tried

<SortIcon onClick={(e) => {
    e.stopPropagation()
    e.preventDefault()
    onSortColumn(value.alias, users)
    return false
}}/>

but it does not prevent dragging :(

Is it possible?

Don't require global babelHelpers in ES6 build

If the user of the library doesn't happen to use the babel helpers transform and the global helpers runtime, there will be a lot of ReferenceError: babelHelpers is not defined that cannot be fixed other than by providing the global helpers.

No animation in IE11

On chrome example works ultra smooth and fast, however in IE11 there is NO animations at all. How can i eliminate this ?

Can't drag after removing row

I'm using Redux to manage the state of my app, and after I dispatch an action to remove a specific row, I can only drag from the rows above the ones I deleted. I'm including some gifs to help with the visualization: here and here.

I'm not using the arrayMove function described in the sample code because I was planning on writing out the logic for that myself using Redux, but I fear that may be the issue.

Better Docs

I have no idea how to get the EXACT replica of the basic usage which is shown on the index page

Like no docs for that, I could get the plugin working but I do not know how to show the dragging element preview to follow the cursor like in the example

Jquery Portability

Is there a way to get this on Jquery ? I was trying to read the source code but no idea how react system works so I was hoping that this can get ported to jQuery or native js.

BUG: `margin`s are not taken into account in the re-positioning of the items

How to reproduce

  1. Go to the demo page
  2. Add .Showcase__style__stylizedItem {margin-bottom: 10px;} to the styles.
  3. Drag the first item of the list bellow the second and don't release the mouse button.

What was expected

The second item slides all the way to the top.

What happens

The second item slides up but not all the way to the top (leaving a margin above it).

Reason

The items are translated by dimension with is either width or height, which in turn are offsetWidth and offsetHeight respectively
But offsetWidth and offsetHeight do not include the margins MDN.

Snapshots

image

image

Clicking without dragging doesn't update newIndex

It seems like the SortableContainer's newIndex only resets when onSortMove is triggered, which doesn't seem to get fired if you just click a SortableElement without dragging it (which makes sense). Just clicking a SortableElement moves the element to the last newIndex that was set in that container (or 0 if it wasn't) and causes some funky sorting.

The examples (like this one) show this happening too when you click an item besides Item 0.

If VirtualScroll is wrapped inside AutoSize dragging an item doesn't trigger scroll

Awesome job @clauderic 👍

I'm having a huge list that I'm using react-virtualized but if I want the width to be dynamic, for this I must wrap it with AutoSize You should already know this 😊

class VirtualList extends Component {
  render() {
    const { items } = this.props;

    return (
      <AutoSizer disableHeight>
        {({ width }) => (
          <VirtualScroll
            ref="VirtualScroll"
            estimatedRowSize={70}
            rowHeight={70}
            rowRenderer={({ index }) => {
              const { product } = items[index];
              return <SortableItem index={index} product={product} />;
            }}
            rowCount={items.length}
            width={width}
            height={500}
          />
        )}
      </AutoSizer>
    );
  }
}

So when I do, I lose the ability to scroll while dragging & item, any hints how to fix this?

Multiple Lists.

Hi! Is there a way to define multiple sortable lists in the same page?

Ability to change handle/card styling onClick

When using the html5 DND api and a handle I can benefit from the :active pseudo class. Since we are creating a copy we don't have an ability to do that. Ideally i'd be able to add some kind of -selected class to my handle or have the :active working on the handle

To give an example of my use case:
My item has a handle and on click + drag the handle changes colour.

Error when I testing a sortable component

When i running a test and this component has react-sortable-hoc inmediately throws this error

react-sortable-hoc/dist/commonjs/utils.js:34
  return pre[0].toUpperCase() + pre.substr(1);
TypeError: Cannot read property '0' of undefined

I think this error is beacuse Jsdom, anyone has had this error ? any test works when the component has this lib

Help me :)

Wrapper component instead of an enhancer function (HOC)

First-off thank you for this great library.
I recently realized that it better to have a wrapper component (like what React-Draggable does) then to have an HOC.
And so I was wandering what is the reasoning behind this choice, and if it's possible to have a non HOC version of the wrappers.

I would be happy to do a PR to add this feature.

Advantages to using a wrapper component

  • No need for withRef option.
  • Can be used to implement HOC.
  • No displayName generation (=> cleaner in the dev-tools panel)

Usage example

const SortableItem = ({value}) => <li>{value}</li>;
const SortableList = ({items}) => (
    <ul>
        {items.map((value, index) => 
            <SortableElement key={`item-${index}`} index={index}>
                <SortableItem value={value} />
            </SortableElement>
        )}
    </ul>
);

class SortableComponent extends Component {
    state = {
        items: ['Item 1', 'Item 2', 'Item 3', 'Item 4', 'Item 5', 'Item 6']
    }
    onSortEnd = ({oldIndex, newIndex}) => {
        this.setState({
            items: arrayMove(this.state.items, oldIndex, newIndex)
        });
    };
    render() {
        return (
            <SortableContainer onSortEnd={this.onSortEnd}>
                <SortableList items={this.state.items} />
            </SortableContainer>
        )
    }
}

or

const SortableItem = ({value, ...props}) => (
    <SortableElement {...props}>
        <li>{value}</li>
    </SortableElement>
);
const SortableList = ({items, ...props}) => (
    <SortableContainer {...props}>
        <ul>
            {items.map((value, index) => 
                <SortableItem key={`item-${index}`} index={index} value={value} />
            )}
        </ul>
    </SortableContainer>
);

class SortableComponent extends Component {
    state = {
        items: ['Item 1', 'Item 2', 'Item 3', 'Item 4', 'Item 5', 'Item 6']
    }
    onSortEnd = ({oldIndex, newIndex}) => {
        this.setState({
            items: arrayMove(this.state.items, oldIndex, newIndex)
        });
    };
    render() {
        return (
            <SortableList items={this.state.items} onSortEnd={this.onSortEnd} />
        )
    }
}

When the sort starts (mouse down) the helper is added but the ghost is not hidden

How to reproduce

  1. Go to the demo page
  2. Add .Showcase__style__stylizedHelper {background: transparent;} to the styles.
  3. Press (without moving or releasing the mouse) on an item of the list.

What happens

The item is still visible (white background), if you move the mouse the item is now hidden (background grey)

What was expected

The item is hidden (background grey)

I can submit a PR if needed.

Adding and Removing items

I'm trying to add and remove items from the lists, using onClick on buttons. Doing Add was easy using the SortableComponent, but I don't know how to use remove because SortableList and SortableItem are functions not classes to add a method(code below and attached file). I'm just starting working, with React ES6, maybe is something that I don't understand.

Thanks.

const SortableList = SortableContainer(({items}) => {
    return (
        <table>
            {items.map((value, index) =>
            <tr>
                <td>
                <SortableItem key={`item-${index}`} index={index} value={value} />
                    </td>
                    <td>
                    <button>Remove{index}</button>
                </td>
             </tr>
            )}
        </table>
    );
});

class SortableComponent extends React.Component{
     constructor(props) {
         super(props);
         this.state = {items: [1, 2, 3, 4]};
         this.onSortEnd = this.onSortEnd.bind(this);
         this.add = this.add.bind(this);
     }


    onSortEnd({oldIndex, newIndex}) {
        this.setState({items: arrayMove(this.state.items, oldIndex, newIndex)});
    }


    add() {
        const items = this.state.items;
        let new_item = this.state.items.length+1;
        items.push(new_item);
        this.setState({items : items})
        console.log(new_item);
    }

    render() {
        return (
            <div>
                <SortableList items={this.state.items} onSortEnd={this.onSortEnd} />
                <button onClick={this.add}>Add Document</button>
            </div>
        );
    }
}

example

Layout changes upon drag

At the moment I have a SortableElement that contains an input textfield and a textarea. When I click to drag the component to a desired location the layout of the SortableElement changes. What extra steps do I need to do to address this issue? Thanks for the help.

before

after

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.