SortableJS / react-sortablejs

React bindings for SortableJS
http://sortablejs.github.io/react-sortablejs/
MIT License
2.05k stars 210 forks source link

Missing nested example #134

Open gregoryforel opened 4 years ago

gregoryforel commented 4 years ago

Hello @waynevanson

Would you happen to have the missing nested example on a codepen or somewhere? It would be great to add it to the examples section.

Thanks in advance

waynevanson commented 4 years ago

@gregoryforel When I rebuild this with hooks I'll add the example.

gregoryforel commented 4 years ago

Thank you for answering! Looking forward to it

jonlepage commented 4 years ago

+1 thanks

Antoshkiv commented 4 years ago

@waynevanson can you tell when this will be added?

l704863851 commented 4 years ago

A nested example I wrote example

karldanninger commented 4 years ago

Hey, I just wrote a nested sortable this morning.

I used the group prop to do it, you can see the put and pull to have more granular control over whether an item is allowed to be dropped in a certain place. I hope this helps

<ReactSortable
  list={sortedItems}
  setList={onSetList}
  group={{ name: 'root', put: ['nested'], pull: true }}
>
  {sortedItems.map(item => {
    return (
      <div key={item.id}>
        <ReactSortable
          group={{
            name: 'nested',
            pull: true,
            put: ['root', 'nested'],
          }}
          animation="150"
          fallbackOnBody
          swapThreshold="0.65"
          list={item.nestedList}
          setList={onSetNestedList}
        >
          {item.nestedList.map(nestedItem => {
            return <div>{nestedItem}</div>;
          })}
        </ReactSortable>
      </div>
    )}
  )}
</ReactSortable>
waynevanson commented 4 years ago

I ran into issues with state management, which is why I made it blank at the time. I believe I have some solution, but can't be applied to major version 2 of react-sortablejs.

IlyasArinov commented 4 years ago

For whoever ends up needing an nested example, here is one that I made, using l704863851's example: https://codesandbox.io/s/react-sortable-js-nested-tu8nn?file=/src/App.js

823239259 commented 3 years ago

For whoever ends up needing an nested example, here is one that I made, using l704863851's example: https://codesandbox.io/s/react-sortable-js-nested-tu8nn?file=/src/App.js

It helped me! thank you!

collab-with-tushar-raj commented 3 years ago

I used the group prop to do it, you can see the put and pull to have more granular control over whether an item is allowed to be dropped in a certain place. I hope this helps

For me, it some times throws the error "TypeError: Cannot read property 'props' of null"

Screenshot 2020-12-14 at 9 41 04 PM

This is an intermittent issue but I am really unsure about the actual root cause.

camilacanhete commented 3 years ago

If you're using classes, I have an example for you. I'm using recursion to achieve the sorting result.

import React from 'react';
import { ReactSortable, Swap, Sortable } from "react-sortablejs";

export default class List extends React.Component {

    constructor (props) {
        super(props);
        this.state = {
            list: [
                {
                    id: "g17201002596u53e7n8dp8m",
                    name: "menu",
                    children: [
                        { id: "g16181002516373h7b8dp8j", name: "shrek", component: "image" },
                        { id: "g1619813226283as3o3xclm", name: "moana", component: "image" },
                        { 
                            id: "g1620327887630yddnv249k", 
                            name: "frozen", 
                            component: "container", 
                            children: [
                                { id: "g16181002516373h7b8dp9t", name: "lion king", component: "image" },
                                { id: "g1619813226283as3o3xc54", name: "toy story", component: "image" },
                                { id: "g1619813226283as3o3xc34", name: "frozen", component: "image" }
                            ]
                        }
                    ]
                },
                {
                    id: "g1727460259hski17n8sjf8",
                    name: "level",
                    children: [
                        { id: "g161810028475373h7b8dp8j", name: "up", component: "image" },
                        { id: "g16198sh5726283as3o3xclm", name: "tangled", component: "image" },
                        { id: "g16203284sf630yddnv249w", name: "snow white", component: "image" }
                    ]              
                }
            ]
        }

        this.sortableOptions = {
            animation: 150,
            fallbackOnBody: true,
            swapThreshold: 0.65,
            ghostClass: "ghost",
            group: "shared"
        };
    }

    //update list recursively
    sortList(currentList, sortedList, parent) {        
        for (let key in currentList) {
            currentList[key] = currentList[key];            
            if(currentList[key].id === parent) {                
                currentList[key].children = sortedList;
            } else {                
                if(currentList[key].children) {                    
                    this.sortList(currentList[key].children, sortedList, parent);
                }
            }
        }
        return currentList;
    }

    //set state
    setList(sortedList, parent) {        
        const list = this.sortList(this.state.list, sortedList, parent);
        this.setState({ list: list });
    }

    //creating list item function
    createItem(child) {  
        let listItem = null;                    
        switch(child.component) {            
            case 'container': 
                listItem = <ul key={ child.id }>
                    {
                        (child.children) ?
                        <ReactSortable
                            { ...this.sortableOptions }
                            list={ child.children }
                            parent={ child.id }
                            setList={
                                (sortedList, element) => {
                                    if(element) this.setList(sortedList, element.options.parent);
                                }
                            }
                        >
                        {
                            child.children.map((item) => this.createItem(item))
                        }
                        </ReactSortable>   
                        : <li></li>
                    }
                </ul>
            break
            default:
                listItem = <li key={ child.id }>{ child.name }</li>
        }
        return listItem;
    }

    //creating list function
    createList(list) {
        return list.map((child) => {                    
            return(
            <li key={ child.id }>  
                { child.name }
                <ul>
                    <ReactSortable
                        { ...this.sortableOptions }                                       
                        list={ child.children }
                        parent={ child.id }
                        setList={
                            (sortedList, element) => {
                                if(element) this.setList(sortedList, element.options.parent);                                    
                            }
                        }                
                    >
                    {
                        child.children.map((item) => {
                            return this.createItem(item);                                        
                        })
                    }
                    </ReactSortable>
                </ul>
            </li>
            )
        });
    }

    //render the component
    render() {
        return(
            <ul>{ this.createList(this.state.list) }</ul>
        )
    }
}
skvanniarajan commented 2 years ago

@IlyasArinov

For whoever ends up needing an nested example, here is one that I made, using l704863851's example: https://codesandbox.io/s/react-sortable-js-nested-tu8nn?file=/src/App.js

Can we make the child from one container not dropped in another

IlyasArinov commented 2 years ago

@IlyasArinov

For whoever ends up needing an nested example, here is one that I made, using l704863851's example: https://codesandbox.io/s/react-sortable-js-nested-tu8nn?file=/src/App.js

Can we make the child from one container not dropped in another

Yeah, use different group name or put, pull

yanghuanrong commented 2 years ago

I wrote a nested example referring to these cases, but nesting will cause reference relationships, I do not understand how the internal operation, can you help me

https://codesandbox.io/s/winter-rain-9gv5y3?file=/src/App.js

Zaimbinjawaid commented 2 years ago

@IlyasArinov or anyone else, if you have a nested example for sortable in Vue.js. Kindly share it.

bonttimo commented 2 years ago

Has anyone managed to fix the react 18 duplication issue?

In the example provided by @IlyasArinov https://codesandbox.io/s/react-sortable-js-nested-tu8nn?file=/src/App.js There is a bug when you drag an item from a container one level up. For example, if you drag text: item 8 under container: item 2. the original item 8 will stay in place but a new one will be created under item 2. The same happens when you drag a nested container to the root level.

Have been struggling with how to fix this issue.

bytewiz commented 2 years ago

There is a bug when you drag an item from a container one level up. For example, if you drag text: item 8 under container: item 2. the original item 8 will stay in place but a new one will be created under item 2. The same happens when you drag a nested container to the root level.

@bonttimo dunno if still relevant but if you haven't fixed it yet I am working on fixing it. I see the issue

Heilemann commented 1 year ago

@bytewiz Did you finish it?

Heilemann commented 1 year ago

Has anyone managed to fix the react 18 duplication issue?

In the example provided by @IlyasArinov https://codesandbox.io/s/react-sortable-js-nested-tu8nn?file=/src/App.js There is a bug when you drag an item from a container one level up. For example, if you drag text: item 8 under container: item 2. the original item 8 will stay in place but a new one will be created under item 2. The same happens when you drag a nested container to the root level.

Have been struggling with how to fix this issue.

Any chance you found a fix for this?

Marius-AdamDT commented 1 year ago

Has anyone managed to fix the react 18 duplication issue?

In the example provided by @IlyasArinov https://codesandbox.io/s/react-sortable-js-nested-tu8nn?file=/src/App.js There is a bug when you drag an item from a container one level up. For example, if you drag text: item 8 under container: item 2. the original item 8 will stay in place but a new one will be created under item 2. The same happens when you drag a nested container to the root level.

Have been struggling with how to fix this issue.

Any updates on the issue?

VuHuuTuan commented 1 year ago

I also have the same problem, I have tried something like using flushSync but it doesn't seem to work well. It took me a while, here's how I fixed it.

SOLVE IDEA: rearrange the orders of changes (stacking changes order and executed the order with deeper elements first). chrome-capture-2023-2-7

This is my sandbox Edit react-sortablejs-nested

pawpaw2022 commented 1 year ago

flushSync

Can you share the code?

VuHuuTuan commented 1 year ago

@pawpaw2022

Can you share the code?

Sandbox link added (https://github.com/SortableJS/react-sortablejs/issues/134#issuecomment-1457692418). Look at my /src/groupTree/GroupTree.tsx/handleSetGroups you can see my flushSync attemp at 2.. (Worked but cause warning "flushSync was called from inside a lifecycle") (My demo currently worked without problem at 3.)

sagun-k commented 1 year ago

Your code completely works fine but only for the single root it is not working for the multiple root as in one root drag to make the child of another root

fineshop commented 11 months ago

I also have the same problem, I have tried something like using flushSync but it doesn't seem to work well. It took me a while, here's how I fixed it.

SOLVE IDEA: rearrange the orders of changes (stacking changes order and executed the order with deeper elements first). chrome-capture-2023-2-7 chrome-capture-2023-2-7

This is my sandbox Edit react-sortablejs-nested

Works fine can you modify it so we can add multiple parents?

VuHuuTuan commented 11 months ago

@sagun-k @fineshop

Your code completely works fine but only for the single root it is not working for the multiple root as in one root drag to make the child of another root

Works fine can you modify it so we can add multiple parents?

I was fixed it a bit to work on multiple root base on groupname, good luck. chrome-capture-2023-10-9 New sandbox Edit react-sortablejs-nested (forked)

edit: reup gif

fineshop commented 11 months ago

@sagun-k @fineshop

Your code completely works fine but only for the single root it is not working for the multiple root as in one root drag to make the child of another root

Works fine can you modify it so we can add multiple parents?

I was fixed it a bit to work on multiple root base on groupname, good luck. chrome-capture-2023-10-9 New sandbox Edit react-sortablejs-nested (forked)

Thank you for this.

srikanth-perisync commented 5 months ago

Hey, I just wrote a nested sortable this morning.

I used the group prop to do it, you can see the put and pull to have more granular control over whether an item is allowed to be dropped in a certain place. I hope this helps

<ReactSortable
  list={sortedItems}
  setList={onSetList}
  group={{ name: 'root', put: ['nested'], pull: true }}
>
  {sortedItems.map(item => {
    return (
      <div key={item.id}>
        <ReactSortable
          group={{
            name: 'nested',
            pull: true,
            put: ['root', 'nested'],
          }}
          animation="150"
          fallbackOnBody
          swapThreshold="0.65"
          list={item.nestedList}
          setList={onSetNestedList}
        >
          {item.nestedList.map(nestedItem => {
            return <div>{nestedItem}</div>;
          })}
        </ReactSortable>
      </div>
    )}
  )}
</ReactSortable>

Can you please share the full code ??

rohanmahen commented 3 months ago

Hi all, not sure if this is solved but I'm using nested sortables with groups in a live app I built – here's my implementation.

For all sortables my sortableOptions are:

const sortableOptions = {
  animation: 150,
  fallbackOnBody: true,
  swapThreshold: 0.1,
  group: "shared",
  revertOnSpill: true,
}; 

Then my nested to-do list structure.

const handleSetList = useCallback(
  (newTodos: ParentTodo[], sortable: any) => {
    if (sortable) {
      flushSync(() => {
        setTodos(newTodos);
      });
    } else {
      setTodos(newTodos);
    }
  },
  [setTodos]
);

const handleSetSubList = useCallback(
  (subTodos: SubTodo[], todo: ParentTodo, sortable: any) => {
    const newSubTodos = todos.map((t) => {
      if (t.id === todo.id) {
        return { ...t, subTodos };
      }
      return t;
    });
    if (sortable) {
      flushSync(() => {
        setTodos(newSubTodos);
      });
    } else {
      setTodos(newSubTodos);
    }
  },
  [todos, setTodos]
);

<ReactSortable
        {...sortableOptions}
        list={todos}
        setList={(newTodos, sortable) => handleSetList(newTodos, sortable)}
      >
        {todos.map((todo) => (
          <div key={todo.id}>
            <div> Parent Todo </div>
            <ReactSortable
              {...sortableOptions}
              list={todo.subTodos as SubTodo[]}
              setList={(subTodos, sortable) =>
                handleSetSubList(subTodos, todo, sortable)
              }
            >
              {todo.subTodos.map((subTodo: Todo) => (
                <div> Sub-todo </div>
              ))}
            </ReactSortable>
          </div>
        ))}
      </ReactSortable>

For reference my Todo structure is an object with a list of subTodos which optionally have subtodos or not.

export interface SubTodo extends Todo {
  subTodos: [];
}

export interface ParentTodo extends Todo {
  subTodos: SubTodo[];
}

It works great for me! If you wanna try it out for yourself, there's a live demo on my site timebox.so :)