Skip to content

Commit 1dde6cb

Browse files
authored
feat(registry) add new Leaderboard block (#363)
1 parent 107e6b9 commit 1dde6cb

File tree

8 files changed

+786
-2
lines changed

8 files changed

+786
-2
lines changed

app/blocks/gaming/gaming.tsx

Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import HealthBar from "@/components/ui/8bit/health-bar";
99
import ManaBar from "@/components/ui/8bit/mana-bar";
1010

1111
import AudioSettings from "../../../components/ui/8bit/blocks/audio-settings";
12+
import Leaderboard from "../../../components/ui/8bit/leaderboard";
1213
import CopyCommandButton from "../../docs/components/copy-command-button";
1314
import { OpenInV0Button } from "../../docs/components/open-in-v0-button";
1415

@@ -277,6 +278,84 @@ export default function GamingBlocks() {
277278
</div>
278279
</div>
279280
</div>
281+
282+
<div className="flex flex-col gap-4 border rounded-lg p-4 min-h-[450px]">
283+
<div className="flex flex-col md:flex-row gap-2 items-center justify-between">
284+
<h2 className="text-sm text-muted-foreground sm:pl-3">Leaderboard</h2>
285+
286+
<div className="flex flex-col md:flex-row items-center gap-2">
287+
<CopyCommandButton
288+
command="pnpm dlx shadcn@latest add @8bitcn/leaderboard"
289+
copyCommand="pnpm dlx shadcn@latest add @8bitcn/leaderboard"
290+
/>
291+
<OpenInV0Button name="8bit-leaderboard" className="w-fit" />
292+
</div>
293+
</div>
294+
295+
<div className="py-14 flex flex-col gap-6">
296+
<div className="flex flex-col gap-6 w-full md:w-[600px] mx-auto">
297+
<Leaderboard
298+
players={[
299+
{
300+
id: "1",
301+
name: "OrcDev",
302+
score: 125000,
303+
avatar: "/avatars/orcdev.jpeg",
304+
avatarFallback: "OD",
305+
},
306+
{
307+
id: "2",
308+
name: "Shadow Mage",
309+
score: 98500,
310+
avatarFallback: "S",
311+
},
312+
{
313+
id: "3",
314+
name: "Dragon Slayer",
315+
score: 87500,
316+
avatarFallback: "D",
317+
},
318+
{
319+
id: "4",
320+
name: "Fire Wizard",
321+
score: 76500,
322+
avatarFallback: "F",
323+
},
324+
{
325+
id: "5",
326+
name: "Retro Lover",
327+
score: 68500,
328+
isCurrentPlayer: true,
329+
avatarFallback: "Y",
330+
},
331+
{
332+
id: "6",
333+
name: "Ice Queen",
334+
score: 59500,
335+
avatarFallback: "I",
336+
},
337+
{
338+
id: "7",
339+
name: "Storm Knight",
340+
score: 55000,
341+
avatarFallback: "S",
342+
},
343+
{
344+
id: "8",
345+
name: "Moon Elf",
346+
score: 50500,
347+
avatarFallback: "M",
348+
},
349+
]}
350+
maxPlayers={8}
351+
showRank={true}
352+
showAvatar={true}
353+
currentPlayerId="5"
354+
className="w-full"
355+
/>
356+
</div>
357+
</div>
358+
</div>
280359
</div>
281360
);
282361
}
Lines changed: 253 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,253 @@
1+
"use client";
2+
3+
import { useState } from "react";
4+
5+
import {
6+
Leaderboard,
7+
LeaderboardPlayer,
8+
} from "@/components/ui/8bit/leaderboard";
9+
import { Button } from "@/components/ui/button";
10+
import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card";
11+
12+
// Sample leaderboard data with fantasy characters
13+
const samplePlayers: LeaderboardPlayer[] = [
14+
{
15+
id: "1",
16+
name: "OrcDev",
17+
score: 125000,
18+
isCurrentPlayer: false,
19+
avatar: "/avatars/orcdev.jpeg",
20+
avatarFallback: "OD",
21+
},
22+
{
23+
id: "2",
24+
name: "ShadowMage",
25+
score: 98500,
26+
isCurrentPlayer: false,
27+
avatarFallback: "S",
28+
},
29+
{
30+
id: "3",
31+
name: "DragonSlayer",
32+
score: 87500,
33+
isCurrentPlayer: false,
34+
avatarFallback: "D",
35+
},
36+
{
37+
id: "4",
38+
name: "FireWizard",
39+
score: 76500,
40+
isCurrentPlayer: false,
41+
avatarFallback: "F",
42+
},
43+
{
44+
id: "5",
45+
name: "IceQueen",
46+
score: 68500,
47+
isCurrentPlayer: false,
48+
avatarFallback: "I",
49+
},
50+
{
51+
id: "6",
52+
name: "You",
53+
score: 59500,
54+
isCurrentPlayer: true,
55+
avatarFallback: "Y",
56+
},
57+
{
58+
id: "7",
59+
name: "StormKnight",
60+
score: 55000,
61+
isCurrentPlayer: false,
62+
avatarFallback: "S",
63+
},
64+
{
65+
id: "8",
66+
name: "MoonElf",
67+
score: 50500,
68+
isCurrentPlayer: false,
69+
avatarFallback: "M",
70+
},
71+
{
72+
id: "9",
73+
name: "ThunderBolt",
74+
score: 46500,
75+
isCurrentPlayer: false,
76+
avatarFallback: "T",
77+
},
78+
{
79+
id: "10",
80+
name: "CrystalMage",
81+
score: 42500,
82+
isCurrentPlayer: false,
83+
avatarFallback: "C",
84+
},
85+
{
86+
id: "11",
87+
name: "WindWalker",
88+
score: 38500,
89+
isCurrentPlayer: false,
90+
avatarFallback: "W",
91+
},
92+
{
93+
id: "12",
94+
name: "EarthGuardian",
95+
score: 34500,
96+
isCurrentPlayer: false,
97+
avatarFallback: "E",
98+
},
99+
];
100+
101+
export default function LeaderboardBlock() {
102+
const [maxPlayers, setMaxPlayers] = useState(10);
103+
const [showRank, setShowRank] = useState(true);
104+
const [showAvatar, setShowAvatar] = useState(true);
105+
106+
const toggleMaxPlayers = () => {
107+
setMaxPlayers(maxPlayers === 10 ? 5 : 10);
108+
};
109+
110+
const toggleShowRank = () => {
111+
setShowRank(!showRank);
112+
};
113+
114+
const toggleShowAvatar = () => {
115+
setShowAvatar(!showAvatar);
116+
};
117+
118+
return (
119+
<div className="space-y-8">
120+
{/* Controls */}
121+
<Card>
122+
<CardHeader>
123+
<CardTitle>Leaderboard Controls</CardTitle>
124+
</CardHeader>
125+
<CardContent>
126+
<div className="flex flex-wrap gap-4">
127+
<Button onClick={toggleMaxPlayers} variant="outline">
128+
Max Players: {maxPlayers}
129+
</Button>
130+
<Button onClick={toggleShowRank} variant="outline">
131+
Show Rank: {showRank ? "Yes" : "No"}
132+
</Button>
133+
<Button onClick={toggleShowAvatar} variant="outline">
134+
Show Avatar: {showAvatar ? "Yes" : "No"}
135+
</Button>
136+
</div>
137+
</CardContent>
138+
</Card>
139+
140+
{/* Leaderboard Examples */}
141+
<div className="space-y-4">
142+
<h3 className="text-lg font-semibold">Leaderboard Examples</h3>
143+
<div className="grid grid-cols-1 lg:grid-cols-2 gap-6">
144+
<Leaderboard
145+
players={samplePlayers}
146+
maxPlayers={maxPlayers}
147+
showRank={showRank}
148+
showAvatar={showAvatar}
149+
title="HIGH SCORES"
150+
currentPlayerId="6"
151+
className="w-full"
152+
/>
153+
154+
<Leaderboard
155+
players={samplePlayers.slice(0, 5)}
156+
maxPlayers={5}
157+
showRank={true}
158+
showAvatar={showAvatar}
159+
title="TOP 5"
160+
currentPlayerId="6"
161+
className="w-full"
162+
/>
163+
</div>
164+
</div>
165+
166+
{/* Additional Examples */}
167+
<div className="space-y-4">
168+
<h3 className="text-lg font-semibold">Additional Examples</h3>
169+
<div className="grid grid-cols-1 lg:grid-cols-2 gap-6">
170+
<Leaderboard
171+
players={samplePlayers}
172+
maxPlayers={maxPlayers}
173+
showRank={showRank}
174+
showAvatar={showAvatar}
175+
title="HIGH SCORES"
176+
currentPlayerId="6"
177+
className="w-full"
178+
/>
179+
180+
<Leaderboard
181+
players={samplePlayers.slice(0, 8)}
182+
maxPlayers={8}
183+
showRank={true}
184+
showAvatar={showAvatar}
185+
title="SCOREBOARD"
186+
currentPlayerId="6"
187+
className="w-full"
188+
/>
189+
</div>
190+
</div>
191+
192+
{/* Compact Examples */}
193+
<div className="space-y-4">
194+
<h3 className="text-lg font-semibold">Compact Examples</h3>
195+
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-4">
196+
<Leaderboard
197+
players={samplePlayers.slice(0, 3)}
198+
maxPlayers={3}
199+
showRank={true}
200+
showAvatar={showAvatar}
201+
title="TOP 3"
202+
currentPlayerId="6"
203+
className="w-full"
204+
/>
205+
206+
<Leaderboard
207+
players={samplePlayers.slice(0, 5)}
208+
maxPlayers={5}
209+
showRank={false}
210+
showAvatar={showAvatar}
211+
title="SCORES"
212+
currentPlayerId="6"
213+
className="w-full"
214+
/>
215+
216+
<Leaderboard
217+
players={samplePlayers.slice(0, 4)}
218+
maxPlayers={4}
219+
showRank={true}
220+
showAvatar={showAvatar}
221+
title="RANKING"
222+
currentPlayerId="6"
223+
className="w-full"
224+
/>
225+
</div>
226+
</div>
227+
228+
{/* Empty State Example */}
229+
<div className="space-y-4">
230+
<h3 className="text-lg font-semibold">Empty State</h3>
231+
<div className="grid grid-cols-1 md:grid-cols-2 gap-6">
232+
<Leaderboard
233+
players={[]}
234+
maxPlayers={10}
235+
showRank={true}
236+
showAvatar={showAvatar}
237+
title="NO PLAYERS YET"
238+
className="w-full"
239+
/>
240+
241+
<Leaderboard
242+
players={[]}
243+
maxPlayers={10}
244+
showRank={true}
245+
showAvatar={showAvatar}
246+
title="EMPTY LEADERBOARD"
247+
className="w-full"
248+
/>
249+
</div>
250+
</div>
251+
</div>
252+
);
253+
}

app/blocks/leaderboard/page.tsx

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
import CopyCommandButton from "@/app/docs/components/copy-command-button";
2+
import { OpenInV0Button } from "@/app/docs/components/open-in-v0-button";
3+
4+
import LeaderboardBlock from "./leaderboard";
5+
6+
export default function LeaderboardPage() {
7+
return (
8+
<div className="flex flex-col gap-10">
9+
<div className="flex flex-col gap-4 border rounded-lg p-4 min-h-[600px]">
10+
<div className="flex flex-col md:flex-row gap-2 items-center justify-between">
11+
<h2 className="text-sm text-muted-foreground sm:pl-3">
12+
Leaderboard Component
13+
</h2>
14+
15+
<div className="flex flex-col md:flex-row items-center gap-2">
16+
<CopyCommandButton
17+
command="pnpm dlx shadcn@latest add @8bitcn/leaderboard"
18+
copyCommand="pnpm dlx shadcn@latest add @8bitcn/leaderboard"
19+
/>
20+
<OpenInV0Button name="8bit-leaderboard" className="w-fit" />
21+
</div>
22+
</div>
23+
24+
<LeaderboardBlock />
25+
</div>
26+
</div>
27+
);
28+
}

0 commit comments

Comments
 (0)