import {NgModule,Component,ElementRef,OnInit,AfterViewInit,AfterContentInit,AfterViewChecked,OnDestroy,Input,Output,Renderer2,EventEmitter,
forwardRef,ViewChild,ChangeDetectorRef,TemplateRef,ContentChildren,QueryList,ContentChild} from '@angular/core';
import {CommonModule} from '@angular/common';
import {SelectItem} from '../common/selectitem';
import {DomHandler} from '../dom/domhandler';
import {ObjectUtils} from '../utils/objectutils';
import {SharedModule,PrimeTemplate,Footer} from '../common/shared';
import {NG_VALUE_ACCESSOR, ControlValueAccessor} from '@angular/forms';
export const MULTISELECT_VALUE_ACCESSOR: any = {
provide: NG_VALUE_ACCESSOR,
useExisting: forwardRef(() => Select),
multi: true
};
@Component({
selector: 'p-select',
template: `
`,
host: {
'[class.ui-inputwrapper-filled]': 'filled',
'[class.ui-inputwrapper-focus]': 'focus'
},
providers: [DomHandler,ObjectUtils,MULTISELECT_VALUE_ACCESSOR]
})
export class Select implements OnInit,AfterViewInit,AfterContentInit,AfterViewChecked,OnDestroy,ControlValueAccessor {
@Input() scrollHeight: string = '200px';
@Input() panelWidth: string = "200px";
@Input() multi: boolean = false;
@Input() defaultLabel: string = '请选择';
@Input() style: any;
@Input() styleClass: string;
@Input() panelStyle: any;
@Input() panelStyleClass: string;
@Input() inputId: string;
@Input() disabled: boolean;
@Input() filter: boolean = true;
@Input() filterPlaceHolder: string;
@Input() overlayVisible: boolean;
@Input() tabindex: number;
@Input() appendTo: any;
@Input() dataKey: string;
@Input() name: string;
@Input() displaySelectedLabel: boolean = true;
@Input() maxSelectedLabels: number = 3;
@Input() selectedItemsLabel: string = '已选择 {0} 个';
@Input() showToggleAll: boolean = true;
@Input() resetFilterOnHide: boolean = false;
@Input() dropdownIcon: string = 'icon ion-arrow-down-b';
@Input() optionLabel: string;
@Input() showHeader: boolean = true;
@ViewChild('container') containerViewChild: ElementRef;
@ViewChild('panel') panelViewChild: ElementRef;
@ViewChild('filterInput') filterInputChild: ElementRef;
@ContentChild(Footer) footerFacet;
@ContentChildren(PrimeTemplate) templates: QueryList;
@Output() onChange: EventEmitter = new EventEmitter();
@Output() onFocus: EventEmitter = new EventEmitter();
@Output() onBlur: EventEmitter = new EventEmitter();
@Output() onPanelShow: EventEmitter = new EventEmitter();
@Output() onPanelHide: EventEmitter = new EventEmitter();
public value: any[];
public onModelChange: Function = () => {};
public onModelTouched: Function = () => {};
public valuesAsString: string;
public focus: boolean;
filled: boolean;
public documentClickListener: any;
public container: HTMLDivElement;
public panel: HTMLDivElement;
public selfClick: boolean;
public panelClick: boolean;
public filterValue: string;
public visibleOptions: SelectItem[];
public filtered: boolean;
public itemTemplate: TemplateRef;
public focusedItemCheckbox: HTMLInputElement;
_options: any[];
constructor(public el: ElementRef, public domHandler: DomHandler, public renderer: Renderer2, public objectUtils: ObjectUtils, private cd: ChangeDetectorRef) {}
@Input() get options(): any[] {
return this._options;
}
set options(val: any[]) {
let opts = this.optionLabel ? this.objectUtils.generateSelectItems(val, this.optionLabel) : val;
this._options = opts;
this.updateLabel();
}
ngOnInit() {
this.updateLabel();
}
ngAfterContentInit() {
this.templates.forEach((item) => {
switch(item.getType()) {
case 'item':
this.itemTemplate = item.template;
break;
default:
this.itemTemplate = item.template;
break;
}
});
}
ngAfterViewInit() {
this.container = this.containerViewChild.nativeElement;
this.panel = this.panelViewChild.nativeElement;
if(this.appendTo) {
if(this.appendTo === 'body')
document.body.appendChild(this.panel);
else
this.domHandler.appendChild(this.panel, this.appendTo);
}
if(this.overlayVisible) {
this.show();
}
}
ngAfterViewChecked() {
if(this.filtered) {
if(this.appendTo)
this.domHandler.absolutePosition(this.panel, this.container);
else
this.domHandler.relativePosition(this.panel, this.container);
this.filtered = false;
}
}
writeValue(value: any) : void {
this.value = value;
this.updateLabel();
this.updateFilledState();
this.cd.markForCheck();
}
updateFilledState() {
this.filled = (this.valuesAsString != null && this.valuesAsString.length > 0);
}
registerOnChange(fn: Function): void {
this.onModelChange = fn;
}
registerOnTouched(fn: Function): void {
this.onModelTouched = fn;
}
setDisabledState(val: boolean): void {
this.disabled = val;
}
onItemClick(event, option) {
if(this.multi){
let selectionIndex = this.findSelectionIndex(option);
if(selectionIndex != -1)
this.value = this.value.filter((val,i) => i!=selectionIndex);
else
this.value = [...this.value||[],option];
this.onModelChange(this.value);
}else {
this.value = [option];
this.onModelChange(this.value[0]);
this.hide();
}
this.onChange.emit({originalEvent: event, value: this.value, itemValue: option.label});
this.updateLabel();
this.updateFilledState();
}
isSelected(value) {
return this.findSelectionIndex(value) != -1;
}
findSelectionIndex(val: any): number {
let index = -1;
if(this.value) {
for(let i = 0; i < this.value.length; i++) {
if(this.objectUtils.equals(this.value[i].label, val.label, this.dataKey)) {
index = i;
break;
}
}
}
return index;
}
toggleAll(event, checkbox) {
if(checkbox.checked) {
this.value = [];
}
else {
let opts = this.getVisibleOptions();
if(opts) {
this.value = [];
for(let i = 0; i < opts.length; i++) {
this.value.push(opts[i].value);
}
}
}
checkbox.checked = !checkbox.checked;
this.onModelChange(this.value);
this.onChange.emit({originalEvent: event, value: this.value});
this.updateLabel();
}
isAllChecked() {
if(this.filterValue && this.filterValue.trim().length)
return this.value&&this.visibleOptions&&this.visibleOptions.length&&(this.value.length == this.visibleOptions.length);
else
return this.value&&this.options&&(this.value.length == this.options.length);
}
show() {
this.overlayVisible = true;
this.panel.style.zIndex = String(++DomHandler.zindex);
this.bindDocumentClickListener();
if(this.appendTo)
this.domHandler.absolutePosition(this.panel, this.container);
else
this.domHandler.relativePosition(this.panel, this.container);
this.domHandler.fadeIn(this.panel, 250);
this.onPanelShow.emit();
}
hide() {
this.overlayVisible = false;
this.unbindDocumentClickListener();
if(this.resetFilterOnHide){
this.filterValue = null;
this.filterInputChild.nativeElement.value = null;
}
this.onPanelHide.emit();
}
close(event) {
this.hide();
event.preventDefault();
event.stopPropagation();
}
onMouseclick(event,input) {
if(this.disabled) {
return;
}
if(!this.panelClick) {
if(this.overlayVisible) {
this.hide();
}
else {
input.focus();
this.show();
}
}
this.selfClick = true;
}
onInputFocus(event) {
this.focus = true;
this.onFocus.emit({originalEvent: event});
}
onInputBlur(event) {
this.focus = false;
this.onBlur.emit({originalEvent: event});
this.onModelTouched();
}
onInputKeydown(event) {
switch(event.which) {
//down
case 40:
if(!this.overlayVisible && event.altKey) {
this.show();
}
event.preventDefault();
break;
//escape and tab
case 27:
case 9:
this.hide();
break;
}
}
updateLabel() {
if(this.value && this.options && this.value.length && this.displaySelectedLabel) {
let label = '';
for(let i = 0; i < this.value.length; i++) {
let itemLabel = this.findLabelByValue(this.value[i].id);
if (itemLabel) {
if(label.length > 0) {
label = label + ', ';
}
label = label + itemLabel;
}
}
if(this.value.length <= this.maxSelectedLabels) {
this.valuesAsString = label;
}
else {
let pattern = /{(.*?)}/;
if (pattern.test(this.selectedItemsLabel)) {
this.valuesAsString = this.selectedItemsLabel.replace(this.selectedItemsLabel.match(pattern)[0], this.value.length + '');
}
}
}
else {
this.valuesAsString = this.defaultLabel;
}
}
findLabelByValue(id: any): string {
let label = null;
for(let i = 0; i < this.options.length; i++) {
let option = this.options[i];
if(id == null || this.objectUtils.equals(id, option.id, this.dataKey)) {
label = option.label;
break;
}
if(option.children){
for(let s = 0; s < option.children.length; s++) {
let children = option.children[s];
if(id == null || this.objectUtils.equals(id, children.id, this.dataKey)) {
label = children.label;
break;
}
}
}
}
return label;
}
onFilter(event) {
this.filterValue = event.target.value.trim().toLowerCase();
this.visibleOptions = [];
for(let i = 0; i < this.options.length; i++) {
let option = this.options[i];
if(option.label.toLowerCase().indexOf(this.filterValue.toLowerCase()) > -1) {
this.visibleOptions.push(option);
}
}
this.filtered = true;
}
isItemVisible(option: SelectItem): boolean {
if(this.filterValue && this.filterValue.trim().length) {
for(let i = 0; i < this.visibleOptions.length; i++) {
if(this.visibleOptions[i].value == option.value) {
return true;
}
}
}
else {
return true;
}
}
getVisibleOptions(): SelectItem[] {
if(this.filterValue && this.filterValue.trim().length) {
let items = [];
for(let i = 0; i < this.options.length; i++) {
let option = this.options[i];
if(option.label.toLowerCase().includes(this.filterValue.toLowerCase())) {
items.push(option);
}
}
return items;
}
else {
return this.options;
}
}
bindDocumentClickListener() {
if(!this.documentClickListener) {
this.documentClickListener = this.renderer.listen('document', 'click', () => {
if(!this.selfClick && !this.panelClick && this.overlayVisible) {
this.hide();
}
this.selfClick = false;
this.panelClick = false;
this.cd.markForCheck();
});
}
}
unbindDocumentClickListener() {
if(this.documentClickListener) {
this.documentClickListener();
this.documentClickListener = null;
}
}
ngOnDestroy() {
this.unbindDocumentClickListener();
if(this.appendTo) {
this.container.appendChild(this.panel);
}
}
}
@NgModule({
imports: [CommonModule,SharedModule],
exports: [Select,SharedModule],
declarations: [Select]
})
export class SelectModule { }