Skip to content

Commit

Permalink
feat(ui5-file-uploader): initial implementation (#1184)
Browse files Browse the repository at this point in the history
  • Loading branch information
dimovpetar committed Feb 14, 2020
1 parent 78a9181 commit e628dbd
Show file tree
Hide file tree
Showing 13 changed files with 761 additions and 1 deletion.
1 change: 1 addition & 0 deletions packages/main/bundle.esm.js
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ import ComboBox from "./dist/ComboBox.js";
import ComboBoxItem from "./dist/ComboBoxItem.js";
import DatePicker from "./dist/DatePicker.js";
import Dialog from "./dist/Dialog.js";
import FileUploader from "./dist/FileUploader.js";
import Icon from "./dist/Icon.js";
import Input from "./dist/Input.js";
import Label from "./dist/Label.js";
Expand Down
31 changes: 31 additions & 0 deletions packages/main/src/FileUploader.hbs
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@

<div class="ui5-file-uploader-root"
@mouseover="{{_onmouseover}}"
@mouseout="{{_onmouseout}}"
>
<div class="ui5-file-uploader-mask">
{{#unless hideInput}}
<ui5-input
value="{{value}}"
value-state="{{valueState}}"
placeholder="{{placeholder}}"
?disabled="{{disabled}}"
></ui5-input>
{{/unless}}
<slot></slot>
</div>
{{#if _keepInputInShadowDOM}}
<input
type="file"
title="upload file"
accept="{{accept}}"
?multiple="{{multiple}}"
?disabled="{{disabled}}"
@change="{{_onChange}}"
>
{{else}}
<slot name="formSupport">
{{!-- input for forms will be placed here on IE, Edge and Firefox --}}
</slot>
{{/if}}
</div>
318 changes: 318 additions & 0 deletions packages/main/src/FileUploader.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,318 @@
import UI5Element from "@ui5/webcomponents-base/dist/UI5Element.js";
import ValueState from "@ui5/webcomponents-base/dist/types/ValueState.js";
import { getFeature } from "@ui5/webcomponents-base/dist/FeaturesRegistry.js";
import litRender from "@ui5/webcomponents-base/dist/renderer/LitRenderer.js";
import {
fetchI18nBundle,
getI18nBundle,
} from "@ui5/webcomponents-base/dist/i18nBundle.js";
import {
FILEUPLOAD_BROWSE,
} from "./generated/i18n/i18n-defaults.js";
import Input from "./Input.js";

// Template
import FileUploaderTemplate from "./generated/templates/FileUploaderTemplate.lit.js";

// Styles
import FileUploaderCss from "./generated/themes/FileUploader.css.js";

/**
* @public
*/
const metadata = {
tag: "ui5-file-uploader",
properties: /** @lends sap.ui.webcomponents.main.FileUploader.prototype */ {
/**
* Comma-separated list of file types that the <code>ui5-file-uploader</code> should accept.
* @type {string}
* @public
*/
accept: {
type: String,
},

/**
* If set to "true", the input field of <code>ui5-file-uploader</code> will not be rendered. Only the default slot that is passed will be rendered.
* @type {boolean}
* @public
*/
hideInput: {
type: Boolean,
},

/**
* Defines whether <code>ui5-file-uploader</code> is in disabled state.
* <br><br>
* <b>Note:</b> A disabled <code>ui5-input</code> is completely noninteractive.
*
* @type {boolean}
* @defaultvalue false
* @public
*/
disabled: {
type: Boolean,
},

/**
* Allows multiple files to be chosen.
* @type {boolean}
* @defaultvalue false
* @public
*/
multiple: {
type: Boolean,
},

/**
* Determines the name with which the <code>ui5-file-uploader</code> will be submitted in an HTML form.
*
* <b>Important:</b> For the <code>name</code> property to have effect, you must add the following import to your project:
* <code>import "@ui5/webcomponents/dist/features/InputElementsFormSupport.js";</code>
*
* <b>Note:</b> When set, a native <code>input</code> HTML element
* will be created inside the <code>ui5-file-uploader</code> so that it can be submitted as
* part of an HTML form. Do not use this property unless you need to submit a form.
*
* @type {string}
* @defaultvalue ""
* @public
*/
name: {
type: String,
},

/**
* Defines a short hint intended to aid the user with data entry when the <code>ui5-file-uploader</code> has no value.
* @type {string}
* @defaultvalue ""
* @public
*/
placeholder: {
type: String,
},

/**
* Name/names of the file/files to upload.
* @type {string}
* @defaultvalue ""
* @public
*/
value: {
type: String,
},

/**
* Defines the value state of the <code>ui5-file-uploader</code>.
* Available options are: <code>None</code>, <code>Success</code>, <code>Warning</code>, and <code>Error</code>.
*
* @type {string}
* @defaultvalue "None"
* @public
*/
valueState: {
type: ValueState,
defaultValue: ValueState.None,
},
},
slots: /** @lends sap.ui.webcomponents.main.FileUploader.prototype */ {
/**
* By default the <code>ui5-file-uploader</code> contains a single input field. With this slot you can pass any content that you wish to add. See the samples for more information
*
* @type {HTMLElement[]}
* @slot
* @public
*/
"default": {
propertyName: "content",
type: HTMLElement,
},
},
events: /** @lends sap.ui.webcomponents.main.FileUploader.prototype */ {
/**
* Event is fired when the value of the file path has been changed.
* Note: Keep in mind that because of the HTML input element of type file, the event is also fired in Chrome browser when the Cancel button of the uploads window is pressed.
*
* @event
* @param {FileList} files The current files.
* @public
*/
change: {
detail: {
files: { type: FileList },
},
},
},
};

/**
* @class
*
* <h3 class="comment-api-title">Overview</h3>
*
*
* <h3>Usage</h3>
*
* For the <code>ui5-file-uploader</code>
* <h3>ES6 Module Import</h3>
*
* <code>import @ui5/webcomponents/dist/FileUploader.js";</code>
*
* @constructor
* @author SAP SE
* @alias sap.ui.webcomponents.main.FileUploader
* @extends UI5Element
* @tagname ui5-file-uploader
* @public
*/
class FileUploader extends UI5Element {
static get formAssociated() {
return true;
}

static get metadata() {
return metadata;
}

static get render() {
return litRender;
}

static get styles() {
return FileUploaderCss;
}

static get template() {
return FileUploaderTemplate;
}

constructor() {
super();
if (this._canUseNativeFormSupport) {
this._internals = this.attachInternals();
}

this.i18nBundle = getI18nBundle("@ui5/webcomponents");
}

_onmouseover() {
this.content.forEach(item => {
item.classList.add("ui5_hovered");
});
}

_onmouseout() {
this.content.forEach(item => {
item.classList.remove("ui5_hovered");
});
}

/**
* FileList of all selected files.
* @readonly
* @type { FileList }
* @public
*/
get files() {
if (this._input) {
return this._input.files;
}

return FileUploader._emptyFilesList;
}

onBeforeRendering() {
this._enableFormSupport();
}

_enableFormSupport() {
const FormSupport = getFeature("FormSupport");

if (FormSupport) {
if (this._canUseNativeFormSupport) {
this._setFormValue();
} else {
FormSupport.syncNativeFileInput(
this,
(element, nativeInput) => {
nativeInput.disabled = element.disabled;
},
this._onChange.bind(this)
);
}
} else if (this.name) {
console.warn(`In order for the "name" property to have effect, you should also: import "@ui5/webcomponents/dist/features/InputElementsFormSupport.js";`); // eslint-disable-line
}
}

_onChange(event) {
this._updateValue(event.target.files);
this.fireEvent("change", {
files: event.target.files,
});
}

_updateValue(files) {
this.value = Array.from(files).reduce((acc, currFile) => {
return `${acc}"${currFile.name}" `;
}, "");
}

_setFormValue() {
const formData = new FormData();

for (let i = 0; i < this.files.length; i++) {
formData.append(this.name, this.files[i]);
}

this._internals.setFormValue(formData);
}

/**
* in case when ui5-file-uploader is not placed in the DOM, return empty FileList, like native input would do
* @private
*/
static get _emptyFilesList() {
if (!this.emptyInput) {
this.emptyInput = document.createElement("input");
this.emptyInput.type = "file";
}
return this.emptyInput.files;
}

get browseText() {
return this.i18nBundle.getText(FILEUPLOAD_BROWSE);
}

get _canUseNativeFormSupport() {
return !!this.attachInternals;
}

get _keepInputInShadowDOM() {
// only put input in the light dom when ui5-file-uploader is placed inside form and there is no support for form elements
return this._canUseNativeFormSupport || !this.name;
}

get _input() {
return this.shadowRoot.querySelector("input[type=file]") || this.querySelector("input[type=file][data-ui5-form-support]");
}

/**
* Determines input helper type in forms.
* @private
*/
get _type() {
return "file";
}

static async onDefine() {
await Promise.all([
Input.define(),
fetchI18nBundle("@ui5/webcomponents"),
]);
}
}

FileUploader.define();

export default FileUploader;
Loading

0 comments on commit e628dbd

Please sign in to comment.