|
1 | 1 | import { inject as service } from '@ember/service';
|
2 | 2 | import Component from '@ember/component';
|
3 |
| -import layout from './template'; |
4 |
| -import { EKMixin, keyUp, keyDown } from 'ember-keyboard'; |
5 |
| -import { on } from '@ember/object/evented'; |
| 3 | +import { action } from '@ember/object'; |
| 4 | +import template from './template'; |
| 5 | +import { keyResponder, onKey } from 'ember-keyboard'; |
| 6 | +import { layout } from '@ember-decorators/component'; |
6 | 7 | import { computed } from '@ember/object';
|
7 | 8 | import { task } from 'ember-concurrency';
|
8 | 9 | import config from 'ember-get-config';
|
9 | 10 |
|
10 | 11 | const projectName = config['ember-cli-addon-docs'].projectName;
|
11 | 12 |
|
12 |
| -export default Component.extend(EKMixin, { |
13 |
| - layout, |
| 13 | +@keyResponder |
| 14 | +@layout(template) |
| 15 | +export default class DocsHeaderSearchResultsComponent extends Component { |
| 16 | + @service docsSearch; |
| 17 | + @service router; |
| 18 | + @service store; |
14 | 19 |
|
15 |
| - docsSearch: service(), |
16 |
| - router: service(), |
17 |
| - store: service(), |
18 |
| - |
19 |
| - query: null, // passed in |
20 |
| - selectedIndex: null, |
21 |
| - |
22 |
| - keyboardActivated: true, |
| 20 | + query = null; // passed in |
| 21 | + selectedIndex = null; |
23 | 22 |
|
24 | 23 | didInsertElement() {
|
25 |
| - this._super(); |
| 24 | + super.didInsertElement(...arguments); |
26 | 25 |
|
27 | 26 | // Start downloading the search index immediately
|
28 | 27 | this.docsSearch.loadSearchIndex();
|
29 |
| - }, |
| 28 | + } |
30 | 29 |
|
31 | 30 | didReceiveAttrs() {
|
32 |
| - this._super(...arguments); |
| 31 | + super.didReceiveAttrs(...arguments); |
33 | 32 |
|
34 | 33 | this.search.perform();
|
35 |
| - }, |
| 34 | + } |
36 | 35 |
|
37 |
| - project: computed(function() { |
| 36 | + get project() { |
38 | 37 | return this.store.peekRecord('project', projectName);
|
39 |
| - }), |
| 38 | + } |
40 | 39 |
|
41 |
| - trimmedQuery: computed('query', function() { |
| 40 | + @computed('query') |
| 41 | + get trimmedQuery() { |
42 | 42 | return this.query.trim();
|
43 |
| - }), |
| 43 | + } |
44 | 44 |
|
45 |
| - search: task(function*() { |
| 45 | + @task({ restartable: true }) |
| 46 | + *search() { |
46 | 47 | let results;
|
47 | 48 |
|
48 | 49 | if (this.trimmedQuery) {
|
49 | 50 | results = yield this.docsSearch.search(this.trimmedQuery);
|
50 | 51 | }
|
51 | 52 |
|
52 |
| - this.set('selectedIndex', (results.length ? 0 : null)); |
| 53 | + this.set('selectedIndex', results.length ? 0 : null); |
53 | 54 | this.set('rawSearchResults', results);
|
54 |
| - }).restartable(), |
| 55 | + } |
55 | 56 |
|
56 |
| - searchResults: computed('project.navigationIndex', 'rawSearchResults.[]', function() { |
| 57 | + @computed('project.navigationIndex', 'rawSearchResults.[]') |
| 58 | + get searchResults() { |
57 | 59 | let rawSearchResults = this.rawSearchResults;
|
58 | 60 | let router = this.router;
|
59 |
| - let routerMicrolib = router._router._routerMicrolib || router._router.router; |
| 61 | + let routerMicrolib = |
| 62 | + router._router._routerMicrolib || router._router.router; |
60 | 63 |
|
61 | 64 | if (rawSearchResults) {
|
62 |
| - return this.rawSearchResults |
63 |
| - // If the doc has a route, ensure it exists |
64 |
| - .filter(({ document }) => { |
65 |
| - if (document.route) { |
66 |
| - let routeExists = routerMicrolib.recognizer.names[document.route]; |
67 |
| - |
68 |
| - return routeExists && document.route !== 'not-found' && document.route !== 'application'; |
69 |
| - } else { |
70 |
| - return true; |
71 |
| - } |
72 |
| - }) |
73 |
| - |
74 |
| - // Filter out the templates of the API items' pages, since we handle them separately |
75 |
| - .filter(({ document }) => { |
76 |
| - let isApiItemTemplate = (document.route === 'docs.api.item' && document.type === 'template'); |
77 |
| - return !isApiItemTemplate; |
78 |
| - }) |
79 |
| - |
80 |
| - // Filter out modules that are not in the navigationIndex |
81 |
| - .filter(({ document }) => { |
82 |
| - if (document.type === 'module') { |
83 |
| - let navigableModules = this.get('project.navigationIndex').find(section => section.type === 'modules'); |
84 |
| - let navigableModuleIds = navigableModules ? navigableModules.items.map(item => item.id) : []; |
85 |
| - |
86 |
| - return navigableModuleIds.includes(document.title); |
87 |
| - } else { |
88 |
| - return true; |
89 |
| - } |
90 |
| - }) |
91 |
| - |
92 |
| - // Add a reference to the Ember Data model to each API item search result |
93 |
| - .map(searchResult => { |
94 |
| - let { document } = searchResult; |
95 |
| - if (document.type !== 'template') { |
96 |
| - let store = this.store; |
97 |
| - searchResult.model = store.peekRecord(document.type, document.item.id) |
98 |
| - } |
99 |
| - |
100 |
| - return searchResult; |
101 |
| - }); |
| 65 | + return ( |
| 66 | + this.rawSearchResults |
| 67 | + // If the doc has a route, ensure it exists |
| 68 | + .filter(({ document }) => { |
| 69 | + if (document.route) { |
| 70 | + let routeExists = routerMicrolib.recognizer.names[document.route]; |
| 71 | + |
| 72 | + return ( |
| 73 | + routeExists && |
| 74 | + document.route !== 'not-found' && |
| 75 | + document.route !== 'application' |
| 76 | + ); |
| 77 | + } else { |
| 78 | + return true; |
| 79 | + } |
| 80 | + }) |
| 81 | + |
| 82 | + // Filter out the templates of the API items' pages, since we handle them separately |
| 83 | + .filter(({ document }) => { |
| 84 | + let isApiItemTemplate = |
| 85 | + document.route === 'docs.api.item' && |
| 86 | + document.type === 'template'; |
| 87 | + return !isApiItemTemplate; |
| 88 | + }) |
| 89 | + |
| 90 | + // Filter out modules that are not in the navigationIndex |
| 91 | + .filter(({ document }) => { |
| 92 | + if (document.type === 'module') { |
| 93 | + let navigableModules = this.get('project.navigationIndex').find( |
| 94 | + (section) => section.type === 'modules' |
| 95 | + ); |
| 96 | + let navigableModuleIds = navigableModules |
| 97 | + ? navigableModules.items.map((item) => item.id) |
| 98 | + : []; |
| 99 | + |
| 100 | + return navigableModuleIds.includes(document.title); |
| 101 | + } else { |
| 102 | + return true; |
| 103 | + } |
| 104 | + }) |
| 105 | + |
| 106 | + // Add a reference to the Ember Data model to each API item search result |
| 107 | + .map((searchResult) => { |
| 108 | + let { document } = searchResult; |
| 109 | + if (document.type !== 'template') { |
| 110 | + let store = this.store; |
| 111 | + searchResult.model = store.peekRecord( |
| 112 | + document.type, |
| 113 | + document.item.id |
| 114 | + ); |
| 115 | + } |
| 116 | + |
| 117 | + return searchResult; |
| 118 | + }) |
| 119 | + ); |
102 | 120 | }
|
103 |
| - }), |
104 | 121 |
|
105 |
| - gotoSelectedItem: on(keyUp('Enter'), function() { |
| 122 | + return undefined; |
| 123 | + } |
| 124 | + |
| 125 | + @onKey('Enter', { event: 'keyup' }) |
| 126 | + gotoSelectedItem() { |
106 | 127 | if (this.selectedIndex !== null) {
|
107 | 128 | let selectedResult = this.searchResults[this.selectedIndex];
|
108 | 129 | if (selectedResult.document.type === 'template') {
|
109 | 130 | this.router.transitionTo(selectedResult.document.route);
|
110 | 131 | } else {
|
111 |
| - this.router.transitionTo('docs.api.item', selectedResult.model.get('routingId')); |
| 132 | + this.router.transitionTo( |
| 133 | + 'docs.api.item', |
| 134 | + selectedResult.model.get('routingId') |
| 135 | + ); |
112 | 136 | }
|
113 | 137 | }
|
114 | 138 |
|
115 | 139 | this.get('on-visit')();
|
116 |
| - }), |
| 140 | + } |
117 | 141 |
|
118 |
| - nextSearchResult: on(keyDown('ctrl+KeyN'), keyDown('ArrowDown'), function() { |
| 142 | + @onKey('ctrl+KeyN') |
| 143 | + @onKey('ArrowDown') |
| 144 | + nextSearchResult() { |
119 | 145 | let hasSearchResults = this.get('searchResults.length');
|
120 |
| - let lastResultIsSelected = (this.selectedIndex + 1 === this.get('searchResults.length')); |
| 146 | + let lastResultIsSelected = |
| 147 | + this.selectedIndex + 1 === this.get('searchResults.length'); |
121 | 148 |
|
122 | 149 | if (hasSearchResults && !lastResultIsSelected) {
|
123 | 150 | this.incrementProperty('selectedIndex');
|
124 | 151 | }
|
125 |
| - }), |
| 152 | + } |
126 | 153 |
|
127 |
| - previousSearchResult: on(keyDown('ctrl+KeyP'), keyDown('ArrowUp'), function() { |
| 154 | + @onKey('ctrl+KeyP') |
| 155 | + @onKey('ArrowUp') |
| 156 | + previousSearchResult() { |
128 | 157 | let hasSearchResults = this.get('searchResults.length');
|
129 |
| - let firstResultIsSelected = (this.selectedIndex === 0); |
| 158 | + let firstResultIsSelected = this.selectedIndex === 0; |
130 | 159 |
|
131 | 160 | if (hasSearchResults && !firstResultIsSelected) {
|
132 | 161 | this.decrementProperty('selectedIndex');
|
133 | 162 | }
|
134 |
| - }), |
| 163 | + } |
135 | 164 |
|
| 165 | + @action |
136 | 166 | clearSearch() {
|
137 | 167 | this.set('query', null);
|
138 |
| - }, |
139 |
| - |
140 |
| - actions: { |
141 |
| - selectResult(index) { |
142 |
| - this.set('selectedIndex', index); |
143 |
| - }, |
144 |
| - |
145 |
| - clearSearch() { |
146 |
| - this.clearSearch(); |
147 |
| - }, |
148 | 168 | }
|
149 | 169 |
|
150 |
| -}); |
| 170 | + @action |
| 171 | + selectResult(index) { |
| 172 | + this.set('selectedIndex', index); |
| 173 | + } |
| 174 | +} |
0 commit comments