From fb905bd6c1a10a76941ca1177048ae3d1e928272 Mon Sep 17 00:00:00 2001 From: Robert Kaussow Date: Wed, 7 Dec 2022 08:57:41 +0100 Subject: [PATCH] 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. --- assets/search/config.json | 3 +- assets/search/data.json | 3 +- exampleSite/config/_default/params.yaml | 1 + layouts/partials/search.html | 3 +- package-lock.json | 1 + package.json | 1 + src/js/groupBy.js | 31 ---------- src/js/search.js | 40 +++++++++---- src/sass/_base.scss | 75 +++++++++++++++---------- src/sass/_color_mode.scss | 2 + src/sass/_defaults.scss | 9 +-- src/sass/_utils.scss | 9 --- 12 files changed, 87 insertions(+), 91 deletions(-) delete mode 100644 src/js/groupBy.js diff --git a/assets/search/config.json b/assets/search/config.json index 6935793..831c8d1 100644 --- a/assets/search/config.json +++ b/assets/search/config.json @@ -3,5 +3,6 @@ { "dataFile": {{ $searchData.RelPermalink | 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 }} } diff --git a/assets/search/data.json b/assets/search/data.json index 26f2463..eeebaa6 100644 --- a/assets/search/data.json +++ b/assets/search/data.json @@ -6,7 +6,8 @@ "href": "{{ $page.RelPermalink }}", "title": {{ (partial "utils/title" $page) | jsonify }}, "parent": {{ with $page.Parent }}{{ (partial "utils/title" .) | jsonify }}{{ else }}""{{ end }}, - "content": {{ $page.Plain | jsonify }} + "content": {{ $page.Plain | jsonify }}, + "description": {{ $page.Summary | plainify | jsonify }} } {{ end }} ] diff --git a/exampleSite/config/_default/params.yaml b/exampleSite/config/_default/params.yaml index b8bcca8..dcbbdc8 100644 --- a/exampleSite/config/_default/params.yaml +++ b/exampleSite/config/_default/params.yaml @@ -14,6 +14,7 @@ geekdocEditPath: edit/main/exampleSite geekdocSearch: true geekdocSearchShowParent: true +geekdocSearchShowDescription: true geekdocLegalNotice: https://thegeeklab.de/legal-notice/#contact-information geekdocPrivacyPolicy: https://thegeeklab.de/legal-notice/#privacy-policy diff --git a/layouts/partials/search.html b/layouts/partials/search.html index ff50330..5bd5eff 100644 --- a/layouts/partials/search.html +++ b/layouts/partials/search.html @@ -5,13 +5,12 @@ type="text" id="gdoc-search-input" class="gdoc-search__input" - placeholder="{{ i18n "form_placeholder_search" }}" + placeholder="{{ i18n "form_placeholder_search" }}..." aria-label="{{ i18n "form_placeholder_search" }}" maxlength="64" data-site-base-url="{{ .Site.BaseURL }}" data-site-lang="{{ .Site.Language.Lang }}" /> - {{ end }} diff --git a/package-lock.json b/package-lock.json index 9e83884..b8fa92c 100644 --- a/package-lock.json +++ b/package-lock.json @@ -13,6 +13,7 @@ "clipboard": "2.0.11", "flexsearch": "0.7.31", "katex": "0.16.3", + "lodash": "4.17.21", "mermaid": "9.2.2", "store2": "2.14.2" }, diff --git a/package.json b/package.json index 4ae9461..e78ee2b 100644 --- a/package.json +++ b/package.json @@ -35,6 +35,7 @@ "clipboard": "2.0.11", "flexsearch": "0.7.31", "katex": "0.16.3", + "lodash": "4.17.21", "mermaid": "9.2.2", "store2": "2.14.2" }, diff --git a/src/js/groupBy.js b/src/js/groupBy.js deleted file mode 100644 index 29a7d79..0000000 --- a/src/js/groupBy.js +++ /dev/null @@ -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) diff --git a/src/js/search.js b/src/js/search.js index 9fe6921..f0ca1da 100644 --- a/src/js/search.js +++ b/src/js/search.js @@ -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 { Validator } = require("@cfworker/json-schema") @@ -19,6 +20,9 @@ document.addEventListener("DOMContentLoaded", function (event) { }, showParent: { type: "boolean" + }, + showDescription: { + type: "boolean" } }, additionalProperties: false @@ -58,8 +62,8 @@ function init(input, searchConfig) { indexCfg.document = { key: "id", - index: ["title", "content"], - store: ["title", "href", "parent"] + index: ["title", "content", "description"], + store: ["title", "href", "parent", "description"] } const index = new FlexSearch.Document(indexCfg) @@ -75,7 +79,7 @@ function init(input, searchConfig) { function search(input, results, searchConfig) { const searchCfg = { enrich: true, - limit: 10 + limit: 5 } while (results.firstChild) { @@ -106,8 +110,12 @@ function search(input, results, searchConfig) { title = item.appendChild(document.createElement("span")), subList = item.appendChild(document.createElement("ul")) + if (!section) { + title.remove() + } + title.classList.add("gdoc-search__section") title.textContent = section - createLinks(searchHits[section], subList) + createLinks(searchHits[section], subList, searchConfig.showDescription) items.push(item) } @@ -117,7 +125,7 @@ function search(input, results, searchConfig) { subList = item.appendChild(document.createElement("ul")) title.textContent = "Results" - createLinks(searchHits, subList) + createLinks(searchHits, subList, searchConfig.showDescription) items.push(item) } @@ -133,20 +141,28 @@ function search(input, results, searchConfig) { * @param {HTMLElement} target Element to which the links should be attatched * @returns {Array} If target is not specified, returns an array of built links */ -function createLinks(pages, target) { +function createLinks(pages, target, showDesc) { const items = [] for (const page of pages) { const item = document.createElement("li"), - entry = item.appendChild(document.createElement("span")), - a = entry.appendChild(document.createElement("a")) - - entry.classList.add("flex") + a = item.appendChild(document.createElement("a")), + entry = a.appendChild(document.createElement("span")) 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") + 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) { target.appendChild(item) continue diff --git a/src/sass/_base.scss b/src/sass/_base.scss index 7a523a2..cb9ca29 100644 --- a/src/sass/_base.scss +++ b/src/sass/_base.scss @@ -228,7 +228,6 @@ svg.gdoc-icon { .gdoc-nav { flex: 0 0 $menu-width; - font-size: $font-size-14; nav { width: $menu-width; @@ -329,7 +328,6 @@ svg.gdoc-icon { } .gdoc-nav__entry, -.gdoc-search__entry, .gdoc-language__entry { flex: 1; color: var(--body-font-color); @@ -349,10 +347,10 @@ svg.gdoc-icon { .gdoc-language__list { background: var(--body-background); 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; margin: 0; - padding: $padding-8 !important; + padding: $padding-8 $padding-4 !important; list-style: none; top: calc(100% + #{$padding-8}); z-index: 2; @@ -576,8 +574,8 @@ svg.gdoc-icon { position: absolute; left: $padding-8; color: var(--control-icons); - width: $font-size-16; - height: $font-size-16; + width: $font-size-20; + height: $font-size-20; } &::after { @@ -591,33 +589,19 @@ svg.gdoc-icon { padding: $padding-8; 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; background: var(--accent-color-lite); color: var(--body-font-color); - &:required + &__spinner { - display: block; + &:focus { + 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 { visibility: hidden; left: 0; @@ -625,20 +609,15 @@ svg.gdoc-icon { ul { list-style: none; - margin-top: $padding-8; padding-left: 0; } - li { - margin: $padding-4 0; - } - > li > span { font-weight: bold; } > li + li { - margin-top: $padding-8; + margin-top: $padding-4; } 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, &__list.has-hits:active { visibility: visible; diff --git a/src/sass/_color_mode.scss b/src/sass/_color_mode.scss index f4d36a3..f00809e 100644 --- a/src/sass/_color_mode.scss +++ b/src/sass/_color_mode.scss @@ -13,6 +13,7 @@ --link-color: #{$link-color}; --link-color-visited: #{$link-color-visited}; + --accent-color-dark: #{$gray-300}; --accent-color: #{$gray-200}; --accent-color-lite: #{$gray-100}; @@ -58,6 +59,7 @@ --link-color: #{$link-color-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-lite: #{darken($body-background-dark, 2)}; diff --git a/src/sass/_defaults.scss b/src/sass/_defaults.scss index fa944c7..55e9f90 100644 --- a/src/sass/_defaults.scss +++ b/src/sass/_defaults.scss @@ -5,6 +5,7 @@ $padding-8: 0.5rem !default; $padding-16: 1rem !default; $padding-24: 1.5rem !default; $padding-32: 2rem !default; +$padding-48: 3rem !default; $padding-64: 4rem !default; $padding-96: 6rem !default; @@ -47,13 +48,13 @@ $body-font-color: $gray-800 !default; $body-font-weight: normal !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; $body-background-dark: $gray-800 !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; $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-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; $header-height: 3.5rem !default; -$menu-width: 16rem !default; +$menu-width: 18rem !default; $sm-breakpoint: $menu-width + $body-min-width + 3rem !default; diff --git a/src/sass/_utils.scss b/src/sass/_utils.scss index 4911ba1..66bc299 100644 --- a/src/sass/_utils.scss +++ b/src/sass/_utils.scss @@ -89,12 +89,3 @@ display: inline-block; min-width: 4rem; } - -@mixin spin($duration) { - animation: spin $duration ease infinite; - @keyframes spin { - 100% { - transform: rotate(360deg); - } - } -}