Hotkey service
This commit is contained in:
parent
4ee76f4edf
commit
5bb577ab20
@ -150,12 +150,12 @@ describe('GlobalSearchComponent', () => {
|
|||||||
|
|
||||||
it('should handle keyboard nav', () => {
|
it('should handle keyboard nav', () => {
|
||||||
const focusSpy = jest.spyOn(component.searchInput.nativeElement, 'focus')
|
const focusSpy = jest.spyOn(component.searchInput.nativeElement, 'focus')
|
||||||
component.handleKeyboardEvent(
|
document.dispatchEvent(
|
||||||
new KeyboardEvent('keydown', { key: 'k', ctrlKey: true })
|
new KeyboardEvent('keydown', { key: 'k', ctrlKey: true })
|
||||||
)
|
)
|
||||||
expect(focusSpy).toHaveBeenCalled()
|
expect(focusSpy).toHaveBeenCalled()
|
||||||
// coverage
|
// coverage
|
||||||
component.handleKeyboardEvent(
|
document.dispatchEvent(
|
||||||
new KeyboardEvent('keydown', { key: 'k', metaKey: true })
|
new KeyboardEvent('keydown', { key: 'k', metaKey: true })
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -5,6 +5,7 @@ import {
|
|||||||
ViewChildren,
|
ViewChildren,
|
||||||
QueryList,
|
QueryList,
|
||||||
HostListener,
|
HostListener,
|
||||||
|
OnInit,
|
||||||
} from '@angular/core'
|
} from '@angular/core'
|
||||||
import { Router } from '@angular/router'
|
import { Router } from '@angular/router'
|
||||||
import { NgbDropdown, NgbModal, NgbModalRef } from '@ng-bootstrap/ng-bootstrap'
|
import { NgbDropdown, NgbModal, NgbModalRef } from '@ng-bootstrap/ng-bootstrap'
|
||||||
@ -39,13 +40,14 @@ import { StoragePathEditDialogComponent } from '../../common/edit-dialog/storage
|
|||||||
import { TagEditDialogComponent } from '../../common/edit-dialog/tag-edit-dialog/tag-edit-dialog.component'
|
import { TagEditDialogComponent } from '../../common/edit-dialog/tag-edit-dialog/tag-edit-dialog.component'
|
||||||
import { UserEditDialogComponent } from '../../common/edit-dialog/user-edit-dialog/user-edit-dialog.component'
|
import { UserEditDialogComponent } from '../../common/edit-dialog/user-edit-dialog/user-edit-dialog.component'
|
||||||
import { WorkflowEditDialogComponent } from '../../common/edit-dialog/workflow-edit-dialog/workflow-edit-dialog.component'
|
import { WorkflowEditDialogComponent } from '../../common/edit-dialog/workflow-edit-dialog/workflow-edit-dialog.component'
|
||||||
|
import { HotKeyService } from 'src/app/services/hot-key.service'
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'pngx-global-search',
|
selector: 'pngx-global-search',
|
||||||
templateUrl: './global-search.component.html',
|
templateUrl: './global-search.component.html',
|
||||||
styleUrl: './global-search.component.scss',
|
styleUrl: './global-search.component.scss',
|
||||||
})
|
})
|
||||||
export class GlobalSearchComponent {
|
export class GlobalSearchComponent implements OnInit {
|
||||||
public DataType = DataType
|
public DataType = DataType
|
||||||
public query: string
|
public query: string
|
||||||
public queryDebounce: Subject<string>
|
public queryDebounce: Subject<string>
|
||||||
@ -60,13 +62,6 @@ export class GlobalSearchComponent {
|
|||||||
@ViewChildren('primaryButton') primaryButtons: QueryList<ElementRef>
|
@ViewChildren('primaryButton') primaryButtons: QueryList<ElementRef>
|
||||||
@ViewChildren('secondaryButton') secondaryButtons: QueryList<ElementRef>
|
@ViewChildren('secondaryButton') secondaryButtons: QueryList<ElementRef>
|
||||||
|
|
||||||
@HostListener('document:keydown', ['$event'])
|
|
||||||
handleKeyboardEvent(event: KeyboardEvent) {
|
|
||||||
if (event.key === 'k' && (event.ctrlKey || event.metaKey)) {
|
|
||||||
this.searchInput.nativeElement.focus()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
private searchService: SearchService,
|
private searchService: SearchService,
|
||||||
private router: Router,
|
private router: Router,
|
||||||
@ -74,7 +69,8 @@ export class GlobalSearchComponent {
|
|||||||
private documentService: DocumentService,
|
private documentService: DocumentService,
|
||||||
private documentListViewService: DocumentListViewService,
|
private documentListViewService: DocumentListViewService,
|
||||||
private permissionsService: PermissionsService,
|
private permissionsService: PermissionsService,
|
||||||
private toastService: ToastService
|
private toastService: ToastService,
|
||||||
|
private hotkeyService: HotKeyService
|
||||||
) {
|
) {
|
||||||
this.queryDebounce = new Subject<string>()
|
this.queryDebounce = new Subject<string>()
|
||||||
|
|
||||||
@ -90,6 +86,15 @@ export class GlobalSearchComponent {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ngOnInit() {
|
||||||
|
this.hotkeyService.addShortcut({ keys: 'meta.k' }).subscribe(() => {
|
||||||
|
this.searchInput.nativeElement.focus()
|
||||||
|
})
|
||||||
|
this.hotkeyService.addShortcut({ keys: 'control.k' }).subscribe(() => {
|
||||||
|
this.searchInput.nativeElement.focus()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
private search(query: string) {
|
private search(query: string) {
|
||||||
this.loading = true
|
this.loading = true
|
||||||
this.searchService.globalSearch(query).subscribe((results) => {
|
this.searchService.globalSearch(query).subscribe((results) => {
|
||||||
|
41
src-ui/src/app/services/hot-key.service.spec.ts
Normal file
41
src-ui/src/app/services/hot-key.service.spec.ts
Normal file
@ -0,0 +1,41 @@
|
|||||||
|
import { TestBed } from '@angular/core/testing'
|
||||||
|
import { EventManager } from '@angular/platform-browser'
|
||||||
|
import { DOCUMENT } from '@angular/common'
|
||||||
|
|
||||||
|
import { HotKeyService } from './hot-key.service'
|
||||||
|
|
||||||
|
describe('HotKeyService', () => {
|
||||||
|
let service: HotKeyService
|
||||||
|
let eventManager: EventManager
|
||||||
|
let document: Document
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
TestBed.configureTestingModule({
|
||||||
|
providers: [HotKeyService, EventManager],
|
||||||
|
})
|
||||||
|
service = TestBed.inject(HotKeyService)
|
||||||
|
eventManager = TestBed.inject(EventManager)
|
||||||
|
document = TestBed.inject(DOCUMENT)
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should support adding a shortcut', () => {
|
||||||
|
const callback = jest.fn()
|
||||||
|
const addEventListenerSpy = jest.spyOn(eventManager, 'addEventListener')
|
||||||
|
|
||||||
|
const observable = service
|
||||||
|
.addShortcut({ keys: 'control.a' })
|
||||||
|
.subscribe(() => {
|
||||||
|
callback()
|
||||||
|
})
|
||||||
|
|
||||||
|
expect(addEventListenerSpy).toHaveBeenCalled()
|
||||||
|
|
||||||
|
document.dispatchEvent(
|
||||||
|
new KeyboardEvent('keydown', { key: 'a', ctrlKey: true })
|
||||||
|
)
|
||||||
|
expect(callback).toHaveBeenCalled()
|
||||||
|
|
||||||
|
//coverage
|
||||||
|
observable.unsubscribe()
|
||||||
|
})
|
||||||
|
})
|
45
src-ui/src/app/services/hot-key.service.ts
Normal file
45
src-ui/src/app/services/hot-key.service.ts
Normal file
@ -0,0 +1,45 @@
|
|||||||
|
import { DOCUMENT } from '@angular/common'
|
||||||
|
import { Inject, Injectable } from '@angular/core'
|
||||||
|
import { EventManager } from '@angular/platform-browser'
|
||||||
|
import { Observable } from 'rxjs'
|
||||||
|
|
||||||
|
export interface ShortcutOptions {
|
||||||
|
element?: any
|
||||||
|
keys: string
|
||||||
|
}
|
||||||
|
|
||||||
|
@Injectable({
|
||||||
|
providedIn: 'root',
|
||||||
|
})
|
||||||
|
export class HotKeyService {
|
||||||
|
defaults: Partial<ShortcutOptions> = {
|
||||||
|
element: this.document,
|
||||||
|
}
|
||||||
|
|
||||||
|
constructor(
|
||||||
|
private eventManager: EventManager,
|
||||||
|
@Inject(DOCUMENT) private document: Document
|
||||||
|
) {}
|
||||||
|
|
||||||
|
addShortcut(options: ShortcutOptions) {
|
||||||
|
const merged = { ...this.defaults, ...options }
|
||||||
|
const event = `keydown.${merged.keys}`
|
||||||
|
|
||||||
|
return new Observable((observer) => {
|
||||||
|
const handler = (e) => {
|
||||||
|
e.preventDefault()
|
||||||
|
observer.next(e)
|
||||||
|
}
|
||||||
|
|
||||||
|
const dispose = this.eventManager.addEventListener(
|
||||||
|
merged.element,
|
||||||
|
event,
|
||||||
|
handler
|
||||||
|
)
|
||||||
|
|
||||||
|
return () => {
|
||||||
|
dispose()
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
Loading…
x
Reference in New Issue
Block a user