Re-order multiple items (multiple selection)
See original GitHub issueHi,
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:
- Created 7 years ago
- Reactions:3
- Comments:7 (1 by maintainers)
Top 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 <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 >Top Related Medium Post
No results found
Top Related StackOverflow Question
No results found
Troubleshoot Live Code
Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start FreeTop Related Reddit Thread
No results found
Top Related Hackernoon Post
No results found
Top Related Tweet
No results found
Top Related Dev.to Post
No results found
Top Related Hashnode Post
No results found
Top GitHub Comments
@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…)
@stevenfabre @ssilve1989 On a related issue, @mixa9269 has it working: https://github.com/clauderic/react-sortable-hoc/pull/138#issuecomment-320627060