|
8 | 8 | var SOLIDUS_KEY_CODE = 191 |
9 | 9 | var SEARCH_FILTER_ACTIVE_KEY = 'docs:search-filter-active' |
10 | 10 | var SAVED_SEARCH_STATE_KEY = 'docs:saved-search-state' |
| 11 | + var SAVED_SEARCH_STATE_VERSION = '1' |
11 | 12 |
|
12 | 13 | activateSearch(require('docsearch.js/dist/cdn/docsearch.js'), document.getElementById('search-script').dataset) |
13 | 14 |
|
14 | 15 | function activateSearch (docsearch, config) { |
15 | 16 | appendStylesheet(config.stylesheet) |
16 | 17 | var baseAlgoliaOptions = { |
17 | | - hitsPerPage: parseInt(config.maxResults) || 15, |
| 18 | + hitsPerPage: parseInt(config.pageSize) || 20, // cannot exceed the hitsPerPage value defined on the index |
18 | 19 | } |
19 | 20 | var searchField = document.getElementById(config.searchFieldId || 'search') |
20 | 21 | searchField.appendChild(Object.assign(document.createElement('div'), { className: 'algolia-autocomplete-results' })) |
|
32 | 33 | autoWidth: false, |
33 | 34 | templates: { |
34 | 35 | footer: |
35 | | - '<div class="ds-footer"><div class="algolia-docsearch-footer">' + |
| 36 | + '<div class="ds-footer"><div class="ds-pagination">' + |
| 37 | + '<span class="ds-pagination--curr">Page 1</span>' + |
| 38 | + '<a href="#" class="ds-pagination--prev">Prev</a>' + |
| 39 | + '<a href="#" class="ds-pagination--next">Next</a></div>' + |
| 40 | + '<div class="algolia-docsearch-footer">' + |
36 | 41 | 'Search by <a class="algolia-docsearch-footer--logo" href="https://www.algolia.com/docsearch" ' + |
37 | 42 | 'target="_blank" rel="noopener">Algolia</a>' + |
38 | 43 | '</div></div>', |
39 | 44 | }, |
40 | 45 | }, |
41 | | - algoliaOptions: baseAlgoliaOptions, |
42 | | - transformData: protectHitOrder, |
43 | | - queryHook: |
44 | | - searchField.classList.contains('has-filter') && |
45 | | - function (query) { |
46 | | - controller.algoliaOptions = typeahead.$facetFilterInput.prop('checked') |
47 | | - ? Object.assign({}, baseAlgoliaOptions, { facetFilters: [typeahead.$facetFilterInput.data('facetFilter')] }) |
48 | | - : baseAlgoliaOptions |
49 | | - }, |
| 46 | + baseAlgoliaOptions: baseAlgoliaOptions, |
50 | 47 | }) |
51 | 48 | var input = controller.input |
52 | 49 | var typeahead = input.data('aaAutocomplete') |
53 | 50 | var dropdown = typeahead.dropdown |
54 | 51 | var menu = dropdown.$menu |
55 | 52 | var dataset = dropdown.datasets[0] |
56 | 53 | dataset.cache = false |
| 54 | + dataset.source = controller.getAutocompleteSource(undefined, processQuery.bind(typeahead, controller)) |
57 | 55 | delete dataset.templates.footer |
| 56 | + controller.queryDataCallback = processQueryData.bind(typeahead) |
58 | 57 | typeahead.setVal() // clear value on page reload |
59 | 58 | input.on('autocomplete:closed', clearSearch.bind(typeahead)) |
60 | 59 | input.on('autocomplete:cursorchanged autocomplete:cursorremoved', saveSearchState.bind(typeahead)) |
|
71 | 70 | .find('.filter input') |
72 | 71 | .on('change', toggleFilter.bind(typeahead)) |
73 | 72 | .prop('checked', window.localStorage.getItem(SEARCH_FILTER_ACTIVE_KEY) === 'true') |
| 73 | + menu.find('.ds-pagination--prev').on('click', paginate.bind(typeahead, -1)).css('visibility', 'hidden') |
| 74 | + menu.find('.ds-pagination--next').on('click', paginate.bind(typeahead, 1)).css('visibility', 'hidden') |
74 | 75 | monitorCtrlKey.call(typeahead) |
75 | 76 | searchField.addEventListener('click', confineEvent) |
76 | 77 | document.documentElement.addEventListener('click', clearSearch.bind(typeahead)) |
|
87 | 88 | } else if (e.persisted && !isClosed(this)) { |
88 | 89 | this.$input.focus() |
89 | 90 | this.$input.val(this.getVal()) |
| 91 | + this.dropdown.datasets[0].page = this.dropdown.$menu.find('.ds-pagination--curr').data('page') |
90 | 92 | } else if (window.sessionStorage.getItem('docs:restore-search-on-back') === 'true') { |
91 | 93 | if (!window.matchMedia('(min-width: 1024px)').matches) document.querySelector('.navbar-burger').click() |
92 | 94 | restoreSearch.call(this) |
|
104 | 106 | var restoring = dropdown.restoring |
105 | 107 | delete dropdown.restoring |
106 | 108 | if (isClosed(this)) return |
107 | | - getScrollableResultsContainer(dropdown).scrollTop(0) |
| 109 | + updatePagination.call(dropdown) |
108 | 110 | if (restoring && restoring.query === this.getVal() && restoring.filter === this.$facetFilterInput.prop('checked')) { |
109 | 111 | var cursor = restoring.cursor |
110 | 112 | if (cursor) dropdown._moveCursor(cursor) |
|
164 | 166 | function onCtrlKeyDown (e) { |
165 | 167 | if (e.keyCode !== CTRL_KEY_CODE) return |
166 | 168 | this.ctrlKeyDown = true |
167 | | - var dropdown = this.dropdown |
168 | | - var container = getScrollableResultsContainer(dropdown) |
| 169 | + var container = getScrollableResultsContainer(this.dropdown) |
169 | 170 | var prevScrollTop = container.scrollTop() |
170 | | - dropdown.getCurrentCursor().find('a').focus() |
| 171 | + this.dropdown.getCurrentCursor().find('a').focus() |
171 | 172 | container.scrollTop(prevScrollTop) // calling focus can cause the container to scroll, so restore it |
172 | 173 | } |
173 | 174 |
|
|
195 | 196 | } |
196 | 197 | } |
197 | 198 |
|
198 | | - function clearSearch () { |
199 | | - this.isActivated = true // we can't rely on this state being correct |
200 | | - this.setVal() |
201 | | - delete this.ctrlKeyDown |
| 199 | + function paginate (delta, e) { |
| 200 | + e.preventDefault() |
| 201 | + var dataset = this.dropdown.datasets[0] |
| 202 | + dataset.page = (dataset.page || 0) + delta |
| 203 | + requery.call(this) |
| 204 | + } |
| 205 | + |
| 206 | + function updatePagination () { |
| 207 | + var result = this.datasets[0].result |
| 208 | + var page = result.page |
| 209 | + var menu = this.$menu |
| 210 | + menu |
| 211 | + .find('.ds-pagination--curr') |
| 212 | + .html(result.pages ? 'Page ' + (page + 1) + ' of ' + result.pages : 'No results') |
| 213 | + .data('page', page) |
| 214 | + menu.find('.ds-pagination--prev').css('visibility', page > 0 ? '' : 'hidden') |
| 215 | + menu.find('.ds-pagination--next').css('visibility', result.pages > page + 1 ? '' : 'hidden') |
| 216 | + getScrollableResultsContainer(this).scrollTop(0) |
202 | 217 | } |
203 | 218 |
|
204 | 219 | function requery (query) { |
|
209 | 224 | this.dropdown.open() |
210 | 225 | } |
211 | 226 |
|
| 227 | + function clearSearch () { |
| 228 | + this.isActivated = true // we can't rely on this state being correct |
| 229 | + this.setVal() |
| 230 | + delete this.ctrlKeyDown |
| 231 | + delete this.dropdown.datasets[0].result |
| 232 | + } |
| 233 | + |
| 234 | + function processQuery (controller, query) { |
| 235 | + var algoliaOptions = {} |
| 236 | + if (this.$facetFilterInput.prop('checked')) { |
| 237 | + algoliaOptions.facetFilters = [this.$facetFilterInput.data('facetFilter')] |
| 238 | + } |
| 239 | + var dataset = this.dropdown.datasets[0] |
| 240 | + var activeResult = dataset.result |
| 241 | + algoliaOptions.page = !activeResult || query === activeResult.query ? dataset.page || 0 : (dataset.page = 0) |
| 242 | + controller.algoliaOptions = Object.keys(algoliaOptions).length |
| 243 | + ? Object.assign({}, controller.baseAlgoliaOptions, algoliaOptions) |
| 244 | + : controller.baseAlgoliaOptions |
| 245 | + } |
| 246 | + |
| 247 | + function processQueryData (data) { |
| 248 | + var result = data.results[0] |
| 249 | + this.dropdown.datasets[0].result = { page: result.page, pages: result.nbPages, query: result.query } |
| 250 | + result.hits = preserveHitOrder(result.hits) |
| 251 | + } |
| 252 | + |
212 | 253 | // preserves the original order of results by qualifying unique occurrences of the same lvl0 and lvl1 values |
213 | | - function protectHitOrder (hits) { |
| 254 | + function preserveHitOrder (hits) { |
214 | 255 | var prevLvl0 |
215 | 256 | var lvl0Qualifiers = {} |
216 | 257 | var lvl1Qualifiers = {} |
|
236 | 277 | function readSavedSearchState () { |
237 | 278 | try { |
238 | 279 | var state = window.localStorage.getItem(SAVED_SEARCH_STATE_KEY) |
239 | | - if (state) return JSON.parse(state) |
| 280 | + if (state && (state = JSON.parse(state))._version.toString() === SAVED_SEARCH_STATE_VERSION) return state |
240 | 281 | } catch (e) { |
241 | 282 | window.localStorage.removeItem(SAVED_SEARCH_STATE_KEY) |
242 | 283 | } |
|
247 | 288 | if (!searchState) return |
248 | 289 | this.dropdown.restoring = searchState |
249 | 290 | this.$facetFilterInput.prop('checked', searchState.filter) // change event will be ignored |
| 291 | + var dataset = this.dropdown.datasets[0] |
| 292 | + dataset.page = searchState.page |
| 293 | + delete dataset.result |
250 | 294 | requery.call(this, searchState.query) // cursor is restored by onResultsUpdated => |
251 | 295 | } |
252 | 296 |
|
|
255 | 299 | window.localStorage.setItem( |
256 | 300 | SAVED_SEARCH_STATE_KEY, |
257 | 301 | JSON.stringify({ |
258 | | - query: this.getVal(), |
259 | | - filter: this.$facetFilterInput.prop('checked'), |
| 302 | + _version: SAVED_SEARCH_STATE_VERSION, |
260 | 303 | cursor: this.dropdown.getCurrentCursor().index() + 1, |
| 304 | + filter: this.$facetFilterInput.prop('checked'), |
| 305 | + page: this.dropdown.datasets[0].page, |
| 306 | + query: this.getVal(), |
261 | 307 | }) |
262 | 308 | ) |
263 | 309 | } |
|
0 commit comments