import { NgModule, Component, ElementRef, Input, Output, EventEmitter, AfterContentInit, ContentChildren, ContentChild, QueryList, TemplateRef, IterableDiffers, forwardRef, ChangeDetectorRef } from '@angular/core'; import { CommonModule } from '@angular/common'; import { SelectItem } from '../common/selectitem'; import { SharedModule, PrimeTemplate, Footer, Header } from '../common/shared'; import { DomHandler } from '../dom/domhandler'; import { ObjectUtils } from '../utils/objectutils'; import { NG_VALUE_ACCESSOR, ControlValueAccessor } from '@angular/forms'; export const LISTBOX_VALUE_ACCESSOR: any = { provide: NG_VALUE_ACCESSOR, useExisting: forwardRef(() => Listbox), multi: true }; @Component({ selector: 'p-listbox', template: `
`, providers: [DomHandler, ObjectUtils, LISTBOX_VALUE_ACCESSOR] }) export class Listbox implements AfterContentInit, ControlValueAccessor { @Input() multiple: boolean; @Input() style: any; @Input() styleClass: string; @Input() listStyle: any; @Input() readonly: boolean; @Input() disabled: boolean; @Input() checkbox: boolean = false; @Input() filter: boolean = false; @Input() filterMode: string = 'contains'; @Input() metaKeySelection: boolean = true; @Input() dataKey: string; @Input() showToggleAll: boolean = true; @Input() optionLabel: string; @Output() onChange: EventEmitter = new EventEmitter(); @Output() onDblClick: EventEmitter = new EventEmitter(); @ContentChild(Header) headerFacet; @ContentChild(Footer) footerFacet; @ContentChildren(PrimeTemplate) templates: QueryList; public itemTemplate: TemplateRef; public filterValue: string; public filtered: boolean; public value: any; public onModelChange: Function = () => { }; public onModelTouched: Function = () => { }; public checkboxClick: boolean; public optionTouched: boolean; public focus: boolean; public _options: any[]; constructor(public el: ElementRef, public domHandler: DomHandler, public objectUtils: ObjectUtils, public 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; } ngAfterContentInit() { this.templates.forEach((item) => { switch (item.getType()) { case 'item': this.itemTemplate = item.template; break; default: this.itemTemplate = item.template; break; } }); } writeValue(value: any): void { this.value = value; this.cd.markForCheck(); } registerOnChange(fn: Function): void { this.onModelChange = fn; } registerOnTouched(fn: Function): void { this.onModelTouched = fn; } setDisabledState(val: boolean): void { this.disabled = val; } onOptionClick(event, option) { if (this.disabled || this.readonly) { return; } if (!this.checkboxClick) { if (this.multiple) this.onOptionClickMultiple(event, option); else this.onOptionClickSingle(event, option); } else { this.checkboxClick = false; } this.optionTouched = false; } onOptionTouchEnd(event, option) { if (this.disabled || this.readonly) { return; } this.optionTouched = true; } onOptionClickSingle(event, option) { let selected = this.isSelected(option); let valueChanged = false; let metaSelection = this.optionTouched ? false : this.metaKeySelection; if (metaSelection) { let metaKey = (event.metaKey || event.ctrlKey); if (selected) { if (metaKey) { this.value = null; valueChanged = true; } } else { this.value = option.value; valueChanged = true; } } else { this.value = selected ? null : option.value; valueChanged = true; } if (valueChanged) { this.onModelChange(this.value); this.onChange.emit({ originalEvent: event, value: this.value }); } } onOptionClickMultiple(event, option) { let selected = this.isSelected(option); let valueChanged = false; let metaSelection = this.optionTouched ? false : this.metaKeySelection; if (metaSelection) { let metaKey = (event.metaKey || event.ctrlKey); if (selected) { if (metaKey) { this.removeOption(option); } else { this.value = [option.value]; } valueChanged = true; } else { this.value = (metaKey) ? this.value || [] : []; this.value = [...this.value, option.value]; valueChanged = true; } } else { if (selected) { this.removeOption(option); } else { this.value = [...this.value || [], option.value]; } valueChanged = true; } if (valueChanged) { this.onModelChange(this.value); this.onChange.emit({ originalEvent: event, value: this.value }); } } removeOption(option: any): void { this.value = this.value.filter(val => !this.objectUtils.equals(val, option.value, this.dataKey)); } isSelected(option: SelectItem) { let selected = false; if (this.multiple) { if (this.value) { for (let val of this.value) { if (this.objectUtils.equals(val, option.value, this.dataKey)) { selected = true; break; } } } } else { selected = this.objectUtils.equals(this.value, option.value, this.dataKey); } return selected; } get allChecked(): boolean { if (this.filterValue) return this.allFilteredSelected(); else return this.value && this.options && (this.value.length === this.options.length); } allFilteredSelected(): boolean { let allSelected: boolean; if (this.value && this.options && this.options.length)  { allSelected = true; for (let opt of this.options) { if (this.isItemVisible(opt)) { if (!this.isSelected(opt)) { allSelected = false; break; } } } } return allSelected; } onFilter(event) { let query = event.target.value.trim().toLowerCase(); this.filterValue = query.length ? query : null; } toggleAll(event, checkbox) { if (this.disabled || this.readonly || !this.options || this.options.length === 0) { return; } if (checkbox.checked) { this.value = []; } else { if (this.options) { this.value = []; for (let i = 0; i < this.options.length; i++) { let opt = this.options[i]; if (this.isItemVisible(opt)) { this.value.push(opt.value); } } } } checkbox.checked = !checkbox.checked; this.onModelChange(this.value); this.onChange.emit({ originalEvent: event, value: this.value }); } isItemVisible(option: SelectItem): boolean { if (this.filterValue) { let visible; switch (this.filterMode) { case 'startsWith': visible = option.label.toLowerCase().indexOf(this.filterValue.toLowerCase()) === 0; break; case 'contains': visible = option.label.toLowerCase().indexOf(this.filterValue.toLowerCase()) > -1; break; default: visible = true; } return visible; } else { return true; } } onDoubleClick(event: Event, option: SelectItem): any { if (this.disabled || this.readonly) { return; } this.onDblClick.emit({ originalEvent: event, value: this.value }) } onCheckboxClick(event: Event, option: SelectItem) { if (this.disabled || this.readonly) { return; } this.checkboxClick = true; let selected = this.isSelected(option); if (selected) { this.removeOption(option); } else { this.value = this.value ? this.value : []; this.value = [...this.value, option.value]; } this.onModelChange(this.value); this.onChange.emit({ originalEvent: event, value: this.value }); } onInputFocus(event) { this.focus = true; } onInputBlur(event) { this.focus = false; } } @NgModule({ imports: [CommonModule, SharedModule], exports: [Listbox, SharedModule], declarations: [Listbox] }) export class ListboxModule { }