mirror of
https://github.com/blitzkit/blitzkit.git
synced 2026-10-24 03:59:50 +02:00
feat: better layout
This commit is contained in:
1 parent
9e340d2e16
commit
6600bba58c
2 files changed
+222
-222
No files matched your search
+216
-215
@@ -204,21 +204,6 @@ export default function Page() {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<Flex justify="center" wrap="wrap" gap="2">
|
<Flex justify="center" wrap="wrap" gap="2">
|
||||||
<Select.Root
|
|
||||||
onValueChange={(value) => setRowsPerPage(parseInt(value))}
|
|
||||||
defaultValue={`${rowsPerPage}`}
|
|
||||||
>
|
|
||||||
<Select.Trigger />
|
|
||||||
|
|
||||||
<Select.Content>
|
|
||||||
{ROWS_OPTIONS.map((size) => (
|
|
||||||
<Select.Item value={`${size}`} key={size}>
|
|
||||||
{size} per page
|
|
||||||
</Select.Item>
|
|
||||||
))}
|
|
||||||
</Select.Content>
|
|
||||||
</Select.Root>
|
|
||||||
|
|
||||||
<Flex gap="2">
|
<Flex gap="2">
|
||||||
<Button variant="soft" onClick={previousPage}>
|
<Button variant="soft" onClick={previousPage}>
|
||||||
<CaretLeftIcon />
|
<CaretLeftIcon />
|
||||||
@@ -261,232 +246,248 @@ export default function Page() {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<PageWrapper color="orange">
|
<PageWrapper color="orange">
|
||||||
<Flex gap="2">
|
<Flex gap="4" wrap="wrap" justify="center">
|
||||||
<Select.Root
|
<Flex gap="2">
|
||||||
defaultValue={region}
|
<Select.Root
|
||||||
onValueChange={(value) => setRegion(value as Region)}
|
defaultValue={region}
|
||||||
>
|
onValueChange={(value) => setRegion(value as Region)}
|
||||||
<Select.Trigger style={{ flex: 1 }} />
|
>
|
||||||
|
<Select.Trigger style={{ flex: 1 }} />
|
||||||
|
|
||||||
<Select.Content>
|
<Select.Content>
|
||||||
{REGIONS.map((region) => (
|
{REGIONS.map((region) => (
|
||||||
<Select.Item key={region} value={region}>
|
<Select.Item key={region} value={region}>
|
||||||
{REGION_NAMES[region]}
|
{REGION_NAMES[region]}
|
||||||
</Select.Item>
|
</Select.Item>
|
||||||
))}
|
))}
|
||||||
</Select.Content>
|
</Select.Content>
|
||||||
</Select.Root>
|
</Select.Root>
|
||||||
|
|
||||||
<Select.Root
|
<Select.Root
|
||||||
value={`${season}`}
|
value={`${season}`}
|
||||||
onValueChange={(value) =>
|
onValueChange={(value) =>
|
||||||
setSeason(value === 'null' ? null : parseInt(value))
|
setSeason(value === 'null' ? null : parseInt(value))
|
||||||
}
|
}
|
||||||
>
|
>
|
||||||
<Select.Trigger style={{ flex: 1 }} />
|
<Select.Trigger style={{ flex: 1 }} />
|
||||||
|
|
||||||
<Select.Content>
|
<Select.Content>
|
||||||
{ratingsInfo?.detail === undefined && (
|
{ratingsInfo?.detail === undefined && (
|
||||||
<Select.Item value="null">Current season</Select.Item>
|
<Select.Item value="null">Current season</Select.Item>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
<Select.Group>
|
<Select.Group>
|
||||||
<Select.Label>Archives</Select.Label>
|
<Select.Label>Archives</Select.Label>
|
||||||
|
|
||||||
{latestArchivedSeasonNumber &&
|
{latestArchivedSeasonNumber &&
|
||||||
range(
|
range(
|
||||||
FIRST_ARCHIVED_RATINGS_SEASON,
|
FIRST_ARCHIVED_RATINGS_SEASON,
|
||||||
latestArchivedSeasonNumber + 1,
|
latestArchivedSeasonNumber + 1,
|
||||||
)
|
)
|
||||||
.map((season) => (
|
.map((season) => (
|
||||||
<Select.Item key={season} value={`${season}`}>
|
<Select.Item key={season} value={`${season}`}>
|
||||||
Season {season}
|
Season {season}
|
||||||
</Select.Item>
|
|
||||||
))
|
|
||||||
.reverse()}
|
|
||||||
</Select.Group>
|
|
||||||
</Select.Content>
|
|
||||||
</Select.Root>
|
|
||||||
</Flex>
|
|
||||||
|
|
||||||
<Flex gap="3" justify="center" wrap="wrap" align="center">
|
|
||||||
<Text>Jump to:</Text>
|
|
||||||
<Dialog.Root open={jumpToScoreOpen} onOpenChange={setJumpToScoreOpen}>
|
|
||||||
<Dialog.Trigger>
|
|
||||||
<Button variant="ghost">Score</Button>
|
|
||||||
</Dialog.Trigger>
|
|
||||||
|
|
||||||
<Dialog.Content>
|
|
||||||
<Flex gap="4" justify="center">
|
|
||||||
<TextField.Input
|
|
||||||
ref={scoreInput}
|
|
||||||
onChange={handleSearchPlayerChange}
|
|
||||||
type="number"
|
|
||||||
placeholder="Type a score..."
|
|
||||||
onKeyDown={(event) => {
|
|
||||||
if (event.key === 'Enter') {
|
|
||||||
handleJumpToScore();
|
|
||||||
setJumpToScoreOpen(false);
|
|
||||||
}
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
|
|
||||||
<Flex gap="2">
|
|
||||||
<Dialog.Close>
|
|
||||||
<Button color="red">Cancel</Button>
|
|
||||||
</Dialog.Close>
|
|
||||||
<Dialog.Close>
|
|
||||||
<Button onClick={handleJumpToScore}>Jump</Button>
|
|
||||||
</Dialog.Close>
|
|
||||||
</Flex>
|
|
||||||
</Flex>
|
|
||||||
</Dialog.Content>
|
|
||||||
</Dialog.Root>
|
|
||||||
|
|
||||||
<Dialog.Root
|
|
||||||
open={jumpToPositionOpen}
|
|
||||||
onOpenChange={setJumpToPositionOpen}
|
|
||||||
>
|
|
||||||
<Dialog.Trigger>
|
|
||||||
<Button variant="ghost">Position</Button>
|
|
||||||
</Dialog.Trigger>
|
|
||||||
|
|
||||||
<Dialog.Content>
|
|
||||||
<Flex gap="4" justify="center">
|
|
||||||
<TextField.Input
|
|
||||||
ref={positionInput}
|
|
||||||
onChange={handleSearchPlayerChange}
|
|
||||||
type="number"
|
|
||||||
placeholder="Type a position..."
|
|
||||||
onKeyDown={(event) => {
|
|
||||||
if (event.key === 'Enter') {
|
|
||||||
handleJumpToPosition();
|
|
||||||
setJumpToPositionOpen(false);
|
|
||||||
}
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
|
|
||||||
<Flex gap="2">
|
|
||||||
<Dialog.Close>
|
|
||||||
<Button color="red">Cancel</Button>
|
|
||||||
</Dialog.Close>
|
|
||||||
<Dialog.Close>
|
|
||||||
<Button onClick={handleJumpToPosition}>Jump</Button>
|
|
||||||
</Dialog.Close>
|
|
||||||
</Flex>
|
|
||||||
</Flex>
|
|
||||||
</Dialog.Content>
|
|
||||||
</Dialog.Root>
|
|
||||||
|
|
||||||
<Dialog.Root>
|
|
||||||
<Dialog.Trigger>
|
|
||||||
<Button variant="ghost">League</Button>
|
|
||||||
</Dialog.Trigger>
|
|
||||||
|
|
||||||
<Dialog.Content>
|
|
||||||
<Flex gap="2" justify="center">
|
|
||||||
<Select.Root
|
|
||||||
value={`${jumpToLeague}`}
|
|
||||||
onValueChange={(value) => setJumpToLeague(parseInt(value))}
|
|
||||||
>
|
|
||||||
<Select.Trigger />
|
|
||||||
|
|
||||||
<Select.Content>
|
|
||||||
{ratingsInfo?.detail === undefined &&
|
|
||||||
ratingsInfo?.leagues.map(({ index, title }) => (
|
|
||||||
<Select.Item key={index} value={`${index}`}>
|
|
||||||
{title}
|
|
||||||
</Select.Item>
|
</Select.Item>
|
||||||
))}
|
))
|
||||||
</Select.Content>
|
.reverse()}
|
||||||
</Select.Root>
|
</Select.Group>
|
||||||
|
</Select.Content>
|
||||||
|
</Select.Root>
|
||||||
|
|
||||||
<Flex gap="2">
|
<Select.Root
|
||||||
<Dialog.Close>
|
onValueChange={(value) => setRowsPerPage(parseInt(value))}
|
||||||
<Button color="red">Cancel</Button>
|
defaultValue={`${rowsPerPage}`}
|
||||||
</Dialog.Close>
|
>
|
||||||
<Dialog.Close>
|
<Select.Trigger />
|
||||||
<Button
|
|
||||||
onClick={() => {
|
|
||||||
if (!ratingsInfo || ratingsInfo.detail || !players.data)
|
|
||||||
return;
|
|
||||||
|
|
||||||
const minScore =
|
<Select.Content>
|
||||||
LEAGUES[jumpToLeague - 1]?.minScore ?? Infinity;
|
{ROWS_OPTIONS.map((size) => (
|
||||||
const firstPlayerIndex = players.data.findIndex(
|
<Select.Item value={`${size}`} key={size}>
|
||||||
(player) => player.score < minScore,
|
{size} per page
|
||||||
);
|
</Select.Item>
|
||||||
|
))}
|
||||||
|
</Select.Content>
|
||||||
|
</Select.Root>
|
||||||
|
</Flex>
|
||||||
|
|
||||||
if (firstPlayerIndex === -1) return;
|
<Flex gap="3" justify="center" wrap="wrap" align="center">
|
||||||
|
<Text>Jump to:</Text>
|
||||||
setPage(Math.floor(firstPlayerIndex / rowsPerPage));
|
<Dialog.Root open={jumpToScoreOpen} onOpenChange={setJumpToScoreOpen}>
|
||||||
setHighlightedPlayerId(players.data[firstPlayerIndex].id);
|
<Dialog.Trigger>
|
||||||
}}
|
<Button variant="ghost">Score</Button>
|
||||||
>
|
</Dialog.Trigger>
|
||||||
Jump
|
|
||||||
</Button>
|
|
||||||
</Dialog.Close>
|
|
||||||
</Flex>
|
|
||||||
</Flex>
|
|
||||||
</Dialog.Content>
|
|
||||||
</Dialog.Root>
|
|
||||||
|
|
||||||
<Dialog.Root>
|
|
||||||
<Dialog.Trigger>
|
|
||||||
<Button variant="ghost">Player</Button>
|
|
||||||
</Dialog.Trigger>
|
|
||||||
|
|
||||||
<Dialog.Content>
|
|
||||||
<Flex gap="4" direction="column">
|
|
||||||
<TextField.Root>
|
|
||||||
<TextField.Slot>
|
|
||||||
<MagnifyingGlassIcon height="16" width="16" />
|
|
||||||
</TextField.Slot>
|
|
||||||
|
|
||||||
|
<Dialog.Content>
|
||||||
|
<Flex gap="4" justify="center">
|
||||||
<TextField.Input
|
<TextField.Input
|
||||||
|
ref={scoreInput}
|
||||||
onChange={handleSearchPlayerChange}
|
onChange={handleSearchPlayerChange}
|
||||||
placeholder="Search for player..."
|
type="number"
|
||||||
|
placeholder="Type a score..."
|
||||||
|
onKeyDown={(event) => {
|
||||||
|
if (event.key === 'Enter') {
|
||||||
|
handleJumpToScore();
|
||||||
|
setJumpToScoreOpen(false);
|
||||||
|
}
|
||||||
|
}}
|
||||||
/>
|
/>
|
||||||
</TextField.Root>
|
|
||||||
|
|
||||||
<Flex direction="column" gap="2">
|
<Flex gap="2">
|
||||||
{searchResults?.map((player) => (
|
<Dialog.Close>
|
||||||
|
<Button color="red">Cancel</Button>
|
||||||
|
</Dialog.Close>
|
||||||
|
<Dialog.Close>
|
||||||
|
<Button onClick={handleJumpToScore}>Jump</Button>
|
||||||
|
</Dialog.Close>
|
||||||
|
</Flex>
|
||||||
|
</Flex>
|
||||||
|
</Dialog.Content>
|
||||||
|
</Dialog.Root>
|
||||||
|
<Dialog.Root
|
||||||
|
open={jumpToPositionOpen}
|
||||||
|
onOpenChange={setJumpToPositionOpen}
|
||||||
|
>
|
||||||
|
<Dialog.Trigger>
|
||||||
|
<Button variant="ghost">Position</Button>
|
||||||
|
</Dialog.Trigger>
|
||||||
|
|
||||||
|
<Dialog.Content>
|
||||||
|
<Flex gap="4" justify="center">
|
||||||
|
<TextField.Input
|
||||||
|
ref={positionInput}
|
||||||
|
onChange={handleSearchPlayerChange}
|
||||||
|
type="number"
|
||||||
|
placeholder="Type a position..."
|
||||||
|
onKeyDown={(event) => {
|
||||||
|
if (event.key === 'Enter') {
|
||||||
|
handleJumpToPosition();
|
||||||
|
setJumpToPositionOpen(false);
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
|
||||||
|
<Flex gap="2">
|
||||||
|
<Dialog.Close>
|
||||||
|
<Button color="red">Cancel</Button>
|
||||||
|
</Dialog.Close>
|
||||||
|
<Dialog.Close>
|
||||||
|
<Button onClick={handleJumpToPosition}>Jump</Button>
|
||||||
|
</Dialog.Close>
|
||||||
|
</Flex>
|
||||||
|
</Flex>
|
||||||
|
</Dialog.Content>
|
||||||
|
</Dialog.Root>
|
||||||
|
<Dialog.Root>
|
||||||
|
<Dialog.Trigger>
|
||||||
|
<Button variant="ghost">League</Button>
|
||||||
|
</Dialog.Trigger>
|
||||||
|
|
||||||
|
<Dialog.Content>
|
||||||
|
<Flex gap="2" justify="center">
|
||||||
|
<Select.Root
|
||||||
|
value={`${jumpToLeague}`}
|
||||||
|
onValueChange={(value) => setJumpToLeague(parseInt(value))}
|
||||||
|
>
|
||||||
|
<Select.Trigger />
|
||||||
|
|
||||||
|
<Select.Content>
|
||||||
|
{ratingsInfo?.detail === undefined &&
|
||||||
|
ratingsInfo?.leagues.map(({ index, title }) => (
|
||||||
|
<Select.Item key={index} value={`${index}`}>
|
||||||
|
{title}
|
||||||
|
</Select.Item>
|
||||||
|
))}
|
||||||
|
</Select.Content>
|
||||||
|
</Select.Root>
|
||||||
|
|
||||||
|
<Flex gap="2">
|
||||||
|
<Dialog.Close>
|
||||||
|
<Button color="red">Cancel</Button>
|
||||||
|
</Dialog.Close>
|
||||||
<Dialog.Close>
|
<Dialog.Close>
|
||||||
<Button
|
<Button
|
||||||
key={player.account_id}
|
|
||||||
variant="ghost"
|
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
const playerIndex = players.data?.findIndex(
|
if (!ratingsInfo || ratingsInfo.detail || !players.data)
|
||||||
(playerData) => playerData.id === player.account_id,
|
return;
|
||||||
);
|
|
||||||
const playerPage = Math.floor(
|
const minScore =
|
||||||
playerIndex! / rowsPerPage,
|
LEAGUES[jumpToLeague - 1]?.minScore ?? Infinity;
|
||||||
|
const firstPlayerIndex = players.data.findIndex(
|
||||||
|
(player) => player.score < minScore,
|
||||||
);
|
);
|
||||||
|
|
||||||
setPage(playerPage);
|
if (firstPlayerIndex === -1) return;
|
||||||
setHighlightedPlayerId(player.account_id);
|
|
||||||
|
setPage(Math.floor(firstPlayerIndex / rowsPerPage));
|
||||||
|
setHighlightedPlayerId(
|
||||||
|
players.data[firstPlayerIndex].id,
|
||||||
|
);
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{player.nickname}
|
Jump
|
||||||
</Button>
|
</Button>
|
||||||
</Dialog.Close>
|
</Dialog.Close>
|
||||||
))}
|
</Flex>
|
||||||
|
|
||||||
{searchResults?.length === 0 && (
|
|
||||||
<Button disabled variant="ghost">
|
|
||||||
No players found in leaderboard
|
|
||||||
</Button>
|
|
||||||
)}
|
|
||||||
</Flex>
|
</Flex>
|
||||||
|
</Dialog.Content>
|
||||||
|
</Dialog.Root>
|
||||||
|
<Dialog.Root>
|
||||||
|
<Dialog.Trigger>
|
||||||
|
<Button variant="ghost">Player</Button>
|
||||||
|
</Dialog.Trigger>
|
||||||
|
|
||||||
<Flex gap="2">
|
<Dialog.Content>
|
||||||
<Dialog.Close>
|
<Flex gap="4" direction="column">
|
||||||
<Button color="red">Cancel</Button>
|
<TextField.Root>
|
||||||
</Dialog.Close>
|
<TextField.Slot>
|
||||||
|
<MagnifyingGlassIcon height="16" width="16" />
|
||||||
|
</TextField.Slot>
|
||||||
|
|
||||||
|
<TextField.Input
|
||||||
|
onChange={handleSearchPlayerChange}
|
||||||
|
placeholder="Search for player..."
|
||||||
|
/>
|
||||||
|
</TextField.Root>
|
||||||
|
|
||||||
|
<Flex direction="column" gap="2">
|
||||||
|
{searchResults?.map((player) => (
|
||||||
|
<Dialog.Close>
|
||||||
|
<Button
|
||||||
|
key={player.account_id}
|
||||||
|
variant="ghost"
|
||||||
|
onClick={() => {
|
||||||
|
const playerIndex = players.data?.findIndex(
|
||||||
|
(playerData) => playerData.id === player.account_id,
|
||||||
|
);
|
||||||
|
const playerPage = Math.floor(
|
||||||
|
playerIndex! / rowsPerPage,
|
||||||
|
);
|
||||||
|
|
||||||
|
setPage(playerPage);
|
||||||
|
setHighlightedPlayerId(player.account_id);
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{player.nickname}
|
||||||
|
</Button>
|
||||||
|
</Dialog.Close>
|
||||||
|
))}
|
||||||
|
|
||||||
|
{searchResults?.length === 0 && (
|
||||||
|
<Button disabled variant="ghost">
|
||||||
|
No players found in leaderboard
|
||||||
|
</Button>
|
||||||
|
)}
|
||||||
|
</Flex>
|
||||||
|
|
||||||
|
<Flex gap="2">
|
||||||
|
<Dialog.Close>
|
||||||
|
<Button color="red">Cancel</Button>
|
||||||
|
</Dialog.Close>
|
||||||
|
</Flex>
|
||||||
</Flex>
|
</Flex>
|
||||||
</Flex>
|
</Dialog.Content>
|
||||||
</Dialog.Content>
|
</Dialog.Root>
|
||||||
</Dialog.Root>
|
</Flex>
|
||||||
</Flex>
|
</Flex>
|
||||||
|
|
||||||
<PageTurner />
|
<PageTurner />
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import { Theme } from '@radix-ui/themes';
|
import { Flex, Theme } from '@radix-ui/themes';
|
||||||
import { ComponentProps } from 'react';
|
import { ComponentProps } from 'react';
|
||||||
|
|
||||||
interface PageWrapperProps extends ComponentProps<'div'> {
|
interface PageWrapperProps extends ComponentProps<typeof Flex> {
|
||||||
color?: ComponentProps<typeof Theme>['accentColor'];
|
color?: ComponentProps<typeof Theme>['accentColor'];
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -12,20 +12,19 @@ export default function PageWrapper({
|
|||||||
}: PageWrapperProps) {
|
}: PageWrapperProps) {
|
||||||
return (
|
return (
|
||||||
<Theme accentColor={color}>
|
<Theme accentColor={color}>
|
||||||
<div
|
<Flex
|
||||||
|
{...props}
|
||||||
|
direction="column"
|
||||||
|
gap="4"
|
||||||
style={{
|
style={{
|
||||||
width: '100%',
|
width: '100%',
|
||||||
maxWidth: 800,
|
maxWidth: 800,
|
||||||
margin: 'auto',
|
margin: 'auto',
|
||||||
display: 'flex',
|
|
||||||
padding: 16,
|
padding: 16,
|
||||||
flexDirection: 'column',
|
|
||||||
gap: 32,
|
|
||||||
boxSizing: 'border-box',
|
boxSizing: 'border-box',
|
||||||
|
|
||||||
...style,
|
...style,
|
||||||
}}
|
}}
|
||||||
{...props}
|
|
||||||
/>
|
/>
|
||||||
</Theme>
|
</Theme>
|
||||||
);
|
);
|
||||||
|
|||||||
Reference in new issue
Block a user