nw-style-guide 21.0.0 → 21.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,13 +1,18 @@
1
1
  import * as i0 from '@angular/core';
2
- import { inject, ChangeDetectorRef, EventEmitter, ViewChild, Output, Input, ChangeDetectionStrategy, Component } from '@angular/core';
2
+ import { inject, ChangeDetectorRef, ElementRef, EventEmitter, ViewChildren, ViewChild, Output, Input, ChangeDetectionStrategy, Component } from '@angular/core';
3
3
  import * as i1 from '@angular/forms';
4
4
  import { FormControl, ReactiveFormsModule } from '@angular/forms';
5
5
  import { isUndefined } from 'lodash-es';
6
- import { NgIf, NgFor, NgClass } from '@angular/common';
6
+ import { NgClass } from '@angular/common';
7
+ import { LiveAnnouncer } from '@angular/cdk/a11y';
7
8
 
8
9
  class NwPickerComponent {
9
10
  constructor() {
10
- this.chRef = inject(ChangeDetectorRef);
11
+ this._cdRef = inject(ChangeDetectorRef);
12
+ this._elementRef = inject(ElementRef);
13
+ this._liveAnnouncer = inject(LiveAnnouncer);
14
+ this.pickerId = `nw-picker-${++NwPickerComponent._idCounter}`;
15
+ this.items = [];
11
16
  this.inputClasses = '';
12
17
  this.placeholderText = 'Search...';
13
18
  this.inputPlaceholderText = 'Search...';
@@ -24,61 +29,44 @@ class NwPickerComponent {
24
29
  this.toggleExclude = new EventEmitter();
25
30
  this.edit = new EventEmitter();
26
31
  this.closed = new EventEmitter();
27
- // eslint-disable-next-line @angular-eslint/no-output-native
28
32
  this.focus = new EventEmitter();
33
+ this.blur = new EventEmitter();
29
34
  this.clearAll = new EventEmitter();
30
35
  this.clearSingle = new EventEmitter();
31
36
  this.clearSearch = new EventEmitter();
32
37
  this.desc = new EventEmitter();
33
38
  this.asc = new EventEmitter();
39
+ this.displayItems = [];
34
40
  this.searchTerm = new FormControl();
35
41
  this.canViewResults = false;
36
42
  this.selectionsAreShowing = false;
37
43
  this.maxHeight = 400;
44
+ this.focusedIndex = -1;
38
45
  this._subs = [];
39
46
  }
47
+ // static counter to generate unique ids for multiple instances of the component on the same page
48
+ static { this._idCounter = 0; }
49
+ get focusedItemId() {
50
+ if (this.focusedIndex >= 0 && this.displayItems?.[this.focusedIndex]) {
51
+ return `${this.pickerId}-option-${this.displayItems[this.focusedIndex].id}`;
52
+ }
53
+ return null;
54
+ }
40
55
  ngOnInit() {
41
56
  this.parentId = this.initialParentId;
42
- this.subscribeToSearchTermChanges();
57
+ this._subscribeToSearchTermChanges();
43
58
  }
44
- ngOnChanges(changes) {
59
+ ngOnChanges() {
45
60
  if (this.isDisabled) {
46
61
  this.searchTerm.disable();
47
62
  }
48
63
  }
49
- subscribeToSearchTermChanges() {
50
- const sub = this.searchTerm.valueChanges.subscribe(val => {
51
- this.selectionsAreShowing = false;
52
- if (val.length) {
53
- const displayItems = this.items.filter(item => {
54
- return ((item.searchValues || []).some(value => {
55
- return value.toLowerCase().includes(val.toLowerCase());
56
- }) || item.displayName.toLowerCase().includes(val.toLowerCase()));
57
- });
58
- // remove duplicate items
59
- this.displayItems = displayItems.reduce((items, item) => (items.find(x => x.id === item.id) ? [...items] : [...items, item]), []);
60
- }
61
- else {
62
- this.setDisplayItemsFromParentId(this.parentId);
63
- }
64
- });
65
- this._subs.push(sub);
66
- }
67
64
  ascend(event, item) {
68
65
  event.stopPropagation();
69
- this.setDisplayItemsFromParentId(item.parentId);
66
+ this._setDisplayItemsFromParentId(item.parentId);
70
67
  this.asc.emit(item);
71
- }
72
- setDisplayItemsFromParentId(parentId, e) {
73
- if (e) {
74
- e.stopPropagation();
75
- }
76
- if (!this.hasChildren(parentId)) {
77
- return;
78
- }
79
- this.resetSearchTerm();
80
- this.parentId = parentId;
81
- this.displayItems = this.items.filter(i => i.parentId === this.parentId);
68
+ this._cdRef.detectChanges();
69
+ this._focusListItem(0);
82
70
  }
83
71
  displaySelectedItems() {
84
72
  this.displayItems = this.getSelections();
@@ -94,16 +82,64 @@ class NwPickerComponent {
94
82
  }
95
83
  editSelections(event) {
96
84
  event.stopPropagation();
85
+ this._focusInput();
97
86
  this.selectionsAreShowing = true;
98
87
  this.edit.emit(event);
99
88
  }
89
+ onBackClick(event) {
90
+ event.preventDefault();
91
+ event.stopPropagation();
92
+ this._focusInput();
93
+ this.selectionsAreShowing = false;
94
+ this._setDisplayItemsFromParentId(null);
95
+ }
96
+ onOptionItemKeydown(e, item, index) {
97
+ switch (e.key) {
98
+ case 'ArrowDown':
99
+ this.focusNextItem(e);
100
+ break;
101
+ case 'ArrowUp':
102
+ this._focusPrevItem(e);
103
+ break;
104
+ case 'ArrowLeft':
105
+ this._onArrowLeft(e);
106
+ break;
107
+ case 'ArrowRight':
108
+ this.onDrilldown(item);
109
+ break;
110
+ case 'Enter':
111
+ this.toggleItemInclusion(item, e);
112
+ break;
113
+ case 'Escape':
114
+ this._onListItemEscape(e, index);
115
+ break;
116
+ }
117
+ }
118
+ onSelectionItemKeydown(e, item) {
119
+ switch (e.key) {
120
+ case 'ArrowDown':
121
+ this.focusNextItem(e);
122
+ break;
123
+ case 'ArrowUp':
124
+ this._focusPrevItem(e);
125
+ break;
126
+ case 'Enter':
127
+ this.clearSelection(e, item);
128
+ break;
129
+ case 'Escape':
130
+ this.onBackClick(e);
131
+ break;
132
+ }
133
+ }
100
134
  clearSelection(event, item) {
135
+ event.preventDefault();
101
136
  event.stopPropagation();
137
+ this._focusInput();
102
138
  item.added = false;
103
139
  item.excluded = false;
104
140
  this.clearSingle.emit(item);
105
141
  if (this.getSelections().length < 1) {
106
- this.setDisplayItemsFromParentId(null);
142
+ this._setDisplayItemsFromParentId(null);
107
143
  this.selectionsAreShowing = false;
108
144
  }
109
145
  this.selections.emit(this.getSelections());
@@ -118,12 +154,13 @@ class NwPickerComponent {
118
154
  return ci;
119
155
  });
120
156
  this.clearAll.emit();
121
- this.setDisplayItemsFromParentId(null);
157
+ this._setDisplayItemsFromParentId(null);
122
158
  this.selectionsAreShowing = false;
123
159
  this.selections.emit(this.getSelections());
160
+ this._announce('All selections cleared');
124
161
  }
125
- toggleItemInclusion(item, e) {
126
- e.stopPropagation();
162
+ toggleItemInclusion(item, event) {
163
+ event.stopPropagation();
127
164
  // we're assuming that if the component is not multiSelect, then only
128
165
  // one item can be selected at any time
129
166
  if (!this.isMultiSelect) {
@@ -142,16 +179,17 @@ class NwPickerComponent {
142
179
  }
143
180
  });
144
181
  }
145
- this.toggleAncestors(item, false, false);
146
- this.toggleDescendants(item, false);
182
+ this._toggleAncestors(item, false, false);
183
+ this._toggleDescendants(item, false);
147
184
  this.toggleInclude.emit({ item: item, searchTerm: this.searchTerm.value });
148
185
  this.selections.emit(this.getSelections());
186
+ this._announce(`${item.displayName} ${item.added ? 'selected' : 'deselected'}`);
149
187
  if (!this.isMultiSelect) {
150
- this.inputEl.nativeElement.blur();
188
+ this.closeResults();
151
189
  }
152
190
  }
153
- toggleItemExclusion(item, e) {
154
- e.stopPropagation();
191
+ toggleItemExclusion(item, event) {
192
+ event.stopPropagation();
155
193
  item.added = false;
156
194
  item.excluded = !item.excluded;
157
195
  // setting flag for duplicate id's as in case of location for selection and deselection on checkbox click
@@ -162,50 +200,69 @@ class NwPickerComponent {
162
200
  }
163
201
  });
164
202
  }
165
- this.toggleDescendants(item, false, false);
166
- this.toggleAncestors(item, undefined, false);
203
+ this._toggleDescendants(item, false, false);
204
+ this._toggleAncestors(item, undefined, false);
167
205
  this.toggleExclude.emit({ item: item, searchTerm: this.searchTerm.value });
168
206
  this.selections.emit(this.getSelections());
207
+ this._announce(`${item.displayName} ${item.excluded ? 'excluded' : 'exclusion removed'}`);
169
208
  if (!this.isMultiSelect) {
170
- this.inputEl.nativeElement.blur();
209
+ this.closeResults();
171
210
  }
172
211
  }
173
- toggleDescendants(item, add, exclude) {
174
- this.items
175
- .filter(ci => ci.parentId === item.id)
176
- .forEach(ci => {
177
- if (!isUndefined(add)) {
178
- ci.added = add;
179
- }
180
- if (!isUndefined(exclude)) {
181
- ci.excluded = exclude;
182
- }
183
- this.toggleDescendants(ci, add, exclude);
184
- });
212
+ // Returns 0 for the focused row, or the first row when nothing is focused (roving tabindex)
213
+ getTabIndex(i) {
214
+ return this.focusedIndex === i || (this.focusedIndex === -1 && i === 0) ? 0 : -1;
185
215
  }
186
- toggleAncestors(item, add, exclude) {
187
- this.items
188
- .filter(ci => ci.id === item.parentId)
189
- .forEach(ci => {
190
- if (!isUndefined(add)) {
191
- ci.added = add;
192
- }
193
- if (!isUndefined(exclude)) {
194
- ci.excluded = exclude;
195
- }
196
- this.toggleAncestors(ci, add, exclude);
197
- });
216
+ // Returns 0 only when this row is focused (for child elements like checkboxes and buttons)
217
+ getChildTabIndex(i) {
218
+ return this.focusedIndex === i ? 0 : -1;
198
219
  }
199
- preventBlur(e) {
200
- // prevent blurring of the search input
201
- e.preventDefault();
220
+ getAriaLabel(item) {
221
+ let label = item.displayName;
222
+ if (item.added)
223
+ label += ', selected';
224
+ if (item.excluded)
225
+ label += ', excluded';
226
+ if (this.hasChildren(item.id))
227
+ label += ', has sub-items';
228
+ return label;
202
229
  }
203
- resetSearchTerm() {
204
- this.searchTerm.setValue('', { emitEvent: false });
230
+ focusNextItem(event) {
231
+ event.preventDefault();
232
+ if (!this.canViewResults) {
233
+ this._openResultsAndFocusFirstItem();
234
+ return;
235
+ }
236
+ const listLength = this.selectionsAreShowing ? this.getSelections().length : this.displayItems.length;
237
+ if (listLength) {
238
+ this._focusListItem(Math.min(this.focusedIndex + 1, listLength - 1));
239
+ this._cdRef.markForCheck();
240
+ }
241
+ }
242
+ onInputEnter() {
243
+ if (!this.canViewResults) {
244
+ this._openResultsAndFocusFirstItem();
245
+ return;
246
+ }
247
+ }
248
+ onListFocusOut(event) {
249
+ const list = event.currentTarget;
250
+ const relatedTarget = event.relatedTarget;
251
+ if (!list.contains(relatedTarget)) {
252
+ this.focusedIndex = -1;
253
+ this._cdRef.markForCheck();
254
+ }
255
+ }
256
+ onContainerFocusOut() {
257
+ setTimeout(() => {
258
+ if (!this._elementRef.nativeElement.contains(document.activeElement)) {
259
+ this.closeResults({ refocusInput: false });
260
+ this.blur.emit(this.inputEl);
261
+ }
262
+ });
205
263
  }
206
264
  onFocus() {
207
265
  if (!this.isDisabled) {
208
- this.showResults();
209
266
  this.focus.emit(this.inputEl);
210
267
  }
211
268
  }
@@ -213,22 +270,69 @@ class NwPickerComponent {
213
270
  if (!this.isDisabled) {
214
271
  this.parentId = this.initialParentId;
215
272
  this.canViewResults = true;
216
- this.setDisplayItemsFromParentId(this.parentId);
273
+ this._setDisplayItemsFromParentId(this.parentId);
217
274
  }
218
275
  }
219
276
  close() {
220
277
  this.inputEl.nativeElement.blur();
221
278
  }
222
- closeResults() {
279
+ closeResults({ refocusInput = true } = {}) {
280
+ if (!this.canViewResults) {
281
+ return;
282
+ }
223
283
  this.canViewResults = false;
224
- this.searchTerm.setValue('');
284
+ this.focusedIndex = -1;
285
+ this.selectionsAreShowing = false;
286
+ this.searchTerm.setValue('', { emitEvent: false });
225
287
  this.closed.emit();
226
- this.chRef.detectChanges();
288
+ this._cdRef.detectChanges();
289
+ if (refocusInput) {
290
+ this._focusInput();
291
+ }
227
292
  }
228
- onReset($event) {
293
+ onReset() {
229
294
  this.clearSearch.emit();
230
295
  this.searchTerm.setValue('');
231
296
  this.showResults();
297
+ this._focusInput();
298
+ }
299
+ onChevronClick() {
300
+ this.showResults();
301
+ this._focusInput();
302
+ }
303
+ onListItemTab(event, index) {
304
+ const { li, children } = this._focusableChildren(index);
305
+ const lastFocusableChild = children[children.length - 1] ?? li;
306
+ if (event.target !== lastFocusableChild)
307
+ return;
308
+ if (index + 1 >= this.displayItems.length)
309
+ return;
310
+ event.preventDefault();
311
+ this.focusedIndex = index + 1;
312
+ this._cdRef.detectChanges();
313
+ const next = this._focusableChildren(this.focusedIndex);
314
+ (next.children[0] ?? next.li).focus();
315
+ }
316
+ onListItemShiftTab(event, index) {
317
+ const { li, children } = this._focusableChildren(index);
318
+ const firstFocusableChild = children[0] ?? li;
319
+ if (event.target !== firstFocusableChild)
320
+ return;
321
+ event.preventDefault();
322
+ if (index === 0) {
323
+ this._focusInput();
324
+ return;
325
+ }
326
+ this.focusedIndex = index - 1;
327
+ this._cdRef.detectChanges();
328
+ const prev = this._focusableChildren(this.focusedIndex);
329
+ (prev.children[prev.children.length - 1] ?? prev.li).focus();
330
+ }
331
+ onDrilldown(item) {
332
+ this._setDisplayItemsFromParentId(item.id);
333
+ this.desc.emit(item);
334
+ this._cdRef.detectChanges();
335
+ this._focusListItem(0);
232
336
  }
233
337
  getPlaceholderText() {
234
338
  return this.getSelections().length ? this.placeholderText : this.noSelectionsPlaceholderText;
@@ -250,506 +354,133 @@ class NwPickerComponent {
250
354
  }
251
355
  return;
252
356
  }
357
+ _setDisplayItemsFromParentId(parentId) {
358
+ if (!this.hasChildren(parentId)) {
359
+ return;
360
+ }
361
+ this._resetSearchTerm();
362
+ this.parentId = parentId;
363
+ this.displayItems = this.items.filter(i => i.parentId === parentId);
364
+ this.focusedIndex = -1;
365
+ }
366
+ // Returns focus to the search input and clears the focused list item index.
367
+ _focusInput() {
368
+ this.focusedIndex = -1;
369
+ this.inputEl.nativeElement.focus();
370
+ }
371
+ // Opens the results dropdown and moves focus to the first list item.
372
+ // Used when the user triggers a key action (ArrowDown/Enter) while the dropdown is closed.
373
+ _openResultsAndFocusFirstItem() {
374
+ this.showResults();
375
+ this._cdRef.detectChanges();
376
+ this._focusListItem(0);
377
+ }
378
+ _focusPrevItem(event) {
379
+ event.preventDefault();
380
+ if (this.focusedIndex <= 0) {
381
+ this._focusInput();
382
+ return;
383
+ }
384
+ this._focusListItem(this.focusedIndex - 1);
385
+ this._cdRef.markForCheck();
386
+ }
387
+ _toggleDescendants(item, add, exclude) {
388
+ this.items
389
+ .filter(ci => ci.parentId === item.id)
390
+ .forEach(ci => {
391
+ if (!isUndefined(add)) {
392
+ ci.added = add;
393
+ }
394
+ if (!isUndefined(exclude)) {
395
+ ci.excluded = exclude;
396
+ }
397
+ this._toggleDescendants(ci, add, exclude);
398
+ });
399
+ }
400
+ _toggleAncestors(item, add, exclude) {
401
+ this.items
402
+ .filter(ci => ci.id === item.parentId)
403
+ .forEach(ci => {
404
+ if (!isUndefined(add)) {
405
+ ci.added = add;
406
+ }
407
+ if (!isUndefined(exclude)) {
408
+ ci.excluded = exclude;
409
+ }
410
+ this._toggleAncestors(ci, add, exclude);
411
+ });
412
+ }
413
+ _onListItemEscape(event, index) {
414
+ const li = this._elementRef.nativeElement.querySelector(`#${this.pickerId}-option-${this.displayItems[index].id}`);
415
+ if (event.target === li) {
416
+ this.closeResults();
417
+ }
418
+ else {
419
+ event.stopPropagation();
420
+ li?.focus();
421
+ }
422
+ }
423
+ _getActiveListItems() {
424
+ return this.selectionsAreShowing ? this.selectionsListItems : this.optionsListItems;
425
+ }
426
+ _focusListItem(index) {
427
+ this.focusedIndex = index;
428
+ const items = this._getActiveListItems().toArray();
429
+ items?.[index].nativeElement.focus();
430
+ }
431
+ _onArrowLeft(event) {
432
+ if (!this.parentId) {
433
+ return;
434
+ }
435
+ event.preventDefault();
436
+ event.stopPropagation();
437
+ const parentItem = this.getParentItem(this.parentId);
438
+ this._setDisplayItemsFromParentId(parentItem.parentId);
439
+ this.asc.emit(parentItem);
440
+ this._cdRef.detectChanges();
441
+ const index = this.displayItems.findIndex(i => i.id === parentItem.id);
442
+ this._focusListItem(index >= 0 ? index : 0);
443
+ }
444
+ _focusableChildren(index) {
445
+ const li = this.optionsListItems.toArray()[index]?.nativeElement;
446
+ const children = Array.from(li?.querySelectorAll('input, button') ?? []);
447
+ return { li, children };
448
+ }
449
+ _resetSearchTerm() {
450
+ this.searchTerm.setValue('', { emitEvent: false });
451
+ }
452
+ _subscribeToSearchTermChanges() {
453
+ const sub = this.searchTerm.valueChanges.subscribe(val => {
454
+ this.selectionsAreShowing = false;
455
+ this.focusedIndex = -1;
456
+ this.canViewResults = true;
457
+ if (val.length) {
458
+ const displayItems = this.items.filter(item => {
459
+ return ((item.searchValues || []).some(value => {
460
+ return value.toLowerCase().includes(val.toLowerCase());
461
+ }) || item.displayName.toLowerCase().includes(val.toLowerCase()));
462
+ });
463
+ // remove duplicate items
464
+ this.displayItems = displayItems.reduce((items, item) => (items.find(x => x.id === item.id) ? items : [...items, item]), []);
465
+ }
466
+ else {
467
+ this._setDisplayItemsFromParentId(this.parentId);
468
+ }
469
+ });
470
+ this._subs.push(sub);
471
+ }
472
+ _announce(text) {
473
+ this._liveAnnouncer.announce(text, 'polite');
474
+ }
253
475
  ngOnDestroy() {
254
476
  this._subs.forEach(sub => sub.unsubscribe());
255
477
  }
256
478
  static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.8", ngImport: i0, type: NwPickerComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
257
- static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "21.2.8", type: NwPickerComponent, isStandalone: true, selector: "nw-angular-picker", inputs: { items: "items", inputClasses: "inputClasses", placeholderText: "placeholderText", inputPlaceholderText: "inputPlaceholderText", noSelectionsPlaceholderText: "noSelectionsPlaceholderText", initialParentId: "initialParentId", shouldShowSelections: "shouldShowSelections", canExclude: "canExclude", isHeightDynamic: "isHeightDynamic", isMultiSelect: "isMultiSelect", isMobileDisplay: "isMobileDisplay", isDisabled: "isDisabled", isChevronHidden: "isChevronHidden" }, outputs: { selections: "selections", toggleInclude: "toggleInclude", toggleExclude: "toggleExclude", edit: "edit", closed: "closed", focus: "focus", clearAll: "clearAll", clearSingle: "clearSingle", clearSearch: "clearSearch", desc: "desc", asc: "asc" }, viewQueries: [{ propertyName: "inputEl", first: true, predicate: ["inputEl"], descendants: true, static: true }], usesOnChanges: true, ngImport: i0, template: `
258
- <div class="nw-picker">
259
- <!-- START: NOT xs screen -->
260
- <div
261
- class="input-container hidden-xs"
262
- [class.disabled]="isDisabled">
263
- <input
264
- type="text"
265
- #inputEl
266
- class="form-control search-input {{ inputClasses }} text-ellipsis"
267
- [formControl]="searchTerm"
268
- (focus)="onFocus()"
269
- (blur)="closeResults()"
270
- (keyup.escape)="inputEl.blur()"
271
- [placeholder]="inputPlaceholderText"
272
- [attr.aria-label]="inputPlaceholderText" />
273
-
274
- <div
275
- class="input-placeholder text-ellipsis"
276
- [innerHTML]="getPlaceholderText()"></div>
277
-
278
- <i
279
- *ngIf="!isChevronHidden"
280
- (click)="showResults(); inputEl.focus()"
281
- class="caret dropdown-icon"></i>
282
- </div>
283
-
284
- <!-- END: NOT xs screen -->
285
-
286
- <!-- START: IS xs screen -->
287
- <div
288
- (click)="showResults()"
289
- class="form-control search-input hidden-sm hidden-md hidden-lg text-ellipsis"
290
- [innerHTML]="getPlaceholderText()"></div>
291
- <i
292
- (click)="showResults()"
293
- class="caret dropdown-icon hidden-sm hidden-md hidden-lg"></i>
294
- <!-- END: IS xs screen -->
295
-
296
- <button
297
- *ngIf="searchTerm.value"
298
- (mousedown)="preventBlur($event)"
299
- (click)="onReset($event); inputEl.focus()"
300
- class="close reset-icon"
301
- aria-label="Clear search">
302
- &times;
303
- </button>
304
-
305
- <div
306
- class="search-results"
307
- *ngIf="canViewResults"
308
- animate.enter="slide-up-in"
309
- animate.leave="slide-down-out"
310
- [class.no-animation]="!isMobileDisplay"
311
- (mousedown)="preventBlur($event)">
312
- <div class="results-header">
313
- <button
314
- class="close"
315
- (click)="closeResults()"
316
- style="color: #000"
317
- aria-label="Close results">
318
- &times;
319
- </button>
320
- </div>
321
-
322
- <!-- Navigate up the tree -->
323
- <div
324
- class="results-actions"
325
- *ngIf="parentId && displayItems.length && !searchTerm.value.length">
326
- <a
327
- tabindex="0"
328
- role="button"
329
- aria-label="Go Back"
330
- class="picker-action"
331
- (click)="ascend($event, getParentItem(parentId))"
332
- (keydown.enter)="ascend($event, getParentItem(parentId))">
333
- <i
334
- class="fas fa-long-arrow-alt-left"
335
- aria-hidden="true"></i>
336
- {{ getParentItem(parentId).displayName }}
337
- </a>
338
- </div>
339
-
340
- <div
341
- class="scroll-container"
342
- #searchResultsScrollEl
343
- [style.max-height]="getMaxHeight(searchResultsScrollEl)">
344
- <div
345
- class="results-actions"
346
- *ngIf="
347
- shouldShowSelections &&
348
- !selectionsAreShowing &&
349
- parentId == null &&
350
- !searchTerm.value.length
351
- ">
352
- <ng-container *ngIf="getSelections().length">
353
- <a
354
- tabindex="0"
355
- role="button"
356
- class="picker-action"
357
- (click)="editSelections($event)"
358
- (keydown.enter)="editSelections($event)"
359
- >Edit selections</a
360
- >
361
- <a
362
- tabindex="0"
363
- role="button"
364
- class="picker-action"
365
- (click)="clearSelections($event)"
366
- (keydown.enter)="clearSelections($event)"
367
- >Clear selections</a
368
- >
369
- </ng-container>
370
-
371
- <ng-container *ngIf="!getSelections().length">
372
- <em>No selections</em>
373
- </ng-container>
374
- </div>
375
-
376
- <!-- DISPLAY THE SELECTED ITEMS -->
377
- <ng-container *ngIf="selectionsAreShowing">
378
- <div class="results-actions">
379
- <a
380
- role="button"
381
- class="picker-action"
382
- (click)="selectionsAreShowing = false">
383
- <i
384
- class="fas fa-long-arrow-alt-left"
385
- aria-hidden="true"></i>
386
- Back
387
- </a>
388
- <a
389
- role="button"
390
- class="picker-action"
391
- *ngIf="getSelections().length"
392
- (click)="clearSelections($event)"
393
- >Clear all</a
394
- >
395
- </div>
396
-
397
- <div class="selected-items">
398
- <div
399
- class="search-result"
400
- [ngClass]="{ active: item.added, excluded: item.excluded }"
401
- *ngFor="let item of getSelections()">
402
- <span class="result-item">
403
- <span class="item-label">{{ item.displayName }}</span>
404
-
405
- <button
406
- class="close"
407
- style="color: #000000"
408
- (click)="clearSelection($event, item)"
409
- [attr.aria-label]="'Remove ' + item.displayName">
410
- &times;
411
- </button>
412
- </span>
413
- </div>
414
- </div>
415
- </ng-container>
416
-
417
- <ng-container *ngIf="!selectionsAreShowing">
418
- <div
419
- class="search-result"
420
- *ngFor="let item of displayItems"
421
- [class.active]="item.added"
422
- [attr.tabindex]="isMultiSelect ? -1 : 0"
423
- [class.excluded]="item.excluded"
424
- [class.has-children]="hasChildren(item.id)"
425
- role="option"
426
- [attr.aria-selected]="item.added">
427
- <span class="result-item">
428
- <div
429
- class="checkbox checkbox-placeholder"
430
- *ngIf="isMultiSelect">
431
- <input
432
- tabindex="0"
433
- id="include-{{ item.id }}"
434
- type="checkbox"
435
- (click)="toggleItemInclusion(item, $event)"
436
- [checked]="item.added"
437
- (keydown.enter)="toggleItemInclusion(item, $event)" />
438
- <label
439
- for="include-{{ item.id }}"
440
- [attr.aria-label]="'Select ' + item.displayName"></label>
441
- </div>
442
-
443
- <div
444
- class="checkbox checkbox-exclusion checkbox-placeholder"
445
- *ngIf="canExclude && isMultiSelect">
446
- <input
447
- tabindex="0"
448
- id="exclude-{{ item.id }}"
449
- type="checkbox"
450
- (click)="toggleItemExclusion(item, $event)"
451
- [checked]="item.excluded"
452
- (keydown.enter)="toggleItemExclusion(item, $event)" />
453
- <label
454
- for="exclude-{{ item.id }}"
455
- [attr.aria-label]="'Exclude ' + item.displayName"></label>
456
- </div>
457
-
458
- <span
459
- class="item-label"
460
- title="{{ item.displayName }}"
461
- (click)="toggleItemInclusion(item, $event)">
462
- {{ item.displayName }}
463
- <ng-container *ngIf="searchTerm.value.length && item.searchValues?.length">
464
- <span>
465
- -
466
- <em
467
- class="small"
468
- *ngFor="let val of item.searchValues; let isLast = last"
469
- >{{ val }}{{ isLast ? '' : ', ' }}</em
470
- >
471
- </span>
472
- </ng-container>
473
- </span>
474
-
475
- <button
476
- class="btn btn-ghost drilldown"
477
- *ngIf="hasChildren(item.id)"
478
- (click)="
479
- setDisplayItemsFromParentId(item.id, $event); desc.emit(getParentItem(parentId))
480
- "
481
- [attr.aria-label]="'Expand ' + item.displayName">
482
- <i
483
- class="fas fa-chevron-right"
484
- aria-hidden="true"></i>
485
- </button>
486
- </span>
487
- </div>
488
-
489
- <div
490
- class="results-actions"
491
- *ngIf="displayItems.length < 1">
492
- <em>No results</em>
493
- </div>
494
- </ng-container>
495
- </div>
496
-
497
- <ng-content select=".results-footer"></ng-content>
498
- </div>
499
- </div>
500
- `, isInline: true, dependencies: [{ kind: "ngmodule", type: ReactiveFormsModule }, { kind: "directive", type: i1.DefaultValueAccessor, selector: "input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]" }, { kind: "directive", type: i1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1.FormControlDirective, selector: "[formControl]", inputs: ["formControl", "disabled", "ngModel"], outputs: ["ngModelChange"], exportAs: ["ngForm"] }, { kind: "directive", type: NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "directive", type: NgFor, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { kind: "directive", type: NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
479
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.8", type: NwPickerComponent, isStandalone: true, selector: "nw-angular-picker", inputs: { items: "items", inputClasses: "inputClasses", placeholderText: "placeholderText", inputPlaceholderText: "inputPlaceholderText", noSelectionsPlaceholderText: "noSelectionsPlaceholderText", initialParentId: "initialParentId", shouldShowSelections: "shouldShowSelections", canExclude: "canExclude", isHeightDynamic: "isHeightDynamic", isMultiSelect: "isMultiSelect", isMobileDisplay: "isMobileDisplay", isDisabled: "isDisabled", isChevronHidden: "isChevronHidden" }, outputs: { selections: "selections", toggleInclude: "toggleInclude", toggleExclude: "toggleExclude", edit: "edit", closed: "closed", focus: "focus", blur: "blur", clearAll: "clearAll", clearSingle: "clearSingle", clearSearch: "clearSearch", desc: "desc", asc: "asc" }, viewQueries: [{ propertyName: "inputEl", first: true, predicate: ["inputEl"], descendants: true, static: true }, { propertyName: "selectionsListItems", predicate: ["selectionsListItems"], descendants: true }, { propertyName: "optionsListItems", predicate: ["optionsListItems"], descendants: true }], usesOnChanges: true, ngImport: i0, template: "<div\n class=\"nw-picker\"\n (focusout)=\"onContainerFocusOut()\">\n <!-- START: NOT xs screen -->\n <div\n class=\"input-container hidden-xs\"\n [class.disabled]=\"isDisabled\">\n <input\n type=\"text\"\n #inputEl\n class=\"form-control search-input text-ellipsis\"\n [class]=\"inputClasses\"\n [formControl]=\"searchTerm\"\n (focus)=\"onFocus()\"\n (click)=\"showResults()\"\n (keydown.escape)=\"closeResults()\"\n (keydown.arrowdown)=\"focusNextItem($event)\"\n (keydown.enter)=\"onInputEnter()\"\n [placeholder]=\"inputPlaceholderText\"\n role=\"combobox\"\n [attr.aria-expanded]=\"canViewResults\"\n aria-haspopup=\"listbox\"\n aria-autocomplete=\"list\"\n [attr.aria-controls]=\"pickerId + '-listbox'\"\n [attr.aria-activedescendant]=\"focusedItemId\"\n [attr.aria-label]=\"inputPlaceholderText\" />\n\n <div\n class=\"input-placeholder text-ellipsis\"\n [innerHTML]=\"getPlaceholderText()\"></div>\n\n @if (!isChevronHidden) {\n <i\n (click)=\"onChevronClick()\"\n class=\"caret dropdown-icon\"\n aria-hidden=\"true\"></i>\n }\n </div>\n <!-- END: NOT xs screen -->\n\n <!-- START: IS xs screen -->\n <div\n (click)=\"showResults()\"\n tabindex=\"0\"\n role=\"button\"\n [attr.aria-expanded]=\"canViewResults\"\n aria-haspopup=\"listbox\"\n class=\"form-control search-input hidden-sm hidden-md hidden-lg text-ellipsis\"\n [innerHTML]=\"getPlaceholderText()\"></div>\n <i\n (click)=\"showResults()\"\n class=\"caret dropdown-icon hidden-sm hidden-md hidden-lg\"\n aria-hidden=\"true\"></i>\n <!-- END: IS xs screen -->\n\n @if (searchTerm.value) {\n <button\n (mousedown)=\"$event.preventDefault()\"\n (click)=\"onReset()\"\n class=\"close reset-icon\"\n aria-label=\"Clear search\">\n <i\n class=\"fas fa-times\"\n aria-hidden=\"true\"></i>\n </button>\n }\n\n @if (canViewResults) {\n <div\n class=\"search-results\"\n animate.enter=\"slide-up-in\"\n animate.leave=\"slide-down-out\"\n [class.no-animation]=\"!isMobileDisplay\"\n (mousedown)=\"$event.preventDefault()\">\n <div class=\"results-header\">\n <button\n class=\"close\"\n (click)=\"closeResults()\"\n aria-label=\"Close results\">\n <i\n class=\"fas fa-times\"\n aria-hidden=\"true\"></i>\n </button>\n </div>\n <!-- Navigate up the tree -->\n @if (parentId && displayItems.length && !searchTerm.value.length) {\n <div class=\"results-actions\">\n <button\n type=\"button\"\n class=\"picker-action btn-link\"\n [attr.aria-label]=\"'Go back to ' + getParentItem(parentId).displayName\"\n (click)=\"ascend($event, getParentItem(parentId))\">\n <i\n class=\"fas fa-long-arrow-alt-left mr-4\"\n aria-hidden=\"true\"></i>\n <span>{{ getParentItem(parentId).displayName }}</span>\n </button>\n </div>\n }\n <div\n class=\"scroll-container\"\n #searchResultsScrollEl\n [style.max-height]=\"getMaxHeight(searchResultsScrollEl)\">\n @if (shouldShowSelections && !selectionsAreShowing && parentId == null && !searchTerm.value.length) {\n <div class=\"results-actions\">\n @if (getSelections().length) {\n <button\n type=\"button\"\n class=\"picker-action btn-link\"\n (click)=\"editSelections($event)\">\n Edit selections\n </button>\n <button\n type=\"button\"\n class=\"picker-action btn-link\"\n (click)=\"clearSelections($event)\">\n Clear selections\n </button>\n }\n @if (!getSelections().length) {\n <p class=\"no-selections-text\">No selections</p>\n }\n </div>\n }\n <!-- DISPLAY THE SELECTED ITEMS -->\n @if (selectionsAreShowing) {\n <div class=\"results-actions\">\n <button\n type=\"button\"\n class=\"picker-action btn-link\"\n (click)=\"onBackClick($event)\">\n <i\n class=\"fas fa-long-arrow-alt-left\"\n aria-hidden=\"true\"></i>\n Back\n </button>\n @if (getSelections().length) {\n <button\n type=\"button\"\n class=\"picker-action btn-link\"\n (click)=\"clearSelections($event)\">\n Clear all\n </button>\n }\n </div>\n <ul\n class=\"selected-items\"\n role=\"listbox\"\n aria-label=\"Current selections\"\n (focusout)=\"onListFocusOut($event)\">\n @for (item of getSelections(); track item; let i = $index) {\n <li\n #selectionsListItems\n class=\"search-result\"\n role=\"option\"\n [id]=\"'selection-' + item.id\"\n [attr.tabindex]=\"getTabIndex(i)\"\n [attr.aria-selected]=\"item.added || item.excluded\"\n [attr.aria-label]=\"getAriaLabel(item)\"\n (focusin)=\"focusedIndex = i\"\n (keydown)=\"onSelectionItemKeydown($event, item)\"\n [ngClass]=\"{ active: item.added, excluded: item.excluded }\">\n <span class=\"result-item\">\n <span class=\"item-label\">{{ item.displayName }}</span>\n <button\n class=\"close\"\n style=\"color: black\"\n (click)=\"clearSelection($event, item)\"\n [attr.aria-label]=\"'Remove ' + item.displayName\">\n <i\n class=\"fas fa-times\"\n aria-hidden=\"true\"></i>\n </button>\n </span>\n </li>\n }\n </ul>\n }\n @if (!selectionsAreShowing) {\n <ul\n role=\"listbox\"\n class=\"picker-options-list\"\n [attr.aria-label]=\"searchTerm.value ? 'Search results' : 'Options'\"\n (focusout)=\"onListFocusOut($event)\">\n @for (item of displayItems; track item; let i = $index) {\n <li\n #optionsListItems\n role=\"option\"\n [id]=\"pickerId + '-option-' + item.id\"\n class=\"search-result\"\n [class.active]=\"item.added\"\n [class.excluded]=\"item.excluded\"\n [class.has-children]=\"hasChildren(item.id)\"\n [class.keyboard-focused]=\"focusedIndex === i\"\n [attr.tabindex]=\"getTabIndex(i)\"\n [attr.aria-selected]=\"item.added\"\n [attr.aria-label]=\"getAriaLabel(item)\"\n (focusin)=\"focusedIndex = i\"\n (keydown)=\"onOptionItemKeydown($event, item, i)\">\n <span class=\"result-item\">\n @if (isMultiSelect) {\n <div class=\"checkbox checkbox-placeholder\">\n <input\n [id]=\"'include-' + item.id\"\n type=\"checkbox\"\n [attr.tabindex]=\"getChildTabIndex(i)\"\n [attr.aria-label]=\"'Include ' + item.displayName\"\n (click)=\"toggleItemInclusion(item, $event)\"\n [checked]=\"item.added\"\n (keydown.enter)=\"toggleItemInclusion(item, $event)\"\n (keydown.tab)=\"onListItemTab($event, i)\"\n (keydown.shift.tab)=\"onListItemShiftTab($event, i)\" />\n <label\n [for]=\"'include-' + item.id\"\n aria-hidden=\"true\"></label>\n </div>\n }\n @if (canExclude && isMultiSelect) {\n <div class=\"checkbox checkbox-exclusion checkbox-placeholder\">\n <input\n [id]=\"'exclude-' + item.id\"\n type=\"checkbox\"\n [attr.tabindex]=\"getChildTabIndex(i)\"\n [attr.aria-label]=\"'Exclude ' + item.displayName\"\n (click)=\"toggleItemExclusion(item, $event)\"\n [checked]=\"item.excluded\"\n (keydown.enter)=\"toggleItemExclusion(item, $event)\"\n (keydown.tab)=\"onListItemTab($event, i)\"\n (keydown.shift.tab)=\"onListItemShiftTab($event, i)\" />\n <label\n [for]=\"'exclude-' + item.id\"\n aria-hidden=\"true\"></label>\n </div>\n }\n <span\n class=\"item-label\"\n title=\"{{ item.displayName }}\"\n (click)=\"toggleItemInclusion(item, $event)\">\n {{ item.displayName }}\n @if (searchTerm.value.length && item.searchValues?.length) {\n <span>\n -\n @for (val of item.searchValues; track val; let isLast = $last) {\n <em class=\"small\">{{ val }}{{ isLast ? '' : ', ' }}</em>\n }\n </span>\n }\n </span>\n @if (hasChildren(item.id)) {\n <button\n class=\"btn btn-ghost drilldown\"\n [attr.tabindex]=\"getChildTabIndex(i)\"\n (click)=\"onDrilldown(item)\"\n (keydown.enter)=\"$event.stopPropagation()\"\n (keydown.tab)=\"onListItemTab($event, i)\"\n (keydown.shift.tab)=\"onListItemShiftTab($event, i)\"\n [attr.aria-label]=\"'Expand ' + item.displayName\">\n <i\n class=\"fas fa-chevron-right\"\n aria-hidden=\"true\"></i>\n </button>\n }\n </span>\n </li>\n }\n </ul>\n @if (displayItems.length < 1) {\n <div class=\"results-actions\">\n <p\n role=\"status\"\n class=\"no-results-text\">\n No results\n </p>\n </div>\n }\n }\n </div>\n <ng-content select=\".results-footer\"></ng-content>\n </div>\n }\n</div>\n", dependencies: [{ kind: "ngmodule", type: ReactiveFormsModule }, { kind: "directive", type: i1.DefaultValueAccessor, selector: "input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]" }, { kind: "directive", type: i1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1.FormControlDirective, selector: "[formControl]", inputs: ["formControl", "disabled", "ngModel"], outputs: ["ngModelChange"], exportAs: ["ngForm"] }, { kind: "directive", type: NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
501
480
  }
502
481
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.8", ngImport: i0, type: NwPickerComponent, decorators: [{
503
482
  type: Component,
504
- args: [{
505
- selector: 'nw-angular-picker',
506
- template: `
507
- <div class="nw-picker">
508
- <!-- START: NOT xs screen -->
509
- <div
510
- class="input-container hidden-xs"
511
- [class.disabled]="isDisabled">
512
- <input
513
- type="text"
514
- #inputEl
515
- class="form-control search-input {{ inputClasses }} text-ellipsis"
516
- [formControl]="searchTerm"
517
- (focus)="onFocus()"
518
- (blur)="closeResults()"
519
- (keyup.escape)="inputEl.blur()"
520
- [placeholder]="inputPlaceholderText"
521
- [attr.aria-label]="inputPlaceholderText" />
522
-
523
- <div
524
- class="input-placeholder text-ellipsis"
525
- [innerHTML]="getPlaceholderText()"></div>
526
-
527
- <i
528
- *ngIf="!isChevronHidden"
529
- (click)="showResults(); inputEl.focus()"
530
- class="caret dropdown-icon"></i>
531
- </div>
532
-
533
- <!-- END: NOT xs screen -->
534
-
535
- <!-- START: IS xs screen -->
536
- <div
537
- (click)="showResults()"
538
- class="form-control search-input hidden-sm hidden-md hidden-lg text-ellipsis"
539
- [innerHTML]="getPlaceholderText()"></div>
540
- <i
541
- (click)="showResults()"
542
- class="caret dropdown-icon hidden-sm hidden-md hidden-lg"></i>
543
- <!-- END: IS xs screen -->
544
-
545
- <button
546
- *ngIf="searchTerm.value"
547
- (mousedown)="preventBlur($event)"
548
- (click)="onReset($event); inputEl.focus()"
549
- class="close reset-icon"
550
- aria-label="Clear search">
551
- &times;
552
- </button>
553
-
554
- <div
555
- class="search-results"
556
- *ngIf="canViewResults"
557
- animate.enter="slide-up-in"
558
- animate.leave="slide-down-out"
559
- [class.no-animation]="!isMobileDisplay"
560
- (mousedown)="preventBlur($event)">
561
- <div class="results-header">
562
- <button
563
- class="close"
564
- (click)="closeResults()"
565
- style="color: #000"
566
- aria-label="Close results">
567
- &times;
568
- </button>
569
- </div>
570
-
571
- <!-- Navigate up the tree -->
572
- <div
573
- class="results-actions"
574
- *ngIf="parentId && displayItems.length && !searchTerm.value.length">
575
- <a
576
- tabindex="0"
577
- role="button"
578
- aria-label="Go Back"
579
- class="picker-action"
580
- (click)="ascend($event, getParentItem(parentId))"
581
- (keydown.enter)="ascend($event, getParentItem(parentId))">
582
- <i
583
- class="fas fa-long-arrow-alt-left"
584
- aria-hidden="true"></i>
585
- {{ getParentItem(parentId).displayName }}
586
- </a>
587
- </div>
588
-
589
- <div
590
- class="scroll-container"
591
- #searchResultsScrollEl
592
- [style.max-height]="getMaxHeight(searchResultsScrollEl)">
593
- <div
594
- class="results-actions"
595
- *ngIf="
596
- shouldShowSelections &&
597
- !selectionsAreShowing &&
598
- parentId == null &&
599
- !searchTerm.value.length
600
- ">
601
- <ng-container *ngIf="getSelections().length">
602
- <a
603
- tabindex="0"
604
- role="button"
605
- class="picker-action"
606
- (click)="editSelections($event)"
607
- (keydown.enter)="editSelections($event)"
608
- >Edit selections</a
609
- >
610
- <a
611
- tabindex="0"
612
- role="button"
613
- class="picker-action"
614
- (click)="clearSelections($event)"
615
- (keydown.enter)="clearSelections($event)"
616
- >Clear selections</a
617
- >
618
- </ng-container>
619
-
620
- <ng-container *ngIf="!getSelections().length">
621
- <em>No selections</em>
622
- </ng-container>
623
- </div>
624
-
625
- <!-- DISPLAY THE SELECTED ITEMS -->
626
- <ng-container *ngIf="selectionsAreShowing">
627
- <div class="results-actions">
628
- <a
629
- role="button"
630
- class="picker-action"
631
- (click)="selectionsAreShowing = false">
632
- <i
633
- class="fas fa-long-arrow-alt-left"
634
- aria-hidden="true"></i>
635
- Back
636
- </a>
637
- <a
638
- role="button"
639
- class="picker-action"
640
- *ngIf="getSelections().length"
641
- (click)="clearSelections($event)"
642
- >Clear all</a
643
- >
644
- </div>
645
-
646
- <div class="selected-items">
647
- <div
648
- class="search-result"
649
- [ngClass]="{ active: item.added, excluded: item.excluded }"
650
- *ngFor="let item of getSelections()">
651
- <span class="result-item">
652
- <span class="item-label">{{ item.displayName }}</span>
653
-
654
- <button
655
- class="close"
656
- style="color: #000000"
657
- (click)="clearSelection($event, item)"
658
- [attr.aria-label]="'Remove ' + item.displayName">
659
- &times;
660
- </button>
661
- </span>
662
- </div>
663
- </div>
664
- </ng-container>
665
-
666
- <ng-container *ngIf="!selectionsAreShowing">
667
- <div
668
- class="search-result"
669
- *ngFor="let item of displayItems"
670
- [class.active]="item.added"
671
- [attr.tabindex]="isMultiSelect ? -1 : 0"
672
- [class.excluded]="item.excluded"
673
- [class.has-children]="hasChildren(item.id)"
674
- role="option"
675
- [attr.aria-selected]="item.added">
676
- <span class="result-item">
677
- <div
678
- class="checkbox checkbox-placeholder"
679
- *ngIf="isMultiSelect">
680
- <input
681
- tabindex="0"
682
- id="include-{{ item.id }}"
683
- type="checkbox"
684
- (click)="toggleItemInclusion(item, $event)"
685
- [checked]="item.added"
686
- (keydown.enter)="toggleItemInclusion(item, $event)" />
687
- <label
688
- for="include-{{ item.id }}"
689
- [attr.aria-label]="'Select ' + item.displayName"></label>
690
- </div>
691
-
692
- <div
693
- class="checkbox checkbox-exclusion checkbox-placeholder"
694
- *ngIf="canExclude && isMultiSelect">
695
- <input
696
- tabindex="0"
697
- id="exclude-{{ item.id }}"
698
- type="checkbox"
699
- (click)="toggleItemExclusion(item, $event)"
700
- [checked]="item.excluded"
701
- (keydown.enter)="toggleItemExclusion(item, $event)" />
702
- <label
703
- for="exclude-{{ item.id }}"
704
- [attr.aria-label]="'Exclude ' + item.displayName"></label>
705
- </div>
706
-
707
- <span
708
- class="item-label"
709
- title="{{ item.displayName }}"
710
- (click)="toggleItemInclusion(item, $event)">
711
- {{ item.displayName }}
712
- <ng-container *ngIf="searchTerm.value.length && item.searchValues?.length">
713
- <span>
714
- -
715
- <em
716
- class="small"
717
- *ngFor="let val of item.searchValues; let isLast = last"
718
- >{{ val }}{{ isLast ? '' : ', ' }}</em
719
- >
720
- </span>
721
- </ng-container>
722
- </span>
723
-
724
- <button
725
- class="btn btn-ghost drilldown"
726
- *ngIf="hasChildren(item.id)"
727
- (click)="
728
- setDisplayItemsFromParentId(item.id, $event); desc.emit(getParentItem(parentId))
729
- "
730
- [attr.aria-label]="'Expand ' + item.displayName">
731
- <i
732
- class="fas fa-chevron-right"
733
- aria-hidden="true"></i>
734
- </button>
735
- </span>
736
- </div>
737
-
738
- <div
739
- class="results-actions"
740
- *ngIf="displayItems.length < 1">
741
- <em>No results</em>
742
- </div>
743
- </ng-container>
744
- </div>
745
-
746
- <ng-content select=".results-footer"></ng-content>
747
- </div>
748
- </div>
749
- `,
750
- changeDetection: ChangeDetectionStrategy.OnPush,
751
- imports: [ReactiveFormsModule, NgIf, NgFor, NgClass]
752
- }]
483
+ args: [{ selector: 'nw-angular-picker', changeDetection: ChangeDetectionStrategy.OnPush, imports: [ReactiveFormsModule, NgClass], template: "<div\n class=\"nw-picker\"\n (focusout)=\"onContainerFocusOut()\">\n <!-- START: NOT xs screen -->\n <div\n class=\"input-container hidden-xs\"\n [class.disabled]=\"isDisabled\">\n <input\n type=\"text\"\n #inputEl\n class=\"form-control search-input text-ellipsis\"\n [class]=\"inputClasses\"\n [formControl]=\"searchTerm\"\n (focus)=\"onFocus()\"\n (click)=\"showResults()\"\n (keydown.escape)=\"closeResults()\"\n (keydown.arrowdown)=\"focusNextItem($event)\"\n (keydown.enter)=\"onInputEnter()\"\n [placeholder]=\"inputPlaceholderText\"\n role=\"combobox\"\n [attr.aria-expanded]=\"canViewResults\"\n aria-haspopup=\"listbox\"\n aria-autocomplete=\"list\"\n [attr.aria-controls]=\"pickerId + '-listbox'\"\n [attr.aria-activedescendant]=\"focusedItemId\"\n [attr.aria-label]=\"inputPlaceholderText\" />\n\n <div\n class=\"input-placeholder text-ellipsis\"\n [innerHTML]=\"getPlaceholderText()\"></div>\n\n @if (!isChevronHidden) {\n <i\n (click)=\"onChevronClick()\"\n class=\"caret dropdown-icon\"\n aria-hidden=\"true\"></i>\n }\n </div>\n <!-- END: NOT xs screen -->\n\n <!-- START: IS xs screen -->\n <div\n (click)=\"showResults()\"\n tabindex=\"0\"\n role=\"button\"\n [attr.aria-expanded]=\"canViewResults\"\n aria-haspopup=\"listbox\"\n class=\"form-control search-input hidden-sm hidden-md hidden-lg text-ellipsis\"\n [innerHTML]=\"getPlaceholderText()\"></div>\n <i\n (click)=\"showResults()\"\n class=\"caret dropdown-icon hidden-sm hidden-md hidden-lg\"\n aria-hidden=\"true\"></i>\n <!-- END: IS xs screen -->\n\n @if (searchTerm.value) {\n <button\n (mousedown)=\"$event.preventDefault()\"\n (click)=\"onReset()\"\n class=\"close reset-icon\"\n aria-label=\"Clear search\">\n <i\n class=\"fas fa-times\"\n aria-hidden=\"true\"></i>\n </button>\n }\n\n @if (canViewResults) {\n <div\n class=\"search-results\"\n animate.enter=\"slide-up-in\"\n animate.leave=\"slide-down-out\"\n [class.no-animation]=\"!isMobileDisplay\"\n (mousedown)=\"$event.preventDefault()\">\n <div class=\"results-header\">\n <button\n class=\"close\"\n (click)=\"closeResults()\"\n aria-label=\"Close results\">\n <i\n class=\"fas fa-times\"\n aria-hidden=\"true\"></i>\n </button>\n </div>\n <!-- Navigate up the tree -->\n @if (parentId && displayItems.length && !searchTerm.value.length) {\n <div class=\"results-actions\">\n <button\n type=\"button\"\n class=\"picker-action btn-link\"\n [attr.aria-label]=\"'Go back to ' + getParentItem(parentId).displayName\"\n (click)=\"ascend($event, getParentItem(parentId))\">\n <i\n class=\"fas fa-long-arrow-alt-left mr-4\"\n aria-hidden=\"true\"></i>\n <span>{{ getParentItem(parentId).displayName }}</span>\n </button>\n </div>\n }\n <div\n class=\"scroll-container\"\n #searchResultsScrollEl\n [style.max-height]=\"getMaxHeight(searchResultsScrollEl)\">\n @if (shouldShowSelections && !selectionsAreShowing && parentId == null && !searchTerm.value.length) {\n <div class=\"results-actions\">\n @if (getSelections().length) {\n <button\n type=\"button\"\n class=\"picker-action btn-link\"\n (click)=\"editSelections($event)\">\n Edit selections\n </button>\n <button\n type=\"button\"\n class=\"picker-action btn-link\"\n (click)=\"clearSelections($event)\">\n Clear selections\n </button>\n }\n @if (!getSelections().length) {\n <p class=\"no-selections-text\">No selections</p>\n }\n </div>\n }\n <!-- DISPLAY THE SELECTED ITEMS -->\n @if (selectionsAreShowing) {\n <div class=\"results-actions\">\n <button\n type=\"button\"\n class=\"picker-action btn-link\"\n (click)=\"onBackClick($event)\">\n <i\n class=\"fas fa-long-arrow-alt-left\"\n aria-hidden=\"true\"></i>\n Back\n </button>\n @if (getSelections().length) {\n <button\n type=\"button\"\n class=\"picker-action btn-link\"\n (click)=\"clearSelections($event)\">\n Clear all\n </button>\n }\n </div>\n <ul\n class=\"selected-items\"\n role=\"listbox\"\n aria-label=\"Current selections\"\n (focusout)=\"onListFocusOut($event)\">\n @for (item of getSelections(); track item; let i = $index) {\n <li\n #selectionsListItems\n class=\"search-result\"\n role=\"option\"\n [id]=\"'selection-' + item.id\"\n [attr.tabindex]=\"getTabIndex(i)\"\n [attr.aria-selected]=\"item.added || item.excluded\"\n [attr.aria-label]=\"getAriaLabel(item)\"\n (focusin)=\"focusedIndex = i\"\n (keydown)=\"onSelectionItemKeydown($event, item)\"\n [ngClass]=\"{ active: item.added, excluded: item.excluded }\">\n <span class=\"result-item\">\n <span class=\"item-label\">{{ item.displayName }}</span>\n <button\n class=\"close\"\n style=\"color: black\"\n (click)=\"clearSelection($event, item)\"\n [attr.aria-label]=\"'Remove ' + item.displayName\">\n <i\n class=\"fas fa-times\"\n aria-hidden=\"true\"></i>\n </button>\n </span>\n </li>\n }\n </ul>\n }\n @if (!selectionsAreShowing) {\n <ul\n role=\"listbox\"\n class=\"picker-options-list\"\n [attr.aria-label]=\"searchTerm.value ? 'Search results' : 'Options'\"\n (focusout)=\"onListFocusOut($event)\">\n @for (item of displayItems; track item; let i = $index) {\n <li\n #optionsListItems\n role=\"option\"\n [id]=\"pickerId + '-option-' + item.id\"\n class=\"search-result\"\n [class.active]=\"item.added\"\n [class.excluded]=\"item.excluded\"\n [class.has-children]=\"hasChildren(item.id)\"\n [class.keyboard-focused]=\"focusedIndex === i\"\n [attr.tabindex]=\"getTabIndex(i)\"\n [attr.aria-selected]=\"item.added\"\n [attr.aria-label]=\"getAriaLabel(item)\"\n (focusin)=\"focusedIndex = i\"\n (keydown)=\"onOptionItemKeydown($event, item, i)\">\n <span class=\"result-item\">\n @if (isMultiSelect) {\n <div class=\"checkbox checkbox-placeholder\">\n <input\n [id]=\"'include-' + item.id\"\n type=\"checkbox\"\n [attr.tabindex]=\"getChildTabIndex(i)\"\n [attr.aria-label]=\"'Include ' + item.displayName\"\n (click)=\"toggleItemInclusion(item, $event)\"\n [checked]=\"item.added\"\n (keydown.enter)=\"toggleItemInclusion(item, $event)\"\n (keydown.tab)=\"onListItemTab($event, i)\"\n (keydown.shift.tab)=\"onListItemShiftTab($event, i)\" />\n <label\n [for]=\"'include-' + item.id\"\n aria-hidden=\"true\"></label>\n </div>\n }\n @if (canExclude && isMultiSelect) {\n <div class=\"checkbox checkbox-exclusion checkbox-placeholder\">\n <input\n [id]=\"'exclude-' + item.id\"\n type=\"checkbox\"\n [attr.tabindex]=\"getChildTabIndex(i)\"\n [attr.aria-label]=\"'Exclude ' + item.displayName\"\n (click)=\"toggleItemExclusion(item, $event)\"\n [checked]=\"item.excluded\"\n (keydown.enter)=\"toggleItemExclusion(item, $event)\"\n (keydown.tab)=\"onListItemTab($event, i)\"\n (keydown.shift.tab)=\"onListItemShiftTab($event, i)\" />\n <label\n [for]=\"'exclude-' + item.id\"\n aria-hidden=\"true\"></label>\n </div>\n }\n <span\n class=\"item-label\"\n title=\"{{ item.displayName }}\"\n (click)=\"toggleItemInclusion(item, $event)\">\n {{ item.displayName }}\n @if (searchTerm.value.length && item.searchValues?.length) {\n <span>\n -\n @for (val of item.searchValues; track val; let isLast = $last) {\n <em class=\"small\">{{ val }}{{ isLast ? '' : ', ' }}</em>\n }\n </span>\n }\n </span>\n @if (hasChildren(item.id)) {\n <button\n class=\"btn btn-ghost drilldown\"\n [attr.tabindex]=\"getChildTabIndex(i)\"\n (click)=\"onDrilldown(item)\"\n (keydown.enter)=\"$event.stopPropagation()\"\n (keydown.tab)=\"onListItemTab($event, i)\"\n (keydown.shift.tab)=\"onListItemShiftTab($event, i)\"\n [attr.aria-label]=\"'Expand ' + item.displayName\">\n <i\n class=\"fas fa-chevron-right\"\n aria-hidden=\"true\"></i>\n </button>\n }\n </span>\n </li>\n }\n </ul>\n @if (displayItems.length < 1) {\n <div class=\"results-actions\">\n <p\n role=\"status\"\n class=\"no-results-text\">\n No results\n </p>\n </div>\n }\n }\n </div>\n <ng-content select=\".results-footer\"></ng-content>\n </div>\n }\n</div>\n" }]
753
484
  }], propDecorators: { items: [{
754
485
  type: Input
755
486
  }], inputClasses: [{
@@ -788,6 +519,8 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.8", ngImpor
788
519
  type: Output
789
520
  }], focus: [{
790
521
  type: Output
522
+ }], blur: [{
523
+ type: Output
791
524
  }], clearAll: [{
792
525
  type: Output
793
526
  }], clearSingle: [{
@@ -801,6 +534,12 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.8", ngImpor
801
534
  }], inputEl: [{
802
535
  type: ViewChild,
803
536
  args: ['inputEl', { static: true }]
537
+ }], selectionsListItems: [{
538
+ type: ViewChildren,
539
+ args: ['selectionsListItems']
540
+ }], optionsListItems: [{
541
+ type: ViewChildren,
542
+ args: ['optionsListItems']
804
543
  }] } });
805
544
 
806
545
  /**