ngx-mat-file-input
Version: 
This is a fork of the file input management for Angular Material initially maintained on https://merlosy.github.io/ngx-material-file-input/
455 lines (445 loc) • 17.3 kB
JavaScript
import { InjectionToken, ElementRef, Renderer2, Optional, Self, Input, HostBinding, HostListener, Component, Inject, Pipe, NgModule } from '@angular/core';
import { __extends, __decorate, __metadata, __param } from 'tslib';
import { FocusMonitor } from '@angular/cdk/a11y';
import { NgControl, NgForm, FormGroupDirective } from '@angular/forms';
import { mixinErrorState, ErrorStateMatcher } from '@angular/material/core';
import { MatFormFieldControl } from '@angular/material/form-field';
import { coerceBooleanProperty } from '@angular/cdk/coercion';
/**
 * Optional token to provide custom configuration to the module
 */
var NGX_MAT_FILE_INPUT_CONFIG = new InjectionToken('ngx-mat-file-input.config');
/**
 * Provide additional configuration to dynamically customize the module injection
 */
var FileInputConfig = /** @class */ (function () {
    function FileInputConfig() {
    }
    return FileInputConfig;
}());
/**
 * The files to be uploaded
 */
var FileInput = /** @class */ (function () {
    function FileInput(_files, delimiter) {
        if (delimiter === void 0) { delimiter = ', '; }
        this._files = _files;
        this.delimiter = delimiter;
        this._fileNames = (this._files || []).map(function (f) { return f.name; }).join(delimiter);
    }
    Object.defineProperty(FileInput.prototype, "files", {
        get: function () {
            return this._files || [];
        },
        enumerable: true,
        configurable: true
    });
    Object.defineProperty(FileInput.prototype, "fileNames", {
        get: function () {
            return this._fileNames;
        },
        enumerable: true,
        configurable: true
    });
    return FileInput;
}());
// Boilerplate for applying mixins to FileInput
/** @docs-private */
var FileInputBase = /** @class */ (function () {
    function FileInputBase(_defaultErrorStateMatcher, _parentForm, _parentFormGroup, ngControl) {
        this._defaultErrorStateMatcher = _defaultErrorStateMatcher;
        this._parentForm = _parentForm;
        this._parentFormGroup = _parentFormGroup;
        this.ngControl = ngControl;
    }
    return FileInputBase;
}());
/**
 * Allows to use a custom ErrorStateMatcher with the file-input component
 */
var FileInputMixinBase = mixinErrorState(FileInputBase);
var FileInputComponent = /** @class */ (function (_super) {
    __extends(FileInputComponent, _super);
    /**
     * @see https://angular.io/api/forms/ControlValueAccessor
     */
    function FileInputComponent(fm, _elementRef, _renderer, _defaultErrorStateMatcher, ngControl, _parentForm, _parentFormGroup) {
        var _this = _super.call(this, _defaultErrorStateMatcher, _parentForm, _parentFormGroup, ngControl) || this;
        _this.fm = fm;
        _this._elementRef = _elementRef;
        _this._renderer = _renderer;
        _this._defaultErrorStateMatcher = _defaultErrorStateMatcher;
        _this.ngControl = ngControl;
        _this._parentForm = _parentForm;
        _this._parentFormGroup = _parentFormGroup;
        _this.focused = false;
        _this.controlType = 'file-input';
        _this.autofilled = false;
        _this._required = false;
        _this.accept = null;
        _this.id = "ngx-mat-file-input-" + FileInputComponent_1.nextId++;
        _this.describedBy = '';
        _this._onChange = function (_) { };
        _this._onTouched = function () { };
        if (_this.ngControl != null) {
            _this.ngControl.valueAccessor = _this;
        }
        fm.monitor(_elementRef.nativeElement, true).subscribe(function (origin) {
            _this.focused = !!origin;
            _this.stateChanges.next();
        });
        return _this;
    }
    FileInputComponent_1 = FileInputComponent;
    FileInputComponent.prototype.setDescribedByIds = function (ids) {
        this.describedBy = ids.join(' ');
    };
    Object.defineProperty(FileInputComponent.prototype, "value", {
        get: function () {
            return this.empty ? null : new FileInput(this._elementRef.nativeElement.value || []);
        },
        set: function (fileInput) {
            if (fileInput) {
                this.writeValue(fileInput);
                this.stateChanges.next();
            }
        },
        enumerable: true,
        configurable: true
    });
    Object.defineProperty(FileInputComponent.prototype, "placeholder", {
        get: function () {
            return this._placeholder;
        },
        set: function (plh) {
            this._placeholder = plh;
            this.stateChanges.next();
        },
        enumerable: true,
        configurable: true
    });
    Object.defineProperty(FileInputComponent.prototype, "empty", {
        /**
         * Whether the current input has files
         */
        get: function () {
            return !this._elementRef.nativeElement.value || this._elementRef.nativeElement.value.length === 0;
        },
        enumerable: true,
        configurable: true
    });
    Object.defineProperty(FileInputComponent.prototype, "shouldLabelFloat", {
        get: function () {
            return this.focused || !this.empty || this.valuePlaceholder !== undefined;
        },
        enumerable: true,
        configurable: true
    });
    Object.defineProperty(FileInputComponent.prototype, "required", {
        get: function () {
            return this._required;
        },
        set: function (req) {
            this._required = coerceBooleanProperty(req);
            this.stateChanges.next();
        },
        enumerable: true,
        configurable: true
    });
    Object.defineProperty(FileInputComponent.prototype, "isDisabled", {
        get: function () {
            return this.disabled;
        },
        enumerable: true,
        configurable: true
    });
    Object.defineProperty(FileInputComponent.prototype, "disabled", {
        get: function () {
            return this._elementRef.nativeElement.disabled;
        },
        set: function (dis) {
            this.setDisabledState(coerceBooleanProperty(dis));
            this.stateChanges.next();
        },
        enumerable: true,
        configurable: true
    });
    FileInputComponent.prototype.onContainerClick = function (event) {
        if (event.target.tagName.toLowerCase() !== 'input' && !this.disabled) {
            this._elementRef.nativeElement.querySelector('input').focus();
            this.focused = true;
            this.open();
        }
    };
    Object.defineProperty(FileInputComponent.prototype, "fileNames", {
        get: function () {
            return this.value ? this.value.fileNames : this.valuePlaceholder;
        },
        enumerable: true,
        configurable: true
    });
    FileInputComponent.prototype.writeValue = function (obj) {
        this._renderer.setProperty(this._elementRef.nativeElement, 'value', obj instanceof FileInput ? obj.files : null);
    };
    FileInputComponent.prototype.registerOnChange = function (fn) {
        this._onChange = fn;
    };
    FileInputComponent.prototype.registerOnTouched = function (fn) {
        this._onTouched = fn;
    };
    /**
     * Remove all files from the file input component
     * @param [event] optional event that may have triggered the clear action
     */
    FileInputComponent.prototype.clear = function (event) {
        if (event) {
            event.preventDefault();
            event.stopPropagation();
        }
        this.value = new FileInput([]);
        this._elementRef.nativeElement.querySelector('input').value = null;
        this._onChange(this.value);
    };
    FileInputComponent.prototype.change = function (event) {
        var fileList = event.target.files;
        var fileArray = [];
        if (fileList) {
            for (var i = 0; i < fileList.length; i++) {
                fileArray.push(fileList[i]);
            }
        }
        this.value = new FileInput(fileArray);
        this._onChange(this.value);
    };
    FileInputComponent.prototype.blur = function () {
        this.focused = false;
        this._onTouched();
    };
    FileInputComponent.prototype.setDisabledState = function (isDisabled) {
        this._renderer.setProperty(this._elementRef.nativeElement, 'disabled', isDisabled);
    };
    FileInputComponent.prototype.ngOnInit = function () {
        this.multiple = coerceBooleanProperty(this.multiple);
    };
    FileInputComponent.prototype.open = function () {
        if (!this.disabled) {
            this._elementRef.nativeElement.querySelector('input').click();
        }
    };
    FileInputComponent.prototype.ngOnDestroy = function () {
        this.stateChanges.complete();
        this.fm.stopMonitoring(this._elementRef.nativeElement);
    };
    FileInputComponent.prototype.ngDoCheck = function () {
        if (this.ngControl) {
            // We need to re-evaluate this on every change detection cycle, because there are some
            // error triggers that we can't subscribe to (e.g. parent form submissions). This means
            // that whatever logic is in here has to be super lean or we risk destroying the performance.
            this.updateErrorState();
        }
    };
    var FileInputComponent_1;
    FileInputComponent.nextId = 0;
    FileInputComponent.ctorParameters = function () { return [
        { type: FocusMonitor },
        { type: ElementRef },
        { type: Renderer2 },
        { type: ErrorStateMatcher },
        { type: NgControl, decorators: [{ type: Optional }, { type: Self }] },
        { type: NgForm, decorators: [{ type: Optional }] },
        { type: FormGroupDirective, decorators: [{ type: Optional }] }
    ]; };
    __decorate([
        Input(),
        __metadata("design:type", Object)
    ], FileInputComponent.prototype, "autofilled", void 0);
    __decorate([
        Input(),
        __metadata("design:type", String)
    ], FileInputComponent.prototype, "valuePlaceholder", void 0);
    __decorate([
        Input(),
        __metadata("design:type", Boolean)
    ], FileInputComponent.prototype, "multiple", void 0);
    __decorate([
        Input(),
        __metadata("design:type", Object)
    ], FileInputComponent.prototype, "accept", void 0);
    __decorate([
        Input(),
        __metadata("design:type", ErrorStateMatcher)
    ], FileInputComponent.prototype, "errorStateMatcher", void 0);
    __decorate([
        HostBinding(),
        __metadata("design:type", Object)
    ], FileInputComponent.prototype, "id", void 0);
    __decorate([
        HostBinding('attr.aria-describedby'),
        __metadata("design:type", Object)
    ], FileInputComponent.prototype, "describedBy", void 0);
    __decorate([
        Input(),
        __metadata("design:type", Object),
        __metadata("design:paramtypes", [Object])
    ], FileInputComponent.prototype, "value", null);
    __decorate([
        Input(),
        __metadata("design:type", Object),
        __metadata("design:paramtypes", [Object])
    ], FileInputComponent.prototype, "placeholder", null);
    __decorate([
        HostBinding('class.mat-form-field-should-float'),
        __metadata("design:type", Object),
        __metadata("design:paramtypes", [])
    ], FileInputComponent.prototype, "shouldLabelFloat", null);
    __decorate([
        Input(),
        __metadata("design:type", Boolean),
        __metadata("design:paramtypes", [Boolean])
    ], FileInputComponent.prototype, "required", null);
    __decorate([
        HostBinding('class.file-input-disabled'),
        __metadata("design:type", Object),
        __metadata("design:paramtypes", [])
    ], FileInputComponent.prototype, "isDisabled", null);
    __decorate([
        Input(),
        __metadata("design:type", Boolean),
        __metadata("design:paramtypes", [Boolean])
    ], FileInputComponent.prototype, "disabled", null);
    __decorate([
        HostListener('change', ['$event']),
        __metadata("design:type", Function),
        __metadata("design:paramtypes", [Event]),
        __metadata("design:returntype", void 0)
    ], FileInputComponent.prototype, "change", null);
    __decorate([
        HostListener('focusout'),
        __metadata("design:type", Function),
        __metadata("design:paramtypes", []),
        __metadata("design:returntype", void 0)
    ], FileInputComponent.prototype, "blur", null);
    FileInputComponent = FileInputComponent_1 = __decorate([
        Component({
            // tslint:disable-next-line:component-selector
            selector: 'ngx-mat-file-input',
            template: "<input #input type=\"file\" [attr.multiple]=\"multiple? '' : null\" [attr.accept]=\"accept\">\n<span class=\"filename\" [title]=\"fileNames\">{{ fileNames }}</span>\n",
            providers: [{ provide: MatFormFieldControl, useExisting: FileInputComponent_1 }],
            styles: [":host{display:inline-block;width:100%}:host:not(.file-input-disabled){cursor:pointer}input{width:0;height:0;opacity:0;overflow:hidden;position:absolute;z-index:-1}.filename{display:inline-block;text-overflow:ellipsis;overflow:hidden;width:100%}"]
        }),
        __param(4, Optional()),
        __param(4, Self()),
        __param(5, Optional()),
        __param(6, Optional()),
        __metadata("design:paramtypes", [FocusMonitor,
            ElementRef,
            Renderer2,
            ErrorStateMatcher,
            NgControl,
            NgForm,
            FormGroupDirective])
    ], FileInputComponent);
    return FileInputComponent;
}(FileInputMixinBase));
var ByteFormatPipe = /** @class */ (function () {
    function ByteFormatPipe(config) {
        this.config = config;
        this.unit = config ? config.sizeUnit : 'Byte';
    }
    ByteFormatPipe.prototype.transform = function (value, args) {
        if (parseInt(value, 10) >= 0) {
            value = this.formatBytes(+value, +args);
        }
        return value;
    };
    ByteFormatPipe.prototype.formatBytes = function (bytes, decimals) {
        if (bytes === 0) {
            return '0 ' + this.unit;
        }
        var B = this.unit.charAt(0);
        var k = 1024;
        var dm = decimals || 2;
        var sizes = [this.unit, 'K' + B, 'M' + B, 'G' + B, 'T' + B, 'P' + B, 'E' + B, 'Z' + B, 'Y' + B];
        var i = Math.floor(Math.log(bytes) / Math.log(k));
        return parseFloat((bytes / Math.pow(k, i)).toFixed(dm)) + ' ' + sizes[i];
    };
    ByteFormatPipe.ctorParameters = function () { return [
        { type: FileInputConfig, decorators: [{ type: Optional }, { type: Inject, args: [NGX_MAT_FILE_INPUT_CONFIG,] }] }
    ]; };
    ByteFormatPipe = __decorate([
        Pipe({
            name: 'byteFormat'
        }),
        __param(0, Optional()),
        __param(0, Inject(NGX_MAT_FILE_INPUT_CONFIG)),
        __metadata("design:paramtypes", [FileInputConfig])
    ], ByteFormatPipe);
    return ByteFormatPipe;
}());
var MaterialFileInputModule = /** @class */ (function () {
    function MaterialFileInputModule() {
    }
    MaterialFileInputModule = __decorate([
        NgModule({
            declarations: [FileInputComponent, ByteFormatPipe],
            providers: [FocusMonitor],
            exports: [FileInputComponent, ByteFormatPipe]
        })
    ], MaterialFileInputModule);
    return MaterialFileInputModule;
}());
var FileValidator;
(function (FileValidator) {
    /**
     * Function to control content of files
     *
     * @param bytes max number of bytes allowed
     *
     * @returns
     */
    function maxContentSize(bytes) {
        return function (control) {
            var size = control && control.value ? control.value.files.map(function (f) { return f.size; }).reduce(function (acc, i) { return acc + i; }, 0) : 0;
            var condition = bytes >= size;
            return condition
                ? null
                : {
                    maxContentSize: {
                        actualSize: size,
                        maxSize: bytes
                    }
                };
        };
    }
    FileValidator.maxContentSize = maxContentSize;
    /**
     *
     * @description Handles allowed file types by controlling whether some specific extensions matches with the uploaded file type
     * @export
     * @param {string[]} extensions
     * @returns {ValidatorFn}
     */
    function allowedExtensions(extensions) {
        return function (control) {
            var allowed = control &&
                control.value &&
                control.value.files.every(function (file) {
                    var fileNameSplit = file.name.split('.');
                    var extension = fileNameSplit[fileNameSplit.length - 1];
                    var foundIndex = extensions.indexOf(extension);
                    return foundIndex !== -1;
                });
            return allowed
                ? null
                : {
                    allowedExtensions: extensions
                };
        };
    }
    FileValidator.allowedExtensions = allowedExtensions;
})(FileValidator || (FileValidator = {}));
/**
 * Generated bundle index. Do not edit.
 */
export { ByteFormatPipe, FileInput, FileInputComponent, FileInputConfig, FileValidator, MaterialFileInputModule, NGX_MAT_FILE_INPUT_CONFIG, FileInputBase as ɵa, FileInputMixinBase as ɵb };
//# sourceMappingURL=ngx-mat-file-input.js.map