Allow arrow navigation within global project select and project include dropdown

feature/43118-access-project-dropdown-entries-via-arrow-keys
Henriette Darge 2 years ago
parent 0e3889a4e6
commit 0fb154a3d1
  1. 2
      frontend/src/app/shared/components/autocompleter/project-menu-autocomplete/project-menu-autocomplete.component.ts
  2. 5
      frontend/src/app/shared/components/project-include/project-include.component.ts
  3. 2
      frontend/src/app/shared/components/project-list/project-list.component.html
  4. 6
      frontend/src/app/shared/components/project-list/project-list.component.ts
  5. 30
      frontend/src/app/shared/components/searchable-project-list/searchable-project-list.service.ts

@ -137,12 +137,14 @@ export class ProjectMenuAutocompleteComponent {
this.dropModalOpen = !this.dropModalOpen;
if (this.dropModalOpen) {
this.searchableProjectListService.loadAllProjects();
this.searchableProjectListService.registerArrowNavigationOnItems();
}
}
close():void {
this.searchableProjectListService.searchText = '';
this.dropModalOpen = false;
this.searchableProjectListService.destroyArrowNavigation();
}
currentProjectName():string {

@ -5,7 +5,6 @@ import {
HostBinding,
OnInit,
} from '@angular/core';
import { HttpClient } from '@angular/common/http';
import {
BehaviorSubject,
combineLatest,
@ -19,8 +18,6 @@ import {
shareReplay,
take,
} from 'rxjs/operators';
import { ApiV3Service } from 'core-app/core/apiv3/api-v3.service';
import { HalResource } from 'core-app/features/hal/resources/hal-resource';
import { WorkPackageViewFiltersService } from 'core-app/features/work-packages/routing/wp-view-base/view-services/wp-view-filters.service';
import { WorkPackageViewIncludeSubprojectsService } from 'core-app/features/work-packages/routing/wp-view-base/view-services/wp-view-include-subprojects.service';
@ -234,6 +231,7 @@ export class OpProjectIncludeComponent extends UntilDestroyedMixin implements On
if (this.opened) {
this.searchableProjectListService.loadAllProjects();
this.searchableProjectListService.registerArrowNavigationOnItems();
this.projectsInFilter$
.pipe(
take(1),
@ -267,5 +265,6 @@ export class OpProjectIncludeComponent extends UntilDestroyedMixin implements On
public close():void {
this.opened = false;
this.searchableProjectListService.destroyArrowNavigation();
}
}

@ -7,6 +7,7 @@
*ngIf="multiSelect"
class="spot-list--item-action op-project-list--item-action"
[ngClass]="{ 'spot-list--item-action_disabled': isDisabled(project) }"
[attr.data-list-action-selector]="projectListActionSelector"
>
<spot-tooltip
class="op-project-list--tooltip"
@ -49,6 +50,7 @@
[ngClass]="{ 'spot-list--item-action_disabled': project.disabled }"
[title]="project.name"
[href]="extendedProjectUrl(project.id)"
[attr.data-list-action-selector]="projectListActionSelector"
>
<span
class="op-project-list--item-title spot-list--item-title"

@ -12,6 +12,8 @@ import SpotDropAlignmentOption from 'core-app/spot/drop-alignment-options';
import { IProjectData } from './project-data';
import { PathHelperService } from 'core-app/core/path-helper/path-helper.service';
export const projectListActionSelector = 'op-project-list--item-action';
@Component({
selector: '[op-project-list]',
changeDetection: ChangeDetectionStrategy.OnPush,
@ -25,7 +27,7 @@ export class OpProjectListComponent {
@Output() update = new EventEmitter<string[]>();
@Input() root:boolean = false;
@Input() root = false;
@Input() projects:IProjectData[] = [];
@ -49,6 +51,8 @@ export class OpProjectListComponent {
current_project: this.I18n.t('js.include_projects.tooltip.current_project'),
};
projectListActionSelector = projectListActionSelector;
constructor(
readonly I18n:I18nService,
readonly currentProjectService:CurrentProjectService,

@ -11,6 +11,8 @@ import { IHALCollection } from 'core-app/core/apiv3/types/hal-collection.type';
import { finalize } from 'rxjs/operators';
import { ApiV3Service } from 'core-app/core/apiv3/api-v3.service';
import { HttpClient } from '@angular/common/http';
import { KeyCodes } from 'core-app/shared/helpers/keyCodes.enum';
import { projectListActionSelector } from 'core-app/shared/components/project-list/project-list.component';
@Injectable()
export class SearchableProjectListService {
@ -25,7 +27,7 @@ export class SearchableProjectListService {
this.searchText$.next(val);
}
searchText$ = new BehaviorSubject('');
searchText$ = new BehaviorSubject<string>('');
allProjects$ = new BehaviorSubject<IProject[]>([]);
@ -73,4 +75,30 @@ export class SearchableProjectListService {
],
};
}
registerArrowNavigationOnItems():void {
let currentIndex = -1;
document.addEventListener('keydown', (event) => {
// Todo: Instead of collecting the items every time, do it once at the beginning.
// Keep in mind that the list needs to be loaded first, and the list might change when searched
const items = document.querySelectorAll(`[data-list-action-selector='${projectListActionSelector}']`);
if (event.keyCode === KeyCodes.UP_ARROW) {
// Decrease the counter
currentIndex = currentIndex > 0 ? currentIndex -= 1 : 0;
} else if (event.keyCode === KeyCodes.DOWN_ARROW) {
// Increase counter
currentIndex = currentIndex < items.length - 1 ? currentIndex += 1 : items.length - 1;
} else {
return;
}
(items[currentIndex] as HTMLElement).focus();
});
}
destroyArrowNavigation():void {
// Todo: Implement
}
}

Loading…
Cancel
Save