You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@myfaces.apache.org by hn...@apache.org on 2019/09/09 15:31:31 UTC

[myfaces-tobago] branch master updated: TOBAGO-1921 Bootstrap file upload; TOBAGO-1633 TS refactoring

This is an automated email from the ASF dual-hosted git repository.

hnoeth pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/myfaces-tobago.git


The following commit(s) were added to refs/heads/master by this push:
     new 2e472db  TOBAGO-1921 Bootstrap file upload; TOBAGO-1633 TS refactoring
2e472db is described below

commit 2e472db97372b71c96e8a4096ad2b38a277381fe
Author: Henning Noeth <hn...@apache.org>
AuthorDate: Mon Sep 9 17:31:12 2019 +0200

    TOBAGO-1921 Bootstrap file upload; TOBAGO-1633 TS refactoring
    
    * implement tobago-file as custom elements
    * render tobago-file with custom-file-* CSS class from bootstrap
---
 .../tobago/internal/component/AbstractUIFile.java  |  2 +-
 .../internal/renderkit/renderer/FileRenderer.java  | 65 +++++++------------
 .../tobago/renderkit/css/BootstrapClass.java       |  3 +
 .../myfaces/tobago/renderkit/css/TobagoClass.java  |  3 +-
 .../tobago/renderkit/html/HtmlElements.java        |  1 +
 tobago-core/src/main/resources/scss/_tobago.scss   | 32 ++++------
 .../src/main/npm/ts/tobago-file.ts                 | 74 +++++++++++++---------
 7 files changed, 85 insertions(+), 95 deletions(-)

diff --git a/tobago-core/src/main/java/org/apache/myfaces/tobago/internal/component/AbstractUIFile.java b/tobago-core/src/main/java/org/apache/myfaces/tobago/internal/component/AbstractUIFile.java
index 3a4e670..16b5ba2 100644
--- a/tobago-core/src/main/java/org/apache/myfaces/tobago/internal/component/AbstractUIFile.java
+++ b/tobago-core/src/main/java/org/apache/myfaces/tobago/internal/component/AbstractUIFile.java
@@ -73,7 +73,7 @@ public abstract class AbstractUIFile extends UIInput implements SupportsLabelLay
 
   @Override
   public String getFieldId(final FacesContext facesContext) {
-    return getClientId(facesContext) + ComponentUtils.SUB_SEPARATOR + "real";
+    return getClientId(facesContext) + ComponentUtils.SUB_SEPARATOR + "field";
   }
 
   public boolean isLabelLayoutSkip() {
diff --git a/tobago-core/src/main/java/org/apache/myfaces/tobago/internal/renderkit/renderer/FileRenderer.java b/tobago-core/src/main/java/org/apache/myfaces/tobago/internal/renderkit/renderer/FileRenderer.java
index 4a58f30..e29695e 100644
--- a/tobago-core/src/main/java/org/apache/myfaces/tobago/internal/renderkit/renderer/FileRenderer.java
+++ b/tobago-core/src/main/java/org/apache/myfaces/tobago/internal/renderkit/renderer/FileRenderer.java
@@ -27,11 +27,9 @@ import org.apache.myfaces.tobago.internal.util.JsonUtils;
 import org.apache.myfaces.tobago.internal.util.PartUtils;
 import org.apache.myfaces.tobago.internal.util.RenderUtils;
 import org.apache.myfaces.tobago.renderkit.css.BootstrapClass;
-import org.apache.myfaces.tobago.renderkit.css.Icons;
 import org.apache.myfaces.tobago.renderkit.css.TobagoClass;
 import org.apache.myfaces.tobago.renderkit.html.DataAttributes;
 import org.apache.myfaces.tobago.renderkit.html.HtmlAttributes;
-import org.apache.myfaces.tobago.renderkit.html.HtmlButtonTypes;
 import org.apache.myfaces.tobago.renderkit.html.HtmlElements;
 import org.apache.myfaces.tobago.renderkit.html.HtmlInputTypes;
 import org.apache.myfaces.tobago.util.ComponentUtils;
@@ -121,6 +119,7 @@ public class FileRenderer extends MessageLayoutRendererBase implements Component
     final String clientId = file.getClientId(facesContext);
     final String fieldId = file.getFieldId(facesContext);
     final String accept = createAcceptFromValidators(file);
+    final String placeholder = file.getPlaceholder();
     final boolean multiple = file.isMultiple() && !file.isRequired();
     final boolean disabled = file.isDisabled();
     final boolean readonly = file.isReadonly();
@@ -130,74 +129,54 @@ public class FileRenderer extends MessageLayoutRendererBase implements Component
 
     final TobagoResponseWriter writer = getResponseWriter(facesContext);
 
-    writer.startElement(HtmlElements.DIV);
+    writer.startElement(HtmlElements.TOBAGO_FILE);
     if (file.isLabelLayoutSkip()) {
       writer.writeIdAttribute(clientId);
     }
     writer.writeClassAttribute(
-        TobagoClass.FILE,
+        BootstrapClass.CUSTOM_FILE,
         TobagoClass.FILE.createMarkup(file.getMarkup()),
         file.getCustomClass());
     HtmlRendererUtils.writeDataAttributes(facesContext, writer, file);
 
-    // visible fake input for a pretty look
-    writer.startElement(HtmlElements.DIV);
-    writer.writeClassAttribute(BootstrapClass.INPUT_GROUP);
-    writer.startElement(HtmlElements.INPUT);
-    writer.writeAttribute(HtmlAttributes.TYPE, HtmlInputTypes.TEXT);
-    writer.writeAttribute(HtmlAttributes.ACCEPT, accept, true);
-    writer.writeAttribute(HtmlAttributes.TABINDEX, -1);
-    writer.writeAttribute(HtmlAttributes.DISABLED, disabled || readonly);
-    writer.writeAttribute(HtmlAttributes.READONLY, readonly);
-    if (!disabled && !readonly) {
-      writer.writeAttribute(HtmlAttributes.PLACEHOLDER, file.getPlaceholder(), true);
-    }
-
-    writer.writeClassAttribute(
-        TobagoClass.FILE__PRETTY,
-        BootstrapClass.FORM_CONTROL,
-        BootstrapClass.borderColor(ComponentUtils.getMaximumSeverity(file)));
-    // TODO Focus
-    //HtmlRendererUtils.renderFocus(clientId, file.isFocus(), ComponentUtils.isError(file), facesContext, writer);
-    writer.endElement(HtmlElements.INPUT);
-
-    // invisible file input
     writer.startElement(HtmlElements.INPUT);
     writer.writeAttribute(HtmlAttributes.MULTIPLE, multiple);
     writer.writeAttribute(HtmlAttributes.TYPE, HtmlInputTypes.FILE);
     writer.writeAttribute(HtmlAttributes.ACCEPT, accept, true);
     writer.writeAttribute(HtmlAttributes.TABINDEX, -1);
     writer.writeIdAttribute(fieldId);
-    writer.writeClassAttribute(TobagoClass.FILE__REAL);
+    writer.writeClassAttribute(
+        BootstrapClass.CUSTOM_FILE_INPUT,
+        BootstrapClass.borderColor(ComponentUtils.getMaximumSeverity(file)));
     writer.writeNameAttribute(clientId);
     final String multiFormat = ResourceUtils.getString(facesContext, "file.selected");
     writer.writeAttribute(DataAttributes.dynamic("tobago-file-multi-format"), multiFormat, true);
     // readonly seems not making sense in browsers.
     writer.writeAttribute(HtmlAttributes.DISABLED, disabled || readonly);
     writer.writeAttribute(HtmlAttributes.READONLY, readonly);
+    if (!disabled && !readonly) {
+      writer.writeAttribute(HtmlAttributes.PLACEHOLDER, placeholder, true);
+    }
     writer.writeAttribute(HtmlAttributes.REQUIRED, file.isRequired());
+    // TODO Focus
+    //HtmlRendererUtils.renderFocus(clientId, file.isFocus(), ComponentUtils.isError(file), facesContext, writer);
     final String title = HtmlRendererUtils.getTitleFromTipAndMessages(facesContext, file);
     if (title != null) {
       writer.writeAttribute(HtmlAttributes.TITLE, title, true);
     }
-
     writer.writeCommandMapAttribute(JsonUtils.encode(RenderUtils.getBehaviorCommands(facesContext, file)));
-
     writer.endElement(HtmlElements.INPUT);
 
-    writer.startElement(HtmlElements.SPAN);
-    writer.writeClassAttribute(BootstrapClass.INPUT_GROUP_APPEND);
-    writer.startElement(HtmlElements.BUTTON);
-    writer.writeAttribute(HtmlAttributes.TABINDEX, file.getTabIndex());
-    writer.writeClassAttribute(BootstrapClass.BTN, BootstrapClass.BTN_SECONDARY);
-    writer.writeAttribute(HtmlAttributes.TYPE, HtmlButtonTypes.BUTTON);
-    writer.writeAttribute(HtmlAttributes.DISABLED, disabled || readonly);
-    writer.startElement(HtmlElements.I);
-    writer.writeClassAttribute(Icons.FA, Icons.FOLDER_OPEN);
-    writer.endElement(HtmlElements.I);
-    writer.endElement(HtmlElements.BUTTON);
-    writer.endElement(HtmlElements.SPAN);
-    writer.endElement(HtmlElements.DIV);
+    writer.startElement(HtmlElements.LABEL);
+    writer.writeClassAttribute(
+        BootstrapClass.CUSTOM_FILE_LABEL,
+        placeholder != null ? TobagoClass.FILE__PLACEHOLDER : null
+    );
+    writer.writeAttribute(HtmlAttributes.FOR, fieldId, false);
+    if (placeholder != null && !disabled && !readonly) {
+      writer.writeText(placeholder);
+    }
+    writer.endElement(HtmlElements.LABEL);
   }
 
   private String createAcceptFromValidators(final AbstractUIFile file) {
@@ -221,7 +200,7 @@ public class FileRenderer extends MessageLayoutRendererBase implements Component
   @Override
   protected void encodeEndField(final FacesContext facesContext, final UIComponent component) throws IOException {
     final TobagoResponseWriter writer = getResponseWriter(facesContext);
-    writer.endElement(HtmlElements.DIV);
+    writer.endElement(HtmlElements.TOBAGO_FILE);
   }
 
   @Override
diff --git a/tobago-core/src/main/java/org/apache/myfaces/tobago/renderkit/css/BootstrapClass.java b/tobago-core/src/main/java/org/apache/myfaces/tobago/renderkit/css/BootstrapClass.java
index b62151e..3d3b21c 100644
--- a/tobago-core/src/main/java/org/apache/myfaces/tobago/renderkit/css/BootstrapClass.java
+++ b/tobago-core/src/main/java/org/apache/myfaces/tobago/renderkit/css/BootstrapClass.java
@@ -240,6 +240,9 @@ public enum BootstrapClass implements CssItem {
   COL_XS_12("col-12"),
   CONTAINER("container"),
   CONTAINER_FLUID("container-fluid"),
+  CUSTOM_FILE("custom-file"),
+  CUSTOM_FILE_INPUT("custom-file-input"),
+  CUSTOM_FILE_LABEL("custom-file-label"),
   CUSTOM_SELECT("custom-select"),
   D_FLEX("d-flex"),
   D_INLINE("d-inline"),
diff --git a/tobago-core/src/main/java/org/apache/myfaces/tobago/renderkit/css/TobagoClass.java b/tobago-core/src/main/java/org/apache/myfaces/tobago/renderkit/css/TobagoClass.java
index 324cdbd..e8ac261 100644
--- a/tobago-core/src/main/java/org/apache/myfaces/tobago/renderkit/css/TobagoClass.java
+++ b/tobago-core/src/main/java/org/apache/myfaces/tobago/renderkit/css/TobagoClass.java
@@ -122,8 +122,7 @@ public enum TobagoClass implements CssItem {
   COLLAPSED("tobago-collapsed"),
   DATE("tobago-date"),
   FILE("tobago-file"),
-  FILE__PRETTY("tobago-file-pretty"),
-  FILE__REAL("tobago-file-real"),
+  FILE__PLACEHOLDER("tobago-file-placeholder"),
   FLEX_LAYOUT("tobago-flexLayout"),
   FLOW_LAYOUT("tobago-flowLayout"),
   FIGURE("tobago-figure"),
diff --git a/tobago-core/src/main/java/org/apache/myfaces/tobago/renderkit/html/HtmlElements.java b/tobago-core/src/main/java/org/apache/myfaces/tobago/renderkit/html/HtmlElements.java
index b84f6e1..b7efb4d 100644
--- a/tobago-core/src/main/java/org/apache/myfaces/tobago/renderkit/html/HtmlElements.java
+++ b/tobago-core/src/main/java/org/apache/myfaces/tobago/renderkit/html/HtmlElements.java
@@ -133,6 +133,7 @@ public enum HtmlElements {
   VIDEO("video"),
   WBR("wbr", Qualifier.VOID),
 
+  TOBAGO_FILE("tobago-file"),
   TOBAGO_IN("tobago-in"),
   TOBAGO_PANEL("tobago-panel"),
   TOBAGO_POPUP("tobago-popup"),
diff --git a/tobago-core/src/main/resources/scss/_tobago.scss b/tobago-core/src/main/resources/scss/_tobago.scss
index e4c2b75..906256d 100644
--- a/tobago-core/src/main/resources/scss/_tobago.scss
+++ b/tobago-core/src/main/resources/scss/_tobago.scss
@@ -72,6 +72,7 @@ $input-btn-focus-color: rgba(theme-color("primary"), .25) !default;
 $input-btn-focus-box-shadow: 0 0 0 $input-btn-focus-width $input-btn-focus-color !default;
 $input-focus-box-shadow: $input-btn-focus-box-shadow !default;
 $form-check-inline-input-margin-x: 0.75rem;
+$input-placeholder-color: $gray-600 !default;
 
 /* used icons ---------------------------------------------------- */
 
@@ -236,23 +237,18 @@ XXX workaround for Bootstrap with datetimepicker needed for popups
 }
 
 /* file -------------------------------------------------------------- */
+.tobago-file {} //TODO remove
+tobago-file {
+  .custom-file-label {
+    &:after {
+      font-family: FontAwesome;
+      content: "\f07c";
+    }
 
-.tobago-file {
-  position: relative;
-}
-
-.tobago-file-pretty {
-  text-overflow: ellipsis;
-}
-
-.tobago-file-real {
-  position: absolute;
-  right: 0;
-  top: 0;
-  bottom: 0;
-  opacity: 0;
-  width: 100%;
-  z-index: 2;
+    &.tobago-file-placeholder {
+      color: $input-placeholder-color;
+    }
+  }
 }
 
 /* flexLayout -------------------------------------------------------------- */
@@ -281,7 +277,7 @@ XXX workaround for Bootstrap with datetimepicker needed for popups
 .tobago-flexLayout.tobago-label-container > {
   .form-control, .form-control-plaintext, .twitter-typeahead,
   .tobago-input-group-outer, .tobago-messages-container,
-  .tobago-file, .tobago-selectManyShuttle {
+  .tobago-selectManyShuttle {
     flex: 1 0 0px;
   }
 }
@@ -502,7 +498,7 @@ a.tobago-messages-button, a.tobago-help-button {
 }
 
 .tobago-flexLayout.tobago-messages-container > {
-  .twitter-typeahead, .tobago-input-group-outer, .tobago-selectManyShuttle, .tobago-file {
+  .twitter-typeahead, .tobago-input-group-outer, .tobago-selectManyShuttle {
     flex: 1 0 0px;
   }
 }
diff --git a/tobago-theme/tobago-theme-standard/src/main/npm/ts/tobago-file.ts b/tobago-theme/tobago-theme-standard/src/main/npm/ts/tobago-file.ts
index 8b57f39..03cbdb7 100644
--- a/tobago-theme/tobago-theme-standard/src/main/npm/ts/tobago-file.ts
+++ b/tobago-theme/tobago-theme-standard/src/main/npm/ts/tobago-file.ts
@@ -15,38 +15,50 @@
  * limitations under the License.
  */
 
-import {Listener, Phase} from "./tobago-listener";
-import {DomUtils} from "./tobago-utils";
-
-class File {
-
-  static init(element: HTMLElement) {
-    for (const e of DomUtils.selfOrQuerySelectorAll(element, ".tobago-file-real")) {
-      const real = e as HTMLInputElement;
-      real.addEventListener("change", function () {
-        const pretty = real.parentElement.querySelector(".tobago-file-pretty") as HTMLInputElement;
-        let text: string;
-        if (real.multiple) {
-          const format: string = real.dataset["tobagoFileMultiFormat"];
-          text = format.replace("{}", String(real.files.length));
-        } else {
-          text = <string>real.value;
-          // remove path, if any. Some old browsers set the path, others like webkit uses the prefix "C:\path\".
-          const pos: number = Math.max(text.lastIndexOf('/'), text.lastIndexOf('\\'));
-          if (pos >= 0) {
-            text = text.substr(pos + 1);
-          }
+export class File extends HTMLElement {
+
+  constructor() {
+    super();
+  }
+
+  connectedCallback() {
+    this.input.form.enctype = "multipart/form-data";
+
+    this.input.addEventListener("change", this.select.bind(this));
+  }
+
+  get input(): HTMLInputElement {
+    return this.querySelector(".custom-file-input");
+  }
+
+  get label(): HTMLInputElement {
+    return this.querySelector(".custom-file-label");
+  }
+
+  select(event: MouseEvent) {
+    if (this.input.value === "") {
+      this.label.classList.add("tobago-file-placeholder");
+      this.label.textContent = this.input.placeholder;
+    } else {
+      this.label.classList.remove("tobago-file-placeholder");
+
+      let text: string;
+      if (this.input.multiple) {
+        const format: string = this.input.dataset["tobagoFileMultiFormat"];
+        text = format.replace("{}", String(this.input.files.length));
+      } else {
+        text = this.input.value;
+        // remove path, if any. Some old browsers set the path, others like webkit uses the prefix "C:\path\".
+        const pos: number = Math.max(text.lastIndexOf('/'), text.lastIndexOf('\\'));
+        if (pos >= 0) {
+          text = text.substr(pos + 1);
         }
-        pretty.value = text;
-      });
-      // click on the button (when using focus with keyboard)
-      real.parentElement.querySelector("button").addEventListener("click", function () {
-        real.click();
-      });
-      real.form.enctype = "multipart/form-data";
+      }
+      this.label.textContent = text;
     }
-  };
+  }
 }
 
-Listener.register(File.init, Phase.DOCUMENT_READY);
-Listener.register(File.init, Phase.AFTER_UPDATE);
+document.addEventListener("DOMContentLoaded", function (event) {
+  window.customElements.define('tobago-file', File);
+});