dbc.Checklist in dmc.Accordition

@hoatran, man, this one had me thinking a while.

In the end I used pattern machting callbacks and a helper function. It’s not perfect as the City- Accordion is still visible even if no State is chosen- this only works at startup. Maybe you can alter the visibility in the callback. Feel free to ask for clarification as this is maybe not best coding style :see_no_evil:

from dash import Dash, dash_table, html, Input, Output, MATCH, ALL, ctx
import pandas as pd
import dash_mantine_components as dmc
import dash_bootstrap_components as dbc

df = pd.read_csv('https://raw.githubusercontent.com/hoatranobita/holiday_challenge/main/solar.csv')


def create_panel(idx, state):
    return dmc.AccordionPanel(
        [
            dbc.Checklist(
                options=[{"label": state, "value": state}],
                value=[],
                id={'type': "checklist-input", 'index': idx}
            ),
            html.Div(id={'type': "city", 'index': idx})
        ]
    )


app = Dash(__name__, external_stylesheets=[dbc.themes.LUX])

app.layout = dbc.Container([
    dbc.Row([
        dbc.Col(
            [
                dmc.Accordion(
                    children=[
                        dmc.AccordionItem(
                            [dmc.AccordionControl("State")] +
                            [create_panel(idx, state) for idx, state in enumerate(df['State'].unique())],
                            value="customization",
                        ),
                    ]
                ),
            ],
            width=2
        ),
        dbc.Col([
            dash_table.DataTable(
                df.to_dict('records'),
                columns=[{"name": i, "id": i} for i in df.columns],
                id={'type': 'tbl', 'index': 0}
            ),
        ], width=10)
    ], className='p-2 align-items-center')
], fluid=True)


@app.callback(
    Output({'type': 'city', 'index': MATCH}, 'children'),
    Input({'type': 'checklist-input', 'index': MATCH}, 'value'),
    prevent_initial_call=True
)
def update_table(checklist):
    # get the triggering index to make sure the indices of checklist-input and checklist-input-2
    # are the same
    idx = ctx.triggered_id['index']

    dff = df[df['State'].isin(checklist)]
    return dmc.Accordion(
        children=[
            dmc.AccordionItem([
                dmc.AccordionControl("City"),
                dmc.AccordionPanel([
                    dbc.Checklist(
                        options=[
                            {"label": x, "value": x} for x in dff['City'].unique()
                        ],
                        value=[],
                        id={'type': 'checklist-input-2', 'index': idx})
                ])
            ],
                value="customization2")
        ])


@app.callback(
    Output({'type': 'tbl', 'index': ALL}, 'data'),
    Input({'type': 'checklist-input', 'index': ALL}, 'value'),
    Input({'type': 'checklist-input-2', 'index': ALL}, 'value'),
    prevent_initial_call=True
)
def update_table(checklist, checklist2):
    # flatten checklist and checklist2 - due to using pattern matching callbacks with wildcard ALL
    # these are a list of lists
    cl_flat = [item for sublist in checklist for item in sublist]
    cl2_flat = [item for sublist in checklist2 for item in sublist]

    if cl_flat != [] and cl2_flat != []:
        dff = df[df['State'].isin(cl_flat)]
        dff = dff[dff['City'].isin(cl2_flat)]
        return [dff.to_dict(orient='records')]

    elif cl_flat != [] and cl2_flat == []:
        dff = df[df['State'].isin(cl_flat)]
        return [dff.to_dict(orient='records')]
    else:
        return [df.to_dict(orient='records')]


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

table

mred accordion

3 Likes