Skip to content

Commit 86cd749

Browse files
committed
#1087 Implement search page
1 parent c3c5949 commit 86cd749

File tree

5 files changed

+190
-0
lines changed

5 files changed

+190
-0
lines changed

src/v2/search/index.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
---
2+
search: true
3+
---

themes/vue/layout/page.ejs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,8 @@
2323
<% } %>
2424
<% if (page.sponsors) { %>
2525
<%- partial('sponsors-page') %>
26+
<% } else if (page.search) { %>
27+
<%- partial('search-page') %>
2628
<% } else { %>
2729
<%- page.content %>
2830
<% } %>

themes/vue/layout/search-page.ejs

Lines changed: 160 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,160 @@
1+
<div id="search-page">
2+
<form class="search-form">
3+
<input
4+
class="search-query"
5+
v-model="search"
6+
placeholder="Search VueJS"
7+
>
8+
<p v-if="totalResults">
9+
<strong>{{ totalResults }} results</strong> found in {{ queryTime }}ms
10+
</p>
11+
</form>
12+
13+
<template v-if="results.length">
14+
<search-result
15+
v-for="(result, i) in results"
16+
:key="i"
17+
:result="result"
18+
></search-result>
19+
</template>
20+
21+
<p v-else>No results were found.</p>
22+
23+
<div ref="infiniteScrollAnchor"></div>
24+
25+
</div>
26+
27+
<script src="//cdn.jsdelivr.net/algoliasearch/3/algoliasearch.min.js"></script>
28+
<script src="//cdn.jsdelivr.net/algoliasearch.helper/2/algoliasearch.helper.min.js"></script>
29+
<script src="//polyfill.io/v2/polyfill.min.js?features=IntersectionObserver"></script>
30+
<script>
31+
var match = window.location.pathname.match(/^\/(v\d+)/)
32+
var version = match ? match[1] : 'v2'
33+
var algolia = algoliasearch('BH4D9OD16A', '85cc3221c9f23bfbaa4e3913dd7625ea')
34+
var algoliaHelper = algoliasearchHelper(algolia, 'vuejs', {
35+
hitsPerPage: 15,
36+
maxValuesPerFacet: 10,
37+
advancedSyntax: true,
38+
facets: ['version'],
39+
})
40+
41+
algoliaHelper.addFacetRefinement('version', version)
42+
algoliaHelper.on('result', parseSearchResults)
43+
44+
var searchPage = new Vue({
45+
el: '#search-page',
46+
components: {
47+
'search-result': {
48+
props: {
49+
result: {
50+
type: Object,
51+
required: true,
52+
},
53+
},
54+
render(h) {
55+
var content = []
56+
content.push(h('a', {
57+
attrs: {
58+
class: 'title',
59+
href: this.result.url,
60+
},
61+
domProps: { innerHTML: this.result.title },
62+
}))
63+
if (this.result.summary) {
64+
content.push(h('p', {
65+
attrs: { class: 'summary' },
66+
domProps: { innerHTML: this.result.summary },
67+
}))
68+
}
69+
content.push(h(
70+
'div',
71+
{ attrs: { class: 'breadcrumbs'} },
72+
this.result.breadcrumbs.map(function(breadcrumb) {
73+
return h('span', {
74+
attrs: { class: 'breadcrumb' },
75+
domProps: { innerHTML: breadcrumb },
76+
})
77+
})
78+
))
79+
return h('div', { attrs: { class: 'search-result' } }, content)
80+
}
81+
}
82+
},
83+
data: {
84+
search: (new URL(location)).searchParams.get('q') || '',
85+
totalResults: 0,
86+
queryTime: 0,
87+
results: [],
88+
totalPages: 0,
89+
lastPage: 0,
90+
},
91+
watch: {
92+
search() {
93+
this.updateSearch()
94+
}
95+
},
96+
created() {
97+
this.updateSearch()
98+
},
99+
mounted() {
100+
var observer = new IntersectionObserver(function(entries) {
101+
if (entries[0].isIntersecting && searchPage.totalPages > searchPage.lastPage + 1) {
102+
searchPage.addPage()
103+
}
104+
})
105+
observer.observe(this.$refs.infiniteScrollAnchor)
106+
},
107+
methods: {
108+
addPage() {
109+
algoliaHelper.setCurrentPage(this.lastPage+1).search()
110+
},
111+
updateSearch() {
112+
algoliaHelper.setCurrentPage(0).setQuery(this.search).search()
113+
},
114+
}
115+
})
116+
117+
function parseSearchResults(content) {
118+
if (content.query === '' || !(content.hits instanceof Array)) {
119+
searchPage.totalResults = 0
120+
searchPage.queryTime = 0
121+
searchPage.results = []
122+
searchPage.totalPages = 0
123+
searchPage.lastPage = 0
124+
return
125+
}
126+
127+
var results = []
128+
129+
for (var hit of content.hits) {
130+
var hierarchy = hit._highlightResult.hierarchy
131+
var titles = []
132+
var level = 0
133+
var levelName
134+
while ((levelName = 'lvl' + level++) in hierarchy) {
135+
titles.push(hierarchy[levelName].value)
136+
}
137+
var summary
138+
if (hit._snippetResult && hit._snippetResult.content) {
139+
summary = hit._snippetResult.content.value + '...'
140+
}
141+
results.push({
142+
title: titles.pop(),
143+
url: hit.url,
144+
summary: summary,
145+
breadcrumbs: titles,
146+
})
147+
}
148+
149+
searchPage.totalResults = content.nbHits
150+
searchPage.queryTime = content.processingTimeMS
151+
searchPage.totalPages = content.nbPages
152+
searchPage.lastPage = content.page
153+
154+
if (searchPage.lastPage === 0) {
155+
searchPage.results = results
156+
} else {
157+
searchPage.results = searchPage.results.concat(results)
158+
}
159+
}
160+
</script>
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
#search-page
2+
.search-form
3+
.search-query
4+
width: 100%
5+
border-radius: 5px
6+
margin-right: 0
7+
p
8+
margin: 0
9+
padding: 0
10+
text-align: right
11+
.search-result
12+
margin-bottom: 15px;
13+
.title
14+
display: block
15+
font-size: 17.55px
16+
.summary
17+
padding: 0
18+
margin: 0
19+
.breadcrumb
20+
color: $light
21+
& + .breadcrumb::before
22+
content: "\203A\A0"
23+
margin-left: 5px
24+
color: $light

themes/vue/source/css/page.styl

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
@import "_demo"
66
@import "_sponsors-page"
77
@import "_sponsors-sidebar"
8+
@import "_search-page"
89
@import "_migration"
910
@import "_sidebar"
1011
@import "_offline-menu"

0 commit comments

Comments
 (0)