import {
Input, Component, ElementRef, NgZone, Renderer2, ChangeDetectorRef,
TemplateRef, OnInit, AfterContentInit, forwardRef,
ViewEncapsulation, Output, EventEmitter, OnChanges, SimpleChanges, HostListener, ChangeDetectionStrategy, ViewChild, Inject
} from '@angular/core';
import { NG_VALUE_ACCESSOR, ControlValueAccessor } from '@angular/forms';
import { Observable } from 'rxjs';
import { LookupGridResult, ServerSideToken } from '@farris/ui-lookup';
import { KeyCode, SelectItem, DEFAULT_COMBOSTATE, defaultComboState, COMBO_FACADE_SERVICE } from '../core/combo-types';
import { ComboComponent } from '../combo.component';
import { ComboFacadeService } from '../core/combo-facade.service';
import { ComboService } from '../combo.service';
import { CommonUtils, FarrisDataItem } from '@farris/ui-common';
import { map, debounceTime } from 'rxjs/operators';
import { DatalistComponent } from '@farris/ui-datalist';
export const INPUT_COMBOBOX_VALUE_ACCESSOR: any = {
provide: NG_VALUE_ACCESSOR,
useExisting: forwardRef(() => CombolistComponent),
multi: true
};
@Component({
selector: 'farris-combolist',
template: `
`,
providers: [
INPUT_COMBOBOX_VALUE_ACCESSOR,
{provide: DEFAULT_COMBOSTATE, useValue: defaultComboState},
{provide: COMBO_FACADE_SERVICE, useClass: ComboFacadeService, deps: [ DEFAULT_COMBOSTATE ] }
],
encapsulation: ViewEncapsulation.None,
changeDetection: ChangeDetectionStrategy.OnPush
})
export class CombolistComponent extends ComboComponent implements OnInit, OnChanges, AfterContentInit, ControlValueAccessor {
@Input() multiSelect = false;
@Input() uri: string;
@Input() data: any;
@Input() disableExpress: (item) => boolean;
/**
* 映射字段 { helpField: targetField }
* - helpField: 控件数据源中的字段
* - targetField: 目标字段
*/
@Input() mapFields: { [helpField: string]: string };
@Output() selected = new EventEmitter();
@Output() unSelected = new EventEmitter();
@Output() selectedValuesChange = new EventEmitter();
@Input() itemTemplate: TemplateRef;
private _dl: DatalistComponent;
@ViewChild(DatalistComponent) set dlComp(cmp: DatalistComponent) {
this._dl = cmp;
}
data$ = this.cmbFacade.data$.pipe(
map((d: FarrisDataItem[]) => {
return d.map( item => item.data );
})
);
constructor(
public el: ElementRef,
public zone: NgZone,
public render: Renderer2,
public cd: ChangeDetectorRef,
@Inject(COMBO_FACADE_SERVICE) public cmbFacade: ComboFacadeService,
public cmbState: ComboService,
private utils: CommonUtils
) {
super(el, zone, render, cd, cmbFacade, cmbState);
}
ngOnInit() {
super.ngOnInit();
const { idField, textField, valueField, multiSelect } = {...this};
this.cmbFacade.initControlState({ idField, valueField, textField, multiSelect});
if (this.disableExpress) {
this.cmbFacade.disableExpress = this.disableExpress;
}
if (this.data) {
this.cmbFacade.loadData(this.data, this.selectedValues);
}
this.cmbFacade.selections$.pipe(
debounceTime(0)
).subscribe( items => {
if (items && items.length) {
this.displayText = items.map(item => this.utils.getValue(this.textField, item)).join(',');
} else {
this.displayText = '';
}
this.onModelChange(this.displayText);
this.updateValues(items);
this.updateChanges();
});
}
ngOnChanges(changes: SimpleChanges) {
}
ngAfterContentInit() {
}
@HostListener('keydown', ['$event'])
handleKeyDown($event: KeyboardEvent) {
if (KeyCode[$event.which]) {
switch ($event.which) {
case KeyCode.ArrowDown:
this._handleArrowDown($event);
break;
case KeyCode.ArrowUp:
this._handleArrowUp($event);
break;
// case KeyCode.Space:
// this._handleSpace($event);
// break;
// case KeyCode.Enter:
// this._handleEnter($event);
// break;
case KeyCode.Tab:
this._handleTab($event);
break;
case KeyCode.Esc:
this.close();
$event.preventDefault();
$event.stopPropagation();
break;
// case KeyCode.Backspace:
// this._handleBackspace();
// break;
}
}
// else if ($event.key && $event.key.length === 1) {
// this._keyPress$.next($event.key.toLocaleLowerCase());
// }
}
getData(uri: string, params?: any): Observable {
return null;
}
clearSelections() {
this.setValue('');
this.cmbFacade.clearSelections();
}
onValueChanged(val: any) {
this.onModelChange(val);
if (!val) {
this.cmbFacade.clearSelections();
}
this.valueChange.emit(val);
}
@HostListener('mousedown', ['$event'])
handleMousedown($event: MouseEvent) {
const target = $event.target as HTMLElement;
if (target.tagName === 'INPUT' || target.tagName === 'I') {
return;
}
$event.preventDefault();
$event.stopPropagation();
}
private _handleArrowDown(event: any) {
}
private _handleArrowUp(event: any) {}
private toggleSelect(selectItem: SelectItem, isSelected: boolean) {
if (isSelected) {
this.selected.emit(selectItem);
} else {
this.unSelected.emit(selectItem);
}
if (!this.multiSelect) {
this.cmbFacade.hidePanel();
}
}
private updateChanges() {
this.cd.markForCheck();
this.cd.detectChanges();
}
private updateValues(selections: any[]) {
if (selections && selections.length) {
const val = selections.map(item => item[this.valueField]).join(',');
this.setValue(val);
} else {
this.setValue('');
}
}
getSelections() {
return this.cmbFacade.getSelections();
}
setValue(val: string) {
this.selectedValuesChange.emit(val);
}
onSelect($event: SelectItem) {
const { data, index } = {...$event};
this.cmbFacade.selectItem(data, index);
this.toggleSelect($event, true);
}
onUnSelected($event: SelectItem) {
const { data, index } = {...$event};
this.cmbFacade.unSelectItem(data);
this.toggleSelect($event, false);
}
onClear() {
this.cmbFacade.clearSelections();
if (this._dl) {
this._dl.clearSelections();
}
this.clear.emit();
}
writeValue(obj: any): void {
this.displayText = obj;
this.updateChanges();
}
registerOnChange(fn: any): void {
this.onModelChange = fn;
}
registerOnTouched(fn: any): void {
this.onModelTouched = fn;
}
setDisabledState?(isDisabled: boolean): void {
this.setDisable(isDisabled);
}
}