import {
AfterViewInit,
ChangeDetectorRef,
Component,
ElementRef,
EventEmitter,
HostBinding,
Injector,
Input,
Output,
SkipSelf,
ViewChild
} from "@angular/core";
import {ngValueAccessor, ValueAccessorBase} from "../../core/form";
import {detectChangesNextFrame} from "../app";
@Component({
selector: 'dui-input',
template: `
`,
styleUrls: ['./input.component.scss'],
host: {
'[class.is-textarea]': 'type === "textarea"',
'[class.light-focus]': 'lightFocus !== false',
'[class.semi-transparent]': 'semiTransparent !== false',
},
providers: [ngValueAccessor(InputComponent)]
})
export class InputComponent extends ValueAccessorBase implements AfterViewInit {
@Input() type: string = 'text';
@Input() step: number = 1;
@Input() placeholder: string = '';
@Input() icon: string = '';
@Input() min?: number;
@Input() max?: number;
@Input() maxLength?: number;
@Input() minLength?: number;
@Input() iconSize: number = 17;
/**
* Focuses this element once created (AfterViewInit).
*/
@Input() focus: boolean | '' = false;
/**
* Uses a more decent focus border.
*/
@Input() lightFocus: boolean | '' = false;
/**
* Appears a little bit transparent. Perfect for blurry background.
*/
@Input() semiTransparent: boolean | '' = false;
@Output() esc = new EventEmitter();
@Output() enter = new EventEmitter();
@ViewChild('input', {static: false}) input?: ElementRef;
@Input() textured: boolean | '' = false;
@Input() readonly: boolean | '' = false;
@Output() focusChange = new EventEmitter();
@HostBinding('class.textured')
get isTextured() {
return false !== this.textured;
}
@HostBinding('class.focused')
get isFocused() {
return this.input ? document.activeElement === this.input!.nativeElement : false;
}
@HostBinding('class.filled')
get isFilled() {
return !!this.innerValue;
}
@Input() round: boolean | '' = false;
@HostBinding('class.round')
get isRound() {
return false !== this.round;
}
@Input() clearer: boolean | '' = false;
@HostBinding('class.has-clearer')
get hasClearer() {
return false !== this.clearer;
}
@HostBinding('class.has-icon')
get hasIcon() {
return !!this.icon;
}
constructor(
protected injector: Injector,
public readonly cd: ChangeDetectorRef,
@SkipSelf() public readonly cdParent: ChangeDetectorRef,
) {
super(injector, cd, cdParent);
}
onBlur() {
this.cdParent.detectChanges();
this.focusChange.next(false);
}
onFocus() {
this.cdParent.detectChanges();
this.focusChange.next(true);
}
public async clear() {
this.innerValue = '';
}
/**
* From
*/
setInnerValue(value: any) {
if (this.type === 'file') return;
this.innerValue = value;
}
async writeValue(value?: any) {
if (this.type === 'file' && !value && this.input) {
//we need to manually reset the field, since writing to it via ngModel is not supported.
this.input!.nativeElement.value = '';
}
super.writeValue(value);
}
onKeyDown(event: KeyboardEvent) {
this.touch();
}
onKeyUp(event: KeyboardEvent) {
if (event.key.toLowerCase() === 'enter' && this.type !== 'textarea') {
this.enter.emit(event);
}
if (event.key.toLowerCase() === 'esc' || event.key.toLowerCase() === 'escape') {
this.esc.emit(event);
}
}
focusInput() {
setTimeout(() => {
this.input!.nativeElement.focus();
});
}
ngAfterViewInit() {
if (this.focus !== false && this.input) {
setTimeout(() => {
this.input!.nativeElement.focus();
detectChangesNextFrame(this.cd);
});
}
}
public async handleFileInput(event: any) {
const files = event.target.files;
this.touch();
const readFile = (file: File): Promise => {
return new Promise((resolve, reject) => {
const reader = new FileReader();
reader.onload = () => {
if (reader.result) {
if (reader.result instanceof ArrayBuffer) {
resolve(reader.result);
} else {
resolve();
}
}
};
reader.onerror = (error) => {
console.log('Error: ', error);
reject();
};
reader.readAsArrayBuffer(file);
});
};
if (files) {
if (files.length > 1) {
const value = [];
for (let i = 0; i < files.length; i++) {
const file = files.item(i);
if (file) {
value.push(await readFile(file));
}
}
this.innerValue = value;
} else if (files.length === 1) {
this.innerValue = await readFile(files.item(0));
}
}
}
}