mirror of
https://github.com/sp-tarkov/forge.git
synced 2025-02-12 20:20:41 -05:00
Global Search Accessibility
Improved the accessibility of the global search field in the header. - When focus is lost, the dropdown disappears - The tab key and up/down arrows can be used to cycle through results - When using the keyboard to cycle through results, focus loops back to the top result - Pressing the esc key will clear the search text and remove the focus lock on the search Resolves #25
This commit is contained in:
parent
9a900bbece
commit
df779135c1
@ -51,8 +51,7 @@ class GlobalSearch extends Component
|
|||||||
$results['total'] = $this->countTotalResults($results['data']);
|
$results['total'] = $this->countTotalResults($results['data']);
|
||||||
}
|
}
|
||||||
|
|
||||||
$this->showDropdown = Str::length($query) > 0;
|
$this->noResults = $results['total'] === 0;
|
||||||
$this->noResults = $results['total'] === 0 && $this->showDropdown;
|
|
||||||
|
|
||||||
return $results;
|
return $results;
|
||||||
}
|
}
|
||||||
@ -94,14 +93,4 @@ class GlobalSearch extends Component
|
|||||||
return $carry + $result->count();
|
return $carry + $result->count();
|
||||||
}, 0);
|
}, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Clear the search query and hide the dropdown.
|
|
||||||
*/
|
|
||||||
public function clearSearch(): void
|
|
||||||
{
|
|
||||||
$this->query = '';
|
|
||||||
$this->showDropdown = false;
|
|
||||||
$this->noResults = false;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,11 @@
|
|||||||
<div id="search-results" aria-live="polite" class="{{ $showDropdown ? 'block' : 'hidden' }} absolute z-10 top-11 w-full mx-auto max-w-2xl transform overflow-hidden rounded-md bg-white dark:bg-gray-900 shadow-2xl border border-gray-300 dark:border-gray-700 transition-all">
|
<div id="search-results"
|
||||||
@if($showDropdown)
|
x-cloak
|
||||||
|
x-show="showDropdown && query.length"
|
||||||
|
x-transition
|
||||||
|
aria-live="polite"
|
||||||
|
class="{{ $showDropdown ? 'block' : 'hidden' }} absolute z-10 top-11 w-full mx-auto max-w-2xl transform overflow-hidden rounded-md bg-white dark:bg-gray-900 shadow-2xl border border-gray-300 dark:border-gray-700 transition-all"
|
||||||
|
>
|
||||||
|
@if ($showDropdown)
|
||||||
<h2 class="sr-only">{{ __('Search Results') }}</h2>
|
<h2 class="sr-only">{{ __('Search Results') }}</h2>
|
||||||
<div class="max-h-96 scroll-py-2 overflow-y-auto" role="list">
|
<div class="max-h-96 scroll-py-2 overflow-y-auto" role="list">
|
||||||
@foreach($results['data'] as $typeName => $typeResults)
|
@foreach($results['data'] as $typeName => $typeResults)
|
||||||
|
@ -1,5 +1,13 @@
|
|||||||
<div class="flex flex-1 justify-center px-2 lg:ml-6 lg:justify-end">
|
<div x-data="{ query: $wire.entangle('query'), showDropdown: $wire.entangle('showDropdown'), noResults: $wire.entangle('noResults') }"
|
||||||
<div class="w-full max-w-lg lg:max-w-md">
|
@keydown.esc.window="showDropdown = false"
|
||||||
|
class="flex flex-1 justify-center px-2 lg:ml-6 lg:justify-end"
|
||||||
|
>
|
||||||
|
<div class="w-full max-w-lg lg:max-w-md"
|
||||||
|
x-trap="showDropdown && query.length"
|
||||||
|
@click.away="showDropdown = false"
|
||||||
|
@keydown.down.prevent="$focus.wrap().next()"
|
||||||
|
@keydown.up.prevent="$focus.wrap().previous()"
|
||||||
|
>
|
||||||
<label for="search" class="sr-only">{{ __('Search') }}</label>
|
<label for="search" class="sr-only">{{ __('Search') }}</label>
|
||||||
<search class="relative group" role="search">
|
<search class="relative group" role="search">
|
||||||
<div class="pointer-events-none absolute inset-y-0 left-0 flex items-center pl-3">
|
<div class="pointer-events-none absolute inset-y-0 left-0 flex items-center pl-3">
|
||||||
@ -8,14 +16,15 @@
|
|||||||
</svg>
|
</svg>
|
||||||
</div>
|
</div>
|
||||||
<input id="global-search"
|
<input id="global-search"
|
||||||
type="search"
|
type="search"
|
||||||
wire:model.live="query"
|
wire:model.live="query"
|
||||||
@keydown.escape.window="$wire.clearSearch()"
|
@focus="showDropdown = true"
|
||||||
placeholder="{{ __('Search') }}"
|
@keydown.escape.window="$wire.query = ''; showDropdown = false; $wire.$refresh()"
|
||||||
aria-controls="search-results"
|
placeholder="{{ __('Search') }}"
|
||||||
aria-expanded="{{ $showDropdown ? 'true' : 'false' }}"
|
aria-controls="search-results"
|
||||||
aria-label="{{ __('Search') }}"
|
:aria-expanded="showDropdown"
|
||||||
class="block w-full rounded-md border-0 bg-white dark:bg-gray-700 py-1.5 pl-10 pr-3 text-gray-900 dark:text-gray-300 ring-1 ring-inset ring-gray-300 dark:ring-gray-700 placeholder:text-gray-400 dark:placeholder:text-gray-400 focus:ring-2 focus:ring-inset focus:ring-gray-600 dark:focus:bg-gray-200 dark:focus:text-black dark:focus:ring-0 sm:text-sm sm:leading-6"
|
aria-label="{{ __('Search') }}"
|
||||||
|
class="block w-full rounded-md border-0 bg-white dark:bg-gray-700 py-1.5 pl-10 pr-3 text-gray-900 dark:text-gray-300 ring-1 ring-inset ring-gray-300 dark:ring-gray-700 placeholder:text-gray-400 dark:placeholder:text-gray-400 focus:ring-2 focus:ring-inset focus:ring-gray-600 dark:focus:bg-gray-200 dark:focus:text-black dark:focus:ring-0 sm:text-sm sm:leading-6"
|
||||||
/>
|
/>
|
||||||
<x-global-search-results :showDropdown="$showDropdown" :noResults="$noResults" :results="$results" />
|
<x-global-search-results :showDropdown="$showDropdown" :noResults="$noResults" :results="$results" />
|
||||||
</search>
|
</search>
|
||||||
|
@ -46,7 +46,10 @@
|
|||||||
|
|
||||||
@auth
|
@auth
|
||||||
{{-- Profile Dropdown --}}
|
{{-- Profile Dropdown --}}
|
||||||
<div x-data="{ profileDropdownOpen: false, openedWithKeyboard: false }" @keydown.esc.window="profileDropdownOpen = false, openedWithKeyboard = false" class="relative">
|
<div x-data="{ profileDropdownOpen: false, openedWithKeyboard: false }"
|
||||||
|
@keydown.esc.window="profileDropdownOpen = false, openedWithKeyboard = false"
|
||||||
|
class="relative"
|
||||||
|
>
|
||||||
<button id="user-menu-button" type="button" @click="profileDropdownOpen = ! profileDropdownOpen" @keydown.space.prevent="openedWithKeyboard = true" @keydown.enter.prevent="openedWithKeyboard = true" @keydown.down.prevent="openedWithKeyboard = true" class="relative flex rounded-full bg-gray-800 text-sm text-white focus:outline-none focus:ring-2 focus:ring-white focus:ring-offset-2 focus:ring-offset-gray-800" :class="profileDropdownOpen || openedWithKeyboard ? 'text-black dark:text-white' : 'text-slate-700 dark:text-slate-300'" :aria-expanded="profileDropdownOpen || openedWithKeyboard" aria-haspopup="true">
|
<button id="user-menu-button" type="button" @click="profileDropdownOpen = ! profileDropdownOpen" @keydown.space.prevent="openedWithKeyboard = true" @keydown.enter.prevent="openedWithKeyboard = true" @keydown.down.prevent="openedWithKeyboard = true" class="relative flex rounded-full bg-gray-800 text-sm text-white focus:outline-none focus:ring-2 focus:ring-white focus:ring-offset-2 focus:ring-offset-gray-800" :class="profileDropdownOpen || openedWithKeyboard ? 'text-black dark:text-white' : 'text-slate-700 dark:text-slate-300'" :aria-expanded="profileDropdownOpen || openedWithKeyboard" aria-haspopup="true">
|
||||||
<span class="absolute -inset-1.5"></span>
|
<span class="absolute -inset-1.5"></span>
|
||||||
<span class="sr-only">{{ __('Open user menu') }}</span>
|
<span class="sr-only">{{ __('Open user menu') }}</span>
|
||||||
@ -60,7 +63,8 @@
|
|||||||
@keydown.down.prevent="$focus.wrap().next()"
|
@keydown.down.prevent="$focus.wrap().next()"
|
||||||
@keydown.up.prevent="$focus.wrap().previous()"
|
@keydown.up.prevent="$focus.wrap().previous()"
|
||||||
class="absolute top-11 right-0 z-10 flex w-full min-w-[12rem] flex-col divide-y divide-slate-300 overflow-hidden rounded-xl border border-gray-300 bg-gray-100 dark:divide-gray-700 dark:border-gray-700 dark:bg-gray-800"
|
class="absolute top-11 right-0 z-10 flex w-full min-w-[12rem] flex-col divide-y divide-slate-300 overflow-hidden rounded-xl border border-gray-300 bg-gray-100 dark:divide-gray-700 dark:border-gray-700 dark:bg-gray-800"
|
||||||
role="menu">
|
role="menu"
|
||||||
|
>
|
||||||
<div class="flex flex-col py-1.5">
|
<div class="flex flex-col py-1.5">
|
||||||
<a href="{{ route('dashboard') }}" class="flex items-center gap-2 bg-slate-100 px-4 py-2 text-sm text-slate-700 hover:bg-slate-800/5 hover:text-black focus-visible:bg-slate-800/10 focus-visible:text-black focus-visible:outline-none dark:bg-slate-800 dark:text-slate-300 dark:hover:bg-slate-100/5 dark:hover:text-white dark:focus-visible:bg-slate-100/10 dark:focus-visible:text-white" role="menuitem">
|
<a href="{{ route('dashboard') }}" class="flex items-center gap-2 bg-slate-100 px-4 py-2 text-sm text-slate-700 hover:bg-slate-800/5 hover:text-black focus-visible:bg-slate-800/10 focus-visible:text-black focus-visible:outline-none dark:bg-slate-800 dark:text-slate-300 dark:hover:bg-slate-100/5 dark:hover:text-white dark:focus-visible:bg-slate-100/10 dark:focus-visible:text-white" role="menuitem">
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="currentColor" class="w-4 h-4">
|
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="currentColor" class="w-4 h-4">
|
||||||
|
Loading…
x
Reference in New Issue
Block a user