531 lines
26 KiB
PHP
531 lines
26 KiB
PHP
@extends('layouts.nova')
|
|
|
|
@php
|
|
use App\Banner;
|
|
@endphp
|
|
|
|
@php
|
|
$seoPage = max(1, (int) request()->query('page', 1));
|
|
$seoBase = url()->current();
|
|
$seoQ = request()->query(); unset($seoQ['page']);
|
|
$seoUrl = fn(int $p) => $seoBase . ($p > 1
|
|
? '?' . http_build_query(array_merge($seoQ, ['page' => $p]))
|
|
: (count($seoQ) ? '?' . http_build_query($seoQ) : ''));
|
|
$seoPrev = $seoPage > 1 ? $seoUrl($seoPage - 1) : null;
|
|
$seoNext = (isset($artworks) && method_exists($artworks, 'nextPageUrl'))
|
|
? $artworks->nextPageUrl() : null;
|
|
@endphp
|
|
|
|
@push('head')
|
|
<link rel="canonical" href="{{ $seoUrl($seoPage) }}">
|
|
@if($seoPrev)<link rel="prev" href="{{ $seoPrev }}">@endif
|
|
@if($seoNext)<link rel="next" href="{{ $seoNext }}">@endif
|
|
<meta name="robots" content="index,follow">
|
|
{{-- OpenGraph --}}
|
|
<meta property="og:type" content="website" />
|
|
<meta property="og:url" content="{{ $page_canonical ?? $seoUrl(1) }}" />
|
|
<meta property="og:title" content="{{ $page_title ?? ($hero_title ?? 'Skinbase') }}" />
|
|
<meta property="og:description" content="{{ $page_meta_description ?? '' }}" />
|
|
<meta property="og:site_name" content="Skinbase" />
|
|
{{-- Twitter card --}}
|
|
<meta name="twitter:card" content="summary" />
|
|
<meta name="twitter:title" content="{{ $page_title ?? ($hero_title ?? 'Skinbase') }}" />
|
|
<meta name="twitter:description" content="{{ $page_meta_description ?? '' }}" />
|
|
|
|
{{-- Breadcrumb structured data --}}
|
|
@if(isset($breadcrumbs) && $breadcrumbs->isNotEmpty())
|
|
<script type="application/ld+json">
|
|
{!! json_encode([
|
|
'@context' => 'https://schema.org',
|
|
'@type' => 'BreadcrumbList',
|
|
'itemListElement' => $breadcrumbs->values()->map(fn ($crumb, $i) => [
|
|
'@type' => 'ListItem',
|
|
'position' => $i + 1,
|
|
'name' => $crumb->name,
|
|
'item' => url($crumb->url),
|
|
])->all(),
|
|
], JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE) !!}
|
|
</script>
|
|
@endif
|
|
@endpush
|
|
|
|
@php
|
|
// ── Rank API endpoint ────────────────────────────────────────────────────
|
|
// Map the active sort alias to the ranking API ?type= parameter.
|
|
// Only trending / fresh / top-rated have pre-computed ranking lists.
|
|
$rankTypeMap = [
|
|
'trending' => 'trending',
|
|
'fresh' => 'new_hot',
|
|
'top-rated' => 'best',
|
|
];
|
|
$rankApiType = $rankTypeMap[$current_sort ?? 'trending'] ?? null;
|
|
$rankApiEndpoint = null;
|
|
if ($rankApiType) {
|
|
if (isset($category) && $category && $category->id ?? null) {
|
|
$rankApiEndpoint = '/api/rank/category/' . $category->id;
|
|
} elseif (isset($contentType) && $contentType && $contentType->slug ?? null) {
|
|
$rankApiEndpoint = '/api/rank/type/' . $contentType->slug;
|
|
} else {
|
|
$rankApiEndpoint = '/api/rank/global';
|
|
}
|
|
}
|
|
@endphp
|
|
|
|
@section('content')
|
|
<div class="container-fluid legacy-page">
|
|
@php Banner::ShowResponsiveAd(); @endphp
|
|
|
|
@php
|
|
$browseSection = isset($contentType) && $contentType ? strtolower((string) $contentType->slug) : 'artworks';
|
|
$browseIconMap = [
|
|
'artworks' => 'fa-border-all',
|
|
'photography' => 'fa-camera',
|
|
'wallpapers' => 'fa-desktop',
|
|
'skins' => 'fa-layer-group',
|
|
'other' => 'fa-folder-open',
|
|
];
|
|
$browseIcon = $browseIconMap[$browseSection] ?? 'fa-border-all';
|
|
@endphp
|
|
|
|
<div class="pt-0">
|
|
<div class="mx-auto w-full">
|
|
<div class="relative min-h-[calc(120vh-64px)] md:min-h-[calc(100vh-64px)]">
|
|
|
|
<main class="w-full">
|
|
|
|
{{-- ── Hero header (discover-style) ── --}}
|
|
<header class="px-6 pt-10 pb-7 md:px-10 border-b border-white/[0.06] bg-gradient-to-b from-sky-500/[0.04] to-transparent">
|
|
@php
|
|
$headerBreadcrumbs = collect(array_filter([
|
|
isset($contentType) && $contentType ? (object) ['name' => 'Explore', 'url' => '/explore'] : null,
|
|
isset($contentType) && $contentType ? (object) ['name' => $contentType->name, 'url' => '/explore/' . strtolower($contentType->slug)] : (object) ['name' => 'Explore', 'url' => '/explore'],
|
|
...(($gallery_type ?? null) === 'category' && isset($breadcrumbs) ? $breadcrumbs->all() : []),
|
|
]));
|
|
@endphp
|
|
|
|
<div class="flex flex-col gap-4 lg:flex-row lg:items-start lg:justify-between">
|
|
<div class="max-w-3xl">
|
|
<p class="text-xs font-semibold uppercase tracking-widest text-white/30 mb-1">Browse</p>
|
|
<h1 class="text-3xl font-bold text-white leading-tight flex items-center gap-3">
|
|
<i class="fa-solid {{ $browseIcon }} text-sky-400 text-2xl"></i>
|
|
{{ $hero_title ?? 'Browse Artworks' }}
|
|
</h1>
|
|
@if(!empty($hero_description))
|
|
<p class="mt-1 text-sm text-white/50">{!! $hero_description !!}</p>
|
|
@endif
|
|
</div>
|
|
|
|
<div class="flex flex-col items-start gap-3 lg:items-end">
|
|
<div class="hidden lg:flex lg:justify-end">
|
|
@include('components.breadcrumbs', ['breadcrumbs' => $headerBreadcrumbs])
|
|
</div>
|
|
@include('gallery._browse_nav', ['section' => $browseSection])
|
|
</div>
|
|
</div>
|
|
|
|
<div class="mt-4 lg:hidden">
|
|
@include('components.breadcrumbs', ['breadcrumbs' => $headerBreadcrumbs])
|
|
</div>
|
|
</header>
|
|
|
|
{{-- ═══════════════════════════════════════════════════════════════ --}}
|
|
{{-- RANKING TABS --}}
|
|
{{-- ═══════════════════════════════════════════════════════════════ --}}
|
|
@php
|
|
$rankingTabs = [
|
|
['value' => 'trending', 'label' => 'Trending', 'icon' => '🔥'],
|
|
['value' => 'fresh', 'label' => 'New & Hot', 'icon' => '🚀'],
|
|
['value' => 'top-rated', 'label' => 'Best', 'icon' => '⭐'],
|
|
['value' => 'latest', 'label' => 'Latest', 'icon' => '🕐'],
|
|
];
|
|
$activeTab = $current_sort ?? 'trending';
|
|
@endphp
|
|
|
|
<div class="sticky top-0 z-30 border-b border-white/10 bg-nova-900/90 backdrop-blur-md" id="gallery-ranking-tabs">
|
|
<div class="px-6 md:px-10">
|
|
<div class="flex items-center justify-between gap-4">
|
|
|
|
{{-- Tab list --}}
|
|
<nav class="flex items-center gap-0 -mb-px nb-scrollbar-none overflow-x-auto" role="tablist" aria-label="Gallery ranking">
|
|
@foreach($rankingTabs as $tab)
|
|
@php $isActive = $activeTab === $tab['value']; @endphp
|
|
<button
|
|
role="tab"
|
|
aria-selected="{{ $isActive ? 'true' : 'false' }}"
|
|
data-rank-tab="{{ $tab['value'] }}"
|
|
class="gallery-rank-tab relative flex items-center gap-1.5 whitespace-nowrap px-5 py-4 text-sm font-medium transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-accent/60 {{ $isActive ? 'text-white' : 'text-neutral-400 hover:text-white' }}"
|
|
>
|
|
<span aria-hidden="true">{{ $tab['icon'] }}</span>
|
|
{{ $tab['label'] }}
|
|
{{-- Active underline indicator --}}
|
|
<span class="nb-tab-indicator absolute bottom-0 left-0 right-0 h-0.5 {{ $isActive ? 'bg-accent scale-x-100' : 'bg-transparent scale-x-0' }} transition-transform duration-300 origin-left rounded-full"></span>
|
|
</button>
|
|
@endforeach
|
|
</nav>
|
|
|
|
{{-- Filters button — wired to slide-over panel (Phase 3) --}}
|
|
<button
|
|
id="gallery-filter-panel-toggle"
|
|
type="button"
|
|
class="hidden md:flex items-center gap-2 shrink-0 rounded-lg border border-white/10 bg-white/5 px-3 py-2 text-sm text-white/80 hover:bg-white/10 hover:text-white transition-colors"
|
|
aria-haspopup="dialog"
|
|
aria-expanded="false"
|
|
aria-controls="gallery-filter-panel"
|
|
>
|
|
<svg class="h-4 w-4" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2" aria-hidden="true">
|
|
<path stroke-linecap="round" stroke-linejoin="round" d="M3 4a1 1 0 011-1h16a1 1 0 011 1v2a1 1 0 01-.293.707L13 13.414V19a1 1 0 01-.553.894l-4 2A1 1 0 017 21v-7.586L3.293 6.707A1 1 0 013 6V4z" />
|
|
</svg>
|
|
Filters
|
|
</button>
|
|
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
{{-- ═══════════════════════════════════════════════════════════════ --}}
|
|
{{-- HORIZONTAL CATEGORY FILTER ROW --}}
|
|
{{-- ═══════════════════════════════════════════════════════════════ --}}
|
|
@php
|
|
$filterItems = $subcategories ?? collect();
|
|
$activeFilterId = isset($category) ? ($category->id ?? null) : null;
|
|
$categoryAllHref = isset($contentType) && $contentType
|
|
? url('/' . $contentType->slug)
|
|
: url('/browse');
|
|
$activeSortSlug = $activeTab !== 'trending' ? $activeTab : null;
|
|
@endphp
|
|
|
|
@if($filterItems->isNotEmpty())
|
|
<div class="sticky top-[57px] z-20 bg-nova-900/80 backdrop-blur-md border-b border-white/[0.06]">
|
|
@php
|
|
$allHref = $categoryAllHref . ($activeSortSlug ? '?sort=' . $activeSortSlug : '');
|
|
$carouselItems = [[
|
|
'label' => 'All',
|
|
'href' => $allHref,
|
|
'active' => !$activeFilterId,
|
|
]];
|
|
|
|
foreach ($filterItems as $sub) {
|
|
$subName = $sub->name ?? $sub->category_name ?? null;
|
|
$subUrl = $sub->url ?? null;
|
|
|
|
if (! $subUrl && isset($sub->slug) && isset($contentType) && $contentType) {
|
|
$subUrl = url('/' . $contentType->slug . '/' . $sub->slug);
|
|
}
|
|
|
|
if (! $subName || ! $subUrl) {
|
|
continue;
|
|
}
|
|
|
|
$sep = str_contains($subUrl, '?') ? '&' : '?';
|
|
$subLinkHref = $activeSortSlug ? ($subUrl . $sep . 'sort=' . $activeSortSlug) : $subUrl;
|
|
$isActiveSub = $activeFilterId && isset($sub->id) && (int) $sub->id === (int) $activeFilterId;
|
|
|
|
$carouselItems[] = [
|
|
'label' => $subName,
|
|
'href' => $subLinkHref,
|
|
'active' => $isActiveSub,
|
|
];
|
|
}
|
|
@endphp
|
|
|
|
<div
|
|
data-react-pill-carousel
|
|
data-aria-label="Filter by category"
|
|
data-items='@json($carouselItems)'
|
|
></div>
|
|
</div>
|
|
@endif
|
|
|
|
@php
|
|
$galleryItems = (is_object($artworks) && method_exists($artworks, 'getCollection'))
|
|
? $artworks->getCollection()
|
|
: collect($artworks);
|
|
|
|
$galleryArtworks = $galleryItems->map(fn ($art) => [
|
|
'id' => $art->id ?? null,
|
|
'name' => $art->name ?? null,
|
|
'thumb' => $art->thumb_url ?? $art->thumb ?? null,
|
|
'thumb_srcset' => $art->thumb_srcset ?? null,
|
|
'uname' => $art->uname ?? '',
|
|
'username' => $art->username ?? $art->uname ?? '',
|
|
'avatar_url' => $art->avatar_url ?? null,
|
|
'category_name' => $art->category_name ?? '',
|
|
'category_slug' => $art->category_slug ?? '',
|
|
'slug' => $art->slug ?? '',
|
|
'width' => $art->width ?? null,
|
|
'height' => $art->height ?? null,
|
|
])->values();
|
|
|
|
$galleryNextPageUrl = (is_object($artworks) && method_exists($artworks, 'nextPageUrl'))
|
|
? $artworks->nextPageUrl()
|
|
: null;
|
|
@endphp
|
|
|
|
<section class="px-6 pb-10 pt-8 md:px-10">
|
|
@if($galleryItems->isEmpty())
|
|
<div class="rounded-xl border border-white/10 bg-white/5 p-8 text-center text-white/60">
|
|
No artworks found yet. Check back soon.
|
|
</div>
|
|
@else
|
|
<div
|
|
data-react-masonry-gallery
|
|
data-artworks="{{ json_encode($galleryArtworks) }}"
|
|
data-gallery-type="{{ $gallery_type ?? 'browse' }}"
|
|
@if($galleryNextPageUrl) data-next-page-url="{{ $galleryNextPageUrl }}" @endif
|
|
@if($rankApiEndpoint) data-rank-api-endpoint="{{ $rankApiEndpoint }}" @endif
|
|
@if($rankApiType) data-rank-type="{{ $rankApiType }}" @endif
|
|
data-limit="24"
|
|
class="min-h-32"
|
|
></div>
|
|
@endif
|
|
</section>
|
|
|
|
{{-- ─── Filter Slide-over Panel ──────────────────────────────────── --}}
|
|
@include('gallery._filter_panel')
|
|
|
|
</main>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
@endsection
|
|
|
|
@push('head')
|
|
<style>
|
|
/* ── Hero ─────────────────────────────────────────────────────── */
|
|
.nb-hero-fade {
|
|
background: linear-gradient(180deg, rgba(17,24,39,0) 0%, rgba(7,10,15,0.9) 60%, rgba(7,10,15,1) 100%);
|
|
}
|
|
.nb-hero-gradient {
|
|
background: linear-gradient(135deg, rgba(224,122,33,0.08) 0%, rgba(15,23,36,0) 50%, rgba(21,36,58,0.4) 100%);
|
|
animation: nb-hero-shimmer 8s ease-in-out infinite alternate;
|
|
}
|
|
@keyframes nb-hero-shimmer {
|
|
0% { opacity: 0.6; }
|
|
100% { opacity: 1; }
|
|
}
|
|
|
|
/* ── Ranking Tabs ─────────────────────────────────────────────── */
|
|
.gallery-rank-tab {
|
|
-webkit-tap-highlight-color: transparent;
|
|
}
|
|
.gallery-rank-tab .nb-tab-indicator {
|
|
transition: transform 300ms cubic-bezier(0.4, 0, 0.2, 1), background-color 200ms ease;
|
|
}
|
|
|
|
/* Legacy: keep nb-scrollbar-none working elsewhere in the page */
|
|
.nb-scrollbar-none {
|
|
-ms-overflow-style: none;
|
|
scrollbar-width: none;
|
|
}
|
|
.nb-scrollbar-none::-webkit-scrollbar { display: none; }
|
|
|
|
/* ── Gallery grid fade-in on page load / tab change ─────────── */
|
|
@keyframes nb-gallery-fade-in {
|
|
from { opacity: 0; transform: translateY(12px); }
|
|
to { opacity: 1; transform: translateY(0); }
|
|
}
|
|
[data-react-masonry-gallery] {
|
|
animation: nb-gallery-fade-in 300ms ease-out both;
|
|
}
|
|
|
|
/* ── Filter panel choice pills ───────────────────────────────── */
|
|
.nb-filter-choice { display: inline-flex; cursor: pointer; }
|
|
.nb-filter-choice--block { display: flex; width: 100%; }
|
|
.nb-filter-choice-label {
|
|
display: inline-flex;
|
|
align-items: center;
|
|
padding: 0.375rem 0.875rem;
|
|
border-radius: 9999px;
|
|
border: 1px solid rgba(255,255,255,0.1);
|
|
background: rgba(255,255,255,0.05);
|
|
color: rgba(214,224,238,0.8);
|
|
font-size: 0.8125rem;
|
|
font-weight: 500;
|
|
transition: background 150ms ease, color 150ms ease, border-color 150ms ease;
|
|
white-space: nowrap;
|
|
}
|
|
.nb-filter-choice--block .nb-filter-choice-label {
|
|
border-radius: 0.6rem;
|
|
width: 100%;
|
|
}
|
|
.nb-filter-choice input:checked ~ .nb-filter-choice-label {
|
|
background: #E07A21;
|
|
border-color: #E07A21;
|
|
color: #fff;
|
|
box-shadow: 0 1px 8px rgba(224,122,33,0.35);
|
|
}
|
|
.nb-filter-choice input:focus-visible ~ .nb-filter-choice-label {
|
|
outline: 2px solid rgba(224,122,33,0.6);
|
|
outline-offset: 2px;
|
|
}
|
|
/* Filter date/text inputs */
|
|
.nb-filter-input {
|
|
appearance: none;
|
|
background: rgba(255,255,255,0.05);
|
|
border: 1px solid rgba(255,255,255,0.1);
|
|
border-radius: 0.5rem;
|
|
color: rgba(255,255,255,0.85);
|
|
font-size: 0.8125rem;
|
|
padding: 0.425rem 0.75rem;
|
|
transition: border-color 150ms ease;
|
|
color-scheme: dark;
|
|
}
|
|
.nb-filter-input:focus {
|
|
outline: none;
|
|
border-color: rgba(224,122,33,0.6);
|
|
box-shadow: 0 0 0 3px rgba(224,122,33,0.15);
|
|
}
|
|
</style>
|
|
@endpush
|
|
|
|
@push('scripts')
|
|
@vite('resources/js/entry-masonry-gallery.jsx')
|
|
@vite('resources/js/entry-pill-carousel.jsx')
|
|
<script src="/js/legacy-gallery-init.js" defer></script>
|
|
<script>
|
|
(function () {
|
|
'use strict';
|
|
|
|
// ── Filter Slide-over Panel ──────────────────────────────────────────
|
|
function initGalleryFilterPanel() {
|
|
var panel = document.getElementById('gallery-filter-panel');
|
|
var backdrop = document.getElementById('gallery-filter-backdrop');
|
|
var drawer = document.getElementById('gallery-filter-drawer');
|
|
var toggleBtn = document.getElementById('gallery-filter-panel-toggle');
|
|
var closeBtn = document.getElementById('gallery-filter-panel-close');
|
|
var applyBtn = document.getElementById('gallery-filter-apply');
|
|
var resetBtn = document.getElementById('gallery-filter-reset');
|
|
if (!panel || !drawer || !backdrop) return;
|
|
|
|
var isOpen = false;
|
|
|
|
function openPanel() {
|
|
isOpen = true;
|
|
panel.setAttribute('aria-hidden', 'false');
|
|
panel.classList.remove('pointer-events-none');
|
|
panel.classList.add('pointer-events-auto');
|
|
backdrop.classList.remove('opacity-0');
|
|
backdrop.classList.add('opacity-100');
|
|
drawer.classList.remove('translate-x-full');
|
|
drawer.classList.add('translate-x-0');
|
|
if (toggleBtn) toggleBtn.setAttribute('aria-expanded', 'true');
|
|
// Focus first interactive element in drawer
|
|
var first = drawer.querySelector('button, input, select, a[href]');
|
|
if (first) { setTimeout(function () { if (first) first.focus(); }, 320); }
|
|
}
|
|
|
|
function closePanel() {
|
|
isOpen = false;
|
|
panel.setAttribute('aria-hidden', 'true');
|
|
panel.classList.add('pointer-events-none');
|
|
panel.classList.remove('pointer-events-auto');
|
|
backdrop.classList.add('opacity-0');
|
|
backdrop.classList.remove('opacity-100');
|
|
drawer.classList.add('translate-x-full');
|
|
drawer.classList.remove('translate-x-0');
|
|
if (toggleBtn) toggleBtn.setAttribute('aria-expanded', 'false');
|
|
}
|
|
|
|
if (toggleBtn) toggleBtn.addEventListener('click', function () { isOpen ? closePanel() : openPanel(); });
|
|
if (closeBtn) closeBtn.addEventListener('click', closePanel);
|
|
backdrop.addEventListener('click', closePanel);
|
|
|
|
// Close on ESC
|
|
document.addEventListener('keydown', function (e) {
|
|
if (isOpen && (e.key === 'Escape' || e.key === 'Esc')) { closePanel(); }
|
|
});
|
|
|
|
// Apply: collect all named inputs and navigate with updated params
|
|
if (applyBtn) {
|
|
applyBtn.addEventListener('click', function () {
|
|
var url = new URL(window.location.href);
|
|
url.searchParams.delete('page');
|
|
|
|
// Radio groups: orientation, resolution, sort
|
|
drawer.querySelectorAll('input[type="radio"]:checked').forEach(function (input) {
|
|
if ((input.name === 'orientation' || input.name === 'resolution') && input.value !== 'any') {
|
|
url.searchParams.set(input.name, input.value);
|
|
} else if (input.name === 'orientation' || input.name === 'resolution') {
|
|
url.searchParams.delete(input.name);
|
|
} else {
|
|
url.searchParams.set(input.name, input.value);
|
|
}
|
|
});
|
|
|
|
// Text inputs: author
|
|
['date_from', 'date_to', 'author'].forEach(function (name) {
|
|
var el = drawer.querySelector('[name="' + name + '"]');
|
|
if (el && el.value) {
|
|
url.searchParams.set(name, el.value);
|
|
} else {
|
|
url.searchParams.delete(name);
|
|
}
|
|
});
|
|
|
|
window.location.href = url.toString();
|
|
});
|
|
}
|
|
|
|
// Reset: strip all filter params, keep only current path
|
|
if (resetBtn) {
|
|
resetBtn.addEventListener('click', function () {
|
|
var url = new URL(window.location.href);
|
|
['orientation', 'resolution', 'author', 'date_from', 'date_to', 'sort', 'page'].forEach(function (p) {
|
|
url.searchParams.delete(p);
|
|
});
|
|
window.location.href = url.toString();
|
|
});
|
|
}
|
|
}
|
|
|
|
// ── Ranking Tab navigation ───────────────────────────────────────────
|
|
// Clicking a tab updates ?sort= in the URL and navigates.
|
|
// Active underline animation plays before navigation for visual feedback.
|
|
function initRankingTabs() {
|
|
var tabBar = document.getElementById('gallery-ranking-tabs');
|
|
if (!tabBar) return;
|
|
|
|
tabBar.addEventListener('click', function (e) {
|
|
var btn = e.target.closest('[data-rank-tab]');
|
|
if (!btn) return;
|
|
|
|
var sortValue = btn.dataset.rankTab;
|
|
if (!sortValue) return;
|
|
|
|
// Optimistic visual feedback — light up the clicked tab
|
|
tabBar.querySelectorAll('[data-rank-tab]').forEach(function (t) {
|
|
var ind = t.querySelector('.nb-tab-indicator');
|
|
if (t === btn) {
|
|
t.classList.add('text-white');
|
|
t.classList.remove('text-neutral-400');
|
|
if (ind) { ind.classList.add('bg-accent', 'scale-x-100'); ind.classList.remove('bg-transparent', 'scale-x-0'); }
|
|
} else {
|
|
t.classList.remove('text-white');
|
|
t.classList.add('text-neutral-400');
|
|
if (ind) { ind.classList.remove('bg-accent', 'scale-x-100'); ind.classList.add('bg-transparent', 'scale-x-0'); }
|
|
}
|
|
});
|
|
|
|
// Navigate to the new URL
|
|
var url = new URL(window.location.href);
|
|
url.searchParams.set('sort', sortValue);
|
|
url.searchParams.delete('page');
|
|
window.location.href = url.toString();
|
|
});
|
|
}
|
|
|
|
function init() {
|
|
initGalleryFilterPanel();
|
|
initRankingTabs();
|
|
}
|
|
|
|
if (document.readyState === 'loading') {
|
|
document.addEventListener('DOMContentLoaded', init);
|
|
} else {
|
|
init();
|
|
}
|
|
}());
|
|
</script>
|
|
@endpush
|