I would like to be able to reorder the children of a div.
I have seen a previous example (Drag and drop cards - #2 by RenaudLN) that allow to drag&drop elements thanks to the Dragula js library.
However, while on the UI, the order of elements are changed, on the children property of the div, the children are not reordered.
I have adapted the code to adapt the children order within the javascript.
What I am still missing is the ability from the JS code to trigger the event “the children of this element has been changed, trigger the callbacks that depends on it”.
To make it clearer, here is an example:
app.py
import dash
import dash_html_components as html
from dash.dependencies import Input, Output, ClientsideFunction, State
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="drag_container",
className="container",
children=[html.Button(id=f"child-{i}", children=f"child-{i}") for i in range(5)],
),
],
)
app.clientside_callback(
ClientsideFunction(namespace="clientside", function_name="make_draggable"),
Output("drag_container", "data-drag"),
[Input("drag_container", "id")],
[State("drag_container", "children")],
)
@app.callback(
Output("order", "children"),
[
Input(component_id="btn", component_property="n_clicks"),
Input(component_id="drag_container", component_property="children"),
],
)
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(debug=True)
asset/script.js
if (!window.dash_clientside) {
window.dash_clientside = {};
}
window.dash_clientside.clientside = {
make_draggable: function (id, children) {
setTimeout(function () {
var drake = dragula({});
var el = document.getElementById(id)
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)
});
// How can I trigger an update on the children property
// ???
})
}, 1)
return window.dash_clientside.no_update
}
}
assets/dargula.css
.gu-mirror {
position: fixed !important;
margin: 0 !important;
z-index: 9999 !important;
opacity: 0.8;
-ms-filter: "progid:DXImageTransform.Microsoft.Alpha(Opacity=80)";
filter: alpha(opacity=80);
}
.gu-hide {
display: none !important;
}
.gu-unselectable {
-webkit-user-select: none !important;
-moz-user-select: none !important;
-ms-user-select: none !important;
user-select: none !important;
}
.gu-transit {
opacity: 0.2;
-ms-filter: "progid:DXImageTransform.Microsoft.Alpha(Opacity=20)";
filter: alpha(opacity=20);
}