diff --git a/assets/css/compiled/main.css b/assets/css/compiled/main.css index ee130305..4fce0a7a 100644 --- a/assets/css/compiled/main.css +++ b/assets/css/compiled/main.css @@ -770,9 +770,6 @@ video { width: -moz-max-content; width: max-content; } -.hx-w-screen { - width: 100vw; -} .hx-min-w-0 { min-width: 0px; } @@ -794,9 +791,6 @@ video { .hx-max-w-\[90rem\] { max-width: 90rem; } -.hx-max-w-\[min\(calc\(100vw-2rem\)\,calc\(100\%\+20rem\)\)\] { - max-width: min(calc(100vw - 2rem),calc(100% + 20rem)); -} .hx-max-w-none { max-width: none; } @@ -2482,6 +2476,10 @@ article details > summary::before { --tw-text-opacity: 1; color: hsl(var(--primary-hue) var(--primary-saturation) calc(calc(var(--primary-lightness) / 50) * 45) / var(--tw-text-opacity)); } +.search-results-desktop { + width: 100vw; + max-width: min(calc(100vw - 2rem),calc(100% + 20rem)); +} @media (max-width: 767px) { .sidebar-container { position: fixed; diff --git a/assets/css/components/search.css b/assets/css/components/search.css index 3d508bc3..4a494d5d 100644 --- a/assets/css/components/search.css +++ b/assets/css/components/search.css @@ -36,3 +36,7 @@ @apply hx-text-primary-600; } } + +.search-results-desktop { + @apply hx-w-screen hx-max-w-[min(calc(100vw-2rem),calc(100%+20rem))]; +} \ No newline at end of file diff --git a/assets/js/flexsearch.js b/assets/js/flexsearch.js index 80c77410..0fe915cf 100644 --- a/assets/js/flexsearch.js +++ b/assets/js/flexsearch.js @@ -19,10 +19,14 @@ document.addEventListener("DOMContentLoaded", function () { // {{ end }} // {{ $noResultsFound := (T "noResultsFound") | default "No results found." }} -(function () { +var search = function (id, defaultSearch, baseUrl) { const searchDataURL = '{{ $searchData.RelPermalink }}'; - const inputElements = document.querySelectorAll('.search-input'); + const wrapperClass = `.search-wrapper-${id}`; + const inputClass = `.search-input-${id}`; + const resultsClass = `.search-results-${id}`; + + const inputElements = document.querySelectorAll(inputClass); for (const el of inputElements) { el.addEventListener('focus', init); el.addEventListener('keyup', search); @@ -30,7 +34,7 @@ document.addEventListener("DOMContentLoaded", function () { el.addEventListener('input', handleInputChange); } - const shortcutElements = document.querySelectorAll('.search-wrapper kbd'); + const shortcutElements = document.querySelectorAll(`${wrapperClass} kbd`); function setShortcutElementsOpacity(opacity) { shortcutElements.forEach(el => { @@ -45,15 +49,20 @@ document.addEventListener("DOMContentLoaded", function () { // Get the search wrapper, input, and results elements. function getActiveSearchElement() { - const inputs = Array.from(document.querySelectorAll('.search-wrapper')).filter(el => el.clientHeight > 0); + const inputs = Array.from(document.querySelectorAll(wrapperClass)).filter(el => el.clientHeight > 0); if (inputs.length === 1) { return { wrapper: inputs[0], - inputElement: inputs[0].querySelector('.search-input'), - resultsElement: inputs[0].querySelector('.search-results') + inputElement: inputs[0].querySelector(inputClass), + resultsElement: inputs[0].querySelector(resultsClass) }; } - return undefined; + + return { + wrapper: undefined, + inputElement: undefined, + resultsElement: undefined + }; } const INPUTS = ['input', 'select', 'button', 'textarea'] @@ -72,10 +81,10 @@ document.addEventListener("DOMContentLoaded", function () { (activeElement && activeElement.isContentEditable)) return; - if ( + if (defaultSearch && ( e.key === '/' || (e.key === 'k' && - (e.metaKey /* for Mac */ || /* for non-Mac */ e.ctrlKey)) + (e.metaKey /* for Mac */ || /* for non-Mac */ e.ctrlKey))) ) { e.preventDefault(); inputElement.focus(); @@ -194,6 +203,10 @@ document.addEventListener("DOMContentLoaded", function () { * @returns {Promise} A promise that resolves when the index is preloaded. */ async function preloadIndex() { + // Only run once + if (window.pageIndex !== undefined) { + return; + } const tokenize = '{{- site.Params.search.flexsearch.tokenize | default "forward" -}}'; window.pageIndex = new FlexSearch.Document({ tokenize, @@ -322,6 +335,10 @@ document.addEventListener("DOMContentLoaded", function () { for (let j = 0; j < sectionResults.length; j++) { const { doc } = sectionResults[j] + if (!doc.url.startsWith(baseUrl)) { + continue; + } + const isMatchingTitle = doc.display !== undefined if (isMatchingTitle) { pageTitleMatches[i]++ @@ -410,7 +427,7 @@ document.addEventListener("DOMContentLoaded", function () { } let li = createElement(`
  • - +
    `+ highlightMatches(result.children.title, query) + `
    ` + (result.children.content ? `
    ` + highlightMatches(result.children.content, query) + `
    ` : '') + ` @@ -424,4 +441,4 @@ document.addEventListener("DOMContentLoaded", function () { resultsElement.appendChild(fragment); resultsElement.dataset.count = results.length; } -})(); +}; diff --git a/exampleSite/content/docs/advanced/customization.md b/exampleSite/content/docs/advanced/customization.md index af1e2bfb..0bfc53ea 100644 --- a/exampleSite/content/docs/advanced/customization.md +++ b/exampleSite/content/docs/advanced/customization.md @@ -109,6 +109,14 @@ The following classes can be used to customize various parts of the theme. - `hextra-filetree-folder` - The filetree folder container +#### Search + +These styles will apply to __all__ search elements, including shortcodes and the search elements in the Navbar. + +- `search-wrapper` - The search wrapper container +- `search-input` - The search input element +- `search-results` - The search results list container + #### Navbar - `nav-container` - The navbar container @@ -120,11 +128,17 @@ The following classes can be used to customize various parts of the theme. - `hextra-footer` - The footer element - `hextra-custom-footer` - The custom footer section container -#### Search +#### Search in Desktop Nav Bar -- `search-wrapper` - The search wrapper container -- `search-input` - The search input element -- `search-results` - The search results list container +- `search-wrapper-desktop` - The search wrapper container for the desktop search element +- `search-input-desktop` - The search input element for the desktop search element +- `search-results-desktop` - The search results list container for the desktop search element + +#### Search in Mobile Nav Bar + +- `search-wrapper-mobile` - The search wrapper container for the mobile search element +- `search-input-mobile` - The search input element for the mobile search element +- `search-results-mobile` - The search results list container for the mobile search element #### Table of Contents diff --git a/exampleSite/content/docs/guide/shortcodes/search.md b/exampleSite/content/docs/guide/shortcodes/search.md new file mode 100644 index 00000000..3850d02d --- /dev/null +++ b/exampleSite/content/docs/guide/shortcodes/search.md @@ -0,0 +1,68 @@ +# Search Component + +Search component that can be used to search across the whole site or just a section. Identical to the search component inside the nav bar. + +## Example + +
    +{{< search name="example1">}} + +
    +{{< search name="example2">}} + +
    +{{< search name="example3" placeholder="Custom placeholder via parameter">}} + +
    +{{< search name="example4" keybinding="true" placeholder="With keybinding">}} + +
    +{{< search name="example5" placeholder="Search only in Shortcodes" base-url="/docs/guide/shortcodes/" >}} + +## Usage + +{{< callout type="warning" >}} +Parameter `name` is required and must be unique. +{{< /callout >}} + +```text {filename="Markdown"} +{{ + keybinding="true" + base-url="/docs/guide/shortcodes/" +*/>}} +``` + +## Parameters + +| Parameter | Description | +| ------------- | --------------------------------------------------------------------------------------------------------------------- | +| `name` | Globally unique identifier (Required) | +| `placeholder` | Custom placeholder. By default `searchPlaceHolder{name}` is used, then `searchPlaceholder` . | +| `keybinding` | Should respond to keybinding. Note: Only the first last search element on the whole page will capture the keybinding. | +| `base-url` | How narrow the search should be. `/` (whole site) by default. | + +## Styling + +Due to CSS limitations out of the box the Search Results element has a fixed size. It is recommended to customize each Search Component individually to your liking. + +### Example + +```scss {filename="custom.css"} +.search-wrapper-mysearch { + // Applies to the search wrapper element with name="myname" +} + +.search-input-mysearch { + // Applies to the search input with name="myname" +} + +.search-results-mysearch { + // Applies to the search results container with name="myname" + + // For example this is the style applied to the search component in the desktop navbar + @apply hx-w-screen hx-max-w-[min(calc(100vw-2rem),calc(100%+20rem))]; +} +``` \ No newline at end of file diff --git a/exampleSite/hugo_stats.json b/exampleSite/hugo_stats.json index 65d58c9f..85548e52 100644 --- a/exampleSite/hugo_stats.json +++ b/exampleSite/hugo_stats.json @@ -376,7 +376,6 @@ "hx-max-w-6xl", "hx-max-w-[50%]", "hx-max-w-[90rem]", - "hx-max-w-[min(calc(100vw-2rem),calc(100%+20rem))]", "hx-max-w-none", "hx-max-w-screen-xl", "hx-mb-10", @@ -536,7 +535,6 @@ "hx-w-[180%]", "hx-w-full", "hx-w-max", - "hx-w-screen", "hx-whitespace-nowrap", "hx-z-10", "hx-z-20", @@ -626,8 +624,29 @@ "rtl:hx-text-left", "rtl:md:hx-right-auto", "search-input", + "search-input-desktop", + "search-input-example1", + "search-input-example2", + "search-input-example3", + "search-input-example4", + "search-input-example5", + "search-input-mobile", "search-results", + "search-results-desktop", + "search-results-example1", + "search-results-example2", + "search-results-example3", + "search-results-example4", + "search-results-example5", + "search-results-mobile", "search-wrapper", + "search-wrapper-desktop", + "search-wrapper-example1", + "search-wrapper-example2", + "search-wrapper-example3", + "search-wrapper-example4", + "search-wrapper-example5", + "search-wrapper-mobile", "sidebar-active-item", "sidebar-container", "sm:hx-block", diff --git a/i18n/en.yaml b/i18n/en.yaml index ac6bff8c..f9ac3606 100644 --- a/i18n/en.yaml +++ b/i18n/en.yaml @@ -12,3 +12,4 @@ onThisPage: "On this page" poweredBy: "Powered by Hextra" readMore: "Read more →" searchPlaceholder: "Search..." +searchPlaceHolderexample2: "Unique Placeholder in en.yaml" diff --git a/layouts/partials/navbar.html b/layouts/partials/navbar.html index 3a6bcbbb..2dcdbdb9 100644 --- a/layouts/partials/navbar.html +++ b/layouts/partials/navbar.html @@ -30,7 +30,7 @@ {{- $currentPage := . -}} {{- range .Site.Menus.main -}} {{- if eq .Params.type "search" -}} - {{- partial "search.html" (dict "params" .Params) -}} + {{- partial "search.html" (dict "params" .Params "name" "desktop" "keybinding" true) -}} {{- else -}} {{- $link := .URL -}} {{- $external := strings.HasPrefix $link "http" -}} diff --git a/layouts/partials/search.html b/layouts/partials/search.html index e421ab00..2ed06910 100644 --- a/layouts/partials/search.html +++ b/layouts/partials/search.html @@ -1,25 +1,36 @@ -{{- $placeholder := (T "searchPlaceholder") | default "Search..." -}} +{{- $name := .name -}} +{{- $searchPlaceHolder := printf "%s%s" "searchPlaceHolder" $name}} +{{- $placeholder := (or .placeholder ((or (T $searchPlaceHolder) (T "searchPlaceholder")))) | default "Search..." -}} +{{- $keybinding := .keybinding | default false -}} +{{- $baseUrl := .baseUrl | default "/" -}} + -
    +
    - - CTRL K - + {{- if $keybinding -}} + + CTRL K + + {{- end -}}
      diff --git a/layouts/partials/sidebar.html b/layouts/partials/sidebar.html index 6698f106..4f6bc0fe 100644 --- a/layouts/partials/sidebar.html +++ b/layouts/partials/sidebar.html @@ -19,7 +19,7 @@