These tweaks works for me.
This works with dynamic children in the drag-container and does trigger an update of children upon reordering:
App.py:
"""
App for testing drag and drop
"""
import dash
import dash_html_components as html
from dash.dependencies import Input, Output, ClientsideFunction, State
SERVER_PORT = 12327
app = dash.Dash(
__name__,
#external_scripts=["https://cdnjs.cloudflare.com/ajax/libs/dragula/3.7.2/dragula.min.js"],
)
app.layout = html.Div(
id="main",
children=[
html.Button(id="btn", children="Refresh display for order of children"),
html.Label(id="order"),
html.Div(id="container"),
html.Button(id="btn_children", children="new children")
],
)
app.clientside_callback(
ClientsideFunction(namespace="clientside", function_name="make_draggable"),
Output("drag_container", "data-drag"),
Input("drag_container", "children"),
State("drag_container", "id")
)
@app.callback(
Output('container', 'children'),
Input('btn_children', 'n_clicks'),
prevent_initial_call=True
)
def test(n_clicks):
print("running")
if (n_clicks % 2) == 0:
range_ = range(5)
else:
range_ = range(5,10)
a = html.Div(
id="drag_container",
className="container",
children=[html.Button(id=f"child-{i}", children=f"child-{i}") for i in range_],
),
return(a)
@app.callback(
Output("order", "children"),
[
Input(component_id="btn", component_property="n_clicks"),
Input(component_id="drag_container", component_property="children"),
],
prevent_initial_call = True
)
def watch_children(nclicks, children):
"""Display on screen the order of children"""
return ", ".join([comp["props"]["id"] for comp in children])
if __name__ == '__main__':
app.run_server(host="0.0.0.0", port=SERVER_PORT ,debug=True, dev_tools_hot_reload=True)
Script.js
if (!window.dash_clientside) {
window.dash_clientside = {};
}
window.dash_clientside.clientside = {
make_draggable: function (children, id) {
setTimeout(function () {
var drake = dragula({});
var el = document.getElementById(id)
var order = document.getElementById("order")
drake.containers.push(el);
drake.on("drop", function (_el, target, source, sibling) {
// a component has been dragged & dropped
// get the order of the ids from the DOM
var order_ids = Array.from(target.children).map(function (child) {
return child.id;
});
// in place sorting of the children to match the new order
children.sort(function (child1, child2) {
return order_ids.indexOf(child1.props.id) - order_ids.indexOf(child2.props.id)
});
// Trigger an update on the children property
order.innerHTML = order_ids
})
}, 1)
return window.dash_clientside.no_update
}
}