facultyai / dash-bootstrap-components

Bootstrap components for Plotly Dash
https://dash-bootstrap-components.opensource.faculty.ai/
Apache License 2.0
1.12k stars 220 forks source link

dbc.Accordion "focused_item" Component Property #882

Closed SterlingButters closed 1 year ago

SterlingButters commented 2 years ago

Greetings,

I have created a nested accordion for navigating a sort of "Table of Contents":

image

Notice I have changed the styling for the Accordion Item that was last pressed - this is only through the css: .accordion-button:focus{...color:white;background-color:rgba(25, 0, 255,.7)}

Using the pattern matching callbacks feature I am able to return the "last pressed" AccordionItem:


d = {
    "1": {
        "1.1": {
            "1.1.1": {}
        },
        "1.2": {
            "1.2.1": {}
        }
    },
    "2": {
        "2.1": {
            "2.1.1": {}
        },
        "2.2": {
            "2.2.2": {}
        }
    }
}

def flatten(list_of_lists):
    if len(list_of_lists) == 0:
        return list_of_lists
    if isinstance(list_of_lists[0], list):
        return flatten(list_of_lists[0]) + flatten(list_of_lists[1:])
    return list_of_lists[:1] + flatten(list_of_lists[1:])

def CI_accordion(input_dictionary):
    output = dbc.Accordion([], start_collapsed=False, always_open=True, flush=True)
    for key, subdict in input_dictionary.items():
        output.children.append(dbc.AccordionItem(CI_accordion(subdict), title=key, item_id=key.split(" ")[0], id=key.split(" ")[0]))
        output.id = {'type': "individual-accordion", 'index': key}
    return output

...
app.layout = ...CI_accordion(d), dcc.Store(id='storage', storage_type='session', data=None)...
...

@app.callback(
    Output("accordion-contents", "children"),
    Output('storage', 'data'),
    Input({'type': "individual-accordion", 'index': ALL}, "active_item"),
    State('storage', 'data')
)
def change_item(active_items, storage_data):
    print(active_items)
    last_item = None
    if storage_data is None:
        last_item = [item for item in flatten(active_items) if item is not None]

    else:
        try:     
            for item in flatten(active_items):
                if item not in flatten(storage_data):
                    last_item = item
                    break

            for item in flatten(storage_data):
                if item not in flatten(active_items):
                    last_item = item
                    break

        except Exception as error:
            print(error)

    print(last_item)
    storage_data = active_items

    return last_item, active_items

The "active_item" property only depicts which menu items are not collapsed, not the most recently activated (focused) menu item. As my feature request, I would like to be able to expose the focused menu item that corresponds to the aforementioned css styling as a component property. Not sure what the best way to do this is or if there is a temporary "hack" I can do. Thanks in advance!

tcbegley commented 2 years ago

Hi @SterlingButters

I'm afraid I can't see a good way to expose the focused state of those items. In particular with a nested setup like you have, there wouldn't really be a way to get the components to communicate with each other...

What are you trying to do with the focused state? Seems to me that you are pretty close with having the id of the last clicked item?

SterlingButters commented 2 years ago

@tcbegley

can't see a good way to expose the focused state

Do you mean in general or for my particular nested setup? If the latter, I don’t need the focused state to “communicate” with the other accordion items (even if I did need to, could I not use the ‘ALL’ pattern-matching callback schema - something similar to what’s implemented for ‘active_item’ property).

Yes, I can get the last focused item no problem. I would like to access the “focused_item” property to “write” it, not “read” it. See, my table of contents nested accordion is quite long - so I would like to implement a searchable drop down that contains the contents. Based on the dropdown component ‘value’ property, I would like to set the focused item in the nested setup. I have already implemented the ability to collapse all items of non-interest using the drop down but I can eliminate the extra step of having to click the accordion item if the focused item property is exposed.

Thanks!