From 7f5b1f14f1890d4cb62553c4a57aa2e28f581474 Mon Sep 17 00:00:00 2001 From: Silvio Giebl <silvio.giebl@hivemq.com> Date: Sun, 29 Dec 2019 19:19:00 +0100 Subject: [PATCH] Search for sections (configurable via search.heading_level) Added more search configurations Display "no results found" --- _config.yml | 5 ++ _layouts/default.html | 4 ++ _sass/search.scss | 47 ++++++++++++++--- assets/js/just-the-docs.js | 91 +++++++++++++++++++++++---------- assets/js/zzzz-search-data.json | 55 ++++++++++++++++++-- 5 files changed, 161 insertions(+), 41 deletions(-) diff --git a/_config.yml b/_config.yml index a0906708..c021b357 100644 --- a/_config.yml +++ b/_config.yml @@ -26,6 +26,11 @@ exclude: ["node_modules/", "*.gemspec", "*.gem", "Gemfile", "Gemfile.lock", "pac # Enable or disable the site search search_enabled: true +search: + heading_level: 2 + preview_words_before: 5 + preview_words_after: 10 + rel_url: false # Set the search token separator for hyphenated-word search: search_tokenizer_separator: /[\s/]+/ diff --git a/_layouts/default.html b/_layouts/default.html index 4a66b45e..81dd721d 100644 --- a/_layouts/default.html +++ b/_layouts/default.html @@ -24,6 +24,10 @@ layout: table_wrappers <title>Expand</title> <path d="M10 6L8.59 7.41 13.17 12l-4.58 4.59L10 18l6-6z"/><path d="M0 0h24v24H0z" fill="none"/> </symbol> + <symbol id="svg-doc" viewBox="0 0 24 24"> + <title>Document</title> + <path fill="none" d="M0 0h24v24H0V0z"/><path d="M14 2H6c-1.1 0-1.99.9-1.99 2L4 20c0 1.1.89 2 1.99 2H18c1.1 0 2-.9 2-2V8l-6-6zM6 20V4h7v5h5v11H6z"/> + </symbol> </svg> <div class="side-bar"> diff --git a/_sass/search.scss b/_sass/search.scss index 6d476098..54f3211b 100644 --- a/_sass/search.scss +++ b/_sass/search.scss @@ -131,29 +131,50 @@ &.active { background-color: $feedback-color; } - - @include mq(md) { - padding-right: $sp-4; - padding-left: $sp-4; - } } .search-result-title { display: block; padding-top: $sp-2; - padding-right: $sp-4; padding-bottom: $sp-2; @include mq(sm) { display: inline-block; width: 40%; + padding-right: $sp-2; word-wrap: break-word; vertical-align: top; } } +.search-result-doc { + display: flex; + align-items: center; + + &.search-result-doc-parent { + opacity: 0.5; + @include fs-3; + + @include mq(md) { + @include fs-2; + } + } + + .search-result-icon { + width: $sp-4; + height: $sp-4; + margin-right: $sp-2; + fill: $link-color; + } +} + +.search-result-section { + margin-left: #{$sp-4 + $sp-2}; +} + .search-result-rel-url { display: block; + margin-left: #{$sp-4 + $sp-2}; overflow: hidden; color: $search-result-preview-color; text-overflow: ellipsis; @@ -166,6 +187,7 @@ padding-top: $sp-2; padding-bottom: $sp-2; padding-left: $sp-4; + margin-left: $sp-2; color: $search-result-preview-color; border-left: $border; border-left-color: $border-color; @@ -174,13 +196,22 @@ @include mq(sm) { display: inline-block; width: 60%; + padding-left: $sp-2; + margin-left: 0; vertical-align: top; } } .search-result-highlight { font-weight: bold; - color: $link-color; +} + +.search-no-result { + padding-top: $sp-2; + padding-right: $sp-3; + padding-bottom: $sp-2; + padding-left: $sp-3; + @include fs-3; } .search-button { @@ -205,7 +236,7 @@ z-index: 1; width: 0; height: 0; - background-color: rgba(0, 0, 0, 0.2); + background-color: rgba(0, 0, 0, 0.3); opacity: 0; transition: opacity ease $transition-duration, width 0s $transition-duration, height 0s $transition-duration; } diff --git a/assets/js/just-the-docs.js b/assets/js/just-the-docs.js index 49479f6b..0d2593ba 100644 --- a/assets/js/just-the-docs.js +++ b/assets/js/just-the-docs.js @@ -81,7 +81,9 @@ function initSearch() { this.ref('id'); this.field('title', { boost: 200 }); this.field('content', { boost: 2 }); - this.field('url'); + {% if site.search.rel_url != false -%} + this.field('relUrl'); + {%- endif %} this.metadataWhitelist = ['position'] for (var i in docs) { @@ -89,7 +91,9 @@ function initSearch() { id: i, title: docs[i].title, content: docs[i].content, - url: docs[i].url + {% if site.search.rel_url != false -%} + relUrl: docs[i].relUrl + {%- endif %} }); } }); @@ -112,20 +116,29 @@ function initSearch() { var searchInput = document.getElementById('search-input'); var searchResults = document.getElementById('search-results'); var mainHeader = document.getElementById('main-header'); + var currentInput; - function clearResults() { - searchResults.innerHTML = ''; - hideResults(); + function showSearch() { + document.documentElement.classList.add('search-active'); } - function hideResults() { + function hideSearch() { document.documentElement.classList.remove('search-active'); } function update() { - clearResults(); - var input = searchInput.value; + if (input === '') { + hideSearch(); + } else { + showSearch(); + window.scroll(0, window.scrollY + mainHeader.getBoundingClientRect().top); + } + if (input === currentInput) { + return; + } + currentInput = input; + searchResults.innerHTML = ''; if (input === '') { return; } @@ -140,10 +153,13 @@ function initSearch() { }); }); - if (results.length > 0) { - window.scroll(0, window.scrollY + mainHeader.getBoundingClientRect().top); - document.documentElement.classList.add('search-active'); + if (results.length == 0) { + var noResultsDiv = document.createElement('div'); + noResultsDiv.classList.add('search-no-result'); + noResultsDiv.innerText = 'No results found'; + searchResults.appendChild(noResultsDiv); + } else { var resultsList = document.createElement('ul'); resultsList.classList.add('search-results-list'); searchResults.appendChild(resultsList); @@ -163,13 +179,26 @@ function initSearch() { var resultTitle = document.createElement('div'); resultTitle.classList.add('search-result-title'); - resultTitle.innerText = doc.title; resultLink.appendChild(resultTitle); - var resultRelUrl = document.createElement('span'); - resultRelUrl.classList.add('search-result-rel-url'); - resultRelUrl.innerText = doc.relUrl; - resultTitle.appendChild(resultRelUrl); + var resultDoc = document.createElement('div'); + resultDoc.classList.add('search-result-doc'); + resultDoc.innerHTML = '<svg viewBox="0 0 24 24" class="search-result-icon"><use xlink:href="#svg-doc"></use></svg>'; + resultTitle.appendChild(resultDoc); + + var resultDocSpan = document.createElement('span'); + resultDocSpan.innerText = doc.doc; + resultDoc.appendChild(resultDocSpan); + var resultDocOrSection = resultDocSpan; + + if (doc.doc != doc.title) { + resultDoc.classList.add('search-result-doc-parent'); + var resultSection = document.createElement('div'); + resultSection.classList.add('search-result-section'); + resultSection.innerText = doc.title; + resultTitle.appendChild(resultSection); + resultDocOrSection = resultSection; + } var metadata = result.matchData.metadata; var contentFound = false; @@ -178,9 +207,9 @@ function initSearch() { var position = metadata[j].title.position[0]; var start = position[0]; var end = position[0] + position[1]; - resultTitle.innerHTML = doc.title.substring(0, start) + '<span class="search-result-highlight">' + doc.title.substring(start, end) + '</span>' + doc.title.substring(end, doc.title.length)+'<span class="search-result-rel-url">'+doc.relUrl+'</span>'; - - } else if (metadata[j].content && !contentFound) { + resultDocOrSection.innerHTML = doc.title.substring(0, start) + '<span class="search-result-highlight">' + doc.title.substring(start, end) + '</span>' + doc.title.substring(end, doc.title.length); + } + if (metadata[j].content && !contentFound) { contentFound = true; var position = metadata[j].content.position[0]; @@ -190,10 +219,10 @@ function initSearch() { var previewEnd = end; var ellipsesBefore = true; var ellipsesAfter = true; - for (var k = 0; k < 3; k++) { + for (var k = 0; k < {{ site.search.preview_words_before | default: 5 }}; k++) { var nextSpace = doc.content.lastIndexOf(' ', previewStart - 2); - var nextDot = doc.content.lastIndexOf('.', previewStart - 2); - if ((nextDot > 0) && (nextDot > nextSpace)) { + var nextDot = doc.content.lastIndexOf('. ', previewStart - 2); + if ((nextDot >= 0) && (nextDot > nextSpace)) { previewStart = nextDot + 1; ellipsesBefore = false; break; @@ -205,10 +234,10 @@ function initSearch() { } previewStart = nextSpace + 1; } - for (var k = 0; k < 10; k++) { + for (var k = 0; k < {{ site.search.preview_words_after | default: 10 }}; k++) { var nextSpace = doc.content.indexOf(' ', previewEnd + 1); - var nextDot = doc.content.indexOf('.', previewEnd + 1); - if ((nextDot > 0) && (nextDot < nextSpace)) { + var nextDot = doc.content.indexOf('. ', previewEnd + 1); + if ((nextDot >= 0) && (nextDot < nextSpace)) { previewEnd = nextDot; ellipsesAfter = false; break; @@ -236,6 +265,13 @@ function initSearch() { resultLink.appendChild(resultPreview); } } + + {% if site.search.rel_url != false -%} + var resultRelUrl = document.createElement('span'); + resultRelUrl.classList.add('search-result-rel-url'); + resultRelUrl.innerText = doc.relUrl; + resultTitle.appendChild(resultRelUrl); + {%- endif %} } } } @@ -247,9 +283,8 @@ function initSearch() { jtd.addEvent(searchInput, 'keyup', function(e){ switch (e.keyCode) { case 27: // When esc key is pressed, hide the results and clear the field - clearResults(); searchInput.value = ''; - return; + break; case 38: // arrow up case 40: // arrow down case 13: // enter @@ -305,7 +340,7 @@ function initSearch() { jtd.addEvent(document, 'click', function(e){ if (e.target != searchInput) { - hideResults(); + hideSearch(); } }); } diff --git a/assets/js/zzzz-search-data.json b/assets/js/zzzz-search-data.json index d38a42f3..d336e8c5 100644 --- a/assets/js/zzzz-search-data.json +++ b/assets/js/zzzz-search-data.json @@ -2,12 +2,57 @@ permalink: /assets/js/search-data.json --- { - {% assign comma = false %} - {% for page in site.html_pages %}{% if page.search_exclude != true %}{% if comma == true%},{% endif %}"{{ forloop.index0 }}": { + {%- assign i = 0 -%} + {% for page in site.html_pages %} + {%- if page.title and page.search_exclude != true -%} + {%- assign page_content = page.content -%} + {%- assign heading_level = site.search.heading_level | default: 1 -%} + {%- for j in (2..heading_level) -%} + {%- assign tag = '<h' | append: j -%} + {%- assign closing_tag = '</h' | append: j -%} + {%- assign page_content = page_content | replace: tag, '<h1' | replace: closing_tag, '</h1' -%} + {%- endfor -%} + {%- assign parts = page_content | split: '<h1' -%} + {%- assign title_found = false -%} + {% for part in parts offset: 1 %} + {%- assign titleAndContent = part | split: '</h1>' -%} + {%- assign title = titleAndContent[0] | replace_first: '>', '<h1>' | split: '<h1>' -%} + {%- assign title = title[1] | strip_html -%} + {%- assign content = titleAndContent[1] -%} + {%- assign url = page.url -%} + {%- if title == page.title and parts[0] == '' -%} + {%- assign title_found = true -%} + {%- else -%} + {%- assign id = titleAndContent[0] -%} + {%- assign id = id | split: 'id="' -%} + {%- if id.size == 2 -%} + {%- assign id = id[1] -%} + {%- assign id = id | split: '"' -%} + {%- assign id = id[0] -%} + {%- capture url -%}{{ url | append: '#' | append: id }}{%- endcapture -%} + {%- endif -%} + {%- endif -%} + {%- unless i == 0 -%},{%- endunless -%} + "{{ i }}": { + "doc": "{{ page.title | escape_once }}", + "title": "{{ title | escape_once }}", + "content": "{{ content | replace: '</h', ' . </h' | replace: '<hr', ' . <hr' | replace: '</p', ' . </p' | replace: '<ul', ' . <ul' | replace: '</ul', ' . </ul' | replace: '<ol', ' . <ol' | replace: '</ol', ' . </ol' | replace: '</tr', ' . </tr' | replace: '<li', ' | <li' | replace: '</li', ' | </li' | replace: '</td', ' | </td' | replace: '<td', ' | <td' | replace: '</th', ' | </th' | replace: '<th', ' | <th' | strip_html | escape_once | remove: 'Table of contents' | replace: '\', '\' | replace: ' . . . ', ' . ' | replace: ' . . ', ' . ' | normalize_whitespace | replace: '| |', '|' }}", + "url": "{{ url | absolute_url }}", + "relUrl": "{{ url }}" + } + {%- assign i = i | plus: 1 -%} + {%- endfor -%} + {%- unless title_found -%} + {%- unless i == 0 -%},{%- endunless -%} + "{{ i }}": { + "doc": "{{ page.title | escape_once }}", "title": "{{ page.title | escape_once }}", - "content": "{{ page.content | replace: '</h', ' . </h' | replace: '<hr', ' . <hr' | replace: '</p', ' . </p' | replace: '</ul', ' . </ul' | replace: '</tr', ' . </tr' | replace: '</li', ' | </li' | replace: '</td', ' | </td' | strip_html | escape_once | remove: 'Table of contents' | replace: '\', ' ' | replace: ' . . . ', ' . ' | replace: ' . . ', ' . ' | normalize_whitespace }}", + "content": "{{ parts[0] | replace: '</h', ' . </h' | replace: '<hr', ' . <hr' | replace: '</p', ' . </p' | replace: '<ul', ' . <ul' | replace: '</ul', ' . </ul' | replace: '<ol', ' . <ol' | replace: '</ol', ' . </ol' | replace: '</tr', ' . </tr' | replace: '<li', ' | <li' | replace: '</li', ' | </li' | replace: '</td', ' | </td' | replace: '<td', ' | <td' | replace: '</th', ' | </th' | replace: '<th', ' | <th' | strip_html | escape_once | remove: 'Table of contents' | replace: '\', '\' | replace: ' . . . ', ' . ' | replace: ' . . ', ' . ' | normalize_whitespace | replace: '| |', '|' }}", "url": "{{ page.url | absolute_url }}", "relUrl": "{{ page.url }}" - }{% assign comma = true %} - {% endif %}{% endfor %} + } + {%- assign i = i | plus: 1 -%} + {%- endunless -%} + {%- endif -%} + {% endfor %} } \ No newline at end of file -- GitLab