Skip to content

Commit e0ab748

Browse files
committed
IBX-4064: Search page (#322)
(cherry picked from commit 03e568e)
1 parent 9a8fba5 commit e0ab748

File tree

12 files changed

+751
-30
lines changed

12 files changed

+751
-30
lines changed

docs/css/instantsearch.css

Lines changed: 150 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,150 @@
1+
.md-content {
2+
margin: 0 auto;
3+
}
4+
5+
footer.md-footer {
6+
left: 0;
7+
}
8+
9+
.md-content .ais-InstantSearch {
10+
letter-spacing: 0.12px;
11+
}
12+
13+
.md-content .ais-InstantSearch h1 {
14+
font-size: 32px;
15+
line-height: 36px;
16+
letter-spacing: 0.17px;
17+
margin: 8px 0 40px;
18+
}
19+
20+
.md-content .ais-InstantSearch .ais-SearchBox .ais-SearchBox-submit {
21+
left: 16px;
22+
}
23+
24+
.md-content .ais-InstantSearch .ais-SearchBox .ais-SearchBox-submitIcon {
25+
width: 16px;
26+
}
27+
28+
.md-content .ais-InstantSearch .ais-SearchBox .ais-SearchBox-input {
29+
font-size: 14px;
30+
line-height: 21px;
31+
height: 48px;
32+
padding-left: 48px;
33+
border-color: #A0A4A8;
34+
}
35+
36+
.md-content .ais-InstantSearch .ais-Hits-list {
37+
margin-left: 0;
38+
}
39+
40+
.md-content .ais-InstantSearch .ais-Hits-item {
41+
padding: 0px;
42+
width: initial;
43+
margin: 0;
44+
border: 0;
45+
box-shadow: none;
46+
}
47+
48+
.md-content .ais-InstantSearch .ais-Hits-item + .ais-Hits-item {
49+
border-top: 1px solid #E0E0E8;
50+
margin-top: 16px;
51+
padding-top: 16px;
52+
}
53+
54+
.md-content .ais-InstantSearch .ais-Highlight-highlighted {
55+
background-color: #ECF4FF;
56+
color: #4191FF;
57+
font-size: inherit;
58+
line-height: inherit;
59+
}
60+
61+
.md-content .ais-InstantSearch .instantsearch__entry {
62+
display: block;
63+
color: var(--ibexa-dusk-black);
64+
}
65+
66+
.md-content .ais-InstantSearch .instantsearch__entry:hover,
67+
.md-content .ais-InstantSearch .instantsearch__entry:hover .instantsearch__entry-header,
68+
.md-content .ais-InstantSearch .instantsearch__entry:hover .instantsearch__entry-content {
69+
color: var(--ibexa-jazzberry)
70+
}
71+
72+
.md-content .ais-InstantSearch .instantsearch__entry:hover mark,
73+
.md-content .ais-InstantSearch .instantsearch__entry:hover .instantsearch__entry-header mark,
74+
.md-content .ais-InstantSearch .instantsearch__entry:hover .instantsearch__entry-content mark {
75+
color: var(--ibexa-jazzberry)
76+
}
77+
78+
.md-content .ais-InstantSearch .instantsearch__entry-header {
79+
font-size: 18px;
80+
line-height: 20px;
81+
letter-spacing: 0.12px;
82+
margin: 4px 0;
83+
}
84+
85+
.md-content .ais-InstantSearch .instantsearch__entry-header .ais-Highlight-highlighted {
86+
font-size: 18px;
87+
line-height: 20px;
88+
}
89+
90+
.md-content .ais-InstantSearch .instantsearch__entry-content {
91+
color: #3B424A;
92+
font-size: 14px;
93+
line-height: 21px;
94+
letter-spacing: 0.12px;
95+
margin-top: 10px;
96+
padding-left: 10px;
97+
border-left: 2px solid var(--mid-grey);
98+
}
99+
100+
.md-content .ais-InstantSearch .instantsearch__entry-content .ais-Highlight-highlighted {
101+
font-size: 14px;
102+
line-height: 21px;
103+
}
104+
105+
.md-content .ais-InstantSearch .instantsearch__entry-breadcrumbs {
106+
font-size: 12px;
107+
line-height: 21px;
108+
letter-spacing: 0.12px;
109+
}
110+
111+
.md-content .ais-InstantSearch .instantsearch__entry-breadcrumbs-item:after {
112+
content: '›';
113+
display: inline-block;
114+
padding: 0 5px;
115+
}
116+
117+
.md-content .ais-InstantSearch .ais-Pagination .ais-Pagination-list:not([hidden]) {
118+
display: flex;
119+
list-style: none;
120+
margin: 15px 0 0;
121+
display: flex;
122+
justify-content: flex-end;
123+
}
124+
125+
.md-content .ais-InstantSearch .ais-Pagination .ais-Pagination-list .ais-Pagination-item {
126+
margin: 0;
127+
}
128+
129+
.md-content .ais-InstantSearch .ais-Pagination .ais-Pagination-link {
130+
border: none;
131+
font-size: 14px;
132+
line-height: 21px;
133+
letter-spacing: 0.12px;
134+
color: var(--ibexa-dusk-black);
135+
height: 40px;
136+
min-width: 40px;
137+
display: flex;
138+
justify-content: center;
139+
align-items: center;
140+
}
141+
142+
.md-content .ais-InstantSearch .ais-Pagination .ais-Pagination-item--selected .ais-Pagination-link,
143+
.md-content .ais-InstantSearch .ais-Pagination .ais-Pagination-link:hover {
144+
background-color: #F3F3F6;
145+
border-radius: 5px;
146+
}
147+
148+
#version {
149+
display: none;
150+
}

docs/css/instantsearch.js

Lines changed: 166 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,166 @@
1+
(function(global, doc) {
2+
let match;
3+
const search_query = (match = doc.location.search.match(/sq=(.*?)(&|$)/)) ? match[1] : '';
4+
const parsed_search_query = decodeURI(search_query.replaceAll('+', ' '));
5+
const search_page = (match = doc.location.search.match(/p=(\d*?)(&|$)/)) ? match[1] : 1;
6+
const parsed_search_page = parseInt(search_page);
7+
let version = doc.location.pathname.split('/')[2];
8+
if (!/^\d+\.\d+$/.test(version) && version !== 'latest') {
9+
version = 'master';
10+
}
11+
const hitsContainer = '#hits';
12+
const statsContainer = '#stats';
13+
const paginationContainer = '#pagination';
14+
const search = instantsearch({
15+
indexName: 'ezplatform',
16+
searchClient: algoliasearch('2DNYOU6YJZ', '21ce3e522455e18e7ee16cf7d66edb4b'),
17+
initialUiState: {
18+
ezplatform: {
19+
query: parsed_search_query,
20+
refinementList: { version: [version] },
21+
page: parsed_search_page,
22+
},
23+
},
24+
searchFunction(helper) {
25+
if (helper.state.query) {
26+
helper.search();
27+
$(statsContainer).css('visibility', 'visible');
28+
$(paginationContainer).show();
29+
} else {
30+
$(hitsContainer).empty();
31+
$(statsContainer).css('visibility', 'hidden');
32+
$(paginationContainer).hide();
33+
}
34+
},
35+
});
36+
37+
getNextSearchURL = () => {
38+
const searchInputElements = document.getElementsByClassName('ais-SearchBox-input');
39+
const text = searchInputElements[0].value.trim();
40+
const selectedPaginationItemElements = doc.getElementsByClassName('ais-Pagination-item--selected');
41+
const page = selectedPaginationItemElements.length ? parseInt(selectedPaginationItemElements[0].innerText) : 1;
42+
const url = new URL(window.location);
43+
url.searchParams.set('sq', text);
44+
url.searchParams.set('p', page);
45+
return url;
46+
};
47+
48+
let idleTimer;
49+
const startIdleTimer = (url) => {
50+
stopIdleTimer();
51+
idleTimer = window.setTimeout(() => {
52+
window.history.pushState({}, '', url);
53+
}, 1500);
54+
};
55+
const stopIdleTimer = () => {
56+
window.clearTimeout(idleTimer);
57+
};
58+
59+
doc.getElementById('searchbox').addEventListener('keyup', function(event) {
60+
const url = getNextSearchURL();
61+
if (url.searchParams.get('sq') != (new URL(window.location)).searchParams.get('sq')) {
62+
url.searchParams.set('p', 1);
63+
startIdleTimer(url);
64+
}
65+
});
66+
67+
doc.getElementById('pagination').addEventListener('click', function(event) {
68+
stopIdleTimer();
69+
const url = getNextSearchURL();
70+
window.history.pushState({}, '', url);
71+
});
72+
73+
window.onpopstate = (event) => {
74+
window.location.reload();
75+
};
76+
77+
search.addWidgets([
78+
instantsearch.widgets.configure({
79+
hitsPerPage: 10,
80+
}),
81+
instantsearch.widgets.stats({
82+
container: statsContainer,
83+
templates: {
84+
text: `<h1>
85+
Search results ({{#helpers.formatNumber}}{{nbHits}}{{/helpers.formatNumber}})
86+
</h1>`,
87+
},
88+
}),
89+
instantsearch.widgets.searchBox({
90+
container: '#searchbox',
91+
}),
92+
instantsearch.widgets.hits({
93+
container: hitsContainer,
94+
templates: {
95+
item: (hit) => {
96+
const hierarchy = Object.entries(hit.hierarchy).filter(([, value]) => value);
97+
const breadcrumbsKeys = hierarchy.map(([key]) => key);
98+
const entryNameKey = breadcrumbsKeys.pop();
99+
100+
const headerHTML = `<h3 class="instantsearch__entry-header">
101+
${instantsearch.highlight({
102+
attribute: `hierarchy.${entryNameKey}`,
103+
highlightedTagName: 'mark',
104+
hit: hit,
105+
})}
106+
</h3>`;
107+
108+
let breadcrumbsHTML = '';
109+
let contentHTML = '';
110+
111+
if (hit.content && hit._highlightResult.content.matchedWords.length && (!hit._highlightResult.content.fullyHighlighted || 1 < hit._highlightResult.content.matchedWords.length)) {
112+
contentHTML = `<div class="instantsearch__entry-content">
113+
${instantsearch.highlight({
114+
attribute: `content`,
115+
highlightedTagName: 'mark',
116+
hit: hit,
117+
}).replaceAll('&amp;', '&')}
118+
</div>`;
119+
}
120+
121+
breadcrumbsKeys?.forEach((breadcrumbKey) => {
122+
breadcrumbsHTML += `<span class="instantsearch__entry-breadcrumbs-item">
123+
${instantsearch.highlight({
124+
attribute: `hierarchy.${breadcrumbKey}`,
125+
highlightedTagName: 'mark',
126+
hit: hit,
127+
})}
128+
</span>`;
129+
});
130+
131+
return resultHTML = `<a class="instantsearch__entry" href="${hit.url}">
132+
<div class="instantsearch__entry-breadcrumbs">
133+
${breadcrumbsHTML}
134+
</div>
135+
${headerHTML}
136+
${contentHTML}
137+
</a>`;
138+
},
139+
},
140+
}),
141+
instantsearch.widgets.pagination({
142+
container: paginationContainer,
143+
padding: 2,
144+
templates: {
145+
first: `<svg class="tile-icon" width="16" height="16">
146+
<use fill="var(--ibexa-dusk-black)" xlink:href="../images/ez-icons.svg#caret-double-back"></use>
147+
</svg>`,
148+
previous: `<svg class="tile-icon" width="20" height="20">
149+
<use fill="var(--ibexa-dusk-black)" xlink:href="../images/ez-icons.svg#caret-back"></use>
150+
</svg>`,
151+
next: `<svg class="tile-icon" width="20" height="20">
152+
<use fill="var(--ibexa-dusk-black)" xlink:href="../images/ez-icons.svg#caret-next"></use>
153+
</svg>`,
154+
last: `<svg class="tile-icon" width="16" height="16">
155+
<use fill="var(--ibexa-dusk-black)" xlink:href="../images/ez-icons.svg#caret-double-next"></use>
156+
</svg>`,
157+
},
158+
}),
159+
instantsearch.widgets.refinementList({
160+
container: document.querySelector('#version'),
161+
attribute: 'version',
162+
}),
163+
]);
164+
165+
search.start();
166+
})(window, window.document);

0 commit comments

Comments
 (0)