Spotlight
This component implements a global search feature triggered by a customizable shortcut. It does not index your site, so you need to implement by yourself a global search function.
Try it
Search for "a" to see what kind of content it returns. In this example, all links point to this page itself. You can change this default shortcut.
⌘ command + G ⊞ win + G ◆ meta + GUsage
Place the spotlight tag somewhere on the main layout.
<body> ... <x-spotlight /> </body>
Create a App\Support\Spotlight
class with a search
method that returns the result.
namespace App\Support; class Spotlight{ public function search(Request $request) { // Do your search logic here // IMPORTANT: apply any security concern here }}
Make sure each item from your collection contains the following keys.
[ 'name' => 'Mary', // Any string 'description' => 'Software Engineer', // Any string 'link' => '/users/1', // Any valid route 'avatar' => 'http://...' // Any image url]
Instead of avatar
you can use any pre-rendered blade icon
.
[ // ... 'icon' => Blade::render("<x-icon name='o-bolt' />")]
... You are done!
Manual activation
You can trigger the Spotlight component by dispatching a mary-search-open
event.
Probably you want to put this search button inside a navbar. In this case place an empty x-data
as describe bellow.
{{-- Place an empty `x-data` on body--}}<body ... x-data> ... <nav> {{-- Notice `@click.stop` --}} <x-button label="Search" @click.stop="$dispatch('mary-search-open')" /> </nav> <main> {{ $slot }} </main> <x-spotlight /></body>
Security
Example
You can organize your search however you want. Don't be restricted exclusively to the approach shown in this example. But, here an example that mixes "users" and "app actions".
namespace App\Support; use App\Models\User;use Illuminate\Http\Request;use Illuminate\Support\Facades\Blade; class Spotlight{ public function search(Request $request) { // Example of security concern // Guests can not search if(! auth()->user()) { return []; } return collect() ->merge($this->actions($request->search)) ->merge($this->users($request->search)); } // Database search public function users(string $search = '') { return User::query() ->where('name', 'like', "%$search%") ->take(5) ->get() ->map(function (User $user) { return [ 'avatar' => $user->profile_picture, 'name' => $user->name, 'description' => $user->email, 'link' => "/users/{$user->id}" ]; }); } // Static search, but it could come from a database public function actions(string $search = '') { $icon = Blade::render("<x-icon name='o-bolt' class='w-11 h-11 p-2 bg-yellow-50 rounded-full' />"); return collect([ [ 'name' => 'Create user', 'description' => 'Create a new user', 'icon' => $icon, 'link' => '/users/create' ], [ // More ... ] ])->filter(fn(array $item) => str($item['name'] . $item['description'])->contains($search, true)); }}
Options
You can change the shortcut
with any combination supported
by Livewire.
<x-spotlight shortcut="meta.slash" search-text="Find docs, app actions or users" no-results-text="Ops! Nothing here." url="/custom/search/url/here" />
Changing the search class
If for some reason you want to change the search class, publish the config file.
php artisan vendor:publish --tag mary.config
// ...'components' => [ 'spotlight' => [ 'class' => 'App\Support\Spotlight' ]]
Slots
Add anything you want and dispatch a mary-search
event with an extra query string.
You can do it in many ways. But, in this example we built it with Alpine.
<x-spotlight> <div x-data="{ query: { withUsers: true, withActions: true } }" x-init="$watch('query', value => $dispatch('mary-search', new URLSearchParams(value).toString()))" class="flex gap-8 p-3" > <x-checkbox label="Users" x-model="query.withUsers" /> <x-checkbox label="Actions" x-model="query.withActions" /> </div></x-spotlight>
Then, adjust your search
method to handle those new request parameters.
class Spotlight{ public function search(Request $request) { // Do your logic here }}