Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 4 additions & 6 deletions assets/css/compiled/main.css
Original file line number Diff line number Diff line change
Expand Up @@ -770,9 +770,6 @@ video {
width: -moz-max-content;
width: max-content;
}
.hx-w-screen {
width: 100vw;
}
.hx-min-w-0 {
min-width: 0px;
}
Expand All @@ -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;
}
Expand Down Expand Up @@ -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;
Expand Down
4 changes: 4 additions & 0 deletions assets/css/components/search.css
Original file line number Diff line number Diff line change
Expand Up @@ -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))];
}
39 changes: 28 additions & 11 deletions assets/js/flexsearch.js
Original file line number Diff line number Diff line change
Expand Up @@ -19,18 +19,22 @@ 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);
el.addEventListener('keydown', handleKeyDown);
el.addEventListener('input', handleInputChange);
}

const shortcutElements = document.querySelectorAll('.search-wrapper kbd');
const shortcutElements = document.querySelectorAll(`${wrapperClass} kbd`);

function setShortcutElementsOpacity(opacity) {
shortcutElements.forEach(el => {
Expand All @@ -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']
Expand All @@ -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();
Expand Down Expand Up @@ -194,6 +203,10 @@ document.addEventListener("DOMContentLoaded", function () {
* @returns {Promise<void>} 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,
Expand Down Expand Up @@ -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]++
Expand Down Expand Up @@ -410,7 +427,7 @@ document.addEventListener("DOMContentLoaded", function () {
}
let li = createElement(`
<li>
<a data-index="${i}" href="${result.route}" class=${i === 0 ? "active" : ""}>
<a data-index="${i}" href="${result.route}" class=not-prose ${i === 0 ? "active" : ""}>
<div class="title">`+ highlightMatches(result.children.title, query) + `</div>` +
(result.children.content ?
`<div class="excerpt">` + highlightMatches(result.children.content, query) + `</div>` : '') + `
Expand All @@ -424,4 +441,4 @@ document.addEventListener("DOMContentLoaded", function () {
resultsElement.appendChild(fragment);
resultsElement.dataset.count = results.length;
}
})();
};
22 changes: 18 additions & 4 deletions exampleSite/content/docs/advanced/customization.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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

Expand Down
68 changes: 68 additions & 0 deletions exampleSite/content/docs/guide/shortcodes/search.md
Original file line number Diff line number Diff line change
@@ -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

<br />
{{< search name="example1">}}

<br />
{{< search name="example2">}}

<br />
{{< search name="example3" placeholder="Custom placeholder via parameter">}}

<br />
{{< search name="example4" keybinding="true" placeholder="With keybinding">}}

<br />
{{< 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"}
{{</* search
name="example1"
placeholder="Custom placeholder"
<!-- Should respond to Ctrl+K -->
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))];
}
```
23 changes: 21 additions & 2 deletions exampleSite/hugo_stats.json
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand Down Expand Up @@ -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",
Expand Down Expand Up @@ -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",
Expand Down
1 change: 1 addition & 0 deletions i18n/en.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -12,3 +12,4 @@ onThisPage: "On this page"
poweredBy: "Powered by Hextra"
readMore: "Read more →"
searchPlaceholder: "Search..."
searchPlaceHolderexample2: "Unique Placeholder in en.yaml"
2 changes: 1 addition & 1 deletion layouts/partials/navbar.html
Original file line number Diff line number Diff line change
Expand Up @@ -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" -}}
Expand Down
29 changes: 20 additions & 9 deletions layouts/partials/search.html
Original file line number Diff line number Diff line change
@@ -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 "/" -}}

<script defer="">
document.addEventListener("DOMContentLoaded", function () {
const searchClass = search("{{- $name -}}", {{- $keybinding -}}, "{{- $baseUrl -}}");
});
</script>

<div class="search-wrapper hx-relative md:hx-w-64">
<div class="search-wrapper search-wrapper-{{ $name }} hx-relative md:hx-w-64">
<div class="hx-relative hx-flex hx-items-center hx-text-gray-900 contrast-more:hx-text-gray-800 dark:hx-text-gray-300 contrast-more:dark:hx-text-gray-300">
<input
placeholder="{{ $placeholder }}"
class="search-input hx-block hx-w-full hx-appearance-none hx-rounded-lg hx-px-3 hx-py-2 hx-transition-colors hx-text-base hx-leading-tight md:hx-text-sm hx-bg-black/[.05] dark:hx-bg-gray-50/10 focus:hx-bg-white dark:focus:hx-bg-dark placeholder:hx-text-gray-500 dark:placeholder:hx-text-gray-400 contrast-more:hx-border contrast-more:hx-border-current"
class="search-input search-input-{{ $name }} hx-block hx-w-full hx-appearance-none hx-rounded-lg hx-px-3 hx-py-2 hx-transition-colors hx-text-base hx-leading-tight md:hx-text-sm hx-bg-black/[.05] dark:hx-bg-gray-50/10 focus:hx-bg-white dark:focus:hx-bg-dark placeholder:hx-text-gray-500 dark:placeholder:hx-text-gray-400 contrast-more:hx-border contrast-more:hx-border-current"
type="search"
value=""
spellcheck="false"
/>
<kbd
class="hx-absolute hx-my-1.5 hx-select-none ltr:hx-right-1.5 rtl:hx-left-1.5 hx-h-5 hx-rounded hx-bg-white hx-px-1.5 hx-font-mono hx-text-[10px] hx-font-medium hx-text-gray-500 hx-border dark:hx-border-gray-100/20 dark:hx-bg-dark/50 contrast-more:hx-border-current contrast-more:hx-text-current contrast-more:dark:hx-border-current hx-items-center hx-gap-1 hx-transition-opacity hx-pointer-events-none hx-hidden sm:hx-flex"
>
CTRL K
</kbd>
{{- if $keybinding -}}
<kbd
class="not-prose hx-absolute hx-my-1.5 hx-select-none ltr:hx-right-1.5 rtl:hx-left-1.5 hx-h-5 hx-rounded hx-bg-white hx-px-1.5 hx-font-mono hx-text-[10px] hx-font-medium hx-text-gray-500 hx-border dark:hx-border-gray-100/20 dark:hx-bg-dark/50 contrast-more:hx-border-current contrast-more:hx-text-current contrast-more:dark:hx-border-current hx-items-center hx-gap-1 hx-transition-opacity hx-pointer-events-none hx-hidden sm:hx-flex"
>
CTRL K
</kbd>
{{- end -}}
</div>

<div>
<ul
class="search-results hextra-scrollbar hx-hidden hx-border hx-border-gray-200 hx-bg-white hx-text-gray-100 dark:hx-border-neutral-800 dark:hx-bg-neutral-900 hx-absolute hx-top-full hx-z-20 hx-mt-2 hx-overflow-auto hx-overscroll-contain hx-rounded-xl hx-py-2.5 hx-shadow-xl hx-max-h-[min(calc(50vh-11rem-env(safe-area-inset-bottom)),400px)] md:hx-max-h-[min(calc(100vh-5rem-env(safe-area-inset-bottom)),400px)] hx-inset-x-0 ltr:md:hx-left-auto rtl:md:hx-right-auto contrast-more:hx-border contrast-more:hx-border-gray-900 contrast-more:dark:hx-border-gray-50 hx-w-screen hx-min-h-[100px] hx-max-w-[min(calc(100vw-2rem),calc(100%+20rem))]"
class="search-results search-results-{{ $name }} not-prose hextra-scrollbar hx-hidden hx-border hx-border-gray-200 hx-bg-white hx-text-gray-100 dark:hx-border-neutral-800 dark:hx-bg-neutral-900 hx-absolute hx-top-full hx-z-20 hx-mt-2 hx-overflow-auto hx-overscroll-contain hx-rounded-xl hx-py-2.5 hx-shadow-xl hx-max-h-[min(calc(50vh-11rem-env(safe-area-inset-bottom)),400px)] md:hx-max-h-[min(calc(100vh-5rem-env(safe-area-inset-bottom)),400px)] hx-inset-x-0 ltr:md:hx-left-auto rtl:md:hx-right-auto contrast-more:hx-border contrast-more:hx-border-gray-900 contrast-more:dark:hx-border-gray-50 hx-w-full hx-min-h-[100px]"
style="transition: max-height 0.2s ease 0s;"
></ul>
</div>
Expand Down
2 changes: 1 addition & 1 deletion layouts/partials/sidebar.html
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@
<aside class="sidebar-container hx-flex hx-flex-col print:hx-hidden md:hx-top-16 md:hx-shrink-0 md:hx-w-64 md:hx-self-start max-md:[transform:translate3d(0,-100%,0)] {{ $sidebarClass }}">
<!-- Search bar on small screen -->
<div class="hx-px-4 hx-pt-4 md:hx-hidden">
{{ partial "search.html" }}
{{ partial "search.html" (dict "context" . "name" "mobile" "keybinding" true) }}
</div>
<div class="hextra-scrollbar hx-overflow-y-auto hx-overflow-x-hidden hx-p-4 hx-grow md:hx-h-[calc(100vh-var(--navbar-height)-var(--menu-height))]">
<ul class="hx-flex hx-flex-col hx-gap-1 md:hx-hidden">
Expand Down
10 changes: 10 additions & 0 deletions layouts/shortcodes/search.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
{{- $name := (.Get "name") }}
{{- $keybinding := or (.Get "keybinding") false }}
{{- $baseUrl := or (.Get "base-url") "/" }}
{{- $placeholder := .Get "placeholder" }}

{{- if not $name }}
{{- errorf "search shortcode: name property is required" -}}
{{- end -}}

{{ partial "partials/search.html" (dict "context" . "name" $name "keybinding" $keybinding "baseUrl" $baseUrl "placeholder" $placeholder) }}
4 changes: 3 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,9 @@
"dev:theme": "hugo server --logLevel=debug --config=hugo.yaml,../dev.toml --environment=theme --source=exampleSite --themesDir=../.. --disableFastRender -D --port 1313",
"dev": "hugo server --source=exampleSite --themesDir=../.. --disableFastRender -D --port 1313",
"build:css": "npx postcss --config postcss.config.js --env production assets/css/styles.css -o assets/css/compiled/main.css",
"build": "hugo --gc --minify --themesDir=../.. --source=exampleSite"
"build": "hugo --gc --minify --themesDir=../.. --source=exampleSite",
"build:theme": "hugo --config=hugo.yaml,../dev.toml --environment=theme --source=exampleSite --themesDir=../.. && npm run build:css"

},
"devDependencies": {
"@tailwindcss/nesting": "^0.0.0-insiders.565cd3e",
Expand Down