From f874529c2292197a1dc745ff1c9b08dd0f3d580a Mon Sep 17 00:00:00 2001 From: "Ahmad K. Bawaneh" Date: Mon, 15 Jul 2024 15:07:01 +0300 Subject: [PATCH] fix #942 FileUpload does not respect setMultiple(false) --- .../domino/ui/config/HasComponentConfig.java | 9 ++ .../domino/ui/config/UploadConfig.java | 11 +++ .../domino/ui/i18n/UploadLabels.java | 11 ++- .../upload/DefaultFilePreviewContainer.java | 14 +++- .../domino/ui/upload/FileUpload.java | 82 +++++++++++++++---- 5 files changed, 109 insertions(+), 18 deletions(-) diff --git a/domino-ui/src/main/java/org/dominokit/domino/ui/config/HasComponentConfig.java b/domino-ui/src/main/java/org/dominokit/domino/ui/config/HasComponentConfig.java index 14aa22626..6fca00de3 100644 --- a/domino-ui/src/main/java/org/dominokit/domino/ui/config/HasComponentConfig.java +++ b/domino-ui/src/main/java/org/dominokit/domino/ui/config/HasComponentConfig.java @@ -15,6 +15,8 @@ */ package org.dominokit.domino.ui.config; +import static java.util.Objects.nonNull; + import org.dominokit.domino.ui.utils.DominoUIConfig; /** HasComponentConfig interface. */ @@ -25,6 +27,13 @@ public interface HasComponentConfig { * @return a T object */ default T getConfig() { + if (nonNull(getOwnConfig())) { + return getOwnConfig(); + } return (T) DominoUIConfig.CONFIG.getUIConfig(); } + + default T getOwnConfig() { + return null; + } } diff --git a/domino-ui/src/main/java/org/dominokit/domino/ui/config/UploadConfig.java b/domino-ui/src/main/java/org/dominokit/domino/ui/config/UploadConfig.java index 493f4f385..e3b990f17 100644 --- a/domino-ui/src/main/java/org/dominokit/domino/ui/config/UploadConfig.java +++ b/domino-ui/src/main/java/org/dominokit/domino/ui/config/UploadConfig.java @@ -81,4 +81,15 @@ default FilePreviewFactory getFilePreviewFactory() { default Supplier> getDefaultFilePreviewContainer() { return DefaultFilePreviewContainer::new; } + + /** + * when the user uploaded a set of files that are less or equals the max uploads allowed, and they + * are already uploaded, should we allow him to upload a new set of files that are in total with + * the previous batch could overflow the max allowed upload limit. + * + * @return boolean default true + */ + default boolean isMaxUploadsOverflowAllowed() { + return true; + } } diff --git a/domino-ui/src/main/java/org/dominokit/domino/ui/i18n/UploadLabels.java b/domino-ui/src/main/java/org/dominokit/domino/ui/i18n/UploadLabels.java index 6e21d8c7a..3ec2b4f0a 100644 --- a/domino-ui/src/main/java/org/dominokit/domino/ui/i18n/UploadLabels.java +++ b/domino-ui/src/main/java/org/dominokit/domino/ui/i18n/UploadLabels.java @@ -55,7 +55,14 @@ default String getDefaultUploadCanceledMessage() { * @param current The current number of uploads. * @return The error message for exceeding the maximum allowed uploads. */ - default String getMaxFileErrorMessage(int maxFiles, int current) { - return "The maximum allowed uploads is : " + maxFiles + ", You have " + current; + default String getMaxFileErrorMessage(int maxFiles, int current, int added, int ignored) { + return "The maximum allowed uploads is : " + + maxFiles + + ", You have : " + + current + + ", added : " + + added + + ", ignored : " + + ignored; } } diff --git a/domino-ui/src/main/java/org/dominokit/domino/ui/upload/DefaultFilePreviewContainer.java b/domino-ui/src/main/java/org/dominokit/domino/ui/upload/DefaultFilePreviewContainer.java index faf549d54..c22e2568b 100644 --- a/domino-ui/src/main/java/org/dominokit/domino/ui/upload/DefaultFilePreviewContainer.java +++ b/domino-ui/src/main/java/org/dominokit/domino/ui/upload/DefaultFilePreviewContainer.java @@ -17,7 +17,9 @@ import static org.dominokit.domino.ui.utils.Domino.*; +import elemental2.dom.DomGlobal; import elemental2.dom.HTMLDivElement; +import org.dominokit.domino.ui.grid.Column; import org.dominokit.domino.ui.grid.Row; import org.dominokit.domino.ui.utils.BaseDominoElement; import org.dominokit.domino.ui.utils.ChildHandler; @@ -50,7 +52,17 @@ public DefaultFilePreviewContainer() { */ @Override public DefaultFilePreviewContainer appendChild(FileItem fileItem) { - rootRow.span2(fileItem); + rootRow.appendChild( + Column.span2() + .apply( + self -> { + self.appendChild(fileItem); + fileItem.onDetached( + mutationRecord -> { + DomGlobal.console.info("delete columns too ------------------"); + self.remove(); + }); + })); return this; } diff --git a/domino-ui/src/main/java/org/dominokit/domino/ui/upload/FileUpload.java b/domino-ui/src/main/java/org/dominokit/domino/ui/upload/FileUpload.java index 3522538df..3a11a6add 100644 --- a/domino-ui/src/main/java/org/dominokit/domino/ui/upload/FileUpload.java +++ b/domino-ui/src/main/java/org/dominokit/domino/ui/upload/FileUpload.java @@ -22,6 +22,7 @@ import elemental2.dom.*; import java.util.*; import java.util.function.Supplier; +import java.util.stream.Collectors; import jsinterop.base.Js; import org.dominokit.domino.ui.IsElement; import org.dominokit.domino.ui.config.HasComponentConfig; @@ -98,6 +99,7 @@ public class FileUpload extends BaseDominoElement private UploadRequestSender requestSender = (XMLHttpRequest::send); private DropEffect dropEffect; + private UploadConfig config; /** * Creates a new instance of the `FileUpload` component. @@ -195,7 +197,7 @@ public FileUpload( elementOf(filesContainer.element()).addCss(dui_file_preview_container); init(this); root.addClickListener(evt -> hiddenFileInput.element().click()); - hiddenFileInput.addEventListener("change", evt -> uploadFiles(hiddenFileInput.element().files)); + hiddenFileInput.addEventListener("change", evt -> tryUpload(hiddenFileInput.element().files)); root.addEventListener( "drop", evt -> { @@ -210,14 +212,7 @@ public FileUpload( ((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); - } + tryUpload(files); removeHover(); }); root.addEventListener( @@ -242,6 +237,37 @@ public FileUpload( }); } + private void tryUpload(FileList files) { + + int maxAllowed = isMultiUpload() ? maxAllowedUploads : 1; + int addedFiles = addedFileItems.size(); + List uploadedFiles = + addedFileItems.stream().filter(FileItem::isUploaded).collect(Collectors.toList()); + int remainingAllowed = Math.max(0, maxAllowed - (addedFiles - uploadedFiles.size())); + int existing = addedFiles - uploadedFiles.size(); + int ignored = files.length - remainingAllowed; + if (files.length > remainingAllowed) { + if (getConfig().isMaxUploadsOverflowAllowed()) { + uploadedFiles.forEach(FileItem::remove); + List toBeUploaded = files.asList().subList(0, remainingAllowed); + uploadFiles(toBeUploaded); + + messagesContainer + .clearElement() + .setTextContent( + getLabels() + .getMaxFileErrorMessage(maxAllowed, existing, remainingAllowed, ignored)); + } else { + messagesContainer + .clearElement() + .setTextContent( + getLabels().getMaxFileErrorMessage(maxAllowed, existing, 0, files.length)); + } + } else { + uploadFiles(files.asList()); + } + } + /** * Creates a new instance of the `FileUpload` component with a custom file preview factory, file * preview container, and decoration element. @@ -294,8 +320,9 @@ public FileUpload setDecoration(Element decoration) { * * @param maxAllowedUploads The maximum number of allowed uploads. */ - public void setMaxAllowedUploads(int maxAllowedUploads) { + public FileUpload setMaxAllowedUploads(int maxAllowedUploads) { this.maxAllowedUploads = maxAllowedUploads; + return this; } /** @@ -333,17 +360,29 @@ private void addHover() { * * @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); + public FileUpload uploadFiles(List files) { + for (int i = 0; i < files.size(); i++) { + File file = files.get(i); addFilePreview(file); } hiddenFileInput.element().value = ""; + return this; } /** Uploads all added files to the server. */ - public void uploadAllFiles() { + public FileUpload uploadAllFiles() { addedFileItems.forEach(fileItem -> fileItem.upload(requestSender)); + return this; + } + + @Override + public UploadConfig getOwnConfig() { + return config; + } + + public FileUpload setConfig(UploadConfig config) { + this.config = config; + return this; } /** @@ -353,7 +392,7 @@ public void uploadAllFiles() { */ private void addFilePreview(File file) { if (isMultiUpload()) { - removeFileItems(); + removeUploadedFiles(); } FileItem fileItem = FileItem.create(file, new UploadOptions(), filePreviewFactory, this); @@ -397,6 +436,12 @@ public HTMLDivElement element() { */ public FileUpload setMultiUpload(boolean multiUpload) { hiddenFileInput.element().multiple = multiUpload; + if (multiUpload) { + hiddenFileInput.setAttribute("multiple", true); + } else { + hiddenFileInput.removeAttribute("multiple"); + } + return this; } @@ -511,6 +556,13 @@ public FileUpload removeFileItems() { return this; } + private void removeUploadedFiles() { + List uploaded = + addedFileItems.stream().filter(FileItem::isUploaded).collect(Collectors.toList()); + addedFileItems.removeAll(uploaded); + uploaded.forEach(FileItem::remove); + } + /** * Gets a list of file item handlers that are executed when a file is added to the component. *