File uploads
Combination of fields and buttons to add documents or media to a form.
When to use
When there is a need to allow users to attach anything to a form (documents, pictures, videos, etc).
Implementation
In order to automatically attach event listeners on your file upload forms, add the following script to your page:
document.addEventListener('DOMContentLoaded', function() {
ECL.fileUploads();
});
{#
- "id" (string): the id of the file input (default: 'default-id')
- "name" (string): the name of the file input (default: 'default-name')
- "value" (string): the name of the file selected (default: 'No file selected.')
- "label_browse" (string): the label of the Browse button (default: 'Browse')
- "has_upload" (boolean): define if form upload has a "upload" button (default: true)
- "label_upload" (string): the label of the Upload button (default: 'Upload')
- "is_disabled" (boolean): define if form upload is disabled (default: false)
- "is_multiple" (boolean): define if form upload is multiple (default: false)
- "has_error" (boolean): define if form upload has error (default: false)
- "extra_classes" (string): extra CSS classes to be added
- "extra_attributes" (array): extra attributes classes (optional, format: [{ 'name': 'name_of_the_attribute', 'value': 'value_of_the_attribute'}])
#}
{% set id = id|default('default-id') %}
{% set name = name|default('default-name') %}
{% set value = value|default('No file selected.') %}
{% set label_browse = label_browse|default('Browse') %}
{% set has_upload = has_upload|default(true) %}
{% set label_upload = label_upload|default('Upload') %}
{% set is_disabled = is_disabled|default(false) %}
{% set is_multiple = is_multiple|default(false) %}
{% set has_error = has_error|default(false) %}
{# Internal properties #}
{% set _css_class = 'ecl-file-upload' %}
{% set _extra_attributes = '' %}
{# Internal logic - Process properties #}
{% if has_error == true %}
{% set _css_class = _css_class ~ ' ecl-file-upload--has-error' %}
{% endif %}
{% if is_disabled == true %}
{% set _css_class = _css_class ~ ' ecl-file-upload--is-disabled' %}
{% endif %}
{% if extra_class is defined %}
{% set _css_class = _css_class ~ ' ' ~ extra_class %}
{% endif %}
{% if extra_attributes is defined %}
{% for attr in extra_attributes %}
{% set _extra_attributes = _extra_attributes ~ ' ' ~ attr.name ~ '="' ~ attr.value ~'"' %}
{% endfor %}
{% endif %}
{# Print the result #}
<div class="{{ _css_class }}" {{ _extra_attributes|raw }}>
<div class="ecl-file-upload__value" tabindex="0">{{ value }}</div>
<label for="{{ id }}">
<span class="ecl-file-upload__browse" role="button" aria-controls="{{ id }}" tabindex="0">{{ label_browse }}</span>
</label>
{% if has_upload == true and is_disabled == false %}
<button class="ecl-file-upload__upload" tabindex="0" type="submit">
{{ label_upload }}
</button>
{% endif %}
<input class="ecl-file-upload__input" name="{{ name }}" type="file" id="{{ id }}" {% if is_disabled == true %}disabled{% endif %} {% if is_multiple == true %}multiple{% endif %}>
</div>
/* Normal file upload */
{
"id": "example-input-id-1",
"name": "example-input-name-1"
}
/* Disabled file upload */
{
"id": "example-input-id-2",
"name": "example-input-name-2",
"is_disabled": true
}
/* Multiple file upload */
{
"id": "example-input-id-3",
"name": "example-input-name-3",
"is_multiple": true
}
/* File upload with error */
{
"id": "example-input-id-4",
"name": "example-input-name-4",
"has_error": true
}
<!-- Normal file upload -->
<div class="ecl-file-upload">
<div class="ecl-file-upload__value" tabindex="0">No file selected.</div>
<label for="example-input-id-1">
<span class="ecl-file-upload__browse" role="button" aria-controls="example-input-id-1" tabindex="0">Browse</span>
</label>
<button class="ecl-file-upload__upload" tabindex="0" type="submit">
Upload
</button>
<input class="ecl-file-upload__input" name="example-input-name-1" type="file" id="example-input-id-1">
</div>
<!-- Disabled file upload -->
<div class="ecl-file-upload ecl-file-upload--is-disabled">
<div class="ecl-file-upload__value" tabindex="0">No file selected.</div>
<label for="example-input-id-2">
<span class="ecl-file-upload__browse" role="button" aria-controls="example-input-id-2" tabindex="0">Browse</span>
</label>
<input class="ecl-file-upload__input" name="example-input-name-2" type="file" id="example-input-id-2" disabled>
</div>
<!-- Multiple file upload -->
<div class="ecl-file-upload">
<div class="ecl-file-upload__value" tabindex="0">No file selected.</div>
<label for="example-input-id-3">
<span class="ecl-file-upload__browse" role="button" aria-controls="example-input-id-3" tabindex="0">Browse</span>
</label>
<button class="ecl-file-upload__upload" tabindex="0" type="submit">
Upload
</button>
<input class="ecl-file-upload__input" name="example-input-name-3" type="file" id="example-input-id-3" multiple>
</div>
<!-- File upload with error -->
<div class="ecl-file-upload ecl-file-upload--has-error">
<div class="ecl-file-upload__value" tabindex="0">No file selected.</div>
<label for="example-input-id-4">
<span class="ecl-file-upload__browse" role="button" aria-controls="example-input-id-4" tabindex="0">Browse</span>
</label>
<button class="ecl-file-upload__upload" tabindex="0" type="submit">
Upload
</button>
<input class="ecl-file-upload__input" name="example-input-name-4" type="file" id="example-input-id-4">
</div>
-
Content:
/** * File uploads related behaviors. */ import { queryAll } from '@ec-europa/ecl-base/helpers/dom'; /** * @param {object} options Object containing configuration overrides */ export const fileUploads = ({ selector: selector = '.ecl-file-upload', inputSelector: inputSelector = '.ecl-file-upload__input', valueSelector: valueSelector = '.ecl-file-upload__value', browseSelector: browseSelector = '.ecl-file-upload__browse', } = {}) => { // SUPPORTS if ( !('querySelector' in document) || !('addEventListener' in window) || !document.documentElement.classList ) return null; // SETUP // set file upload element NodeLists const fileUploadContainers = queryAll(selector); // ACTIONS function updateFileName(element, files) { if (files.length === 0) return; let filename = ''; for (let i = 0; i < files.length; i += 1) { const file = files[i]; if ('name' in file) { if (i > 0) { filename += ', '; } filename += file.name; } } // Show the selected filename in the field. const messageElement = element; messageElement.innerHTML = filename; } // EVENTS function eventValueChange(e) { if ('files' in e.target) { const fileUploadElements = queryAll(valueSelector, e.target.parentNode); fileUploadElements.forEach(fileUploadElement => { updateFileName(fileUploadElement, e.target.files); }); } } function eventBrowseKeydown(e) { // collect header targets, and their prev/next const isModifierKey = e.metaKey || e.altKey; const inputElements = queryAll(inputSelector, e.target.parentNode); inputElements.forEach(inputElement => { // don't catch key events when ⌘ or Alt modifier is present if (isModifierKey) return; // catch enter/space, left/right and up/down arrow key events // if new panel show it, if next/prev move focus switch (e.keyCode) { case 13: case 32: e.preventDefault(); inputElement.click(); break; default: break; } }); } // BIND EVENTS function bindFileUploadEvents(fileUploadContainer) { // bind all file upload change events const fileUploadInputs = queryAll(inputSelector, fileUploadContainer); fileUploadInputs.forEach(fileUploadInput => { fileUploadInput.addEventListener('change', eventValueChange); }); // bind all file upload keydown events const fileUploadBrowses = queryAll(browseSelector, fileUploadContainer); fileUploadBrowses.forEach(fileUploadBrowse => { fileUploadBrowse.addEventListener('keydown', eventBrowseKeydown); }); } // UNBIND EVENTS function unbindFileUploadEvents(fileUploadContainer) { const fileUploadInputs = queryAll(inputSelector, fileUploadContainer); // unbind all file upload change events fileUploadInputs.forEach(fileUploadInput => { fileUploadInput.removeEventListener('change', eventValueChange); }); const fileUploadBrowses = queryAll(browseSelector, fileUploadContainer); // bind all file upload keydown events fileUploadBrowses.forEach(fileUploadBrowse => { fileUploadBrowse.removeEventListener('keydown', eventBrowseKeydown); }); } // DESTROY function destroy() { fileUploadContainers.forEach(fileUploadContainer => { unbindFileUploadEvents(fileUploadContainer); }); } // INIT function init() { if (fileUploadContainers.length) { fileUploadContainers.forEach(fileUploadContainer => { bindFileUploadEvents(fileUploadContainer); }); } } init(); // REVEAL API return { init, destroy, }; }; // module exports export default fileUploads;
- URL: /components/raw/ecl-forms-file-uploads/ecl-forms-file-uploads.js
- Filesystem Path: framework/components/ecl-forms/ecl-forms-file-uploads/ecl-forms-file-uploads.js
- Size: 3.8 KB
-
Content:
/* * File upload * @define file-upload */ .ecl-file-upload { display: inline-flex; margin: 0; width: 100%; } .ecl-file-upload__value { background-color: #fff; background-image: none; border: 1px solid $ecl-color-shade; color: $ecl-color-shade; display: block; flex-grow: 1; font-family: $ecl-font-family-sans-serif; font-size: map-get($ecl-font-size, 's'); line-height: 1.6; margin: 0; overflow: hidden; padding: map-get($ecl-spacing, 'xxxs') map-get($ecl-spacing, 'xxs'); text-overflow: ellipsis; white-space: nowrap; &:focus { border-color: map-get($ecl-colors, 'yellow-110'); outline: 3px solid map-get($ecl-colors, 'yellow-110'); outline-offset: 0; text-decoration: none; } } .ecl-file-upload__browse { background-color: $ecl-color-shade; border: 2px solid transparent; color: #fff; display: inline-block; font-family: $ecl-font-family-sans-serif; font-size: map-get($ecl-font-size, 's'); font-weight: 600; line-height: 1.6; margin: 0; padding: map-get($ecl-spacing, 'xxxs') map-get($ecl-spacing, 'xs'); &:hover, &:focus, &:active { background-color: $ecl-color-primary; outline: 3px solid map-get($ecl-colors, 'yellow-110'); outline-offset: -3px; } } .ecl-file-upload__upload { background-color: $ecl-color-primary; border: 2px solid transparent; color: #fff; display: inline-block; font-family: $ecl-font-family-sans-serif; font-size: map-get($ecl-font-size, 's'); font-weight: 600; line-height: 1.6; margin-left: map-get($ecl-spacing, 'xxxs'); padding: map-get($ecl-spacing, 'xxxs') map-get($ecl-spacing, 'xs'); &:hover, &:focus, &:active { background-color: #fff; border-color: $ecl-color-primary; color: $ecl-color-primary; text-decoration: underline; } &:focus { outline: 3px solid map-get($ecl-colors, 'yellow-110'); outline-offset: -3px; } } .ecl-file-upload__input { display: none; } // disabled .ecl-file-upload--is-disabled { .ecl-file-upload__value { background-color: #eee; cursor: not-allowed; } .ecl-file-upload__browse { cursor: not-allowed; } } // error .ecl-file-upload--has-error { .ecl-file-upload__value { border-color: $ecl-color-error; border-width: 2px; } .ecl-file-upload__browse { background-color: $ecl-color-error; } }
- URL: /components/raw/ecl-forms-file-uploads/ecl-forms-file-uploads.scss
- Filesystem Path: framework/components/ecl-forms/ecl-forms-file-uploads/ecl-forms-file-uploads.scss
- Size: 2.4 KB
- Handle: @ec-europa/ecl-forms-file-uploads
- Tags: atom
- Preview:
- Filesystem Path: framework/components/ecl-forms/ecl-forms-file-uploads/ecl-forms-file-uploads.twig