Skip to Content
We're rolling out the first batch of Game UIs right now, and it might take a few days to fully wrap up. If you're already testing the UI comps, we'd love to hear any feedback you've got! 🙏

Base Usage

import { h } from "onejs-preact" import { VirtualList } from "@/comps/echo/virtual" import { useEffect, useState } from "onejs-preact/hooks" const items = Array.from({ length: 1000 }, (_, i) => `${['Solar', 'Lunar', 'Stellar', 'Galactic', 'Nebular'][i % 5]} ${['Voyager', 'Ranger', 'Explorer', 'Pilot', 'Navigator'][i % 5]}`) const palette = [ "bg-amber-200", "bg-stone-200" ] export const VirtualBaseDemo = () => ( <div class="echo-theme w-full h-full p-20 justify-center items-center bold"> <VirtualList class="w-[340px]" data={items} rowHeight={50} overscanCount={2} renderRow={Row} scrollviewProps={{ mouseWheelScrollSize: 200 }} /> </div> ) function Row(item: string, index: number) { const [ready, setReady] = useState(false) useEffect(() => { // Simulate a delay to show the loading state const id = setTimeout(() => { setReady(true) }, 200) return () => { setReady(false); clearTimeout(id) } }, []) return ready ? ( <div class={`h-[50px] flex-row items-center px-4 rounded mb-1 ${palette[index % palette.length]}`}> <div class="mr-2">{`${index + 1}.`}</div> <div class="grow shrink">{item}</div> </div> ) : <div class="h-[50px] mb-1 rounded bg-gray-200"></div> }

Path of Exile 2 Leaderboard Sample

import { h } from "preact" import { VirtualList } from "@/comps/echo/virtual" import { useEffect, useState } from "preact/hooks" const webapi = new CS.OneJS.WebApi() export const PoELeaderboardSimple = () => { const [entries, setEntries] = useState<Entry[]>([]) const [ladder, setLadder] = useState<string>("standard") useEffect(() => { const endpoint = "https://pathofexile2.com/internal-api/content/game-ladder/id/Standard" webapi.getText(endpoint, text => { const res: LeaderboardResponseData = JSON.parse(text) setEntries(res.context.ladder.entries) }) }, []) useEffect(() => { setEntries([]) fetchLeaderboard(ladder, (res: LeaderboardResponseData) => { setEntries(res.context.ladder.entries) }) }, [ladder]) return <div class="echo-theme h-[300px] w-[340px]"> <div class="flex-row mb-2 justify-between"> <button onClick={() => setLadder("standard")}>Standard</button> <button onClick={() => setLadder("hardcore")}>Harcore</button> <button onClick={() => setLadder("ssf")}>SSF</button> <button onClick={() => setLadder("hardcore_ssf")}>Harcore SSF</button> </div> <div class="flex-row h-[48px] mb-[2px] text-[#eee] bold"> <Cell class="w-[60px] bg-[#306c9d]">Rank</Cell> <Cell class="grow shrink bg-[#306c9d]">Account</Cell> <Cell class="w-[65px] bg-[#306c9d]">Level</Cell> </div> {entries.length ? ( <VirtualList class="h-full [&_Scroller]:absolute [&_ScrollView_Scroller]:right-[-12px]" data={entries} rowHeight={48} overscanCount={2} renderRow={Row} /> ) : ( <div class="flex-row justify-center items-center h-[300px] text-white text-xl"> Loading... </div> )} </div> } function Row(entry: Entry) { const [ready, setReady] = useState(false) useEffect(() => { // Simulate a delay to show the loading state const id = setTimeout(() => { setReady(true) }, 150) return () => { setReady(false); clearTimeout(id) } }, []) return ready ? ( <div class="flex-row h-[48px] mb-[1px] bg-[#212f4bbc] text-[#fff]"> <Cell class="w-[60px]">{entry.rank}</Cell> <Cell class="grow shrink">{entry.account.name}</Cell> <Cell class="w-[65px]">{entry.character.level}</Cell> </div> ) : <div class="h-[50px] mb-1 bg-[#212f4bbc]"></div> } function Cell({ children, class: cn }: any) { return <div class={`p-3 mr-[1px] text-sm text-center bold ${cn}`}>{children}</div> } function fetchLeaderboard(ladder: string, callback: Function) { const endpoints = { "standard": "https://pathofexile2.com/internal-api/content/game-ladder/id/Standard", "hardcore": "https://pathofexile2.com/internal-api/content/game-ladder/id/Hardcore", "ssf": "https://pathofexile2.com/internal-api/content/game-ladder/id/Solo%20Self-Found", "hardcore_ssf": "https://pathofexile2.com/internal-api/content/game-ladder/id/Hardcore%20SSF" } as { [key: string]: string } const endpoint = endpoints[ladder] webapi.getText(endpoint, (text) => { const resObj = JSON.parse(text) callback(resObj) }, JSON.stringify({ "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/134.0.0.0 Safari/537.36 Edg/134.0.0.0" })) } interface LeaderboardResponseData { context: { ladder: { entries: Entry[] } } } interface Entry { rank: number account: { name: string } character: { level: number } }