Is There a Way to Place Dropdown Options Inside the Option Selection Menu?

Particularly for my specific situation, using the dcc.Dropdown works perfectly. The only problem is that in many situations, the user might select multiple options, which will look really ugly visually.

Is there a way to make the dropdown like this image below:

image

I have already thought about creating a menu that opens with options that can be selected as a checklist, but that would be more complex to code because I want to allow the user to search for the option they want just like in the standard dcc.Dropdown

HI @ArchieMarques welcome to the forum. This reminded me of an older answer in a different topic, maybe it helps:

3 Likes

Thank you, that helped me a lot to resolve my problem.

This topic helped me to create a solution that almost resolved my problem.

However, I still don’t have the complete solution to my problem.

Example code:

import dash_bootstrap_components as dbc
from dash import Dash, html, dcc, Input, Output, callback
import pathlib

options = [f"option_{idx}" for idx in range(6)]

dropdown_items_list = []

for idx, items in enumerate(options):
    dropdown_item = dcc.Checklist(
        id=f"check-{idx}",
        options=[items],
    )
    dropdown_items_list.append(dropdown_item)

app = Dash(
    external_stylesheets=[
        [pathlib.Path("assets", "style")],
        dbc.themes.BOOTSTRAP,
    ]
)

app.layout = html.Div(
    [
        dbc.DropdownMenu(
            id="dropdown-menu",
            children=dropdown_items_list,
            toggle_style={"width": "17svh"},
        ),
    ],
    className="selector_containerFinal",
    style={"justify-content": "center"},
)

def generate_input_ids(prefix, values):
    return [Input(f"{prefix}-{value}", "value") for value in values]

@callback(
    Output("dropdown-menu", "label"),
    generate_input_ids("check", range(len(options))),
    prevent_initial_call=True,
)
def show(*selected_values):
    selected = [
        val
        for sublist in selected_values
        if sublist is not None
        for val in sublist
    ]
    if len(selected) == 1:
        return selected[0]
    if len(selected) > 1 and len(selected) < len(options):
        return "Multiple options"
    if len(selected) == len(options):
        return "All"
    return "All"

if __name__ == "__main__":
    app.run(debug=True)

assets/style.css:

label:hover {
  background-color: #c9e75d;
}

input {
  margin: 0 0.5rem;
}

This dropdown is almost perfect, but due to the complexity of the search, I still need an input that searches and filters the options similar to Google Sheets filter or native dcc.Dropdown.

Example:
cost element

To be honest, that topic helped me more than I imagined because by showing examples and what I actually wanted, I ended up thinking of the solution. I just needed to make a small adaptation.

Solution:

import dash_bootstrap_components as dbc
from dash import Dash, html, dcc, Input, Output, callback
import pathlib
import pandas as pd
import numpy as np

options = [f"option_{idx}" for idx in range(6)]
options.extend([f"{idx}_option" for idx in range(6)])

dropdown_items_list = [dcc.Input(id="input_search", type="text", value="")]

for idx, items in enumerate(options):
    dropdown_item = dcc.Checklist(
        id=f"check-{idx}",
        options=[items],
    )
    dropdown_items_list.append(dropdown_item)

app = Dash(
    external_stylesheets=[
        [pathlib.Path("assets", "style")],
        dbc.themes.BOOTSTRAP,
    ]
)

app.layout = html.Div(
    [
        dbc.DropdownMenu(
            id="dropdown-menu",
            children=dropdown_items_list,
            toggle_style={"width": "17svh"},
        ),
    ],
    className="selector_containerFinal",
    style={"justify-content": "center"},
)


def generate_input_ids(prefix, values):
    return [Input(f"{prefix}-{value}", "value") for value in values]

def generate_output_ids(prefix, values):
    return [Output(f"{prefix}-{value}", "className") for value in values]

@callback(
    Output("dropdown-menu", "label"),
    generate_input_ids("check", range(len(options))),
)
def show_options(*selected_values):
    selected = [
        val
        for sublist in selected_values
        if sublist is not None
        for val in sublist
    ]
    if len(selected) == 1:
        return selected[0]
    if len(selected) > 1 and len(selected) < len(options):
        return "Multiple options"
    if len(selected) == len(options):
        return "All"
    return "All"

@callback(
    generate_output_ids("check", range(len(options))),
    Input("input_search", "value"),
)
def filter_options(searched_text):
    return tuple(
        np.where(
            pd.Series(options).str.contains(searched_text,case='False'),
            "", "hidden"
        )
    )

if __name__ == "__main__":
    app.run(debug=True)

assets/style.css

label:hover {
  background-color: #c9e75d;
}

input {
  margin: 0 0.5rem;
}

.hidden {
  display: none;
}

Result:

filter_options

1 Like

Thanks for sharing the solution! Happy you found some inspiration in the old thread!