Skip to content

Instantly share code, notes, and snippets.

@wsydney76
Created October 25, 2024 15:12
Show Gist options
  • Save wsydney76/27c779c11c49ca8aca5cffae8c322300 to your computer and use it in GitHub Desktop.
Save wsydney76/27c779c11c49ca8aca5cffae8c322300 to your computer and use it in GitHub Desktop.
Instant Craft CMS instant search
<?php
namespace modules\main\controllers;
use craft\elements\Entry;
use craft\web\Controller;
use yii\web\Response;
/**
* Content controller
*/
class ContentController extends Controller
{
protected array|int|bool $allowAnonymous = self::ALLOW_ANONYMOUS_LIVE;
public function actionGetSearchResults(string $query): Response
{
return $this->asJson(Entry::find()
->uri(':notempty:')
->search("title:$query")
->orderBy('score')
->collect()
->map(fn($entry) => [
'title' => $entry->title,
'href' => $entry->url,
'section' => $entry->section->name,
]));
}
}
{# https://alpinejs.dev/component/dropdown #}
<div class="relative"
x-data="instantSearch"
x-ref="instantsearch"
x-id="['results-panel']"
@keydown.window.ctrl.k.prevent="closeAndFocus($refs.input)"
@keydown.escape.prevent.stop="closeAndFocus($refs.input)"
@focusin.window="!$refs.instantsearch.contains($event.target) && close()"
@keydown.down.prevent="$focus.next()"
@keydown.up.prevent="$focus.previous()"
>
{# Search input #}
<div class="flex">
<input class="w-72 text-sm"
type="search"
placeholder="Suchen im Titel (min 3 Zeichen)"
autocomplete="off"
x-ref="input"
x-model="query"
@input.debounce.400ms="handleInput()"
@keyup.enter="handleInput()"
aria-label="Suchen im Titel (min 3 Zeichen)"
:aria-expanded="open"
:aria-controls="$id('results-panel')">
</div>
{# Panel #}
<div class="absolute top-[37px] left-0 z-50"
:id="$id('results-panel')"
x-show="open"
x-cloak
x-transition.origin.top.left.duration.150ms
@mousedown.outside.prevent.stop="closeAndFocus($refs.input)"
>
<div class="w-72 max-h-[80vh] overflow-y-scroll border border-black bg-white p-4 shadow-lg text-sm">
<template x-if="results.length">
<template x-for="result in results">
<a class="block p-2 hover:bg-gray-100 focus:bg-gray-100 transition-colors"
:href="result.href">
<div class="text-gray-500" x-text="result.section"></div>
<div x-text="result.title"></div>
</a>
</template>
</template>
<template x-if="!results.length">
<div class="px-4">
Nichts gefunden.
</div>
</template>
</div>
</div>
</div>
{% js %}
function instantSearch() {
return {
query: '',
open: false,
results: [],
async handleInput() {
if (this.query.length < 3) {
this.close();
return;
}
const response = await fetch('/actions/main/content/get-search-results?query=' + this.query);
this.results = await response.json();
this.open = true;
},
closeAndFocus(element) {
this.close();
element.focus();
element.select();
},
close() {
this.open = false
}
}
}
{% endjs %}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment