vaadin / flow-components

Java counterpart of Vaadin Web Components
100 stars 66 forks source link

Convenience APIs for adding icons/images to list items #2688

Open rolfsmeds opened 2 years ago

rolfsmeds commented 2 years ago

Describe your motivation

Rendering icons or images as part of items in lists is an extremely common use case:

Yet implementing these with Vaadin components like ComboBox, Select, MenuBar and ContextMenu requires a significant amount of boilerplate code for creating the layout containing the image and the label, and applying a classname or a bit of inline css to it. What could be a single line of code tends to be closer to at least five, and it is far from trivial to figure out how to do it.

Describe the solution you'd like

Simple, single-liner APIs for rendering images, icons, or why not any arbitrary component as a "prefix" to items in menus and lists.

Vaadin 8 had the ItemIconGenerator API, so that would seem like a pretty good option for ComboBox and Select (although I think ItemImageGenerator, or even ItemPrefixGenerator, would be preferable, as it's not always icons specifically).

The add-methods of MenuBar and ContextMenu could simply be overloaded with a second parameter for the prefix component:

menubar.addItem(VaadinIcon.ABACUS.create(), "Calculate stuff");

In terms of implementation, it would seem logical to add a slot for the prefix element in vaadin-item, which all of the above components use for their items, and ensuring that it, and the label next to it, are rendered nicely.

Additional context

This would also make it easier to migrate applications from Vaadin 7 and 8, in which corresponding APIs are used a lot.

yuriy-fix commented 2 years ago

Possibly related: https://github.com/vaadin/web-components/issues/459

sosa-vaadin commented 2 years ago

The example provided in the menu-bar docs is a good example of how to solve this using a factory method.

Regarding this:

The add-methods of MenuBar and ContextMenu could simply be overloaded with a second parameter for the prefix component: menubar.addItem(VaadinIcon.ABACUS.create(), "Calculate stuff");

I think this could be implemented with a default method in the HasMenuItems interface. This solution overloads the other methods in the interface and provides developers with a one-line solution to add items with an icon and a label or just an icon.

default MenuItem addItem(Icon icon, String text, ComponentEventListener<ClickEvent<MenuItem>> clickListener) {
    MenuItem item = addItem(icon, clickListener);
    if (text != null) {
        item.add(new Text(text));
    }
    return item;
}

This, however, doesn't provide a solution for ComboBox or Select since they don't implement the HasMenuItems interface. It should also be noted, that additional styling might be needed to have the items render nicely in sub-menus

mvysny commented 10 months ago

The solution above doesn't look quite good when items both with and without icon are mixed:

@Route("")
public class MainView extends VerticalLayout {

    private static MenuItem addItem(HasMenuItems self, Icon icon, String text) {
        MenuItem item = self.addItem(icon, event -> {});
        if (text != null) {
            item.add(new Text(text));
        }
        return item;
    }

    public MainView() {
        final MenuBar menuBar = new MenuBar();
        add(menuBar);
        final MenuItem fooItem = menuBar.addItem("Foo");
        addItem(fooItem.getSubMenu(), VaadinIcon.ABACUS.create(), "Abacus");
        fooItem.getSubMenu().addItem("Text", e -> {});
    }
}

Screenshot from 2023-11-15 10-12-33

The icon solution should look similar to checkable items solution:

image

jouni commented 10 months ago

The solution above doesn't look quite good when items both with and without icon are mixed

I think that case should be fixed with an empty icon instead of forcing the padding on all items.