From a72adb0e75349ee9377ca699efa368cd3932b0eb Mon Sep 17 00:00:00 2001 From: shamoon <4887959+shamoon@users.noreply.github.com> Date: Sun, 7 Apr 2024 18:34:35 -0700 Subject: [PATCH] Add shortcut keys for filters --- .../filterable-dropdown.component.spec.ts | 13 +++++++ .../filterable-dropdown.component.ts | 39 +++++++++++++++++-- .../bulk-editor/bulk-editor.component.html | 20 ++++++---- .../bulk-editor/bulk-editor.component.ts | 5 ++- .../document-list.component.html | 4 +- .../filter-editor.component.html | 16 ++++++-- .../filter-editor/filter-editor.component.ts | 3 ++ 7 files changed, 82 insertions(+), 18 deletions(-) diff --git a/src-ui/src/app/components/common/filterable-dropdown/filterable-dropdown.component.spec.ts b/src-ui/src/app/components/common/filterable-dropdown/filterable-dropdown.component.spec.ts index fe377cc70..a285144f4 100644 --- a/src-ui/src/app/components/common/filterable-dropdown/filterable-dropdown.component.spec.ts +++ b/src-ui/src/app/components/common/filterable-dropdown/filterable-dropdown.component.spec.ts @@ -26,6 +26,7 @@ import { TagComponent } from '../tag/tag.component' import { FormsModule, ReactiveFormsModule } from '@angular/forms' import { ClearableBadgeComponent } from '../clearable-badge/clearable-badge.component' import { NgxBootstrapIconsModule, allIcons } from 'ngx-bootstrap-icons' +import { HotKeyService } from 'src/app/services/hot-key.service' const items: Tag[] = [ { @@ -53,6 +54,7 @@ let selectionModel: FilterableDropdownSelectionModel describe('FilterableDropdownComponent & FilterableDropdownSelectionModel', () => { let component: FilterableDropdownComponent let fixture: ComponentFixture + let hotkeyService: HotKeyService beforeEach(async () => { TestBed.configureTestingModule({ @@ -72,6 +74,7 @@ describe('FilterableDropdownComponent & FilterableDropdownSelectionModel', () => ], }).compileComponents() + hotkeyService = TestBed.inject(HotKeyService) fixture = TestBed.createComponent(FilterableDropdownComponent) component = fixture.componentInstance selectionModel = new FilterableDropdownSelectionModel() @@ -577,4 +580,14 @@ describe('FilterableDropdownComponent & FilterableDropdownSelectionModel', () => expect(selectionModel.getSelectedItems()).toEqual([items[0]]) expect(selectionModel.getExcludedItems()).toEqual([items[1]]) }) + + it('should support shortcut keys', () => { + component.items = items + component.icon = 'tag-fill' + component.shortcutKey = 't' + fixture.detectChanges() + const openSpy = jest.spyOn(component.dropdown, 'open') + document.dispatchEvent(new KeyboardEvent('keydown', { key: 't' })) + expect(openSpy).toHaveBeenCalled() + }) }) diff --git a/src-ui/src/app/components/common/filterable-dropdown/filterable-dropdown.component.ts b/src-ui/src/app/components/common/filterable-dropdown/filterable-dropdown.component.ts index 4f39d32c3..4a3c70953 100644 --- a/src-ui/src/app/components/common/filterable-dropdown/filterable-dropdown.component.ts +++ b/src-ui/src/app/components/common/filterable-dropdown/filterable-dropdown.component.ts @@ -5,14 +5,17 @@ import { Output, ElementRef, ViewChild, + OnInit, + OnDestroy, } from '@angular/core' import { FilterPipe } from 'src/app/pipes/filter.pipe' import { NgbDropdown } from '@ng-bootstrap/ng-bootstrap' import { ToggleableItemState } from './toggleable-dropdown-button/toggleable-dropdown-button.component' import { MatchingModel } from 'src/app/data/matching-model' -import { Subject } from 'rxjs' +import { Subject, filter, take, takeUntil } from 'rxjs' import { SelectionDataItem } from 'src/app/services/rest/document.service' import { ObjectWithPermissions } from 'src/app/data/object-with-permissions' +import { HotKeyService } from 'src/app/services/hot-key.service' export interface ChangedItems { itemsToAdd: MatchingModel[] @@ -322,7 +325,7 @@ export class FilterableDropdownSelectionModel { templateUrl: './filterable-dropdown.component.html', styleUrls: ['./filterable-dropdown.component.scss'], }) -export class FilterableDropdownComponent { +export class FilterableDropdownComponent implements OnDestroy, OnInit { @ViewChild('listFilterTextInput') listFilterTextInput: ElementRef @ViewChild('dropdown') dropdown: NgbDropdown @ViewChild('buttonItems') buttonItems: ElementRef @@ -419,6 +422,9 @@ export class FilterableDropdownComponent { @Input() documentCounts: SelectionDataItem[] + @Input() + shortcutKey: string + get name(): string { return this.title ? this.title.replace(/\s/g, '_').toLowerCase() : null } @@ -427,12 +433,39 @@ export class FilterableDropdownComponent { private keyboardIndex: number - constructor(private filterPipe: FilterPipe) { + private unsubscribeNotifier: Subject = new Subject() + + constructor( + private filterPipe: FilterPipe, + private hotkeyService: HotKeyService + ) { this.selectionModelChange.subscribe((updatedModel) => { this.modelIsDirty = updatedModel.isDirty() }) } + ngOnInit(): void { + if (this.shortcutKey) { + this.hotkeyService + .addShortcut({ + keys: this.shortcutKey, + description: $localize`Open ${this.title} filter`, + }) + .pipe( + takeUntil(this.unsubscribeNotifier), + filter(() => !this.disabled) + ) + .subscribe(() => { + this.dropdown.open() + }) + } + } + + ngOnDestroy(): void { + this.unsubscribeNotifier.next(true) + this.unsubscribeNotifier.complete() + } + applyClicked() { if (this.selectionModel.isDirty()) { this.dropdown.close() diff --git a/src-ui/src/app/components/document-list/bulk-editor/bulk-editor.component.html b/src-ui/src/app/components/document-list/bulk-editor/bulk-editor.component.html index 865502569..ec4df184b 100644 --- a/src-ui/src/app/components/document-list/bulk-editor/bulk-editor.component.html +++ b/src-ui/src/app/components/document-list/bulk-editor/bulk-editor.component.html @@ -21,7 +21,7 @@ + (apply)="setTags($event)" + shortcutKey="t"> } @if (permissionService.currentUserCan(PermissionAction.View, PermissionType.Correspondent)) { + (apply)="setCorrespondents($event)" + shortcutKey="y"> } @if (permissionService.currentUserCan(PermissionAction.View, PermissionType.DocumentType)) { + (apply)="setDocumentTypes($event)" + shortcutKey="u"> } @if (permissionService.currentUserCan(PermissionAction.View, PermissionType.StoragePath)) { + (apply)="setStoragePaths($event)" + shortcutKey="i"> } diff --git a/src-ui/src/app/components/document-list/bulk-editor/bulk-editor.component.ts b/src-ui/src/app/components/document-list/bulk-editor/bulk-editor.component.ts index 556a1ff13..2d2723051 100644 --- a/src-ui/src/app/components/document-list/bulk-editor/bulk-editor.component.ts +++ b/src-ui/src/app/components/document-list/bulk-editor/bulk-editor.component.ts @@ -1,4 +1,4 @@ -import { Component, OnDestroy, OnInit } from '@angular/core' +import { Component, Input, OnDestroy, OnInit } from '@angular/core' import { Tag } from 'src/app/data/tag' import { Correspondent } from 'src/app/data/correspondent' import { DocumentType } from 'src/app/data/document-type' @@ -74,6 +74,9 @@ export class BulkEditorComponent downloadUseFormatting: new FormControl(false), }) + @Input() + public disabled: boolean = false + constructor( private documentTypeService: DocumentTypeService, private tagService: TagService, diff --git a/src-ui/src/app/components/document-list/document-list.component.html b/src-ui/src/app/components/document-list/document-list.component.html index 3cce1496b..576e7a6e1 100644 --- a/src-ui/src/app/components/document-list/document-list.component.html +++ b/src-ui/src/app/components/document-list/document-list.component.html @@ -80,8 +80,8 @@
- - + +
diff --git a/src-ui/src/app/components/document-list/filter-editor/filter-editor.component.html b/src-ui/src/app/components/document-list/filter-editor/filter-editor.component.html index c6c287ef5..3e43f1d22 100644 --- a/src-ui/src/app/components/document-list/filter-editor/filter-editor.component.html +++ b/src-ui/src/app/components/document-list/filter-editor/filter-editor.component.html @@ -44,7 +44,9 @@ (selectionModelChange)="updateRules()" (opened)="onTagsDropdownOpen()" [documentCounts]="tagDocumentCounts" - [allowSelectNone]="true"> + [allowSelectNone]="true" + [disabled]="disabled" + shortcutKey="t"> } @if (permissionsService.currentUserCan(PermissionAction.View, PermissionType.Correspondent)) { + [allowSelectNone]="true" + [disabled]="disabled" + shortcutKey="y"> } @if (permissionsService.currentUserCan(PermissionAction.View, PermissionType.DocumentType)) { + [allowSelectNone]="true" + [disabled]="disabled" + shortcutKey="u"> } @if (permissionsService.currentUserCan(PermissionAction.View, PermissionType.StoragePath)) { + [allowSelectNone]="true" + [disabled]="disabled" + shortcutKey="i"> }
diff --git a/src-ui/src/app/components/document-list/filter-editor/filter-editor.component.ts b/src-ui/src/app/components/document-list/filter-editor/filter-editor.component.ts index b7be4b57c..3c4ff4593 100644 --- a/src-ui/src/app/components/document-list/filter-editor/filter-editor.component.ts +++ b/src-ui/src/app/components/document-list/filter-editor/filter-editor.component.ts @@ -876,6 +876,9 @@ export class FilterEditorComponent textFilterDebounce: Subject + @Input() + public disabled: boolean = false + ngOnInit() { if ( this.permissionsService.currentUserCan(