feat: per-page theming

This commit is contained in:
TrèsAbhi committed 2023-11-07 10:06:15 -06:00
1 parent bd1e17bac9
commit 1bb83da253
6 files changed
+106 -97

No files matched your search

+1 -6
View File
@@ -41,12 +41,7 @@ export default function RootLayout({ children }: RootLayoutProps) {
: 'url(https://i.imgur.com/PhS06NJ.png)', : 'url(https://i.imgur.com/PhS06NJ.png)',
}} }}
> >
<Theme <Theme appearance="dark" panelBackground="translucent" radius="full">
appearance="dark"
panelBackground="translucent"
radius="full"
accentColor="blue"
>
{!isEmbed && <Navbar />} {!isEmbed && <Navbar />}
<Flex direction="column">{children}</Flex> <Flex direction="column">{children}</Flex>
+74 -70
View File
@@ -5,14 +5,7 @@ import {
CaretRightIcon, CaretRightIcon,
MagnifyingGlassIcon, MagnifyingGlassIcon,
} from '@radix-ui/react-icons'; } from '@radix-ui/react-icons';
import { import { Button, Flex, Popover, Select, TextField } from '@radix-ui/themes';
Button,
Flex,
Popover,
Select,
Text,
TextField,
} from '@radix-ui/themes';
import { produce } from 'immer'; import { produce } from 'immer';
import { range } from 'lodash'; import { range } from 'lodash';
import { useEffect, useRef, useState } from 'react'; import { useEffect, useRef, useState } from 'react';
@@ -73,11 +66,15 @@ export default function Page() {
} }
}); });
const [page, setPage] = useState(0); const [page, setPage] = useState(0);
const pageInput = useRef<HTMLInputElement>(null);
const playerSlice = players.data?.slice( const playerSlice = players.data?.slice(
page * rowsPerPage, page * rowsPerPage,
page * rowsPerPage + rowsPerPage, page * rowsPerPage + rowsPerPage,
); );
const previousPage = () => setPage((page) => Math.max(page - 1, 0));
const nextPage = () =>
setPage((page) =>
Math.min(page + 1, Math.floor((players.data?.length ?? 0) / rowsPerPage)),
);
cachePage(page - 1); cachePage(page - 1);
cachePage(page); cachePage(page);
@@ -115,16 +112,51 @@ export default function Page() {
} }
} }
useEffect(() => {
if (pageInput.current) pageInput.current.value = `${page + 1}`;
}, [page]);
useEffect(() => { useEffect(() => {
setPage(0); setPage(0);
}, [season, region]); }, [season, region]);
function PageTurner() {
const pageInput = useRef<HTMLInputElement>(null);
useEffect(() => {
if (pageInput.current) pageInput.current.value = `${page + 1}`;
}, [page]);
return (
<TextField.Root className={noArrows}>
<TextField.Slot>Page</TextField.Slot>
<TextField.Input
className={noArrows}
type="number"
ref={pageInput}
style={{ width: 64, textAlign: 'center' }}
onBlur={(event) => {
setPage(
Math.max(
0,
Math.min(
Math.floor((players.data?.length ?? 0) / rowsPerPage),
event.target.valueAsNumber - 1,
),
),
);
}}
onKeyDown={(event) => {
if (event.key === 'Enter') {
(event.target as HTMLInputElement).blur();
}
}}
/>
<TextField.Slot>
out of {Math.ceil((players.data?.length ?? 0) / rowsPerPage)}
</TextField.Slot>
</TextField.Root>
);
}
return ( return (
<PageWrapper> <PageWrapper color="tomato">
<Flex gap="2"> <Flex gap="2">
<Select.Root <Select.Root
defaultValue={region} defaultValue={region}
@@ -256,72 +288,44 @@ export default function Page() {
</Select.Root> </Select.Root>
<Flex gap="2" align="center"> <Flex gap="2" align="center">
<Button <Button variant="soft" onClick={previousPage}>
variant="soft"
onClick={() => setPage((page) => Math.max(page - 1, 0))}
>
<CaretLeftIcon /> <CaretLeftIcon />
</Button> </Button>
<TextField.Root className={noArrows}> <PageTurner />
<TextField.Slot>Page</TextField.Slot> <Button variant="soft" onClick={nextPage}>
<TextField.Input
className={noArrows}
type="number"
ref={pageInput}
style={{ width: 64, textAlign: 'center' }}
onBlur={(event) => {
setPage(
Math.max(
0,
Math.min(
Math.floor((players.data?.length ?? 0) / rowsPerPage),
event.target.valueAsNumber - 1,
),
),
);
}}
onKeyDown={(event) => {
if (event.key === 'Enter') {
(event.target as HTMLInputElement).blur();
}
}}
/>
<TextField.Slot>
out of {Math.ceil((players.data?.length ?? 0) / rowsPerPage)}
</TextField.Slot>
</TextField.Root>
<Button
variant="soft"
onClick={() =>
setPage((page) =>
Math.min(
page + 1,
Math.floor((players.data?.length ?? 0) / rowsPerPage),
),
)
}
>
<CaretRightIcon /> <CaretRightIcon />
</Button> </Button>
</Flex> </Flex>
</Flex> </Flex>
<Leaderboard.Root> <Leaderboard.Root>
{playerSlice?.map((player, index) => ( {players.isLoading ? (
<Leaderboard.Item <Leaderboard.Gap message="Loading players..." />
nickname={ ) : (
usernameCache[region][player.id] ?? playerSlice?.map((player, index) => (
`Loading player ${player.id}...` <Leaderboard.Item
} nickname={
position={page * rowsPerPage + index + 1} usernameCache[region][player.id] ??
score={player.score} `Loading player ${player.id}...`
clan={clanCache[region][player.id]} }
key={player.id} position={page * rowsPerPage + index + 1}
/> score={player.score}
))} clan={clanCache[region][player.id]}
key={player.id}
/>
))
)}
</Leaderboard.Root> </Leaderboard.Root>
{players.isLoading && <Text>Loading...</Text>} <Flex justify="center" gap="2">
<Button variant="soft" onClick={previousPage}>
<CaretLeftIcon />
</Button>
<PageTurner />
<Button variant="soft" onClick={nextPage}>
<CaretRightIcon />
</Button>
</Flex>
</PageWrapper> </PageWrapper>
); );
} }
+1 -1
View File
@@ -51,7 +51,7 @@ export default function Page() {
}, [session]); }, [session]);
return ( return (
<PageWrapper> <PageWrapper color="blue">
<div className={styles.toolBar}> <div className={styles.toolBar}>
<div style={{ flex: 1, boxSizing: 'border-box', position: 'relative' }}> <div style={{ flex: 1, boxSizing: 'border-box', position: 'relative' }}>
<TextField.Root> <TextField.Root>
+4 -2
View File
@@ -474,10 +474,12 @@ export const ratingsCommand = new Promise<CommandRegistryRaw>(
{items && ( {items && (
<Leaderboard.Root> <Leaderboard.Root>
{playersBefore > 0 && ( {playersBefore > 0 && (
<Leaderboard.Gap number={playersBefore} /> <Leaderboard.Gap message={`+ ${playersBefore} more`} />
)} )}
{items} {items}
{playersAfter > 0 && <Leaderboard.Gap number={playersAfter} />} {playersAfter > 0 && (
<Leaderboard.Gap message={`+ ${playersAfter} more`} />
)}
</Leaderboard.Root> </Leaderboard.Root>
)} )}
</Wrapper> </Wrapper>
@@ -1,10 +1,10 @@
import { theme } from '../../../stitches.config'; import { theme } from '../../../stitches.config';
export interface GapProps { export interface GapProps {
number: number; message: string;
} }
export function Gap({ number }: GapProps) { export function Gap({ message: number }: GapProps) {
return ( return (
<div <div
style={{ style={{
@@ -22,7 +22,7 @@ export function Gap({ number }: GapProps) {
fontSize: 16, fontSize: 16,
}} }}
> >
+ {number.toLocaleString()} more {number.toLocaleString()}
</span> </span>
</div> </div>
); );
+23 -15
View File
@@ -1,24 +1,32 @@
import { Theme } from '@radix-ui/themes';
import { ComponentProps } from 'react'; import { ComponentProps } from 'react';
interface PageWrapperProps extends ComponentProps<'div'> {
color?: ComponentProps<typeof Theme>['accentColor'];
}
export default function PageWrapper({ export default function PageWrapper({
style, style,
color,
...props ...props
}: ComponentProps<'div'>) { }: PageWrapperProps) {
return ( return (
<div <Theme accentColor={color}>
style={{ <div
width: '100%', style={{
maxWidth: 800, width: '100%',
margin: 'auto', maxWidth: 800,
display: 'flex', margin: 'auto',
padding: 16, display: 'flex',
flexDirection: 'column', padding: 16,
gap: 16, flexDirection: 'column',
boxSizing: 'border-box', gap: 16,
boxSizing: 'border-box',
...style, ...style,
}} }}
{...props} {...props}
/> />
</Theme>
); );
} }