dmc.Select and dmc.MultiSelect blended into the title using Styles API and PIL

Hello everyone,

this is the answer for @AIMPED from another thread but I decided to show it solely with appropriate context :slight_smile:.

Lately I wanted to blend few Selects (Mantine equivalent of dropdown) into my Title. It is much cleaner UI than having it separatedly and i.e. updating title via callback (for example if your chart shows distribution of [views/sales] by [some attribute] you can get very nice section Title which encourages user to change the parameters. Here it is:

I used the fantastic feature of dmc library called Styles API which allows you to target the right selector of the component and change its attribute. In this case I am targetting Value selector and changing font size, font weight, color, background color and spacing around the text.

There was one more problem I needed to solve and it was the width of the component. dmc.Select and dmc.MultiSelect uses some fixed width and do not respond on the content very well. I was not able to find any elegant CSS solution for that but I found the library PIL which is able to return pixel width of the corresponding string using corresponding font. So I always find the maximal width of the value from all the options and set it as my width. It is not perfect because when you have one very long option and one very short option it leaves a lot of blank space when you select the short option but at least the very long option is never cut out when it is selected.

And here is the code:

import dash_mantine_components as dmc
import dash
from PIL import ImageFont

app = dash.Dash(__name__, suppress_callback_exceptions=True)
server = app.server

options = [
    ["Option 1", "OPTION1"],
    ["Option 2", "OPTION2"],
    ["Op 3", "OPTION3"],
    ["Option 236", "OPTION4"],
    ["Option 6", "OPTION6"],
]


def size_on_screen(lst, font_name, font_size):
    font = ImageFont.truetype(font_name, font_size)
    lens = [font.getlength(x) for x in lst]
    px_len = max(lens)
    return px_len


layout = dmc.Grid(
    [
        dmc.Col(
            [dmc.Title("My name is", order=4)],
            span="content",
            style={"padding": "0vh", "padding-right": "1vh"},
        ),
        dmc.Col(
            dmc.Select(
                value="OPTION1",
                data=[
                    {
                        "label": x[0],
                        "value": x[1],
                    }
                    for x in options
                ],
                variant="filled",
                radius="lg",
                size="sm",
                transition="scale",
                transitionDuration=150,
                styles={
                    "input": {
                        "font-size": "16px",
                        "font-weight": "700",
                        "width": size_on_screen(
                            [x[0] for x in options],
                            "segoeuib.ttf",
                            16,
                        )
                        + 37,
                        "padding-right": "18px",
                        "height": "30px",
                        "min-height": "30px",
                    }
                },
            ),
            span="content",
            style={"padding": "0.5vh"},
        ),
        dmc.Col(
            [dmc.Title("and I love DMC. And here", order=4)],
            span="content",
            style={"padding": "0vh", "padding-right": "1vh", "padding-left": "1vh"},
        ),
        dmc.Col(
            dmc.MultiSelect(
                value=["OPTION1"],
                data=[
                    {
                        "label": x[0],
                        "value": x[1],
                    }
                    for x in options
                ],
                variant="filled",
                size="sm",
                radius="lg",
                persistence=True,
                persistence_type="session",
                transition="scale",
                transitionDuration=150,
                styles={
                    "input": {
                        "padding-right": "20px",
                        "height": "30px",
                        "min-height": "30px",
                        "min-width": size_on_screen(
                            [x[0] for x in options],
                            "segoeuib.ttf",
                            16,
                        )
                        + 69,
                    },
                    "value": {
                        "font-size": "16px",
                        "font-weight": "700",
                        "color": "black",
                        "height": "20px",
                        "margin-top": "4px",
                        "margin-left": "0px",
                        "margin-bottom": "10px",
                        "background-color": "#fcfcfd",
                    },
                    "searchInput": {"min-width": "0px"},
                },
            ),
            span="content",
            style={"padding": "0.5vh"},
        ),
        dmc.Col(
            [dmc.Title("you gonna select them all.", order=4)],
            span="content",
            style={"padding": "0vh", "padding-left": "1vh"},
        ),
    ],
    align="center",
    style={"margin": "2vh"},
)


app.layout = layout
app.run_server(debug=True, port="8048")
3 Likes