Files
livewire-material/resources/views/components/search.blade.php
T
Andreas Reinhold / reiniandClaude Opus 5 1e580c573e Resolve the package's own components through its namespace
Package views now write <x-livewire-material::button>, so a configured
prefix (which Blade spells <x-m::button>) and an application component
of the same name can no longer break or shadow them. The showcase
rewrites its examples to the configured prefix. Adds the README, and
waits for the adaptive rail to hear a resize before the navigation
tests press its menu button.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01V9NnLxnPp8vaaurb3Z1MFy
2026-09-13 09:05:24 +02:00

92 lines
3.5 KiB
PHP

{{-- M3's search: a search bar that opens into a search view with the results.
Bind the input like any other (`wire:model.live.debounce.300ms="query"`) and render the
results in the slot, from a Livewire property or computed property that follows the query;
`empty` is shown instead when the slot renders nothing (say, "No shares match"). Results are
usually `<x-list-item>`s with a `link`, or buttons: choosing one closes the view.
Docked under the bar from `sm`, full screen below it with a back arrow (resources/css/
components/search.css); `docked` keeps it docked at every width. `placeholder` ("Search"),
`label` (the input's name when it differs from the placeholder), leading `icon` (`search`), and
a `trailing` slot for an avatar or icon buttons in the bar. Every other attribute reaches the
`<input type="search">`. --}}
@props([
'placeholder' => null,
'label' => null,
'icon' => 'search',
'docked' => false,
])
@php
$model = $attributes->wire('model')->value() ?: null;
$placeholder ??= __('Search');
$id = $attributes->get('id') ?? 'material-search-'.substr(md5($model.'|'.$placeholder), 0, 10);
@endphp
<div
x-data="materialSearch({{ $docked ? 'true' : 'false' }})"
x-on:keydown.escape="if (open) { $event.stopPropagation(); close(true); }"
x-on:focusout="leave($event)"
x-on:pointerdown.outside="close()"
x-trap.noscroll="fullScreen"
x-bind:data-open="open ? '' : null"
x-bind:data-full-screen="fullScreen ? '' : null"
data-search
{{ $attributes->only(['class', 'wire:key'])->class(['relative']) }}
>
<div data-search-bar role="search" x-on:click="if ($event.target === $el) $refs.input.focus()">
<span data-search-leading x-show="! fullScreen">
<x-livewire-material::icon :name="$icon" />
</span>
<button type="button" data-search-leading data-search-back x-show="fullScreen" x-cloak x-on:click="close()" aria-label="{{ __('Back') }}">
<x-livewire-material::icon name="arrow_back" />
</button>
<input
{{ $attributes->except(['class', 'wire:key', 'id', 'placeholder', 'type']) }}
x-ref="input"
id="{{ $id }}"
type="search"
autocomplete="off"
enterkeyhint="search"
placeholder="{{ $placeholder }}"
aria-label="{{ $label ?? $placeholder }}"
aria-controls="{{ $id }}-view"
aria-expanded="false"
x-bind:aria-expanded="open.toString()"
x-on:focus="focused()"
x-on:click="show()"
x-on:input="show()"
x-on:keydown.arrow-down.prevent="show(); $nextTick(() => step(1))"
data-search-input
/>
<button type="button" data-search-clear x-on:click="clear()" aria-label="{{ __('Clear') }}">
<x-livewire-material::icon name="close" />
</button>
@isset($trailing)
<span data-search-trailing>{{ $trailing }}</span>
@endisset
</div>
<div
x-ref="view"
id="{{ $id }}-view"
data-search-view
x-cloak
x-show="open"
x-on:keydown.arrow-down.prevent="step(1)"
x-on:keydown.arrow-up.prevent="step(-1)"
x-on:click="choose($event)"
>
@if ($slot->hasActualContent())
<div data-search-results>{{ $slot }}</div>
@elseif (isset($empty))
<div data-search-results><p class="px-4 py-3 type-body-md text-on-surface-variant">{{ $empty }}</p></div>
@endif
</div>
</div>