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

org.dominokit.domino.ui.upload.FileUpload Maven / Gradle / Ivy

/*
 * Copyright © 2019 Dominokit
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package org.dominokit.domino.ui.upload;

import static java.util.Objects.nonNull;
import static org.dominokit.domino.ui.utils.Domino.*;
import static org.dominokit.domino.ui.utils.DominoUIConfig.CONFIG;

import elemental2.dom.*;
import java.util.*;
import java.util.function.Supplier;
import jsinterop.base.Js;
import org.dominokit.domino.ui.IsElement;
import org.dominokit.domino.ui.config.HasComponentConfig;
import org.dominokit.domino.ui.config.UploadConfig;
import org.dominokit.domino.ui.elements.DivElement;
import org.dominokit.domino.ui.elements.InputElement;
import org.dominokit.domino.ui.i18n.HasLabels;
import org.dominokit.domino.ui.i18n.UploadLabels;
import org.dominokit.domino.ui.utils.BaseDominoElement;
import org.dominokit.domino.ui.utils.DominoElement;
import org.dominokit.domino.ui.utils.HasName;
import org.dominokit.domino.ui.utils.LazyChild;

/**
 * The `FileUpload` class provides a user interface for uploading files to a server. It allows users
 * to select and upload one or multiple files, with options for customization and configuration.
 *
 * 

Usage example: * *

 * // Create a FileUpload component
 * FileUpload fileUpload = FileUpload.create();
 *
 * // Set the maximum number of allowed uploads
 * fileUpload.setMaxAllowedUploads(5);
 *
 * // Set a custom request sender for handling file uploads
 * fileUpload.setRequestSender(myCustomRequestSender);
 *
 * // Enable auto-upload (files are uploaded automatically after selection)
 * fileUpload.autoUpload();
 *
 * // Set the accepted file types
 * fileUpload.accept("image/*,application/pdf");
 *
 * // Add an event handler for when a file is added
 * fileUpload.onAddFile(fileItem -> {
 *     // Handle the added fileItem
 * });
 *
 * // Set a custom decoration for the component
 * fileUpload.setDecoration(myCustomDecorationElement);
 * 
* * @see FileItem * @see FilePreviewFactory * @see FilePreviewContainer * @see UploadOptions * @see UploadRequestSender * @see DropEffect * @see BaseDominoElement */ public class FileUpload extends BaseDominoElement implements HasName, FileUploadStyles, HasComponentConfig, HasLabels { public static final Supplier> DEFAULT_SUCCESS_CODES = () -> Arrays.asList(200, 201, 202, 203, 204, 205, 206, 207, 208, 226); private final DivElement root; private final DivElement messagesContainer; private final InputElement hiddenFileInput; private final FilePreviewContainer filesContainer; private final List addedFileItems = new ArrayList<>(); private final FilePreviewFactory filePreviewFactory; private LazyChild> decoration; private final List fileItemHandlers = new ArrayList<>(); private boolean autoUpload = true; private int maxAllowedUploads = Integer.MAX_VALUE; private UploadRequestSender requestSender = (XMLHttpRequest::send); private DropEffect dropEffect; /** * Creates a new instance of the `FileUpload` component. * * @return A new `FileUpload` instance. */ public static FileUpload create() { return new FileUpload(); } /** * Creates a new instance of the `FileUpload` component with a custom file preview factory and * file preview container. * * @param filePreviewFactory The file preview factory to use. * @param filePreviewContainer The file preview container to use. * @return A new `FileUpload` instance with the specified factory and container. */ public static FileUpload create( FilePreviewFactory filePreviewFactory, FilePreviewContainer filePreviewContainer) { return new FileUpload(filePreviewFactory, filePreviewContainer); } /** * Creates a new instance of the `FileUpload` component with a default file preview factory and a * custom file preview container. * * @param filePreviewContainer The file preview container to use. * @return A new `FileUpload` instance with the default factory and the specified container. */ public static FileUpload create(FilePreviewContainer filePreviewContainer) { return new FileUpload(CONFIG.getUIConfig().getFilePreviewFactory(), filePreviewContainer); } /** * Creates a new instance of the `FileUpload` component with a custom file preview factory and the * default file preview container. * * @param filePreviewFactory The file preview factory to use. * @return A new `FileUpload` instance with the specified factory and the default container. */ public static FileUpload create(FilePreviewFactory filePreviewFactory) { return new FileUpload(filePreviewFactory); } /** * Creates a new instance of the `FileUpload` component with a custom file preview factory, file * preview container, and decoration element. * * @param filePreviewFactory The file preview factory to use. * @param filePreviewContainer The file preview container to use. * @param decoration The decoration element to use. * @return A new `FileUpload` instance with the specified factory, container, and decoration. */ public static FileUpload create( FilePreviewFactory filePreviewFactory, FilePreviewContainer filePreviewContainer, IsElement decoration) { return new FileUpload(filePreviewFactory, filePreviewContainer, decoration); } /** Creates a new instance of the `FileUpload` component with default configurations. */ public FileUpload() { this( CONFIG.getUIConfig().getFilePreviewFactory(), CONFIG.getUIConfig().getDefaultFilePreviewContainer().get()); } /** * Creates a new instance of the `FileUpload` component with a custom file preview factory and the * default file preview container. * * @param filePreviewFactory The file preview factory to use. */ public FileUpload(FilePreviewFactory filePreviewFactory) { this(filePreviewFactory, CONFIG.getUIConfig().getDefaultFilePreviewContainer().get()); } /** * Creates a new instance of the `FileUpload` component with a custom file preview factory and * file preview container. * * @param filePreviewFactory The file preview factory to use. * @param filePreviewContainer The file preview container to use. */ public FileUpload( FilePreviewFactory filePreviewFactory, FilePreviewContainer filePreviewContainer) { this.filePreviewFactory = filePreviewFactory; root = div() .addCss(dui_file_upload) .appendChild(messagesContainer = div().addCss(dui_file_upload_messages)) .appendChild(hiddenFileInput = input("file").addCss(dui_file_upload_input)) .appendChild(filesContainer = filePreviewContainer); elementOf(filesContainer.element()).addCss(dui_file_preview_container); init(this); root.addClickListener(evt -> hiddenFileInput.element().click()); hiddenFileInput.addEventListener("change", evt -> uploadFiles(hiddenFileInput.element().files)); root.addEventListener( "drop", evt -> { evt.stopPropagation(); evt.preventDefault(); FileList files = ((DragEvent) evt).dataTransfer.files; Optional.ofNullable(dropEffect) .ifPresent( effect -> { if (files.length > 0) { ((DragEvent) evt).dataTransfer.dropEffect = effect.getEffect(); } }); int maxAllowed = hiddenFileInput.element().multiple ? maxAllowedUploads : 1; if (maxAllowed > files.length) { messagesContainer .clearElement() .setTextContent(getLabels().getMaxFileErrorMessage(maxAllowed, files.length)); } else { uploadFiles(files); } removeHover(); }); root.addEventListener( "dragover", evt -> { evt.stopPropagation(); evt.preventDefault(); Optional.ofNullable(dropEffect) .ifPresent(effect -> ((DragEvent) evt).dataTransfer.dropEffect = effect.getEffect()); addHover(); }); root.addEventListener( "dragleave", evt -> { evt.stopPropagation(); evt.preventDefault(); Optional.ofNullable(dropEffect) .ifPresent(effect -> ((DragEvent) evt).dataTransfer.dropEffect = effect.getEffect()); if (Js.uncheckedCast(evt.target) == root.element()) { removeHover(); } }); } /** * Creates a new instance of the `FileUpload` component with a custom file preview factory, file * preview container, and decoration element. * * @param filePreviewFactory The file preview factory to use. * @param filePreviewContainer The file preview container to use. * @param decoration The decoration element to use. */ public FileUpload( FilePreviewFactory filePreviewFactory, FilePreviewContainer filePreviewContainer, IsElement decoration) { this(filePreviewFactory, filePreviewContainer); setDecoration(decoration.element()); } /** * Sets a custom decoration element for the `FileUpload` component using an {@link IsElement} * instance. The decoration element is typically used to enhance or style the appearance of the * component. * * @param decoration The decoration element provided as an {@link IsElement} instance. * @return The current `FileUpload` instance for method chaining. */ public FileUpload setDecoration(IsElement decoration) { return setDecoration(decoration.element()); } /** * Sets a custom decoration element for the `FileUpload` component using a native {@link Element}. * The decoration element is typically used to enhance or style the appearance of the component. * * @param decoration The decoration element provided as a native {@link Element}. * @return The current `FileUpload` instance for method chaining. */ public FileUpload setDecoration(Element decoration) { if (nonNull(this.decoration) && this.decoration.isInitialized()) { this.decoration.remove(); } if (nonNull(decoration)) { this.decoration = LazyChild.of(elementOf(decoration).addCss(dui_file_upload_decoration), root); this.decoration.get(); } return this; } /** * Sets the maximum number of allowed uploads. * * @param maxAllowedUploads The maximum number of allowed uploads. */ public void setMaxAllowedUploads(int maxAllowedUploads) { this.maxAllowedUploads = maxAllowedUploads; } /** * Gets the maximum number of allowed uploads. * * @return The maximum number of allowed uploads. */ public int getMaxAllowedUploads() { return maxAllowedUploads; } /** * Sets a custom request sender for handling file uploads. * * @param requestSender The custom request sender. * @return The current `FileUpload` instance for method chaining. */ public FileUpload setRequestSender(UploadRequestSender requestSender) { if (nonNull(requestSender)) { this.requestSender = requestSender; } return this; } /** * Adds a CSS class to the root element to indicate that a file is being dragged over the * component. */ private void addHover() { root.addCss("dui-hovered"); } /** * Uploads a list of files to the server. * * @param files The list of files to upload. */ public void uploadFiles(FileList files) { for (int i = 0; i < files.length; i++) { File file = files.item(i); addFilePreview(file); } hiddenFileInput.element().value = ""; } /** Uploads all added files to the server. */ public void uploadAllFiles() { addedFileItems.forEach(fileItem -> fileItem.upload(requestSender)); } /** * Adds a file preview for the given file and handles its actions. * * @param file The file to create a preview for. */ private void addFilePreview(File file) { if (isMultiUpload()) { removeFileItems(); } FileItem fileItem = FileItem.create(file, new UploadOptions(), filePreviewFactory, this); fileItemHandlers.forEach(handler -> handler.handle(fileItem)); fileItem.validateSize(); filesContainer.appendChild(fileItem); addedFileItems.add(fileItem); fileItem.addRemoveHandler( removedFile -> { addedFileItems.remove(fileItem); }); if (fileItem.isCanceled()) { fileItem.remove(); } if (autoUpload && !fileItem.isCanceled() && !fileItem.isRemoved()) { fileItem.upload(requestSender); } } /** Removes the CSS class indicating that a file is being dragged over the component. */ private void removeHover() { root.removeCss("dui-hovered"); } /** {@inheritDoc} */ @Override public HTMLDivElement element() { return root.element(); } /** * Sets whether the component allows multiple file uploads. * * @param multiUpload Set to `true` to allow multiple file uploads, `false` otherwise. * @return The current `FileUpload` instance for method chaining. */ public FileUpload setMultiUpload(boolean multiUpload) { hiddenFileInput.element().multiple = multiUpload; return this; } /** * Checks if the component allows multiple file uploads. * * @return `true` if multiple file uploads are allowed, `false` otherwise. */ public boolean isMultiUpload() { return hiddenFileInput.element().multiple; } /** * Sets the accepted file types for file selection. * * @param acceptedFiles A comma-separated list of accepted file types (e.g., * "image/*,application/pdf"). * @return The current `FileUpload` instance for method chaining. */ public FileUpload accept(String acceptedFiles) { hiddenFileInput.element().accept = acceptedFiles; return this; } /** * Sets the accepted file types for file selection from a collection of accepted file types. * * @param acceptedFiles A collection of accepted file types. * @return The current `FileUpload` instance for method chaining. */ public FileUpload accept(Collection acceptedFiles) { return accept(String.join(",", acceptedFiles)); } /** * Adds a handler for when a file is added to the component. * * @param fileItemHandler The file item handler to add. * @return The current `FileUpload` instance for method chaining. */ public FileUpload onAddFile(FileItemHandler fileItemHandler) { fileItemHandlers.add(fileItemHandler); return this; } /** * Enables auto-upload mode, where files are uploaded automatically after selection. * * @return The current `FileUpload` instance for method chaining. */ public FileUpload autoUpload() { this.autoUpload = true; return this; } /** * Enables manual-upload mode, where files are not automatically uploaded after selection. * * @return The current `FileUpload` instance for method chaining. */ public FileUpload manualUpload() { this.autoUpload = false; return this; } /** * Sets the upload mode for the component. * * @param autoUpload Set to `true` for auto-upload mode, `false` for manual-upload mode. * @return The current `FileUpload` instance for method chaining. */ public FileUpload setAutoUpload(boolean autoUpload) { this.autoUpload = autoUpload; return this; } /** * Gets the input element used for selecting files. * * @return The input element for file selection. */ public InputElement getInputElement() { return hiddenFileInput; } /** * Gets the container element that holds the added file items. * * @return The container element for added file items. */ public DominoElement getFilesContainer() { return (DominoElement) filesContainer; } /** * Gets the list of added file items. * * @return A list of added file items. */ public List getAddedFileItems() { return addedFileItems; } /** * Removes all added file items from the component. * * @return The current `FileUpload` instance for method chaining. */ public FileUpload removeFileItems() { addedFileItems.forEach(FileItem::remove); addedFileItems.clear(); return this; } /** * Gets a list of file item handlers that are executed when a file is added to the component. * * @return A list of file item handlers. */ public List getOnAddFileHandlers() { return fileItemHandlers; } /** * Checks if the component is in auto-upload mode. * * @return `true` if the component is in auto-upload mode, `false` otherwise. */ public boolean isAutoUpload() { return autoUpload; } /** * Retrieves the name attribute of the hidden file input element associated with this `FileUpload` * component. The name attribute is used when submitting the uploaded file(s) as part of a form. * * @return The name attribute of the hidden file input element. */ @Override public String getName() { return hiddenFileInput.element().name; } /** * Sets the name attribute for the hidden file input element associated with this `FileUpload` * component. The name attribute is used when submitting the uploaded file(s) as part of a form. * * @param name The name to be set as the name attribute. * @return The current `FileUpload` instance for method chaining. */ @Override public FileUpload setName(String name) { hiddenFileInput.element().name = name; return this; } /** * Gets the current drop effect for file drag-and-drop operations. * * @return An optional drop effect, or `null` if not set. */ public Optional getDropEffect() { return Optional.ofNullable(dropEffect); } /** * Sets the drop effect for file drag-and-drop operations. * * @param dropEffect The drop effect to set. * @return The current `FileUpload` instance for method chaining. */ public FileUpload setDropEffect(DropEffect dropEffect) { if (nonNull(dropEffect)) { this.dropEffect = dropEffect; } return this; } /** * A functional interface for handling file items when they are added to the `FileUpload` * component. */ @FunctionalInterface public interface FileItemHandler { /** * Handles a file item. * * @param fileItem The file item to handle. */ void handle(FileItem fileItem); } }




© 2015 - 2025 Weber Informatics LLC | Privacy Policy