937 lines
40 KiB
JavaScript
937 lines
40 KiB
JavaScript
import { CdkVirtualScrollViewport, ScrollingModule } from '@angular/cdk/scrolling';
|
|
import { forwardRef, EventEmitter, Component, Input, Output, ChangeDetectionStrategy, ViewEncapsulation, ElementRef, Renderer2, ChangeDetectorRef, NgZone, ViewChild, ContentChildren, NgModule } from '@angular/core';
|
|
import { trigger, transition, style, animate } from '@angular/animations';
|
|
import { CommonModule } from '@angular/common';
|
|
import { PrimeTemplate, SharedModule } from 'primeng/api';
|
|
import { DomHandler, ConnectedOverlayScrollHandler } from 'primeng/dom';
|
|
import { ObjectUtils, FilterUtils } from 'primeng/utils';
|
|
import { NG_VALUE_ACCESSOR } from '@angular/forms';
|
|
import { TooltipModule } from 'primeng/tooltip';
|
|
import { RippleModule } from 'primeng/ripple';
|
|
|
|
const DROPDOWN_VALUE_ACCESSOR = {
|
|
provide: NG_VALUE_ACCESSOR,
|
|
useExisting: forwardRef(() => Dropdown),
|
|
multi: true
|
|
};
|
|
class DropdownItem {
|
|
constructor() {
|
|
this.onClick = new EventEmitter();
|
|
}
|
|
onOptionClick(event) {
|
|
this.onClick.emit({
|
|
originalEvent: event,
|
|
option: this.option
|
|
});
|
|
}
|
|
}
|
|
DropdownItem.decorators = [
|
|
{ type: Component, args: [{
|
|
selector: 'p-dropdownItem',
|
|
template: `
|
|
<li (click)="onOptionClick($event)" role="option" pRipple
|
|
[attr.aria-label]="option.label" [attr.aria-selected]="selected"
|
|
[ngStyle]="{'height': itemSize + 'px'}"
|
|
[ngClass]="{'p-dropdown-item':true, 'p-highlight': selected, 'p-disabled':(option.disabled)}">
|
|
<span *ngIf="!template">{{option.label||'empty'}}</span>
|
|
<ng-container *ngTemplateOutlet="template; context: {$implicit: option}"></ng-container>
|
|
</li>
|
|
`
|
|
},] }
|
|
];
|
|
DropdownItem.propDecorators = {
|
|
option: [{ type: Input }],
|
|
selected: [{ type: Input }],
|
|
disabled: [{ type: Input }],
|
|
visible: [{ type: Input }],
|
|
itemSize: [{ type: Input }],
|
|
template: [{ type: Input }],
|
|
onClick: [{ type: Output }]
|
|
};
|
|
class Dropdown {
|
|
constructor(el, renderer, cd, zone) {
|
|
this.el = el;
|
|
this.renderer = renderer;
|
|
this.cd = cd;
|
|
this.zone = zone;
|
|
this.scrollHeight = '200px';
|
|
this.filterBy = 'label';
|
|
this.resetFilterOnHide = false;
|
|
this.dropdownIcon = 'pi pi-chevron-down';
|
|
this.autoDisplayFirst = true;
|
|
this.emptyFilterMessage = 'No results found';
|
|
this.autoZIndex = true;
|
|
this.baseZIndex = 0;
|
|
this.showTransitionOptions = '.12s cubic-bezier(0, 0, 0.2, 1)';
|
|
this.hideTransitionOptions = '.1s linear';
|
|
this.filterMatchMode = "contains";
|
|
this.tooltip = '';
|
|
this.tooltipPosition = 'right';
|
|
this.tooltipPositionStyle = 'absolute';
|
|
this.autofocusFilter = true;
|
|
this.onChange = new EventEmitter();
|
|
this.onFocus = new EventEmitter();
|
|
this.onBlur = new EventEmitter();
|
|
this.onClick = new EventEmitter();
|
|
this.onShow = new EventEmitter();
|
|
this.onHide = new EventEmitter();
|
|
this.onModelChange = () => { };
|
|
this.onModelTouched = () => { };
|
|
this.viewPortOffsetTop = 0;
|
|
}
|
|
get disabled() {
|
|
return this._disabled;
|
|
}
|
|
;
|
|
set disabled(_disabled) {
|
|
if (_disabled)
|
|
this.focused = false;
|
|
this._disabled = _disabled;
|
|
if (!this.cd.destroyed) {
|
|
this.cd.detectChanges();
|
|
}
|
|
}
|
|
ngAfterContentInit() {
|
|
this.templates.forEach((item) => {
|
|
switch (item.getType()) {
|
|
case 'item':
|
|
this.itemTemplate = item.template;
|
|
break;
|
|
case 'selectedItem':
|
|
this.selectedItemTemplate = item.template;
|
|
break;
|
|
case 'group':
|
|
this.groupTemplate = item.template;
|
|
break;
|
|
default:
|
|
this.itemTemplate = item.template;
|
|
break;
|
|
}
|
|
});
|
|
}
|
|
ngOnInit() {
|
|
this.optionsToDisplay = this.options;
|
|
this.updateSelectedOption(null);
|
|
}
|
|
get options() {
|
|
return this._options;
|
|
}
|
|
set options(val) {
|
|
let opts = this.optionLabel ? ObjectUtils.generateSelectItems(val, this.optionLabel) : val;
|
|
this._options = opts;
|
|
this.optionsToDisplay = this._options;
|
|
this.updateSelectedOption(this.value);
|
|
this.optionsChanged = true;
|
|
this.updateFilledState();
|
|
if (this.filterValue && this.filterValue.length) {
|
|
this.activateFilter();
|
|
}
|
|
}
|
|
ngAfterViewInit() {
|
|
if (this.editable) {
|
|
this.updateEditableLabel();
|
|
}
|
|
}
|
|
get label() {
|
|
return (this.selectedOption ? this.selectedOption.label : null);
|
|
}
|
|
updateEditableLabel() {
|
|
if (this.editableInputViewChild && this.editableInputViewChild.nativeElement) {
|
|
this.editableInputViewChild.nativeElement.value = (this.selectedOption ? this.selectedOption.label : this.value || '');
|
|
}
|
|
}
|
|
onItemClick(event) {
|
|
const option = event.option;
|
|
if (!option.disabled) {
|
|
this.selectItem(event, option);
|
|
this.accessibleViewChild.nativeElement.focus();
|
|
}
|
|
setTimeout(() => {
|
|
this.hide(event);
|
|
}, 150);
|
|
}
|
|
selectItem(event, option) {
|
|
if (this.selectedOption != option) {
|
|
this.selectedOption = option;
|
|
this.value = option.value;
|
|
this.filled = true;
|
|
this.onModelChange(this.value);
|
|
this.updateEditableLabel();
|
|
this.onChange.emit({
|
|
originalEvent: event.originalEvent,
|
|
value: this.value
|
|
});
|
|
if (this.virtualScroll) {
|
|
setTimeout(() => {
|
|
this.viewPortOffsetTop = this.viewPort ? this.viewPort.measureScrollOffset() : 0;
|
|
}, 1);
|
|
}
|
|
}
|
|
}
|
|
ngAfterViewChecked() {
|
|
if (this.optionsChanged && this.overlayVisible) {
|
|
this.optionsChanged = false;
|
|
if (this.virtualScroll) {
|
|
this.updateVirtualScrollSelectedIndex(true);
|
|
}
|
|
this.zone.runOutsideAngular(() => {
|
|
setTimeout(() => {
|
|
this.alignOverlay();
|
|
}, 1);
|
|
});
|
|
}
|
|
if (this.selectedOptionUpdated && this.itemsWrapper) {
|
|
if (this.virtualScroll && this.viewPort) {
|
|
let range = this.viewPort.getRenderedRange();
|
|
this.updateVirtualScrollSelectedIndex(false);
|
|
if (range.start > this.virtualScrollSelectedIndex || range.end < this.virtualScrollSelectedIndex) {
|
|
this.viewPort.scrollToIndex(this.virtualScrollSelectedIndex);
|
|
}
|
|
}
|
|
let selectedItem = DomHandler.findSingle(this.overlay, 'li.p-highlight');
|
|
if (selectedItem) {
|
|
DomHandler.scrollInView(this.itemsWrapper, DomHandler.findSingle(this.overlay, 'li.p-highlight'));
|
|
}
|
|
this.selectedOptionUpdated = false;
|
|
}
|
|
}
|
|
writeValue(value) {
|
|
if (this.filter) {
|
|
this.resetFilter();
|
|
}
|
|
this.value = value;
|
|
this.updateSelectedOption(value);
|
|
this.updateEditableLabel();
|
|
this.updateFilledState();
|
|
this.cd.markForCheck();
|
|
}
|
|
resetFilter() {
|
|
this.filterValue = null;
|
|
if (this.filterViewChild && this.filterViewChild.nativeElement) {
|
|
this.filterViewChild.nativeElement.value = '';
|
|
}
|
|
this.optionsToDisplay = this.options;
|
|
}
|
|
updateSelectedOption(val) {
|
|
this.selectedOption = this.findOption(val, this.optionsToDisplay);
|
|
if (this.autoDisplayFirst && !this.placeholder && !this.selectedOption && this.optionsToDisplay && this.optionsToDisplay.length && !this.editable) {
|
|
this.selectedOption = this.optionsToDisplay[0];
|
|
}
|
|
this.selectedOptionUpdated = true;
|
|
}
|
|
registerOnChange(fn) {
|
|
this.onModelChange = fn;
|
|
}
|
|
registerOnTouched(fn) {
|
|
this.onModelTouched = fn;
|
|
}
|
|
setDisabledState(val) {
|
|
this.disabled = val;
|
|
this.cd.markForCheck();
|
|
}
|
|
onMouseclick(event) {
|
|
if (this.disabled || this.readonly || this.isInputClick(event)) {
|
|
return;
|
|
}
|
|
this.onClick.emit(event);
|
|
this.accessibleViewChild.nativeElement.focus();
|
|
if (this.overlayVisible)
|
|
this.hide(event);
|
|
else
|
|
this.show();
|
|
this.cd.detectChanges();
|
|
}
|
|
isInputClick(event) {
|
|
return DomHandler.hasClass(event.target, 'p-dropdown-clear-icon') ||
|
|
event.target.isSameNode(this.accessibleViewChild.nativeElement) ||
|
|
(this.editableInputViewChild && event.target.isSameNode(this.editableInputViewChild.nativeElement));
|
|
}
|
|
isOutsideClicked(event) {
|
|
return !(this.el.nativeElement.isSameNode(event.target) || this.el.nativeElement.contains(event.target) || (this.overlay && this.overlay.contains(event.target)));
|
|
}
|
|
onEditableInputClick() {
|
|
this.bindDocumentClickListener();
|
|
}
|
|
onEditableInputFocus(event) {
|
|
this.focused = true;
|
|
this.hide(event);
|
|
this.onFocus.emit(event);
|
|
}
|
|
onEditableInputChange(event) {
|
|
this.value = event.target.value;
|
|
this.updateSelectedOption(this.value);
|
|
this.onModelChange(this.value);
|
|
this.onChange.emit({
|
|
originalEvent: event,
|
|
value: this.value
|
|
});
|
|
}
|
|
show() {
|
|
this.overlayVisible = true;
|
|
}
|
|
onOverlayAnimationStart(event) {
|
|
switch (event.toState) {
|
|
case 'visible':
|
|
this.overlay = event.element;
|
|
let itemsWrapperSelector = this.virtualScroll ? '.cdk-virtual-scroll-viewport' : '.p-dropdown-items-wrapper';
|
|
this.itemsWrapper = DomHandler.findSingle(this.overlay, itemsWrapperSelector);
|
|
this.appendOverlay();
|
|
if (this.autoZIndex) {
|
|
this.overlay.style.zIndex = String(this.baseZIndex + (++DomHandler.zindex));
|
|
}
|
|
this.alignOverlay();
|
|
this.bindDocumentClickListener();
|
|
this.bindDocumentResizeListener();
|
|
this.bindScrollListener();
|
|
if (this.options && this.options.length) {
|
|
if (!this.virtualScroll) {
|
|
let selectedListItem = DomHandler.findSingle(this.itemsWrapper, '.p-dropdown-item.p-highlight');
|
|
if (selectedListItem) {
|
|
DomHandler.scrollInView(this.itemsWrapper, selectedListItem);
|
|
}
|
|
}
|
|
}
|
|
if (this.filterViewChild && this.filterViewChild.nativeElement) {
|
|
this.preventModelTouched = true;
|
|
if (this.autofocusFilter) {
|
|
this.filterViewChild.nativeElement.focus();
|
|
}
|
|
}
|
|
this.onShow.emit(event);
|
|
break;
|
|
case 'void':
|
|
this.onOverlayHide();
|
|
break;
|
|
}
|
|
}
|
|
scrollToSelectedVirtualScrollElement() {
|
|
if (!this.virtualAutoScrolled) {
|
|
if (this.viewPortOffsetTop) {
|
|
this.viewPort.scrollToOffset(this.viewPortOffsetTop);
|
|
}
|
|
else if (this.virtualScrollSelectedIndex > -1) {
|
|
this.viewPort.scrollToIndex(this.virtualScrollSelectedIndex);
|
|
}
|
|
}
|
|
this.virtualAutoScrolled = true;
|
|
}
|
|
updateVirtualScrollSelectedIndex(resetOffset) {
|
|
if (this.selectedOption && this.optionsToDisplay && this.optionsToDisplay.length) {
|
|
if (resetOffset) {
|
|
this.viewPortOffsetTop = 0;
|
|
}
|
|
this.virtualScrollSelectedIndex = this.findOptionIndex(this.selectedOption.value, this.optionsToDisplay);
|
|
}
|
|
}
|
|
appendOverlay() {
|
|
if (this.appendTo) {
|
|
if (this.appendTo === 'body')
|
|
document.body.appendChild(this.overlay);
|
|
else
|
|
DomHandler.appendChild(this.overlay, this.appendTo);
|
|
if (!this.overlay.style.minWidth) {
|
|
this.overlay.style.minWidth = DomHandler.getWidth(this.containerViewChild.nativeElement) + 'px';
|
|
}
|
|
}
|
|
}
|
|
restoreOverlayAppend() {
|
|
if (this.overlay && this.appendTo) {
|
|
this.el.nativeElement.appendChild(this.overlay);
|
|
}
|
|
}
|
|
hide(event) {
|
|
this.overlayVisible = false;
|
|
if (this.filter && this.resetFilterOnHide) {
|
|
this.resetFilter();
|
|
}
|
|
if (this.virtualScroll) {
|
|
this.virtualAutoScrolled = false;
|
|
}
|
|
this.cd.markForCheck();
|
|
this.onHide.emit(event);
|
|
}
|
|
alignOverlay() {
|
|
if (this.overlay) {
|
|
if (this.appendTo)
|
|
DomHandler.absolutePosition(this.overlay, this.containerViewChild.nativeElement);
|
|
else
|
|
DomHandler.relativePosition(this.overlay, this.containerViewChild.nativeElement);
|
|
}
|
|
}
|
|
onInputFocus(event) {
|
|
this.focused = true;
|
|
this.onFocus.emit(event);
|
|
}
|
|
onInputBlur(event) {
|
|
this.focused = false;
|
|
this.onBlur.emit(event);
|
|
if (!this.preventModelTouched) {
|
|
this.onModelTouched();
|
|
}
|
|
this.preventModelTouched = false;
|
|
}
|
|
findPrevEnabledOption(index) {
|
|
let prevEnabledOption;
|
|
if (this.optionsToDisplay && this.optionsToDisplay.length) {
|
|
for (let i = (index - 1); 0 <= i; i--) {
|
|
let option = this.optionsToDisplay[i];
|
|
if (option.disabled) {
|
|
continue;
|
|
}
|
|
else {
|
|
prevEnabledOption = option;
|
|
break;
|
|
}
|
|
}
|
|
if (!prevEnabledOption) {
|
|
for (let i = this.optionsToDisplay.length - 1; i >= index; i--) {
|
|
let option = this.optionsToDisplay[i];
|
|
if (option.disabled) {
|
|
continue;
|
|
}
|
|
else {
|
|
prevEnabledOption = option;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return prevEnabledOption;
|
|
}
|
|
findNextEnabledOption(index) {
|
|
let nextEnabledOption;
|
|
if (this.optionsToDisplay && this.optionsToDisplay.length) {
|
|
for (let i = (index + 1); index < (this.optionsToDisplay.length - 1); i++) {
|
|
let option = this.optionsToDisplay[i];
|
|
if (option.disabled) {
|
|
continue;
|
|
}
|
|
else {
|
|
nextEnabledOption = option;
|
|
break;
|
|
}
|
|
}
|
|
if (!nextEnabledOption) {
|
|
for (let i = 0; i < index; i++) {
|
|
let option = this.optionsToDisplay[i];
|
|
if (option.disabled) {
|
|
continue;
|
|
}
|
|
else {
|
|
nextEnabledOption = option;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return nextEnabledOption;
|
|
}
|
|
onKeydown(event, search) {
|
|
if (this.readonly || !this.optionsToDisplay || this.optionsToDisplay.length === null) {
|
|
return;
|
|
}
|
|
switch (event.which) {
|
|
//down
|
|
case 40:
|
|
if (!this.overlayVisible && event.altKey) {
|
|
this.show();
|
|
}
|
|
else {
|
|
if (this.group) {
|
|
let selectedItemIndex = this.selectedOption ? this.findOptionGroupIndex(this.selectedOption.value, this.optionsToDisplay) : -1;
|
|
if (selectedItemIndex !== -1) {
|
|
let nextItemIndex = selectedItemIndex.itemIndex + 1;
|
|
if (nextItemIndex < (this.optionsToDisplay[selectedItemIndex.groupIndex].items.length)) {
|
|
this.selectItem(event, this.optionsToDisplay[selectedItemIndex.groupIndex].items[nextItemIndex]);
|
|
this.selectedOptionUpdated = true;
|
|
}
|
|
else if (this.optionsToDisplay[selectedItemIndex.groupIndex + 1]) {
|
|
this.selectItem(event, this.optionsToDisplay[selectedItemIndex.groupIndex + 1].items[0]);
|
|
this.selectedOptionUpdated = true;
|
|
}
|
|
}
|
|
else {
|
|
this.selectItem(event, this.optionsToDisplay[0].items[0]);
|
|
}
|
|
}
|
|
else {
|
|
let selectedItemIndex = this.selectedOption ? this.findOptionIndex(this.selectedOption.value, this.optionsToDisplay) : -1;
|
|
let nextEnabledOption = this.findNextEnabledOption(selectedItemIndex);
|
|
if (nextEnabledOption) {
|
|
this.selectItem(event, nextEnabledOption);
|
|
this.selectedOptionUpdated = true;
|
|
}
|
|
}
|
|
}
|
|
event.preventDefault();
|
|
break;
|
|
//up
|
|
case 38:
|
|
if (this.group) {
|
|
let selectedItemIndex = this.selectedOption ? this.findOptionGroupIndex(this.selectedOption.value, this.optionsToDisplay) : -1;
|
|
if (selectedItemIndex !== -1) {
|
|
let prevItemIndex = selectedItemIndex.itemIndex - 1;
|
|
if (prevItemIndex >= 0) {
|
|
this.selectItem(event, this.optionsToDisplay[selectedItemIndex.groupIndex].items[prevItemIndex]);
|
|
this.selectedOptionUpdated = true;
|
|
}
|
|
else if (prevItemIndex < 0) {
|
|
let prevGroup = this.optionsToDisplay[selectedItemIndex.groupIndex - 1];
|
|
if (prevGroup) {
|
|
this.selectItem(event, prevGroup.items[prevGroup.items.length - 1]);
|
|
this.selectedOptionUpdated = true;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
else {
|
|
let selectedItemIndex = this.selectedOption ? this.findOptionIndex(this.selectedOption.value, this.optionsToDisplay) : -1;
|
|
let prevEnabledOption = this.findPrevEnabledOption(selectedItemIndex);
|
|
if (prevEnabledOption) {
|
|
this.selectItem(event, prevEnabledOption);
|
|
this.selectedOptionUpdated = true;
|
|
}
|
|
}
|
|
event.preventDefault();
|
|
break;
|
|
//space
|
|
case 32:
|
|
case 32:
|
|
if (!this.overlayVisible) {
|
|
this.show();
|
|
event.preventDefault();
|
|
}
|
|
break;
|
|
//enter
|
|
case 13:
|
|
if (!this.filter || (this.optionsToDisplay && this.optionsToDisplay.length > 0)) {
|
|
this.hide(event);
|
|
}
|
|
event.preventDefault();
|
|
break;
|
|
//escape and tab
|
|
case 27:
|
|
case 9:
|
|
this.hide(event);
|
|
break;
|
|
//search item based on keyboard input
|
|
default:
|
|
if (search) {
|
|
this.search(event);
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
search(event) {
|
|
if (this.searchTimeout) {
|
|
clearTimeout(this.searchTimeout);
|
|
}
|
|
const char = event.key;
|
|
this.previousSearchChar = this.currentSearchChar;
|
|
this.currentSearchChar = char;
|
|
if (this.previousSearchChar === this.currentSearchChar)
|
|
this.searchValue = this.currentSearchChar;
|
|
else
|
|
this.searchValue = this.searchValue ? this.searchValue + char : char;
|
|
let newOption;
|
|
if (this.group) {
|
|
let searchIndex = this.selectedOption ? this.findOptionGroupIndex(this.selectedOption.value, this.optionsToDisplay) : { groupIndex: 0, itemIndex: 0 };
|
|
newOption = this.searchOptionWithinGroup(searchIndex);
|
|
}
|
|
else {
|
|
let searchIndex = this.selectedOption ? this.findOptionIndex(this.selectedOption.value, this.optionsToDisplay) : -1;
|
|
newOption = this.searchOption(++searchIndex);
|
|
}
|
|
if (newOption && !newOption.disabled) {
|
|
this.selectItem(event, newOption);
|
|
this.selectedOptionUpdated = true;
|
|
}
|
|
this.searchTimeout = setTimeout(() => {
|
|
this.searchValue = null;
|
|
}, 250);
|
|
}
|
|
searchOption(index) {
|
|
let option;
|
|
if (this.searchValue) {
|
|
option = this.searchOptionInRange(index, this.optionsToDisplay.length);
|
|
if (!option) {
|
|
option = this.searchOptionInRange(0, index);
|
|
}
|
|
}
|
|
return option;
|
|
}
|
|
searchOptionInRange(start, end) {
|
|
for (let i = start; i < end; i++) {
|
|
let opt = this.optionsToDisplay[i];
|
|
if (opt.label.toLocaleLowerCase(this.filterLocale).startsWith(this.searchValue.toLocaleLowerCase(this.filterLocale)) && !opt.disabled) {
|
|
return opt;
|
|
}
|
|
}
|
|
return null;
|
|
}
|
|
searchOptionWithinGroup(index) {
|
|
let option;
|
|
if (this.searchValue) {
|
|
for (let i = index.groupIndex; i < this.optionsToDisplay.length; i++) {
|
|
for (let j = (index.groupIndex === i) ? (index.itemIndex + 1) : 0; j < this.optionsToDisplay[i].items.length; j++) {
|
|
let opt = this.optionsToDisplay[i].items[j];
|
|
if (opt.label.toLocaleLowerCase(this.filterLocale).startsWith(this.searchValue.toLocaleLowerCase(this.filterLocale)) && !opt.disabled) {
|
|
return opt;
|
|
}
|
|
}
|
|
}
|
|
if (!option) {
|
|
for (let i = 0; i <= index.groupIndex; i++) {
|
|
for (let j = 0; j < ((index.groupIndex === i) ? index.itemIndex : this.optionsToDisplay[i].items.length); j++) {
|
|
let opt = this.optionsToDisplay[i].items[j];
|
|
if (opt.label.toLocaleLowerCase(this.filterLocale).startsWith(this.searchValue.toLocaleLowerCase(this.filterLocale)) && !opt.disabled) {
|
|
return opt;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return null;
|
|
}
|
|
findOptionIndex(val, opts) {
|
|
let index = -1;
|
|
if (opts) {
|
|
for (let i = 0; i < opts.length; i++) {
|
|
if ((val == null && opts[i].value == null) || ObjectUtils.equals(val, opts[i].value, this.dataKey)) {
|
|
index = i;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
return index;
|
|
}
|
|
findOptionGroupIndex(val, opts) {
|
|
let groupIndex, itemIndex;
|
|
if (opts) {
|
|
for (let i = 0; i < opts.length; i++) {
|
|
groupIndex = i;
|
|
itemIndex = this.findOptionIndex(val, opts[i].items);
|
|
if (itemIndex !== -1) {
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
if (itemIndex !== -1) {
|
|
return { groupIndex: groupIndex, itemIndex: itemIndex };
|
|
}
|
|
else {
|
|
return -1;
|
|
}
|
|
}
|
|
findOption(val, opts, inGroup) {
|
|
if (this.group && !inGroup) {
|
|
let opt;
|
|
if (opts && opts.length) {
|
|
for (let optgroup of opts) {
|
|
opt = this.findOption(val, optgroup.items, true);
|
|
if (opt) {
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
return opt;
|
|
}
|
|
else {
|
|
let index = this.findOptionIndex(val, opts);
|
|
return (index != -1) ? opts[index] : null;
|
|
}
|
|
}
|
|
onFilter(event) {
|
|
let inputValue = event.target.value;
|
|
if (inputValue && inputValue.length) {
|
|
this.filterValue = inputValue;
|
|
this.activateFilter();
|
|
}
|
|
else {
|
|
this.filterValue = null;
|
|
this.optionsToDisplay = this.options;
|
|
}
|
|
this.optionsChanged = true;
|
|
}
|
|
activateFilter() {
|
|
let searchFields = this.filterBy.split(',');
|
|
if (this.options && this.options.length) {
|
|
if (this.group) {
|
|
let filteredGroups = [];
|
|
for (let optgroup of this.options) {
|
|
let filteredSubOptions = FilterUtils.filter(optgroup.items, searchFields, this.filterValue, this.filterMatchMode, this.filterLocale);
|
|
if (filteredSubOptions && filteredSubOptions.length) {
|
|
filteredGroups.push({
|
|
label: optgroup.label,
|
|
value: optgroup.value,
|
|
items: filteredSubOptions
|
|
});
|
|
}
|
|
}
|
|
this.optionsToDisplay = filteredGroups;
|
|
}
|
|
else {
|
|
this.optionsToDisplay = FilterUtils.filter(this.options, searchFields, this.filterValue, this.filterMatchMode, this.filterLocale);
|
|
}
|
|
this.optionsChanged = true;
|
|
}
|
|
}
|
|
applyFocus() {
|
|
if (this.editable)
|
|
DomHandler.findSingle(this.el.nativeElement, '.p-dropdown-label.p-inputtext').focus();
|
|
else
|
|
DomHandler.findSingle(this.el.nativeElement, 'input[readonly]').focus();
|
|
}
|
|
focus() {
|
|
this.applyFocus();
|
|
}
|
|
bindDocumentClickListener() {
|
|
if (!this.documentClickListener) {
|
|
const documentTarget = this.el ? this.el.nativeElement.ownerDocument : 'document';
|
|
this.documentClickListener = this.renderer.listen(documentTarget, 'click', (event) => {
|
|
if (this.isOutsideClicked(event)) {
|
|
this.hide(event);
|
|
this.unbindDocumentClickListener();
|
|
}
|
|
this.cd.markForCheck();
|
|
});
|
|
}
|
|
}
|
|
unbindDocumentClickListener() {
|
|
if (this.documentClickListener) {
|
|
this.documentClickListener();
|
|
this.documentClickListener = null;
|
|
}
|
|
}
|
|
bindDocumentResizeListener() {
|
|
this.documentResizeListener = this.onWindowResize.bind(this);
|
|
window.addEventListener('resize', this.documentResizeListener);
|
|
}
|
|
unbindDocumentResizeListener() {
|
|
if (this.documentResizeListener) {
|
|
window.removeEventListener('resize', this.documentResizeListener);
|
|
this.documentResizeListener = null;
|
|
}
|
|
}
|
|
onWindowResize() {
|
|
if (!DomHandler.isAndroid()) {
|
|
this.hide(event);
|
|
}
|
|
}
|
|
bindScrollListener() {
|
|
if (!this.scrollHandler) {
|
|
this.scrollHandler = new ConnectedOverlayScrollHandler(this.containerViewChild.nativeElement, (event) => {
|
|
if (this.overlayVisible) {
|
|
this.hide(event);
|
|
}
|
|
});
|
|
}
|
|
this.scrollHandler.bindScrollListener();
|
|
}
|
|
unbindScrollListener() {
|
|
if (this.scrollHandler) {
|
|
this.scrollHandler.unbindScrollListener();
|
|
}
|
|
}
|
|
updateFilledState() {
|
|
this.filled = (this.selectedOption != null);
|
|
}
|
|
clear(event) {
|
|
this.value = null;
|
|
this.onModelChange(this.value);
|
|
this.onChange.emit({
|
|
originalEvent: event,
|
|
value: this.value
|
|
});
|
|
this.updateSelectedOption(this.value);
|
|
this.updateEditableLabel();
|
|
this.updateFilledState();
|
|
}
|
|
onOverlayHide() {
|
|
this.unbindDocumentClickListener();
|
|
this.unbindDocumentResizeListener();
|
|
this.unbindScrollListener();
|
|
this.overlay = null;
|
|
this.itemsWrapper = null;
|
|
this.onModelTouched();
|
|
}
|
|
ngOnDestroy() {
|
|
if (this.scrollHandler) {
|
|
this.scrollHandler.destroy();
|
|
this.scrollHandler = null;
|
|
}
|
|
this.restoreOverlayAppend();
|
|
this.onOverlayHide();
|
|
}
|
|
}
|
|
Dropdown.decorators = [
|
|
{ type: Component, args: [{
|
|
selector: 'p-dropdown',
|
|
template: `
|
|
<div #container [ngClass]="{'p-dropdown p-component':true,
|
|
'p-disabled':disabled, 'p-dropdown-open':overlayVisible, 'p-focus':focused, 'p-dropdown-clearable': showClear && !disabled}"
|
|
(click)="onMouseclick($event)" [ngStyle]="style" [class]="styleClass">
|
|
<div class="p-hidden-accessible">
|
|
<input #in [attr.id]="inputId" type="text" [attr.aria-label]="selectedOption ? selectedOption.label : ' '" readonly (focus)="onInputFocus($event)" aria-haspopup="listbox"
|
|
aria-haspopup="listbox" [attr.aria-expanded]="overlayVisible" [attr.aria-labelledby]="ariaLabelledBy" (blur)="onInputBlur($event)" (keydown)="onKeydown($event, true)"
|
|
[disabled]="disabled" [attr.tabindex]="tabindex" [attr.autofocus]="autofocus" role="listbox">
|
|
</div>
|
|
<span [ngClass]="{'p-dropdown-label p-inputtext':true,'p-dropdown-label-empty':(label == null || label.length === 0)}" *ngIf="!editable && (label != null)" [pTooltip]="tooltip" [tooltipPosition]="tooltipPosition" [positionStyle]="tooltipPositionStyle" [tooltipStyleClass]="tooltipStyleClass">
|
|
<ng-container *ngIf="!selectedItemTemplate">{{label||'empty'}}</ng-container>
|
|
<ng-container *ngTemplateOutlet="selectedItemTemplate; context: {$implicit: selectedOption}"></ng-container>
|
|
</span>
|
|
<span [ngClass]="{'p-dropdown-label p-inputtext p-placeholder':true,'p-dropdown-label-empty': (placeholder == null || placeholder.length === 0)}" *ngIf="!editable && (label == null)">{{placeholder||'empty'}}</span>
|
|
<input #editableInput type="text" [attr.maxlength]="maxlength" [attr.aria-label]="selectedOption ? selectedOption.label : ' '" class="p-dropdown-label p-inputtext" *ngIf="editable" [disabled]="disabled" [attr.placeholder]="placeholder"
|
|
aria-haspopup="listbox" [attr.aria-expanded]="overlayVisible" (click)="onEditableInputClick()" (input)="onEditableInputChange($event)" (focus)="onEditableInputFocus($event)" (blur)="onInputBlur($event)">
|
|
<i class="p-dropdown-clear-icon pi pi-times" (click)="clear($event)" *ngIf="value != null && showClear && !disabled"></i>
|
|
<div class="p-dropdown-trigger" role="button" aria-haspopup="listbox" [attr.aria-expanded]="overlayVisible">
|
|
<span class="p-dropdown-trigger-icon" [ngClass]="dropdownIcon"></span>
|
|
</div>
|
|
<div *ngIf="overlayVisible" [ngClass]="'p-dropdown-panel p-component'" [@overlayAnimation]="{value: 'visible', params: {showTransitionParams: showTransitionOptions, hideTransitionParams: hideTransitionOptions}}" (@overlayAnimation.start)="onOverlayAnimationStart($event)" [ngStyle]="panelStyle" [class]="panelStyleClass">
|
|
<div class="p-dropdown-header" *ngIf="filter" >
|
|
<div class="p-dropdown-filter-container" (click)="$event.stopPropagation()">
|
|
<input #filter type="text" autocomplete="off" [value]="filterValue||''" class="p-dropdown-filter p-inputtext p-component" [attr.placeholder]="filterPlaceholder"
|
|
(keydown.enter)="$event.preventDefault()" (keydown)="onKeydown($event, false)" (input)="onFilter($event)" [attr.aria-label]="ariaFilterLabel">
|
|
<span class="p-dropdown-filter-icon pi pi-search"></span>
|
|
</div>
|
|
</div>
|
|
<div class="p-dropdown-items-wrapper" [style.max-height]="virtualScroll ? 'auto' : (scrollHeight||'auto')">
|
|
<ul class="p-dropdown-items" role="listbox">
|
|
<ng-container *ngIf="group">
|
|
<ng-template ngFor let-optgroup [ngForOf]="optionsToDisplay">
|
|
<li class="p-dropdown-item-group">
|
|
<span *ngIf="!groupTemplate">{{optgroup.label||'empty'}}</span>
|
|
<ng-container *ngTemplateOutlet="groupTemplate; context: {$implicit: optgroup}"></ng-container>
|
|
</li>
|
|
<ng-container *ngTemplateOutlet="itemslist; context: {$implicit: optgroup.items, selectedOption: selectedOption}"></ng-container>
|
|
</ng-template>
|
|
</ng-container>
|
|
<ng-container *ngIf="!group">
|
|
<ng-container *ngTemplateOutlet="itemslist; context: {$implicit: optionsToDisplay, selectedOption: selectedOption}"></ng-container>
|
|
</ng-container>
|
|
<ng-template #itemslist let-options let-selectedOption="selectedOption">
|
|
<ng-container *ngIf="!virtualScroll; else virtualScrollList">
|
|
<ng-template ngFor let-option let-i="index" [ngForOf]="options">
|
|
<p-dropdownItem [option]="option" [selected]="selectedOption == option"
|
|
(onClick)="onItemClick($event)"
|
|
[template]="itemTemplate"></p-dropdownItem>
|
|
</ng-template>
|
|
</ng-container>
|
|
<ng-template #virtualScrollList>
|
|
<cdk-virtual-scroll-viewport (scrolledIndexChange)="scrollToSelectedVirtualScrollElement()" #viewport [ngStyle]="{'height': scrollHeight}" [itemSize]="itemSize" *ngIf="virtualScroll && optionsToDisplay && optionsToDisplay.length">
|
|
<ng-container *cdkVirtualFor="let option of options; let i = index; let c = count; let f = first; let l = last; let e = even; let o = odd">
|
|
<p-dropdownItem [option]="option" [selected]="selectedOption == option"
|
|
(onClick)="onItemClick($event)"
|
|
[template]="itemTemplate"></p-dropdownItem>
|
|
</ng-container>
|
|
</cdk-virtual-scroll-viewport>
|
|
</ng-template>
|
|
</ng-template>
|
|
<li *ngIf="filter && (!optionsToDisplay || (optionsToDisplay && optionsToDisplay.length === 0))" class="p-dropdown-empty-message">{{emptyFilterMessage}}</li>
|
|
</ul>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
`,
|
|
animations: [
|
|
trigger('overlayAnimation', [
|
|
transition(':enter', [
|
|
style({ opacity: 0, transform: 'scaleY(0.8)' }),
|
|
animate('{{showTransitionParams}}')
|
|
]),
|
|
transition(':leave', [
|
|
animate('{{hideTransitionParams}}', style({ opacity: 0 }))
|
|
])
|
|
])
|
|
],
|
|
host: {
|
|
'[class.p-inputwrapper-filled]': 'filled',
|
|
'[class.p-inputwrapper-focus]': 'focused'
|
|
},
|
|
providers: [DROPDOWN_VALUE_ACCESSOR],
|
|
changeDetection: ChangeDetectionStrategy.OnPush,
|
|
encapsulation: ViewEncapsulation.None,
|
|
styles: [".p-dropdown{-moz-user-select:none;-ms-user-select:none;-webkit-user-select:none;cursor:pointer;display:-ms-inline-flexbox;display:inline-flex;position:relative;user-select:none}.p-dropdown-clear-icon{margin-top:-.5rem;position:absolute;top:50%}.p-dropdown-trigger{-ms-flex-align:center;-ms-flex-negative:0;-ms-flex-pack:center;align-items:center;display:-ms-flexbox;display:flex;flex-shrink:0;justify-content:center}.p-dropdown-label{-ms-flex:1 1 auto;cursor:pointer;display:block;flex:1 1 auto;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;width:1%}.p-dropdown-label-empty{overflow:hidden;visibility:hidden}input.p-dropdown-label{cursor:default}.p-dropdown .p-dropdown-panel{min-width:100%}.p-dropdown-panel{position:absolute}.p-dropdown-items-wrapper{overflow:auto}.p-dropdown-item{cursor:pointer;font-weight:400;overflow:hidden;position:relative;white-space:nowrap}.p-dropdown-items{list-style-type:none;margin:0;padding:0}.p-dropdown-filter{width:100%}.p-dropdown-filter-container{position:relative}.p-dropdown-filter-icon{margin-top:-.5rem;position:absolute;top:50%}.p-fluid .p-dropdown{display:-ms-flexbox;display:flex}.p-fluid .p-dropdown .p-dropdown-label{width:1%}"]
|
|
},] }
|
|
];
|
|
Dropdown.ctorParameters = () => [
|
|
{ type: ElementRef },
|
|
{ type: Renderer2 },
|
|
{ type: ChangeDetectorRef },
|
|
{ type: NgZone }
|
|
];
|
|
Dropdown.propDecorators = {
|
|
scrollHeight: [{ type: Input }],
|
|
filter: [{ type: Input }],
|
|
name: [{ type: Input }],
|
|
style: [{ type: Input }],
|
|
panelStyle: [{ type: Input }],
|
|
styleClass: [{ type: Input }],
|
|
panelStyleClass: [{ type: Input }],
|
|
readonly: [{ type: Input }],
|
|
required: [{ type: Input }],
|
|
editable: [{ type: Input }],
|
|
appendTo: [{ type: Input }],
|
|
tabindex: [{ type: Input }],
|
|
placeholder: [{ type: Input }],
|
|
filterPlaceholder: [{ type: Input }],
|
|
filterLocale: [{ type: Input }],
|
|
inputId: [{ type: Input }],
|
|
selectId: [{ type: Input }],
|
|
dataKey: [{ type: Input }],
|
|
filterBy: [{ type: Input }],
|
|
autofocus: [{ type: Input }],
|
|
resetFilterOnHide: [{ type: Input }],
|
|
dropdownIcon: [{ type: Input }],
|
|
optionLabel: [{ type: Input }],
|
|
autoDisplayFirst: [{ type: Input }],
|
|
group: [{ type: Input }],
|
|
showClear: [{ type: Input }],
|
|
emptyFilterMessage: [{ type: Input }],
|
|
virtualScroll: [{ type: Input }],
|
|
itemSize: [{ type: Input }],
|
|
autoZIndex: [{ type: Input }],
|
|
baseZIndex: [{ type: Input }],
|
|
showTransitionOptions: [{ type: Input }],
|
|
hideTransitionOptions: [{ type: Input }],
|
|
ariaFilterLabel: [{ type: Input }],
|
|
ariaLabelledBy: [{ type: Input }],
|
|
filterMatchMode: [{ type: Input }],
|
|
maxlength: [{ type: Input }],
|
|
tooltip: [{ type: Input }],
|
|
tooltipPosition: [{ type: Input }],
|
|
tooltipPositionStyle: [{ type: Input }],
|
|
tooltipStyleClass: [{ type: Input }],
|
|
autofocusFilter: [{ type: Input }],
|
|
onChange: [{ type: Output }],
|
|
onFocus: [{ type: Output }],
|
|
onBlur: [{ type: Output }],
|
|
onClick: [{ type: Output }],
|
|
onShow: [{ type: Output }],
|
|
onHide: [{ type: Output }],
|
|
containerViewChild: [{ type: ViewChild, args: ['container',] }],
|
|
filterViewChild: [{ type: ViewChild, args: ['filter',] }],
|
|
accessibleViewChild: [{ type: ViewChild, args: ['in',] }],
|
|
viewPort: [{ type: ViewChild, args: [CdkVirtualScrollViewport,] }],
|
|
editableInputViewChild: [{ type: ViewChild, args: ['editableInput',] }],
|
|
templates: [{ type: ContentChildren, args: [PrimeTemplate,] }],
|
|
disabled: [{ type: Input }],
|
|
options: [{ type: Input }]
|
|
};
|
|
class DropdownModule {
|
|
}
|
|
DropdownModule.decorators = [
|
|
{ type: NgModule, args: [{
|
|
imports: [CommonModule, SharedModule, ScrollingModule, TooltipModule, RippleModule],
|
|
exports: [Dropdown, SharedModule, ScrollingModule],
|
|
declarations: [Dropdown, DropdownItem]
|
|
},] }
|
|
];
|
|
|
|
/**
|
|
* Generated bundle index. Do not edit.
|
|
*/
|
|
|
|
export { DROPDOWN_VALUE_ACCESSOR, Dropdown, DropdownItem, DropdownModule };
|
|
//# sourceMappingURL=primeng-dropdown.js.map
|