1 | import { CdkVirtualScrollViewport, ScrollingModule } from '@angular/cdk/scrolling';
|
2 | import { forwardRef, EventEmitter, Component, Input, Output, ChangeDetectionStrategy, ViewEncapsulation, ElementRef, Renderer2, ChangeDetectorRef, NgZone, ViewChild, ContentChildren, NgModule } from '@angular/core';
|
3 | import { trigger, transition, style, animate } from '@angular/animations';
|
4 | import { CommonModule } from '@angular/common';
|
5 | import { PrimeTemplate, SharedModule } from 'primeng/api';
|
6 | import { DomHandler, ConnectedOverlayScrollHandler } from 'primeng/dom';
|
7 | import { ObjectUtils, FilterUtils } from 'primeng/utils';
|
8 | import { NG_VALUE_ACCESSOR } from '@angular/forms';
|
9 | import { TooltipModule } from 'primeng/tooltip';
|
10 | import { RippleModule } from 'primeng/ripple';
|
11 |
|
12 | const DROPDOWN_VALUE_ACCESSOR = {
|
13 | provide: NG_VALUE_ACCESSOR,
|
14 | useExisting: forwardRef(() => Dropdown),
|
15 | multi: true
|
16 | };
|
17 | class DropdownItem {
|
18 | constructor() {
|
19 | this.onClick = new EventEmitter();
|
20 | }
|
21 | onOptionClick(event) {
|
22 | this.onClick.emit({
|
23 | originalEvent: event,
|
24 | option: this.option
|
25 | });
|
26 | }
|
27 | }
|
28 | DropdownItem.decorators = [
|
29 | { type: Component, args: [{
|
30 | selector: 'p-dropdownItem',
|
31 | template: `
|
32 | <li (click)="onOptionClick($event)" role="option" pRipple
|
33 | [attr.aria-label]="option.label" [attr.aria-selected]="selected"
|
34 | [ngStyle]="{'height': itemSize + 'px'}"
|
35 | [ngClass]="{'p-dropdown-item':true, 'p-highlight': selected, 'p-disabled':(option.disabled)}">
|
36 | <span *ngIf="!template">{{option.label||'empty'}}</span>
|
37 | <ng-container *ngTemplateOutlet="template; context: {$implicit: option}"></ng-container>
|
38 | </li>
|
39 | `
|
40 | },] }
|
41 | ];
|
42 | DropdownItem.propDecorators = {
|
43 | option: [{ type: Input }],
|
44 | selected: [{ type: Input }],
|
45 | disabled: [{ type: Input }],
|
46 | visible: [{ type: Input }],
|
47 | itemSize: [{ type: Input }],
|
48 | template: [{ type: Input }],
|
49 | onClick: [{ type: Output }]
|
50 | };
|
51 | class Dropdown {
|
52 | constructor(el, renderer, cd, zone) {
|
53 | this.el = el;
|
54 | this.renderer = renderer;
|
55 | this.cd = cd;
|
56 | this.zone = zone;
|
57 | this.scrollHeight = '200px';
|
58 | this.filterBy = 'label';
|
59 | this.resetFilterOnHide = false;
|
60 | this.dropdownIcon = 'pi pi-chevron-down';
|
61 | this.autoDisplayFirst = true;
|
62 | this.emptyFilterMessage = 'No results found';
|
63 | this.autoZIndex = true;
|
64 | this.baseZIndex = 0;
|
65 | this.showTransitionOptions = '.12s cubic-bezier(0, 0, 0.2, 1)';
|
66 | this.hideTransitionOptions = '.1s linear';
|
67 | this.filterMatchMode = "contains";
|
68 | this.tooltip = '';
|
69 | this.tooltipPosition = 'right';
|
70 | this.tooltipPositionStyle = 'absolute';
|
71 | this.autofocusFilter = true;
|
72 | this.onChange = new EventEmitter();
|
73 | this.onFocus = new EventEmitter();
|
74 | this.onBlur = new EventEmitter();
|
75 | this.onClick = new EventEmitter();
|
76 | this.onShow = new EventEmitter();
|
77 | this.onHide = new EventEmitter();
|
78 | this.onModelChange = () => { };
|
79 | this.onModelTouched = () => { };
|
80 | this.viewPortOffsetTop = 0;
|
81 | }
|
82 | get disabled() {
|
83 | return this._disabled;
|
84 | }
|
85 | ;
|
86 | set disabled(_disabled) {
|
87 | if (_disabled)
|
88 | this.focused = false;
|
89 | this._disabled = _disabled;
|
90 | if (!this.cd.destroyed) {
|
91 | this.cd.detectChanges();
|
92 | }
|
93 | }
|
94 | ngAfterContentInit() {
|
95 | this.templates.forEach((item) => {
|
96 | switch (item.getType()) {
|
97 | case 'item':
|
98 | this.itemTemplate = item.template;
|
99 | break;
|
100 | case 'selectedItem':
|
101 | this.selectedItemTemplate = item.template;
|
102 | break;
|
103 | case 'group':
|
104 | this.groupTemplate = item.template;
|
105 | break;
|
106 | default:
|
107 | this.itemTemplate = item.template;
|
108 | break;
|
109 | }
|
110 | });
|
111 | }
|
112 | ngOnInit() {
|
113 | this.optionsToDisplay = this.options;
|
114 | this.updateSelectedOption(null);
|
115 | }
|
116 | get options() {
|
117 | return this._options;
|
118 | }
|
119 | set options(val) {
|
120 | let opts = this.optionLabel ? ObjectUtils.generateSelectItems(val, this.optionLabel) : val;
|
121 | this._options = opts;
|
122 | this.optionsToDisplay = this._options;
|
123 | this.updateSelectedOption(this.value);
|
124 | this.optionsChanged = true;
|
125 | this.updateFilledState();
|
126 | if (this.filterValue && this.filterValue.length) {
|
127 | this.activateFilter();
|
128 | }
|
129 | }
|
130 | ngAfterViewInit() {
|
131 | if (this.editable) {
|
132 | this.updateEditableLabel();
|
133 | }
|
134 | }
|
135 | get label() {
|
136 | return (this.selectedOption ? this.selectedOption.label : null);
|
137 | }
|
138 | updateEditableLabel() {
|
139 | if (this.editableInputViewChild && this.editableInputViewChild.nativeElement) {
|
140 | this.editableInputViewChild.nativeElement.value = (this.selectedOption ? this.selectedOption.label : this.value || '');
|
141 | }
|
142 | }
|
143 | onItemClick(event) {
|
144 | const option = event.option;
|
145 | if (!option.disabled) {
|
146 | this.selectItem(event, option);
|
147 | this.accessibleViewChild.nativeElement.focus();
|
148 | }
|
149 | setTimeout(() => {
|
150 | this.hide(event);
|
151 | }, 150);
|
152 | }
|
153 | selectItem(event, option) {
|
154 | if (this.selectedOption != option) {
|
155 | this.selectedOption = option;
|
156 | this.value = option.value;
|
157 | this.filled = true;
|
158 | this.onModelChange(this.value);
|
159 | this.updateEditableLabel();
|
160 | this.onChange.emit({
|
161 | originalEvent: event.originalEvent,
|
162 | value: this.value
|
163 | });
|
164 | if (this.virtualScroll) {
|
165 | setTimeout(() => {
|
166 | this.viewPortOffsetTop = this.viewPort ? this.viewPort.measureScrollOffset() : 0;
|
167 | }, 1);
|
168 | }
|
169 | }
|
170 | }
|
171 | ngAfterViewChecked() {
|
172 | if (this.optionsChanged && this.overlayVisible) {
|
173 | this.optionsChanged = false;
|
174 | if (this.virtualScroll) {
|
175 | this.updateVirtualScrollSelectedIndex(true);
|
176 | }
|
177 | this.zone.runOutsideAngular(() => {
|
178 | setTimeout(() => {
|
179 | this.alignOverlay();
|
180 | }, 1);
|
181 | });
|
182 | }
|
183 | if (this.selectedOptionUpdated && this.itemsWrapper) {
|
184 | if (this.virtualScroll && this.viewPort) {
|
185 | let range = this.viewPort.getRenderedRange();
|
186 | this.updateVirtualScrollSelectedIndex(false);
|
187 | if (range.start > this.virtualScrollSelectedIndex || range.end < this.virtualScrollSelectedIndex) {
|
188 | this.viewPort.scrollToIndex(this.virtualScrollSelectedIndex);
|
189 | }
|
190 | }
|
191 | let selectedItem = DomHandler.findSingle(this.overlay, 'li.p-highlight');
|
192 | if (selectedItem) {
|
193 | DomHandler.scrollInView(this.itemsWrapper, DomHandler.findSingle(this.overlay, 'li.p-highlight'));
|
194 | }
|
195 | this.selectedOptionUpdated = false;
|
196 | }
|
197 | }
|
198 | writeValue(value) {
|
199 | if (this.filter) {
|
200 | this.resetFilter();
|
201 | }
|
202 | this.value = value;
|
203 | this.updateSelectedOption(value);
|
204 | this.updateEditableLabel();
|
205 | this.updateFilledState();
|
206 | this.cd.markForCheck();
|
207 | }
|
208 | resetFilter() {
|
209 | this.filterValue = null;
|
210 | if (this.filterViewChild && this.filterViewChild.nativeElement) {
|
211 | this.filterViewChild.nativeElement.value = '';
|
212 | }
|
213 | this.optionsToDisplay = this.options;
|
214 | }
|
215 | updateSelectedOption(val) {
|
216 | this.selectedOption = this.findOption(val, this.optionsToDisplay);
|
217 | if (this.autoDisplayFirst && !this.placeholder && !this.selectedOption && this.optionsToDisplay && this.optionsToDisplay.length && !this.editable) {
|
218 | this.selectedOption = this.optionsToDisplay[0];
|
219 | }
|
220 | this.selectedOptionUpdated = true;
|
221 | }
|
222 | registerOnChange(fn) {
|
223 | this.onModelChange = fn;
|
224 | }
|
225 | registerOnTouched(fn) {
|
226 | this.onModelTouched = fn;
|
227 | }
|
228 | setDisabledState(val) {
|
229 | this.disabled = val;
|
230 | this.cd.markForCheck();
|
231 | }
|
232 | onMouseclick(event) {
|
233 | if (this.disabled || this.readonly || this.isInputClick(event)) {
|
234 | return;
|
235 | }
|
236 | this.onClick.emit(event);
|
237 | this.accessibleViewChild.nativeElement.focus();
|
238 | if (this.overlayVisible)
|
239 | this.hide(event);
|
240 | else
|
241 | this.show();
|
242 | this.cd.detectChanges();
|
243 | }
|
244 | isInputClick(event) {
|
245 | return DomHandler.hasClass(event.target, 'p-dropdown-clear-icon') ||
|
246 | event.target.isSameNode(this.accessibleViewChild.nativeElement) ||
|
247 | (this.editableInputViewChild && event.target.isSameNode(this.editableInputViewChild.nativeElement));
|
248 | }
|
249 | isOutsideClicked(event) {
|
250 | return !(this.el.nativeElement.isSameNode(event.target) || this.el.nativeElement.contains(event.target) || (this.overlay && this.overlay.contains(event.target)));
|
251 | }
|
252 | onEditableInputClick() {
|
253 | this.bindDocumentClickListener();
|
254 | }
|
255 | onEditableInputFocus(event) {
|
256 | this.focused = true;
|
257 | this.hide(event);
|
258 | this.onFocus.emit(event);
|
259 | }
|
260 | onEditableInputChange(event) {
|
261 | this.value = event.target.value;
|
262 | this.updateSelectedOption(this.value);
|
263 | this.onModelChange(this.value);
|
264 | this.onChange.emit({
|
265 | originalEvent: event,
|
266 | value: this.value
|
267 | });
|
268 | }
|
269 | show() {
|
270 | this.overlayVisible = true;
|
271 | }
|
272 | onOverlayAnimationStart(event) {
|
273 | switch (event.toState) {
|
274 | case 'visible':
|
275 | this.overlay = event.element;
|
276 | let itemsWrapperSelector = this.virtualScroll ? '.cdk-virtual-scroll-viewport' : '.p-dropdown-items-wrapper';
|
277 | this.itemsWrapper = DomHandler.findSingle(this.overlay, itemsWrapperSelector);
|
278 | this.appendOverlay();
|
279 | if (this.autoZIndex) {
|
280 | this.overlay.style.zIndex = String(this.baseZIndex + (++DomHandler.zindex));
|
281 | }
|
282 | this.alignOverlay();
|
283 | this.bindDocumentClickListener();
|
284 | this.bindDocumentResizeListener();
|
285 | this.bindScrollListener();
|
286 | if (this.options && this.options.length) {
|
287 | if (!this.virtualScroll) {
|
288 | let selectedListItem = DomHandler.findSingle(this.itemsWrapper, '.p-dropdown-item.p-highlight');
|
289 | if (selectedListItem) {
|
290 | DomHandler.scrollInView(this.itemsWrapper, selectedListItem);
|
291 | }
|
292 | }
|
293 | }
|
294 | if (this.filterViewChild && this.filterViewChild.nativeElement) {
|
295 | this.preventModelTouched = true;
|
296 | if (this.autofocusFilter) {
|
297 | this.filterViewChild.nativeElement.focus();
|
298 | }
|
299 | }
|
300 | this.onShow.emit(event);
|
301 | break;
|
302 | case 'void':
|
303 | this.onOverlayHide();
|
304 | break;
|
305 | }
|
306 | }
|
307 | scrollToSelectedVirtualScrollElement() {
|
308 | if (!this.virtualAutoScrolled) {
|
309 | if (this.viewPortOffsetTop) {
|
310 | this.viewPort.scrollToOffset(this.viewPortOffsetTop);
|
311 | }
|
312 | else if (this.virtualScrollSelectedIndex > -1) {
|
313 | this.viewPort.scrollToIndex(this.virtualScrollSelectedIndex);
|
314 | }
|
315 | }
|
316 | this.virtualAutoScrolled = true;
|
317 | }
|
318 | updateVirtualScrollSelectedIndex(resetOffset) {
|
319 | if (this.selectedOption && this.optionsToDisplay && this.optionsToDisplay.length) {
|
320 | if (resetOffset) {
|
321 | this.viewPortOffsetTop = 0;
|
322 | }
|
323 | this.virtualScrollSelectedIndex = this.findOptionIndex(this.selectedOption.value, this.optionsToDisplay);
|
324 | }
|
325 | }
|
326 | appendOverlay() {
|
327 | if (this.appendTo) {
|
328 | if (this.appendTo === 'body')
|
329 | document.body.appendChild(this.overlay);
|
330 | else
|
331 | DomHandler.appendChild(this.overlay, this.appendTo);
|
332 | if (!this.overlay.style.minWidth) {
|
333 | this.overlay.style.minWidth = DomHandler.getWidth(this.containerViewChild.nativeElement) + 'px';
|
334 | }
|
335 | }
|
336 | }
|
337 | restoreOverlayAppend() {
|
338 | if (this.overlay && this.appendTo) {
|
339 | this.el.nativeElement.appendChild(this.overlay);
|
340 | }
|
341 | }
|
342 | hide(event) {
|
343 | this.overlayVisible = false;
|
344 | if (this.filter && this.resetFilterOnHide) {
|
345 | this.resetFilter();
|
346 | }
|
347 | if (this.virtualScroll) {
|
348 | this.virtualAutoScrolled = false;
|
349 | }
|
350 | this.cd.markForCheck();
|
351 | this.onHide.emit(event);
|
352 | }
|
353 | alignOverlay() {
|
354 | if (this.overlay) {
|
355 | if (this.appendTo)
|
356 | DomHandler.absolutePosition(this.overlay, this.containerViewChild.nativeElement);
|
357 | else
|
358 | DomHandler.relativePosition(this.overlay, this.containerViewChild.nativeElement);
|
359 | }
|
360 | }
|
361 | onInputFocus(event) {
|
362 | this.focused = true;
|
363 | this.onFocus.emit(event);
|
364 | }
|
365 | onInputBlur(event) {
|
366 | this.focused = false;
|
367 | this.onBlur.emit(event);
|
368 | if (!this.preventModelTouched) {
|
369 | this.onModelTouched();
|
370 | }
|
371 | this.preventModelTouched = false;
|
372 | }
|
373 | findPrevEnabledOption(index) {
|
374 | let prevEnabledOption;
|
375 | if (this.optionsToDisplay && this.optionsToDisplay.length) {
|
376 | for (let i = (index - 1); 0 <= i; i--) {
|
377 | let option = this.optionsToDisplay[i];
|
378 | if (option.disabled) {
|
379 | continue;
|
380 | }
|
381 | else {
|
382 | prevEnabledOption = option;
|
383 | break;
|
384 | }
|
385 | }
|
386 | if (!prevEnabledOption) {
|
387 | for (let i = this.optionsToDisplay.length - 1; i >= index; i--) {
|
388 | let option = this.optionsToDisplay[i];
|
389 | if (option.disabled) {
|
390 | continue;
|
391 | }
|
392 | else {
|
393 | prevEnabledOption = option;
|
394 | break;
|
395 | }
|
396 | }
|
397 | }
|
398 | }
|
399 | return prevEnabledOption;
|
400 | }
|
401 | findNextEnabledOption(index) {
|
402 | let nextEnabledOption;
|
403 | if (this.optionsToDisplay && this.optionsToDisplay.length) {
|
404 | for (let i = (index + 1); index < (this.optionsToDisplay.length - 1); i++) {
|
405 | let option = this.optionsToDisplay[i];
|
406 | if (option.disabled) {
|
407 | continue;
|
408 | }
|
409 | else {
|
410 | nextEnabledOption = option;
|
411 | break;
|
412 | }
|
413 | }
|
414 | if (!nextEnabledOption) {
|
415 | for (let i = 0; i < index; i++) {
|
416 | let option = this.optionsToDisplay[i];
|
417 | if (option.disabled) {
|
418 | continue;
|
419 | }
|
420 | else {
|
421 | nextEnabledOption = option;
|
422 | break;
|
423 | }
|
424 | }
|
425 | }
|
426 | }
|
427 | return nextEnabledOption;
|
428 | }
|
429 | onKeydown(event, search) {
|
430 | if (this.readonly || !this.optionsToDisplay || this.optionsToDisplay.length === null) {
|
431 | return;
|
432 | }
|
433 | switch (event.which) {
|
434 |
|
435 | case 40:
|
436 | if (!this.overlayVisible && event.altKey) {
|
437 | this.show();
|
438 | }
|
439 | else {
|
440 | if (this.group) {
|
441 | let selectedItemIndex = this.selectedOption ? this.findOptionGroupIndex(this.selectedOption.value, this.optionsToDisplay) : -1;
|
442 | if (selectedItemIndex !== -1) {
|
443 | let nextItemIndex = selectedItemIndex.itemIndex + 1;
|
444 | if (nextItemIndex < (this.optionsToDisplay[selectedItemIndex.groupIndex].items.length)) {
|
445 | this.selectItem(event, this.optionsToDisplay[selectedItemIndex.groupIndex].items[nextItemIndex]);
|
446 | this.selectedOptionUpdated = true;
|
447 | }
|
448 | else if (this.optionsToDisplay[selectedItemIndex.groupIndex + 1]) {
|
449 | this.selectItem(event, this.optionsToDisplay[selectedItemIndex.groupIndex + 1].items[0]);
|
450 | this.selectedOptionUpdated = true;
|
451 | }
|
452 | }
|
453 | else {
|
454 | this.selectItem(event, this.optionsToDisplay[0].items[0]);
|
455 | }
|
456 | }
|
457 | else {
|
458 | let selectedItemIndex = this.selectedOption ? this.findOptionIndex(this.selectedOption.value, this.optionsToDisplay) : -1;
|
459 | let nextEnabledOption = this.findNextEnabledOption(selectedItemIndex);
|
460 | if (nextEnabledOption) {
|
461 | this.selectItem(event, nextEnabledOption);
|
462 | this.selectedOptionUpdated = true;
|
463 | }
|
464 | }
|
465 | }
|
466 | event.preventDefault();
|
467 | break;
|
468 |
|
469 | case 38:
|
470 | if (this.group) {
|
471 | let selectedItemIndex = this.selectedOption ? this.findOptionGroupIndex(this.selectedOption.value, this.optionsToDisplay) : -1;
|
472 | if (selectedItemIndex !== -1) {
|
473 | let prevItemIndex = selectedItemIndex.itemIndex - 1;
|
474 | if (prevItemIndex >= 0) {
|
475 | this.selectItem(event, this.optionsToDisplay[selectedItemIndex.groupIndex].items[prevItemIndex]);
|
476 | this.selectedOptionUpdated = true;
|
477 | }
|
478 | else if (prevItemIndex < 0) {
|
479 | let prevGroup = this.optionsToDisplay[selectedItemIndex.groupIndex - 1];
|
480 | if (prevGroup) {
|
481 | this.selectItem(event, prevGroup.items[prevGroup.items.length - 1]);
|
482 | this.selectedOptionUpdated = true;
|
483 | }
|
484 | }
|
485 | }
|
486 | }
|
487 | else {
|
488 | let selectedItemIndex = this.selectedOption ? this.findOptionIndex(this.selectedOption.value, this.optionsToDisplay) : -1;
|
489 | let prevEnabledOption = this.findPrevEnabledOption(selectedItemIndex);
|
490 | if (prevEnabledOption) {
|
491 | this.selectItem(event, prevEnabledOption);
|
492 | this.selectedOptionUpdated = true;
|
493 | }
|
494 | }
|
495 | event.preventDefault();
|
496 | break;
|
497 |
|
498 | case 32:
|
499 | case 32:
|
500 | if (!this.overlayVisible) {
|
501 | this.show();
|
502 | event.preventDefault();
|
503 | }
|
504 | break;
|
505 |
|
506 | case 13:
|
507 | if (!this.filter || (this.optionsToDisplay && this.optionsToDisplay.length > 0)) {
|
508 | this.hide(event);
|
509 | }
|
510 | event.preventDefault();
|
511 | break;
|
512 |
|
513 | case 27:
|
514 | case 9:
|
515 | this.hide(event);
|
516 | break;
|
517 |
|
518 | default:
|
519 | if (search) {
|
520 | this.search(event);
|
521 | }
|
522 | break;
|
523 | }
|
524 | }
|
525 | search(event) {
|
526 | if (this.searchTimeout) {
|
527 | clearTimeout(this.searchTimeout);
|
528 | }
|
529 | const char = event.key;
|
530 | this.previousSearchChar = this.currentSearchChar;
|
531 | this.currentSearchChar = char;
|
532 | if (this.previousSearchChar === this.currentSearchChar)
|
533 | this.searchValue = this.currentSearchChar;
|
534 | else
|
535 | this.searchValue = this.searchValue ? this.searchValue + char : char;
|
536 | let newOption;
|
537 | if (this.group) {
|
538 | let searchIndex = this.selectedOption ? this.findOptionGroupIndex(this.selectedOption.value, this.optionsToDisplay) : { groupIndex: 0, itemIndex: 0 };
|
539 | newOption = this.searchOptionWithinGroup(searchIndex);
|
540 | }
|
541 | else {
|
542 | let searchIndex = this.selectedOption ? this.findOptionIndex(this.selectedOption.value, this.optionsToDisplay) : -1;
|
543 | newOption = this.searchOption(++searchIndex);
|
544 | }
|
545 | if (newOption && !newOption.disabled) {
|
546 | this.selectItem(event, newOption);
|
547 | this.selectedOptionUpdated = true;
|
548 | }
|
549 | this.searchTimeout = setTimeout(() => {
|
550 | this.searchValue = null;
|
551 | }, 250);
|
552 | }
|
553 | searchOption(index) {
|
554 | let option;
|
555 | if (this.searchValue) {
|
556 | option = this.searchOptionInRange(index, this.optionsToDisplay.length);
|
557 | if (!option) {
|
558 | option = this.searchOptionInRange(0, index);
|
559 | }
|
560 | }
|
561 | return option;
|
562 | }
|
563 | searchOptionInRange(start, end) {
|
564 | for (let i = start; i < end; i++) {
|
565 | let opt = this.optionsToDisplay[i];
|
566 | if (opt.label.toLocaleLowerCase(this.filterLocale).startsWith(this.searchValue.toLocaleLowerCase(this.filterLocale)) && !opt.disabled) {
|
567 | return opt;
|
568 | }
|
569 | }
|
570 | return null;
|
571 | }
|
572 | searchOptionWithinGroup(index) {
|
573 | let option;
|
574 | if (this.searchValue) {
|
575 | for (let i = index.groupIndex; i < this.optionsToDisplay.length; i++) {
|
576 | for (let j = (index.groupIndex === i) ? (index.itemIndex + 1) : 0; j < this.optionsToDisplay[i].items.length; j++) {
|
577 | let opt = this.optionsToDisplay[i].items[j];
|
578 | if (opt.label.toLocaleLowerCase(this.filterLocale).startsWith(this.searchValue.toLocaleLowerCase(this.filterLocale)) && !opt.disabled) {
|
579 | return opt;
|
580 | }
|
581 | }
|
582 | }
|
583 | if (!option) {
|
584 | for (let i = 0; i <= index.groupIndex; i++) {
|
585 | for (let j = 0; j < ((index.groupIndex === i) ? index.itemIndex : this.optionsToDisplay[i].items.length); j++) {
|
586 | let opt = this.optionsToDisplay[i].items[j];
|
587 | if (opt.label.toLocaleLowerCase(this.filterLocale).startsWith(this.searchValue.toLocaleLowerCase(this.filterLocale)) && !opt.disabled) {
|
588 | return opt;
|
589 | }
|
590 | }
|
591 | }
|
592 | }
|
593 | }
|
594 | return null;
|
595 | }
|
596 | findOptionIndex(val, opts) {
|
597 | let index = -1;
|
598 | if (opts) {
|
599 | for (let i = 0; i < opts.length; i++) {
|
600 | if ((val == null && opts[i].value == null) || ObjectUtils.equals(val, opts[i].value, this.dataKey)) {
|
601 | index = i;
|
602 | break;
|
603 | }
|
604 | }
|
605 | }
|
606 | return index;
|
607 | }
|
608 | findOptionGroupIndex(val, opts) {
|
609 | let groupIndex, itemIndex;
|
610 | if (opts) {
|
611 | for (let i = 0; i < opts.length; i++) {
|
612 | groupIndex = i;
|
613 | itemIndex = this.findOptionIndex(val, opts[i].items);
|
614 | if (itemIndex !== -1) {
|
615 | break;
|
616 | }
|
617 | }
|
618 | }
|
619 | if (itemIndex !== -1) {
|
620 | return { groupIndex: groupIndex, itemIndex: itemIndex };
|
621 | }
|
622 | else {
|
623 | return -1;
|
624 | }
|
625 | }
|
626 | findOption(val, opts, inGroup) {
|
627 | if (this.group && !inGroup) {
|
628 | let opt;
|
629 | if (opts && opts.length) {
|
630 | for (let optgroup of opts) {
|
631 | opt = this.findOption(val, optgroup.items, true);
|
632 | if (opt) {
|
633 | break;
|
634 | }
|
635 | }
|
636 | }
|
637 | return opt;
|
638 | }
|
639 | else {
|
640 | let index = this.findOptionIndex(val, opts);
|
641 | return (index != -1) ? opts[index] : null;
|
642 | }
|
643 | }
|
644 | onFilter(event) {
|
645 | let inputValue = event.target.value;
|
646 | if (inputValue && inputValue.length) {
|
647 | this.filterValue = inputValue;
|
648 | this.activateFilter();
|
649 | }
|
650 | else {
|
651 | this.filterValue = null;
|
652 | this.optionsToDisplay = this.options;
|
653 | }
|
654 | this.optionsChanged = true;
|
655 | }
|
656 | activateFilter() {
|
657 | let searchFields = this.filterBy.split(',');
|
658 | if (this.options && this.options.length) {
|
659 | if (this.group) {
|
660 | let filteredGroups = [];
|
661 | for (let optgroup of this.options) {
|
662 | let filteredSubOptions = FilterUtils.filter(optgroup.items, searchFields, this.filterValue, this.filterMatchMode, this.filterLocale);
|
663 | if (filteredSubOptions && filteredSubOptions.length) {
|
664 | filteredGroups.push({
|
665 | label: optgroup.label,
|
666 | value: optgroup.value,
|
667 | items: filteredSubOptions
|
668 | });
|
669 | }
|
670 | }
|
671 | this.optionsToDisplay = filteredGroups;
|
672 | }
|
673 | else {
|
674 | this.optionsToDisplay = FilterUtils.filter(this.options, searchFields, this.filterValue, this.filterMatchMode, this.filterLocale);
|
675 | }
|
676 | this.optionsChanged = true;
|
677 | }
|
678 | }
|
679 | applyFocus() {
|
680 | if (this.editable)
|
681 | DomHandler.findSingle(this.el.nativeElement, '.p-dropdown-label.p-inputtext').focus();
|
682 | else
|
683 | DomHandler.findSingle(this.el.nativeElement, 'input[readonly]').focus();
|
684 | }
|
685 | focus() {
|
686 | this.applyFocus();
|
687 | }
|
688 | bindDocumentClickListener() {
|
689 | if (!this.documentClickListener) {
|
690 | const documentTarget = this.el ? this.el.nativeElement.ownerDocument : 'document';
|
691 | this.documentClickListener = this.renderer.listen(documentTarget, 'click', (event) => {
|
692 | if (this.isOutsideClicked(event)) {
|
693 | this.hide(event);
|
694 | this.unbindDocumentClickListener();
|
695 | }
|
696 | this.cd.markForCheck();
|
697 | });
|
698 | }
|
699 | }
|
700 | unbindDocumentClickListener() {
|
701 | if (this.documentClickListener) {
|
702 | this.documentClickListener();
|
703 | this.documentClickListener = null;
|
704 | }
|
705 | }
|
706 | bindDocumentResizeListener() {
|
707 | this.documentResizeListener = this.onWindowResize.bind(this);
|
708 | window.addEventListener('resize', this.documentResizeListener);
|
709 | }
|
710 | unbindDocumentResizeListener() {
|
711 | if (this.documentResizeListener) {
|
712 | window.removeEventListener('resize', this.documentResizeListener);
|
713 | this.documentResizeListener = null;
|
714 | }
|
715 | }
|
716 | onWindowResize() {
|
717 | if (!DomHandler.isAndroid()) {
|
718 | this.hide(event);
|
719 | }
|
720 | }
|
721 | bindScrollListener() {
|
722 | if (!this.scrollHandler) {
|
723 | this.scrollHandler = new ConnectedOverlayScrollHandler(this.containerViewChild.nativeElement, (event) => {
|
724 | if (this.overlayVisible) {
|
725 | this.hide(event);
|
726 | }
|
727 | });
|
728 | }
|
729 | this.scrollHandler.bindScrollListener();
|
730 | }
|
731 | unbindScrollListener() {
|
732 | if (this.scrollHandler) {
|
733 | this.scrollHandler.unbindScrollListener();
|
734 | }
|
735 | }
|
736 | updateFilledState() {
|
737 | this.filled = (this.selectedOption != null);
|
738 | }
|
739 | clear(event) {
|
740 | this.value = null;
|
741 | this.onModelChange(this.value);
|
742 | this.onChange.emit({
|
743 | originalEvent: event,
|
744 | value: this.value
|
745 | });
|
746 | this.updateSelectedOption(this.value);
|
747 | this.updateEditableLabel();
|
748 | this.updateFilledState();
|
749 | }
|
750 | onOverlayHide() {
|
751 | this.unbindDocumentClickListener();
|
752 | this.unbindDocumentResizeListener();
|
753 | this.unbindScrollListener();
|
754 | this.overlay = null;
|
755 | this.itemsWrapper = null;
|
756 | this.onModelTouched();
|
757 | }
|
758 | ngOnDestroy() {
|
759 | if (this.scrollHandler) {
|
760 | this.scrollHandler.destroy();
|
761 | this.scrollHandler = null;
|
762 | }
|
763 | this.restoreOverlayAppend();
|
764 | this.onOverlayHide();
|
765 | }
|
766 | }
|
767 | Dropdown.decorators = [
|
768 | { type: Component, args: [{
|
769 | selector: 'p-dropdown',
|
770 | template: `
|
771 | <div #container [ngClass]="{'p-dropdown p-component':true,
|
772 | 'p-disabled':disabled, 'p-dropdown-open':overlayVisible, 'p-focus':focused, 'p-dropdown-clearable': showClear && !disabled}"
|
773 | (click)="onMouseclick($event)" [ngStyle]="style" [class]="styleClass">
|
774 | <div class="p-hidden-accessible">
|
775 | <input #in [attr.id]="inputId" type="text" [attr.aria-label]="selectedOption ? selectedOption.label : ' '" readonly (focus)="onInputFocus($event)" aria-haspopup="listbox"
|
776 | aria-haspopup="listbox" [attr.aria-expanded]="overlayVisible" [attr.aria-labelledby]="ariaLabelledBy" (blur)="onInputBlur($event)" (keydown)="onKeydown($event, true)"
|
777 | [disabled]="disabled" [attr.tabindex]="tabindex" [attr.autofocus]="autofocus" role="listbox">
|
778 | </div>
|
779 | <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">
|
780 | <ng-container *ngIf="!selectedItemTemplate">{{label||'empty'}}</ng-container>
|
781 | <ng-container *ngTemplateOutlet="selectedItemTemplate; context: {$implicit: selectedOption}"></ng-container>
|
782 | </span>
|
783 | <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>
|
784 | <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"
|
785 | aria-haspopup="listbox" [attr.aria-expanded]="overlayVisible" (click)="onEditableInputClick()" (input)="onEditableInputChange($event)" (focus)="onEditableInputFocus($event)" (blur)="onInputBlur($event)">
|
786 | <i class="p-dropdown-clear-icon pi pi-times" (click)="clear($event)" *ngIf="value != null && showClear && !disabled"></i>
|
787 | <div class="p-dropdown-trigger" role="button" aria-haspopup="listbox" [attr.aria-expanded]="overlayVisible">
|
788 | <span class="p-dropdown-trigger-icon" [ngClass]="dropdownIcon"></span>
|
789 | </div>
|
790 | <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">
|
791 | <div class="p-dropdown-header" *ngIf="filter" >
|
792 | <div class="p-dropdown-filter-container" (click)="$event.stopPropagation()">
|
793 | <input #filter type="text" autocomplete="off" [value]="filterValue||''" class="p-dropdown-filter p-inputtext p-component" [attr.placeholder]="filterPlaceholder"
|
794 | (keydown.enter)="$event.preventDefault()" (keydown)="onKeydown($event, false)" (input)="onFilter($event)" [attr.aria-label]="ariaFilterLabel">
|
795 | <span class="p-dropdown-filter-icon pi pi-search"></span>
|
796 | </div>
|
797 | </div>
|
798 | <div class="p-dropdown-items-wrapper" [style.max-height]="virtualScroll ? 'auto' : (scrollHeight||'auto')">
|
799 | <ul class="p-dropdown-items" role="listbox">
|
800 | <ng-container *ngIf="group">
|
801 | <ng-template ngFor let-optgroup [ngForOf]="optionsToDisplay">
|
802 | <li class="p-dropdown-item-group">
|
803 | <span *ngIf="!groupTemplate">{{optgroup.label||'empty'}}</span>
|
804 | <ng-container *ngTemplateOutlet="groupTemplate; context: {$implicit: optgroup}"></ng-container>
|
805 | </li>
|
806 | <ng-container *ngTemplateOutlet="itemslist; context: {$implicit: optgroup.items, selectedOption: selectedOption}"></ng-container>
|
807 | </ng-template>
|
808 | </ng-container>
|
809 | <ng-container *ngIf="!group">
|
810 | <ng-container *ngTemplateOutlet="itemslist; context: {$implicit: optionsToDisplay, selectedOption: selectedOption}"></ng-container>
|
811 | </ng-container>
|
812 | <ng-template #itemslist let-options let-selectedOption="selectedOption">
|
813 | <ng-container *ngIf="!virtualScroll; else virtualScrollList">
|
814 | <ng-template ngFor let-option let-i="index" [ngForOf]="options">
|
815 | <p-dropdownItem [option]="option" [selected]="selectedOption == option"
|
816 | (onClick)="onItemClick($event)"
|
817 | [template]="itemTemplate"></p-dropdownItem>
|
818 | </ng-template>
|
819 | </ng-container>
|
820 | <ng-template #virtualScrollList>
|
821 | <cdk-virtual-scroll-viewport (scrolledIndexChange)="scrollToSelectedVirtualScrollElement()" #viewport [ngStyle]="{'height': scrollHeight}" [itemSize]="itemSize" *ngIf="virtualScroll && optionsToDisplay && optionsToDisplay.length">
|
822 | <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">
|
823 | <p-dropdownItem [option]="option" [selected]="selectedOption == option"
|
824 | (onClick)="onItemClick($event)"
|
825 | [template]="itemTemplate"></p-dropdownItem>
|
826 | </ng-container>
|
827 | </cdk-virtual-scroll-viewport>
|
828 | </ng-template>
|
829 | </ng-template>
|
830 | <li *ngIf="filter && (!optionsToDisplay || (optionsToDisplay && optionsToDisplay.length === 0))" class="p-dropdown-empty-message">{{emptyFilterMessage}}</li>
|
831 | </ul>
|
832 | </div>
|
833 | </div>
|
834 | </div>
|
835 | `,
|
836 | animations: [
|
837 | trigger('overlayAnimation', [
|
838 | transition(':enter', [
|
839 | style({ opacity: 0, transform: 'scaleY(0.8)' }),
|
840 | animate('{{showTransitionParams}}')
|
841 | ]),
|
842 | transition(':leave', [
|
843 | animate('{{hideTransitionParams}}', style({ opacity: 0 }))
|
844 | ])
|
845 | ])
|
846 | ],
|
847 | host: {
|
848 | '[class.p-inputwrapper-filled]': 'filled',
|
849 | '[class.p-inputwrapper-focus]': 'focused'
|
850 | },
|
851 | providers: [DROPDOWN_VALUE_ACCESSOR],
|
852 | changeDetection: ChangeDetectionStrategy.OnPush,
|
853 | encapsulation: ViewEncapsulation.None,
|
854 | 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%}"]
|
855 | },] }
|
856 | ];
|
857 | Dropdown.ctorParameters = () => [
|
858 | { type: ElementRef },
|
859 | { type: Renderer2 },
|
860 | { type: ChangeDetectorRef },
|
861 | { type: NgZone }
|
862 | ];
|
863 | Dropdown.propDecorators = {
|
864 | scrollHeight: [{ type: Input }],
|
865 | filter: [{ type: Input }],
|
866 | name: [{ type: Input }],
|
867 | style: [{ type: Input }],
|
868 | panelStyle: [{ type: Input }],
|
869 | styleClass: [{ type: Input }],
|
870 | panelStyleClass: [{ type: Input }],
|
871 | readonly: [{ type: Input }],
|
872 | required: [{ type: Input }],
|
873 | editable: [{ type: Input }],
|
874 | appendTo: [{ type: Input }],
|
875 | tabindex: [{ type: Input }],
|
876 | placeholder: [{ type: Input }],
|
877 | filterPlaceholder: [{ type: Input }],
|
878 | filterLocale: [{ type: Input }],
|
879 | inputId: [{ type: Input }],
|
880 | selectId: [{ type: Input }],
|
881 | dataKey: [{ type: Input }],
|
882 | filterBy: [{ type: Input }],
|
883 | autofocus: [{ type: Input }],
|
884 | resetFilterOnHide: [{ type: Input }],
|
885 | dropdownIcon: [{ type: Input }],
|
886 | optionLabel: [{ type: Input }],
|
887 | autoDisplayFirst: [{ type: Input }],
|
888 | group: [{ type: Input }],
|
889 | showClear: [{ type: Input }],
|
890 | emptyFilterMessage: [{ type: Input }],
|
891 | virtualScroll: [{ type: Input }],
|
892 | itemSize: [{ type: Input }],
|
893 | autoZIndex: [{ type: Input }],
|
894 | baseZIndex: [{ type: Input }],
|
895 | showTransitionOptions: [{ type: Input }],
|
896 | hideTransitionOptions: [{ type: Input }],
|
897 | ariaFilterLabel: [{ type: Input }],
|
898 | ariaLabelledBy: [{ type: Input }],
|
899 | filterMatchMode: [{ type: Input }],
|
900 | maxlength: [{ type: Input }],
|
901 | tooltip: [{ type: Input }],
|
902 | tooltipPosition: [{ type: Input }],
|
903 | tooltipPositionStyle: [{ type: Input }],
|
904 | tooltipStyleClass: [{ type: Input }],
|
905 | autofocusFilter: [{ type: Input }],
|
906 | onChange: [{ type: Output }],
|
907 | onFocus: [{ type: Output }],
|
908 | onBlur: [{ type: Output }],
|
909 | onClick: [{ type: Output }],
|
910 | onShow: [{ type: Output }],
|
911 | onHide: [{ type: Output }],
|
912 | containerViewChild: [{ type: ViewChild, args: ['container',] }],
|
913 | filterViewChild: [{ type: ViewChild, args: ['filter',] }],
|
914 | accessibleViewChild: [{ type: ViewChild, args: ['in',] }],
|
915 | viewPort: [{ type: ViewChild, args: [CdkVirtualScrollViewport,] }],
|
916 | editableInputViewChild: [{ type: ViewChild, args: ['editableInput',] }],
|
917 | templates: [{ type: ContentChildren, args: [PrimeTemplate,] }],
|
918 | disabled: [{ type: Input }],
|
919 | options: [{ type: Input }]
|
920 | };
|
921 | class DropdownModule {
|
922 | }
|
923 | DropdownModule.decorators = [
|
924 | { type: NgModule, args: [{
|
925 | imports: [CommonModule, SharedModule, ScrollingModule, TooltipModule, RippleModule],
|
926 | exports: [Dropdown, SharedModule, ScrollingModule],
|
927 | declarations: [Dropdown, DropdownItem]
|
928 | },] }
|
929 | ];
|
930 |
|
931 |
|
932 |
|
933 |
|
934 |
|
935 | export { DROPDOWN_VALUE_ACCESSOR, Dropdown, DropdownItem, DropdownModule };
|
936 |
|