refactor file upload JS utility

This commit is contained in:
Felix Hamann 2019-04-06 14:51:16 +02:00
parent c2d01e9489
commit 4973fd0b08
5 changed files with 112 additions and 87 deletions

View File

@ -1076,8 +1076,8 @@ siteLayout' headingOverride widget = do
-- addScript $ StaticR js_utils_checkAll_js -- addScript $ StaticR js_utils_checkAll_js
-- addScript $ StaticR js_utils_httpClient_js -- addScript $ StaticR js_utils_httpClient_js
-- addScript $ StaticR js_utils_form_js -- addScript $ StaticR js_utils_form_js
-- addScript $ StaticR js_utils_inputs_js
-- JavaScript utils -- JavaScript utils
addScript $ StaticR js_utils_inputs_js
addScript $ StaticR js_utils_modal_js addScript $ StaticR js_utils_modal_js
addScript $ StaticR js_utils_poc_js addScript $ StaticR js_utils_poc_js
addScript $ StaticR js_utils_showHide_js addScript $ StaticR js_utils_showHide_js

View File

@ -196,7 +196,11 @@ option {
} }
} }
/* CUSTOM FILE INPUT */ /* FILE INPUT */
.file-input {
display: none;
}
.file-input__label { .file-input__label {
cursor: pointer; cursor: pointer;
display: inline-block; display: inline-block;
@ -206,6 +210,15 @@ option {
border-radius: 3px; border-radius: 3px;
} }
.file-input__input--hidden { .file-input__multi-info {
display: none; font-size: .9rem;
font-style: italic;
margin-top: 10px;
color: var(--color-fontsec);
}
.file-input__list {
margin-left: 15px;
margin-top: 10px;
font-weight: 600;
} }

View File

@ -1,8 +1,6 @@
(function() { (function() {
'use strict'; 'use strict';
window.utils = window.utils || {};
var JS_INITIALIZED_CLASS = 'js-inputs-initialized'; var JS_INITIALIZED_CLASS = 'js-inputs-initialized';
window.utils.inputs = function(wrapper, options) { window.utils.inputs = function(wrapper, options) {
@ -53,95 +51,127 @@
}; };
}; };
// (multiple) dynamic file uploads /**
// expects i18n object with following strings: *
// »filesSelected«: label of multi-upload button after selection * FileUpload Utility
// example: "Dateien ausgewählt" (will be prepended by number of selected files) *
// »selectFile«: label of single-upload button before selection * Attribute: uw-file-upload
// example: "Datei auswählen" * (element must be an input of type='file')
// »selectFiles«: label of multi-upload button before selection *
// example: "Datei(en) auswählen" * Example usage:
* <input type='file' uw-file-upload>
*
* Internationalization:
* This utility expects the following translations to be available:
* »filesSelected«: label of multi-upload button after selection
* example: "Dateien ausgewählt" (will be prepended by number of selected files)
* »selectFile«: label of single-upload button before selection
* example: "Datei auswählen"
* »selectFiles«: label of multi-upload button before selection
* example: "Datei(en) auswählen"
*
*/
var FILE_UPLOAD_INPUT_LIST_CLASS = 'file-input__list'; var FILE_UPLOAD_UTIL_NAME = 'fileUpload';
var FILE_UPLOAD_INPUT_UNPACK_CHECKBOX_CLASS = 'file-input__unpack'; var FILE_UPLOAD_UTIL_SELECTOR = 'input[type="file"][uw-file-upload]';
var FILE_UPLOAD_INPUT_LABEL_CLASS = 'file-input__label';
var FILE_UPLOAD_INPUT_HIDDEN_CLASS = 'file-input__input--hidden';
window.utils.fileUpload = function(input, options) { var FILE_UPLOAD_CLASS = 'file-input';
var isMulti = input.hasAttribute('multiple'); var FILE_UPLOAD_INITIALIZED_CLASS = 'file-input--initialized';
var fileList = isMulti ? addFileList() : null; var FILE_UPLOAD_LIST_CLASS = 'file-input__list';
var label = addFileLabel(); var FILE_UPLOAD_UNPACK_CHECKBOX_CLASS = 'file-input__unpack';
var i18n = options.i18n; var FILE_UPLOAD_LABEL_CLASS = 'file-input__label';
if (!i18n) { var fileUploadUtil = function(element) {
throw new Error('window.utils.fileUpload(input, options) needs to be passed i18n object via options'); var isMultiFileUpload = false;
var fileList;
var label;
function init() {
if (!element) {
throw new Error('FileUpload utility cannot be setup without an element!');
}
if (element.classList.contains(FILE_UPLOAD_INITIALIZED_CLASS)) {
throw new Error('FileUpload utility already initialized!');
}
// check if is multi-file upload
isMultiFileUpload = element.hasAttribute('multiple');
if (isMultiFileUpload) {
fileList = createFileList();
}
label = createFileLabel();
updateLabel();
// add change listener
element.addEventListener('change', function() {
updateLabel();
renderFileList();
});
// add util class for styling and mark as initialized
element.classList.add(FILE_UPLOAD_CLASS, FILE_UPLOAD_INITIALIZED_CLASS);
return {
name: FILE_UPLOAD_UTIL_NAME,
element: element,
destroy: function() {},
};
} }
function renderFileList(files) { function renderFileList() {
if (!fileList) {
return;
}
var files = element.files;
fileList.innerHTML = ''; fileList.innerHTML = '';
Array.from(files).forEach(function(file, index) { Array.from(files).forEach(function(file) {
var fileDisplayEl = document.createElement('li'); var fileDisplayEl = document.createElement('li');
fileDisplayEl.innerHTML = file.name; fileDisplayEl.innerHTML = file.name;
fileList.appendChild(fileDisplayEl); fileList.appendChild(fileDisplayEl);
}); });
} }
function updateLabel(files) { function createFileList() {
if (files.length) {
if (isMulti) {
label.innerText = files.length + ' ' + i18n.filesSelected;
} else {
label.innerHTML = files[0].name;
}
} else {
resetFileLabel();
}
}
function addFileList() {
var list = document.createElement('ol'); var list = document.createElement('ol');
list.classList.add(FILE_UPLOAD_INPUT_LIST_CLASS); list.classList.add(FILE_UPLOAD_LIST_CLASS);
var unpackEl = input.parentElement.querySelector('.' + FILE_UPLOAD_INPUT_UNPACK_CHECKBOX_CLASS); var unpackEl = element.parentElement.querySelector('.' + FILE_UPLOAD_UNPACK_CHECKBOX_CLASS);
if (unpackEl) { if (unpackEl) {
input.parentElement.insertBefore(list, unpackEl); element.parentElement.insertBefore(list, unpackEl);
} else { } else {
input.parentElement.appendChild(list); element.parentElement.appendChild(list);
} }
return list; return list;
} }
function addFileLabel() { function createFileLabel() {
var label = document.createElement('label'); var label = document.createElement('label');
label.classList.add(FILE_UPLOAD_INPUT_LABEL_CLASS); label.classList.add(FILE_UPLOAD_LABEL_CLASS);
label.setAttribute('for', input.id); label.setAttribute('for', element.id);
input.parentElement.insertBefore(label, input); element.parentElement.insertBefore(label, element);
return label; return label;
} }
function resetFileLabel() { function updateLabel() {
if (isMulti) { var files = element.files;
label.innerText = i18n.selectFiles; if (files && files.length) {
label.innerText = isMultiFileUpload ? files.length + ' ' + I18n.get('filesSelected') : files[0].name;
} else { } else {
label.innerText = i18n.selectFile; label.innerText = isMultiFileUpload ? I18n.get('selectFiles') : I18n.get('selectFile');
} }
} }
// initial setup return init();
resetFileLabel(); }
input.classList.add(FILE_UPLOAD_INPUT_HIDDEN_CLASS);
input.addEventListener('change', function() {
input.dispatchEvent(new Event('input'));
if (isMulti) {
renderFileList(input.files);
}
updateLabel(input.files); if (UtilRegistry) {
UtilRegistry.register({
name: FILE_UPLOAD_UTIL_NAME,
selector: FILE_UPLOAD_UTIL_SELECTOR,
setup: fileUploadUtil,
}); });
return {
scope: input,
destroy: function() {},
};
} }
// to remove previously uploaded files // to remove previously uploaded files
@ -158,7 +188,7 @@
} }
// initial setup // initial setup
function setup() { function init() {
var cont = input.parentNode; var cont = input.parentNode;
while (cont !== document.body) { while (cont !== document.body) {
if (cont.matches('.' + FILE_UPLOAD_CONTAINER_CLASS)) { if (cont.matches('.' + FILE_UPLOAD_CONTAINER_CLASS)) {
@ -169,12 +199,7 @@
addListener(cont); addListener(cont);
} }
setup(); return init();
return {
scope: input,
destroy: function() {},
};
} }
// turns native checkboxes into custom ones // turns native checkboxes into custom ones

View File

@ -7,7 +7,7 @@ $forall FileUploadInfo{..} <- fileInfos
<label for=#{fuiHtmlId}> <label for=#{fuiHtmlId}>
$# new files $# new files
<input type="file" name=#{fieldName} id=#{fieldId} multiple :req:required="required"> <input type="file" uw-file-upload name=#{fieldName} id=#{fieldId} multiple :req:required="required">
<div .file-input__multi-info> <div .file-input__multi-info>
_{MsgMultiFileUploadInfo} _{MsgMultiFileUploadInfo}

View File

@ -10,19 +10,6 @@
} }
} }
.file-input__multi-info {
font-size: .9rem;
font-style: italic;
margin-top: 10px;
color: var(--color-fontsec);
}
.file-input__list {
margin-left: 15px;
margin-top: 10px;
font-weight: 600;
}
.file-container { .file-container {
display: flex; display: flex;
align-items: center; align-items: center;