Skip to content
Permalink
Browse files

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
  • Loading branch information...
henningn committed Sep 9, 2019
1 parent cf19a23 commit 2e472db97372b71c96e8a4096ad2b38a277381fe
@@ -73,7 +73,7 @@ private void addErrorMessage(final FacesContext facesContext) {

@Override
public String getFieldId(final FacesContext facesContext) {
return getClientId(facesContext) + ComponentUtils.SUB_SEPARATOR + "real";
return getClientId(facesContext) + ComponentUtils.SUB_SEPARATOR + "field";
}

public boolean isLabelLayoutSkip() {
@@ -27,11 +27,9 @@
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 @@ protected void encodeBeginField(final FacesContext facesContext, final UICompone
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 @@ protected void encodeBeginField(final FacesContext facesContext, final UICompone

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 @@ private String createAcceptFromValidators(final AbstractUIFile file) {
@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
@@ -240,6 +240,9 @@
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"),
@@ -122,8 +122,7 @@
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"),
@@ -133,6 +133,7 @@
VIDEO("video"),
WBR("wbr", Qualifier.VOID),

TOBAGO_FILE("tobago-file"),
TOBAGO_IN("tobago-in"),
TOBAGO_PANEL("tobago-panel"),
TOBAGO_POPUP("tobago-popup"),
@@ -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;
}
}
@@ -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);
});

0 comments on commit 2e472db

Please sign in to comment.
You can’t perform that action at this time.