Leaflet map inside dmc.AccordionItem

Hey @davidharris

I think this is similar to the issue described here:

Try triggering a dummy resize via callback as shown here.

If I’m not mistaken, this could be a workaround for the issue. Note that you’ll need dash 2.17 for the example below due to the missing Output() for the clientside callback.

from dash import Dash, html, Input, Output, ClientsideFunction
from dash.exceptions import PreventUpdate
import dash_mantine_components as dmc
from dash_iconify import DashIconify
import dash_leaflet as dl

app = Dash(__name__)


def cre_main_accordion():
    items_list = [
        {
            "id": "map-item",
            "image": DashIconify(icon="noto-v1:world-map", width=50),
            "label": "Map",
            "description": "Goes wrong if viewport is resized while accordion item is closed",
            "content": dmc.Text("Processing...", size="sm"),
        },
        {
            "id": "selection-item",
            "image": DashIconify(icon="noto:globe-showing-asia-australia", width=50),
            "label": "Select countries",
            "description": "",
            "content": dmc.Text("To be completed...", size="sm")
        },
    ]
    result = dmc.Accordion(
        children=[
            dmc.AccordionItem(
                [
                    create_accordion_label(
                        item["label"], item["image"], item["description"]
                    ),
                    dmc.AccordionPanel(item["content"], id=f"{item['id']}-content"),
                ],
                value=item["id"],
            )
            for item in items_list
        ],
        id="accord",
    )
    return result


def create_accordion_label(label, image, description):
    return dmc.AccordionControl(
        dmc.Group(
            [
                dmc.Avatar(src=image, radius="xl", size="lg") if type(image) == str else image,
                html.Div(
                    [
                        dmc.Text(label),
                        dmc.Text(description, size="sm", fw=400, c="dimmed"),
                    ]
                ),
            ]
        )
    )


def create_map():
    return dl.Map(
        children=dl.TileLayer(),
        center=[56, 10], zoom=6,
        style={'width': '100%', 'height': '50vh'}
    )


app.layout = dmc.MantineProvider(children=[cre_main_accordion()])


@app.callback(
    Output("map-item-content", "children"),
    Input("accord", "value")
)
def refresh_map(val):
    if val == "map-item":
        return create_map()
    else:
        raise PreventUpdate


app.clientside_callback(
    ClientsideFunction(namespace="clientside", function_name="resize"),
    Input("map-item-content", "children")
)

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

custom.js in assets folder:

if (!window.dash_clientside) {
    window.dash_clientside = {};
}
window.dash_clientside.clientside = {
    resize: function(value) {
        console.log("resizing..."); // for testing
        setTimeout(function() {
            window.dispatchEvent(new Event("resize"));
            console.log("fired resize");
        }, 500);
    return null;
    },
};
3 Likes