Source: map/importer/ui/FileUi.js

import { UiElement } from "../../../ui/UiElement.js";
import { FileDropTarget } from "../../../ui/dnd/FileDropTarget.js";
import { FileBinder } from "../binder/FileBinder.js";
import "./FileUi.css";

export { FileUi };

/**
 * Ui class for importing File based layers
 * 
 * @memberof vef.map.importer.ui
 */
class FileUi extends UiElement {

    title = "File";

    classes = ["importer-ui", "file-ui"];
    html = `
        <div class="url-input-form">
            <input spellcheck="false" type="text" placeholder="Insert File URL..." class="url-input"/><!--
         --><button class="url-input-button"><i class="fas fa-plus"></i></button>
        </div>
        <div class="file-list"></div>
        <p class="file-drop-area">
            <i class="fas fa-upload"></i><br/>
            Drop Files here<br/>
            or<br/>
            <a href="#" class="browse-file-link">Browse Files</a><br/><br/>
            <i style="font-size:13px; color:#BBB;">Tab/Comma-Separated and GeoJSON Files are Supported</i>
        </p>
        <div class="button-group">
            <button class="btn-add" disabled="true"><i class="fas fa-plus"></i> Add</button>
        </div>
    `;

    constructor(target, fileOptions) {
        super(target, {
            "add_layers": [],
            "close": []
        });

        this.fileOptions_ = Object.assign({}, fileOptions);
        this.files_ = [];

        this.setHtml(this.html);
        this.setClass(this.classes);
        this.initEvents_();
    }

    addFile_(file) {
        if (!(file instanceof File) && (typeof file != "string")) return;
        const filename = (typeof file == "string") ? file : file.name;
        const type = FileBinder.guessFileType(filename);

        const list = this.query(".file-list");
        const entry = document.createElement("div");
        entry.classList.add("file-list-entry");
        entry.innerHTML = `
            <i class="fas fa-times"></i>
            <div class="filename"></div>
            <div class="status"></div>
            <select class="type-dropdown">
                <option value="geojson">GeoJSON</option>
                <option value="csv">CSV</option>
            </select>
        `;

        entry.querySelector(".filename").innerText = filename;
        entry.querySelector(".type-dropdown").value = type;
        list.appendChild(entry);

        const item = {
            type: type,
            filename: filename,
            status: "new",
            file: file,
            element: entry
        };

        entry.querySelector(".fas.fa-times").onclick = () => {
            entry.remove();
            const index = this.files_.indexOf(item);
            if (index > -1) this.files_.splice(index, 1);
            this.checkAddButtonState_();
        };

        this.files_.push(item);
        this.checkAddButtonState_();
    }

    checkAddButtonState_() {
        let enabled = false;
        for (let i = 0; i < this.files_.length; ++i) {
            const file = this.files_[i];
            if (["new", "error"].includes(file.status)) {
                enabled = true;
                break;
            }
        }
        this.query(".button-group .btn-add").disabled = !enabled;
    }

    initEvents_() {
        this.query(".browse-file-link").addEventListener("click", e => {
            e.preventDefault();

            const fileInput = document.createElement("input");
            fileInput.type = "file";
            fileInput.onchange = fileEvent => {
                for (let i = 0; i < fileEvent.target.files.length; ++i) {
                    this.addFile_(fileEvent.target.files[i]);
                }
            };
            fileInput.click();
        });

        new FileDropTarget(this.query(".file-drop-area")).on("drop", files => {
            for (let i = 0; i < files.length; ++i) {
                this.addFile_(files[i]);
            }
        });

        this.query(".button-group .btn-add").addEventListener("click", e => {
            this.query(".button-group .btn-add").disabled = true;
            this.apply();
        });

        const urlInput = this.query(".url-input");
        const inputListener = () => {
            if (urlInput.value.length > 0) {
                this.addFile_(urlInput.value);
                urlInput.value = "";
            }
        }

        this.query(".url-input-button").onclick = inputListener;
        urlInput.onkeydown = e => {
            // "enter" key
            if (e.which == 13) inputListener();
        };

    }

    apply() {
        const promises = [];
        const result = [];
        let errors = false;
        for (let i = 0; i < this.files_.length; ++i) {
            const file = this.files_[i];
            if (file.status == "success") continue;

            const promise = FileBinder.loadFileLayer(file.file, this.fileOptions_, file.element.querySelector(".type-dropdown").value);
            promises.push(promise);

            const statusIcon = file.element.querySelector(".status");
            statusIcon.innerHTML = '<i style="color:var(--primary-color);" class="fas fa-spinner fa-spin fa-fw"></i>'

            promise.then(data => {
                if (data) {
                    file.status = "success";
                    statusIcon.innerHTML = '<i style="color:green;" class="fas fa-check"></i>';
                    result.push(data);
                } else {
                    errors = true;
                    file.status = "error";
                    statusIcon.innerHTML = '<i title="could not load layers" style="color:red;" class="fas fa-exclamation-triangle"></i>'
                }
            }).catch(error => {
                console.warn(error);
            });
        }

        Promise.all(promises).then(() => {
            this.checkAddButtonState_();
            if (result.length > 0) this.fire("add_layers", result)
            if (!errors) {
                this.reset()
                this.fire("close");
            }
        });
    }

    reset() {
        this.query(".file-list").innerHTML = "";
        this.files_ = [];
        this.checkAddButtonState_();
    }

}