All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.ikasan.dashboard.ui.general.component.LazyDownloadButton Maven / Gradle / Ivy

There is a newer version: 4.0.1
Show newest version
package org.ikasan.dashboard.ui.general.component;

import com.vaadin.flow.component.*;
import com.vaadin.flow.component.button.Button;
import com.vaadin.flow.component.html.Anchor;
import com.vaadin.flow.dom.DomEvent;
import com.vaadin.flow.dom.Element;
import com.vaadin.flow.server.InputStreamFactory;
import com.vaadin.flow.server.StreamResource;
import com.vaadin.flow.shared.Registration;
import org.ikasan.dashboard.ui.util.VaadinThreadFactory;

import java.io.InputStream;
import java.util.Objects;
import java.util.Optional;
import java.util.concurrent.Executors;
import java.util.function.Supplier;

/**
 * Extension of anchor, that will display a vaadin button as clickable instance to initiate a download. The download
 * content is generated at click time.
 */

public class LazyDownloadButton extends Button {

    private static final String DEFAULT_FILE_NAME = "download";
    private static final Supplier DEFAULT_FILE_NAME_SUPPLIER = () -> DEFAULT_FILE_NAME;
    private Supplier fileNameCallback;
    private InputStreamFactory inputStreamCallback;

    private Anchor anchor;

    public LazyDownloadButton() {
    }

    public LazyDownloadButton(String text) {
        super(text);
    }

    public LazyDownloadButton(Component icon) {
        super(icon);
    }

    public LazyDownloadButton(String text, InputStreamFactory inputStreamFactory) {
        this(text, DEFAULT_FILE_NAME_SUPPLIER, inputStreamFactory);
    }

    public LazyDownloadButton(Component icon, InputStreamFactory inputStreamFactory) {
        this(icon, DEFAULT_FILE_NAME_SUPPLIER, inputStreamFactory);
    }

    public LazyDownloadButton(String text, Component icon, InputStreamFactory inputStreamFactory) {
        this(text, icon, DEFAULT_FILE_NAME_SUPPLIER, inputStreamFactory);
    }

    public LazyDownloadButton(String text, Supplier fileNameCallback, InputStreamFactory inputStreamFactory) {
        this(text, null, fileNameCallback, inputStreamFactory);
    }

    public LazyDownloadButton(Component icon, Supplier fileNameCallback, InputStreamFactory inputStreamFactory) {
        this("", icon, fileNameCallback, inputStreamFactory);
    }

    /**
     * Creates a download button. The first two parameters are used for the button displayment.
     * 

* The third callback is called, when the download button is clicked. This is called inside the UI thread before * the input stream generation starts. It can be used for instance to deactivate the button. *

* The fourth parameter is a callback, that is used to generate the download file name *

* The fifth parameter is a callback to generate the input stream sent to the client. This callback will be * called in a separate thread (so that the UI thread is not blocked). *

* The sixth callback is called when the download anchor on the client side has been clicked (means the input * stream content is now sent to the user). This callback is called inside the UI thread. * BE AWARE THAT THE FILE IS STILL SENT TO THE CLIENT AT THIS POINT. Deleting or removing things can interrupt the * download. * * @param text button text * @param icon button icon * @param fileNameCallback callback for file name generation * @param inputStreamCallback callback for input stream generation */ public LazyDownloadButton(String text, Component icon, Supplier fileNameCallback, InputStreamFactory inputStreamCallback) { super(text); this.fileNameCallback = fileNameCallback; this.inputStreamCallback = inputStreamCallback; if (icon != null) { setIcon(icon); } super.addClickListener(event -> { // we add the anchor to download in the parent of the button - if there are scenarios where the anchor // should be placed somewhere else, this needs to be extended. Cannot be placed inside of the button // since the button might be disabled or invisible thus makes the anchor not usable. // The anchor must not be removed by this component, since the download failes otherwise getParent().ifPresent(component -> { Objects.requireNonNull(fileNameCallback, "File name callback must not be null"); Objects.requireNonNull(inputStreamCallback, "Input stream callback must not be null"); if (anchor == null) { anchor = new Anchor(); Element anchorElement = anchor.getElement(); anchorElement.setAttribute("download", true); anchorElement.getStyle().set("display", "none"); component.getElement().appendChild(anchor.getElement()); anchorElement.addEventListener("click", event1 -> fireEvent(new DownloadStartsEvent(this, true, event1))); } Optional optionalUI = getUI(); Executors.newSingleThreadExecutor(new VaadinThreadFactory("LazyDownloadButton")).execute(() -> { try { InputStream inputStream = inputStreamCallback.createInputStream(); optionalUI.ifPresent(ui -> { if (ui.isAttached()) { ui.access(() -> { StreamResource href = new StreamResource(fileNameCallback.get(), () -> inputStream); href.setCacheTime(0); anchor.setHref(href); anchor.getElement().callJsFunction("click"); }); } }); } catch (Exception e) { throw new RuntimeException(e); } }); }); }); } @Override protected void onDetach(DetachEvent detachEvent) { if (anchor != null) { getParent().map(Component::getElement).ifPresent(parentElement -> { Element anchorElement = anchor.getElement(); if (anchorElement != null && parentElement.getChildren().anyMatch(anchorElement::equals)) { parentElement.removeChild(anchorElement); } }); } } public Registration addDownloadStartsListener(ComponentEventListener listener) { return addListener(DownloadStartsEvent.class, listener); } public Supplier getFileNameCallback() { return fileNameCallback; } public void setFileNameCallback(Supplier fileNameCallback) { this.fileNameCallback = fileNameCallback; } public InputStreamFactory getInputStreamCallback() { return inputStreamCallback; } public void setInputStreamCallback(InputStreamFactory inputStreamCallback) { this.inputStreamCallback = inputStreamCallback; } public void reset() { this.anchor = null; } public static class DownloadStartsEvent extends ComponentEvent { private final DomEvent clientSideEvent; /** * Creates a new event using the given source and indicator whether the * event originated from the client side or the server side. * * @param source the source component * @param fromClient true if the event originated from the client */ public DownloadStartsEvent(LazyDownloadButton source, boolean fromClient, DomEvent clientSideEvent) { super(source, fromClient); this.clientSideEvent = clientSideEvent; } public DomEvent getClientSideEvent() { return clientSideEvent; } } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy