Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support nesting DragDropContext #302

Open
alexreardon opened this issue Jan 30, 2018 · 42 comments
Open

Support nesting DragDropContext #302

alexreardon opened this issue Jan 30, 2018 · 42 comments

Comments

@alexreardon
Copy link
Collaborator

Currently this is not supported. It would be worth adding a warning if this is detected

@epferrari
Copy link

What's the reasoning behind not supporting nested DragDropContexts?

When rendering a tree structure of n depth where each tier has a collection of sortable items, the logic in the top level's onDragEnd becomes complex, especially when a a wrapper component is responsible for rendering the Droppable.

For example, in the snippet you refactored here, https://codesandbox.io/s/vmm0nplz5y, the onDragEnd of the top level component does not gracefully handle the item reordering in the nested list. While this could be fixed easily in this example with a single nesting level, n-depth nesting presents more of a challenge.

Any plans to include nested DragDropContexts on the roadmap?

@alexreardon
Copy link
Collaborator Author

It is possible @epferrari!! And as you said - it would make some things easier. This issue is more a stop gap - at least warn people that it is currently not supported.

A lot of the features are already context aware - however some are not. It would be a matter of going through and ensuring everything works as intended in nested contexts - adding tests and so on.

@alexreardon alexreardon changed the title Log warning when nesting DragDropContext Support nesting DragDropContext Jan 31, 2018
@Offirmo
Copy link

Offirmo commented Feb 27, 2018

@alexreardon please add this feature, would like to be able to do a WorkFlowy clone!

@Offirmo
Copy link

Offirmo commented Mar 1, 2018

For anything file-browser like, we need to be able to drop "files" into "folders" at the same level.
Is that possible or is it too hard?

@Offirmo
Copy link

Offirmo commented Mar 4, 2018

Just for the sake of nitpicking, without more freedom, react-beautiful-dnd's name is misleading. It should be "react-beautiful-reordering-with-dnd" :)

@alexreardon
Copy link
Collaborator Author

We hope to support this feature soon. We try to ensure that everything we build is done well with all the details thought through. From there we can grow feature sets and expand use cases.

@andymac4182
Copy link

This will be great. I have nested modules that are isolated from each other and aren't reliant on the parent ordering. 👍

@dwaltrip
Copy link

dwaltrip commented Jul 15, 2018

First thing: awesome library, it seems incredibly polished. Thank you for open-sourcing this.

Alternative solution to this problem: Would it be possible to move onDragEnd into the individual droppables? With this setup, the hook would only fire if the drag operation was completed while over that specific Droppable. It would allow for clean separation of logic in how different types of drag operations should be handled -- each in their own separate onDragEnd callback, passed to the appropriate droppable.

EDIT: I just saw this comment: #498 (comment). That is exactly what I'm talking about. It looks you all have awareness of this idea already. I guess just count this as me throwing in my support! Thanks again.

@dwaltrip
Copy link

dwaltrip commented Jul 15, 2018

I made a wrapper that allows passing onDragEnd directly to Droppable instances. I did a quick test and it seems to be working nicely, but there's definitely a chance I missed something. It hasn't been battle-tested yet.

With those caveats, here is the code: https://gist.github.com/dwaltrip/005a520aeaebe328a8a33998be2babc4

Change the import statement to use wrapped-react-beautiful-dnd instead of the actual library, remove the onDragEnd from your DragDropContext, and then add onDragEnd hooks to all your droppables, and then things should work nicely.

@alexreardon
Copy link
Collaborator Author

I love the passion behind this issue. Please keep your ideas coming! We hope to look at this after #484 ships (it is a beast!)

@eduludi
Copy link

eduludi commented Aug 9, 2018

Just an idea: Having the ability of disabling DragDropContext, Droppable and Draggable behaviour, that makes them just dumb containers, can help in some nested cases.

In my case, I need to force a re-render (with a condition) to allow "nested" drag and drops. Here is an example:

Edit 34l0l98x75

But this leads to a bad UX (hello flickering), and performance issues due to the re-render.

So, if DragDropContext, Droppable and Draggable have a disable prop that converts them in dummy containers of their children, a re-render can be avoided.

Any thoughts? Can this be achieved in a different way? Thanks!

@dcporter44
Copy link

Any news on plans for implementing this?

@dcporter44
Copy link

Just FYI to anyone else looking for this functionality, I used a separate library for DnD nesting:
https://github.com/frontend-collective/react-sortable-tree

Works really nicely

@abettadapur
Copy link

abettadapur commented Jan 7, 2019

@alexreardon a colleague and I have implemented a way to do this, I can submit a PR for your review if you would like.

The way we solved it is by having the child contexts determine that they are nested, and subscribe the onDrag* handlers to the parent context

  return (
            <NestedDragDropContext.Consumer>
                {context => {
                    if (context) {
                        // if context is defined, render as nested consumer using it.
                        return <NestedDragDropContextConsumer{...this.props} context={context} />;
                    } else {
                        // if context is undefined, render as parent/provider to create a new context.
                        return <NestedDragDropContextProvider {...this.props} />;
                    }
                }}
            </NestedDragDropContext.Consumer>
        );

class NestedDragDropContextProvider extends React.Component {
    private _onDragStartListeners: OnDragStartFunc[];
    private _onDragEndListeners: OnDragEndFunc[];
    private _context: INestedDragDropContextData;

    constructor(props) {
        super(props);

        this._onDragStartListeners = [];
        this._onDragEndListeners = [];
        this._context = {
            subscribeOnDragStart: this._subscribeOnDragStartListeners,
            subscribeOnDragEnd: this._subscribeOnDragEndListeners
        };
    }

    public render(): JSX.Element {
        return (
            <NestedDragDropContextContext.Provider value={this._context}>
                <DragDropContext onDragStart={this._onDragStart} onDragEnd={this._onDragEnd}>
                    <NestedDragDropContextConsumer {...this.props} context={this._context} />;
                </DragDropContext>
            </NestedDragDropContextContext.Provider>
        );
    }

    /**
     * Track provided onDragStart listener function to receive future onDragStart events.
     * Returns function to unsubscribe and remove this listener from the collection.
     */
    private _subscribeOnDragStartListeners = (listener: OnDragStartFunc): UnsubscribeFunc => {
        this._onDragStartListeners.push(listener);
        return () => {
            this._onDragStartListeners = this._onDragStartListeners.filter(l => l != listener);
        };
    };

    /**
     * Track provided onDragEnd listener function to receive future onDragEnd events.
     * Returns function to unsubscribe and remove this listener from the collection.
     */
    private _subscribeOnDragEndListeners = (listener: OnDragEndFunc): UnsubscribeFunc => {
        this._onDragEndListeners.push(listener);
        return () => {
            this._onDragEndListeners = this._onDragEndListeners.filter(l => l != listener);
        };
    };

    /**
     * Pass onDragStart events from the react-beautiful-dnd DragDropContext to all registered listeners.
     */
    private _onDragStart = (initial: DragStart, provided: HookProvided): void => {
        this._onDragStartListeners.forEach(listener => listener && listener(initial, provided));
    };

    /**
     * Pass onDragEnd events from the react-beautiful-dnd DragDropContext to all registered listeners.
     */
    private _onDragEnd = (result: DropResult, provided: HookProvided): void => {
        this._onDragEndListeners.forEach(listener => listener && listener(result, provided));
    };
}


class NestedDragDropContextConsumer extends React.Component {
    private _unsubscribeOnDragStart: UnsubscribeFunc;
    private _unsubscribeOnDragEnd: UnsubscribeFunc;

    public render(): JSX.Element {
        return <>{this.props.children}</>;
    }

    public componentWillMount() {
        const { context, onDragStart, onDragEnd } = this.props;
        if (onDragStart) {
            this._unsubscribeOnDragStart = context.subscribeOnDragStart(onDragStart);
        }
        if (onDragEnd) {
            this._unsubscribeOnDragEnd = context.subscribeOnDragEnd(onDragEnd);
        }
    }

    public componentWillUnmount() {
        if (this._unsubscribeOnDragStart) {
            this._unsubscribeOnDragStart();
        }

        if (this._unsubscribeOnDragEnd) {
            this._unsubscribeOnDragEnd();
        }
    }
}

The parent context will multicast the drag drop events to the nested children with this approach.

Right now, this wraps the library's DragDropContext, but we could modify the DragDropContext itself to make this multicasting of events transparent to the user.

What are your thoughts?

@thewei
Copy link

thewei commented Jan 17, 2019

@abettadapur can you show a demo?

@marcoacierno
Copy link

marcoacierno commented Jan 31, 2019

Do you have any update for this?

Thanks :)

@abettadapur
Copy link

abettadapur commented Jan 31, 2019

@navneet-g, can you show some code here?
(I have since left the company)

@ronaiza-cardoso
Copy link

can you help me? I want to do this, https://codesandbox.io/s/34l0l98x75, but with the subfields also changing.

  Field A
     Sub-Field 1 - A
   Field B
     Sub-Field 1 - B

Drap Sub-Field 1 - B to Field A

image

@elvisvasc
Copy link

elvisvasc commented Apr 5, 2019

Hi there,
i faced the same problem last week. I solved this using a context api approach.

See here: https://codesandbox.io/vnzw490w0y

Nested Dnd - Using Context API - CodeSandbox

@ndrsllwngr
Copy link

@elvisvasc it seems like your https://codesandbox.io/ demo is down, would you mind reposting it again? thank you in advance!

@elvisvasc
Copy link

elvisvasc commented May 8, 2019

@ndrsllwngr , check this link: https://codesandbox.io/embed/vnzw490w0y

@DarthVitalus
Copy link

@alexreardon a colleague and I have implemented a way to do this, I can submit a PR for your review if you would like.

The way we solved...

What are your thoughts?

hi! i understand that some time has passed, but is your solution still alive?) how to use it? what is NestedDragDropContext in your example?

@vestimir
Copy link

Any news about this feature?

@shrutikarekal
Copy link

Any https://codesandbox.io for nested list items for multi drag examples?

@ccpu
Copy link

ccpu commented Sep 10, 2019

I hope this feature implemented in the react-beautiful-dnd, its must-have feature. In the meanwhile, you may want to look at the atlaskit tree component, you may find it useful.

https://atlaskit.atlassian.com/packages/core/tree/example/drag_and_drop_with_nesting

@DarthVitalus
Copy link

@vestimir, hi! I assume you're trying to implement some sort of tree, which has branches and leaves and stuff. I wanted to do some sort of it, and wanted to be able to drag any leaf/branch and drop it in any other branch/root. And i managed it only after rearranging drop areas in order, when they are not overlayed by others. I mean, drop zone of a child is not on top of larger drop zone of parent, but comes after it! Does it make sense?)

@vestimir
Copy link

@DarthVitalus nope it's not a tree view – it's a CMS – we want to use the DND for managing idividual widgets on pages, and the widgets can have nested DND in their settings (for example to reorder UI cards inside of a card widget).

@castroCrea
Copy link

Hello @alexreardon, what is going on with this feature ?
Support nesting DragDropContext
Any news ?

@Ackos95
Copy link

Ackos95 commented Mar 25, 2020

Hi @alexreardon

Is there any news on this feature? I assume it's not started yet, is it planned for some near future, I'd like to switch my current implementation to this library, but I'm not able to without nested drag/drop logic. So my question is should I wait for it, or should I just carry on with what I already have?

@elvisvasc
Copy link

Hey guys! Check out this sandbox: https://codesandbox.io/embed/vnzw490w0y
I'm using context api to solve this problem.

@Ackos95
Copy link

Ackos95 commented Mar 27, 2020

@elvisvasc thanks, but that's not actually what this issue is about, those kind of examples do exist already. Issue here is with changing droppable targets, something like CMS as someone said earlier, so that in your example you could take one item from one list and put it on lists level, so it can be sorted with lists, or taking list and putting it into another list etc.

@grrowl
Copy link

grrowl commented Apr 29, 2020

but I'm not able to without nested drag/drop logic

Nested drag/drop is available using type= props, but since you'll only have one Context you'll only have one onDragEnd: (type: string, draggableId: string, index: number) => void. I found pthis nested list example pretty helpful](https://github.com/atlassian/react-beautiful-dnd/blob/master/stories/src/vertical-nested/quote-app.jsx)

It works great if your nested drag and drops are relatively close to each other (they can span multiple components) but not if your Context is rendered too high up in the app.

@Ackos95
Copy link

Ackos95 commented Apr 29, 2020

@grrowl I saw that example, but it doesn't support what I'm looking for, on the side those flaws you've mentioned, there is still one bigger problem - you can't pull item from "top list" into "nested list" or vice versa. In given example "nested list" is seen as an element of a "top list", and other "top list" elements can't be added to "nested list" (because it's just another element), and that is basically the core of my issue

@damikun
Copy link

damikun commented May 13, 2020

Im looking for same solution as you @Ackos95 i just use workaround by handling all by myself i found this example as start point.. https://codesandbox.io/s/5v2yvpjn7n?file=/ServiceCommandUnit.js:2242-2253

@Serivy
Copy link

Serivy commented Mar 30, 2021

I have a project which looks like was nesting DragDropContext's but it looks like in react-beautiful-dnd 11 it was working, however I needed to upgrade and in 12 and 13 it is not?

Here is an example of it working:
https://codesandbox.io/s/sub-dragdropcontext-problem-dl3vn
If you drag sub items around they drag as expected, main items drag as expected too.

And if you change the version to 12 or 13, reload the right panel and then try drag sub items,
The first drags sub item, second drags the parent, third drags sub item, fourth drags parent.

Has anyone come across this? I see here the solution is to not nest the DragDropContext, So in the mean time ill look at what refactor is required to do this.

@joshf
Copy link

joshf commented Mar 30, 2021

@Serivy i noticed the same, works in version 11 but broken after that

@jigarlodaya12
Copy link

Hi, Is there any way we can implement DnD with mutliple depths?
For example, While reordering, i would like to drop depth 2 item at depth 0 or vice versa.

@tarunbaraskar
Copy link

Hi , Any example with multi-level/nesting drag and drop?

@alexmarqs
Copy link

I have a project which looks like was nesting DragDropContext's but it looks like in react-beautiful-dnd 11 it was working, however I needed to upgrade and in 12 and 13 it is not?

Here is an example of it working: https://codesandbox.io/s/sub-dragdropcontext-problem-dl3vn If you drag sub items around they drag as expected, main items drag as expected too.

And if you change the version to 12 or 13, reload the right panel and then try drag sub items, The first drags sub item, second drags the parent, third drags sub item, fourth drags parent.

Has anyone come across this? I see here the solution is to not nest the DragDropContext, So in the mean time ill look at what refactor is required to do this.

@Serivy I’m facing the same problem, did you find any workaround without removing the nested context?

@Serivy
Copy link

Serivy commented Dec 2, 2021

@alexmarqs It is a bad work around but it managed something for my legacy purposes.

I created a new component DragDropContextNested which took the same props as DragDropContextProps and a droppableid. Using a new react context i can determine if there is a DragDropContext as a parent and just render the children, otherwise i hook up the context to match on the droppableid for the right callback. It's far from perfect though and really only to bridge a gap until its all re-written.

@alexmarqs
Copy link

Thanks @Serivy for sharing your workaround, in my case I just implemented an event mechanism, in the parent onDragEnd callback, I can understand if the event comes from nested components checking the type (I need to make sure that the types are unique)

@mlimberg
Copy link

mlimberg commented Jan 6, 2023

Nesting DragDropContext seems to work fine for me, did it finally get added? Or am I setting myself up for failure?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests