Search code examples
vaadinvaadin14vaadin-upload

How to style vaadin-upload-file component inside Vaadin Upload


I would like to style Vaadin Upload component (vaadin-upload) by changing appearance of the elements in file list, e.g. hide commands buttons (start, remove, retry). The file list contains vaadin-upload-file elements.

For now I'm only able to customize vaadin-upload itself by adding custom theme to it and importing proper css - just like in this example: https://cookbook.vaadin.com/large-upload-area.

@CssImport(value = "./styles/custom-upload.css", themeFor = "vaadin-upload")
public class MainView extends VerticalLayout implements HasUrlParameter<String> {

    public MainView() {
        Upload upload = new Upload();
        upload.getElement().getThemeList().add("custom-upload");
        add(upload);
    }
}

custom-upload.css:

:host([theme~="custom-upload"]) {
  border: 0;
}

:host([theme~="custom-upload"]) [part="commands"] {
  display: none;
}

Simplified DOM:

<vaadin-upload theme="custom-upload" target="VAADIN/dynamic/resource/1/70860bf9-21c4-474e-9418-fd5516c28736/upload">
    #shadow-root
        <div part="primary-buttons">...</div>
        <slot name="file-list">
            <div id="fileList" part="file-list">
                <vaadin-upload-file>...</vaadin-upload-file>
            </div>
        </slot>
        ...
</vaadin-upload>

Documentation states that:

Is there a way to attach custom theme to vaadin-upload-file component?


Solution

  • Yes, you just need a separate style module targeting the vaadin-upload-file component, which is doable using the @CssImport annotation. For example,

    @CssImport(value = "./styles/custom-upload-file.css", themeFor = "vaadin-upload-file")
    

    Then the custom CSS can be added to {project_root}/frontend/styles/custom-upload-file.css.

    EDIT: unlike other Vaadin components, a theme name that is assigned to vaadin-upload doesn't (unfortunately) get propagated down to vaadin-upload-file. Thus, one cannot rely on the theme attribute in order to selectively style some vaadin-upload-file components within the same application.

    If selective styling is necessary, a hacky workaround is possible by making a JavaScript call that adds a classname to the vaadin-upload-file component. Such call would, however, only work after the vaadin-upload-file is rendered in the DOM (which typically occurs upon the success of a file upload). Hence, the JS call should be made from within an upload-success listener.

    Here's this workaround in action used to selectively hide the clear button of the vaadin-upload-file:

    @Route
    @CssImport(value = "./styles/vaadin-upload-styles.css", themeFor = "vaadin-upload-file")
    public class MainView extends VerticalLayout {
    
        public MainView() {
    
            MemoryBuffer buffer = new MemoryBuffer();
            Upload upload = new Upload(buffer);
            upload.addSucceededListener(e -> {
                upload.getElement()
                        .executeJs("this.shadowRoot.querySelector('vaadin-upload-file').className = 'hidden-clear'");
            });
    
            Button showClear = new Button("Show clear button", e -> upload.getElement()
                    .executeJs("this.shadowRoot.querySelector('vaadin-upload-file').className = ''"));
    
            add(upload, showClear);
        }
    
    }
    

    Then in vaadin-upload-styles.css, one can do something like:

    :host(.hidden-clear) [part~=clear-button] {
        display: none;
    }