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