feat: add optional page description to search (#550)

Add optional page description to the search result list. The feature can be enabled by `geekdocSearchShowDescription=true` and is disabled by default. The max length of the description is set to `55` and will be truncated automatically if the limit is exceeded.
This commit is contained in:
Robert Kaussow 2022-12-07 08:57:41 +01:00 committed by GitHub
parent d82d05fffc
commit fb905bd6c1
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
12 changed files with 87 additions and 91 deletions

View file

@ -3,5 +3,6 @@
{ {
"dataFile": {{ $searchData.RelPermalink | jsonify }}, "dataFile": {{ $searchData.RelPermalink | jsonify }},
"indexConfig": {{ .Site.Params.GeekdocSearchConfig | jsonify }}, "indexConfig": {{ .Site.Params.GeekdocSearchConfig | jsonify }},
"showParent": {{ if .Site.Params.GeekdocSearchShowParent }}true{{ else }}false{{ end }} "showParent": {{ if .Site.Params.GeekdocSearchShowParent }}true{{ else }}false{{ end }},
"showDescription": {{ if .Site.Params.GeekdocSearchshowDescription }}true{{ else }}false{{ end }}
} }

View file

@ -6,7 +6,8 @@
"href": "{{ $page.RelPermalink }}", "href": "{{ $page.RelPermalink }}",
"title": {{ (partial "utils/title" $page) | jsonify }}, "title": {{ (partial "utils/title" $page) | jsonify }},
"parent": {{ with $page.Parent }}{{ (partial "utils/title" .) | jsonify }}{{ else }}""{{ end }}, "parent": {{ with $page.Parent }}{{ (partial "utils/title" .) | jsonify }}{{ else }}""{{ end }},
"content": {{ $page.Plain | jsonify }} "content": {{ $page.Plain | jsonify }},
"description": {{ $page.Summary | plainify | jsonify }}
} }
{{ end }} {{ end }}
] ]

View file

@ -14,6 +14,7 @@ geekdocEditPath: edit/main/exampleSite
geekdocSearch: true geekdocSearch: true
geekdocSearchShowParent: true geekdocSearchShowParent: true
geekdocSearchShowDescription: true
geekdocLegalNotice: https://thegeeklab.de/legal-notice/#contact-information geekdocLegalNotice: https://thegeeklab.de/legal-notice/#contact-information
geekdocPrivacyPolicy: https://thegeeklab.de/legal-notice/#privacy-policy geekdocPrivacyPolicy: https://thegeeklab.de/legal-notice/#privacy-policy

View file

@ -5,13 +5,12 @@
type="text" type="text"
id="gdoc-search-input" id="gdoc-search-input"
class="gdoc-search__input" class="gdoc-search__input"
placeholder="{{ i18n "form_placeholder_search" }}" placeholder="{{ i18n "form_placeholder_search" }}..."
aria-label="{{ i18n "form_placeholder_search" }}" aria-label="{{ i18n "form_placeholder_search" }}"
maxlength="64" maxlength="64"
data-site-base-url="{{ .Site.BaseURL }}" data-site-base-url="{{ .Site.BaseURL }}"
data-site-lang="{{ .Site.Language.Lang }}" data-site-lang="{{ .Site.Language.Lang }}"
/> />
<div class="gdoc-search__spinner spinner hidden"></div>
<ul id="gdoc-search-results" class="gdoc-search__list"></ul> <ul id="gdoc-search-results" class="gdoc-search__list"></ul>
</div> </div>
{{ end }} {{ end }}

1
package-lock.json generated
View file

@ -13,6 +13,7 @@
"clipboard": "2.0.11", "clipboard": "2.0.11",
"flexsearch": "0.7.31", "flexsearch": "0.7.31",
"katex": "0.16.3", "katex": "0.16.3",
"lodash": "4.17.21",
"mermaid": "9.2.2", "mermaid": "9.2.2",
"store2": "2.14.2" "store2": "2.14.2"
}, },

View file

@ -35,6 +35,7 @@
"clipboard": "2.0.11", "clipboard": "2.0.11",
"flexsearch": "0.7.31", "flexsearch": "0.7.31",
"katex": "0.16.3", "katex": "0.16.3",
"lodash": "4.17.21",
"mermaid": "9.2.2", "mermaid": "9.2.2",
"store2": "2.14.2" "store2": "2.14.2"
}, },

View file

@ -1,31 +0,0 @@
/**
* Part of [Canivete](http://canivete.leofavre.com/#deepgroupby)
*
* Groups the contents of an array by one or more iteratees.
* Unlike Lodash [`groupBy()`](https://lodash.com/docs/4.17.4#groupBy),
* this function can create nested groups, but cannot receive
* strings for iteratees.
*/
export const groupBy = (e, ...t) => {
let r = e.map((e) => t.map((t) => t(e))),
a = {}
return (
r.forEach((t, r) => {
let l = (_simpleAt(a, t) || []).concat([e[r]])
_simpleSet(a, t, l)
}),
a
)
},
_isPlainObject = (e) => null != e && "object" == typeof e && e.constructor == Object,
_parsePath = (e) => (Array.isArray(e) ? e : `${e}`.split(".")),
_simpleAt = (e, t) =>
_parsePath(t).reduce((e, t) => (null != e && e.hasOwnProperty(t) ? e[t] : void 0), e),
_simpleSet = (e, t, r) =>
_parsePath(t).reduce((e, t, a, l) => {
let s = a === l.length - 1
return (
(e.hasOwnProperty(t) && (s || _isPlainObject(e[t]))) || (e[t] = {}), s ? (e[t] = r) : e[t]
)
}, e)

View file

@ -1,4 +1,5 @@
const { groupBy } = require("./groupBy") const groupBy = require("lodash/groupBy")
const truncate = require("lodash/truncate")
const { FlexSearch } = require("flexsearch/dist/flexsearch.compact") const { FlexSearch } = require("flexsearch/dist/flexsearch.compact")
const { Validator } = require("@cfworker/json-schema") const { Validator } = require("@cfworker/json-schema")
@ -19,6 +20,9 @@ document.addEventListener("DOMContentLoaded", function (event) {
}, },
showParent: { showParent: {
type: "boolean" type: "boolean"
},
showDescription: {
type: "boolean"
} }
}, },
additionalProperties: false additionalProperties: false
@ -58,8 +62,8 @@ function init(input, searchConfig) {
indexCfg.document = { indexCfg.document = {
key: "id", key: "id",
index: ["title", "content"], index: ["title", "content", "description"],
store: ["title", "href", "parent"] store: ["title", "href", "parent", "description"]
} }
const index = new FlexSearch.Document(indexCfg) const index = new FlexSearch.Document(indexCfg)
@ -75,7 +79,7 @@ function init(input, searchConfig) {
function search(input, results, searchConfig) { function search(input, results, searchConfig) {
const searchCfg = { const searchCfg = {
enrich: true, enrich: true,
limit: 10 limit: 5
} }
while (results.firstChild) { while (results.firstChild) {
@ -106,8 +110,12 @@ function search(input, results, searchConfig) {
title = item.appendChild(document.createElement("span")), title = item.appendChild(document.createElement("span")),
subList = item.appendChild(document.createElement("ul")) subList = item.appendChild(document.createElement("ul"))
if (!section) {
title.remove()
}
title.classList.add("gdoc-search__section")
title.textContent = section title.textContent = section
createLinks(searchHits[section], subList) createLinks(searchHits[section], subList, searchConfig.showDescription)
items.push(item) items.push(item)
} }
@ -117,7 +125,7 @@ function search(input, results, searchConfig) {
subList = item.appendChild(document.createElement("ul")) subList = item.appendChild(document.createElement("ul"))
title.textContent = "Results" title.textContent = "Results"
createLinks(searchHits, subList) createLinks(searchHits, subList, searchConfig.showDescription)
items.push(item) items.push(item)
} }
@ -133,20 +141,28 @@ function search(input, results, searchConfig) {
* @param {HTMLElement} target Element to which the links should be attatched * @param {HTMLElement} target Element to which the links should be attatched
* @returns {Array} If target is not specified, returns an array of built links * @returns {Array} If target is not specified, returns an array of built links
*/ */
function createLinks(pages, target) { function createLinks(pages, target, showDesc) {
const items = [] const items = []
for (const page of pages) { for (const page of pages) {
const item = document.createElement("li"), const item = document.createElement("li"),
entry = item.appendChild(document.createElement("span")), a = item.appendChild(document.createElement("a")),
a = entry.appendChild(document.createElement("a")) entry = a.appendChild(document.createElement("span"))
entry.classList.add("flex")
a.href = page.href a.href = page.href
a.textContent = page.title entry.classList.add("gdoc-search__entry--title")
entry.textContent = page.title
a.classList.add("gdoc-search__entry") a.classList.add("gdoc-search__entry")
if (showDesc === true) {
const desc = a.appendChild(document.createElement("span"))
desc.classList.add("gdoc-search__entry--description")
desc.textContent = truncate(page.description, {
length: 55,
separator: " "
})
}
if (target) { if (target) {
target.appendChild(item) target.appendChild(item)
continue continue

View file

@ -228,7 +228,6 @@ svg.gdoc-icon {
.gdoc-nav { .gdoc-nav {
flex: 0 0 $menu-width; flex: 0 0 $menu-width;
font-size: $font-size-14;
nav { nav {
width: $menu-width; width: $menu-width;
@ -329,7 +328,6 @@ svg.gdoc-icon {
} }
.gdoc-nav__entry, .gdoc-nav__entry,
.gdoc-search__entry,
.gdoc-language__entry { .gdoc-language__entry {
flex: 1; flex: 1;
color: var(--body-font-color); color: var(--body-font-color);
@ -349,10 +347,10 @@ svg.gdoc-icon {
.gdoc-language__list { .gdoc-language__list {
background: var(--body-background); background: var(--body-background);
border-radius: $border-radius; border-radius: $border-radius;
box-shadow: 0 1px 3px 0 var(--accent-color), 0 1px 2px 0 var(--accent-color-lite); box-shadow: 0 1px 3px 0 var(--accent-color-dark), 0 1px 2px 0 var(--accent-color);
position: absolute; position: absolute;
margin: 0; margin: 0;
padding: $padding-8 !important; padding: $padding-8 $padding-4 !important;
list-style: none; list-style: none;
top: calc(100% + #{$padding-8}); top: calc(100% + #{$padding-8});
z-index: 2; z-index: 2;
@ -576,8 +574,8 @@ svg.gdoc-icon {
position: absolute; position: absolute;
left: $padding-8; left: $padding-8;
color: var(--control-icons); color: var(--control-icons);
width: $font-size-16; width: $font-size-20;
height: $font-size-16; height: $font-size-20;
} }
&::after { &::after {
@ -591,33 +589,19 @@ svg.gdoc-icon {
padding: $padding-8; padding: $padding-8;
padding-left: $padding-32; padding-left: $padding-32;
border: $border-1 solid var(--accent-color); transition: border-color 0.15s ease-in-out, box-shadow 0.15s ease-in-out;
border: 1px solid transparent;
border-radius: $border-radius; border-radius: $border-radius;
background: var(--accent-color-lite); background: var(--accent-color-lite);
color: var(--body-font-color); color: var(--body-font-color);
&:required + &__spinner { &:focus {
display: block; outline: none !important;
border: 1px solid var(--accent-color);
} }
} }
&__spinner {
position: absolute;
margin: $padding-8;
right: 0;
top: 0;
width: $padding-16;
height: $padding-16;
border: $border-1 solid transparent;
border-top-color: var(--body-font-color);
border-radius: 50%;
@include spin(1s);
}
&__list { &__list {
visibility: hidden; visibility: hidden;
left: 0; left: 0;
@ -625,20 +609,15 @@ svg.gdoc-icon {
ul { ul {
list-style: none; list-style: none;
margin-top: $padding-8;
padding-left: 0; padding-left: 0;
} }
li {
margin: $padding-4 0;
}
> li > span { > li > span {
font-weight: bold; font-weight: bold;
} }
> li + li { > li + li {
margin-top: $padding-8; margin-top: $padding-4;
} }
svg.gdoc-icon { svg.gdoc-icon {
@ -646,6 +625,40 @@ svg.gdoc-icon {
} }
} }
&__section {
display: flex;
flex-direction: column;
padding: $padding-4 !important;
}
&__entry {
display: flex;
flex-direction: column;
color: var(--body-font-color);
padding: $padding-4 !important;
border-radius: $border-radius;
&:hover,
&.is-active {
background: var(--accent-color-lite);
text-decoration: none;
.gdoc-search__entry--title {
text-decoration-style: dashed !important;
text-decoration: underline;
}
}
&:visited {
color: var(--body-font-color);
}
&--description {
font-size: $font-size-14;
font-style: italic;
}
}
&:focus-within &__list.has-hits, &:focus-within &__list.has-hits,
&__list.has-hits:active { &__list.has-hits:active {
visibility: visible; visibility: visible;

View file

@ -13,6 +13,7 @@
--link-color: #{$link-color}; --link-color: #{$link-color};
--link-color-visited: #{$link-color-visited}; --link-color-visited: #{$link-color-visited};
--accent-color-dark: #{$gray-300};
--accent-color: #{$gray-200}; --accent-color: #{$gray-200};
--accent-color-lite: #{$gray-100}; --accent-color-lite: #{$gray-100};
@ -58,6 +59,7 @@
--link-color: #{$link-color-dark}; --link-color: #{$link-color-dark};
--link-color-visited: #{$link-color-visited-dark}; --link-color-visited: #{$link-color-visited-dark};
--accent-color-dark: #{darken($body-background-dark, 6)};
--accent-color: #{darken($body-background-dark, 4)}; --accent-color: #{darken($body-background-dark, 4)};
--accent-color-lite: #{darken($body-background-dark, 2)}; --accent-color-lite: #{darken($body-background-dark, 2)};

View file

@ -5,6 +5,7 @@ $padding-8: 0.5rem !default;
$padding-16: 1rem !default; $padding-16: 1rem !default;
$padding-24: 1.5rem !default; $padding-24: 1.5rem !default;
$padding-32: 2rem !default; $padding-32: 2rem !default;
$padding-48: 3rem !default;
$padding-64: 4rem !default; $padding-64: 4rem !default;
$padding-96: 6rem !default; $padding-96: 6rem !default;
@ -47,13 +48,13 @@ $body-font-color: $gray-800 !default;
$body-font-weight: normal !default; $body-font-weight: normal !default;
$body-min-width: 20rem !default; $body-min-width: 20rem !default;
$code-font-color: rgba(95, 95, 95, 1) !default; $code-font-color: rgba(70, 70, 70, 1) !default;
$code-font-color-dark: rgba(185, 185, 185, 1) !default; $code-font-color-dark: rgba(185, 185, 185, 1) !default;
$body-background-dark: $gray-800 !default; $body-background-dark: $gray-800 !default;
$body-font-color-dark: $gray-100 !default; $body-font-color-dark: $gray-100 !default;
$container-max-width: 80rem !default; $container-max-width: 82rem !default;
$main-color: rgba(65, 134, 201, 1) !default; $main-color: rgba(65, 134, 201, 1) !default;
$second-color: rgba(47, 51, 62, 1) !default; $second-color: rgba(47, 51, 62, 1) !default;
@ -63,11 +64,11 @@ $body-background-dark: mix(invert($body-background, 75%), $second-color) !defaul
$link-color-dark: rgba(110, 168, 212, 1) !default; $link-color-dark: rgba(110, 168, 212, 1) !default;
$link-color-visited-dark: rgba(186, 142, 240) !default; $link-color-visited-dark: rgba(186, 142, 240) !default;
$code-background: lighten($gray-200, 4) !default; $code-background: $gray-100 !default;
$code-background-dark: darken($body-background-dark, 2) !default; $code-background-dark: darken($body-background-dark, 2) !default;
$header-height: 3.5rem !default; $header-height: 3.5rem !default;
$menu-width: 16rem !default; $menu-width: 18rem !default;
$sm-breakpoint: $menu-width + $body-min-width + 3rem !default; $sm-breakpoint: $menu-width + $body-min-width + 3rem !default;

View file

@ -89,12 +89,3 @@
display: inline-block; display: inline-block;
min-width: 4rem; min-width: 4rem;
} }
@mixin spin($duration) {
animation: spin $duration ease infinite;
@keyframes spin {
100% {
transform: rotate(360deg);
}
}
}