Skip to content

Commit 05eb212

Browse files
author
Jens Vannerum
committed
110088: changes to keyboard navigation / selection
1 parent 3a265bb commit 05eb212

2 files changed

Lines changed: 44 additions & 6 deletions

File tree

src/app/shared/form/builder/ds-dynamic-form-ui/models/scrollable-dropdown/dynamic-scrollable-dropdown.component.html

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@
2626
(keydown)="selectOnKeyDown($event, sdRef)">
2727
</div>
2828

29-
<div ngbDropdownMenu
29+
<div #dropdownMenu ngbDropdownMenu
3030
class="dropdown-menu scrollable-dropdown-menu w-100"
3131
aria-haspopup="true"
3232
aria-expanded="false"
@@ -42,7 +42,8 @@
4242
[scrollWindow]="false">
4343

4444
<button class="dropdown-item disabled" *ngIf="optionsList && optionsList.length == 0">{{'form.no-results' | translate}}</button>
45-
<button class="dropdown-item collection-item text-truncate" *ngFor="let listEntry of optionsList"
45+
<button class="dropdown-item collection-item text-truncate" *ngFor="let listEntry of optionsList; let i = index"
46+
[class.active]="i === selectedIndex"
4647
(keydown.enter)="onSelect(listEntry); sdRef.close()" (mousedown)="onSelect(listEntry); sdRef.close()"
4748
title="{{ listEntry.display }}" role="option"
4849
[attr.id]="listEntry.display == (currentValue|async) ? ('combobox_' + id + '_selected') : null">

src/app/shared/form/builder/ds-dynamic-form-ui/models/scrollable-dropdown/dynamic-scrollable-dropdown.component.ts

Lines changed: 41 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,13 @@
1-
import { ChangeDetectorRef, Component, EventEmitter, Input, OnInit, Output } from '@angular/core';
1+
import {
2+
ChangeDetectorRef,
3+
Component,
4+
EventEmitter,
5+
Input,
6+
OnInit,
7+
Output,
8+
ViewChild,
9+
ElementRef
10+
} from '@angular/core';
211
import { UntypedFormGroup } from '@angular/forms';
312

413
import { Observable, of as observableOf } from 'rxjs';
@@ -28,6 +37,8 @@ import { FormFieldMetadataValueObject } from '../../../models/form-field-metadat
2837
templateUrl: './dynamic-scrollable-dropdown.component.html'
2938
})
3039
export class DsDynamicScrollableDropdownComponent extends DsDynamicVocabularyComponent implements OnInit {
40+
@ViewChild('dropdownMenu', { read: ElementRef }) dropdownMenu: ElementRef;
41+
3142
@Input() bindId = true;
3243
@Input() group: UntypedFormGroup;
3344
@Input() model: DynamicScrollableDropdownModel;
@@ -41,6 +52,7 @@ export class DsDynamicScrollableDropdownComponent extends DsDynamicVocabularyCom
4152
public pageInfo: PageInfo;
4253
public optionsList: any;
4354
public inputText: string = null;
55+
public selectedIndex = 0;
4456
public acceptableKeys = ['Space', 'NumpadMultiply', 'NumpadAdd', 'NumpadSubtract', 'NumpadDecimal', 'Semicolon', 'Equal', 'Comma', 'Minus', 'Period', 'Quote', 'Backquote'];
4557

4658
constructor(protected vocabularyService: VocabularyService,
@@ -73,6 +85,7 @@ export class DsDynamicScrollableDropdownComponent extends DsDynamicVocabularyCom
7385
list.pageInfo.totalElements,
7486
list.pageInfo.totalPages
7587
);
88+
this.selectedIndex = 0;
7689
this.cdr.detectChanges();
7790
});
7891
}
@@ -96,6 +109,23 @@ export class DsDynamicScrollableDropdownComponent extends DsDynamicVocabularyCom
96109
}
97110
}
98111

112+
navigateDropdown(event: KeyboardEvent) {
113+
if (event.key === 'ArrowDown') {
114+
this.selectedIndex = Math.min(this.selectedIndex + 1, this.optionsList.length - 1);
115+
} else if (event.key === 'ArrowUp') {
116+
this.selectedIndex = Math.max(this.selectedIndex - 1, 0);
117+
}
118+
this.scrollToSelected();
119+
}
120+
121+
scrollToSelected() {
122+
const dropdownItems = this.dropdownMenu.nativeElement.querySelectorAll('.dropdown-item');
123+
const selectedItem = dropdownItems[this.selectedIndex];
124+
if (selectedItem) {
125+
selectedItem.scrollIntoView({ block: 'nearest' });
126+
}
127+
}
128+
99129
/**
100130
* KeyDown handler to allow toggling the dropdown via keyboard
101131
* @param event KeyboardEvent
@@ -104,12 +134,19 @@ export class DsDynamicScrollableDropdownComponent extends DsDynamicVocabularyCom
104134
selectOnKeyDown(event: KeyboardEvent, sdRef: NgbDropdown) {
105135
const keyName = event.key;
106136

107-
if (keyName === ' ' || keyName === 'Enter') {
137+
if (keyName === 'Enter') {
108138
event.preventDefault();
109139
event.stopPropagation();
110-
sdRef.toggle();
140+
if (sdRef.isOpen()) {
141+
this.onSelect(this.optionsList[this.selectedIndex]);
142+
sdRef.close();
143+
} else {
144+
sdRef.open();
145+
}
111146
} else if (keyName === 'ArrowDown' || keyName === 'ArrowUp') {
112-
this.openDropdown(sdRef);
147+
event.preventDefault();
148+
event.stopPropagation();
149+
this.navigateDropdown(event);
113150
} else if (keyName === 'Backspace') {
114151
this.removeKeyFromInput();
115152
} else if (this.isAcceptableKey(keyName)) {

0 commit comments

Comments
 (0)