Browse Source

add Meilisearch JavaScript and CSS files.

These files are provided by the Meilisearch project. This commit is larger than
usual because of this. The CSS files are copied over from my personal website's
repository so they will need modifying going forward. I've added them here as a
starting point.
stable
Craig Oates 2 years ago
parent
commit
ad0954e319
  1. 418
      static/css/full-search.css
  2. 121
      static/css/search.css
  3. 97
      static/js/full-search.js
  4. 29
      static/js/instant-meilisearch-0.8.0.js
  5. 3
      static/js/instantsearch-4.43.1.js
  6. 101
      static/js/quick-search.js

418
static/css/full-search.css

@ -0,0 +1,418 @@
.search-dashboard {
margin: 0px;
padding: 0px;
display: flex;
flex-direction: column;
}
.refinements-panel h2,
.refinements-panel p {
padding: 0px;
margin: 0px;
}
.refinements-panel p {
color : silver;
font-size: 12px;
}
#clear-refinements {
margin-bottom: 20px;
}
.ais-ClearRefinements-button {
font-size: 16px;
height: 40px;
width: 100%;
margin: 0px;
font-family: 'main', sans-serif;
color: white;
background-color: #0094ff;
box-shadow: 2px 2px 1px black;
border-radius: 4px;
padding: 10px;
cursor: pointer;
text-transform: uppercase;
border: none;
}
.ais-ClearRefinements-button:hover {
background: lightblue;
color: #0094ff;
text-decoration: none;
box-shadow: none;
}
.ais-ClearRefinements-button--disabled {
text-align: center;
padding: 10px;
border: 2px solid silver;
border-radius: 4px;
text-transform: uppercase;
color: silver;
background: transparent;
box-shadow: none;
}
.ais-ClearRefinements-button--disabled:hover {
background: transparent;
color: silver;
}
.search-refinement-list {
margin: 20px 0px;
border-bottom: 2px solid black;
}
#searchbox {
width: 100%;
margin-top: 20px;
margin-bottom: 20px;
margin-left: auto;
margin-right: auto;
}
.ais-SearchBox,
.ais-ClearRefinements-button {
max-width: 600px;
}
.search-results-container {
display: flex;
flex-direction: column;
}
.ais-SearchBox-form {
display: flex;
flex-wrap: nowrap;
justify-content: center;
align-items: center;
}
.ais-SearchBox-input {
width: 100%;
height: 40px;
padding: 10px;
border: 2px solid #0094ff;
border-radius: 4px;
font-family: 'main', sans-serif;
font-size: 16px;
}
.ais-SearchBox-input::placeholder {
font-family: 'main', sans-serif;
font-size: 16px;
}
.ais-SearchBox-form input:focus-visible {
outline: none;
}
.ais-SearchBox-submitIcon,
.ais-SearchBox-submit,
.ais-SearchBox-reset {
display: none;
}
.ais-RefinementList-list {
list-style: none;
padding: 0px;
margin: 0px 0px 40px 0px;
display: flex;
flex-direction: row;
width: 100%;
overflow: scroll;
}
.ais-RefinementList-label {
display: flex;
align-items: center ;
width: max-content;
margin-right: 6px;
}
.ais-RefinementList-label > span {
padding: 0px 2px;
}
.ais-RefinementList-checkbox {
width: 30px;
height: 30px;
margin: 0px;
}
.search-hits-panel {
width: -webkit-fill-available;
}
#hits {
width: 100%;
min-width: 350px;
display: flex;
flex-direction: column;
}
.ais-Hits-list {
padding: 0px;
margin: 0px;
display: flex;
flex-direction: column;
list-style: none;
}
.ais-Hits-item {
margin: 12px 0px 12px 0px;
max-height: 400px;
border: 2px #0094ff solid;
border-radius: 4px;
overflow: hidden;
display: flex;
flex-direction: column;
box-shadow: 3px 3px 3px black;
}
.ais-Hits-item:hover {
border-color: lightblue;
box-shadow: none;
}
#pagination {
display: block;
margin: 40px 0px 40px 0px;
overflow: auto;
width: 100%;
}
.ais-Pagination-list {
list-style: none;
padding: 0px;
margin: 0px;
display: flex;
justify-content: center;
align-items: center;
}
.ais-Pagination-item {
margin: 0px;
display: flex;
justify-content: center;
align-items: center;
max-width: 64px;
height: 50px;
}
/* Hack to change content of pagination links
================================================================================
https://stackoverflow.com/questions/48907242/how-can-i-remove-and-replace-content-in-a-html-tag-using-css
Because Meilsearch adds the pagination stuff via JavaScript, I can't set the
contents of the various HTML elements via the djula templates. This means I have
to update the content after it's loaded. I didn't want to do this via JavaScript
because I want to keep the JavaScript to a minimum. So, to fix the problem, I
came across a hack (see Stack Overflow URL above). You need to change the
visibility (to hidden) of the element(s) you want to change and then use the
'content' property to set the new text value. When that is done, you adjust the
element's visibility back to 'visible'.
I ACKNOWLEDGE THIS IS JACKY BUT IT WORKS.
*/
.ais-Pagination-item--firstPage a,
.ais-Pagination-item--previousPage a,
.ais-Pagination-item--nextPage a {
visibility: hidden;
}
/* Meilisearch not designed for extended pagination use (disabled 'Last' link)
================================================================================
https://github.com/meilisearch/documentation/issues/561
Meilisearch adds a 'Last Page' link with its built-in pagination features. With
that said, the people developing the project acknowledge Meilisearch's
pagination features are limited. They recommend on not using or relying on it
too much. See the URL above for more information.
Whilst developing this part of the site, I found the 'Last Page' link was
behaving inconsistently. You would click the link and it would jump ahead
(I.E. more than one page) but it took several clicks to get to the 'final/last
page'. Because of that, I've just turned it off by hiding it with the
style-rule below. Again, see URL above for more info. on this behaviour.
*/
.ais-Pagination-item.ais-Pagination-item--lastPage {
display: none;
}
.ais-Pagination-item--firstPage.ais-Pagination-item--disabled span,
.ais-Pagination-item--previousPage.ais-Pagination-item--disabled span,
.ais-Pagination-item--nextPage.ais-Pagination-item--disabled span {
visibility: hidden;
}
/* The new text is set here: See 'Hack to change content of pagination links'
note above for more information. */
.ais-Pagination-item--firstPage.ais-Pagination-item--disabled span:before {
content: "First";
visibility: visible;
}
.ais-Pagination-item--previousPage.ais-Pagination-item--disabled span:before {
content: "Prev.";
visibility: visible;
}
.ais-Pagination-item--nextPage.ais-Pagination-item--disabled span:before {
content: "Next";
visibility: visible;
margin-left: 8px;
}
.ais-Pagination-item--firstPage.ais-Pagination-item--disabled span:before,
.ais-Pagination-item--previousPage.ais-Pagination-item--disabled span:before,
.ais-Pagination-item--nextPage.ais-Pagination-item--disabled span:before {
width: 70px;
height: 39px;
text-transform: uppercase;
border: 2px solid silver;
border-radius: 4px;
text-align: center;
padding: 8px;
color: silver;
}
.ais-Pagination-item--firstPage a:before {
content: "First";
visibility: visible;
}
.ais-Pagination-item--previousPage a:before {
content: "Prev.";
visibility: visible;
}
.ais-Pagination-item--nextPage a:before {
content: "Next";
visibility: visible;
margin-left: 8px;
}
.ais-Pagination-item--firstPage a:link::before,
.ais-Pagination-item--previousPage a:link::before,
.ais-Pagination-item--nextPage a:link::before {
width: 100%;
height: 39px;
text-transform: uppercase;
background: #0094ff;
color: white;
border: 2px solid #0094ff;
border-radius: 4px;
text-align: center;
padding: 8px;
text-decoration: none;
box-shadow: 2px 2px 3px black;
}
.ais-Pagination-item--page a:link {
background: #0094ff;
color: white;
padding: 10px;
border-radius: 4px;
text-transform: uppercase;
text-decoration: none;
margin: 0px 2px;
box-shadow: 2px 2px 3px black;
}
.ais-Pagination-item--selected a:link,
.ais-Pagination-item--selected a:hover {
background: silver !important;
color: white;
box-shadow: none;
}
.ais-Pagination-item--page a:hover {
background: lightblue;
text-decoration: none;
box-shadow: none;
}
.ais-Pagination-item--firstPage a:hover::before,
.ais-Pagination-item--previousPage a:hover::before,
.ais-Pagination-item--nextPage a:hover::before {
background: lightblue;
border: 2px solid lightblue;
border-radius: 4px;
text-align: center;
color: #0094ff;
box-shadow: none;
}
.ais-Pagination-item--firstPage a:hover,
.ais-Pagination-item--previousPage a:hover,
.ais-Pagination-item--nextPage a:hover {
text-decoration: none !important;
}
@media (min-width:600px) {
.ais-ClearRefinements-button {
max-width: 200px;
}
}
@media (min-width:961px) {
.search-results-container {
flex-direction: row;
}
.refinements-panel {
max-width: 300px;
width: 100%;
margin-right: 20px;
}
.ais-RefinementList-list {
font-size: 12px;
flex-direction: column;
overflow: auto;
}
.ais-RefinementList-item {
margin: 2px 0px;
}
.ais-RefinementList-checkbox {
width: 20px;
margin-right: 4px;
}
#hits {
flex-direction: row;
flex-wrap: wrap;
}
.ais-Hits-list {
padding: 0px;
margin: 0px;
display: flex;
flex-direction: row;
flex-wrap: wrap;
list-style: none;
}
.ais-Hits-item {
width: 200px;
height: 280px;
margin: 12px;
height: 100%;
display: flex;
flex-direction: column;
}
.ais-Hits-item .ui-link-card .ui-card-text {
height: 75px;
/* white-space: nowrap; */
text-overflow: ellipsis;
}
.ais-Hits-item .ui-link-card .ui-card-text .ui-card-secondary {
text-overflow: ellipsis;
}
}

121
static/css/search.css

@ -0,0 +1,121 @@
.ui-search-container {
margin: 0px;
display: flex;
position: abosolute;
flex-direction: column;
align-items: center;
height: 120px;
border-bottom: 2px solid black;
}
#searchbox {
position: relative;
top: 40px;
width: 100%;
max-width: 600px;
}
.ais-SearchBox-form {
display: flex;
flex-wrap: nowrap;
justify-content: center;
align-items: center;
}
.ais-SearchBox-input {
width: 100%;
height: 40px;
padding: 10px;
border: 2px solid #0094ff;
border-radius: 4px 4px 0px 0px;
font-family: 'main', sans-serif;
font-size: 16px;
}
.ais-SearchBox-input::placeholder {
font-family: 'main', sans-serif;
font-size: 16px;
}
.ais-SearchBox-form input:focus-visible {
outline: none;
}
.ais-SearchBox-submit {
font-size: 16px;
height: 40px;
margin: 0px;
font-family: 'main', sans-serif;
color: white;
background-color: #0094ff;
border-radius: 4px;
padding: 10px;
cursor: pointer;
text-transform: uppercase;
border: none;
}
.ais-SearchBox-submitIcon,
.ais-SearchBox-submit,
.ais-SearchBox-reset,
#hits {
display: none;
}
#hits {
position: relative;
top:40px;
background: white;
width: calc(100% - 4px);
max-width: calc(600px - 4px);
border-right: 2px solid #0094ff;
border-bottom: 2px solid #0094ff;
border-left: 2px solid #0094ff;
border-radius: 0px 0px 4px 4px;
}
.ais-Hits-list {
list-style: none;
padding: 0px 2px;
margin: 0px;
}
.ais-Hits-list:first-child {
display: flex;
flex-wrap: nowrap;
flex-direction: column;
justify-content: flex-start;
}
.ais-Hits-item {
margin: 4px 0px 0px 0px;
}
.ui-link-search-card {
overflow: hidden;
white-space: nowrap;
text-overflow: ellipsis;
width: 100%;
}
.ui-link-search-card:link {
text-decoration: none;
float: left;
border-radius: 0px;
padding: 4px 0px;
}
.ui-link-search-card img {
width: 30px;
height: 30px;
display: inline;
float: left;
padding: 0px 6px;
}
.ui-link-search-card span {
vertical-align: middle;
}

97
static/js/full-search.js

@ -0,0 +1,97 @@
/**
Full Search
================================================================================
This file's main focus in providing the Meilisearch features to the
search/index.html template.
For more information on Meilisearch, use:
- https://docs.meilisearch.com/
- https://github.com/meilisearch/instant-meilisearch
Meilisearch provides an 'InstantSearch' add-on (plug-in?) which
links the Meilisearch instance you have running to this website (link above for
more information).
*/
let server = "";
let apiKey = "";
if (location.hostname === "localhost"
|| location.hostname === "127.0.0.1"
|| location.hostname === "beta.nera.com") {
server = "http://localhost:7700";
apiKey= "meilisearch-beta-key";
} else {
server = "https://www.nera.net";
apiKey = "meilisearch-production-key-nera";
}
const search = instantsearch({
indexName: "nera",
searchClient: instantMeiliSearch(
server,
apiKey,
{
primaryKey: 'id',
}
)
});
search.addWidgets([
instantsearch.widgets.searchBox({
container: "#searchbox",
placeholder: "Search...",
}),
instantsearch.widgets.clearRefinements({
container: "#clear-refinements"
}),
instantsearch.widgets.refinementList({
container: "#year-list",
attribute: "year"
}),
instantsearch.widgets.refinementList({
container: "#month-list",
attribute: "month"
}),
instantsearch.widgets.refinementList({
container: "#keywords-list",
attribute: "keywords"
}),
instantsearch.widgets.configure({
hitsPerPage: 30,
snippetEllipsisText: "...",
attributesToSnippet: ["title:100"]
}),
instantsearch.widgets.hits({
container: "#hits",
templates: {
item: `
<a class="ui-link-card" href="{{server}}/{{relative-path}}">
<div>
<img src="{{server}}/{{thumbnail-path}}"/>
<div class="ui-card-text">
<span class="ui-card-title">
{{#helpers.highlight}}{"attribute": "title"}{{/helpers.highlight}}
</span>
<span class="ui-card-secondary">
{{#helpers.highlight}}{"attribute": "year"}{{/helpers.highlight}}
</span>
<span class="ui-card-secondary">
{{#helpers.highlight}}{"attribute": "month"}{{/helpers.highlight}}
</span>
<span class="ui-card-keywords">
{{keywords}}
</span>
</div>
</div>
</a>
`
}
}),
instantsearch.widgets.pagination({
container: "#pagination"
})
]);
search.start();

29
static/js/instant-meilisearch-0.8.0.js

File diff suppressed because one or more lines are too long

3
static/js/instantsearch-4.43.1.js

File diff suppressed because one or more lines are too long

101
static/js/quick-search.js

@ -0,0 +1,101 @@
/**
Quick Search
================================================================================
This file's main focus is to provide the functionality for the 'quick search'
bar feature, which is available across the site. The search features are
provided by Meilisearch. Use the links below for more information.
For more information on Meilisearch, use:
- https://docs.meilisearch.com/
- https://github.com/meilisearch/instant-meilisearch
Meilisearch provides an 'InstantSearch' add-on (plug-in?) which links the
Meilisearch instance you have running to this website (link above for more
information).
*/
let server = "";
let apiKey = "";
if (location.hostname === "localhost"
|| location.hostname === "127.0.0.1"
|| location.hostname === "beta.craigoates.net") {
server = "http://beta-search.craigoates.net";
apiKey= "meilisearch-beta-key";
} else {
server = "https://search.craigoates.net";
apiKey = "meilisearch-production-key-26-07-2022";
}
const search = instantsearch({
indexName: "project",
searchClient: instantMeiliSearch(
server,
apiKey,
{
primaryKey: 'id',
}
)
});
search.addWidgets([
instantsearch.widgets.searchBox({
container: "#searchbox",
autofocus: false,
placeholder: "Quick Search...",
}),
instantsearch.widgets.configure({ hitsPerPage: 8 }),
instantsearch.widgets.hits({
container: "#hits",
templates: {
item: `
<a class="ui-link-search-card" href="{{server}}/{{relative-path}}">
<img src="{{server}}/{{thumbnail-path}}"/>
<span>{{#helpers.highlight}}{"attribute": "title"}{{/helpers.highlight}}</span>
</a>
`
}
})
]);
search.start();
const searchBox =
document.querySelector(".ais-SearchBox-input");
const searchResultsBox =
document.getElementById('hits');
const searchResultsList =
document.querySelector(".ais-Hits-list");
searchBox.addEventListener("keydown", showSearchResults);
document.onmousedown = () => {
if (document.activeElement !== searchBox) {
hideSearchResults();
}
};
function showSearchResults() {
document.getElementById('hits').style.display = 'block';
document.onkeydown = (evt) => {
if (evt.keyCode === 9) {
searchResultsBox.focus();
}
if (evt.keyCode === 27) {
searchBox.blur();
hideSearchResults();
}
};
document.onkeyup = (evt) => {
if (searchBox.value.length == 0) {
hideSearchResults();
}
};
}
function hideSearchResults() {
document.getElementById('hits').style.display = 'none';
}
Loading…
Cancel
Save