From 77127cad030c2ff7ca7c1d41d2642dffff53e076 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?V=C3=ADt=20Semr=C3=A1d?= <76877124+VitroidFPV@users.noreply.github.com> Date: Tue, 12 Dec 2023 23:13:16 +0100 Subject: [PATCH] Fix: Search bar, update styles (#264) Style Fix: Last minute touch up --- src/theme/SearchBar/DocSearch.js | 14 +- .../SearchBar/HighlightSearchResults.jsx | 53 +++++++ src/theme/SearchBar/algolia.css | 98 ++++++++++++- src/theme/SearchBar/index.js | 100 -------------- src/theme/SearchBar/index.jsx | 130 ++++++++++++++++++ src/theme/SearchBar/templates.js | 1 + src/theme/SearchBar/utils.js | 2 +- 7 files changed, 295 insertions(+), 103 deletions(-) create mode 100644 src/theme/SearchBar/HighlightSearchResults.jsx delete mode 100644 src/theme/SearchBar/index.js create mode 100644 src/theme/SearchBar/index.jsx diff --git a/src/theme/SearchBar/DocSearch.js b/src/theme/SearchBar/DocSearch.js index b8240e8e8e..35525ae2cb 100644 --- a/src/theme/SearchBar/DocSearch.js +++ b/src/theme/SearchBar/DocSearch.js @@ -22,7 +22,7 @@ class DocSearch { queryHook = false, handleSelected = false, enhancedSearchInput = false, - layout = 'collumns', + layout = 'column', }) { this.input = DocSearch.getInputFromSelector(inputSelector); this.queryDataCallback = queryDataCallback || null; @@ -70,6 +70,16 @@ class DocSearch { if (enhancedSearchInput) { DocSearch.bindSearchBoxEvent(); } + + // Ctrl/Cmd + K should focus the search bar, emulating the Algolia search UI + document.addEventListener('keydown', (e) => { + if ((e.ctrlKey || e.metaKey) && e.key == 'k') { + this.input.focus(); + + // By default, using Ctrl + K in Chrome will open the location bar, so disable this + e.preventDefault(); + } + }); } static injectSearchBox(input) { @@ -176,6 +186,7 @@ class DocSearch { const isLvl2 = displayTitle && displayTitle !== '' && displayTitle !== subcategory; const isLvl1 = !isLvl2 && subcategory && subcategory !== '' && subcategory !== category; const isLvl0 = !isLvl1 && !isLvl2; + const version = hit.version; return { isLvl0, @@ -190,6 +201,7 @@ class DocSearch { title: displayTitle, text, url, + version, }; }); } diff --git a/src/theme/SearchBar/HighlightSearchResults.jsx b/src/theme/SearchBar/HighlightSearchResults.jsx new file mode 100644 index 0000000000..8e703994b4 --- /dev/null +++ b/src/theme/SearchBar/HighlightSearchResults.jsx @@ -0,0 +1,53 @@ +//copied from https://github.com/cmfcmf/docusaurus-search-local +import Mark from "mark.js"; +import { useEffect, useState } from "react"; +import { useLocation } from "@docusaurus/router"; +import useDocusaurusContext from "@docusaurus/useDocusaurusContext"; +import { useHistory } from "@docusaurus/router"; + +export function HighlightSearchResults() { + const location = useLocation(); + const history = useHistory(); + const { + siteConfig: { baseUrl }, + } = useDocusaurusContext(); + + const [highlightData, setHighlightData] = useState({ wordToHighlight: '', isTitleSuggestion: false , titleText: '' }); + + useEffect(() => { + if ( + !location.state?.highlightState || + location.state.highlightState.wordToHighlight.length === 0 + ) { + return; + } + setHighlightData(location.state.highlightState); + + const { highlightState, ...state } = location.state; + history.replace({ + ...location, + state, + }); + }, [location.state?.highlightState, history, location]); + + useEffect(() => { + if (highlightData.wordToHighlight.length === 0) { + return; + } + + // Make sure to also adjust parse.js if you change the top element here. + const root = document.getElementsByTagName("article")[0] ?? document.getElementsByTagName("main")[0] ; + if (!root) { + return; + } + + const mark = new Mark(root); + const options = { + ignoreJoiners: true, + }; + mark.mark(highlightData.wordToHighlight , options); + return () => mark.unmark(options); + }, [highlightData, baseUrl]); + + return null; +} diff --git a/src/theme/SearchBar/algolia.css b/src/theme/SearchBar/algolia.css index 4de6e12bcd..58a56cc4a0 100644 --- a/src/theme/SearchBar/algolia.css +++ b/src/theme/SearchBar/algolia.css @@ -264,6 +264,7 @@ padding: 0; text-align: left; height: auto; + position: relative; background: transparent; border: none; z-index: 999; @@ -386,10 +387,10 @@ float: left; width: 30%; display: none; - padding: 5.33333px 10.66667px; padding-left: 0; text-align: right; position: relative; + padding: 5.33333px 10.66667px; color: #777; font-size: 0.9em; word-wrap: break-word; @@ -435,6 +436,14 @@ padding-right: 2px; } +.algolia-autocomplete .algolia-docsearch-suggestion--version { + display: block; + font-size: 0.65em; + color: #a6aab1; + padding-top: 2px; + padding-right: 2px; +} + .algolia-autocomplete .algolia-docsearch-suggestion--no-results { width: 100%; padding: 8px 0; @@ -499,3 +508,90 @@ margin-left: auto; margin-right: 5px; } + +html[data-theme='dark'] .algolia-docsearch-suggestion--category-header, +html[data-theme='dark'] .algolia-docsearch-suggestion--wrapper, +html[data-theme='dark'] .algolia-docsearch-footer { + background: var(--ifm-background-color) !important; + color: var(--ifm-font-color-base) !important; +} + +html[data-theme='dark'] .algolia-docsearch-suggestion--title { + color: var(--ifm-font-color-base) !important; +} + +html[data-theme='dark'] .ds-cursor .algolia-docsearch-suggestion--wrapper { + background: var(--ifm-background-surface-color) !important; +} + +mark { + background-color: lightblue; +} + +/* CUSTOM STYLES */ + +.algolia-autocomplete .algolia-docsearch-suggestion--category-header { + padding: 8px 16px !important; + color: var(--ifm-color-primary) !important; +} + +html[data-theme='light'] .algolia-autocomplete .algolia-docsearch-suggestion--category-header { + background: #f0f0f0 !important; +} + +.algolia-autocomplete .algolia-docsearch-suggestion--highlight { + background: rgb(255 187 0 / 20%); + color: var(--ifm-color-primary); + border-radius: 8px; +} + +.algolia-autocomplete .algolia-docsearch-suggestion--content:before { + background: rgb(255, 255, 255, 0.1); +} + +.algolia-autocomplete .algolia-docsearch-suggestion--subcategory-column:before { + background: transparent; +} + +.algolia-autocomplete .algolia-docsearch-suggestion--text .algolia-docsearch-suggestion--highlight { + border-radius: 0px; + border-bottom: 1px solid var(--ifm-color-primary); + box-shadow: none; +} + +.algolia-autocomplete .ds-dropdown-menu { + margin-top: 24px !important; + border-radius: 16px !important; + box-shadow: none !important; +} + +.algolia-autocomplete .ds-cursor .algolia-docsearch-suggestion--wrapper { + box-shadow: none; + border-right: 3px solid var(--ifm-color-primary); +} + +.algolia-docsearch-suggestion--title .algolia-docsearch-suggestion--highlight { + outline: 4px solid rgb(255 187 0 / 20%); +} + +.algolia-docsearch-suggestion--wrapper:hover { + border-color: var(--ifm-color-primary); +} + +.algolia-docsearch-suggestion--wrapper { + border-right: 3px solid transparent; +} + +span.ds-suggestions { + border-radius: 16px !important; +} + +.algolia-autocomplete .ds-dropdown-menu [class^='ds-dataset-'] { + border-radius: 16px !important; + background: transparent; + border: 3px solid rgb(115 115 115 / 0.1); +} + +.algolia-autocomplete .ds-dropdown-menu:before { + display: none; +} diff --git a/src/theme/SearchBar/index.js b/src/theme/SearchBar/index.js deleted file mode 100644 index b9417b35af..0000000000 --- a/src/theme/SearchBar/index.js +++ /dev/null @@ -1,100 +0,0 @@ -import React, { useRef, useCallback, useState } from 'react'; -import classnames from 'classnames'; -import { useHistory } from '@docusaurus/router'; -import useDocusaurusContext from '@docusaurus/useDocusaurusContext'; -import { usePluginData } from '@docusaurus/useGlobalData'; -import useIsBrowser from '@docusaurus/useIsBrowser'; - -const Search = (props) => { - const initialized = useRef(false); - const searchBarRef = useRef(null); - const [indexReady, setIndexReady] = useState(false); - const history = useHistory(); - const { siteConfig = {} } = useDocusaurusContext(); - const isBrowser = useIsBrowser(); - const { baseUrl } = siteConfig; - const initAlgolia = (searchDocs, searchIndex, DocSearch) => { - return new DocSearch({ - searchDocs, - searchIndex, - baseUrl, - inputSelector: '#search_input_react', - // Override algolia's default selection event, allowing us to do client-side - // navigation and avoiding a full page refresh. - handleSelected: (_input, _event, suggestion) => { - const url = suggestion.url || '/'; - // Use an anchor tag to parse the absolute url into a relative url - // Alternatively, we can use new URL(suggestion.url) but its not supported in IE - const a = document.createElement('a'); - a.href = url; - // Algolia use closest parent element id #__docusaurus when a h1 page title does not have an id - // So, we can safely remove it. See https://github.com/facebook/docusaurus/issues/1828 for more details. - - history.push(url); - }, - }); - }; - - const pluginData = usePluginData('docusaurus-lunr-search'); - const getSearchDoc = () => (process.env.NODE_ENV === 'production' ? fetch(`${baseUrl}${pluginData.fileNames.searchDoc}`).then((content) => content.json()) : Promise.resolve([])); - - const getLunrIndex = () => (process.env.NODE_ENV === 'production' ? fetch(`${baseUrl}${pluginData.fileNames.lunrIndex}`).then((content) => content.json()) : Promise.resolve([])); - - const loadAlgolia = () => { - if (!initialized.current) { - Promise.all([getSearchDoc(), getLunrIndex(), import('./DocSearch'), import('./algolia.css')]).then(([searchDocs, searchIndex, { default: DocSearch }]) => { - if (searchDocs.length === 0) { - return; - } - initAlgolia(searchDocs, searchIndex, DocSearch); - setIndexReady(true); - }); - initialized.current = true; - } - }; - - const toggleSearchIconClick = useCallback( - (e) => { - if (!searchBarRef.current.contains(e.target)) { - searchBarRef.current.focus(); - } - - props.handleSearchBarToggle?.(!props.isSearchBarExpanded); - }, - [props], - ); - - if (isBrowser) { - loadAlgolia(); - } - - return ( -