How to create multi-select dropdown inside a table cell? bonus: colors

Using Dash Python, How can i create something like this (like notion/airtable):
image

i guess i would have to use this control, right?

but,

  1. how can I put it inside a table cell? with the ability to edit it, just like in notion/airtable.
  2. different colors?

Hey @algtr welcome to the forums.

As always, I would suggest taking a look into ag-grid when using tabular data.

1 Like

This suggestion (copying the dmc.Select example and rework it to dmc.Multiselect) worked like a charm for me dmc.MultiSelect as Dash AG Grid custom component - #6 by Jocelyn

2 Likes

@Werner I tried to take the example with Select and replace it with Multiselect, without success. do you have an example code for that?

@algtr yes, for sure:
js function:

// cell editor custom component  - dmc.MultiSelect
dagfuncs.DMC_MultiSelect = class {
    // gets called once before the renderer is used
    init(params) {
        // create the cell
        this.params = params;

        // function for when Dash is trying to send props back to the component / server
        var setProps = (props) => {
            if (typeof props.value != typeof undefined) {
                // updates the value of the editor
                this.value = props.value;

                // re-enables keyboard event
                delete params.colDef.suppressKeyboardEvent;

                // tells the grid to stop editing the cell
                params.api.stopEditing();

                // sets focus back to the grid's previously active cell
                this.prevFocus.focus();
            }
        };
        this.eInput = document.createElement('div');

        // renders component into the editor element
        ReactDOM.render(
            React.createElement(window.dash_mantine_components.MultiSelect, {
                data: params.options,
                value: params.value,
                setProps,
                style: {width: params.column.actualWidth-2,  ...params.style},
                className: params.className,
                clearable: params.clearable,
                searchable: params.searchable || true,
                creatable: params.creatable,
                debounce: params.debounce,
                disabled: params.disabled,
                limit: params.limit,
                maxDropdownHeight: params.maxDropdownHeight,
                nothingFound: params.nothingFound,
                placeholder: params.placeholder,
                required: params.required,
                searchValue: params.searchValue,
                shadow: params.shadow,
                size: params.size,
                styles: params.styles,
                switchDirectionOnFlip: params.switchDirectionOnFlip,
                variant: params.variant,
            }),
            this.eInput
        );

        // allows focus event
        this.eInput.tabIndex = '0';

        // sets editor value to the value from the cell
        this.value = params.value;
    }

    // gets called once when grid ready to insert the element
    getGui() {
        return this.eInput;
    }

    focusChild() {
        // needed to delay and allow the component to render
        setTimeout(() => {
            var inp = this.eInput.getElementsByClassName(
                'mantine-MultiSelect-input'
            )[0];
            inp.tabIndex = '1';

            // disables keyboard event
            this.params.colDef.suppressKeyboardEvent = (params) => {
                const gridShouldDoNothing = params.editing;
                return gridShouldDoNothing;
            };
            // shows dropdown options
            inp.focus();
        }, 100);
    }

    // focus and select can be done after the gui is attached
    afterGuiAttached() {
        // stores the active cell
        this.prevFocus = document.activeElement;

        // adds event listener to trigger event to go into dash component
        this.eInput.addEventListener('focus', this.focusChild());

        // triggers focus event
        this.eInput.focus();
    }

    // returns the new value after editing
    getValue() {
        return this.value;
    }

    // any cleanup we need to be done here
    destroy() {
        // sets focus back to the grid's previously active cell
        this.prevFocus.focus();
    }
};

Field def:

{
      'field': 'tags',
      'cellEditor':{"function":"DMC_MultiSelect"},
      "editable":True,
      "cellEditorParams": {
          "options": tags_options,
          "placeholder": "Select Tags",
          "creatable": True
      },
      "cellEditorPopup": True
  }

tag_options I select from my SQL-database. You can use there any list with dict elements (“label”, “value”) as in the dmc.Select example

1 Like

OK thank you @Werner I managed to get it working, and it looks good, but two things are missing from a complete solution:

  1. The most important part: how to show the tags all the time, not only in edit mode? right now, when you exit the edit mode, the display goes back from tags to a simple list of texts with comma separating the items. This is very different than Notion/Airtable tables, which shows you the tags inside rectangles all the time, and not just when you edit the cell.
  2. How to color each tag with a unique color?

Here is the difference between regular and edit display:
image

Hello @algtr,

You need to also add a cellRenderer that will render each selection into its own component/card.

You can do this by splitting the value at the , and then building your children from there.

Has anyone managed to get the MultiSelect to stay opened once you click a value? I find it unpractical that it closes everytime you click a value, I would like to select multiple values at once and then close it once i click outside of the dropdown menu.

Agreed - find that to be extremely annoying. Hoping someone has a fix!

This is the default in V0.14.5

1 Like

Thanks @AnnMarieW - is there a way to do this with 0.12? My project is rather old and I unfortunately will have to refactor a lot of code if I was to upgrade to 0.14.5

Actually, I just checked and that is the default for dmc.MultiSelect in V0.12 as well - so it must be something about how the component works with the grid.