|
<script lang="ts"> |
|
import Leaderboard from "./Leaderboard.svelte"; |
|
import ModelDetails from "./ModelDetails.svelte"; |
|
import Viewer from "./Viewer.svelte"; |
|
import Vote from "./Vote.svelte"; |
|
import About from "./About.svelte"; |
|
import { Filter, CheckmarkOutline } from "carbon-icons-svelte"; |
|
import { onMount } from "svelte"; |
|
import { CaretDown, Code } from "carbon-icons-svelte"; |
|
|
|
interface Scene { |
|
name: string; |
|
url: string; |
|
thumbnail: string; |
|
} |
|
|
|
let currentView: "Leaderboard" | "Vote" | "ModelDetails" | "Viewer" | "About" = "Vote"; |
|
let selectedEntry: { name: string } | null = null; |
|
let selectedScene: Scene | null = null; |
|
let showOnlyOpenSource = false; |
|
let showFilter = false; |
|
let filterContainer: HTMLDivElement; |
|
|
|
function handleClickOutside(event: MouseEvent) { |
|
if (filterContainer && !filterContainer.contains(event.target as Node)) { |
|
showFilter = false; |
|
} |
|
} |
|
|
|
onMount(() => { |
|
document.addEventListener("click", handleClickOutside); |
|
return () => { |
|
document.removeEventListener("click", handleClickOutside); |
|
}; |
|
}); |
|
|
|
function goHome() { |
|
window.location.href = "/"; |
|
} |
|
|
|
function showModelDetails(entry: { name: string }) { |
|
selectedEntry = entry; |
|
currentView = "ModelDetails"; |
|
} |
|
|
|
function showScene(scene: Scene) { |
|
selectedScene = scene; |
|
currentView = "Viewer"; |
|
} |
|
</script> |
|
|
|
<div class="container"> |
|
<div on:pointerdown={goHome} class="banner"> |
|
<h1>3D Arena</h1> |
|
<p>Generative 3D Leaderboard</p> |
|
</div> |
|
|
|
{#if currentView === "Leaderboard" || currentView === "Vote" || currentView === "About"} |
|
<div class="tabs"> |
|
<button on:click={() => (currentView = "Vote")} class={currentView === "Vote" ? "active" : ""}>Vote</button> |
|
<button on:click={() => (currentView = "Leaderboard")} class={currentView === "Leaderboard" ? "active" : ""} |
|
>Leaderboard</button |
|
> |
|
<button on:click={() => (currentView = "About")} class={currentView === "About" ? "active" : ""} |
|
>About</button |
|
> |
|
{#if currentView === "Leaderboard"} |
|
<div class="filter-container" bind:this={filterContainer}> |
|
<button |
|
class="filter-button" |
|
on:click={() => (showFilter = !showFilter)} |
|
aria-expanded={showFilter} |
|
aria-haspopup="true" |
|
> |
|
<Filter size={20} /> |
|
<CaretDown size={16} class="caret" /> |
|
</button> |
|
{#if showFilter} |
|
<div class="filter-dropdown" role="menu" aria-label="Filter options"> |
|
<div class="filter-section"> |
|
<div class="filter-section-title">Filter Options</div> |
|
<div |
|
class="filter-option {showOnlyOpenSource ? 'active' : ''}" |
|
on:click={() => (showOnlyOpenSource = !showOnlyOpenSource)} |
|
on:keydown={(e) => e.key === "Enter" && (showOnlyOpenSource = !showOnlyOpenSource)} |
|
role="menuitemcheckbox" |
|
aria-checked={showOnlyOpenSource} |
|
tabindex="0" |
|
> |
|
<div class="filter-label"> |
|
<Code size={16} class="filter-icon" /> |
|
Open source |
|
</div> |
|
<span class="filter-checkbox">✓</span> |
|
</div> |
|
</div> |
|
</div> |
|
{/if} |
|
</div> |
|
{/if} |
|
</div> |
|
{/if} |
|
|
|
{#if currentView === "Leaderboard"} |
|
<Leaderboard onEntryClick={showModelDetails} {showOnlyOpenSource} /> |
|
{:else if currentView === "Vote"} |
|
<Vote /> |
|
{:else if currentView === "ModelDetails" && selectedEntry} |
|
<ModelDetails |
|
modelName={selectedEntry.name} |
|
onBack={() => (currentView = "Leaderboard")} |
|
onSceneClick={showScene} |
|
/> |
|
{:else if currentView === "Viewer" && selectedScene && selectedEntry} |
|
<Viewer modelName={selectedEntry.name} scene={selectedScene} onBack={() => (currentView = "ModelDetails")} /> |
|
{:else if currentView === "About"} |
|
<About /> |
|
{/if} |
|
</div> |
|
|