Took me a little longer to do it, but here is how this works:
import dash
from dash import html, dcc, Dash, Input, Output, ALL, State, ctx
app = Dash(__name__, use_pages=True)
app.layout = html.Div(
[
# main app framework
html.Div("Testing", style={'fontSize':50, 'textAlign':'center'}),
html.Div([
html.A(page['name']+" | ", id={'index':page['name'], 'type':'childrenLinks'},
style={'cursor':'pointer'}, n_clicks=0)
for page in dash.page_registry.values()
]),
html.Hr(),
html.Div(id='childrenContent', children=[], style={'display':'flex', 'alignItems':'stretch',
'alignContent':'stretch', 'gap':'10px'}),
# # content of each page
dash.page_container
]
)
@app.callback(
Output('childrenContent','children'),
Input({'index':ALL, 'type':'childrenLinks'}, 'n_clicks'),
Input({'index':ALL, 'type':'removeButton'}, 'n_clicks'),
State('childrenContent','children'),
)
def childrenContent(n, b, c):
if ctx.triggered:
if ctx.triggered_id.type == 'childrenLinks':
for page in dash.page_registry.values():
if page['name'] == ctx.triggered_id.index:
newLayout = page['layout']
newId, newButton = {'index':sum(n), 'type':'addedChildren'}, \
html.Button('X', id={'index':sum(n), 'type':'removeButton'},
style={'position':'absolute', 'right':'0', 'padding':'5'})
try:
c.append(html.Div([newButton, newLayout()], id=newId, style={'flexGrow':'1', 'position':'relative'}))
except:
c.append(html.Div([newButton, newLayout], id=newId, style={'flexGrow':'1', 'position':'relative'}))
else:
for child in c:
if child['props']['id']['index'] == ctx.triggered_id.index:
c.remove(child)
return c
return dash.no_update
if __name__ == "__main__":
app.run(debug=True, port=12345)
note: There is probably a better way to have this interact, like having a dedicated page where this interact exists instead of the overall layout.
You could even use it where you want to have the interactions be driven from a dropdown or accordion of the page.