diff --git a/packages/file-handling/assets/index.html b/packages/file-handling/assets/index.html index 858fbdaac68c66e545074c1db5061a38d66c3fa2..1252468a638b3c07cdad8bba6fa1397eb73772a4 100644 --- a/packages/file-handling/assets/index.html +++ b/packages/file-handling/assets/index.html @@ -3,15 +3,20 @@ <head> <meta charset="UTF-8"> <script type="module" id="vpu-fileupload-src" src="bundle.js"></script> + <style> + vpu-fileupload-demo { + --FUBorderWidth: 2px; + --FUBorderStyle: dotted; + --FUBorderColor: #555; + --FUBorderColorHighlight: #935; + --FUBorderRadius: 10px; + --FUBorder: 2px dashed red; + --FUMargin: 20px; + --FUPadding: 5px; + --FUWidth: 450px; + } + </style> </head> -<style> - vpu-fileupload-demo { - --FUBorderRadius: 10px; - --FUBorder: 2px dashed red; - --FUMargin: 20px; - --FUPadding: 5px; - } -</style> <body> <vpu-fileupload-demo lang="de" url="http://127.0.0.1:8080"></vpu-fileupload-demo> diff --git a/packages/file-handling/src/demo.js b/packages/file-handling/src/demo.js index 3f705072aa7cdc24bf7c8baaaa480af493b28678..6f201e5764847e49e8c72ebdd3ac6d17812ceb7e 100644 --- a/packages/file-handling/src/demo.js +++ b/packages/file-handling/src/demo.js @@ -1,5 +1,6 @@ import {i18n} from './i18n'; import {html, LitElement} from 'lit-element'; +import {unsafeHTML} from 'lit-html/directives/unsafe-html.js'; import './vpu-fileupload'; import * as commonUtils from 'vpu-common/utils'; @@ -17,6 +18,17 @@ class FileUploadDemo extends LitElement { }; } + connectedCallback() { + super.connectedCallback(); + + this.updateComplete.then(() => { + this.shadowRoot.querySelectorAll('vpu-fileupload') + .forEach(element => { + element.addEventListener('vpu-fileupload-finished', this.addLogEntry.bind(this)); + }); + }); + } + update(changedProperties) { changedProperties.forEach((oldValue, propName) => { if (propName === "lang") { @@ -27,14 +39,26 @@ class FileUploadDemo extends LitElement { super.update(changedProperties); } + addLogEntry(ev) { + const ul = this.shadowRoot.querySelector('#log'); + const li = document.createElement('li'); + li.innerHTML = `<li><b>${ev.detail.status}</b> <tt>${ev.detail.filename}</tt>`; + + ul.appendChild(li); + } + render() { return html` <style> vpu-fileupload.clean { - --FUBorder: initial; + --FUBorderWidth: initial; + --FUBorderStyle: initial; + --FUBorderColor: initial; + --FUBorderColorHighlight: initial; --FUBorderRadius: initial; --FUMargin: initial; --FUPadding: initial; + --FUWidth: initial; } vpu-fileupload.opt { --FUBorder: 2px solid blue; @@ -43,13 +67,19 @@ class FileUploadDemo extends LitElement { <section class="section"> <div class="content"> - <h1 class="title">File-Upload-Demo</h1> - <p>You need an upload server listening at <tt>${this.url}</tt> to receive the files...</p> + <h1 class="title">${i18n.t('demo-title')}</h1> + <p>${unsafeHTML(i18n.t('required-server', { url: this.url}))}</p> </div> <div class="content"> - <h2 class="subtitle">Send to Server</h2> - <p>Drop some files here:</p> + <h2 class="subtitle">Send any File to Server</h2> + <p>There is no restriction for a specific file type:</p> <vpu-fileupload lang="de" url="${this.url}"></vpu-fileupload> + <p>Only images are allowed here (JPG, PNG, GIF, TIF, ...):</p> + <vpu-fileupload lang="de" url="${this.url}" accept="image/*" + text="Abgabe nur fÞr Bilder "></vpu-fileupload> + <p>This is for PDF only:</p> + <vpu-fileupload lang="de" url="${this.url}" accept="application/pdf" + text="Einreichung als PDF" button-label="PDF auswÃĪhlen"></vpu-fileupload> </div> <div class="content"> <h2>Log of uploads</h2> diff --git a/packages/file-handling/src/i18n/de/translation.json b/packages/file-handling/src/i18n/de/translation.json index 6e81531dca5285b89ec941d0a30b28d611839b00..d303d239e0bc895c840c948d2658d8c90bb4109d 100644 --- a/packages/file-handling/src/i18n/de/translation.json +++ b/packages/file-handling/src/i18n/de/translation.json @@ -3,5 +3,10 @@ "is-forbidden": "ist verboten", "troubled-server": "macht Probleme am Server", "unknown-problems": "mit unbekanntem Problem", - "was-not-found": "wurde nicht gefunden" + "was-not-found": "wurde nicht gefunden", + + "demo-title": "Datei Abgabe Demo", + "server-required": "Es wird unter der URL <a href=\"{{- url}}\"><tt>{{- url}}</tt></a> ein Server benÃķtigt um die Dateien zu empfangen.", + "intro": "Laden Sie mehrere Dateien mit dem Auswahldialog oder durch Ziehen und Fallenlassen in diesem Bereich hoch", + "upload-label": "Dateiauswahl" } diff --git a/packages/file-handling/src/i18n/en/translation.json b/packages/file-handling/src/i18n/en/translation.json index 05eb31df511ec6c271e18d96dd2bdd94045f7170..c3cee2427e9abdf421a2da4aabd6937029ba0127 100644 --- a/packages/file-handling/src/i18n/en/translation.json +++ b/packages/file-handling/src/i18n/en/translation.json @@ -3,5 +3,10 @@ "is-forbidden": "is forbidden", "troubled-server": "troubled server", "unknown-problems": "with unknown problems", - "was-not-found": "was not found" + "was-not-found": "was not found", + + "demo-title": "File Upload Demo", + "required-server": "You need an upload server listening at <a href=\"{{- url}}\"><tt>{{- url}}</tt></a> to receive the files...", + "intro": "Upload multiple files with the file dialog or by dragging and dropping images onto the dashed region", + "upload-label": "Select some files" } diff --git a/packages/file-handling/src/vpu-fileupload.js b/packages/file-handling/src/vpu-fileupload.js index b629a7945bfbe8ee7d4f7b4181dfd276d039f1f5..f7f80c237de3da010d977c749413ec687ebc7b5d 100644 --- a/packages/file-handling/src/vpu-fileupload.js +++ b/packages/file-handling/src/vpu-fileupload.js @@ -1,10 +1,10 @@ import {i18n} from './i18n'; -import {html} from 'lit-element'; +import {css, html} from 'lit-element'; +import {ifDefined} from 'lit-html/directives/if-defined'; // import JSONLD from 'vpu-common/jsonld'; import VPULitElement from 'vpu-common/vpu-lit-element' import "vpu-common/vpu-mini-spinner.js"; import * as commonUtils from "vpu-common/utils"; -import {unsafeHTML} from 'lit-html/directives/unsafe-html.js'; import 'vpu-common/vpu-icon.js'; /** @@ -15,6 +15,10 @@ class VPUFileUpload extends VPULitElement { super(); this.lang = 'de'; this.url = ''; + this.dropArea = null; + this.accept = ''; + this.text = ''; + this.buttonLabel = ''; } /** @@ -24,6 +28,9 @@ class VPUFileUpload extends VPULitElement { return { lang: { type: String }, url: { type: String }, + accept: { type: String }, + text: { type: String }, + buttonLabel: { type: String, attribute: 'button-label'}, }; } @@ -38,56 +45,130 @@ class VPUFileUpload extends VPULitElement { super.update(changedProperties); } + connectedCallback() { + super.connectedCallback(); + + this.updateComplete.then(() => { + this.dropArea = this.shadowRoot.querySelector('#dropArea'); + ['dragenter', 'dragover', 'dragleave', 'drop'].forEach(eventName => { + this.dropArea.addEventListener(eventName, this.preventDefaults, false) + }); + ['dragenter', 'dragover'].forEach(eventName => { + this.dropArea.addEventListener(eventName, this.highlight.bind(this), false) + }); + ['dragleave', 'drop'].forEach(eventName => { + this.dropArea.addEventListener(eventName, this.unhighlight.bind(this), false) + }); + this.dropArea.addEventListener('drop', this.handleDrop.bind(this), false); + this.shadowRoot.querySelector('#fileElem').addEventListener('change', this.handleChange.bind(this)); + + }); + } + + preventDefaults (e) { + e.preventDefault(); + e.stopPropagation(); + } + + highlight(e) { + this.dropArea.classList.add('highlight') + } + + unhighlight(e) { + this.dropArea.classList.remove('highlight') + } + + handleDrop(e) { + let dt = e.dataTransfer; + console.dir(dt); + let files = dt.files; + + this.handleFiles(files); + } + + handleChange(e) { + this.handleFiles(this.shadowRoot.querySelector('#fileElem').files); + } + + handleFiles(files) { + console.log('handleFiles: files.length = ' + files.length); + ([...files]).forEach(this.uploadFile.bind(this)) + } + + sendFinishedEvent(status, filename) { + const data = { + status: status, + filename: filename + }; + const event = new CustomEvent("vpu-fileupload-finished", { "detail": data, bubbles: true, composed: true }); + this.dispatchEvent(event); + } + + uploadFile(file) { + let url = this.url; + let formData = new FormData(); + + formData.append('my_file', file); + + fetch(url, { + method: 'POST', + body: formData + }) + .then((response) => { + /* Done. Inform the user */ + console.log(`Status: ${response.status} for file ${file.name}`); + this.sendFinishedEvent(response.status, file.name); + }) + .catch((response) => { + /* Error. Inform the user */ + console.log(`Status: ${response.status} for file ${file.name}`); + this.sendFinishedEvent(response.status, file.name); + }) + } + + static get styles() { + // language=css + return css` + #dropArea { + border: var(--FUBorderWidth, 2px) var(--FUBorderStyle, dashed) var(--FUBBorderColor, #ccc); + border-radius: var(--FUBorderRadius, 0); + width: var(--FUWidth, auto); + margin: var(--FUMargin, 10px); + padding: var(--FUPadding, 20px); + } + #dropArea.highlight { + border-color: var(--FUBorderColorHighlight, purple); + } + p { + margin-top: 0; + } + .my-form { + margin-bottom: 10px; + } + .button { + display: inline-block; + padding: 10px; + background: #ccc; + cursor: pointer; + border-radius: calc(var(--FUBorderRadius, 0)/2); + border: 1px solid #ccc; + } + .button:hover { + background: #ddd; + } + #fileElem { + display: none; + } + `; + } render() { return html` - <style> - #drop-area { - border: 2px dashed #ccc; - border-radius: 20px; - width: 480px; - font-family: sans-serif; - margin: 100px auto; - padding: 20px; - } - #drop-area.highlight { - border-color: purple; - } - p { - margin-top: 0; - } - .my-form { - margin-bottom: 10px; - } - #gallery { - margin-top: 10px; - } - #gallery img { - width: 150px; - margin-bottom: 10px; - margin-right: 10px; - vertical-align: middle; - } - .button { - display: inline-block; - padding: 10px; - background: #ccc; - cursor: pointer; - border-radius: 5px; - border: 1px solid #ccc; - } - .button:hover { - background: #ddd; - } - #fileElem { - display: none; - } - </style> - <div id="drop-area"> + <div id="dropArea"> <form class="my-form"> - <p>Upload multiple files with the file dialog or by dragging and dropping images onto the dashed region</p> - <input type="file" id="fileElem" multiple accept="image/*" name='my_file' onchange="handleFiles(this.files)"> - <label class="button" for="fileElem">Select some files</label> + <p>${this.text || i18n.t('intro')}</p> + <input type="file" id="fileElem" multiple accept="${ifDefined(this.accept)}" name='my_file'> + <label class="button" for="fileElem">${this.buttonLabel || i18n.t('upload-label')}</label> </form> </div> `;