Created
October 25, 2024 15:12
-
-
Save wsydney76/27c779c11c49ca8aca5cffae8c322300 to your computer and use it in GitHub Desktop.
Instant Craft CMS instant search
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?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, | |
])); | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
{# 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