Refactor CSS for search results. Add support for keyboard control to full search results page

This commit is contained in:
Magnus Hoff 2017-10-25 16:02:30 +02:00
parent b90d7b9d1e
commit 74b8040d39
5 changed files with 71 additions and 49 deletions

View file

@ -68,25 +68,35 @@ function debouncer(interval, callback) {
form.classList.remove("focus");
});
function moveFocus(delta) {
function moveFocus(element, delta) {
const focusIndexText = document.activeElement.getAttribute("data-focusindex");
if (!focusIndexText) return;
const currentIndex = parseInt(focusIndexText, 10);
const nextIndex = currentIndex + delta;
const nextIndex = focusIndexText ? parseInt(focusIndexText, 10) + delta : 0;
const candidate = form.querySelector("[data-focusindex=\"" + nextIndex + "\"]");
const candidate = element.querySelector("[data-focusindex=\"" + nextIndex + "\"]");
if (candidate) candidate.focus();
}
form.addEventListener('keydown', function (ev) {
function focusControl(element, ev) {
if (ev.key === 'ArrowUp') {
ev.preventDefault();
ev.stopPropagation();
moveFocus(-1);
moveFocus(element, -1);
} else if (ev.key === 'ArrowDown') {
ev.preventDefault();
ev.stopPropagation();
moveFocus(1);
moveFocus(element, 1);
}
});
}
for (let element of document.querySelectorAll(".keyboard-focus-control")) {
const captureElement = element;
element.addEventListener('keydown', ev => focusControl(captureElement, ev));
}
const defaultKeyboardFocusControl = document.querySelector(".default-keyboard-focus-control");
if (defaultKeyboardFocusControl) {
document.addEventListener('keydown', ev => {
focusControl(defaultKeyboardFocusControl, ev);
});
}
})();

View file

@ -6,6 +6,10 @@
url('amatic-sc-v9-latin-regular.woff') format('woff');
}
.prototype {
display: none;
}
html {
font-family: "Apple Garamond", "Baskerville",
"Times New Roman", "Droid Serif", "Times",
@ -292,13 +296,29 @@ article ul.search-results {
}
.search-result {
border: 1px solid #ccc;
padding: 8px 16px;
margin-bottom: 16px;
padding: 0;
margin-bottom: 8px;
}
.search-result .title {
font-weight: bold;
}
.snippet {
white-space: pre-line;
}
.search-result p {
margin: 0;
}
.search-result a {
display: block;
color: inherit;
text-decoration: none;
padding: 8px;
}
.search-result a:hover, .search-result a:focus {
background: #0074D9;
color: white;
}
.search {
text-align: center;
@ -347,33 +367,10 @@ article ul.search-results {
}
.live-results .search-result {
padding: 0;
border-top: none;
margin: 0;
}
.live-results .search-result li {
padding: 0;
}
.live-results .search-result a {
display: block;
color: inherit;
text-decoration: none;
padding: 8px;
}
.live-results .search-result a:hover, .live-results .search-result a:focus {
background: #0074D9;
color: white;
}
.live-results .search-result .title {
font-weight: bold;
}
.prototype {
display: none;
}
@media (min-width: 630px) {
.search {
text-align: right;

View file

@ -128,29 +128,36 @@ impl Resource for SearchResource {
}
fn get(self: Box<Self>) -> ResponseFuture {
#[derive(BartDisplay)]
#[template="templates/search.html"]
struct Template<'a> {
query: &'a str,
hits: Vec<models::SearchResult>,
}
#[derive(Serialize)]
struct JsonResponse<'a> {
query: &'a str,
hits: &'a [models::SearchResult],
}
impl models::SearchResult {
fn link(&self) -> String {
struct Hit<'a> {
index: usize,
slug: &'a str,
title: &'a str,
snippet: &'a str,
}
impl<'a> Hit<'a> {
fn link(&self) -> &'a str {
if self.slug == "" {
".".to_owned()
"."
} else {
self.slug.clone()
self.slug
}
}
}
#[derive(BartDisplay)]
#[template="templates/search.html"]
struct Template<'a> {
query: &'a str,
hits: &'a [Hit<'a>],
}
// TODO: Show a search "front page" when no query is given:
let query = self.query.as_ref().map(|x| x.clone()).unwrap_or("".to_owned());
@ -172,7 +179,15 @@ impl Resource for SearchResource {
title: "Search",
body: &Template {
query: self.query.as_ref().map(|x| &**x).unwrap_or(""),
hits: data,
hits: &data.iter()
.enumerate()
.map(|(i, result)| Hit {
index: i,
slug: &result.slug,
title: &result.title,
snippet: &result.snippet,
})
.collect::<Vec<_>>(),
},
}.to_string())),
}

View file

@ -8,7 +8,7 @@
<link href="_assets/style-{{style_css_checksum()}}.css" rel="stylesheet">
</head>
<body>
<form class=search action=_search method=GET>
<form class="search keyboard-focus-control" action=_search method=GET>
<input data-focusindex="0" type=search name=q placeholder=search autocomplete=off>
<ul class="live-results search-results">
</ul>

View file

@ -7,9 +7,9 @@
{{#hits?}}
<p>Search results for the query <b>{{query}}</b>:</p>
<ul class="search-results">
<ul class="search-results default-keyboard-focus-control">
{{#hits}}
<li class="search-result"><a href="{{.link()}}">{{.title}}</a> &ndash; <span class="snippet">{{.snippet}}</span></li>
<li class="search-result"><a data-focusindex="{{.index}}" class="link" href="{{.link()}}"><p class="title">{{.title}}</p><p class="snippet">{{.snippet}}</p></a></li>
{{/hits}}
</ul>
{{/hits}}