Components nesting - pattern matching

Hello there

I am working on a app, which has a offCanvas (AIO component) coponent as filter’s container. This component is used on each page .
In this OffCanvas, I have checkLists for filtering data, the checkLists are one AIO component.
Now the problem is I can’t find a optimal way of adding select all option to the AIO checklist, because in the offCanvas component I refer to multiple checklists objects, which I collect selections from.
In order to get this working, I had to give up the select all option and set AIO_List id as id of components from offCanvas. It seems that lack of select options might be deal breaker for some users.

I know it is a little bit confusing, here some code that might help grasp the problem:

components / AIO_List_BASIC (the commented code is for select all, but not working) :

from dash import Dash, Output, Input, State, html, dcc, callback, MATCH, ctx, html, callback_context
import uuid
import dash_bootstrap_components as dbc
import base64
from components.grids import grid_1x2
import dash_mantine_components as dmc
#from utils.cache import set_specyfic_cache


# All-in-One Components should be suffixed with 'AIO'
class AIO_List(html.Div):  # html.Div will be the "parent" component

    #list_of_values = []
    # A set of functions that create pattern-matching callbacks of the subcomponents
    class ids:
        checklist = lambda aio_id: {
            'component': 'AIO_List',
            'subcomponent': 'checklist',
            'aio_id': aio_id
        }

        div = lambda aio_id: {
            'component': 'AIO_List',
            'subcomponent': 'div',
            'aio_id': aio_id
        }
        select_all = lambda aio_id: {
            'component': 'AIO_List',
            'subcomponent': 'select_all',
            'aio_id': aio_id
        }
    
    # Make the ids class a public class
    ids = ids

    # Define the arguments of the All-in-One component
    def __init__(
        self,
        available_list,
        choices_list,
        list_title = '',
        aio_id=None,
    ):
        #self.component_id = component_id
        self.available_list = available_list
        self.choices_list = choices_list
        self.list_title = list_title

        if aio_id is None:
            aio_id =  str(uuid.uuid4())

        title = html.Div(self.list_title)
        #print('self.ids.checklist(aio_id)', self.ids.checklist(aio_id))
        main_checklist = dcc.Checklist(
        id = aio_id
        #id = self.ids.checklist(aio_id)
        ,options=self.available_list
        ,value=self.choices_list
        ,inline = False
        ,persistence= True
        ,persistence_type='session'
        
        )

        # select_all_checkbox = dcc.Checklist(
        #     id=self.ids.select_all(aio_id),
        #     options=[{'label': 'Select All', 'value': 'all'}],
        #     value=[],
        #     inline=False,
        #     persistence= True,
        #     persistence_type='session'
        # )
        # Define the component's layout

        super().__init__([  # Equivalent to `html.Div([...])`
            html.Br(),
            title,
            #select_all_checkbox,
            html.Div(main_checklist, style = {"height":"100%",
                                              "overflow-y": "auto", 
                                                "scrollbar-width":"thin",
                                                "display":"flex",
                                              }),
            html.Br()
        ]   
        ,style = {'height': "25vh" ,
                  })
    # @callback(
    #     Output(ids.checklist(MATCH), 'value'),
    #     [Input(ids.select_all(MATCH), 'value'),
    #     State(ids.main_checklist_id(MATCH), "options")]
    # )
    # def select_all(selected_values, av_values):
    #     if 'all' in selected_values:
    #         # If "Select All" is checked, return all option values
    #         return [option['value'] for option in av_values
    #     else:
    #         # If "Select All" is unchecked, return an empty list
    #         return []



components / AIO_OffCanvas_BASIC


import dash_bootstrap_components as dbc
from components.AIO_List import AIO_List
from dash import Output, Input, State, html, callback, MATCH, html
import uuid
import dash_bootstrap_components as dbc
import dash_mantine_components as dmc
import json

def grid_1x2(i1j1, i1j2):
    """insert html objects to cells of i'th rows and j'th columns"""
    return html.Div(
        [
        
        dbc.Row(
            [
                dbc.Col(
        
                    html.Div(
                    html.Div(i1j1, className='box' ,style={"height": "100%"})
                    , className='box', style={'margin' : '10px', "height": "100%", 'padding':'10px'})

                    ,xs={'size': 12, 'offset': 0, 'order': 1}
                    ,sm={'size': 12, 'offset': 0, 'order': 1}
                    ,md={'size': 6, 'offset': 0, 'order': 1}
                    ,lg={'size': 6, 'offset': 0, 'order': 1}
                    ,xl={'size': 6, 'offset': 0, 'order': 1}
                ),
                dbc.Col(
                    html.Div(
                    html.Div(i1j2, className='box'  ,style={"height": "100%"})
                    , className='box', style={'margin' : '10px', "height": "100%", 'padding':'10px'})

                    ,xs={'size': 12, 'offset': 0, 'order': 2}
                    ,sm={'size': 12, 'offset': 0, 'order': 2}
                    ,md={'size': 6, 'offset': 0, 'order': 2}
                    ,lg={'size': 6, 'offset': 0, 'order': 2}
                    ,xl={'size': 6, 'offset': 0, 'order': 2}
                ),
                
            ],
            className='row justify-content-center'
            #,style={'margin' : '10px'}
        )
        ,html.Br()
    ],
    className='grid-container'
    ,style={ "background-color": "#FFF", 'padding-left':'2%'}
    )

# All-in-One Components should be suffixed with 'AIO'
class AIO_OffCanvas(html.Div):  # html.Div will be the "parent" component

    btn_style_openCanvas = {
        "width":"1.5%",
        "height":"1800px",
        "border-radius":"0",
        "position": "absolute",
        "text-align":"left",
        "padding":"0 0 0 0",
        "margin": "0 0 0 0"

    } 
   
    

    # A set of functions that create pattern-matching callbacks of the subcomponents
    class ids:
        div = lambda aio_id: {
            'component': 'AIO_OffCanvas',
            'subcomponent': 'div',
            'aio_id': aio_id
        }
        title = lambda aio_id: {
            'component': 'AIO_OffCanvas',
            'subcomponent': 'title',
            'aio_id': aio_id
        }
        
        button_applyFilters = lambda aio_id: {
            'component': 'AIO_OffCanvas',
            'subcomponent': 'button_applyFilters',
            'aio_id': aio_id
        }

        filtersDiv = lambda aio_id: {
            'component': 'AIO_OffCanvas',
            'subcomponent': 'filtersDiv',
            'aio_id': aio_id
        }

        offCanvas = lambda aio_id: {
            'component': 'AIO_OffCanvas',
            'subcomponent': 'offCanvas',
            'aio_id': aio_id
        }
        offCanvasOpenButton = lambda aio_id: {
            'component': 'AIO_OffCanvas',
            'subcomponent': 'offCanvasOpenButton',
            'aio_id': aio_id
        }

        yearsFilter = lambda aio_id: {
            'component': 'AIO_OffCanvas',
            'subcomponent': 'yearsFilter',
            'aio_id': aio_id
        }
        monthsFilter = lambda aio_id: {
            'component': 'AIO_OffCanvas',
            'subcomponent': 'monthsFilter',
            'aio_id': aio_id
        }
        daysFilter = lambda aio_id: {
            'component': 'AIO_OffCanvas',
            'subcomponent': 'daysFilter',
            'aio_id': aio_id
        }
        costCenterFilter = lambda aio_id: {
            'component': 'AIO_OffCanvas',
            'subcomponent': 'costCenterFilter',
            'aio_id': aio_id
        }
      
    # Make the ids class a public class
    ids = ids

    # Define the arguments of the All-in-One component
    def __init__(
        self,
        # component_id,
        userName, 
        pageName,
        #filters,
        aio_id=None,
    ):
        self.userName = userName

        if aio_id is None:
            aio_id = str(uuid.uuid4())

        self.navi_choices = ["Dashboard", "Details", 'Map']
        self.navi_value = pageName
        self.navi_choices = html.Div([
                    #html.Div("Choose the page", style = {'text-align':'center'}),
                    html.Div([
                        html.Br(),
                        
                        dmc.ChipGroup([dmc.Chip(html.A(x, href=f'/{x.lower()}/', target='_self') , value=x) for x in self.navi_choices],
                            value=self.navi_value, 
                            style = {'align-items':'center', 'justify-content': 'center', 'display': 'flex'},
                            ),
                html.Br()]
                , style = {'border-style':'outset', 'border-radius':'15px', 'border-width': '5px'}),
            html.Br()
            ])


        self.YearsFilter = AIO_List([2018,2019,2020,2021,2022,2023], [2023], list_title = 'Years', aio_id=self.ids.yearsFilter(aio_id))
        self.MonthsFilter = AIO_List([1,2,3,4,5,6,7,8,9,10,11,12], [8], list_title = 'Months', aio_id=self.ids.monthsFilter(aio_id))
        self.DaysFilter = AIO_List([1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31], [20], list_title = 'Days', aio_id=self.ids.daysFilter(aio_id))
        self.CostCenterFilter = AIO_List(['12312321', '13123123', '123123'], ['13123123'], list_title = 'CostCenters', aio_id=self.ids.costCenterFilter(aio_id))

        # Define the component's layout
        super().__init__([  # Equivalent to `html.Div([...])`
            html.Div(
        [

        dbc.Button(" ➠", id=self.ids.offCanvasOpenButton(aio_id), n_clicks=0,
                style = self.btn_style_openCanvas
                ),
        dbc.Offcanvas([
            html.P(id=self.ids.title(aio_id)),
            html.P(
                """This is Dash Eliot, the following report should give you knowledge about this this and that."""
            ),

            self.navi_choices,

            dbc.Accordion(
                [
                    dbc.AccordionItem(
                        children=[
                            "This is the content of the first section",
                            html.Div(dbc.Button("Apply changes", id = self.ids.button_applyFilters(aio_id)), n_clicks=0 , style = {'text-align': 'right'}), 
                            grid_1x2(self.YearsFilter,
                                     self.MonthsFilter
                                     ),
                            #self.DaysFilter,
                            grid_1x2(self.DaysFilter, self.CostCenterFilter),
                            html.Br(), 
                            html.Div(id=self.ids.filtersDiv(aio_id), children = ''),
                            
                            ]
                        , title="Filters"
                    ),

                    dbc.AccordionItem(
                        ["Content ",
                          'one two three',
                         ], title="User preferences"
                    ),
                    dbc.AccordionItem(
                        ["Content "
                         , 'Here should be: '
                         ], title="Additional settings"
                    ),
                    
                ],
                start_collapsed=True,
                always_open=True,

            ),
            
            html.Br(),
            
            html.Br(),
    
            ],
            id=self.ids.offCanvas(aio_id),
            title= 'Hello '+ self.userName,
            is_open=False,
            scrollable=True,
            backdrop = False
        ),
    ]
)
])
    
############################################### CALLBACKS

    @callback(
        Output(ids.offCanvas(MATCH), "is_open"),
        Input(ids.offCanvasOpenButton(MATCH), "n_clicks"),
        [State(ids.offCanvas(MATCH), "is_open")],
    )
    def toggle_offcanvas(n1, is_open):
        
        if n1:
            return not is_open
        return is_open

    @callback(
        Output( ids.filtersDiv(MATCH), 'children'),
        [Input(ids.yearsFilter(MATCH), 'value'),
         Input(ids.monthsFilter(MATCH), 'value'),
         Input(ids.daysFilter(MATCH), 'value'),
         Input(ids.costCenterFilter(MATCH), 'value'),
         Input(ids.button_applyFilters(MATCH), 'n_clicks')
         ]
    )
    def updateFilters(years, months, days, costCenter,n_clicks):
        response = {'Years': sorted(years),
                    'Months': sorted(months),
                    'Days':sorted(days),
                    'CostCenter':sorted(costCenter),

                    'n_clicks':n_clicks}

        return json.dumps(response)



Example usage main.py:

import dash
from dash import dcc, html
import dash_bootstrap_components as dbc
from dash.dependencies import Input, Output
from components.AIO_OffCanvas_BASIC import AIO_OffCanvas
from components.AIO_List_BASIC import AIO_List

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


app.layout = html.Div([
    AIO_OffCanvas('testUser', 'MainPage', 'MainPage123'),
    
    html.Div(id='output-selected-values', children=[
        html.H1('MainPage', style = {"text-align":"center"})
    ] )
])

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

Requirements:
dash==2.12.1
dash-bootstrap-components==1.4.1

This AIO_OffCanvas can be instantiated in dash page and will work.
Any ideas how I can still make callbacks with AIO_List in AIO_OffCanvas and have working callback inside AIO_List for select all?
The goal here is to make it all reusable and maintainable.

When trying to implement solutions I get bunch of errors about MATCH wildcard.

Much love, Kamil