/
typeahead-window.ts
124 lines (103 loc) · 3.37 KB
/
typeahead-window.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
import {Component, EventEmitter, Input, OnInit, Output, TemplateRef, ViewEncapsulation} from '@angular/core';
import {toString} from '../util/util';
/**
* The context for the typeahead result template in case you want to override the default one.
*/
export interface ResultTemplateContext {
/**
* Your typeahead result item.
*/
result: any;
/**
* Search term from the `<input>` used to get current result.
*/
term: string;
}
@Component({
selector: 'ngb-typeahead-window',
exportAs: 'ngbTypeaheadWindow',
encapsulation: ViewEncapsulation.None,
host: {'(mousedown)': '$event.preventDefault()', 'class': 'dropdown-menu show', 'role': 'listbox', '[id]': 'id'},
template: `
<ng-template #rt let-result="result" let-term="term" let-formatter="formatter">
<ngb-highlight [result]="formatter(result)" [term]="term"></ngb-highlight>
</ng-template>
<ng-template ngFor [ngForOf]="results" let-result let-idx="index">
<button type="button" class="dropdown-item" role="option"
[id]="id + '-' + idx"
[class.active]="idx === activeIdx"
(mouseenter)="markActive(idx)"
(click)="select(result)">
<ng-template [ngTemplateOutlet]="resultTemplate || rt"
[ngTemplateOutletContext]="{result: result, term: term, formatter: formatter}"></ng-template>
</button>
</ng-template>
`
})
export class NgbTypeaheadWindow implements OnInit {
activeIdx = 0;
/**
* The id for the typeahead window. The id should be unique and the same
* as the associated typeahead's id.
*/
@Input() id: string;
/**
* Flag indicating if the first row should be active initially
*/
@Input() focusFirst = true;
/**
* Typeahead match results to be displayed
*/
@Input() results;
/**
* Search term used to get current results
*/
@Input() term: string;
/**
* A function used to format a given result before display. This function should return a formatted string without any
* HTML markup
*/
@Input() formatter = toString;
/**
* A template to override a matching result default display
*/
@Input() resultTemplate: TemplateRef<ResultTemplateContext>;
/**
* Event raised when user selects a particular result row
*/
@Output('select') selectEvent = new EventEmitter();
@Output('activeChange') activeChangeEvent = new EventEmitter();
hasActive() { return this.activeIdx > -1 && this.activeIdx < this.results.length; }
getActive() { return this.results[this.activeIdx]; }
markActive(activeIdx: number) {
this.activeIdx = activeIdx;
this._activeChanged();
}
next() {
if (this.activeIdx === this.results.length - 1) {
this.activeIdx = this.focusFirst ? (this.activeIdx + 1) % this.results.length : -1;
} else {
this.activeIdx++;
}
this._activeChanged();
}
prev() {
if (this.activeIdx < 0) {
this.activeIdx = this.results.length - 1;
} else if (this.activeIdx === 0) {
this.activeIdx = this.focusFirst ? this.results.length - 1 : -1;
} else {
this.activeIdx--;
}
this._activeChanged();
}
resetActive() {
this.activeIdx = this.focusFirst ? 0 : -1;
this._activeChanged();
}
select(item) { this.selectEvent.emit(item); }
ngOnInit() { this.resetActive(); }
private _activeChanged() {
this.activeChangeEvent.emit(this.activeIdx >= 0 ? this.id + '-' + this.activeIdx : undefined);
}
}