Python Callback from JS on dragdrop (with(out) dash-extensions)

Hi
After doing some sniffing around I got it working (thx to anyone who contributed):
The callback returns dragdrop info such as : element dragged, source/target container ids and their ordered children.
I kept within the scope of a mre but this method can easily be upgraded to support more dragula info if needed.

Python

import dash
from dash import dcc
import dash_bootstrap_components as dbc
from dash_extensions.enrich import DashProxy, html, Input, Output, State, ClientsideFunction, NoOutputTransform
import json
from pprint import pprint

app = DashProxy(transforms=[NoOutputTransform()])
app.config.external_stylesheets = ["https://epsi95.github.io/dash-draggable-css-scipt/dragula.css", dbc.themes.BOOTSTRAP]
app.config.external_scripts = ["https://cdnjs.cloudflare.com/ajax/libs/dragula/3.7.2/dragula.min.js",
                               "https://epsi95.github.io/dash-draggable-css-scipt/script.js"]

rows = ['aaa','bbb','ccc','ddd','eee','fff']

app.layout = dbc.Container(
    [
            html.Div([
                dbc.Input(type='text', id='client_event_receiver', placeholder='', style={'visibility': 'hidden'}),
                dbc.Row([
                    dbc.Col([
                        html.Div([
                            html.Div([
                                html.P(
                                        x,
                                        style={
                                            'text-indent':'28px'
                                            }
                                        )
                                    ],
                                     id = x,
                                     style={
                                         'backgroundColor':'rgb(180,180,180)',
                                         'border': '1px solid black',
                                         'height':'28px'
                                         }
                                    )
                                    for x in rows
                                    ],
                                 id="drag_container1",
                                 className="container",
                                 style ={
                                        'backgroundColor' : 'rgb(100,100,100)',
                                        'margin': 0,
                                        'padding': 0,
                                        'height' : '300px',
                                        'overflow-y':'auto',
                                        }
                                    ),
                        ]),
                    dbc.Col([
                        html.Div(
                            [],
                            id="drag_container2",
                            className="container",
                            style ={
                                'backgroundColor' : 'rgb(100,100,100)',
                                'margin': 0,
                                'padding': 0,
                                'height' : '300px',
                                'overflow-y':'auto',
                                }
                            )
                    ]),
                ]
                        )],
                     id="drag_container",
                     className="container",
                     style ={
                         'width':'80%'
                         }
                     ),
        ],
        className="dbc",
        fluid=True,
        )

app.clientside_callback(
    ClientsideFunction(namespace="clientside", function_name="make_draggable_mre"),
    Output("drag_container", "data-drag"),
    [Input("drag_container1", "id"),Input("drag_container2", "id")
     ]
)
@app.callback(Input('client_event_receiver', 'value'))
def get_dragdrop_info(json_str):
    if json_str:
        value = json.loads(json_str)
        print('-'*100)
        pprint(value)


if __name__ == "__main__":
    app.run_server(debug=True)

assets/script.js

if (!window.dash_clientside) {
    window.dash_clientside = {};
    var __rendezvouz_setter = Object.getOwnPropertyDescriptor(window.HTMLInputElement.prototype, "value").set;

}
window.dash_clientside.clientside = {
    make_draggable_mre: function () {
        let args = Array.from(arguments);
        var containers = [];
        setTimeout(function () {
            for (i = 0; i < args.length; i++) {
                containers[i] = document.getElementById(args[i]);
            }
            dragul = dragula(containers);
            dragul.on("drop", function (el, target, source, sibling) {
                var result = {
                    'element': el.id,
                    'target_id': target.id,
                    'target_children': Array.from(target.children).map(function (child) {return child.id;})
                    }              
                if (source.id != target.id) {
                    result['source_id'] = source.id;
                    result['source_children'] = Array.from(source.children).map(function (child) {return child.id;});
				}
                var client_event_receiver = document.getElementById("client_event_receiver");
                __rendezvouz_setter.call(client_event_receiver, JSON.stringify(result));
                var client_event = new Event('input', { bubbles: true });
                client_event_receiver.dispatchEvent(client_event);
                    
            })
        }, 1)
        return window.dash_clientside.no_update
    }
}

Prints the following when dragging the rows around:

Untitled

Note that only the targets are returned when reordering inside the same container

As a base, I used:

var __rendezvouz_setter = Object.getOwnPropertyDescriptor(window.HTMLInputElement.prototype, "value").set;

to trigger events on a dummy dbc.Input object.

Guess it has something to do with React?
Although I can’t foresee many drawbacks to this method yet, I don’t have the necessary expertise to determine if this is good practice or not. I do not completely understand what I did, so if anyone can clarify, It would be nice receive some further explanations.

For as far as I know, dash-extensions doesn’t provide a way to return a callback from js. (please, correct me if I’m wrong)
I encountered quite a few questions similar to this one.
It would be nice if dash-extensions would implement something similar to the above for communicating client events to the server… in that that lovely, clean, streamlined fashion. Big up to the dash-extensions devs! :wink: