import {NgModule,Component,ElementRef,Input,Output,EventEmitter,AfterContentInit,ContentChildren,QueryList,TemplateRef,forwardRef,ViewChild,ChangeDetectionStrategy, ViewEncapsulation, ChangeDetectorRef} from '@angular/core';
import {CommonModule} from '@angular/common';
import {SharedModule,PrimeTemplate} from 'mirra-ui/api';
import {InputTextModule} from 'mirra-ui/inputtext';
import {NG_VALUE_ACCESSOR, ControlValueAccessor} from '@angular/forms';
export const CHIPS_VALUE_ACCESSOR: any = {
provide: NG_VALUE_ACCESSOR,
useExisting: forwardRef(() => Chips),
multi: true
};
@Component({
selector: 'p-chips',
template: `
`,
host: {
'[class.p-inputwrapper-filled]': 'filled',
'[class.p-inputwrapper-focus]': 'focus'
},
providers: [CHIPS_VALUE_ACCESSOR],
changeDetection: ChangeDetectionStrategy.OnPush,
encapsulation: ViewEncapsulation.None,
styleUrls: ['./chips.scss']
})
export class Chips implements AfterContentInit,ControlValueAccessor {
@Input() style: any;
@Input() styleClass: string;
@Input() disabled: boolean;
@Input() field: string;
@Input() placeholder: string;
@Input() max: number;
@Input() ariaLabelledBy: string;
@Input() tabindex: number;
@Input() inputId: string;
@Input() allowDuplicate: boolean = true;
@Input() inputStyle: any;
@Input() inputStyleClass: any;
@Input() addOnTab: boolean;
@Input() addOnBlur: boolean;
@Input() separator: string;
@Output() onAdd: EventEmitter = new EventEmitter();
@Output() onRemove: EventEmitter = new EventEmitter();
@Output() onFocus: EventEmitter = new EventEmitter();
@Output() onBlur: EventEmitter = new EventEmitter();
@Output() onChipClick: EventEmitter = new EventEmitter();
@ViewChild('inputtext') inputViewChild: ElementRef;
@ContentChildren(PrimeTemplate) templates: QueryList;
public itemTemplate: TemplateRef;
value: any;
onModelChange: Function = () => {};
onModelTouched: Function = () => {};
valueChanged: boolean;
focus: boolean;
filled: boolean;
constructor(public el: ElementRef, private cd: ChangeDetectorRef) {}
ngAfterContentInit() {
this.templates.forEach((item) => {
switch(item.getType()) {
case 'item':
this.itemTemplate = item.template;
break;
default:
this.itemTemplate = item.template;
break;
}
});
}
onClick() {
this.inputViewChild.nativeElement.focus();
}
onInput() {
this.updateFilledState();
}
onPaste(event) {
if (this.separator) {
let pastedData = (event.clipboardData || window['clipboardData']).getData('Text');
pastedData.split(this.separator).forEach(val => {
this.addItem(event, val, true);
});
this.inputViewChild.nativeElement.value = '';
}
this.updateFilledState();
}
updateFilledState() {
if (!this.value || this.value.length === 0) {
this.filled = (this.inputViewChild.nativeElement && this.inputViewChild.nativeElement.value != '');
}
else {
this.filled = true;
}
}
onItemClick(event: Event, item: any) {
this.onChipClick.emit({
originalEvent: event,
value: item
});
}
writeValue(value: any) : void {
this.value = value;
this.updateMaxedOut();
this.cd.markForCheck();
}
registerOnChange(fn: Function): void {
this.onModelChange = fn;
}
registerOnTouched(fn: Function): void {
this.onModelTouched = fn;
}
setDisabledState(val: boolean): void {
this.disabled = val;
}
resolveFieldData(data: any, field: string): any {
if (data && field) {
if (field.indexOf('.') == -1) {
return data[field];
}
else {
let fields: string[] = field.split('.');
let value = data;
for(var i = 0, len = fields.length; i < len; ++i) {
value = value[fields[i]];
}
return value;
}
}
else {
return null;
}
}
onInputFocus(event: FocusEvent) {
this.focus = true;
this.onFocus.emit(event);
}
onInputBlur(event: FocusEvent) {
this.focus = false;
if (this.addOnBlur && this.inputViewChild.nativeElement.value) {
this.addItem(event, this.inputViewChild.nativeElement.value, false);
}
this.onModelTouched();
this.onBlur.emit(event);
}
removeItem(event: Event, index: number): void {
if (this.disabled) {
return;
}
let removedItem = this.value[index];
this.value = this.value.filter((val, i) => i!=index);
this.onModelChange(this.value);
this.onRemove.emit({
originalEvent: event,
value: removedItem
});
this.updateFilledState();
this.updateMaxedOut();
}
addItem(event: Event, item: string, preventDefault: boolean): void {
this.value = this.value||[];
if (item && item.trim().length) {
if (this.allowDuplicate || this.value.indexOf(item) === -1) {
this.value = [...this.value, item];
this.onModelChange(this.value);
this.onAdd.emit({
originalEvent: event,
value: item
});
}
}
this.updateFilledState();
this.updateMaxedOut();
this.inputViewChild.nativeElement.value = '';
if (preventDefault) {
event.preventDefault();
}
}
onKeydown(event: KeyboardEvent): void {
switch(event.which) {
//backspace
case 8:
if (this.inputViewChild.nativeElement.value.length === 0 && this.value && this.value.length > 0) {
this.value = [...this.value];
let removedItem = this.value.pop();
this.onModelChange(this.value);
this.onRemove.emit({
originalEvent: event,
value: removedItem
});
this.updateFilledState();
}
break;
//enter
case 13:
this.addItem(event, this.inputViewChild.nativeElement.value, true);
break;
case 9:
if (this.addOnTab && this.inputViewChild.nativeElement.value !== '') {
this.addItem(event, this.inputViewChild.nativeElement.value, true);
}
break;
default:
if (this.max && this.value && this.max === this.value.length) {
event.preventDefault();
}
else if (this.separator) {
if (this.separator === ',' && event.which === 188) {
this.addItem(event, this.inputViewChild.nativeElement.value, true);
}
}
break;
}
}
updateMaxedOut() {
if (this.inputViewChild && this.inputViewChild.nativeElement) {
if (this.max && this.value && this.max === this.value.length)
this.inputViewChild.nativeElement.disabled = true;
else
this.inputViewChild.nativeElement.disabled = this.disabled || false;
}
}
}
@NgModule({
imports: [CommonModule,InputTextModule,SharedModule],
exports: [Chips,InputTextModule,SharedModule],
declarations: [Chips]
})
export class ChipsModule { }