diff options
author | Rigel Kent <sendmemail@rigelk.eu> | 2020-01-25 16:32:06 +0100 |
---|---|---|
committer | Rigel Kent <sendmemail@rigelk.eu> | 2020-02-13 16:32:21 +0100 |
commit | 6af662a5961b48ac12682df2b8b971060a2cc67d (patch) | |
tree | de1efc71cbe8543b5b832e5de99a407a54c37220 /client/src/app/header/search-typeahead.component.ts | |
parent | f409f0c3b91d85c66b4841d72ea65b5fbe1483d8 (diff) | |
download | PeerTube-6af662a5961b48ac12682df2b8b971060a2cc67d.tar.gz PeerTube-6af662a5961b48ac12682df2b8b971060a2cc67d.tar.zst PeerTube-6af662a5961b48ac12682df2b8b971060a2cc67d.zip |
Add keyboard navigation and hepler to typeahead
Diffstat (limited to 'client/src/app/header/search-typeahead.component.ts')
-rw-r--r-- | client/src/app/header/search-typeahead.component.ts | 70 |
1 files changed, 58 insertions, 12 deletions
diff --git a/client/src/app/header/search-typeahead.component.ts b/client/src/app/header/search-typeahead.component.ts index d12a9682e..084bdd58b 100644 --- a/client/src/app/header/search-typeahead.component.ts +++ b/client/src/app/header/search-typeahead.component.ts | |||
@@ -1,23 +1,31 @@ | |||
1 | import { Component, ViewChild, ElementRef, AfterViewInit, OnInit } from '@angular/core' | 1 | import { |
2 | Component, | ||
3 | ViewChild, | ||
4 | ElementRef, | ||
5 | AfterViewInit, | ||
6 | OnInit, | ||
7 | OnDestroy, | ||
8 | QueryList | ||
9 | } from '@angular/core' | ||
2 | import { Router, NavigationEnd } from '@angular/router' | 10 | import { Router, NavigationEnd } from '@angular/router' |
3 | import { AuthService } from '@app/core' | 11 | import { AuthService } from '@app/core' |
4 | import { I18n } from '@ngx-translate/i18n-polyfill' | 12 | import { I18n } from '@ngx-translate/i18n-polyfill' |
5 | import { filter } from 'rxjs/operators' | 13 | import { filter } from 'rxjs/operators' |
6 | import { ListKeyManager, ListKeyManagerOption } from '@angular/cdk/a11y' | 14 | import { ListKeyManager } from '@angular/cdk/a11y' |
7 | import { UP_ARROW, DOWN_ARROW, ENTER } from '@angular/cdk/keycodes' | 15 | import { UP_ARROW, DOWN_ARROW, ENTER, TAB } from '@angular/cdk/keycodes' |
16 | import { SuggestionComponent } from './suggestion.component' | ||
8 | 17 | ||
9 | @Component({ | 18 | @Component({ |
10 | selector: 'my-search-typeahead', | 19 | selector: 'my-search-typeahead', |
11 | templateUrl: './search-typeahead.component.html', | 20 | templateUrl: './search-typeahead.component.html', |
12 | styleUrls: [ './search-typeahead.component.scss' ] | 21 | styleUrls: [ './search-typeahead.component.scss' ] |
13 | }) | 22 | }) |
14 | export class SearchTypeaheadComponent implements OnInit, AfterViewInit { | 23 | export class SearchTypeaheadComponent implements OnInit, OnDestroy, AfterViewInit { |
15 | @ViewChild('contentWrapper', { static: true }) contentWrapper: ElementRef | 24 | @ViewChild('contentWrapper', { static: true }) contentWrapper: ElementRef |
16 | @ViewChild('optionsList', { static: true }) optionsList: ElementRef | ||
17 | 25 | ||
18 | hasChannel = false | 26 | hasChannel = false |
19 | inChannel = false | 27 | inChannel = false |
20 | keyboardEventsManager: ListKeyManager<ListKeyManagerOption> | 28 | newSearch = true |
21 | 29 | ||
22 | searchInput: HTMLInputElement | 30 | searchInput: HTMLInputElement |
23 | URIPolicy: 'only-followed' | 'any' = 'any' | 31 | URIPolicy: 'only-followed' | 'any' = 'any' |
@@ -25,7 +33,9 @@ export class SearchTypeaheadComponent implements OnInit, AfterViewInit { | |||
25 | URIPolicyText: string | 33 | URIPolicyText: string |
26 | inAllText: string | 34 | inAllText: string |
27 | inThisChannelText: string | 35 | inThisChannelText: string |
36 | globalSearchIndex = 'https://index.joinpeertube.org' | ||
28 | 37 | ||
38 | keyboardEventsManager: ListKeyManager<SuggestionComponent> | ||
29 | results: any[] = [] | 39 | results: any[] = [] |
30 | 40 | ||
31 | constructor ( | 41 | constructor ( |
@@ -33,7 +43,7 @@ export class SearchTypeaheadComponent implements OnInit, AfterViewInit { | |||
33 | private router: Router, | 43 | private router: Router, |
34 | private i18n: I18n | 44 | private i18n: I18n |
35 | ) { | 45 | ) { |
36 | this.URIPolicyText = this.i18n('Determines whether you can resolve any distant content from its URL, or if your instance only allows doing so for instances it follows.') | 46 | this.URIPolicyText = this.i18n('Determines whether you can resolve any distant content, or if your instance only allows doing so for instances it follows.') |
37 | this.inAllText = this.i18n('In all PeerTube') | 47 | this.inAllText = this.i18n('In all PeerTube') |
38 | this.inThisChannelText = this.i18n('In this channel') | 48 | this.inThisChannelText = this.i18n('In this channel') |
39 | } | 49 | } |
@@ -48,16 +58,30 @@ export class SearchTypeaheadComponent implements OnInit, AfterViewInit { | |||
48 | }) | 58 | }) |
49 | } | 59 | } |
50 | 60 | ||
61 | ngOnDestroy () { | ||
62 | if (this.keyboardEventsManager) this.keyboardEventsManager.change.unsubscribe() | ||
63 | } | ||
64 | |||
51 | ngAfterViewInit () { | 65 | ngAfterViewInit () { |
52 | this.searchInput = this.contentWrapper.nativeElement.childNodes[0] | 66 | this.searchInput = this.contentWrapper.nativeElement.childNodes[0] |
53 | this.searchInput.addEventListener('input', this.computeResults.bind(this)) | 67 | this.searchInput.addEventListener('input', this.computeResults.bind(this)) |
68 | this.searchInput.addEventListener('keyup', this.handleKeyUp.bind(this)) | ||
54 | } | 69 | } |
55 | 70 | ||
56 | get hasSearch () { | 71 | get hasSearch () { |
57 | return !!this.searchInput && !!this.searchInput.value | 72 | return !!this.searchInput && !!this.searchInput.value |
58 | } | 73 | } |
59 | 74 | ||
75 | get activeResult () { | ||
76 | return this.keyboardEventsManager && this.keyboardEventsManager.activeItem && this.keyboardEventsManager.activeItem.result | ||
77 | } | ||
78 | |||
79 | get showHelp () { | ||
80 | return this.hasSearch && this.newSearch && this.activeResult && this.activeResult.type === 'search-global' || false | ||
81 | } | ||
82 | |||
60 | computeResults () { | 83 | computeResults () { |
84 | this.newSearch = true | ||
61 | let results = [ | 85 | let results = [ |
62 | { | 86 | { |
63 | text: 'Maître poney', | 87 | text: 'Maître poney', |
@@ -73,6 +97,10 @@ export class SearchTypeaheadComponent implements OnInit, AfterViewInit { | |||
73 | }, | 97 | }, |
74 | { | 98 | { |
75 | text: this.searchInput.value, | 99 | text: this.searchInput.value, |
100 | type: 'search-instance' | ||
101 | }, | ||
102 | { | ||
103 | text: this.searchInput.value, | ||
76 | type: 'search-global' | 104 | type: 'search-global' |
77 | }, | 105 | }, |
78 | ...results | 106 | ...results |
@@ -90,20 +118,38 @@ export class SearchTypeaheadComponent implements OnInit, AfterViewInit { | |||
90 | ) | 118 | ) |
91 | } | 119 | } |
92 | 120 | ||
121 | initKeyboardEventsManager (event: { items: QueryList<SuggestionComponent>, index?: number }) { | ||
122 | if (this.keyboardEventsManager) this.keyboardEventsManager.change.unsubscribe() | ||
123 | this.keyboardEventsManager = new ListKeyManager(event.items) | ||
124 | if (event.index !== undefined) { | ||
125 | this.keyboardEventsManager.setActiveItem(event.index) | ||
126 | event.items.forEach(e => e.active = false) | ||
127 | this.keyboardEventsManager.activeItem.active = true | ||
128 | } | ||
129 | this.keyboardEventsManager.change.subscribe( | ||
130 | val => { | ||
131 | event.items.forEach(e => e.active = false) | ||
132 | this.keyboardEventsManager.activeItem.active = true | ||
133 | } | ||
134 | ) | ||
135 | } | ||
136 | |||
93 | isUserLoggedIn () { | 137 | isUserLoggedIn () { |
94 | return this.authService.isLoggedIn() | 138 | return this.authService.isLoggedIn() |
95 | } | 139 | } |
96 | 140 | ||
97 | handleKeyUp (event: KeyboardEvent) { | 141 | handleKeyUp (event: KeyboardEvent, indexSelected?: number) { |
98 | event.stopImmediatePropagation() | 142 | event.stopImmediatePropagation() |
99 | if (this.keyboardEventsManager) { | 143 | if (this.keyboardEventsManager) { |
100 | if (event.keyCode === DOWN_ARROW || event.keyCode === UP_ARROW) { | 144 | if (event.keyCode === TAB) { |
101 | // passing the event to key manager so we get a change fired | 145 | this.keyboardEventsManager.setNextItemActive() |
146 | return false | ||
147 | } else if (event.keyCode === DOWN_ARROW || event.keyCode === UP_ARROW) { | ||
102 | this.keyboardEventsManager.onKeydown(event) | 148 | this.keyboardEventsManager.onKeydown(event) |
103 | return false | 149 | return false |
104 | } else if (event.keyCode === ENTER) { | 150 | } else if (event.keyCode === ENTER) { |
105 | // when we hit enter, the keyboardManager should call the selectItem method of the `ListItemComponent` | 151 | this.newSearch = false |
106 | // this.keyboardEventsManager.activeItem | 152 | // this.router.navigate(this.keyboardEventsManager.activeItem.result) |
107 | return false | 153 | return false |
108 | } | 154 | } |
109 | } | 155 | } |