@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
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)
mred accordion