import * as i0 from '@angular/core';
import { Pipe, EventEmitter, inject, ChangeDetectorRef, booleanAttribute, ContentChildren, Input, Output, ViewEncapsulation, ChangeDetectionStrategy, Component, HostListener, ViewChildren, HostBinding, Injectable } from '@angular/core';
import { NG_VALUE_ACCESSOR } from '@angular/forms';
import * as i2$1 from '@ngx-translate/core';
import { TranslateModule } from '@ngx-translate/core';
import * as i1 from '@eui/components/eui-button';
import { EUI_BUTTON } from '@eui/components/eui-button';
import { EuiTemplateDirective } from '@eui/components/directives';
import { NgTemplateOutlet } from '@angular/common';
import * as i2 from '@eui/components/eui-icon';
import { EUI_ICON } from '@eui/components/eui-icon';
import { EUI_LABEL } from '@eui/components/eui-label';
import { HttpClient, HttpEventType } from '@angular/common/http';
import { tap, filter, map } from 'rxjs/operators';
import { pipe, zip, of, Observable } from 'rxjs';

/**
 * A pipe that transforms a file size in bytes into a human-readable format (e.g., KB, MB, GB).
 *
 * @example
 * {{ 1024 | filesize }}  // Outputs: "1.00 KB"
 * {{ 1048576 | filesize:1 }}  // Outputs: "1.0 MB"
 *
 * @param {number} bytes - The file size in bytes.
 * @param {number} [precision=2] - The number of decimal places to include in the output.
 * @returns {string} The formatted file size with the appropriate unit.
 */
class EuiFileSizePipe {
    constructor() {
        this.units = ['bytes', 'KB', 'MB', 'GB', 'TB', 'PB'];
    }
    /**
     * Method used to format a size.
     *
     * @param bytes Bytes to format.
     * @param precision Number of decimal
     * @returns String with formated size and unit.
     */
    transform(bytes = 0, precision = 2) {
        if (isNaN(parseFloat(String(bytes))) || !isFinite(bytes)) {
            return '?';
        }
        let unit = 0;
        while (bytes >= 1024) {
            bytes /= 1024;
            unit++;
        }
        return bytes.toFixed(+precision) + ' ' + this.units[unit];
    }
    static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.1", ngImport: i0, type: EuiFileSizePipe, deps: [], target: i0.ɵɵFactoryTarget.Pipe }); }
    static { this.ɵpipe = i0.ɵɵngDeclarePipe({ minVersion: "14.0.0", version: "21.2.1", ngImport: i0, type: EuiFileSizePipe, isStandalone: true, name: "filesize" }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.1", ngImport: i0, type: EuiFileSizePipe, decorators: [{
            type: Pipe,
            args: [{
                    name: 'filesize',
                }]
        }] });

/**
 * @description
 * Internal class used by `eui-file-upload` to display the files to be uploaded.
 */
class EuiFilePreviewComponent {
    constructor() {
        /**
         * Object matching extension and icon.
         */
        this.previewAsIconConfig = {
            avi: 'eui-file-video:eui-file',
            html: 'eui-file-html:eui-file',
            htm: 'eui-file-code:eui-file',
            js: 'eui-file-code:eui-file',
            json: 'eui-file-code:eui-file',
            mp3: 'eui-file-audio:eui-file',
            mp4: 'eui-file-video:eui-file',
            pdf: 'eui-file-pdf:eui-file',
            png: 'eui-file-png:eui-file',
            svg: 'eui-file-svg:eui-file',
            txt: 'eui-file-txt:eui-file',
            xml: 'eui-file-code:eui-file',
            jpeg: 'eui-file-jpg:eui-file',
            jpg: 'eui-file-jpg:eui-file',
            zip: 'eui-file-archive:eui-file',
            doc: 'eui-file-word:eui-file',
            docx: 'eui-file-word:eui-file',
            xls: 'eui-file-xls:eui-file',
            xlsx: 'eui-file-xls:eui-file',
            ppt: 'eui-file-ppt:eui-file',
            csv: 'eui-file-csv:eui-file',
            rtf: 'eui-file-txt:eui-file',
        };
        /**
         * Event emitted when the remove button is clicked.
         */
        this.removeFromList = new EventEmitter();
        this.isLoading = false;
        this.typeFromFileExtension = null;
        /**
         * Event emitted when a chosen file to upload is clicked.
         */
        // TODO: find the correct type or turn into a generic, https://www.typescriptlang.org/docs/handbook/2/generics.html
        // eslint-disable-next-line @typescript-eslint/no-explicit-any
        this.itemClick = new EventEmitter();
        /**
         * Whether a preview, image or icon, is displayed for a file to upload.
         *
         * @default true
         */
        this.hasPreview = true;
        /**
         * Whether the filename is displayed.
         *
         * @default true
         */
        this.isFilenameDisplayed = true;
        /**
         * Whether the filesize is displayed.
         *
         * @default true
         */
        this.isFilesizeDisplayed = true;
        /**
         * Whether the file type is displayed.
         *
         * @default true
         */
        this.isFiletypeDisplayed = true;
        /**
         * Whether a preview is displayed as image.
         *
         * @default true
         */
        this.hasPreviewAsImage = true;
        /**
         * Whether a preview is displayed as a file format icon.
         *
         * @default false
         */
        this.hasPreviewAsIcon = false;
        /**
         * Whether items can be clicked. `itemClick` event is emitted.
         *
         * @default false
         */
        this.isItemsClickable = false;
        /**
         * Whether the file is `File` or has been provided by the user as initial value.
         *
         * @default false
         */
        this.isFileObject = false;
        this.cd = inject(ChangeDetectorRef);
    }
    ngOnInit() {
        if (this.hasPreviewAsIcon) {
            this.hasPreviewAsImage = false;
        }
        if (this.file instanceof Blob) {
            const reader = new FileReader();
            reader.readAsArrayBuffer(this.file);
            // TODO: find the correct type or turn into a generic, https://www.typescriptlang.org/docs/handbook/2/generics.html
            // eslint-disable-next-line @typescript-eslint/no-explicit-any
            reader.onload = (event) => {
                this.url = event.target.result;
                this.fileExtension = this.getFileExtension(this.file.name);
                this.icon = this.getIconForMime(this.fileExtension);
                if (this.fileExtension === 'msg') {
                    this.typeFromFileExtension = 'application/vnd.ms-outlook';
                }
                const blob = new Blob([reader.result], { type: this.file.type });
                const imageUrl = URL.createObjectURL(blob);
                this.previewData = this.getPreviewForMime(this.fileExtension, imageUrl);
                this.cd.detectChanges();
            };
            reader.onloadstart = (event) => {
                this.isLoading = true;
                this.cd.detectChanges();
            };
            reader.onloadend = (event) => {
                this.isLoading = false;
                this.cd.detectChanges();
            };
        }
        else {
            this.url = this.file.url;
            this.fileExtension = this.getFileExtension(this.file.name);
            this.icon = this.getIconForMime(this.fileExtension);
            this.previewData = this.getPreviewForMime(this.fileExtension, this.url);
            this.cd.detectChanges();
        }
    }
    ngAfterViewInit() {
        this.templates.forEach((item) => {
            if (item.getType() === 'uploadedFile') {
                this.uploadedFileTemplate = item.template;
            }
        });
    }
    /**
     * Remove from the list handler.
     */
    onRemoveFromList() {
        this.removeFromList.emit(this.index);
    }
    /**
     * Click item handler.
     * @param e The item clicked.
     */
    onItemClick(e) {
        this.itemClick.emit(this.file);
        e.preventDefault();
    }
    /**
     * Method returning a file extension.
     *
     * @param fileName Filename of the file
     * @returns the extension as a string
     */
    getFileExtension(fileName) {
        const ext = fileName.split('.');
        return ext[ext.length - 1];
    }
    /**
     * Method that returns the preview for a file.
     *
     * @param fileExtension Extension of the file
     * @param imgUrl URL of the file
     * @returns The image URL of valid image otherwise an icon of the extension
     */
    getPreviewForMime(fileExtension, imgUrl) {
        const f = fileExtension.toLowerCase();
        const image = f === 'jpeg' || f === 'jpg' || f === 'png' || f === 'gif' ? true : false;
        const display = f === 'jpeg' || f === 'jpg' || f === 'png' || f === 'gif' ? imgUrl : this.getIconForMime(f);
        return { image, display };
    }
    /**
     * Method that return the icon for a MIME type.
     *
     * @param fileExtension Extension of the file
     * @returns An icon string
     */
    getIconForMime(fileExtension) {
        const f = fileExtension.toLowerCase();
        return !this.previewAsIconConfig[f] ? 'eui-file-empty-o' : this.previewAsIconConfig[f];
    }
    static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.1", ngImport: i0, type: EuiFilePreviewComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
    static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.1", type: EuiFilePreviewComponent, isStandalone: true, selector: "eui-file-preview", inputs: { file: "file", uploadedFileTemplate: "uploadedFileTemplate", index: "index", previewAsIconConfig: "previewAsIconConfig", hasPreview: ["hasPreview", "hasPreview", booleanAttribute], isFilenameDisplayed: ["isFilenameDisplayed", "isFilenameDisplayed", booleanAttribute], isFilesizeDisplayed: ["isFilesizeDisplayed", "isFilesizeDisplayed", booleanAttribute], isFiletypeDisplayed: ["isFiletypeDisplayed", "isFiletypeDisplayed", booleanAttribute], hasPreviewAsImage: ["hasPreviewAsImage", "hasPreviewAsImage", booleanAttribute], hasPreviewAsIcon: ["hasPreviewAsIcon", "hasPreviewAsIcon", booleanAttribute], isItemsClickable: ["isItemsClickable", "isItemsClickable", booleanAttribute], isFileObject: ["isFileObject", "isFileObject", booleanAttribute] }, outputs: { removeFromList: "removeFromList", itemClick: "itemClick" }, queries: [{ propertyName: "templates", predicate: EuiTemplateDirective }], ngImport: i0, template: "@if (uploadedFileTemplate) {\n    <ng-template\n        [ngTemplateOutlet]=\"uploadedFileTemplate\"\n        [ngTemplateOutletContext]=\"{ $implicit: { file: { id: file.id, name: file.name, url: file.url || previewData?.display, size: file.size, type: file.type }, index, isFileObject } }\" />\n} @else {\n    <div class=\"eui-file-upload__preview\">\n        <div class=\"eui-file-upload__preview--left\">\n            @if (hasPreview && hasPreviewAsImage) {\n                <div class=\"eui-file-upload__preview--left__image\">\n                    @if (url && previewData.image) {\n                        <img [src]=\"previewData.display\" alt=\"File preview image\" />\n                    }\n                    @if (previewData && !previewData.image) {\n                        <eui-icon-svg icon=\"{{ previewData.display }}\" size=\"l\" class=\"eui-u-mr-s\"></eui-icon-svg>\n                    }\n                    @if (isLoading) {\n                        <div class=\"loading-container\">\n                            <eui-icon-svg isLoading size=\"2xl\"></eui-icon-svg>\n                        </div>\n                    }\n                </div>\n            }\n            @if (hasPreview && hasPreviewAsIcon) {\n                <div>\n                    <eui-icon-svg icon=\"{{ icon }}\" size=\"l\" class=\"eui-u-mr-s\"></eui-icon-svg>\n                </div>\n            }\n            @if (isFilenameDisplayed) {\n                <div class=\"eui-file-upload__preview--left__filename eui-u-text-truncate\">\n                    @if (!isItemsClickable) {\n                        {{ file.name }}\n                    }\n                    @if (isItemsClickable) {\n                        <a href=\"#\" (click)=\"onItemClick($event)\" class=\"eui-u-text-link eui-u-text-truncate\">{{ file.name }}</a>\n                    }\n                </div>\n            }\n        </div>\n        <div class=\"eui-file-upload__preview--right\">\n            @if (isFilesizeDisplayed) {\n                <div class=\"eui-u-text-no-wrap\">{{ file.size | filesize }}</div>\n            }\n            @if (isFiletypeDisplayed) {\n                <div>{{ file.type.length > 0 ? file.type : typeFromFileExtension }}</div>\n            }\n            <div>\n                <button euiButton euiDanger euiRounded euiIconButton euiBasicButton type=\"button\" (click)=\"onRemoveFromList()\" attr.aria-label=\"Remove {{ file.name }} from the list\">\n                    <eui-icon-svg icon=\"eui-trash\" />\n                </button>\n            </div>\n        </div>\n    </div>\n}\n", dependencies: [{ kind: "directive", type: NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }, { kind: "component", type: i1.EuiButtonComponent, selector: "button[euiButton], a[euiButton]", inputs: ["e2eAttr", "id", "euiBasicButton", "euiCTAButton", "euiBlockButton", "euiIconButton", "euiAvatarButton", "euiLineWrap", "isCompact", "hasNoFocusRing", "isChecked", "euiDisabled"], outputs: ["buttonClick"] }, { kind: "component", type: i2.EuiIconSvgComponent, selector: "eui-icon-svg, span[euiIconSvg], i[euiIconSvg]", inputs: ["icon", "fillColor", "set", "size", "style", "iconUrl", "transform", "ariaLabel", "ariaHidden", "focusable", "isLoading", "isInputIcon", "euiStart", "euiEnd"] }, { kind: "pipe", type: EuiFileSizePipe, name: "filesize" }], changeDetection: i0.ChangeDetectionStrategy.OnPush, encapsulation: i0.ViewEncapsulation.None }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.1", ngImport: i0, type: EuiFilePreviewComponent, decorators: [{
            type: Component,
            args: [{ selector: 'eui-file-preview', changeDetection: ChangeDetectionStrategy.OnPush, encapsulation: ViewEncapsulation.None, imports: [
                        NgTemplateOutlet,
                        ...EUI_BUTTON,
                        ...EUI_ICON,
                        ...EUI_LABEL,
                        EuiFileSizePipe,
                    ], template: "@if (uploadedFileTemplate) {\n    <ng-template\n        [ngTemplateOutlet]=\"uploadedFileTemplate\"\n        [ngTemplateOutletContext]=\"{ $implicit: { file: { id: file.id, name: file.name, url: file.url || previewData?.display, size: file.size, type: file.type }, index, isFileObject } }\" />\n} @else {\n    <div class=\"eui-file-upload__preview\">\n        <div class=\"eui-file-upload__preview--left\">\n            @if (hasPreview && hasPreviewAsImage) {\n                <div class=\"eui-file-upload__preview--left__image\">\n                    @if (url && previewData.image) {\n                        <img [src]=\"previewData.display\" alt=\"File preview image\" />\n                    }\n                    @if (previewData && !previewData.image) {\n                        <eui-icon-svg icon=\"{{ previewData.display }}\" size=\"l\" class=\"eui-u-mr-s\"></eui-icon-svg>\n                    }\n                    @if (isLoading) {\n                        <div class=\"loading-container\">\n                            <eui-icon-svg isLoading size=\"2xl\"></eui-icon-svg>\n                        </div>\n                    }\n                </div>\n            }\n            @if (hasPreview && hasPreviewAsIcon) {\n                <div>\n                    <eui-icon-svg icon=\"{{ icon }}\" size=\"l\" class=\"eui-u-mr-s\"></eui-icon-svg>\n                </div>\n            }\n            @if (isFilenameDisplayed) {\n                <div class=\"eui-file-upload__preview--left__filename eui-u-text-truncate\">\n                    @if (!isItemsClickable) {\n                        {{ file.name }}\n                    }\n                    @if (isItemsClickable) {\n                        <a href=\"#\" (click)=\"onItemClick($event)\" class=\"eui-u-text-link eui-u-text-truncate\">{{ file.name }}</a>\n                    }\n                </div>\n            }\n        </div>\n        <div class=\"eui-file-upload__preview--right\">\n            @if (isFilesizeDisplayed) {\n                <div class=\"eui-u-text-no-wrap\">{{ file.size | filesize }}</div>\n            }\n            @if (isFiletypeDisplayed) {\n                <div>{{ file.type.length > 0 ? file.type : typeFromFileExtension }}</div>\n            }\n            <div>\n                <button euiButton euiDanger euiRounded euiIconButton euiBasicButton type=\"button\" (click)=\"onRemoveFromList()\" attr.aria-label=\"Remove {{ file.name }} from the list\">\n                    <eui-icon-svg icon=\"eui-trash\" />\n                </button>\n            </div>\n        </div>\n    </div>\n}\n" }]
        }], propDecorators: { file: [{
                type: Input
            }], uploadedFileTemplate: [{
                type: Input
            }], index: [{
                type: Input
            }], previewAsIconConfig: [{
                type: Input
            }], removeFromList: [{
                type: Output
            }], itemClick: [{
                type: Output
            }], hasPreview: [{
                type: Input,
                args: [{ transform: booleanAttribute }]
            }], isFilenameDisplayed: [{
                type: Input,
                args: [{ transform: booleanAttribute }]
            }], isFilesizeDisplayed: [{
                type: Input,
                args: [{ transform: booleanAttribute }]
            }], isFiletypeDisplayed: [{
                type: Input,
                args: [{ transform: booleanAttribute }]
            }], hasPreviewAsImage: [{
                type: Input,
                args: [{ transform: booleanAttribute }]
            }], hasPreviewAsIcon: [{
                type: Input,
                args: [{ transform: booleanAttribute }]
            }], isItemsClickable: [{
                type: Input,
                args: [{ transform: booleanAttribute }]
            }], isFileObject: [{
                type: Input,
                args: [{ transform: booleanAttribute }]
            }], templates: [{
                type: ContentChildren,
                args: [EuiTemplateDirective]
            }] } });

/**
 * `eui-file-upload-progress` component for displaying file upload completion status.
 * Renders a visual indicator showing the percentage of upload progress.
 * Typically used internally by `eui-file-upload` but can be used standalone for custom upload implementations.
 */
class EuiFileUploadProgressComponent {
    constructor() {
        /**
         * Current upload progress percentage to display in the progress bar.
         * Value should be between 0 and 100, where 0 represents no progress and 100 represents completion.
         * @default 0
         */
        this.progress = 0;
    }
    static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.1", ngImport: i0, type: EuiFileUploadProgressComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
    static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.1", type: EuiFileUploadProgressComponent, isStandalone: true, selector: "eui-file-upload-progress", inputs: { progress: "progress" }, ngImport: i0, template: "@if (progress > 0) {\n    <div class=\"eui-file-upload__progress-bar-container\">\n        <div class=\"progress-bar\" [style.width]=\"progress + '%'\"></div>\n    </div>\n    <div>{{ progress }}%</div>\n}\n", changeDetection: i0.ChangeDetectionStrategy.OnPush, encapsulation: i0.ViewEncapsulation.None }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.1", ngImport: i0, type: EuiFileUploadProgressComponent, decorators: [{
            type: Component,
            args: [{ selector: 'eui-file-upload-progress', changeDetection: ChangeDetectionStrategy.OnPush, encapsulation: ViewEncapsulation.None, template: "@if (progress > 0) {\n    <div class=\"eui-file-upload__progress-bar-container\">\n        <div class=\"progress-bar\" [style.width]=\"progress + '%'\"></div>\n    </div>\n    <div>{{ progress }}%</div>\n}\n" }]
        }], propDecorators: { progress: [{
                type: Input
            }] } });

/**
 * @description
 * `eui-file-upload` component supporting both traditional file selection and drag-and-drop functionality.
 * Implements ControlValueAccessor for seamless integration with Angular reactive and template-driven forms.
 * `eui-file-upload` provides file preview, upload progress tracking, file type filtering, and multiple file management.
 * `eui-file-upload` displays uploaded files with customizable templates for previews, buttons, and progress indicators.
 *
 * @usageNotes
 * ### Basic file upload
 * ```html
 * <eui-file-upload
 *   [isMultiple]="true"
 *   accept=".pdf,.doc,.docx"
 *   [maxFiles]="5">
 * </eui-file-upload>
 * ```
 *
 * ### With form control
 * ```typescript
 * fileControl = new FormControl([]);
 * ```
 * ```html
 * <eui-file-upload
 *   [formControl]="fileControl"
 *   [hasDragArea]="true"
 *   [progress]="uploadProgress">
 * </eui-file-upload>
 * ```
 *
 * ### Accessibility
 * - Native file input is keyboard accessible
 * - Drag-and-drop area announces state changes to screen readers
 * - File list items are navigable and removable via keyboard
 *
 * ### Notes
 * - Use `accept` to filter file types (e.g., "image/*", ".pdf")
 * - Progress tracking requires external upload logic to update `progress` input
 * - Customize UI with templates: uploadedFile, browseButton, resetButton, progressBar
 * - Set `maxFiles` to limit simultaneous uploads
 */
class EuiFileUploadComponent {
    constructor() {
        /**
         * Data attribute used for end-to-end testing identification.
         * @default 'eui-file-upload'
         */
        this.e2eAttr = 'eui-file-upload';
        /**
         * Comma-separated list of allowed file extensions for upload filtering.
         * Files not matching the specified extensions will be rejected.
         * Use '*' to allow all file types.
         * @default '*'
         */
        this.accept = '*';
        /**
         * Emitted when files are dropped into the drag-and-drop area.
         * Triggered after files are processed and added to the upload queue.
         * No payload is emitted.
         */
        this.fileDrop = new EventEmitter();
        /**
         * Emitted when a file in the upload list is clicked.
         * Only triggers when isItemsClickable is true.
         * Payload: Blob, EuiUploadedFileInterface, or any file object representing the clicked item.
         */
        // TODO: find the correct type or turn into a generic, https://www.typescriptlang.org/docs/handbook/2/generics.html
        // eslint-disable-next-line @typescript-eslint/no-explicit-any
        this.itemClick = new EventEmitter();
        this.files = [];
        this.filesTotalSize = 0;
        /**
         * Random number generated to give an unique id to the field.
         */
        this.randomNumberId = Math.floor(Math.random() * 10000);
        /**
         * List of existing files to display in the component.
         *
         * @type {EuiUploadedFileInterface[]}
         */
        this.uploadedFiles = [];
        // eslint-disable-next-line
        this.dragEntered = false;
        /**
         * Enables selection of multiple files in a single upload operation.
         * When false, only one file can be selected at a time.
         * @default true
         */
        this.isMultiple = true;
        /**
         * Displays a progress bar indicating upload completion percentage.
         * Progress value must be provided via the progress input property.
         * @default true
         */
        this.hasProgressBar = true;
        /**
         * Displays a visual preview for each file in the upload list.
         * Preview type is controlled by hasPreviewAsImage and hasPreviewAsIcon properties.
         * @default true
         */
        this.hasPreview = true;
        /**
         * Renders file previews as thumbnail images for supported image file types.
         * Automatically disabled when hasPreviewAsIcon is true.
         * @default true
         */
        this.hasPreviewAsImage = true;
        /**
         * Renders file previews as file format icons instead of image thumbnails.
         * When enabled, automatically sets hasPreviewAsImage to false.
         * @default false
         */
        this.hasPreviewAsIcon = false;
        /**
         * Shows the total combined size of all files in the upload queue.
         * Provides users with visibility into total upload size.
         * @default true
         */
        this.hasTotalSizeDisplayed = true;
        /**
         * Displays a button to clear all files from the upload queue.
         * Only visible when isMultiple is true and files are present.
         * @default true
         */
        this.hasResetButton = true;
        /**
         * Enables drag-and-drop functionality for file selection.
         * Displays a drop zone area where users can drag files from their file system.
         * @default false
         */
        this.hasDragArea = false;
        /**
         * Makes files in the upload list clickable, emitting itemClick events on interaction.
         * Useful for implementing file preview or detail views.
         * @default false
         */
        this.isItemsClickable = false;
        // TODO: find the correct type or turn into a generic, https://www.typescriptlang.org/docs/handbook/2/generics.html
        // eslint-disable-next-line @typescript-eslint/no-explicit-any
        this.onChange = () => { };
    }
    /**
     * Sets the `class` attribute for the host element.
     *
     * @returns A string with all CSS classes applied.
     */
    get cssClasses() {
        return [
            'eui-file-upload',
        ]
            .join(' ')
            .trim();
    }
    /**
     * Drag and drop area listener.
     *
     * @param fileList `FileList` containing dropped file and initial file.
     */
    emitFiles(fileList) {
        let i = [...this.uploadedFiles, ...this.files].length;
        for (const property in fileList) {
            if (fileList[property] && typeof fileList[property] === 'object' && (!this.maxFiles || i < this.maxFiles)) {
                const fileName = fileList[property].name.split('.');
                const fileExtension = fileName[fileName.length - 1];
                if (this.accept.indexOf(fileExtension.toLowerCase()) !== -1 || this.accept === '*') {
                    this.files.push(fileList[property]);
                }
                i++;
            }
        }
        this.getTotalSize();
        // eslint-disable-next-line
        this.uploadedFiles ? this.onChange([...this.uploadedFiles, ...this.files]) : this.onChange(this.files);
        if (this.fileInputs) {
            this.fileInputs.first.nativeElement.value = '';
            this.fileInputs.last.nativeElement.value = '';
        }
        this.dragEntered = false;
        this.fileDrop.emit();
    }
    /**
     * Determines whether the reset button should be displayed.
     *
     * @returns {boolean} `true` if the reset button should be shown, otherwise `false`.
     */
    get showResetBtn() {
        return this.hasResetButton && this.isMultiple && (this.files.length > 0 || this.uploadedFiles.length > 0);
    }
    /**
     * Determines whether the drop area should be disabled.
     *
     * @returns {boolean} `true` if the drop area is disabled, otherwise `false`.
     */
    get isDropAreaDisabled() {
        return (!this.isMultiple && this.files.length === 1) || [...this.uploadedFiles, ...this.files].length === this.maxFiles;
    }
    ngOnInit() {
        if (this.hasPreviewAsIcon) {
            this.hasPreviewAsImage = false;
        }
    }
    ngAfterViewInit() {
        // TODO EUI19: Change detection to OnPush to get rid of setTimeout workaround.
        setTimeout(() => {
            this.templates.forEach((item) => {
                if (item.getType() === 'uploadedFile') {
                    this.uploadedFileTemplate = item.template;
                }
                if (item.getType() === 'browseButton') {
                    this.browseButtonTemplate = item.template;
                }
                if (item.getType() === 'resetButton') {
                    this.resetButtonTemplate = item.template;
                }
                if (item.getType() === 'progressBar') {
                    this.progressBarTemplate = item.template;
                }
            });
        });
    }
    onDragEnter() {
        if (!this.isDropAreaDisabled) {
            this.dragEntered = true;
        }
    }
    onDragLeave() {
        this.dragEntered = false;
    }
    /**
     * Remove a file from the array of files choosen by the user.
     *
     * @param index Index of the file to remove in the array.
     */
    removeFromList(index) {
        this.files = this.files.filter((f, i) => i !== index);
        if (this.files.length === 0) {
            this.progress = 0;
        }
        this.getTotalSize();
        this.onChange([...this.uploadedFiles, ...this.files]);
    }
    /**
     * Remove a file from the array of files provided to the component as existing values.
     *
     * @param index Index of the file to remove in the array.
     */
    removeFromUploadedList(index) {
        this.uploadedFiles = this.uploadedFiles.filter((f, i) => i !== index);
        if (this.files.length === 0) {
            this.progress = 0;
        }
        this.getTotalSize();
        this.onChange([...this.uploadedFiles, ...this.files]);
    }
    /**
     * Reset the list of files in the component.
     */
    resetList() {
        this.files = [];
        this.uploadedFiles = [];
        this.getTotalSize();
        this.progress = 0;
        this.onChange([...this.uploadedFiles, ...this.files]);
    }
    openBrowseWindow() {
        this.fileInputs.first.nativeElement.click();
    }
    /**
     * Click item handler.
     * @param e The item clicked. Will be a `Blob` for a file to be uploaded, `EuiUploadedFileInterface` for an initial file.
     */
    // TODO: find the correct type or turn into a generic, https://www.typescriptlang.org/docs/handbook/2/generics.html
    // eslint-disable-next-line @typescript-eslint/no-explicit-any
    onItemClick(e) {
        this.itemClick.emit(e);
    }
    // TODO: find the correct type or turn into a generic, https://www.typescriptlang.org/docs/handbook/2/generics.html
    // eslint-disable-next-line @typescript-eslint/no-explicit-any
    writeValue(value) {
        this.uploadedFiles = value || [];
        this.files = [];
        this.getTotalSize();
    }
    // TODO: find the correct type or turn into a generic, https://www.typescriptlang.org/docs/handbook/2/generics.html
    // eslint-disable-next-line @typescript-eslint/no-explicit-any
    registerOnChange(fn) {
        this.onChange = fn;
    }
    // TODO: find the correct type or turn into a generic, https://www.typescriptlang.org/docs/handbook/2/generics.html
    // eslint-disable-next-line @typescript-eslint/no-explicit-any
    registerOnTouched(fn) { }
    /**
     * Calculates the total size to be uploaded.
     */
    getTotalSize() {
        this.filesTotalSize = 0;
        this.files.forEach((f) => {
            this.filesTotalSize += f.size;
        });
    }
    static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.1", ngImport: i0, type: EuiFileUploadComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
    static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.1", type: EuiFileUploadComponent, isStandalone: true, selector: "eui-file-upload", inputs: { e2eAttr: "e2eAttr", progress: "progress", accept: "accept", maxFiles: "maxFiles", isMultiple: ["isMultiple", "isMultiple", booleanAttribute], hasProgressBar: ["hasProgressBar", "hasProgressBar", booleanAttribute], hasPreview: ["hasPreview", "hasPreview", booleanAttribute], hasPreviewAsImage: ["hasPreviewAsImage", "hasPreviewAsImage", booleanAttribute], hasPreviewAsIcon: ["hasPreviewAsIcon", "hasPreviewAsIcon", booleanAttribute], hasTotalSizeDisplayed: ["hasTotalSizeDisplayed", "hasTotalSizeDisplayed", booleanAttribute], hasResetButton: ["hasResetButton", "hasResetButton", booleanAttribute], hasDragArea: ["hasDragArea", "hasDragArea", booleanAttribute], isItemsClickable: ["isItemsClickable", "isItemsClickable", booleanAttribute] }, outputs: { fileDrop: "fileDrop", itemClick: "itemClick" }, host: { listeners: { "change": "emitFiles($any($event.target).files)" }, properties: { "class": "this.cssClasses" } }, providers: [
            {
                provide: NG_VALUE_ACCESSOR,
                useExisting: EuiFileUploadComponent,
                multi: true,
            },
        ], queries: [{ propertyName: "templates", predicate: EuiTemplateDirective }], viewQueries: [{ propertyName: "fileInputs", predicate: ["input"], descendants: true }], ngImport: i0, template: "@if (hasDragArea) {\n    <div\n        class=\"eui-file-upload__drop-area\"\n        [class.eui-file-upload__drop-area--active]=\"dragEntered\"\n        [class.eui-file-upload__drop-area--disabled]=\"isDropAreaDisabled\">\n        <span class=\"eui-file-upload__fake-btn\">{{ 'eui.euifileupload.CHOOSE-FILE' | translate }}</span>\n        <span>{{ 'eui.euifileupload.DRAG-AND-DROP-FILE-HERE' | translate }}</span>\n        <input\n            #input\n            class=\"eui-file-upload__file-input\"\n            attr.aria-label=\"{{ 'eui.euifileupload.CHOOSE-FILE' | translate }} 'or' {{\n                'eui.euifileupload.DRAG-AND-DROP-FILE-HERE' | translate\n            }}\"\n            type=\"file\"\n            accept=\"{{ accept }}\"\n            [attr.disabled]=\"isDropAreaDisabled ? 'disabled' : null\"\n            [multiple]=\"isMultiple\"\n            (dragenter)=\"onDragEnter()\"\n            (dragleave)=\"onDragLeave()\" />\n    </div>\n} @else {\n    <div class=\"eui-file-upload__simple-input-file\">\n        <label for=\"file-{{ randomNumberId }}\">\n            @if (browseButtonTemplate) {\n                <ng-template [ngTemplateOutlet]=\"browseButtonTemplate\"></ng-template>\n            } @else {\n                <button euiButton type=\"button\" (click)=\"openBrowseWindow()\">{{ 'eui.euifileupload.CHOOSE-FILE' | translate }}</button>\n            }\n        </label>\n    \n        @if (isMultiple) {\n            <input\n                #input\n                aria-label=\"Choose Multiple Files\"\n                class=\"file-input\"\n                id=\"file-{{ randomNumberId }}\"\n                type=\"file\"\n                accept=\"{{ accept }}\"\n                multiple />\n        } @else {\n            <input\n                #input\n                aria-label=\"Choose a single file\"\n                class=\"file-input\"\n                id=\"file-{{ randomNumberId }}\"\n                type=\"file\"\n                accept=\"{{ accept }}\" />\n        }\n    </div>\n}\n\n@if (hasProgressBar) {\n    @if (progressBarTemplate) {\n        <ng-template [ngTemplateOutlet]=\"progressBarTemplate\"\n            [ngTemplateOutletContext]=\"{ $implicit: { progress } }\" />\n    } @else {\n        <eui-file-upload-progress [progress]=\"progress\" />\n    }\n}\n\n@for (uploadedFile of uploadedFiles; let i = $index; track $index) {\n    <div>\n        <eui-file-preview\n            [isItemsClickable]=\"isItemsClickable\"\n            [index]=\"i\"\n            [file]=\"uploadedFile\"\n            [hasPreview]=\"hasPreview\"\n            [hasPreviewAsImage]=\"hasPreviewAsImage\"\n            [hasPreviewAsIcon]=\"hasPreviewAsIcon\"\n            [uploadedFileTemplate]=\"uploadedFileTemplate\"\n            (itemClick)=\"onItemClick($event)\"\n            (removeFromList)=\"removeFromUploadedList($event)\">\n        </eui-file-preview>\n    </div>\n}\n\n@for (file of files; let i = $index; track $index) {\n    <div>\n        <eui-file-preview\n            isFileObject\n            [isItemsClickable]=\"isItemsClickable\"\n            [index]=\"i\"\n            [file]=\"file\"\n            [hasPreview]=\"hasPreview\"\n            [hasPreviewAsImage]=\"hasPreviewAsImage\"\n            [hasPreviewAsIcon]=\"hasPreviewAsIcon\"\n            [uploadedFileTemplate]=\"uploadedFileTemplate\"\n            (itemClick)=\"onItemClick($event)\"\n            (removeFromList)=\"removeFromList($event)\">\n        </eui-file-preview>\n    </div>\n}\n\n@if (filesTotalSize && hasTotalSizeDisplayed) {\n    <div class=\"eui-file-upload__total-size\">\n        {{ filesTotalSize | filesize }} {{ 'eui.euifileupload.TOTAL-SIZE' | translate }}\n    </div>\n}\n\n<ng-content />\n\n@if (showResetBtn) {\n    @if (resetButtonTemplate) {\n        <ng-template [ngTemplateOutlet]=\"resetButtonTemplate\" />\n    } @else {\n        <button euiButton type=\"button\" (click)=\"resetList()\">\n            {{ 'eui.euifileupload.RESET-LIST' | translate }}\n        </button>\n    }\n}\n", styles: [".eui-21 .eui-file-upload__total-size{margin-bottom:1rem;font:var(--eui-f-m-semi-bold)}.eui-21 .eui-file-upload__drop-area{outline:1px dashed var(--eui-c-primary-base);border-radius:var(--eui-br-s);margin:0 0 10px;padding:30px;position:relative;-webkit-transition:.2s;transition:.2s}.eui-21 .eui-file-upload__drop-area--active{background-color:var(--eui-c-active-bg-light);outline:3px dashed var(--eui-c-primary-base);border-radius:var(--eui-br-s);margin:0 0 10px;padding:30px;position:relative;-webkit-transition:.2s;transition:.2s}.eui-21 .eui-file-upload__drop-area--disabled{opacity:var(--eui-o-75);cursor:not-allowed}.eui-21 .eui-file-upload__drop-area--disabled .file-input{cursor:default}.eui-21 .eui-file-upload__fake-btn{background-color:#fff;border:var(--eui-bw-xs) solid #9e9ec4;border-radius:var(--eui-br-s);margin-right:8px;padding:8px 15px}.eui-21 .eui-file-upload__file-input{height:100%;left:0;opacity:0;position:absolute;top:0;width:100%}.eui-21 .eui-file-upload__file-input:not(:disabled){cursor:pointer}.eui-21 .eui-file-upload__simple-input-file{margin-bottom:var(--eui-s-xs)}.eui-21 .eui-file-upload__simple-input-file .file-input{display:none}.eui-21 .eui-file-upload__preview{align-items:center;display:flex;justify-content:space-between;margin:5px 0}.eui-21 .eui-file-upload__preview--left{align-items:center;display:flex;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.eui-21 .eui-file-upload__preview--left__image{margin-right:var(--eui-s-m)}.eui-21 .eui-file-upload__preview--left__image img{width:180px}.eui-21 .eui-file-upload__preview--left__image .loading-container{text-align:center;width:180px}.eui-21 .eui-file-upload__preview--left__filename{display:block}.eui-21 .eui-file-upload__preview--right{align-items:center;display:flex;margin-left:auto;gap:var(--eui-s-m)}.eui-21 .eui-file-upload__progress-bar-container{margin-bottom:var(--eui-s-3xs)}.eui-21 .eui-file-upload__progress-bar-container .progress-bar{background-color:var(--eui-c-primary-base);height:var(--eui-s-3xs)}\n"], dependencies: [{ kind: "directive", type: NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }, { kind: "ngmodule", type: TranslateModule }, { kind: "component", type: EuiFilePreviewComponent, selector: "eui-file-preview", inputs: ["file", "uploadedFileTemplate", "index", "previewAsIconConfig", "hasPreview", "isFilenameDisplayed", "isFilesizeDisplayed", "isFiletypeDisplayed", "hasPreviewAsImage", "hasPreviewAsIcon", "isItemsClickable", "isFileObject"], outputs: ["removeFromList", "itemClick"] }, { kind: "component", type: EuiFileUploadProgressComponent, selector: "eui-file-upload-progress", inputs: ["progress"] }, { kind: "component", type: i1.EuiButtonComponent, selector: "button[euiButton], a[euiButton]", inputs: ["e2eAttr", "id", "euiBasicButton", "euiCTAButton", "euiBlockButton", "euiIconButton", "euiAvatarButton", "euiLineWrap", "isCompact", "hasNoFocusRing", "isChecked", "euiDisabled"], outputs: ["buttonClick"] }, { kind: "pipe", type: i2$1.TranslatePipe, name: "translate" }, { kind: "pipe", type: EuiFileSizePipe, name: "filesize" }], encapsulation: i0.ViewEncapsulation.None }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.1", ngImport: i0, type: EuiFileUploadComponent, decorators: [{
            type: Component,
            args: [{ selector: 'eui-file-upload', providers: [
                        {
                            provide: NG_VALUE_ACCESSOR,
                            useExisting: EuiFileUploadComponent,
                            multi: true,
                        },
                    ], encapsulation: ViewEncapsulation.None, imports: [
                        NgTemplateOutlet,
                        TranslateModule,
                        EuiFilePreviewComponent,
                        EuiFileUploadProgressComponent,
                        EuiFileSizePipe,
                        ...EUI_BUTTON,
                    ], template: "@if (hasDragArea) {\n    <div\n        class=\"eui-file-upload__drop-area\"\n        [class.eui-file-upload__drop-area--active]=\"dragEntered\"\n        [class.eui-file-upload__drop-area--disabled]=\"isDropAreaDisabled\">\n        <span class=\"eui-file-upload__fake-btn\">{{ 'eui.euifileupload.CHOOSE-FILE' | translate }}</span>\n        <span>{{ 'eui.euifileupload.DRAG-AND-DROP-FILE-HERE' | translate }}</span>\n        <input\n            #input\n            class=\"eui-file-upload__file-input\"\n            attr.aria-label=\"{{ 'eui.euifileupload.CHOOSE-FILE' | translate }} 'or' {{\n                'eui.euifileupload.DRAG-AND-DROP-FILE-HERE' | translate\n            }}\"\n            type=\"file\"\n            accept=\"{{ accept }}\"\n            [attr.disabled]=\"isDropAreaDisabled ? 'disabled' : null\"\n            [multiple]=\"isMultiple\"\n            (dragenter)=\"onDragEnter()\"\n            (dragleave)=\"onDragLeave()\" />\n    </div>\n} @else {\n    <div class=\"eui-file-upload__simple-input-file\">\n        <label for=\"file-{{ randomNumberId }}\">\n            @if (browseButtonTemplate) {\n                <ng-template [ngTemplateOutlet]=\"browseButtonTemplate\"></ng-template>\n            } @else {\n                <button euiButton type=\"button\" (click)=\"openBrowseWindow()\">{{ 'eui.euifileupload.CHOOSE-FILE' | translate }}</button>\n            }\n        </label>\n    \n        @if (isMultiple) {\n            <input\n                #input\n                aria-label=\"Choose Multiple Files\"\n                class=\"file-input\"\n                id=\"file-{{ randomNumberId }}\"\n                type=\"file\"\n                accept=\"{{ accept }}\"\n                multiple />\n        } @else {\n            <input\n                #input\n                aria-label=\"Choose a single file\"\n                class=\"file-input\"\n                id=\"file-{{ randomNumberId }}\"\n                type=\"file\"\n                accept=\"{{ accept }}\" />\n        }\n    </div>\n}\n\n@if (hasProgressBar) {\n    @if (progressBarTemplate) {\n        <ng-template [ngTemplateOutlet]=\"progressBarTemplate\"\n            [ngTemplateOutletContext]=\"{ $implicit: { progress } }\" />\n    } @else {\n        <eui-file-upload-progress [progress]=\"progress\" />\n    }\n}\n\n@for (uploadedFile of uploadedFiles; let i = $index; track $index) {\n    <div>\n        <eui-file-preview\n            [isItemsClickable]=\"isItemsClickable\"\n            [index]=\"i\"\n            [file]=\"uploadedFile\"\n            [hasPreview]=\"hasPreview\"\n            [hasPreviewAsImage]=\"hasPreviewAsImage\"\n            [hasPreviewAsIcon]=\"hasPreviewAsIcon\"\n            [uploadedFileTemplate]=\"uploadedFileTemplate\"\n            (itemClick)=\"onItemClick($event)\"\n            (removeFromList)=\"removeFromUploadedList($event)\">\n        </eui-file-preview>\n    </div>\n}\n\n@for (file of files; let i = $index; track $index) {\n    <div>\n        <eui-file-preview\n            isFileObject\n            [isItemsClickable]=\"isItemsClickable\"\n            [index]=\"i\"\n            [file]=\"file\"\n            [hasPreview]=\"hasPreview\"\n            [hasPreviewAsImage]=\"hasPreviewAsImage\"\n            [hasPreviewAsIcon]=\"hasPreviewAsIcon\"\n            [uploadedFileTemplate]=\"uploadedFileTemplate\"\n            (itemClick)=\"onItemClick($event)\"\n            (removeFromList)=\"removeFromList($event)\">\n        </eui-file-preview>\n    </div>\n}\n\n@if (filesTotalSize && hasTotalSizeDisplayed) {\n    <div class=\"eui-file-upload__total-size\">\n        {{ filesTotalSize | filesize }} {{ 'eui.euifileupload.TOTAL-SIZE' | translate }}\n    </div>\n}\n\n<ng-content />\n\n@if (showResetBtn) {\n    @if (resetButtonTemplate) {\n        <ng-template [ngTemplateOutlet]=\"resetButtonTemplate\" />\n    } @else {\n        <button euiButton type=\"button\" (click)=\"resetList()\">\n            {{ 'eui.euifileupload.RESET-LIST' | translate }}\n        </button>\n    }\n}\n", styles: [".eui-21 .eui-file-upload__total-size{margin-bottom:1rem;font:var(--eui-f-m-semi-bold)}.eui-21 .eui-file-upload__drop-area{outline:1px dashed var(--eui-c-primary-base);border-radius:var(--eui-br-s);margin:0 0 10px;padding:30px;position:relative;-webkit-transition:.2s;transition:.2s}.eui-21 .eui-file-upload__drop-area--active{background-color:var(--eui-c-active-bg-light);outline:3px dashed var(--eui-c-primary-base);border-radius:var(--eui-br-s);margin:0 0 10px;padding:30px;position:relative;-webkit-transition:.2s;transition:.2s}.eui-21 .eui-file-upload__drop-area--disabled{opacity:var(--eui-o-75);cursor:not-allowed}.eui-21 .eui-file-upload__drop-area--disabled .file-input{cursor:default}.eui-21 .eui-file-upload__fake-btn{background-color:#fff;border:var(--eui-bw-xs) solid #9e9ec4;border-radius:var(--eui-br-s);margin-right:8px;padding:8px 15px}.eui-21 .eui-file-upload__file-input{height:100%;left:0;opacity:0;position:absolute;top:0;width:100%}.eui-21 .eui-file-upload__file-input:not(:disabled){cursor:pointer}.eui-21 .eui-file-upload__simple-input-file{margin-bottom:var(--eui-s-xs)}.eui-21 .eui-file-upload__simple-input-file .file-input{display:none}.eui-21 .eui-file-upload__preview{align-items:center;display:flex;justify-content:space-between;margin:5px 0}.eui-21 .eui-file-upload__preview--left{align-items:center;display:flex;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.eui-21 .eui-file-upload__preview--left__image{margin-right:var(--eui-s-m)}.eui-21 .eui-file-upload__preview--left__image img{width:180px}.eui-21 .eui-file-upload__preview--left__image .loading-container{text-align:center;width:180px}.eui-21 .eui-file-upload__preview--left__filename{display:block}.eui-21 .eui-file-upload__preview--right{align-items:center;display:flex;margin-left:auto;gap:var(--eui-s-m)}.eui-21 .eui-file-upload__progress-bar-container{margin-bottom:var(--eui-s-3xs)}.eui-21 .eui-file-upload__progress-bar-container .progress-bar{background-color:var(--eui-c-primary-base);height:var(--eui-s-3xs)}\n"] }]
        }], propDecorators: { e2eAttr: [{
                type: Input
            }], progress: [{
                type: Input
            }], accept: [{
                type: Input
            }], maxFiles: [{
                type: Input
            }], fileDrop: [{
                type: Output
            }], itemClick: [{
                type: Output
            }], cssClasses: [{
                type: HostBinding,
                args: ['class']
            }], templates: [{
                type: ContentChildren,
                args: [EuiTemplateDirective]
            }], fileInputs: [{
                type: ViewChildren,
                args: ['input']
            }], isMultiple: [{
                type: Input,
                args: [{ transform: booleanAttribute }]
            }], hasProgressBar: [{
                type: Input,
                args: [{ transform: booleanAttribute }]
            }], hasPreview: [{
                type: Input,
                args: [{ transform: booleanAttribute }]
            }], hasPreviewAsImage: [{
                type: Input,
                args: [{ transform: booleanAttribute }]
            }], hasPreviewAsIcon: [{
                type: Input,
                args: [{ transform: booleanAttribute }]
            }], hasTotalSizeDisplayed: [{
                type: Input,
                args: [{ transform: booleanAttribute }]
            }], hasResetButton: [{
                type: Input,
                args: [{ transform: booleanAttribute }]
            }], hasDragArea: [{
                type: Input,
                args: [{ transform: booleanAttribute }]
            }], isItemsClickable: [{
                type: Input,
                args: [{ transform: booleanAttribute }]
            }], emitFiles: [{
                type: HostListener,
                args: ['change', ['$any($event.target).files']]
            }] } });

class EuiFileUploadUtilsService {
    constructor() {
        this.httpClient = inject(HttpClient);
    }
    /**
     * @description
     * Method that will format data in `FormData` and send them to backend.
     *
     * @example
     * Call the method and log the response in the console.
     *
     * ```
     * this.euiFileUploadUtilsService.sendData(this.form.value, 'http://localhost:3000/api/fake-api').pipe(
     *     this.euiFileUploadUtilsService.uploadProgress((progress) => {
     *         console.log(progress);
     *     }),
     *     this.euiFileUploadUtilsService.toResponseBody(),
     * ).subscribe((response) => {
     *      console.log(response);
     * });
     * ```
     *
     * @param data Data to send to backend
     * @param APIEndPoint Endpoint URL
     * @returns The backend response.
     */
    // TODO: find the correct type or turn into a generic, https://www.typescriptlang.org/docs/handbook/2/generics.html
    // eslint-disable-next-line @typescript-eslint/no-explicit-any
    sendData(data, APIEndPoint) {
        return this.httpClient.post(APIEndPoint, this.toFormData(data), {
            reportProgress: true,
            observe: 'events',
        });
    }
    /**
     * @description
     * Method that will return the upload progress.
     *
     * @example
     * Call the method and log the progress in the console.
     *
     * ```
     * this.euiFileUploadUtilsService.sendData(this.form.value, 'http://localhost:3000/api/fake-api').pipe(
     *     this.euiFileUploadUtilsService.uploadProgress((progress) => {
     *         console.log(progress);
     *     }),
     *     this.euiFileUploadUtilsService.toResponseBody(),
     * ).subscribe((response) => {
     *      console.log(response);
     * });
     * ```
     *
     * @param cb A method to execute on each progress step.
     * @returns A number between 0 and 100.
     */
    uploadProgress(cb) {
        return tap((event) => {
            if (event.type === HttpEventType.UploadProgress) {
                cb(Math.round((100 * event.loaded) / event.total));
            }
        });
    }
    /**
     * @description
     * Method that will return only the body of the backend response.
     *
     * @example
     * Call the method and log the response in the console.
     *
     * ```
     * this.euiFileUploadUtilsService.sendData(this.form.value, 'http://localhost:3000/api/fake-api').pipe(
     *     this.euiFileUploadUtilsService.uploadProgress((progress) => {
     *         console.log(progress);
     *     }),
     *     this.euiFileUploadUtilsService.toResponseBody(),
     * ).subscribe((response) => {
     *      console.log(response);
     * });
     * ```
     *
     * @returns The backend response.
     */
    toResponseBody() {
        return pipe(filter((event) => event.type === HttpEventType.Response), map((res) => res.body));
    }
    /**
     * @description
     * Return the icon to be used with eui-icon-svg of the given extension or a default file icon
     *
     * @param fileExtension
     * @returns Icon name as string
     */
    getIconForExtension(fileExtension) {
        const previewAsIconConfig = {
            avi: 'eui-file-video',
            html: 'eui-file-html',
            htm: 'eui-file-code',
            js: 'eui-file-code',
            json: 'eui-file-code',
            mp3: 'eui-file-audio',
            mp4: 'eui-file-video',
            pdf: 'eui-file-pdf',
            png: 'eui-file-image',
            svg: 'eui-file-image',
            txt: 'eui-file-text',
            xml: 'eui-file-code',
            jpeg: 'eui-file-image',
            jpg: 'eui-file-image',
            zip: 'eui-file-archive',
            doc: 'eui-file-word',
            docx: 'eui-file-word-o',
            xls: 'eui-file-excel-o',
            xlsx: 'eui-file-excel-o',
            ppt: 'eui-file-powerpoint',
            csv: 'eui-file-text',
            rtf: 'eui-file-text',
        };
        const f = fileExtension.toLowerCase();
        return !previewAsIconConfig[f] ? 'eui-file-empty-o' : previewAsIconConfig[f];
    }
    /**
     * Internal method returning `FormData` object.
     *
     * @param formValue Data to send to form
     * @returns A `FormData` object
     */
    // TODO: find the correct type or turn into a generic, https://www.typescriptlang.org/docs/handbook/2/generics.html
    // eslint-disable-next-line @typescript-eslint/no-explicit-any
    toFormData(formValue) {
        const formData = new FormData();
        for (const key of Object.keys(formValue)) {
            if (Array.isArray(formValue[key])) {
                let fileIndex = 0;
                // TODO: find the correct type or turn into a generic, https://www.typescriptlang.org/docs/handbook/2/generics.html
                // eslint-disable-next-line @typescript-eslint/no-explicit-any
                formValue[key].forEach((value) => {
                    if (value instanceof File) {
                        formData.append(key + fileIndex, value);
                        fileIndex++;
                    }
                });
                const a = [];
                // TODO: find the correct type or turn into a generic, https://www.typescriptlang.org/docs/handbook/2/generics.html
                // eslint-disable-next-line @typescript-eslint/no-explicit-any
                formValue[key].forEach((value) => {
                    a.push(value);
                });
                formData.append(key, JSON.stringify(a));
            }
            else {
                if (typeof formValue[key] === 'string') {
                    formData.set(key, formValue[key]);
                }
                else {
                    formData.set(key, JSON.stringify(formValue[key]));
                }
            }
        }
        return formData;
    }
    static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.1", ngImport: i0, type: EuiFileUploadUtilsService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
    static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.2.1", ngImport: i0, type: EuiFileUploadUtilsService }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.1", ngImport: i0, type: EuiFileUploadUtilsService, decorators: [{
            type: Injectable
        }] });

const getMimeType = (header) => {
    // convert Uint8Array to hex string
    const hex = uint8ArrayToHexString(header);
    // map hex string to mime type
    if (hex.startsWith('ffd8ff'))
        return 'image/jpeg';
    if (hex.startsWith('89504e47'))
        return 'image/png';
    if (hex.startsWith('464c4946'))
        return 'image/flif';
    if (hex.startsWith('67696d7020786366'))
        return 'image/x-xcf';
    if (hex.startsWith('49492a00'))
        return 'image/x-canon-cr2';
    if (hex.startsWith('49492a00'))
        return 'image/x-canon-cr3';
    if (hex.startsWith('49492a00'))
        return 'image/tiff';
    if (hex.startsWith('424d'))
        return 'image/bmp';
    if (hex.startsWith('69636e73'))
        return 'image/icns';
    if (hex.startsWith('49491a0000004845415050'))
        return 'image/vnd.ms-photo';
    if (hex.startsWith('38425053'))
        return 'image/vnd.adobe.photoshop';
    if (hex.startsWith('06054b50'))
        return 'application/x-indesign';
    // Handle ZIP-based formats by examining content
    if (hex.startsWith('504b0304')) {
        return identifyZipBasedFormat(header);
    }
    if (hex.startsWith('7b2274797065223a226a736f6e227d'))
        return 'application/json';
    if (hex.startsWith('7573746172'))
        return 'application/x-tar';
    if (hex.startsWith('526172211a0700'))
        return 'application/x-rar-compressed';
    if (hex.startsWith('1f8b08'))
        return 'application/gzip';
    if (hex.startsWith('425a68'))
        return 'application/x-bzip2';
    if (hex.startsWith('377abcaf271c'))
        return 'application/x-7z-compressed';
    if (hex.startsWith('78da'))
        return 'application/x-apple-diskimage';
    if (hex.startsWith('00000020667479706d70'))
        return 'video/mp4';
    if (hex.startsWith('4d546864'))
        return 'audio/midi';
    if (hex.startsWith('1a45dfa393428288'))
        return 'video/x-matroska';
    if (hex.startsWith('1a45dfa3'))
        return 'video/webm';
    if (hex.startsWith('00000014667479707174'))
        return 'video/quicktime';
    if (hex.startsWith('52494646'))
        return 'video/vnd.avi';
    if (hex.startsWith('52494646'))
        return 'audio/vnd.wave';
    if (hex.startsWith('0a010301'))
        return 'audio/qcelp';
    if (hex.startsWith('3026b2758e66cf11'))
        return identifyAsfFormat(header);
    if (hex.startsWith('000001ba'))
        return 'video/mpeg';
    if (hex.startsWith('00000020667479703367'))
        return 'video/3gpp';
    if (hex.startsWith('494433'))
        return 'audio/mpeg';
    if (hex.startsWith('00000020667479704d344120'))
        return 'audio/mp4';
    if (hex.startsWith('4f707573'))
        return 'audio/opus';
    if (hex.startsWith('4f676753'))
        return identifyOggFormat(header);
    if (hex.startsWith('664c6143'))
        return 'audio/x-flac';
    if (hex.startsWith('4d414320'))
        return 'audio/ape';
    if (hex.startsWith('7776706b'))
        return 'audio/wavpack';
    if (hex.startsWith('2321414d520a'))
        return 'audio/amr';
    if (hex.startsWith('255044462d312e'))
        return 'application/pdf';
    if (hex.startsWith('7f454c46'))
        return 'application/x-elf';
    if (hex.startsWith('4d5a'))
        return 'application/x-msdownload';
    if (hex.startsWith('435753'))
        return 'application/x-shockwave-flash';
    if (hex.startsWith('7b5c72746631'))
        return 'application/rtf';
    if (hex.startsWith('0061736d'))
        return 'application/wasm';
    if (hex.startsWith('774f4646'))
        return 'font/woff';
    if (hex.startsWith('774f4632'))
        return 'font/woff2';
    if (hex.startsWith('000100000008'))
        return 'application/vnd.ms-fontobject';
    if (hex.startsWith('0001000000'))
        return 'font/ttf';
    if (hex.startsWith('4f54544f00'))
        return 'font/otf';
    if (hex.startsWith('000001000100'))
        return 'image/x-icon';
    if (hex.startsWith('464c560105'))
        return 'video/x-flv';
    if (hex.startsWith('25215053'))
        return 'application/postscript';
    if (hex.startsWith('25215053'))
        return 'application/eps';
    if (hex.startsWith('fd377a585a00'))
        return 'application/x-xz';
    if (hex.startsWith('53514c69746520666f726d6174203300'))
        return 'application/x-sqlite3';
    if (hex.startsWith('4e45531a00000001'))
        return 'application/x-nintendo-nes-rom';
    if (hex.startsWith('4d534346'))
        return 'application/vnd.ms-cab-compressed';
    if (hex.startsWith('213c617263683e0a'))
        return 'application/x-deb';
    if (hex.startsWith('1f8b08'))
        return 'application/x-unix-archive';
    if (hex.startsWith('edabeedb'))
        return 'application/x-rpm';
    if (hex.startsWith('1f9d90'))
        return 'application/x-compress';
    if (hex.startsWith('4c5a4950'))
        return 'application/x-lzip';
    if (hex.startsWith('d0cf11e0a1b11ae1'))
        return 'application/x-cfb';
    if (hex.startsWith('4d49455f'))
        return 'application/x-mie';
    if (hex.startsWith('4141523146'))
        return 'application/x-apache-arrow';
    if (hex.startsWith('060e2b3402050101'))
        return 'application/mxf';
    if (hex.startsWith('47'))
        return 'video/mp2t';
    if (hex.startsWith('4250e4'))
        return 'application/x-blender';
    if (hex.startsWith('425047fb'))
        return 'image/bpg';
    if (hex.startsWith('ff4fff51'))
        return 'image/j2c';
    if (hex.startsWith('0000000c6a5020200d0a'))
        return 'image/jp2';
    if (hex.startsWith('6a5020200d0a870a'))
        return 'image/jpx';
    if (hex.startsWith('6a5020200d0a870a'))
        return 'image/jpm';
    if (hex.startsWith('0000000c6a5020200d0a'))
        return 'image/mj2';
    if (hex.startsWith('464f524d'))
        return 'audio/aiff';
    if (hex.startsWith('3c3f786d6c20'))
        return 'application/xml';
    if (hex.startsWith('424f4f4b4d4f4249'))
        return 'application/x-mobipocket-ebook';
    if (hex.startsWith('667479706174'))
        return identifyHeifFormat(header);
    if (hex.startsWith('4b545820'))
        return 'image/ktx';
    if (hex.startsWith('4449434d'))
        return 'application/dicom';
    if (hex.startsWith('4d50434b'))
        return 'audio/x-musepack';
    if (hex.startsWith('56656e64'))
        return 'text/calendar';
    if (hex.startsWith('424547494e3a5643415244'))
        return 'text/vcard';
    if (hex.startsWith('676c5458'))
        return 'model/gltf-binary';
    if (hex.startsWith('d4c3b2a1'))
        return 'application/vnd.tcpdump.pcap';
    if (hex.startsWith('464f524d'))
        return 'audio/x-voc';
    if (hex.startsWith('64646f6c'))
        return 'audio/vnd.dolby.dd-raw';
    return null;
};
const identifyZipBasedFormat = (data) => {
    try {
        const fileEntries = extractZipFileList(data);
        // Chrome extension
        if (fileEntries.includes('manifest.json')) {
            return 'application/x-google-chrome-extension';
        }
        // EPUB
        if (fileEntries.includes('META-INF/container.xml') || fileEntries.includes('mimetype')) {
            return 'application/epub+zip';
        }
        // OpenDocument formats
        if (fileEntries.includes('META-INF/manifest.xml')) {
            if (fileEntries.some(f => f.endsWith('.odp')))
                return 'application/vnd.oasis.opendocument.presentation';
            if (fileEntries.some(f => f.endsWith('.ods')))
                return 'application/vnd.oasis.opendocument.spreadsheet';
            if (fileEntries.some(f => f.endsWith('.odt')))
                return 'application/vnd.oasis.opendocument.text';
        }
        // Microsoft Office formats
        if (fileEntries.includes('[Content_Types].xml')) {
            if (fileEntries.includes('ppt/presentation.xml'))
                return 'application/vnd.openxmlformats-officedocument.presentationml.presentation';
            if (fileEntries.includes('xl/workbook.xml'))
                return 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet';
            if (fileEntries.includes('word/document.xml'))
                return 'application/vnd.openxmlformats-officedocument.wordprocessingml.document';
        }
        // Default to generic ZIP
        return 'application/zip';
    }
    catch {
        return 'application/zip';
    }
};
const extractZipFileList = (data) => {
    // Simplified ZIP parsing - looks for central directory entries
    const files = [];
    let offset = 0;
    while (offset < data.length - 4) {
        // Look for local file header signature (0x04034b50)
        if (data[offset] === 0x50 && data[offset + 1] === 0x4b &&
            data[offset + 2] === 0x03 && data[offset + 3] === 0x04) {
            const filenameLength = data[offset + 26] | (data[offset + 27] << 8);
            const extraFieldLength = data[offset + 28] | (data[offset + 29] << 8);
            if (filenameLength > 0 && offset + 30 + filenameLength <= data.length) {
                const filename = new TextDecoder().decode(data.slice(offset + 30, offset + 30 + filenameLength));
                files.push(filename);
            }
            offset += 30 + filenameLength + extraFieldLength;
        }
        else {
            offset++;
        }
    }
    return files;
};
const uint8ArrayToHexString = (uint8Array) => {
    return Array.prototype.map.call(uint8Array, (byte) => ('00' + byte.toString(16)).slice(-2)).join('');
};
const identifyAsfFormat = (data) => {
    // ASF files all start with the same GUID: 3026b2758e66cf11a6d900aa0062ce6c
    // To distinguish between audio, video, and application ASF, we need to examine
    // the stream properties in the ASF header
    try {
        // Look for stream properties object GUID starting at offset 30
        // Audio stream properties: b7dc0791a9b711cf8ee600c00c205365
        // Video stream properties: bc19efc05b4d11cf9b1100aa00bbcb8b
        if (data.length < 100) {
            // Not enough data to analyze, default to application
            return 'application/vnd.ms-asf';
        }
        const hex = uint8ArrayToHexString(data);
        // Look for video stream properties GUID (indicates video content)
        if (hex.includes('bc19efc05b4d11cf9b1100aa00bbcb8b')) {
            return 'video/x-ms-asf';
        }
        // Look for audio stream properties GUID (indicates audio content)
        if (hex.includes('b7dc0791a9b711cf8ee600c00c205365')) {
            return 'audio/x-ms-asf';
        }
        // Check for Windows Media Audio/Video codec identifiers
        // WMA codec: 161 (0xa1) or 162 (0xa2) or 163 (0xa3)
        // WMV codec: Common values include various Windows Media Video identifiers
        // Scan through the header for codec information
        for (let i = 0; i < Math.min(data.length - 4, 1000); i++) {
            // Look for audio codec indicators
            if (data[i] === 0xa1 || data[i] === 0xa2 || data[i] === 0xa3) {
                // Found WMA codec indicator
                return 'audio/x-ms-asf';
            }
        }
        // Look for common video indicators in the first 1KB
        const textContent = new TextDecoder('utf-8', { fatal: false }).decode(data.slice(0, Math.min(1000, data.length)));
        // Check for video-related strings
        if (textContent.toLowerCase().includes('video') ||
            textContent.toLowerCase().includes('wmv') ||
            textContent.toLowerCase().includes('mpeg')) {
            return 'video/x-ms-asf';
        }
        // Check for audio-related strings
        if (textContent.toLowerCase().includes('audio') ||
            textContent.toLowerCase().includes('wma') ||
            textContent.toLowerCase().includes('music')) {
            return 'audio/x-ms-asf';
        }
        // Default to application/vnd.ms-asf if we can't determine the specific type
        return 'application/vnd.ms-asf';
    }
    catch {
        // If any error occurs during analysis, default to application type
        return 'application/vnd.ms-asf';
    }
};
const identifyHeifFormat = (data) => {
    // HEIF/HEIC files all start with the same ftyp box signature: 667479706174
    // To distinguish between formats, we need to examine the brand and compatible brands
    // in the ftyp (file type) box
    try {
        if (data.length < 20) {
            // Not enough data to analyze, default to HEIF
            return 'image/heif';
        }
        const hex = uint8ArrayToHexString(data);
        // The ftyp box structure:
        // Bytes 0-3: Box size (4 bytes)
        // Bytes 4-7: Box type "ftyp" (4 bytes) - 667479706174
        // Bytes 8-11: Major brand (4 bytes)
        // Bytes 12-15: Minor version (4 bytes)
        // Bytes 16+: Compatible brands (4 bytes each)
        // Extract major brand (4 bytes starting at offset 8)
        // Convert to ASCII string for easier comparison
        const majorBrand = String.fromCharCode(data[8], data[9], data[10], data[11]);
        // Look for compatible brands in the first 100 bytes
        const compatibleBrandsHex = hex.substring(32, Math.min(200, hex.length)); // Start after major brand + minor version
        // HEIC brands: 'heic', 'heix', 'hevc', 'hevx'
        if (majorBrand === 'heic' ||
            compatibleBrandsHex.includes('68656963') || // 'heic'
            compatibleBrandsHex.includes('68656978') || // 'heix'
            compatibleBrandsHex.includes('68657663') || // 'hevc'
            compatibleBrandsHex.includes('68657678')) { // 'hevx'
            // Check for sequence/animation indicators
            if (compatibleBrandsHex.includes('68657673') || // 'hevs' - HEIC sequence
                compatibleBrandsHex.includes('6d736631') || // 'msf1' - sequence
                majorBrand === 'hevs') {
                return 'image/heic-sequence';
            }
            return 'image/heic';
        }
        // HEIF brands: 'mif1', 'msf1'
        if (majorBrand === 'mif1' || majorBrand === 'msf1' ||
            compatibleBrandsHex.includes('6d696631') || // 'mif1'
            compatibleBrandsHex.includes('6d736631')) { // 'msf1'
            // Check for sequence/animation indicators
            if (majorBrand === 'msf1' ||
                compatibleBrandsHex.includes('6d736631')) { // 'msf1' - sequence
                return 'image/heif-sequence';
            }
            return 'image/heif';
        }
        // Check for specific sequence indicators in the data
        if (hex.includes('6d736631') || // 'msf1' - multi-image sequence
            hex.includes('68657673')) { // 'hevs' - HEIC sequence
            // If we find HEIC-related brands, it's HEIC sequence
            if (hex.includes('68656963') || hex.includes('68657663')) {
                return 'image/heic-sequence';
            }
            return 'image/heif-sequence';
        }
        // If we find HEIC indicators but no sequence, it's single HEIC
        if (hex.includes('68656963') || // 'heic'
            hex.includes('68657663') || // 'hevc'
            hex.includes('68656978') || // 'heix'
            hex.includes('68657678')) { // 'hevx'
            return 'image/heic';
        }
        // Default to HEIF for any other case
        return 'image/heif';
    }
    catch {
        // If any error occurs during analysis, default to HEIF
        return 'image/heif';
    }
};
const identifyOggFormat = (data) => {
    // OGG files all start with "OggS" (4f676753) but can contain different codecs
    // To distinguish between audio, video, and application OGG, we need to examine
    // the codec information in the OGG page headers
    try {
        if (data.length < 64) {
            // Not enough data to analyze, default to audio
            return 'audio/ogg';
        }
        const hex = uint8ArrayToHexString(data);
        // Look for codec identification patterns in the first few hundred bytes
        // Common video codecs in OGG:
        // - Theora: "theora" or "\x80theora"
        // - VP8/VP9: various signatures
        // - Dirac: "BBCD"
        // Common audio codecs in OGG:
        // - Vorbis: "vorbis" or "\x01vorbis"
        // - Opus: "OpusHead"
        // - FLAC: "fLaC"
        // - Speex: "Speex"
        // Convert some bytes to text for string matching
        const textContent = new TextDecoder('utf-8', { fatal: false }).decode(data.slice(0, Math.min(512, data.length)));
        const lowerContent = textContent.toLowerCase();
        // Check for video codec indicators first (as they're more specific)
        if (lowerContent.includes('theora') ||
            hex.includes('8074686f726120') || // "\x80theora "
            hex.includes('7468656f7261') || // "theora"
            lowerContent.includes('dirac') ||
            hex.includes('42424344') || // "BBCD" - Dirac
            lowerContent.includes('vp8') ||
            lowerContent.includes('vp9')) {
            return 'video/ogg';
        }
        // Check for audio codec indicators
        if (lowerContent.includes('vorbis') ||
            hex.includes('01766f72626973') || // "\x01vorbis"
            hex.includes('766f72626973') || // "vorbis"
            lowerContent.includes('opushead') ||
            hex.includes('4f7075734865616420') || // "OpusHead "
            lowerContent.includes('speex') ||
            hex.includes('53706565782020') || // "Speex  "
            lowerContent.includes('flac') ||
            hex.includes('664c6143')) { // "fLaC"
            return 'audio/ogg';
        }
        // Look for OGG stream structure patterns
        // OGG pages have a specific structure, look for multiple "OggS" signatures
        // which might indicate a complex multimedia container
        let oggSCount = 0;
        for (let i = 0; i < Math.min(data.length - 4, 1000); i += 4) {
            if (data[i] === 0x4f && data[i + 1] === 0x67 &&
                data[i + 2] === 0x67 && data[i + 3] === 0x53) {
                oggSCount++;
            }
        }
        // Multiple OGG pages might indicate a more complex application format
        if (oggSCount > 3) {
            // Check if it's likely a multimedia container vs pure audio/video
            if (lowerContent.includes('application') ||
                lowerContent.includes('metadata') ||
                lowerContent.includes('index') ||
                (!lowerContent.includes('audio') && !lowerContent.includes('video') &&
                    !lowerContent.includes('vorbis') && !lowerContent.includes('theora'))) {
                return 'application/ogg';
            }
        }
        // Look for file extension hints in metadata (if present)
        if (lowerContent.includes('.ogv') || lowerContent.includes('video')) {
            return 'video/ogg';
        }
        if (lowerContent.includes('.oga') || lowerContent.includes('audio') || lowerContent.includes('music')) {
            return 'audio/ogg';
        }
        // Check the OGG page header flags
        // Byte 5 in OGG page header contains flags
        // If we have enough data, check the stream type
        if (data.length > 26) {
            const pageHeaderType = data[5];
            // Fresh packet start (0x02) often indicates beginning of codec data
            if ((pageHeaderType & 0x02) === 0x02) {
                // Look at the packet data starting around byte 27
                const packetStart = data.slice(27, Math.min(data.length, 50));
                const packetHex = uint8ArrayToHexString(packetStart);
                // Check for Vorbis identification header
                if (packetHex.startsWith('01766f72626973')) {
                    return 'audio/ogg';
                }
                // Check for Theora identification header
                if (packetHex.startsWith('8074686f726120')) {
                    return 'video/ogg';
                }
            }
        }
        // Default to audio/ogg as it's the most common OGG format
        return 'audio/ogg';
    }
    catch {
        // If any error occurs during analysis, default to audio
        return 'audio/ogg';
    }
};

/**
 * @description
 * Validator that requires the maximum length of files that can be uploaded.
 *
 * @example
 * Validate that the user selected maximum 3 files:
 *
 * ```
 * const fileUpload = new FormControl(null, maxFilesValidator(3));
 * console.log(fileUpload.errors); // { maxFiles: 3 }
 * ```
 *
 * @param {MimeType[]} maxFiles - The maximum number of files allowed.
 * @returns A validator function that returns an error map with the
 * `maxFiles` if the validation check fails, otherwise `null`.
 */
const maxFilesValidator = (maxFiles) => (control) => control.value && control.value.length > maxFiles ? { maxFiles } : null;
/**
 * @description
 * Validator that checks the **total size** (in bytes) of the selected files.
 * Returns an error when the combined size exceeds the provided limit.
 *
 * @example
 * Validate that the user doesn't select files whose **combined size** exceeds 300 kB (300000 bytes):
 *
 * ```ts
 * const fileUpload = new FormControl<File[] | null>(null, maxSizeValidator(300000));
 * console.log(fileUpload.errors); // { maxSize: 300000 } if totalSize > 300000
 * ```
 *
 * @param {number} maxSize - Maximum allowed **total size** in bytes.
 * @returns A validator function that returns `{ maxSize }` if the validation check fails,
 * otherwise `null`.
 */
const maxSizeValidator = (maxSize) => (control) => {
    let totalSize = 0;
    if (control.value) {
        control.value.forEach((file) => {
            totalSize += file.size;
        });
    }
    return control.value && totalSize > maxSize ? { maxSize } : null;
};
/**
 * @description
 * Validator that checks the **maximum size per file** (in bytes).
 * Returns an error if one or more selected files exceed the configured limit.
 *
 * @example
 * Validate that the user doesn't select files that exceed 300 kB (300000 bytes) per file:
 *
 * ```ts
 * const fileUpload = new FormControl<File[] | null>(null, maxFileSizeValidator(300000));
 * console.log(fileUpload.errors); // { maxFileSize: 300000, indexes: [0, 2] }
 * ```
 *
 * @param {number} maxFileSize - Maximum allowed size **per file** in bytes.
 * @returns A validator function that returns `{ maxFileSize, indexes }` if the validation check fails,
 * otherwise `null`. `indexes` contains the indices of the files that exceeded the limit.
 */
const maxFileSizeValidator = (maxFileSize) => (control) => {
    const fileIndexes = [];
    let maxFileExceeded = false;
    if (control.value) {
        control.value.forEach((file, index) => {
            if (file.size > maxFileSize) {
                fileIndexes.push(index);
                maxFileExceeded = true;
            }
        });
    }
    return control.value && maxFileExceeded ? { maxFileSize, indexes: fileIndexes } : null;
};
/**
 * @description
 * Validator that requires an array of mime type that can be uploaded.
 *
 * @example
 * Validate that the user doesn't select a file having another extension than .jpg or .jpeg:
 * ```
 * const fileUpload = new FormControl(null, mimeTypeExtensionValidator(['image/jpeg'])),
 * console.log(fileUpload.errors); // { invalidFileExtension: ['image/jpeg'] }
 * ```
 *
 * @param {MimeType[]} mimeTypes - The list of allowed MIME types.
 * @returns A validator function that returns an error map with the
 * `invalidFileExtension` if the validation check fails, otherwise `null`.
 */
const mimeTypeExtensionValidator = (mimeTypes) => (control) => {
    const invalidFileExtension = [];
    if (control.value) {
        control.value.forEach((file) => {
            if (mimeTypes.indexOf(file.type) === -1) {
                invalidFileExtension.push(file.name);
            }
        });
    }
    return control.value && invalidFileExtension.length > 0 ? { invalidFileExtension } : null;
};
/**
 * @description
 * Validator that requires an array of mime type that can be uploaded.
 *
 * @example
 * Validate that the user doesn't select a file having another extension than .jpg or .jpeg:
 *
 * ```
 * const fileUpload = new FormControl(null, mimeTypeExtensionValidator(['image/jpeg'])),
 * console.log(fileUpload.errors); // { invalidMimeFileType: ['image/jpeg'] }
 * ```
 * @param {MimeType[]} mimeTypes - The list of allowed MIME types.
 * @returns An Async validator function that returns an error map with the
 * `invalidMimeFileType` if the validation check fails, otherwise `null`.
 */
const asyncMimeTypeExtensionValidator = (mimeTypes) => (control) => {
    if (control.value) {
        const fileErrorObservables = [];
        // iterate over files
        control.value.forEach((file) => {
            // push observable which will check the mime validation type
            fileErrorObservables.push(validateFileMimeType(file, mimeTypes));
        });
        return zip(...fileErrorObservables).pipe(map((fileErrors) => {
            const errors = fileErrors.filter((fileError) => fileError !== null);
            // Error should be { fileName: FileType }
            if (errors.length === 0) {
                return null;
            }
            return { invalidMimeFileType: errors };
        }));
    }
    return of(null);
};
/**
 * Validates the MIME type of a file against a list of allowed MIME types.
 *
 * @param {File} file - The file to validate.
 * @param {MimeType[]} mimeTypes - The list of allowed MIME types.
 * @returns {Observable<{ [key: string]: MimeType }>} An observable that emits an object containing the file name as the key and
 * its detected MIME type as the value if the file type is not allowed.
 */
const validateFileMimeType = (file, mimeTypes) => {
    return new Observable((subscriber) => {
        const reader = new FileReader();
        reader.onloadend = () => {
            const buffer = reader.result;
            // Read more bytes to support complex MIME type detection (e.g., ASF format analysis)
            const headerBytes = new Uint8Array(buffer).slice(0, Math.min(1024, buffer.byteLength));
            const mime = getMimeType(headerBytes);
            if (mimeTypes.includes(mime)) {
                subscriber.next(null);
            }
            else {
                subscriber.next({ [file.name]: mime });
            }
            subscriber.complete();
        };
        reader.readAsArrayBuffer(file);
    });
};

const EUI_FILE_UPLOAD = [
    EuiFileUploadComponent,
    EuiFileUploadProgressComponent,
    EuiFilePreviewComponent,
    EuiFileSizePipe,
];

/**
 * Generated bundle index. Do not edit.
 */

export { EUI_FILE_UPLOAD, EuiFilePreviewComponent, EuiFileSizePipe, EuiFileUploadComponent, EuiFileUploadProgressComponent, EuiFileUploadUtilsService, asyncMimeTypeExtensionValidator, getMimeType, maxFileSizeValidator, maxFilesValidator, maxSizeValidator, mimeTypeExtensionValidator };
//# sourceMappingURL=eui-components-eui-file-upload.mjs.map
