import {Component, Input, OnInit} from '@angular/core';
import {DomSanitizer, SafeStyle} from '@angular/platform-browser';
import {ControlValueAccessor, NG_VALUE_ACCESSOR} from '@angular/forms';
import {HelperService} from 'gp-admin-abstract';
import {ImageUploadModel} from './image-upload.model';
@Component({
selector: 'gp-image-upload',
template: `
`,
styles: [
`@charset "UTF-8";
/**
* Переменные
*/
:root {
--color-white: white;
--color-success: #00afec;
--color-warning: #ffb827;
--color-error: #f35252;
--color-disabled: #dbdbdb;
--color-black: black;
--color-span: #b5b5b5;
--color-span-second: #212121;
--color-bg: #f8f8f8;
--color-bg-second: #f8f8f8;
--color-bg-success: rgba(0, 175, 236, 0.1);
--color-bg-warning: var(--color-warning);
--color-bg-error: var(--color-error);
--color-placeholder: #b5b5b5;
--color-border: #dbdbdb;
--color-radio-border: #d3d3d3;
--font-roboto: 'Roboto', sans-serif;
--font-helvetica: 'Helvetica', sans-serif; }
/**
*
*/
/**
* Стили для компонента "app-realty-image-list"
*/
.img-uploader {
font-family: "Roboto", sans-serif;
margin-top: .8rem;
margin-bottom: .8rem; }
.img-uploader--loaded .img-uploader__label:hover {
cursor: default;
opacity: 1; }
.img-uploader--error .img-uploader__default {
background-image: url("/assets/img/camera--error.svg");
border-color: #f35252; }
.img-uploader--disabled .img-uploader__default {
background-image: url("/assets/img/camera--disabled.svg");
border-color: #b5b5b5; }
.img-uploader__label {
display: block;
cursor: pointer; }
.img-uploader__label:hover {
opacity: 0.5; }
.img-uploader__input {
display: none; }
.img-uploader__title, .img-uploader__description {
font: 500 1.4rem/1.6rem "Roboto", sans-serif;
letter-spacing: .03rem;
margin-bottom: .3rem;
width: 100%; }
.img-uploader__title {
color: #00afec; }
.img-uploader__description {
color: #b5b5b5; }
.img-uploader__info {
align-content: center;
margin-left: 1.6rem; }
.img-uploader__default {
background-image: url("/assets/img/camera.svg");
background-position: center center;
background-repeat: no-repeat;
border: 0.2rem dotted #00afec;
border-radius: .3rem;
width: 6rem;
height: 6rem; }
.img-uploader__img {
background-color: #ccc;
background-position: center;
background-repeat: no-repeat;
background-size: contain;
border-radius: .3rem;
width: 6rem;
height: 6rem;
position: relative;
margin-right: 1.6rem; }
.img-uploader__delete {
background-color: rgba(255, 255, 255, 0.8);
width: 2.5rem;
height: 2.5rem;
position: absolute;
top: 0;
right: 0;
border-radius: 0 0 0 3rem;
cursor: pointer; }
.img-uploader__delete:before, .img-uploader__delete:after {
content: "";
display: block;
width: .2rem;
height: 1rem;
background-color: #f35252;
position: absolute; }
.img-uploader__delete:before {
-moz-transform: translate(1.5rem, 0.5rem) rotate(45deg);
-o-transform: translate(1.5rem, 0.5rem) rotate(45deg);
-ms-transform: translate(1.5rem, 0.5rem) rotate(45deg);
-webkit-transform: translate(1.5rem, 0.5rem) rotate(45deg);
transform: translate(1.5rem, 0.5rem) rotate(45deg); }
.img-uploader__delete:after {
-moz-transform: translate(15px, 5px) rotate(-45deg);
-o-transform: translate(15px, 5px) rotate(-45deg);
-ms-transform: translate(15px, 5px) rotate(-45deg);
-webkit-transform: translate(15px, 5px) rotate(-45deg);
transform: translate(15px, 5px) rotate(-45deg); }
.img-uploader__delete:hover {
background-color: white; }
`],
providers: [
{
provide: NG_VALUE_ACCESSOR,
useExisting: ImageUploadComponent,
multi: true
}
],
})
export class ImageUploadComponent implements OnInit, ControlValueAccessor {
/**
* Список отображемых изображений на форме
*
* @type {Array}
*/
internalImages: any[] = [];
/**
* Список удаленных картинок (полученных с сервера)
*
* @type {Array}
*/
deletedImages: string[] = [];
/**
* Возвращаемое значение
*/
private returnValue: ImageUploadModel;
/**
* Заголовок
* @type {string}
*/
@Input() title = '';
/**
* Описание
* @type {string}
*/
@Input() description = '';
/**
* Количество загружаемых изображений
*
* @type {number}
*/
@Input() maxCountFiles: number;
/**
* Список разрешенных MIME-типов
*/
@Input() mimeTypes: Array | string = ['image/png', 'image/jpeg', 'image/jpg'];
/**
* Максимальный размер файла в байтах
*/
@Input() maxSize: number;
/**
* Кастомная ошибка
* @type {boolean}
*/
@Input() error: string|boolean = false;
/**
* Устанавливает активность компонента
*
* @type {boolean}
*/
@Input() disabled: boolean = false;
/**
* Префикс URL для отображения изображений
* @type {string}
*/
@Input() imageUrlPrefix: string;
constructor(private sanitizer: DomSanitizer,
private helperService: HelperService) {
}
ngOnInit() {
this.returnValue = new ImageUploadModel();
this.internalImages.push({
isLoaded: false,
error: null,
imageGuid: '',
selectedFile: null,
selectedFileSrc: null
});
}
writeValue(value: ImageUploadModel): void {
if (value && value.existsGuids) {
for (const imageGuid of value.existsGuids) {
this.internalImages.push({
isLoaded: true,
error: this.error,
imageGuid,
selectedFile: null,
selectedFileSrc: null
});
}
this.checkForInsert();
this.returnValue.existsGuids = value.existsGuids;
}
}
registerOnChange(fn: any): void {
this.propagateChange = fn;
}
registerOnTouched(fn: any): void {
}
setDisabledState(value: boolean) {
this.disabled = value;
}
propagateChange = (_: any) => {
}
/**
* Выбрали и загружаем картинку
*
* @param e
* @param index
*/
onChange(e: any, index: number) {
if (!this.disabled) {
const el = e.srcElement || e.target;
const file = el.files.length > 0 ? el.files[0] : null;
if (file) {
this.updateModel(file, index);
}
}
}
/**
* Запрещаем изменение картинки
* И если disabled - делаем кнопку неактивной
*
* @param e
* @param index
*/
onClick(e: any, index: number) {
if (this.disabled || this.internalImages[index] && this.internalImages[index].isLoaded) {
e.preventDefault();
}
}
/**
* Проверка картинки на загрузку
*
* @param file
* @param index
*/
private updateModel(file: File, index: number) {
if (this.mimeTypes.indexOf(file.type) === -1) {
this.internalImages[index].error = 'Неверный тип файла!';
this.internalImages[index].isLoaded = false;
} else if (!!this.maxSize && (file.size > this.maxSize)) {
const pretty = this.helperService.filesize(this.maxSize);
this.internalImages[index].error = `Размер превышает ${pretty}`;
this.internalImages[index].isLoaded = false;
} else {
this.internalImages[index] = {
isLoaded: true,
error: false,
selectedFile: file,
selectedFileSrc: URL.createObjectURL(file)
};
this.propagateChange(this.returnValues());
this.checkForInsert();
}
}
/**
* Удалить загруженную картинку
*
* @param e
* @param index
*/
deleteLoadedImage(e: Event, index: number) {
e.preventDefault();
if (this.internalImages[index].imageGuid) {
this.deletedImages.push(this.internalImages[index].imageGuid);
}
this.internalImages.splice(index, 1);
this.propagateChange(this.returnValues());
this.checkForInsert();
}
reset() {
this.internalImages = [];
this.deletedImages = [];
this.returnValue = new ImageUploadModel();
this.internalImages.push({
isLoaded: false,
error: null,
imageGuid: '',
selectedFile: null,
selectedFileSrc: null
});
}
/**
* Получить загруженную картинку и установить ее отображение
*
* @returns {SafeStyle}
*/
public imageSrc(index: number): SafeStyle {
const src = (this.internalImages[index] && this.internalImages[index].selectedFile)
? this.internalImages[index].selectedFileSrc : this.imageUrlPrefix + this.internalImages[index].imageGuid;
return this.sanitizer.bypassSecurityTrustStyle('url(' + src + ')');
}
/**
* Получить разрешенные типы картинки
*
* @returns {any}
*/
get acceptMimeTypes(): string {
return (this.mimeTypes instanceof Array) ? this.mimeTypes.join(',') : this.mimeTypes;
}
/**
* Проверка на отображение кнопки "добавления фото"
*/
private checkForInsert() {
const alreadyShowInsert = this.internalImages.find(f => f.isLoaded === false),
currentLength = alreadyShowInsert ? this.internalImages.length - 1 : this.internalImages.length;
if (!alreadyShowInsert && (currentLength < this.maxCountFiles)) {
this.internalImages.push({
isLoaded: false,
error: this.error,
imageGuid: null,
selectedFile: null,
selectedFileSrc: null
});
} else if (alreadyShowInsert && (currentLength === this.maxCountFiles)) {
const index = this.internalImages.indexOf(alreadyShowInsert);
this.internalImages.splice(index, 1);
}
}
/**
* Возвращаемое значение
*
* @returns {Object}
*/
private returnValues() {
const uploadedImages = this.internalImages
.filter(f => f.isLoaded === true && f.selectedFile)
.map((f) => f.selectedFile);
this.returnValue.addedFiles = uploadedImages;
this.returnValue.deletedGuids = this.deletedImages;
return this.returnValue;
}
}