diff --git a/src/Foundation.hs b/src/Foundation.hs index cc5e85bcf..047a69955 100644 --- a/src/Foundation.hs +++ b/src/Foundation.hs @@ -1076,8 +1076,8 @@ siteLayout' headingOverride widget = do -- addScript $ StaticR js_utils_checkAll_js -- addScript $ StaticR js_utils_httpClient_js -- addScript $ StaticR js_utils_form_js - -- addScript $ StaticR js_utils_inputs_js -- JavaScript utils + addScript $ StaticR js_utils_inputs_js addScript $ StaticR js_utils_modal_js addScript $ StaticR js_utils_poc_js addScript $ StaticR js_utils_showHide_js diff --git a/static/css/utils/inputs.scss b/static/css/utils/inputs.scss index db16a94d3..f2b479979 100644 --- a/static/css/utils/inputs.scss +++ b/static/css/utils/inputs.scss @@ -196,7 +196,11 @@ option { } } -/* CUSTOM FILE INPUT */ +/* FILE INPUT */ +.file-input { + display: none; +} + .file-input__label { cursor: pointer; display: inline-block; @@ -206,6 +210,15 @@ option { border-radius: 3px; } -.file-input__input--hidden { - display: none; +.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; } diff --git a/static/js/utils/inputs.js b/static/js/utils/inputs.js index 3a9f7ebc4..b544e2272 100644 --- a/static/js/utils/inputs.js +++ b/static/js/utils/inputs.js @@ -1,8 +1,6 @@ (function() { 'use strict'; - window.utils = window.utils || {}; - var JS_INITIALIZED_CLASS = 'js-inputs-initialized'; 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 - // 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" + /** + * + * FileUpload Utility + * + * Attribute: uw-file-upload + * (element must be an input of type='file') + * + * Example usage: + * + * + * 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_INPUT_UNPACK_CHECKBOX_CLASS = 'file-input__unpack'; - var FILE_UPLOAD_INPUT_LABEL_CLASS = 'file-input__label'; - var FILE_UPLOAD_INPUT_HIDDEN_CLASS = 'file-input__input--hidden'; + var FILE_UPLOAD_UTIL_NAME = 'fileUpload'; + var FILE_UPLOAD_UTIL_SELECTOR = 'input[type="file"][uw-file-upload]'; - window.utils.fileUpload = function(input, options) { - var isMulti = input.hasAttribute('multiple'); - var fileList = isMulti ? addFileList() : null; - var label = addFileLabel(); - var i18n = options.i18n; + var FILE_UPLOAD_CLASS = 'file-input'; + var FILE_UPLOAD_INITIALIZED_CLASS = 'file-input--initialized'; + var FILE_UPLOAD_LIST_CLASS = 'file-input__list'; + var FILE_UPLOAD_UNPACK_CHECKBOX_CLASS = 'file-input__unpack'; + var FILE_UPLOAD_LABEL_CLASS = 'file-input__label'; - if (!i18n) { - throw new Error('window.utils.fileUpload(input, options) needs to be passed i18n object via options'); + var fileUploadUtil = function(element) { + 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 = ''; - Array.from(files).forEach(function(file, index) { + Array.from(files).forEach(function(file) { var fileDisplayEl = document.createElement('li'); fileDisplayEl.innerHTML = file.name; fileList.appendChild(fileDisplayEl); }); } - function updateLabel(files) { - if (files.length) { - if (isMulti) { - label.innerText = files.length + ' ' + i18n.filesSelected; - } else { - label.innerHTML = files[0].name; - } - } else { - resetFileLabel(); - } - } - - function addFileList() { + function createFileList() { var list = document.createElement('ol'); - list.classList.add(FILE_UPLOAD_INPUT_LIST_CLASS); - var unpackEl = input.parentElement.querySelector('.' + FILE_UPLOAD_INPUT_UNPACK_CHECKBOX_CLASS); + list.classList.add(FILE_UPLOAD_LIST_CLASS); + var unpackEl = element.parentElement.querySelector('.' + FILE_UPLOAD_UNPACK_CHECKBOX_CLASS); if (unpackEl) { - input.parentElement.insertBefore(list, unpackEl); + element.parentElement.insertBefore(list, unpackEl); } else { - input.parentElement.appendChild(list); + element.parentElement.appendChild(list); } return list; } - function addFileLabel() { + function createFileLabel() { var label = document.createElement('label'); - label.classList.add(FILE_UPLOAD_INPUT_LABEL_CLASS); - label.setAttribute('for', input.id); - input.parentElement.insertBefore(label, input); + label.classList.add(FILE_UPLOAD_LABEL_CLASS); + label.setAttribute('for', element.id); + element.parentElement.insertBefore(label, element); return label; } - function resetFileLabel() { - if (isMulti) { - label.innerText = i18n.selectFiles; + function updateLabel() { + var files = element.files; + if (files && files.length) { + label.innerText = isMultiFileUpload ? files.length + ' ' + I18n.get('filesSelected') : files[0].name; } else { - label.innerText = i18n.selectFile; + label.innerText = isMultiFileUpload ? I18n.get('selectFiles') : I18n.get('selectFile'); } } - // initial setup - resetFileLabel(); - input.classList.add(FILE_UPLOAD_INPUT_HIDDEN_CLASS); - input.addEventListener('change', function() { - input.dispatchEvent(new Event('input')); - if (isMulti) { - renderFileList(input.files); - } + return init(); + } - 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 @@ -158,7 +188,7 @@ } // initial setup - function setup() { + function init() { var cont = input.parentNode; while (cont !== document.body) { if (cont.matches('.' + FILE_UPLOAD_CONTAINER_CLASS)) { @@ -169,12 +199,7 @@ addListener(cont); } - setup(); - - return { - scope: input, - destroy: function() {}, - }; + return init(); } // turns native checkboxes into custom ones diff --git a/templates/multiFileField.hamlet b/templates/multiFileField.hamlet index 7fbcc896d..0ac0dc594 100644 --- a/templates/multiFileField.hamlet +++ b/templates/multiFileField.hamlet @@ -7,7 +7,7 @@ $forall FileUploadInfo{..} <- fileInfos $# new files - + _{MsgMultiFileUploadInfo} diff --git a/templates/multiFileField.lucius b/templates/multiFileField.lucius index 21712010e..8d0bf62d2 100644 --- a/templates/multiFileField.lucius +++ b/templates/multiFileField.lucius @@ -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 { display: flex; align-items: center;