question-mark
Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. It collects links to all the places you might be looking at while hunting down a tough bug.

And, if you’re still stuck at the end, we’re happy to hop on a call to see how we can help out.

Re-order multiple items (multiple selection)

See original GitHub issue

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) }} />

Issue Analytics

  • State:closed
  • Created 7 years ago
  • Reactions:3
  • Comments:7 (1 by maintainers)

github_iconTop GitHub Comments

1reaction
furiousOystercommented, Dec 27, 2016

@stouch I think it is more user friendly to clump all the selected items into a contiguous block, then drag this around ? I can’t think of a use case where a user would select a disjointed group, then expect to move that group around, maintaining the disjointedness. I’m imaginging at onMoveStart to cause them to all clump together, so that if you immediately released the drag, they would have new positions in a single block. (my apologies… this is much harder to describe than to visualize…)

Read more comments on GitHub >

github_iconTop Results From Across the Web

Multi-Select to reorder items in a playlist - Knowledge Base
Simply select multiple items by ctrl or shift + left click on the items to add or remove from your selection. Once you...
Read more >
Multi-select backlog items, reorder when changing columns ...
Multi -select items on the product backlog; Reorder cards when changing columns ... On all the backlogs, you can select multiple items (using ......
Read more >
How to re-order the value of a multiple select based on user ...
My solution is to add an html5 attribute to the multiselect element, data-sorted-values, to which I append new values, and remove un-selected ......
Read more >
Reorder multiple &lt;select> fields in a web page - CodeProject
Simple (continuous) multiple selection may be done by pressing and holding down the Shift key while clicking to select. All elements between the...
Read more >
Reorder Multiple Items in the Sortable - Kendo UI for jQuery
Learn how to reorder multiple items using the Kendo UI Sortable widget. ... <p>Select multiple items with CTRL key</p> <div class="list-wrapper"> <ul ...
Read more >

github_iconTop Related Medium Post

No results found

github_iconTop Related StackOverflow Question

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found