|
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