/*!
 * Built with Stencil
 * By PostNord.
 */
import { t as transformTag, r as registerInstance, c as createEvent, g as getElement, h, a as Host } from './index-CAEP792y.js';
import { uuidv4, getBytesFromHumanReadableFileSize, awaitTopbar, ripple, en } from './index.js';
import { a as alert_exclamation_circle } from './alert_exclamation_circle-B88w-Zxn.js';
import { c as check } from './check-BpI8peGn.js';

const icon$2 = '<svg class="pn-icon-svg" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24"><path fill="#000" fill-rule="evenodd" d="M9 7.5a4.5 4.5 0 1 1 9 0V16a6 6 0 0 1-12 0v-3a1 1 0 1 1 2 0v3a4 4 0 0 0 8 0V7.5a2.5 2.5 0 0 0-5 0V16a1 1 0 1 0 2 0v-6a1 1 0 1 1 2 0v6a3 3 0 1 1-6 0zM7 9a1 1 0 0 1 1 1v.1a1 1 0 1 1-2 0V10a1 1 0 0 1 1-1" clip-rule="evenodd"/></svg>';
const attachment = icon$2;

const icon$1 = '<svg class="pn-icon-svg" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24"><path fill="#000" fill-rule="evenodd" d="M10.5 4a1 1 0 0 0-1 1h5a1 1 0 0 0-1-1zm6 1a3 3 0 0 0-3-3h-3a3 3 0 0 0-3 3h-2A2.5 2.5 0 0 0 5 9.95V19a3 3 0 0 0 3 3h8a3 3 0 0 0 3-3V9.95A2.5 2.5 0 0 0 18.5 5zm.5 5H7v9a1 1 0 0 0 1 1h8a1 1 0 0 0 1-1zM5.5 7a.5.5 0 0 0 0 1h13a.5.5 0 0 0 0-1zm4.5 5a1 1 0 0 1 1 1v4a1 1 0 1 1-2 0v-4a1 1 0 0 1 1-1m4 0a1 1 0 0 1 1 1v4a1 1 0 1 1-2 0v-4a1 1 0 0 1 1-1" clip-rule="evenodd"/></svg>';
const trash_can = icon$1;

const icon = '<svg class="pn-icon-svg" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24"><path fill="#000" fill-rule="evenodd" d="M3 8a3 3 0 0 1 3-3h12a3 3 0 0 1 3 3v7a3 3 0 0 1-3 3h-2a1 1 0 1 1 0-2h2a1 1 0 0 0 1-1V8a1 1 0 0 0-1-1H6a1 1 0 0 0-1 1v7a1 1 0 0 0 1 1h2a1 1 0 1 1 0 2H6a3 3 0 0 1-3-3zm9 0a1 1 0 0 1 .707.293l4 4a1 1 0 0 1-1.414 1.414L13 11.414V17a1 1 0 1 1-2 0v-5.586l-2.293 2.293a1 1 0 0 1-1.414-1.414l4-4A1 1 0 0 1 12 8m-1 12.1V20a1 1 0 1 1 2 0v.1a1 1 0 1 1-2 0" clip-rule="evenodd"/></svg>';
const upload = icon;

class PnFileUploadError extends Error {
    element;
    code;
    data;
    constructor(message, element, code = 'INVALID', data = null) {
        super(message);
        this.name = 'PNFileUploadError';
        this.element = element;
        this.code = code;
        this.data = data;
    }
}

const translations = {
    CLICKORDRAG: {
        sv: 'Klicka eller dra en fil hit',
        en: 'Click or drag a file here',
        da: 'Klik eller træk en fil hertil',
        fi: 'Klikkaa tai vedä tiedosto tähän',
        no: 'Klikk eller dra en fil hit',
    },
    ACCEPTEDFORMATS: {
        sv: 'Filformat som stöds',
        en: 'Supported formats',
        da: 'Støttede filformater',
        fi: 'Tuetut tiedostomuodot',
        no: 'Understøttede filformater',
    },
    MAXFILESIZE: {
        sv: 'Max filstorlek',
        en: 'Max filesize',
        da: 'Max filstørrelse',
        fi: 'Maksimitiedostokoko',
        no: 'Maks filstørrelse',
    },
    REMOVE_FILE: {
        sv: 'Ta bort filen',
        en: 'Remove file',
        da: 'Fjern fil',
        fi: 'Poista tiedosto',
        no: 'Fjern fil',
    },
};

const pnFileUploadCss = () => `${transformTag("pn-file-upload")}.hover .pn-file-upload-container .pn-dropzone-container{background-color:#e0f8ff}${transformTag("pn-file-upload")} .pn-file-upload-input{position:absolute;z-index:1;left:0;top:0;opacity:0;height:100%;width:100%;-webkit-tap-highlight-color:transparent}${transformTag("pn-file-upload")} .pn-file-upload-input:hover{cursor:pointer}${transformTag("pn-file-upload")} .pn-file-upload-input:hover+.pn-dropzone-inner{border-color:#005d92;background-color:#e0f8ff}${transformTag("pn-file-upload")} .pn-file-upload-input:focus-visible+.pn-dropzone-inner{border-color:#005d92;outline-color:#005d92}${transformTag("pn-file-upload")} .pn-file-upload-input:disabled{pointer-events:none}${transformTag("pn-file-upload")} .pn-file-upload-input:disabled+.pn-dropzone-inner{pointer-events:none;background-color:#f3f2f2;border-color:#2d2013}${transformTag("pn-file-upload")} .pn-file-upload-input:disabled+.pn-dropzone-inner .headline a{color:#5e554a}${transformTag("pn-file-upload")} .pn-file-upload-input:disabled+.pn-dropzone-inner .pn-icon-svg path{fill:#5e554a}${transformTag("pn-file-upload")} .pn-file-upload-container{position:relative;display:flex;flex-direction:column;gap:1em}${transformTag("pn-file-upload")} .pn-file-upload-container .pn-dropzone-container{position:relative}${transformTag("pn-file-upload")} .pn-file-upload-container .pn-dropzone-inner{position:relative;text-align:center;overflow:hidden;padding:2.5em;border:0.0625em #969087 dashed;border-radius:0.5em;display:flex;flex-direction:column;align-items:center;gap:0.5em;outline:0.2rem solid transparent;outline-offset:0.2rem;transition-property:background-color, outline-color, border-color;transition-duration:0.2s;transition-timing-function:cubic-bezier(0.7, 0, 0.3, 1)}@media (prefers-reduced-motion: reduce){${transformTag("pn-file-upload")} .pn-file-upload-container .pn-dropzone-inner{transition-duration:0s;transition-delay:0s}}${transformTag("pn-file-upload")} .pn-file-upload-container .pn-dropzone-inner .pn-ripple{animation:ripple 0.4s cubic-bezier(0.7, 0, 0.3, 1);position:absolute;border-radius:50%;background-color:#005d92;transform:translate(-50%, -50%) scale(0);opacity:0.1;pointer-events:none;z-index:3}@keyframes ripple{to{transform:translate(-50%, -50%) scale(1);opacity:0}}${transformTag("pn-file-upload")} .pn-file-upload-container .pn-dropzone-inner[data-hover]{background-color:#e0f8ff;border-color:#005d92}${transformTag("pn-file-upload")} .pn-file-upload-container .pn-dropzone-inner[data-invalid]{border-color:#a70707}${transformTag("pn-file-upload")} .pn-file-upload-container .pn-dropzone-inner .pn-file-upload-dropzone{display:flex;flex-direction:row;align-items:center;justify-content:center;gap:0.5em}${transformTag("pn-file-upload")} .files-list{list-style:none;padding:0;margin:0;display:flex;flex-direction:column;gap:1em}${transformTag("pn-file-upload")} .files-list .filename{font-weight:400;font-size:0.75em}${transformTag("pn-file-upload")} .files-list .files-list-item{display:flex;flex-direction:row;flex-wrap:nowrap;justify-content:space-between;align-items:center;background:#f9f8f8;padding:1em;border-radius:0.5em}${transformTag("pn-file-upload")} .files-list .files-list-item .file-list-item-title{display:flex;align-items:center;gap:0.5em;margin-right:0.5em}${transformTag("pn-file-upload")} .files-list .files-list-item>${transformTag("pn-progress-bar")}{margin-right:25em;flex:1}${transformTag("pn-file-upload")} .files-list .files-list-item.files-list-item-error{color:#a70707;background-color:#fdefee}${transformTag("pn-file-upload")} .files-list .files-list-item-text-error{color:#a70707;font-size:0.875em;font-weight:300;margin-top:0.25em}${transformTag("pn-file-upload")} .pn-file-upload-title{color:#2d2013}${transformTag("pn-file-upload")} .pn-file-upload-subtitle{color:#5e554a;font-size:0.75em}`;

const PnFileUpload = class {
    constructor(hostRef) {
        registerInstance(this, hostRef);
        this.filesAdded = createEvent(this, "filesAdded");
        this.update = createEvent(this, "update");
        this.fileuploaderror = createEvent(this, "fileuploaderror");
        this.valid = createEvent(this, "valid");
        this.uploadFile = createEvent(this, "uploadFile");
        this.uploadCancelled = createEvent(this, "uploadCancelled");
        this.uploadCompleted = createEvent(this, "uploadCompleted");
    }
    id = `pn-file-upload-${uuidv4()}`;
    idFileUpload = `${this.id}-label`;
    idHelpertext = `${this.id}-helpertext`;
    idAccepttext = `${this.id}-accept`;
    fileInputElement;
    invalid = false;
    maxSizeInBytes = 0;
    get hostElement() { return getElement(this); }
    files = [];
    isHovering = false;
    /** The default label is "Click or drag a file here". @since v7.1.0 */
    label;
    /**
     * The helpertext defaults to "Supported formats {x} and max filesize {y}",
     * if there is a value set for the prop `accept`.
     * @since v7.1.0
     **/
    helpertext;
    /** Manually set the language. @category Features */
    language = null;
    /** Always hide the progress bar, even when upload has begun. @category Features  */
    hideProgress = false;
    /**
     * Set a HTML name.
     * @since v7.1.0
     * @category HTML attributes
     */
    name;
    /** Set which file types the input accepts. Ex: .docs,.xml @category HTML attributes */
    accept;
    /** Set the maximum upload size. @category HTML attributes */
    maxSize = '';
    /**
     * Use the capture mode.
     * @since v7.1.0
     * @category HTML attributes
     */
    capture;
    /**
     * Set maximum amount of files to be selected.
     * Any value above 1 will force the `multiple` to be `true`.
     * @category HTML attributes
     **/
    limit = 1;
    /** Allow multiple files to be selected. Will be overwritten to true, if `limit` is above 1. @category HTML attributes */
    multiple;
    /** Set the input as required. @category HTML attributes */
    required = false;
    /** Disable the input. @category HTML attributes */
    disabled = false;
    handleLimit() {
        this.multiple = this.limit > 1;
    }
    handleMaxBytes() {
        this.maxSizeInBytes = getBytesFromHumanReadableFileSize(this.maxSize);
    }
    /** This event is emitted when you add files. Either via drag and drop or using the file explorer. */
    filesAdded;
    /** Emitted everytime an update is made to one of the files. */
    update;
    /** If any error occurs, such as: invalid file type, size, limit or error during upload. */
    fileuploaderror;
    /** Emitted everytime you select a valid file. It's a good idea to use this event in order to make use of the built in validation features. */
    valid;
    /** Emitted once the file upload has begun. */
    uploadFile;
    /** Emitted when an upload has been cancelled before completion. */
    uploadCancelled;
    /** Emitted once the upload is finished. */
    uploadCompleted;
    async componentWillLoad() {
        this.handleLimit();
        this.handleMaxBytes();
        if (this.language === null)
            await awaitTopbar(this.hostElement);
    }
    /** Start the upload of all selected files (excludes invalid files). */
    async startUpload() {
        if (!this.files.length)
            return;
        this.files = this.files.map((file, index) => {
            if (file.status === 'queue') {
                file.status = 'start';
                this.uploadFile.emit({
                    file: file,
                    index: index,
                    uploader: this,
                });
            }
            return file;
        });
        const promises = this.files.map(file => file.xhrPromise().catch(() => { }));
        return Promise.all(promises).then(data => {
            const added = data.filter(item => item);
            this.uploadCompleted.emit(added);
        });
    }
    /** Clear the selected files, state and input value. */
    async clearUpload() {
        this.files = [];
        this.fileInputElement.value = '';
        this.invalid = false;
        this.filesAdded.emit([]);
    }
    /** Remove the selected file. */
    async removeFile(targetFile) {
        let valid = true;
        const files = [];
        for (const fileIndex in this.files) {
            const file = this.files[fileIndex];
            if (!file.filename) {
                continue;
            }
            // add to files if the uuid doesn't match
            if (file.uuid !== targetFile.uuid) {
                try {
                    this._validateFileSize(file);
                    this._validateFileType(file);
                    valid = true;
                }
                catch (error) {
                    valid = false;
                    file.error = error;
                    file.progress = 0;
                    file.status = 'error';
                }
                files.push(file);
            }
            else if (file.status === 'start') {
                file.xhr.abort();
                this.uploadCancelled.emit(file);
            }
        }
        this.filesAdded.emit(files);
        this.files = files;
        this.invalid = !valid || !this._isNotGreaterThanLimit();
        this._checkValidity();
        this._emitUpdateEvent();
    }
    /** Update an existing file. */
    async updateFile(file, index = null) {
        let files = this.files;
        // find the index if not defined
        if (index === null) {
            for (const fIndex in files) {
                const fFile = files[fIndex];
                if (fFile.uuid === file.uuid) {
                    index = fIndex;
                    break;
                }
            }
        }
        if (files[index]) {
            if (file.progress >= 100) {
                file.progress = 100;
                file.status = 'completed';
            }
            else if (file.progress < 0) {
                file.status = 'start';
                file.progress = 0;
            }
            if (file.error) {
                file.progress = 0;
                file.status = 'error';
                file.errorMessage = file.errorMessage || 'There was an error uploading your file, please try again';
            }
            files[index] = file;
        }
        this.files = [...files];
    }
    async _addFilesFromFileList(fileList) {
        if (this.disabled || this.invalid) {
            return;
        }
        const files = Array.from(fileList);
        const promises = [];
        if (this.multiple) {
            files.forEach(file => promises.push(this._addFile(file)));
        }
        else {
            const file = files[0];
            if (this.limit === 1) {
                this.files = [];
                promises.push(this._addFile(file));
            }
            else {
                promises.push(this._addFile(file));
            }
        }
        return Promise.all(promises).then(() => {
            this.filesAdded.emit(this.files);
            this._isNotGreaterThanLimit();
            this._checkValidity();
            this._emitUpdateEvent();
        });
    }
    async _onAddFile() {
        const files = this.fileInputElement.files;
        return await this._addFilesFromFileList(files);
    }
    _onDragOverFile(event) {
        event.preventDefault();
        event.stopPropagation();
        this.isHovering = true;
    }
    _onDragLeaveFile(event) {
        event.preventDefault();
        event.stopPropagation();
        this.isHovering = false;
    }
    _onDropFile(event) {
        event.preventDefault();
        event.stopPropagation();
        this.isHovering = false;
        ripple(event, this.hostElement, '.pn-dropzone-inner');
        this._addFilesFromFileList(event.dataTransfer.files);
    }
    _validateFileType(file) {
        const accepts = this.accept ? this.accept.split(',') : [];
        if (accepts.length) {
            let valid = false;
            for (const accept of accepts) {
                const type = accept.trim().toLowerCase();
                const isMimeType = /^\w+\/[a-z\.\-]+$/i;
                const extension = file.filename.match(/\.\w{3,4}($|\?)/)?.[0]?.toLowerCase();
                if ((isMimeType.test(type) && file.contentType === type) || extension === type) {
                    valid = true;
                    break;
                }
            }
            if (!valid) {
                const error = new PnFileUploadError(`Invalid file type.`, this.hostElement, 'INVALID_FILE_TYPE', file);
                this.invalid = true;
                this.fileuploaderror.emit(error);
                throw error;
            }
        }
    }
    _validateFileSize(file) {
        if (this.maxSizeInBytes > 0 && file.filesize > this.maxSizeInBytes) {
            const error = new PnFileUploadError(`You can only add up to ${this.maxSize}.`, this.hostElement, 'MAX_FILE_SIZE', file);
            this.invalid = true;
            this.fileuploaderror.emit(error);
            throw error;
        }
    }
    _validateLimit() {
        if (this.limit && this.files.length > this.limit) {
            const error = new PnFileUploadError(`You can only add up to ${this.limit} files.`, this.hostElement, 'MAX_LIMIT', this.files);
            this.invalid = true;
            this.fileuploaderror.emit(error);
            throw error;
        }
    }
    _isNotGreaterThanLimit() {
        try {
            this._validateLimit();
            return true;
        }
        catch (error) {
            return false;
        }
    }
    _checkValidity() {
        if (!this.invalid) {
            this.valid.emit(this);
        }
        return !this.invalid;
    }
    _createBase64FromFile(file) {
        return new Promise((resolve, reject) => {
            const reader = new FileReader();
            reader.onload = () => resolve(reader.result);
            reader.onerror = error => reject(error);
            reader.onabort = () => reject('ABORTED');
            reader.readAsDataURL(file);
        });
    }
    async _addFile(file) {
        return this._createBase64FromFile(file).then(data => {
            const split = data.split(',');
            const base64 = split[1];
            const contentType = split[0].split(':')[1].split(';')[0];
            const request = new XMLHttpRequest();
            const uploadFile = {
                uuid: uuidv4(),
                filename: file.name,
                filesize: file.size,
                progress: 0,
                status: 'queue',
                base64,
                contentType,
                error: null,
                errorMessage: '',
                file,
                xhr: request,
            };
            try {
                this._validateFileSize(uploadFile);
                this._validateFileType(uploadFile);
            }
            catch (error) {
                uploadFile.error = error;
                uploadFile.progress = 0;
                uploadFile.status = 'error';
            }
            uploadFile.xhrPromise = () => {
                return new Promise((resolve, reject) => {
                    request.responseType = 'json';
                    request.addEventListener('progress', event => {
                        if (event.lengthComputable) {
                            uploadFile.progress = (event.loaded / event.total) * 100;
                            this.updateFile(uploadFile);
                        }
                        else {
                            uploadFile.progress = 100;
                            this.updateFile(uploadFile);
                        }
                    });
                    request.addEventListener('load', (response) => resolve(response.target.response));
                    request.addEventListener('error', error => {
                        uploadFile.error = error;
                        uploadFile.errorMessage = 'There was an error uploading your file, please try again';
                        this.updateFile(uploadFile);
                        const errorEvent = new PnFileUploadError('There was an error uploading your file, please try again', this.hostElement, 'UPLOAD_SERVER_ERROR', uploadFile);
                        this.fileuploaderror.emit(errorEvent);
                        reject(error);
                    });
                });
            };
            this.files = [...this.files, uploadFile];
            return uploadFile;
        });
    }
    _emitUpdateEvent() {
        this.update.emit({
            files: this.files,
            element: this.hostElement,
        });
    }
    translate(prop) {
        return translations?.[prop]?.[this.language || en] || prop;
    }
    isLoading(file) {
        return file.status === 'start';
    }
    getFileItemClass = (file) => {
        let itemClass = 'files-list-item';
        if (file.error) {
            itemClass += ' files-list-item-error';
        }
        return itemClass;
    };
    getFileIcon(file) {
        if (file.error)
            return alert_exclamation_circle;
        if (file.progress === 100)
            return check;
        return attachment;
    }
    getFileColor(file) {
        if (file.error)
            return 'warning';
        if (file.progress === 100)
            return 'green700';
        return 'gray900';
    }
    describedBy() {
        const list = [this.accept && this.idAccepttext, this.helpertext && this.idHelpertext].filter(Boolean);
        if (!list.length)
            return null;
        return list.join(',');
    }
    render() {
        return (h(Host, { key: 'bce5d5c3684f31a2841ecf631155ab384f318879' }, h("div", { key: '07abc549d0ad86fb818f446e4fa223e3b4ea6c84', class: "pn-file-upload-container" }, h("div", { key: '1a9d09dd8cf39db11dc741732a7c04276e10a6a9', class: "pn-dropzone-container" }, h("input", { key: '387071f47d9aa658929448b041285c01d5187da4', id: this.idFileUpload, class: "pn-file-upload-input", type: "file", name: this.name, multiple: this.multiple, accept: this.accept, capture: this.capture, disabled: this.disabled, required: this.required, "aria-describedby": this.describedBy(), "aria-invalid": this.invalid?.toString(), onClick: e => ripple(e, this.hostElement, '.pn-dropzone-inner'), onChange: () => this._onAddFile(), onDragOver: e => this._onDragOverFile(e), onDragLeave: e => this._onDragLeaveFile(e), onDrop: e => this._onDropFile(e), ref: el => (this.fileInputElement = el) }), h("div", { key: '71c627aa6044908835b0f6e2227d7744da76680e', class: "pn-dropzone-inner", "data-hover": this.isHovering, "data-invalid": this.invalid }, h("pn-icon", { key: 'a621fd0abb16c8c7b2efcb67932a0e50e12942dd', class: "pn-file-upload-icon-element", icon: upload, color: "blue700" }), h("label", { key: 'cc90d1af83fe3814b8ea9991bcdf85c40f48f874', htmlFor: this.idFileUpload, class: "pn-file-upload-title" }, this.label || this.translate('CLICKORDRAG')), this.helpertext && (h("p", { key: '8d4032bcd909516779d07f042be878105f3479a1', id: this.idHelpertext, class: "pn-file-upload-subtitle" }, this.helpertext)), this.accept && (h("p", { key: '715cc9ed4c8878068182a30affa8649c904f041d', id: this.idAccepttext, class: "pn-file-upload-subtitle" }, this.translate('ACCEPTEDFORMATS'), ": ", this.accept, this.maxSize && (h("span", { key: 'a217dcfd8528d86deb9aee37b7750eb574936a56' }, ' ', "(", this.translate('MAXFILESIZE'), ": ", this.maxSize, ")")))))), !!this.files.length && (h("ul", { key: 'c1a1cd0c816992c6f524eaf82296ad4fd3300a6b', class: "files-list" }, this.files.map((file) => (h("li", { class: "files-list-item-container", key: file.uuid }, h("div", { class: this.getFileItemClass(file) }, h("div", { class: "file-list-item-title" }, h("pn-icon", { icon: this.getFileIcon(file), color: this.getFileColor(file) }), this.hideProgress || file.progress === 0 ? (h("div", { class: "filename" }, file.filename)) : (h("pn-progress-bar", { label: file.filename, progress: file.progress, error: file.error && file.errorMessage }))), h("pn-button", { small: true, variant: "borderless", arialabel: `${this.translate('REMOVE_FILE')} ${file.filename}`, appearance: file.error ? 'warning' : 'light', icon: trash_can, iconOnly: true, tooltip: this.translate('REMOVE_FILE'), loading: this.isLoading(file), onClick: () => this.removeFile(file) })), file.error ? (h("div", { class: "files-list-item-text-error" }, file.errorMessage || file.error.message)) : null))))), h("slot", { key: '2690ccdc2c5e97a41c99a3b385e68739bb69cecb' }))));
    }
    static get watchers() { return {
        "limit": [{
                "handleLimit": 0
            }],
        "maxSize": [{
                "handleMaxBytes": 0
            }]
    }; }
};
PnFileUpload.style = pnFileUploadCss();

export { PnFileUpload as pn_file_upload };
