mirror of
https://github.com/sp-tarkov/forge.git
synced 2025-02-12 12:10:41 -05:00
Global Search Structure
Reconfigured the global search to include more than one model. Refactored the search front-end to work inline instead of inside a model/popup.
This commit is contained in:
parent
c335825bfd
commit
a1504fe622
1
.gitignore
vendored
1
.gitignore
vendored
@ -22,3 +22,4 @@ Homestead.json
|
||||
Homestead.yaml
|
||||
npm-debug.log
|
||||
yarn-error.log
|
||||
config/psysh
|
||||
|
@ -3,19 +3,74 @@
|
||||
namespace App\Livewire;
|
||||
|
||||
use App\Models\Mod;
|
||||
use App\Models\User;
|
||||
use Illuminate\Support\Str;
|
||||
use Illuminate\View\View;
|
||||
use Livewire\Component;
|
||||
|
||||
class GlobalSearch extends Component
|
||||
{
|
||||
/**
|
||||
* The search query.
|
||||
*/
|
||||
public string $query = '';
|
||||
|
||||
/**
|
||||
* Whether to show the search result dropdown.
|
||||
*/
|
||||
public bool $showDropdown = false;
|
||||
|
||||
/**
|
||||
* Whether to show the "no results found" message.
|
||||
*/
|
||||
public bool $noResults = false;
|
||||
|
||||
public function render(): View
|
||||
{
|
||||
$results = $this->query ? Mod::search($this->query)->get() : collect();
|
||||
|
||||
return view('livewire.global-search', [
|
||||
'results' => $results,
|
||||
'results' => $this->executeSearch($this->query),
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute the search against each of the searchable models.
|
||||
*/
|
||||
protected function executeSearch(string $query): array
|
||||
{
|
||||
$query = Str::trim($query);
|
||||
$results = ['data' => [], 'total' => 0];
|
||||
|
||||
if (Str::length($query)) {
|
||||
$results['data'] = [
|
||||
'user' => User::search($query)->get(),
|
||||
'mod' => Mod::search($query)->get(),
|
||||
];
|
||||
$results['total'] = $this->countTotalResults($results['data']);
|
||||
}
|
||||
|
||||
$this->showDropdown = Str::length($query) > 0;
|
||||
$this->noResults = $results['total'] === 0 && $this->showDropdown;
|
||||
|
||||
return $results;
|
||||
}
|
||||
|
||||
/**
|
||||
* Count the total number of results across all models.
|
||||
*/
|
||||
protected function countTotalResults($results): int
|
||||
{
|
||||
return collect($results)->reduce(function ($carry, $result) {
|
||||
return $carry + $result->count();
|
||||
}, 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Clear the search query and hide the dropdown.
|
||||
*/
|
||||
public function clearSearch(): void
|
||||
{
|
||||
$this->query = '';
|
||||
$this->showDropdown = false;
|
||||
$this->noResults = false;
|
||||
}
|
||||
}
|
||||
|
@ -74,7 +74,7 @@ class User extends Authenticatable implements MustVerifyEmail
|
||||
|
||||
public function isMod(): bool
|
||||
{
|
||||
return Str::lower($this->role?->name) === 'moderator' || $this->isAdmin();
|
||||
return Str::lower($this->role?->name) === 'moderator';
|
||||
}
|
||||
|
||||
public function isAdmin(): bool
|
||||
|
@ -49,6 +49,9 @@
|
||||
}
|
||||
},
|
||||
"scripts": {
|
||||
"phpstan": [
|
||||
"./vendor/bin/phpstan analyse --debug --memory-limit=2G"
|
||||
],
|
||||
"post-autoload-dump": [
|
||||
"Illuminate\\Foundation\\ComposerScripts::postAutoloadDump",
|
||||
"@php artisan package:discover --ansi",
|
||||
|
487
composer.lock
generated
487
composer.lock
generated
File diff suppressed because it is too large
Load Diff
@ -19,7 +19,7 @@ return [
|
||||
|
|
||||
*/
|
||||
|
||||
'driver' => env('SCOUT_DRIVER', 'algolia'),
|
||||
'driver' => env('SCOUT_DRIVER', 'meilisearch'),
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
@ -137,9 +137,13 @@ return [
|
||||
'host' => env('MEILISEARCH_HOST', 'http://localhost:7700'),
|
||||
'key' => env('MEILISEARCH_KEY'),
|
||||
'index-settings' => [
|
||||
User::class => [
|
||||
'filterableAttributes' => [],
|
||||
'sortableAttributes' => [],
|
||||
],
|
||||
Mod::class => [
|
||||
'filterableAttributes' => ['featured'],
|
||||
'sortableAttributes' => ['created_at', 'updated_at'],
|
||||
'filterableAttributes' => [],
|
||||
'sortableAttributes' => [],
|
||||
],
|
||||
],
|
||||
],
|
||||
|
@ -13,8 +13,8 @@ class LicenseFactory extends Factory
|
||||
public function definition(): array
|
||||
{
|
||||
return [
|
||||
'name' => $this->faker->name(),
|
||||
'link' => $this->faker->url,
|
||||
'name' => fake()->name(),
|
||||
'link' => fake()->url(),
|
||||
'created_at' => Carbon::now(),
|
||||
'updated_at' => Carbon::now(),
|
||||
];
|
||||
|
@ -7,27 +7,31 @@ use App\Models\Mod;
|
||||
use App\Models\User;
|
||||
use Illuminate\Database\Eloquent\Factories\Factory;
|
||||
use Illuminate\Support\Str;
|
||||
use Random\RandomException;
|
||||
|
||||
class ModFactory extends Factory
|
||||
{
|
||||
protected $model = Mod::class;
|
||||
|
||||
/**
|
||||
* @throws RandomException
|
||||
*/
|
||||
public function definition(): array
|
||||
{
|
||||
$name = $this->faker->words(3, true);
|
||||
$name = fake()->catchPhrase();
|
||||
|
||||
return [
|
||||
'user_id' => User::factory(),
|
||||
'name' => $name,
|
||||
'slug' => Str::slug($name),
|
||||
'teaser' => $this->faker->sentence,
|
||||
'description' => $this->faker->sentences(6, true),
|
||||
'teaser' => fake()->sentence(),
|
||||
'description' => fake()->paragraphs(random_int(4, 20), true),
|
||||
'license_id' => License::factory(),
|
||||
'source_code_link' => $this->faker->url(),
|
||||
'featured' => $this->faker->boolean,
|
||||
'contains_ai_content' => $this->faker->boolean,
|
||||
'contains_ads' => $this->faker->boolean,
|
||||
'disabled' => $this->faker->boolean,
|
||||
'source_code_link' => fake()->url(),
|
||||
'featured' => fake()->boolean(),
|
||||
'contains_ai_content' => fake()->boolean(),
|
||||
'contains_ads' => fake()->boolean(),
|
||||
'disabled' => fake()->boolean(),
|
||||
'created_at' => now(),
|
||||
'updated_at' => now(),
|
||||
];
|
||||
|
@ -16,13 +16,13 @@ class ModVersionFactory extends Factory
|
||||
{
|
||||
return [
|
||||
'mod_id' => Mod::factory(),
|
||||
'version' => $this->faker->numerify('1.#.#'),
|
||||
'description' => $this->faker->text(),
|
||||
'link' => $this->faker->url(),
|
||||
'version' => fake()->numerify('1.#.#'),
|
||||
'description' => fake()->text(),
|
||||
'link' => fake()->url(),
|
||||
'spt_version_id' => SptVersion::factory(),
|
||||
'virus_total_link' => $this->faker->url(),
|
||||
'downloads' => $this->faker->randomNumber(),
|
||||
'disabled' => $this->faker->boolean,
|
||||
'virus_total_link' => fake()->url(),
|
||||
'downloads' => fake()->randomNumber(),
|
||||
'disabled' => fake()->boolean(),
|
||||
'created_at' => Carbon::now(),
|
||||
'updated_at' => Carbon::now(),
|
||||
];
|
||||
|
@ -19,7 +19,7 @@ class UserFactory extends Factory
|
||||
public function definition(): array
|
||||
{
|
||||
return [
|
||||
'name' => fake()->name(),
|
||||
'name' => fake()->userName(),
|
||||
'email' => fake()->unique()->safeEmail(),
|
||||
'email_verified_at' => now(),
|
||||
'password' => static::$password ??= Hash::make('password'),
|
||||
|
40
package-lock.json
generated
40
package-lock.json
generated
@ -1013,9 +1013,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/caniuse-lite": {
|
||||
"version": "1.0.30001638",
|
||||
"resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001638.tgz",
|
||||
"integrity": "sha512-5SuJUJ7cZnhPpeLHaH0c/HPAnAHZvS6ElWyHK9GSIbVOQABLzowiI2pjmpvZ1WEbkyz46iFd4UXlOHR5SqgfMQ==",
|
||||
"version": "1.0.30001640",
|
||||
"resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001640.tgz",
|
||||
"integrity": "sha512-lA4VMpW0PSUrFnkmVuEKBUovSWKhj7puyCg8StBChgu298N1AtuF1sKWEvfDuimSEDbhlb/KqPKC3fs1HbuQUA==",
|
||||
"dev": true,
|
||||
"funding": [
|
||||
{
|
||||
@ -1161,9 +1161,9 @@
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/electron-to-chromium": {
|
||||
"version": "1.4.812",
|
||||
"resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.812.tgz",
|
||||
"integrity": "sha512-7L8fC2Ey/b6SePDFKR2zHAy4mbdp1/38Yk5TsARO66W3hC5KEaeKMMHoxwtuH+jcu2AYLSn9QX04i95t6Fl1Hg==",
|
||||
"version": "1.4.816",
|
||||
"resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.816.tgz",
|
||||
"integrity": "sha512-EKH5X5oqC6hLmiS7/vYtZHZFTNdhsYG5NVPRN6Yn0kQHNBlT59+xSM8HBy66P5fxWpKgZbPqb+diC64ng295Jw==",
|
||||
"dev": true,
|
||||
"license": "ISC"
|
||||
},
|
||||
@ -1581,9 +1581,9 @@
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/lru-cache": {
|
||||
"version": "10.2.2",
|
||||
"resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.2.2.tgz",
|
||||
"integrity": "sha512-9hp3Vp2/hFQUiIwKo8XCeFVnrg8Pk3TYNPIR7tJADKi5YfcF7vEaK7avFHTlSy3kOKYaJQaalfEo6YuXdceBOQ==",
|
||||
"version": "10.3.0",
|
||||
"resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.3.0.tgz",
|
||||
"integrity": "sha512-CQl19J/g+Hbjbv4Y3mFNNXFEL/5t/KCg8POCuUqd4rMKjGG+j1ybER83hxV58zL+dFI1PTkt3GNFSHRt+d8qEQ==",
|
||||
"dev": true,
|
||||
"license": "ISC",
|
||||
"engines": {
|
||||
@ -1833,9 +1833,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/postcss": {
|
||||
"version": "8.4.38",
|
||||
"resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.38.tgz",
|
||||
"integrity": "sha512-Wglpdk03BSfXkHoQa3b/oulrotAkwrlLDRSOb9D0bN86FdRyE9lppSp33aHNPgBa0JKCoB+drFLZkQoRRYae5A==",
|
||||
"version": "8.4.39",
|
||||
"resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.39.tgz",
|
||||
"integrity": "sha512-0vzE+lAiG7hZl1/9I8yzKLx3aR9Xbof3fBHKunvMfOCYAtMhrsnccJY2iTURb9EZd5+pLuiNV9/c/GZJOHsgIw==",
|
||||
"dev": true,
|
||||
"funding": [
|
||||
{
|
||||
@ -1854,7 +1854,7 @@
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"nanoid": "^3.3.7",
|
||||
"picocolors": "^1.0.0",
|
||||
"picocolors": "^1.0.1",
|
||||
"source-map-js": "^1.2.0"
|
||||
},
|
||||
"engines": {
|
||||
@ -2535,9 +2535,9 @@
|
||||
"license": "Apache-2.0"
|
||||
},
|
||||
"node_modules/update-browserslist-db": {
|
||||
"version": "1.0.16",
|
||||
"resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.0.16.tgz",
|
||||
"integrity": "sha512-KVbTxlBYlckhF5wgfyZXTWnMn7MMZjMu9XG8bPlliUOP9ThaF4QnhP8qrjrH7DRzHfSk0oQv1wToW+iA5GajEQ==",
|
||||
"version": "1.1.0",
|
||||
"resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.1.0.tgz",
|
||||
"integrity": "sha512-EdRAaAyk2cUE1wOf2DkEhzxqOQvFOoRJFNS6NeyJ01Gp2beMRpBAINjM2iDXE3KCuKhwnvHIQCJm6ThL2Z+HzQ==",
|
||||
"dev": true,
|
||||
"funding": [
|
||||
{
|
||||
@ -2573,14 +2573,14 @@
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/vite": {
|
||||
"version": "5.3.1",
|
||||
"resolved": "https://registry.npmjs.org/vite/-/vite-5.3.1.tgz",
|
||||
"integrity": "sha512-XBmSKRLXLxiaPYamLv3/hnP/KXDai1NDexN0FpkTaZXTfycHvkRHoenpgl/fvuK/kPbB6xAgoyiryAhQNxYmAQ==",
|
||||
"version": "5.3.3",
|
||||
"resolved": "https://registry.npmjs.org/vite/-/vite-5.3.3.tgz",
|
||||
"integrity": "sha512-NPQdeCU0Dv2z5fu+ULotpuq5yfCS1BzKUIPhNbP3YBfAMGJXbt2nS+sbTFu+qchaqWTD+H3JK++nRwr6XIcp6A==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"esbuild": "^0.21.3",
|
||||
"postcss": "^8.4.38",
|
||||
"postcss": "^8.4.39",
|
||||
"rollup": "^4.13.0"
|
||||
},
|
||||
"bin": {
|
||||
|
6
public/vendor/livewire/livewire.esm.js
vendored
6
public/vendor/livewire/livewire.esm.js
vendored
@ -8660,7 +8660,7 @@ function extractDestinationFromLink(linkEl) {
|
||||
return createUrlObjectFromString(linkEl.getAttribute("href"));
|
||||
}
|
||||
function createUrlObjectFromString(urlString) {
|
||||
return new URL(urlString, document.baseURI);
|
||||
return urlString !== null && new URL(urlString, document.baseURI);
|
||||
}
|
||||
function getUriStringFromUrlObject(urlObject) {
|
||||
return urlObject.pathname + urlObject.search + urlObject.hash;
|
||||
@ -9087,12 +9087,16 @@ function navigate_default(Alpine19) {
|
||||
let shouldPrefetchOnHover = modifiers.includes("hover");
|
||||
shouldPrefetchOnHover && whenThisLinkIsHoveredFor(el, 60, () => {
|
||||
let destination = extractDestinationFromLink(el);
|
||||
if (!destination)
|
||||
return;
|
||||
prefetchHtml(destination, (html, finalDestination) => {
|
||||
storeThePrefetchedHtmlForWhenALinkIsClicked(html, destination, finalDestination);
|
||||
});
|
||||
});
|
||||
whenThisLinkIsPressed(el, (whenItIsReleased) => {
|
||||
let destination = extractDestinationFromLink(el);
|
||||
if (!destination)
|
||||
return;
|
||||
prefetchHtml(destination, (html, finalDestination) => {
|
||||
storeThePrefetchedHtmlForWhenALinkIsClicked(html, destination, finalDestination);
|
||||
});
|
||||
|
6
public/vendor/livewire/livewire.js
vendored
6
public/vendor/livewire/livewire.js
vendored
@ -7305,7 +7305,7 @@ ${expression ? 'Expression: "' + expression + '"\n\n' : ""}`, el);
|
||||
return createUrlObjectFromString(linkEl.getAttribute("href"));
|
||||
}
|
||||
function createUrlObjectFromString(urlString) {
|
||||
return new URL(urlString, document.baseURI);
|
||||
return urlString !== null && new URL(urlString, document.baseURI);
|
||||
}
|
||||
function getUriStringFromUrlObject(urlObject) {
|
||||
return urlObject.pathname + urlObject.search + urlObject.hash;
|
||||
@ -7730,12 +7730,16 @@ ${expression ? 'Expression: "' + expression + '"\n\n' : ""}`, el);
|
||||
let shouldPrefetchOnHover = modifiers.includes("hover");
|
||||
shouldPrefetchOnHover && whenThisLinkIsHoveredFor(el, 60, () => {
|
||||
let destination = extractDestinationFromLink(el);
|
||||
if (!destination)
|
||||
return;
|
||||
prefetchHtml(destination, (html, finalDestination) => {
|
||||
storeThePrefetchedHtmlForWhenALinkIsClicked(html, destination, finalDestination);
|
||||
});
|
||||
});
|
||||
whenThisLinkIsPressed(el, (whenItIsReleased) => {
|
||||
let destination = extractDestinationFromLink(el);
|
||||
if (!destination)
|
||||
return;
|
||||
prefetchHtml(destination, (html, finalDestination) => {
|
||||
storeThePrefetchedHtmlForWhenALinkIsClicked(html, destination, finalDestination);
|
||||
});
|
||||
|
4
public/vendor/livewire/livewire.min.js
vendored
4
public/vendor/livewire/livewire.min.js
vendored
File diff suppressed because one or more lines are too long
4
public/vendor/livewire/livewire.min.js.map
vendored
4
public/vendor/livewire/livewire.min.js.map
vendored
File diff suppressed because one or more lines are too long
2
public/vendor/livewire/manifest.json
vendored
2
public/vendor/livewire/manifest.json
vendored
@ -1,2 +1,2 @@
|
||||
|
||||
{"/livewire.js":"87e1046f"}
|
||||
{"/livewire.js":"c4fc8c5d"}
|
||||
|
@ -0,0 +1,9 @@
|
||||
<a href="/mod/{{ $result->id }}/{{ $result->slug }}" class="{{ $linkClass }}" role="listitem" tabindex="0">
|
||||
@if(empty($result->thumbnail))
|
||||
<img src="https://placehold.co/450x450/EEE/31343C?font=source-sans-pro&text={{ $result->name }}" alt="{{ $result->name }}" class="block dark:hidden h-6 w-6 flex-none border border-gray-200 group-hover/global-search-link:border-gray-400">
|
||||
<img src="https://placehold.co/450x450/31343C/EEE?font=source-sans-pro&text={{ $result->name }}" alt="{{ $result->name }}" class="hidden dark:block h-6 w-6 flex-none border border-gray-700 group-hover/global-search-link:border-gray-600">
|
||||
@else
|
||||
<img src="{{ Storage::url($result->thumbnail) }}" alt="{{ $result->name }}" class="h-6 w-6 flex-none">
|
||||
@endif
|
||||
<p>{{ $result->name }}</p>
|
||||
</a>
|
@ -0,0 +1,3 @@
|
||||
<a href="#/{{ Str::slug($result->name) }}" class="{{ $linkClass }}">
|
||||
<p>{{ $result->name }}</p>
|
||||
</a>
|
32
resources/views/components/global-search-results.blade.php
Normal file
32
resources/views/components/global-search-results.blade.php
Normal file
@ -0,0 +1,32 @@
|
||||
<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">
|
||||
@if($showDropdown)
|
||||
<h2 class="sr-only">{{ __('Search Results') }}</h2>
|
||||
<div class="max-h-96 scroll-py-2 overflow-y-auto" role="list">
|
||||
@foreach($results['data'] as $typeName => $typeResults)
|
||||
@if($typeResults->count())
|
||||
<h4 class="flex flex-row gap-1.5 py-2.5 px-4 text-[0.6875rem] font-semibold uppercase text-gray-700 dark:text-gray-300 bg-gray-100 dark:bg-gray-950">
|
||||
<span>{{ Str::plural($typeName) }}</span>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" class="w-4 h-4">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" d="m19.5 8.25-7.5 7.5-7.5-7.5" />
|
||||
</svg>
|
||||
</h4>
|
||||
@foreach($typeResults as $result)
|
||||
@component('components.global-search-result-' . Str::lower($typeName), [
|
||||
'result' => $result,
|
||||
'linkClass' => 'group/global-search-link flex flex-row gap-3 py-1.5 px-4 text-gray-900 dark:text-gray-100 hover:bg-gray-200 dark:hover:bg-gray-800 transition-colors duration-100 ease-in-out',
|
||||
])
|
||||
@endcomponent
|
||||
@endforeach
|
||||
@endif
|
||||
@endforeach
|
||||
</div>
|
||||
@endif
|
||||
@if($noResults)
|
||||
<div class="px-6 py-14 text-center sm:px-14">
|
||||
<svg class="mx-auto h-6 w-6 text-gray-400 dark:text-gray-500" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" aria-hidden="true">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" d="M19.5 14.25v-2.625a3.375 3.375 0 0 0-3.375-3.375h-1.5A1.125 1.125 0 0 1 13.5 7.125v-1.5a3.375 3.375 0 0 0-3.375-3.375H8.25m5.231 13.481L15 17.25m-4.5-15H5.625c-.621 0-1.125.504-1.125 1.125v16.5c0 .621.504 1.125 1.125 1.125h12.75c.621 0 1.125-.504 1.125-1.125V11.25a9 9 0 0 0-9-9Zm3.75 11.625a2.625 2.625 0 1 1-5.25 0 2.625 2.625 0 0 1 5.25 0Z" />
|
||||
</svg>
|
||||
<p class="mt-4 text-sm text-gray-900 dark:text-gray-200">{{ __("We couldn't find any content with that query. Please try again.") }}</p>
|
||||
</div>
|
||||
@endif
|
||||
</div>
|
@ -29,7 +29,7 @@
|
||||
@vite(['resources/css/app.css', 'resources/js/app.js'])
|
||||
@livewireStyles
|
||||
</head>
|
||||
<body class="font-sans antialiased" x-data="{ searchOpen: false }">
|
||||
<body class="font-sans antialiased">
|
||||
|
||||
<x-warning/>
|
||||
|
||||
@ -53,9 +53,7 @@
|
||||
|
||||
<x-footer/>
|
||||
|
||||
@livewire('global-search')
|
||||
@stack('modals')
|
||||
|
||||
@livewireScriptConfig
|
||||
</body>
|
||||
</html>
|
||||
|
@ -1,39 +1,23 @@
|
||||
<div class="relative z-10" role="dialog" aria-modal="true">
|
||||
<div x-cloak x-show="searchOpen" x-transition:enter="transition ease-out duration-300 transform" x-transition:enter-start="opacity-0" x-transition:enter-end="opacity-100" x-transition:leave="transition ease-in duration-200 transform" x-transition:leave-start="opacity-100" x-transition:leave-end="opacity-0" class="fixed inset-0 bg-gray-900 dark:bg-gray-400 bg-opacity-80 dark:bg-opacity-80 transition-opacity"></div>
|
||||
<div x-cloak x-show="searchOpen" x-transition:enter="transition ease-out duration-300 transform" x-transition:enter-start="opacity-0" x-transition:enter-end="opacity-100" x-transition:leave="transition ease-in duration-200 transform" x-transition:leave-start="opacity-100" x-transition:leave-end="opacity-0" @keyup.escape.window="searchOpen = false" class="fixed inset-0 z-10 w-screen overflow-y-auto p-4 sm:p-6 md:p-20">
|
||||
<div @click.outside="searchOpen = false" class="mx-auto max-w-2xl transform divide-y divide-gray-100 dark:divide-gray-500 overflow-hidden rounded-xl bg-white dark:bg-gray-900 shadow-2xl ring-1 ring-black ring-opacity-5 transition-all">
|
||||
<div class="relative">
|
||||
<svg class="pointer-events-none absolute left-4 top-3.5 h-5 w-5 text-gray-400" viewBox="0 0 20 20" fill="currentColor" aria-hidden="true">
|
||||
<path fill-rule="evenodd" d="M9 3.5a5.5 5.5 0 100 11 5.5 5.5 0 000-11zM2 9a7 7 0 1112.452 4.391l3.328 3.329a.75.75 0 11-1.06 1.06l-3.329-3.328A7 7 0 012 9z" clip-rule="evenodd"/>
|
||||
<div 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">
|
||||
<label for="search" class="sr-only">{{ __('Search') }}</label>
|
||||
<search class="relative group" role="search">
|
||||
<div class="pointer-events-none absolute inset-y-0 left-0 flex items-center pl-3">
|
||||
<svg class="h-5 w-5 text-gray-400" viewBox="0 0 20 20" fill="currentColor" aria-hidden="true">
|
||||
<path fill-rule="evenodd" d="M9 3.5a5.5 5.5 0 100 11 5.5 5.5 0 000-11zM2 9a7 7 0 1112.452 4.391l3.328 3.329a.75.75 0 11-1.06 1.06l-3.329-3.328A7 7 0 012 9z" clip-rule="evenodd" />
|
||||
</svg>
|
||||
<input wire:model.live="query" id="global-search" type="text" class="h-12 w-full border-0 bg-transparent pl-11 pr-4 text-gray-900 dark:text-white placeholder:text-gray-400 focus:ring-0 sm:text-sm" placeholder="{{ __('Search for a mod...') }}">
|
||||
</div>
|
||||
|
||||
@if($results->count() && $this->query)
|
||||
<ul class="max-h-80 scroll-py-2 divide-y divide-gray-100 dark:divide-gray-500 overflow-y-auto">
|
||||
<h2 class="sr-only">{{ __('Search Results') }}</h2>
|
||||
@foreach($results as $result)
|
||||
<li class="text-sm group">
|
||||
<a href="/mod/{{ $result->id }}/{{ $result->slug }}" class="block w-full group flex select-none items-center rounded-md p-3 text-gray-700 hover:text-black focus:text-black dark:text-gray-400 dark:hover:text-white">
|
||||
@if(empty($result->thumbnail))
|
||||
<img src="https://placehold.co/450x450/EEE/31343C?font=source-sans-pro&text={{ $result->name }}" alt="{{ $result->name }}" class="block dark:hidden h-6 w-6 flex-none">
|
||||
<img src="https://placehold.co/450x450/31343C/EEE?font=source-sans-pro&text={{ $result->name }}" alt="{{ $result->name }}" class="hidden dark:block h-6 w-6 flex-none">
|
||||
@else
|
||||
<img src="{{ Storage::url($result->thumbnail) }}" alt="{{ $result->name }}" class="h-6 w-6 flex-none">
|
||||
@endif
|
||||
<span class="ml-3 flex-auto truncate">{{ $result->name }}</span>
|
||||
<span class="ml-3 flex-none text-xs font-semibold text-gray-400">Mod</span> </a>
|
||||
</li>
|
||||
@endforeach
|
||||
</ul>
|
||||
@elseif(!$results->count() && $this->query)
|
||||
<div class="px-6 py-14 text-center sm:px-14">
|
||||
<svg class="mx-auto h-6 w-6 text-gray-400 dark:text-gray-500" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" aria-hidden="true">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" d="M19.5 14.25v-2.625a3.375 3.375 0 0 0-3.375-3.375h-1.5A1.125 1.125 0 0 1 13.5 7.125v-1.5a3.375 3.375 0 0 0-3.375-3.375H8.25m5.231 13.481L15 17.25m-4.5-15H5.625c-.621 0-1.125.504-1.125 1.125v16.5c0 .621.504 1.125 1.125 1.125h12.75c.621 0 1.125-.504 1.125-1.125V11.25a9 9 0 0 0-9-9Zm3.75 11.625a2.625 2.625 0 1 1-5.25 0 2.625 2.625 0 0 1 5.25 0Z"/>
|
||||
</svg>
|
||||
<p class="mt-4 text-sm text-gray-900 dark:text-gray-200">{{ __("We couldn't find any content with that query. Please try again.") }}</p>
|
||||
</div>
|
||||
@endif
|
||||
</div>
|
||||
<input id="global-search"
|
||||
type="search"
|
||||
wire:model.live="query"
|
||||
@keydown.escape.window="$wire.clearSearch()"
|
||||
placeholder="{{ __('Search') }}"
|
||||
aria-controls="search-results"
|
||||
aria-expanded="{{ $showDropdown ? 'true' : 'false' }}"
|
||||
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" />
|
||||
</search>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -16,19 +16,9 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="flex flex-1 justify-center px-2 lg:ml-6 lg:justify-end">
|
||||
<div class="w-full max-w-lg lg:max-w-xs">
|
||||
<label for="search" class="sr-only">{{ __('Search') }}</label>
|
||||
<div class="relative group">
|
||||
<div class="pointer-events-none absolute inset-y-0 left-0 flex items-center pl-3">
|
||||
<svg class="h-5 w-5 text-gray-400" viewBox="0 0 20 20" fill="currentColor" aria-hidden="true">
|
||||
<path fill-rule="evenodd" d="M9 3.5a5.5 5.5 0 100 11 5.5 5.5 0 000-11zM2 9a7 7 0 1112.452 4.391l3.328 3.329a.75.75 0 11-1.06 1.06l-3.329-3.328A7 7 0 012 9z" clip-rule="evenodd" />
|
||||
</svg>
|
||||
</div>
|
||||
<input id="search" name="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" placeholder="{{ __('Search') }}" type="search">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@livewire('global-search')
|
||||
|
||||
<div class="flex lg:hidden">
|
||||
{{-- Mobile Menu Button --}}
|
||||
<button @click="mobileMenuOpen = !mobileMenuOpen" type="button" class="relative inline-flex items-center justify-center rounded-md p-2 text-gray-400 hover:bg-gray-700 hover:text-white focus:outline-none focus:ring-2 focus:ring-inset focus:ring-white" aria-controls="mobile-menu" :aria-expanded="mobileMenuOpen">
|
||||
|
Loading…
x
Reference in New Issue
Block a user