✊🏿 Black Lives Matter. Please consider donating to Black Girls Code today.
⚡️ Concerned about the grid? Kyle Baranko teaches how to predicting peak loads using XGBoost. Register for the August webinar!

Dragable Toast?

Just a quick question regarding dash’s toast widget.
Is it possible to make it drag- or resizable by the user?
If not: where would I start looking for other solutions for the same requirements, considering that I have no clue of
js and would like to leave it like that :smiley:

I’ve already read these ones and would like to know if there has been some progress on the matter:


Hey @luggie, here’s how I make toast (or other fixed position elements) draggable:

app.py

# ...

app.layout = html.Div([
    # ...
   dbc.Toast(
        id="my_toast",
       # ...
    )
])

# ...


# This clientside callback will be called once when the element is injected into the DOM and will give
# it some javascript event listeners to make it draggable around the viewport.
app.clientside_callback(
    ClientsideFunction(namespace="clientside", function_name="make_draggable"),
    Output("my_toast", "className"), # the attribute here will not be updated, it is just used as a dummy
    [Input("my_toast", "id")],
)

assets/style.css

#my_toast {
    position: fixed;
    /*top: wherever;
    left: wherever;*/
}

assets/scripts.js

if (!window.dash_clientside) {
    window.dash_clientside = {};
}
window.dash_clientside.clientside = {
    make_draggable: function(id) {
        if (id) {
            dragElement(document.getElementById(id));

            function dragElement(elmnt) {
                var pos1 = 0, pos2 = 0, pos3 = 0, pos4 = 0;
                elmnt.children[0].onmousedown = dragMouseDown;

                function dragMouseDown(e) {
                    e = e || window.event;
                    e.preventDefault();
                    // get the mouse cursor position at startup:
                    pos3 = e.clientX;
                    pos4 = e.clientY;
                    document.onmouseup = closeDragElement;
                    // call a function whenever the cursor moves:
                    document.onmousemove = elementDrag;
                }

                function elementDrag(e) {
                    e = e || window.event;
                    e.preventDefault();
                    // calculate the new cursor position:
                    pos1 = pos3 - e.clientX;
                    pos2 = pos4 - e.clientY;
                    pos3 = e.clientX;
                    pos4 = e.clientY;
                    // set the element's new position:
                    elmnt.style.top = (elmnt.offsetTop - pos2) + "px";
                    elmnt.style.left = (elmnt.offsetLeft - pos1) + "px";
                }

                function closeDragElement() {
                    // stop moving when mouse button is released:
                    document.onmouseup = null;
                    document.onmousemove = null;
                }
            }
        }
        return window.dash_clientside.no_update;
    }
}

Thanks @RenaudLN or your code so far. I’m still facing two things I’d like to fix:

  • I’d like to have a toggle button that switches the toast on and off. The minimal example code underneath works without the toggle button but when the toast is set is_open again but the callback when it is initiated, the toast is not draggable anymore

  • The toast should be able to be dragged over a dcc.Graph whose orientation should not be affected by the moving toast above it.

Any idea on this? Again,thanks so far!

python:

import dash
from dash.dependencies import Input, Output, State, ClientsideFunction
import dash_bootstrap_components as dbc
import dash_daq as daq
import dash_core_components as dcc
import dash_html_components as html
import plotly.express as px

df = px.data.iris()
fig = px.scatter_3d(df, x='sepal_length', y='sepal_width', z='petal_width',
                    color='species')
    

app = dash.Dash(__name__, external_stylesheets=[dbc.themes.BOOTSTRAP],
                    meta_tags=[{"name": "viewport", "content": "width=device-width, initial-scale=1"}])

app.layout = html.Div([
   dbc.Col(daq.BooleanSwitch(id="btn_toast_toggle", color="#123456"), width="auto"),
   dbc.Toast(children=dbc.Card(dbc.CardBody('some text')), id="my_toast", dismissable=True, icon="danger"),
   html.Div([dcc.Graph(id='fig_3d', figure=fig, style={'height': 'inherit'})])], className="figcontainer")
   
@app.callback(Output("my_toast", "is_open"),
                [Input("btn_toast_toggle", "on")])
def toggle_toast(n):
    if n:
        return True
    return False

    
app.clientside_callback(
    ClientsideFunction(namespace="clientside", function_name="make_draggable"),
    Output("my_toast", "className"),
    [Input("my_toast", "id")])

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

/assets/style.css:

#my_toast {
    position: fixed;
    top: 66;
    right: 50;
    width: 350;
    z-index: 2147483647;
}

.figcontainer {
    z-index: 1;
    height: 500px;
  }

assets/script.js is unchanged

I managed to fix my first request with simply changing the input of the clientside.callback like this:

   app.clientside_callback(
            ClientsideFunction(namespace="clientside", function_name="make_draggable"),
            Output("my_toast", "className"),  # the attribute here will not be updated, it is just used as a dummy
            [Input("my_toast", "is_open")],
            [State("my_toast", 'id')])

and the /assets/style.css

...
window.dash_clientside.clientside = {
    make_draggable: function(is_open, id) {
        if (id && is_open){
...

However I’d be really happy if someone could have a starting idea on how to temporary switch off dragging the dcc.Graph while moving the toast

Also: How to make a component resizable by the user? :slight_smile:

You can use the resize css property.

1 Like

Very interesting repo has emerged for dragable and resizeable components: