Added page grouping for search results

This commit is contained in:
squidfunk 2017-03-12 17:58:34 +01:00
parent f72a88721e
commit ba3565625d
7 changed files with 163 additions and 98 deletions

View File

@ -90,7 +90,7 @@ let args = yargs
}) })
.option("optimize", { .option("optimize", {
describe: chalk.grey("optimize and minify assets"), describe: chalk.grey("optimize and minify assets"),
default: true, default: false,
global: true global: true
}) })
.option("revision", { .option("revision", {

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -38,7 +38,7 @@
<script src="{{ base_url }}/assets/javascripts/modernizr-56ade86843.js"></script> <script src="{{ base_url }}/assets/javascripts/modernizr-56ade86843.js"></script>
{% endblock %} {% endblock %}
{% block styles %} {% block styles %}
<link rel="stylesheet" href="{{ base_url }}/assets/stylesheets/application-1d1da4857d.css"> <link rel="stylesheet" href="{{ base_url }}/assets/stylesheets/application-b825c62f38.css">
{% if config.extra.palette %} {% if config.extra.palette %}
<link rel="stylesheet" href="{{ base_url }}/assets/stylesheets/application-66fa0d9bba.palette.css"> <link rel="stylesheet" href="{{ base_url }}/assets/stylesheets/application-66fa0d9bba.palette.css">
{% endif %} {% endif %}
@ -151,7 +151,7 @@
{% endblock %} {% endblock %}
</div> </div>
{% block scripts %} {% block scripts %}
<script src="{{ base_url }}/assets/javascripts/application-42beea1040.js"></script> <script src="{{ base_url }}/assets/javascripts/application-095a104376.js"></script>
<script>app.initialize({url:{base:"{{ base_url }}"}})</script> <script>app.initialize({url:{base:"{{ base_url }}"}})</script>
{% for path in extra_javascript %} {% for path in extra_javascript %}
<script src="{{ path }}"></script> <script src="{{ path }}"></script>

View File

@ -107,51 +107,36 @@ export default class Result {
/* eslint-enable no-invalid-this, lines-around-comment */ /* eslint-enable no-invalid-this, lines-around-comment */
}) })
/* /* Preprocess and index sections and documents */
* The MkDocs search index provides all pages as specified in the this.docs_ = data.reduce((docs, doc) => {
* mkdocs.yml in order with an entry for the content of the whole const [path, hash] = doc.location.split("#")
* page followed by separate entries for all subsections also in
* order of appearance.
*/
// 1. Reduce docs so that useless entries are not included, using a /* Associate section with parent document */
// quick spliting hack - only one test is necessary. if (hash) {
const main = [] doc.parent = docs.get(path)
const reduced = data.reduce((docs, doc) => {
if (docs.length && doc.location.split(
`${docs[docs.length - 1].location}#`).length > 1)
main.push(docs.pop())
doc.main = main.length - 1 // main entry
return docs.concat(doc)
}, [])
// now we have the main pages. /* Override page title with document title if first section */
if (doc.parent && !doc.parent.done) {
doc.parent.title = doc.title
doc.parent.text = doc.text
doc.parent.done = true
}
}
// Only return top-level pages /* Some cleanup on the text */
// const reduced = data.reduce((docs, doc) => {
// if (!doc.location.match("#"))
// docs.push(doc)
// return docs
// }, [])
// 2. Trim texts
const trimmed = reduced.map(doc => {
doc.text = doc.text doc.text = doc.text
.replace(/\n/g, " ") /* Remove newlines */ .replace(/\n/g, " ") /* Remove newlines */
.replace(/\s+/g, " ") /* Compact whitespace */ .replace(/\s+/g, " ") /* Compact whitespace */
.replace(/\s+([,.:;!?])/g, /* Correct punctuation */ .replace(/\s+([,.:;!?])/g, /* Correct punctuation */
(_, char) => char) (_, char) => char)
return doc
})
const data2 = trimmed /* Index sections and documents, but skip top-level headline */
if (!doc.parent || doc.parent.title !== doc.title) {
/* Index documents */
this.docs_ = data2.reduce((docs, doc) => {
this.index_.add(doc) this.index_.add(doc)
docs[doc.location] = doc docs.set(doc.location, doc)
}
return docs return docs
}, {}) }, new Map)
} }
/* Initialize index after short timeout to account for transition */ /* Initialize index after short timeout to account for transition */
@ -171,57 +156,102 @@ export default class Result {
while (this.list_.firstChild) while (this.list_.firstChild)
this.list_.removeChild(this.list_.firstChild) this.list_.removeChild(this.list_.firstChild)
/* Perform search on index and render documents */ /* Perform search on index and group sections by document */
const result = this.index_.search(target.value) const result = this.index_
.search(target.value)
.reduce((items, item) => {
const doc = this.docs_.get(item.ref)
if (doc.parent) {
const ref = doc.parent.location
items.set(ref, (items.get(ref) || []).concat(item))
}
return items
}, new Map)
/* Assemble highlight regex from query string */
const match = new RegExp(
`\\b(${target.value.trim().replace(" ", "|")})`, "img")
/* Render results */
result.forEach((items, ref) => {
const doc = this.docs_.get(ref)
// TODO: unify teaser and stuff again and use modifier --document --section
// TODO: write a highlight function which receives a document --> can be abstracted later
/* Append search result */
this.list_.appendChild(
<li class="md-search-result__item">
<a href={doc.location} title={doc.title}
class="md-search-result__link">
<article class="md-search-result__article
md-search-result__article--document">
<h1 class="md-search-result__title">
{{ __html:
doc.title.replace(match, string => `<em>${string}</em>`)
}}
</h1>
<p class="md-search-result__teaser">
{{ __html:
doc.text.replace(match, string => `<em>${string}</em>`)
}}
</p>
</article>
</a>
{items.map(item => {
const section = this.docs_.get(item.ref)
return (
<a href={section.location} title={section.title}
class="md-search-result__link" data-md-rel="anchor">
<article class="md-search-result__article
md-search-result__article--section">
<h1 class="md-search-result__title">
{{ __html:
section.title.replace(match,
string => `<em>${string}</em>`)
}}
</h1>
<p class="md-search-result__teaser">
{{ __html:
section.text.replace(match,
string => `<em>${string}</em>`)
}}
</p>
</article>
</a>
)
})}
</li>
) /* {this.truncate_(doc.text, 140)} */
})
// process results! // process results!
const re = new RegExp(`\\b${target.value}`, "img") // const re = new RegExp(`\\b${target.value}`, "img")
// result.map(item => { // result.map(item => {
// // console.time("someFunction") // // console.time("someFunction")
// text = text.replace(re, "*XXX*") // do in parallel and collect! // text = text.replace(re, "*XXX*") // do in parallel and collect!
// // console.timeEnd("someFunction") // // console.timeEnd("someFunction")
// }) // })
result.forEach(item => { // result.forEach(item => {
const doc = this.data_[item.ref] // const doc = this.docs_[item.ref]
// console.log(item.score) // // console.log(item.score)
//
/* Check if it's a anchor link on the current page */ // /* Check if it's a anchor link on the current page */
let [pathname] = doc.location.split("#") // let [pathname] = doc.location.split("#")
pathname = pathname.replace(/^(\/?\.{2})+/g, "") // pathname = pathname.replace(/^(\/?\.{2})+/g, "")
//
// TODO: match in children but show top level entry with merged // // TODO: match in children but show top level entry with merged
// sentences split top level and main entries! index one top level, // // sentences split top level and main entries! index one top level,
// when there is no child // // when there is no child
//
let text = doc.text // let text = doc.text
// const re = new RegExp(`\\b${ev.target.value}`, "img") // // const re = new RegExp(`\\b${ev.target.value}`, "img")
// console.time("someFunction") // // console.time("someFunction")
text = text.replace(re, string => { // text = text.replace(re, string => {
return `<b style="color: red">${string}</b>` // return `<b style="color: red">${string}</b>`
}) // })
// console.log(text) // })
// console.timeEnd("someFunction")
/* Append search result */
this.list_.appendChild(
<li class="md-search-result__item">
<a href={doc.location} title={doc.title}
class="md-search-result__link" data-md-rel={
pathname === document.location.pathname
? "anchor" : ""}>
<article class="md-search-result__article">
<h1 class="md-search-result__title">
{doc.title}
</h1>
<p class="md-search-result__teaser">
{{ __html: text }}
</p>
</article>
</a>
</li>
) /* {this.truncate_(doc.text, 140)} */
})
/* Bind click handlers for anchors */ /* Bind click handlers for anchors */
const anchors = this.list_.querySelectorAll("[data-md-rel=anchor]") const anchors = this.list_.querySelectorAll("[data-md-rel=anchor]")

View File

@ -346,6 +346,8 @@
// Search result // Search result
.md-search-result { .md-search-result {
color: $md-color-black;
word-break: break-word;
// Search metadata // Search metadata
&__meta { &__meta {
@ -377,42 +379,75 @@
// Link inside item // Link inside item
&__link { &__link {
display: block; display: block;
padding: 0 1.6rem;
transition: background 0.25s; transition: background 0.25s;
overflow: auto; overflow: hidden;
// Hovered link // Hovered link
&:hover { &:hover {
background-color: transparentize($md-color-accent, 0.9); background-color: transparentize($md-color-accent, 0.9);
} }
// Add a little spacing on the last link
&:last-child {
padding-bottom: 0.8rem;
}
}
// Article - document or section
&__article {
position: relative;
padding: 0 1.6rem;
overflow: auto;
// [tablet landscape +]: Increase left indent // [tablet landscape +]: Increase left indent
@include break-from-device(tablet landscape) { @include break-from-device(tablet landscape) {
padding-left: 4.8rem; padding-left: 4.8rem;
} }
// Document
&--document {
padding-left: 0;
// Icon
&::before {
@extend %md-icon, %md-icon__button;
position: absolute;
left: 0;
color: $md-color-black--light;
content: "find_in_page";
} }
// Search result content // Title
&__article { .md-search-result__title {
margin: 1em 0; margin: 1.3rem 0;
}
// Search result title
&__title {
margin-top: 0.5em;
margin-bottom: 0;
color: $md-color-black;
font-size: ms(0); font-size: ms(0);
font-weight: 400; font-weight: 400;
line-height: 1.4; line-height: 1.4;
} }
}
}
// Search result teaser // Title
&__title {
margin: 0.5em 0;
font-size: ms(-1);
font-weight: 700;
line-height: 1.4;
}
// Teaser
&__teaser { &__teaser {
margin: 0.5em 0; margin: 0.5em 0;
color: $md-color-black--light; color: $md-color-black--light;
font-size: ms(-1); font-size: ms(-1);
line-height: 1.4; line-height: 1.4;
word-break: break-word; }
// Highlighting
em {
font-style: normal;
font-weight: 700;
text-decoration: underline;
} }
} }