import { __decorate, __metadata } from "tslib";
import { h, Host } from "@stencil/core";
import { ariaBooleanToString } from "../../utils/aria";
import { areArraysEqual } from "../../utils/array";
import { defaultBalAriaForm } from "../../utils/form";
import { inputHandleBlur, inputHandleFocus, inputHandleHostClick, inputSetBlur, inputSetFocus, stopEventBubbling, } from "../../utils/form-input";
import { Logger } from "../../utils/log";
import { FileListComponent } from "./components/file-list";
import { toFileArray, toFileList } from "./utils/file-list.util";
import { validateFileArray } from "./utils/file-validation.util";
export class FileUpload {
    constructor() {
        this.fileUploadId = `bal-file-upload-${FileUploadIds++}`;
        this.files = [];
        this.focused = false;
        this.ariaForm = defaultBalAriaForm;
        /**
         * PUBLIC PROPERTY API
         * ------------------------------------------------------
         */
        /**
         * The name of the control, which is submitted with the form data.
         */
        this.name = this.fileUploadId;
        /**
         * Input value.
         */
        this.value = [];
        this.initialValue = this.value || [];
        /**
         * Label of the drop area.
         */
        this.label = 'Choose or drop a file...';
        /**
         * If `true` multiple file upload is possible.
         */
        this.multiple = true;
        /**
         * If `true`, the element is not mutable, focusable, or even submitted with the form. The user can neither edit nor focus on the control, nor its form control descendants.
         */
        this.disabled = false;
        /**
         * If `true` the element can not mutated, meaning the user can not edit the control.
         */
        this.readonly = false;
        /**
         * If `true` the file upload is disabled and shows a spinner
         */
        this.loading = false;
        /**
         * If `true`, the user must fill in a value before submitting a form.
         */
        this.required = false;
        /**
         * If `true` below the drop-down area it generates a file list.
         */
        this.hasFileList = true;
        /**
         * If `true` the component gets a invalid style.
         */
        this.invalid = false;
        /**
         * If `true`, in Angular reactive forms the control will not be set invalid
         */
        this.autoInvalidOff = false;
        /**
         * PRIVATE METHODS
         * ------------------------------------------------------
         */
        this.addEventListenerDragAndDrop = () => {
            if (this.labelEl) {
                this.labelEl.addEventListener('dragenter', this.onDragenter, false);
                this.labelEl.addEventListener('dragover', this.onDragover, false);
                this.labelEl.addEventListener('drop', this.onDrop, false);
            }
        };
        this.removeEventListenerDragAndDrop = () => {
            if (this.labelEl) {
                this.labelEl.removeEventListener('dragenter', this.onDragenter, false);
                this.labelEl.removeEventListener('dragover', this.onDragover, false);
                this.labelEl.removeEventListener('drop', this.onDrop, false);
            }
        };
        this.handleFiles = (fileList) => {
            const files = toFileArray(fileList);
            const validatedFiles = validateFileArray(this.files, files, {
                accept: this.accept,
                maxFileSize: this.maxFileSize,
                maxBundleSize: this.maxBundleSize,
                maxFiles: this.maxFiles,
            });
            if (validatedFiles.invalidFiles.length > 0) {
                validatedFiles.invalidFiles.forEach(file => this.balRejectedFile.emit(file));
            }
            if (validatedFiles.validFiles.length > 0) {
                this.files = [...this.files, ...validatedFiles.validFiles];
                this.balChange.emit(this.files);
                this.balFilesAdded.emit(validatedFiles.validFiles);
            }
            this.updateFileInput();
        };
        this.updateFileInput = () => {
            var _a;
            if ((_a = this.nativeInput) === null || _a === void 0 ? void 0 : _a.files) {
                this.nativeInput.files = toFileList(this.files);
            }
        };
        /**
         * HANDLERS
         * ------------------------------------------------------
         */
        this.onDragenter = (ev) => {
            stopEventBubbling(ev);
        };
        this.onDragover = (ev) => {
            stopEventBubbling(ev);
        };
        this.onDrop = (ev) => {
            stopEventBubbling(ev);
            if (!this.disabled && !this.readonly && !this.loading) {
                const dataTransfer = ev.dataTransfer;
                if (dataTransfer) {
                    this.handleFiles(dataTransfer.files);
                }
            }
        };
        this.onInputChange = () => {
            var _a;
            if (!this.disabled && !this.readonly && !this.loading) {
                if ((_a = this.nativeInput) === null || _a === void 0 ? void 0 : _a.files) {
                    this.handleFiles(this.nativeInput.files);
                }
            }
        };
        this.onRemoveFile = (ev, index) => {
            stopEventBubbling(ev);
            if (index >= 0 && index < this.files.length) {
                const files = this.files;
                const removedFiles = files.splice(index, 1);
                this.balFilesRemoved.emit(removedFiles);
                this.files = [...files];
                this.balChange.emit(this.files);
                this.updateFileInput();
            }
        };
        this.onHostClick = (ev) => inputHandleHostClick(this, ev);
        this.onInputFocus = (ev) => inputHandleFocus(this, ev);
        this.onInputBlur = (ev) => inputHandleBlur(this, ev);
        this.onInputClick = (ev) => {
            if (this.nativeInput && !this.disabled && !this.readonly && !this.loading) {
                this.nativeInput.value = '';
            }
            this.balInputClick.emit(ev);
        };
    }
    createLogger(log) {
        this.log = log;
    }
    onValueChange() {
        if (!areArraysEqual(this.files, this.value)) {
            this.files = this.value;
        }
    }
    /**
     * LIFECYCLE
     * ------------------------------------------------------
     */
    componentWillLoad() {
        this.onValueChange();
    }
    connectedCallback() {
        this.initialValue = this.value || [];
        this.addEventListenerDragAndDrop();
    }
    componentDidLoad() {
        this.addEventListenerDragAndDrop();
    }
    disconnectedCallback() {
        this.removeEventListenerDragAndDrop();
    }
    resetHandler(ev) {
        const formElement = ev.target;
        if (formElement === null || formElement === void 0 ? void 0 : formElement.contains(this.el)) {
            this.files = [...this.initialValue];
            clearTimeout(this.resetHandlerTimer);
            this.resetHandlerTimer = setTimeout(() => {
                if (this.nativeInput) {
                    this.nativeInput.files = toFileList(this.initialValue);
                }
            });
        }
    }
    /**
     * PUBLIC METHODS
     * ------------------------------------------------------
     */
    /**
     * Sets the file list to an empty list
     */
    async clear() {
        this.files = [];
        this.updateFileInput();
    }
    /**
     * Sets focus on the native `input`. Use this method instead of the global
     * `input.focus()`.
     */
    async setFocus() {
        inputSetFocus(this);
    }
    /**
     * Sets blur on the native `input`. Use this method instead of the global
     * `input.blur()`.
     * @internal
     */
    async setBlur() {
        inputSetBlur(this);
    }
    /**
     * Returns the native `<input>` element used under the hood.
     */
    getInputElement() {
        return Promise.resolve(this.nativeInput);
    }
    /**
     * @internal
     */
    async setAriaForm(ariaForm) {
        this.ariaForm = Object.assign({}, ariaForm);
    }
    /**
     * RENDER
     * ------------------------------------------------------
     */
    render() {
        const id = this.ariaForm.controlId || this.fileUploadId;
        return (h(Host, { key: '777b6af9407be7027865f204a05be3b51977a8a7', onClick: this.onHostClick, "aria-disabled": ariaBooleanToString(this.disabled), class: {
                'bal-file-upload': true,
            } }, h("div", { key: 'a5a40cd9aa548183c4f0feaab16a30ac504da6c7', class: {
                'file': true,
                'is-disabled': this.disabled || this.readonly || this.loading,
                'is-danger': this.invalid,
            } }, h("label", { key: 'ed88786390952890387e357fd8d7f8e6a356fb02', htmlFor: id, ref: el => (this.labelEl = el), class: {
                'file-label': true,
                'is-disabled': this.disabled || this.loading || this.readonly,
            } }, h("input", { key: '79bcf9f330b894a80dbe44b30b99bb631aad9f6f', class: "file-input", type: "file", id: id, "aria-labelledby": this.ariaForm.labelId, "aria-describedby": this.ariaForm.messageId, "aria-invalid": this.invalid === true ? 'true' : 'false', "aria-disabled": ariaBooleanToString(this.disabled), name: this.name, multiple: this.multiple, disabled: this.disabled || this.loading || this.readonly, readonly: this.readonly, required: this.required, accept: this.accept, onClick: this.onInputClick, onChange: this.onInputChange, onFocus: this.onInputFocus, onBlur: this.onInputBlur, ref: el => (this.nativeInput = el), "data-testid": "bal-file-upload-input" }), this.loading ? (h("span", { class: "file-cta" }, h("bal-spinner", null))) : (h("span", { class: "file-cta" }, h("span", { class: "file-icon" }, h("bal-icon", { name: "upload", size: "medium", color: this.disabled || this.loading || this.readonly ? 'grey-light' : this.invalid ? 'danger' : 'blue' })), h("span", { class: "file-label", "data-testid": "bal-file-upload-label" }, this.label))))), this.hasFileList && this.files.length > 0 ? (h(FileListComponent, { files: this.files, disabled: this.disabled || this.readonly || this.loading, subTitle: this.subTitle, onRemoveFile: this.onRemoveFile })) : ('')));
    }
    static get is() { return "bal-file-upload"; }
    static get originalStyleUrls() {
        return {
            "$": ["bal-file-upload.sass"]
        };
    }
    static get styleUrls() {
        return {
            "$": ["bal-file-upload.css"]
        };
    }
    static get properties() {
        return {
            "name": {
                "type": "string",
                "mutable": false,
                "complexType": {
                    "original": "string",
                    "resolved": "string",
                    "references": {}
                },
                "required": false,
                "optional": false,
                "docs": {
                    "tags": [],
                    "text": "The name of the control, which is submitted with the form data."
                },
                "getter": false,
                "setter": false,
                "reflect": false,
                "attribute": "name",
                "defaultValue": "this.fileUploadId"
            },
            "value": {
                "type": "unknown",
                "mutable": false,
                "complexType": {
                    "original": "File[]",
                    "resolved": "File[]",
                    "references": {
                        "File": {
                            "location": "global",
                            "id": "global::File"
                        }
                    }
                },
                "required": false,
                "optional": false,
                "docs": {
                    "tags": [],
                    "text": "Input value."
                },
                "getter": false,
                "setter": false,
                "defaultValue": "[]"
            },
            "label": {
                "type": "string",
                "mutable": false,
                "complexType": {
                    "original": "string",
                    "resolved": "string",
                    "references": {}
                },
                "required": false,
                "optional": false,
                "docs": {
                    "tags": [],
                    "text": "Label of the drop area."
                },
                "getter": false,
                "setter": false,
                "reflect": false,
                "attribute": "label",
                "defaultValue": "'Choose or drop a file...'"
            },
            "multiple": {
                "type": "boolean",
                "mutable": false,
                "complexType": {
                    "original": "boolean",
                    "resolved": "boolean",
                    "references": {}
                },
                "required": false,
                "optional": false,
                "docs": {
                    "tags": [],
                    "text": "If `true` multiple file upload is possible."
                },
                "getter": false,
                "setter": false,
                "reflect": false,
                "attribute": "multiple",
                "defaultValue": "true"
            },
            "disabled": {
                "type": "boolean",
                "mutable": false,
                "complexType": {
                    "original": "boolean",
                    "resolved": "boolean",
                    "references": {}
                },
                "required": false,
                "optional": false,
                "docs": {
                    "tags": [],
                    "text": "If `true`, the element is not mutable, focusable, or even submitted with the form. The user can neither edit nor focus on the control, nor its form control descendants."
                },
                "getter": false,
                "setter": false,
                "reflect": false,
                "attribute": "disabled",
                "defaultValue": "false"
            },
            "readonly": {
                "type": "boolean",
                "mutable": false,
                "complexType": {
                    "original": "boolean",
                    "resolved": "boolean",
                    "references": {}
                },
                "required": false,
                "optional": false,
                "docs": {
                    "tags": [],
                    "text": "If `true` the element can not mutated, meaning the user can not edit the control."
                },
                "getter": false,
                "setter": false,
                "reflect": false,
                "attribute": "readonly",
                "defaultValue": "false"
            },
            "loading": {
                "type": "boolean",
                "mutable": false,
                "complexType": {
                    "original": "boolean",
                    "resolved": "boolean",
                    "references": {}
                },
                "required": false,
                "optional": false,
                "docs": {
                    "tags": [],
                    "text": "If `true` the file upload is disabled and shows a spinner"
                },
                "getter": false,
                "setter": false,
                "reflect": false,
                "attribute": "loading",
                "defaultValue": "false"
            },
            "required": {
                "type": "boolean",
                "mutable": false,
                "complexType": {
                    "original": "boolean",
                    "resolved": "boolean",
                    "references": {}
                },
                "required": false,
                "optional": false,
                "docs": {
                    "tags": [],
                    "text": "If `true`, the user must fill in a value before submitting a form."
                },
                "getter": false,
                "setter": false,
                "reflect": false,
                "attribute": "required",
                "defaultValue": "false"
            },
            "accept": {
                "type": "string",
                "mutable": false,
                "complexType": {
                    "original": "string",
                    "resolved": "string",
                    "references": {}
                },
                "required": false,
                "optional": true,
                "docs": {
                    "tags": [],
                    "text": "Accepted MIME-Types like `image/png,image/jpeg`."
                },
                "getter": false,
                "setter": false,
                "reflect": false,
                "attribute": "accept"
            },
            "maxFiles": {
                "type": "number",
                "mutable": false,
                "complexType": {
                    "original": "number",
                    "resolved": "number",
                    "references": {}
                },
                "required": false,
                "optional": true,
                "docs": {
                    "tags": [],
                    "text": "Allowed number of files in the bundle."
                },
                "getter": false,
                "setter": false,
                "reflect": false,
                "attribute": "max-files"
            },
            "maxFileSize": {
                "type": "number",
                "mutable": false,
                "complexType": {
                    "original": "number",
                    "resolved": "number",
                    "references": {}
                },
                "required": false,
                "optional": true,
                "docs": {
                    "tags": [],
                    "text": "Allowed max file size in bytes."
                },
                "getter": false,
                "setter": false,
                "reflect": false,
                "attribute": "max-file-size"
            },
            "maxBundleSize": {
                "type": "number",
                "mutable": false,
                "complexType": {
                    "original": "number",
                    "resolved": "number",
                    "references": {}
                },
                "required": false,
                "optional": true,
                "docs": {
                    "tags": [],
                    "text": "Allowed max bundle size in bytes."
                },
                "getter": false,
                "setter": false,
                "reflect": false,
                "attribute": "max-bundle-size"
            },
            "hasFileList": {
                "type": "boolean",
                "mutable": false,
                "complexType": {
                    "original": "boolean",
                    "resolved": "boolean",
                    "references": {}
                },
                "required": false,
                "optional": false,
                "docs": {
                    "tags": [],
                    "text": "If `true` below the drop-down area it generates a file list."
                },
                "getter": false,
                "setter": false,
                "reflect": false,
                "attribute": "has-file-list",
                "defaultValue": "true"
            },
            "invalid": {
                "type": "boolean",
                "mutable": false,
                "complexType": {
                    "original": "boolean",
                    "resolved": "boolean",
                    "references": {}
                },
                "required": false,
                "optional": false,
                "docs": {
                    "tags": [],
                    "text": "If `true` the component gets a invalid style."
                },
                "getter": false,
                "setter": false,
                "reflect": false,
                "attribute": "invalid",
                "defaultValue": "false"
            },
            "subTitle": {
                "type": "unknown",
                "mutable": false,
                "complexType": {
                    "original": "(file: File) => string",
                    "resolved": "(file: File) => string",
                    "references": {
                        "File": {
                            "location": "global",
                            "id": "global::File"
                        }
                    }
                },
                "required": false,
                "optional": true,
                "docs": {
                    "tags": [],
                    "text": "Overrides the default subtitle file size"
                },
                "getter": false,
                "setter": false
            },
            "autoInvalidOff": {
                "type": "boolean",
                "mutable": false,
                "complexType": {
                    "original": "boolean",
                    "resolved": "boolean",
                    "references": {}
                },
                "required": false,
                "optional": false,
                "docs": {
                    "tags": [],
                    "text": "If `true`, in Angular reactive forms the control will not be set invalid"
                },
                "getter": false,
                "setter": false,
                "reflect": true,
                "attribute": "auto-invalid-off",
                "defaultValue": "false"
            }
        };
    }
    static get states() {
        return {
            "files": {},
            "focused": {},
            "ariaForm": {}
        };
    }
    static get events() {
        return [{
                "method": "balChange",
                "name": "balChange",
                "bubbles": true,
                "cancelable": true,
                "composed": true,
                "docs": {
                    "tags": [],
                    "text": "Triggers when a file is added or removed."
                },
                "complexType": {
                    "original": "BalEvents.BalFileUploadChangeDetail",
                    "resolved": "File[]",
                    "references": {
                        "BalEvents": {
                            "location": "global",
                            "id": "global::BalEvents"
                        }
                    }
                }
            }, {
                "method": "balFilesAdded",
                "name": "balFilesAdded",
                "bubbles": true,
                "cancelable": true,
                "composed": true,
                "docs": {
                    "tags": [],
                    "text": "Triggers when a file is added."
                },
                "complexType": {
                    "original": "BalEvents.BalFileUploadFilesAddedDetail",
                    "resolved": "File[]",
                    "references": {
                        "BalEvents": {
                            "location": "global",
                            "id": "global::BalEvents"
                        }
                    }
                }
            }, {
                "method": "balFilesRemoved",
                "name": "balFilesRemoved",
                "bubbles": true,
                "cancelable": true,
                "composed": true,
                "docs": {
                    "tags": [],
                    "text": "Triggers when a file is removed."
                },
                "complexType": {
                    "original": "BalEvents.BalFileUploadFilesRemovedDetail",
                    "resolved": "File[]",
                    "references": {
                        "BalEvents": {
                            "location": "global",
                            "id": "global::BalEvents"
                        }
                    }
                }
            }, {
                "method": "balRejectedFile",
                "name": "balRejectedFile",
                "bubbles": true,
                "cancelable": true,
                "composed": true,
                "docs": {
                    "tags": [],
                    "text": "Triggers when a file is rejected due to not allowed MIME-Type and so on."
                },
                "complexType": {
                    "original": "BalEvents.BalFileUploadRejectedFileDetail",
                    "resolved": "FileUploadRejectedFile",
                    "references": {
                        "BalEvents": {
                            "location": "global",
                            "id": "global::BalEvents"
                        }
                    }
                }
            }, {
                "method": "balInputClick",
                "name": "balInputClick",
                "bubbles": true,
                "cancelable": true,
                "composed": true,
                "docs": {
                    "tags": [],
                    "text": "Emitted when the input has clicked."
                },
                "complexType": {
                    "original": "BalEvents.BalFileUploadInputClickDetail",
                    "resolved": "MouseEvent",
                    "references": {
                        "BalEvents": {
                            "location": "global",
                            "id": "global::BalEvents"
                        }
                    }
                }
            }, {
                "method": "balBlur",
                "name": "balBlur",
                "bubbles": true,
                "cancelable": true,
                "composed": true,
                "docs": {
                    "tags": [],
                    "text": "Emitted when the input loses focus."
                },
                "complexType": {
                    "original": "BalEvents.BalFileUploadBlurDetail",
                    "resolved": "FocusEvent",
                    "references": {
                        "BalEvents": {
                            "location": "global",
                            "id": "global::BalEvents"
                        }
                    }
                }
            }, {
                "method": "balFocus",
                "name": "balFocus",
                "bubbles": true,
                "cancelable": true,
                "composed": true,
                "docs": {
                    "tags": [],
                    "text": "Emitted when the input has focus."
                },
                "complexType": {
                    "original": "BalEvents.BalFileUploadFocusDetail",
                    "resolved": "FocusEvent",
                    "references": {
                        "BalEvents": {
                            "location": "global",
                            "id": "global::BalEvents"
                        }
                    }
                }
            }];
    }
    static get methods() {
        return {
            "clear": {
                "complexType": {
                    "signature": "() => Promise<void>",
                    "parameters": [],
                    "references": {
                        "Promise": {
                            "location": "global",
                            "id": "global::Promise"
                        }
                    },
                    "return": "Promise<void>"
                },
                "docs": {
                    "text": "Sets the file list to an empty list",
                    "tags": []
                }
            },
            "setFocus": {
                "complexType": {
                    "signature": "() => Promise<void>",
                    "parameters": [],
                    "references": {
                        "Promise": {
                            "location": "global",
                            "id": "global::Promise"
                        }
                    },
                    "return": "Promise<void>"
                },
                "docs": {
                    "text": "Sets focus on the native `input`. Use this method instead of the global\n`input.focus()`.",
                    "tags": []
                }
            },
            "setBlur": {
                "complexType": {
                    "signature": "() => Promise<void>",
                    "parameters": [],
                    "references": {
                        "Promise": {
                            "location": "global",
                            "id": "global::Promise"
                        }
                    },
                    "return": "Promise<void>"
                },
                "docs": {
                    "text": "Sets blur on the native `input`. Use this method instead of the global\n`input.blur()`.",
                    "tags": [{
                            "name": "internal",
                            "text": undefined
                        }]
                }
            },
            "getInputElement": {
                "complexType": {
                    "signature": "() => Promise<HTMLInputElement | undefined>",
                    "parameters": [],
                    "references": {
                        "Promise": {
                            "location": "global",
                            "id": "global::Promise"
                        },
                        "HTMLInputElement": {
                            "location": "global",
                            "id": "global::HTMLInputElement"
                        }
                    },
                    "return": "Promise<HTMLInputElement>"
                },
                "docs": {
                    "text": "Returns the native `<input>` element used under the hood.",
                    "tags": []
                }
            },
            "setAriaForm": {
                "complexType": {
                    "signature": "(ariaForm: BalAriaForm) => Promise<void>",
                    "parameters": [{
                            "name": "ariaForm",
                            "type": "BalAriaForm",
                            "docs": ""
                        }],
                    "references": {
                        "Promise": {
                            "location": "global",
                            "id": "global::Promise"
                        },
                        "BalAriaForm": {
                            "location": "import",
                            "path": "../../utils/form",
                            "id": "src/utils/form.ts::BalAriaForm"
                        }
                    },
                    "return": "Promise<void>"
                },
                "docs": {
                    "text": "",
                    "tags": [{
                            "name": "internal",
                            "text": undefined
                        }]
                }
            }
        };
    }
    static get elementRef() { return "el"; }
    static get watchers() {
        return [{
                "propName": "value",
                "methodName": "onValueChange"
            }];
    }
    static get listeners() {
        return [{
                "name": "reset",
                "method": "resetHandler",
                "target": "document",
                "capture": true,
                "passive": false
            }];
    }
}
__decorate([
    Logger('bal-file-upload'),
    __metadata("design:type", Function),
    __metadata("design:paramtypes", [Function]),
    __metadata("design:returntype", void 0)
], FileUpload.prototype, "createLogger", null);
let FileUploadIds = 0;
