File size: 4,766 Bytes
31a2d08 c99cc8d 7e2471a 5e88463 31a2d08 7e2471a 5679415 31a2d08 5e88463 31a2d08 5679415 31a2d08 b1aa559 31a2d08 7e2471a 31a2d08 7e2471a 5e88463 31a2d08 f4c31a8 5e88463 f4c31a8 31a2d08 |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 |
<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>
|