Merge branch 'listing-per-page' into develop

This commit is contained in:
Refringe 2024-10-07 21:27:55 -06:00
commit 5cfffef2b4
Signed by: Refringe
SSH Key Fingerprint: SHA256:t865XsQpfTeqPRBMN2G6+N8wlDjkgUCZF3WGW6O9N/k
5 changed files with 100 additions and 18 deletions

View File

@ -9,6 +9,7 @@ use Illuminate\Contracts\View\View;
use Illuminate\Database\Eloquent\Collection;
use Illuminate\Support\Facades\Cache;
use Livewire\Attributes\Computed;
use Livewire\Attributes\Locked;
use Livewire\Attributes\Session;
use Livewire\Attributes\Url;
use Livewire\Component;
@ -32,6 +33,19 @@ class Listing extends Component
#[Url]
public string $order = 'created';
/**
* The number of results to show on a single page.
*/
#[Session]
#[Url]
public int $perPage = 12;
/**
* The options that are available for the per page setting.
*/
#[Locked]
public array $perPageOptions = [6, 12, 24, 50];
/**
* The SPT versions filter value.
*/
@ -62,7 +76,15 @@ class Listing extends Component
return SptVersion::getVersionsForLastThreeMinors();
});
$this->sptVersions = $this->sptVersions ?? $this->getLatestMinorVersions()->pluck('version')->toArray();
$this->sptVersions = $this->sptVersions ?? $this->getDefaultSptVersions();
}
/**
* Get the default values for the SPT Versions filter.
*/
protected function getDefaultSptVersions(): array
{
return $this->getLatestMinorVersions()->pluck('version')->toArray();
}
/**
@ -80,6 +102,8 @@ class Listing extends Component
*/
public function render(): View
{
$this->validatePerPage();
// Fetch the mods using the filters saved to the component properties.
$filters = [
'query' => $this->query,
@ -87,13 +111,32 @@ class Listing extends Component
'order' => $this->order,
'sptVersions' => $this->sptVersions,
];
$mods = (new ModFilter($filters))->apply()->paginate(16);
$mods = (new ModFilter($filters))->apply()->paginate($this->perPage);
$this->redirectOutOfBoundsPage($mods);
return view('livewire.mod.listing', compact('mods'));
}
/**
* Validate that the option selected is an option that is available by setting it to the closest available version.
*/
public function validatePerPage(): void
{
$this->perPage = collect($this->perPageOptions)->pipe(function ($data) {
$closest = null;
foreach ($data as $item) {
if ($closest === null || abs($this->perPage - $closest) > abs($item - $this->perPage)) {
$closest = $item;
}
}
return $closest;
});
}
/**
* Check if the current page is greater than the last page. Redirect if it is.
*/
@ -110,7 +153,7 @@ class Listing extends Component
public function resetFilters(): void
{
$this->query = '';
$this->sptVersions = $this->getLatestMinorVersions()->pluck('version')->toArray();
$this->sptVersions = $this->getDefaultSptVersions();
$this->featured = 'include';
}

View File

@ -0,0 +1,8 @@
@props(['filterName', 'filter', 'currentFilter'])
<a href="#{{ $filter }}"
@click.prevent="$wire.set('{{ $filterName }}', '{{ $filter }}')"
class="flex items-center gap-2 bg-slate-100 px-4 py-2 text-sm hover:bg-slate-800/5 focus-visible:bg-slate-800/10 focus-visible:text-black focus-visible:outline-none dark:bg-slate-800 dark:hover:bg-slate-100/5 dark:focus-visible:bg-slate-100/10 dark:focus-visible:text-white {{ strval($filter) === strval($currentFilter) ? "font-bold text-cyan-500 dark:text-cyan-500 hover:text-cyan-400 dark:hover:text-cyan-400" : "text-slate-700 dark:text-slate-300 hover:text-black dark:hover:text-white" }}"
role="menuitem" tabindex="-1">
{{ $slot }}
</a>

View File

@ -1,8 +0,0 @@
@props(['order', 'currentOrder'])
<a href="#{{ $order }}"
@click.prevent="$wire.set('order', '{{ $order }}')"
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 {{ $order === $currentOrder ? "font-bold text-slate-900 dark:text-white" : "" }}"
role="menuitem" tabindex="-1">
{{ $slot }}
</a>

View File

@ -99,7 +99,44 @@
</div>
</div>
<div class="col-start-1 row-start-1 py-4">
<div class="mx-auto flex max-w-7xl justify-end px-4 sm:px-6 lg:px-8">
<div class="mx-auto flex max-w-7xl justify-end px-4 sm:px-6 lg:px-8 gap-6">
{{-- Results Per Page Dropdown --}}
<div class="relative inline-block" x-data="{ isResultsPerPageOpen: false }" @click.away="isResultsPerPageOpen = false">
<div class="flex">
{{-- Large display can show full text --}}
<button type="button" @click="isResultsPerPageOpen = !isResultsPerPageOpen" class="hidden lg:flex group inline-flex justify-center text-sm font-medium text-gray-700 dark:text-gray-400 hover:text-gray-900 dark:hover:text-gray-100" id="menu-button" :aria-expanded="isResultsPerPageOpen.toString()" aria-haspopup="true">
{{ __('Per Page') }}
<svg class="-mr-1 ml-1 h-5 w-5 flex-shrink-0 text-gray-400 group-hover:text-gray-500" viewBox="0 0 20 20" fill="currentColor" aria-hidden="true">
<path fill-rule="evenodd" d="M5.23 7.21a.75.75 0 011.06.02L10 11.168l3.71-3.938a.75.75 0 111.08 1.04l-4.25 4.5a.75.75 0 01-1.08 0l-4.25-4.5a.75.75 0 01.02-1.06z" clip-rule="evenodd" />
</svg>
</button>
{{-- Only show selected number on smaller screens --}}
<button type="button" @click="isResultsPerPageOpen = !isResultsPerPageOpen" class="lg:hidden group inline-flex justify-center text-sm font-medium text-gray-700 dark:text-gray-400 hover:text-gray-900 dark:hover:text-gray-100" id="menu-button" :aria-expanded="isResultsPerPageOpen.toString()" aria-haspopup="true" title="{{ __(':perPage results per page', ['perPage' => $perPage]) }}">
{{ __(':perPage/p', ['perPage' => $perPage]) }}
<svg class="-mr-1 ml-1 h-5 w-5 flex-shrink-0 text-gray-400 group-hover:text-gray-500" viewBox="0 0 20 20" fill="currentColor" aria-hidden="true">
<path fill-rule="evenodd" d="M5.23 7.21a.75.75 0 011.06.02L10 11.168l3.71-3.938a.75.75 0 111.08 1.04l-4.25 4.5a.75.75 0 01-1.08 0l-4.25-4.5a.75.75 0 01.02-1.06z" clip-rule="evenodd" />
</svg>
</button>
</div>
<div x-cloak
x-show="isResultsPerPageOpen"
x-transition:enter="transition ease-out duration-100"
x-transition:enter-start="transform opacity-0 scale-95"
x-transition:enter-end="transform opacity-100 scale-100"
x-transition:leave="transition ease-in duration-75"
x-transition:leave-start="transform opacity-100 scale-100"
x-transition:leave-end="transform opacity-0 scale-95"
class="absolute top-7 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" aria-orientation="vertical" aria-labelledby="menu-button" tabindex="-1">
<div class="flex flex-col py-1.5">
@foreach($perPageOptions as $option)
<x-filter-menu-item filterName="perPage" :filter="$option" :currentFilter="$perPage">{{ $option }}</x-filter-menu-item>
@endforeach
</div>
</div>
</div>
{{-- Sort Dropdown --}}
<div class="relative inline-block" x-data="{ isSortOpen: false }" @click.away="isSortOpen = false">
<div class="flex">
<button type="button" @click="isSortOpen = !isSortOpen" class="group inline-flex justify-center text-sm font-medium text-gray-700 dark:text-gray-400 hover:text-gray-900 dark:hover:text-gray-100" id="menu-button" :aria-expanded="isSortOpen.toString()" aria-haspopup="true">
@ -120,9 +157,9 @@
class="absolute top-7 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" aria-orientation="vertical" aria-labelledby="menu-button" tabindex="-1">
<div class="flex flex-col py-1.5">
<x-filter-sort-menu-item order="created" :currentOrder="$order">{{ __('Newest') }}</x-filter-sort-menu-item>
<x-filter-sort-menu-item order="updated" :currentOrder="$order">{{ __('Recently Updated') }}</x-filter-sort-menu-item>
<x-filter-sort-menu-item order="downloaded" :currentOrder="$order">{{ __('Most Downloaded') }}</x-filter-sort-menu-item>
<x-filter-menu-item filterName="order" filter="created" :currentFilter="$order">{{ __('Newest') }}</x-filter-menu-item>
<x-filter-menu-item filterName="order" filter="updated" :currentFilter="$order">{{ __('Recently Updated') }}</x-filter-menu-item>
<x-filter-menu-item filterName="order" filter="downloaded" :currentFilter="$order">{{ __('Most Downloaded') }}</x-filter-menu-item>
</div>
</div>
</div>

View File

@ -75,8 +75,8 @@
</div>
{{-- Mods --}}
<div x-show="selectedTab === 'mods'" class="">
@if($mods)
<div x-show="selectedTab === 'mods'">
@if($mods->count())
<div class="mb-4">
{{ $mods->links() }}
</div>
@ -89,7 +89,9 @@
{{ $mods->links() }}
</div>
@else
<p>{{ __('This user has not yet published any mods.') }}</p>
<p class="p-4 sm:p-6 bg-white dark:bg-gray-950 rounded-xl shadow-md dark:shadow-gray-950 text-gray-800 dark:text-gray-200 drop-shadow-2xl">
{{ __('This user has not yet published any mods.') }}
</p>
@endif
</div>