mirror of
https://github.com/blitzkit/blitzkit.git
synced 2026-10-26 03:59:53 +01:00
feat: more layout
This commit is contained in:
1 parent
c900af6e33
commit
9d0abdb135
4 files changed
+265
-218
No files matched your search
@@ -1,11 +0,0 @@
|
|||||||
import { style } from '@vanilla-extract/css';
|
|
||||||
|
|
||||||
export const bkniIndicator = style({
|
|
||||||
scale: 0.8,
|
|
||||||
|
|
||||||
'@media': {
|
|
||||||
'screen and (min-width: 1024px)': {
|
|
||||||
scale: 1,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
});
|
|
||||||
@@ -1,24 +1,31 @@
|
|||||||
'use client';
|
'use client';
|
||||||
|
|
||||||
|
import { TimerIcon } from '@radix-ui/react-icons';
|
||||||
import {
|
import {
|
||||||
Box,
|
Box,
|
||||||
|
Button,
|
||||||
Flex,
|
Flex,
|
||||||
Heading,
|
Heading,
|
||||||
Link,
|
Link,
|
||||||
Popover,
|
Popover,
|
||||||
|
SegmentedControl,
|
||||||
Text,
|
Text,
|
||||||
Theme,
|
Theme,
|
||||||
} from '@radix-ui/themes';
|
} from '@radix-ui/themes';
|
||||||
import { useState } from 'react';
|
import { useState } from 'react';
|
||||||
import { BkniIndicator } from '../../../../components/BkniIndicator';
|
|
||||||
import { DatePicker } from '../../../../components/DatePicker';
|
import { DatePicker } from '../../../../components/DatePicker';
|
||||||
|
import { Speedometer } from '../../../../components/Speedometer';
|
||||||
import { getAccountInfo } from '../../../../core/blitz/getAccountInfo';
|
import { getAccountInfo } from '../../../../core/blitz/getAccountInfo';
|
||||||
import { getClanAccountInfo } from '../../../../core/blitz/getClanAccountInfo';
|
import { getClanAccountInfo } from '../../../../core/blitz/getClanAccountInfo';
|
||||||
import { idToRegion } from '../../../../core/blitz/idToRegion';
|
import { idToRegion } from '../../../../core/blitz/idToRegion';
|
||||||
import { parseBkni } from '../../../../core/blitzkit/parseBkni';
|
import { parseBkni } from '../../../../core/blitzkit/parseBkni';
|
||||||
|
import { PeriodType } from '../../../../core/discord/addPeriodSubCommands';
|
||||||
|
import {
|
||||||
|
BKNI_COLORS,
|
||||||
|
BKNI_PERCENTILES,
|
||||||
|
} from '../../../../core/statistics/getBkniPercentile';
|
||||||
import { useAwait } from '../../../../hooks/useAwait';
|
import { useAwait } from '../../../../hooks/useAwait';
|
||||||
import strings from '../../../../lang/en-US.json';
|
import strings from '../../../../lang/en-US.json';
|
||||||
import * as styles from './page.css';
|
|
||||||
|
|
||||||
export default function Page({ params }: { params: { id: string } }) {
|
export default function Page({ params }: { params: { id: string } }) {
|
||||||
const id = parseInt(params.id);
|
const id = parseInt(params.id);
|
||||||
@@ -26,18 +33,18 @@ export default function Page({ params }: { params: { id: string } }) {
|
|||||||
const accountInfo = useAwait(getAccountInfo(region, id));
|
const accountInfo = useAwait(getAccountInfo(region, id));
|
||||||
const clanAccountInfo = useAwait(getClanAccountInfo(region, id, ['clan']));
|
const clanAccountInfo = useAwait(getClanAccountInfo(region, id, ['clan']));
|
||||||
// const bkni = ((Math.round(Date.now() / 1000 / 60) / 100) % 2) - 1;
|
// const bkni = ((Math.round(Date.now() / 1000 / 60) / 100) % 2) - 1;
|
||||||
const bkni = 2 * Math.random() - 1;
|
const bkni = 0.81;
|
||||||
const [period, setPeriod] = useState<'custom' | number>(30);
|
const winrate = 0.6;
|
||||||
const { bkniColor } = parseBkni(bkni);
|
const [period, setPeriod] = useState<PeriodType>('30');
|
||||||
const isTracking = false;
|
const { bkniColor, bkniMetric, bkniFraction, bkniPercentile } =
|
||||||
|
parseBkni(bkni);
|
||||||
const [customFrom, setCustomFrom] = useState<Date>(
|
const [customFrom, setCustomFrom] = useState<Date>(
|
||||||
new Date(Date.now() - 29 * 24 * 60 * 60 * 1000),
|
new Date(Date.now() - 29 * 24 * 60 * 60 * 1000),
|
||||||
);
|
);
|
||||||
const stats = [
|
const stats = [
|
||||||
['Winrate', '70%'],
|
|
||||||
['Survival', '70%'],
|
|
||||||
['Damage', '3,203'],
|
['Damage', '3,203'],
|
||||||
['Tier', '9.83'],
|
['Tier', '9.83'],
|
||||||
|
['Survival', '70%'],
|
||||||
['Kills', '1.54'],
|
['Kills', '1.54'],
|
||||||
['Damage ratio', '2.45'],
|
['Damage ratio', '2.45'],
|
||||||
['Accuracy', '91%'],
|
['Accuracy', '91%'],
|
||||||
@@ -52,110 +59,213 @@ export default function Page({ params }: { params: { id: string } }) {
|
|||||||
<Flex
|
<Flex
|
||||||
direction="column"
|
direction="column"
|
||||||
align="center"
|
align="center"
|
||||||
p="8"
|
justify="center"
|
||||||
gap="8"
|
py="8"
|
||||||
|
gap="6"
|
||||||
style={{
|
style={{
|
||||||
background: `linear-gradient(var(--${bkniColor}-a2), var(--${bkniColor}-a1))`,
|
background: `linear-gradient(var(--${bkniColor}-a3), var(--${bkniColor}-a1))`,
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<Flex width="100%" maxWidth="1600px" align="center">
|
<Flex direction="column" align="center">
|
||||||
<Flex style={{ flex: 1 }} direction="column" align="start" gap="4">
|
<Heading
|
||||||
<Heading size="9">{accountInfo.nickname}</Heading>
|
size={{
|
||||||
|
initial: '8',
|
||||||
<Text color="gray">
|
sm: '9',
|
||||||
{clanAccountInfo?.clan && `[${clanAccountInfo.clan.tag}] • `}
|
}}
|
||||||
{
|
align="center"
|
||||||
(clanAccountInfo?.clan
|
>
|
||||||
? strings.common.regions.short
|
{accountInfo.nickname}
|
||||||
: strings.common.regions.normal)[region]
|
</Heading>
|
||||||
}
|
<Text color="gray" align="center">
|
||||||
</Text>
|
{clanAccountInfo?.clan && `[${clanAccountInfo.clan.tag}] • `}
|
||||||
|
{
|
||||||
{/* <Flex gap="4" mt="-1">
|
(clanAccountInfo?.clan
|
||||||
<Link href="/tools/tankopedia">
|
? strings.common.regions.short
|
||||||
<Button variant="ghost" size="1" ml="-1">
|
: strings.common.regions.normal)[region]
|
||||||
<ChevronLeftIcon />
|
}
|
||||||
Back
|
</Text>
|
||||||
</Button>
|
|
||||||
</Link>
|
|
||||||
</Flex> */}
|
|
||||||
</Flex>
|
|
||||||
|
|
||||||
<Flex style={{ flex: 1 }} justify="center">
|
|
||||||
<BkniIndicator bkni={bkni} className={styles.bkniIndicator} />
|
|
||||||
</Flex>
|
|
||||||
|
|
||||||
<Flex style={{ flex: 1 }} wrap="wrap" gap="2" justify="center">
|
|
||||||
{stats.map((stat) => (
|
|
||||||
<Flex minWidth="96px" direction="column" align="center">
|
|
||||||
<Text size="7">{stat[1]}</Text>
|
|
||||||
<Text color="gray" size="2">
|
|
||||||
{stat[0]}
|
|
||||||
</Text>
|
|
||||||
</Flex>
|
|
||||||
))}
|
|
||||||
</Flex>
|
|
||||||
</Flex>
|
</Flex>
|
||||||
|
|
||||||
{period === 'custom' && (
|
<Flex direction="column" align="center" gap="3">
|
||||||
<Text>
|
<SegmentedControl.Root
|
||||||
From{' '}
|
value={period}
|
||||||
<Popover.Root
|
onValueChange={(value) => setPeriod(value as PeriodType)}
|
||||||
open={fromSelectorOpen}
|
>
|
||||||
onOpenChange={setFromSelectorOpen}
|
<SegmentedControl.Item value={'today' satisfies PeriodType}>
|
||||||
>
|
Today
|
||||||
|
</SegmentedControl.Item>
|
||||||
|
<SegmentedControl.Item value={'30' satisfies PeriodType}>
|
||||||
|
30 days
|
||||||
|
</SegmentedControl.Item>
|
||||||
|
<SegmentedControl.Item value={'career' satisfies PeriodType}>
|
||||||
|
Career
|
||||||
|
</SegmentedControl.Item>
|
||||||
|
<SegmentedControl.Item value={'custom' satisfies PeriodType}>
|
||||||
|
Custom
|
||||||
|
</SegmentedControl.Item>
|
||||||
|
</SegmentedControl.Root>
|
||||||
|
|
||||||
|
{period === 'custom' && (
|
||||||
|
<Text>
|
||||||
|
From{' '}
|
||||||
|
<Popover.Root
|
||||||
|
open={fromSelectorOpen}
|
||||||
|
onOpenChange={setFromSelectorOpen}
|
||||||
|
>
|
||||||
|
<Popover.Trigger>
|
||||||
|
<Link underline="always" href="#">
|
||||||
|
{customFrom.toLocaleString(undefined, {
|
||||||
|
month: 'short',
|
||||||
|
day: 'numeric',
|
||||||
|
year: 'numeric',
|
||||||
|
})}
|
||||||
|
</Link>
|
||||||
|
</Popover.Trigger>
|
||||||
|
|
||||||
|
<Popover.Content>
|
||||||
|
<DatePicker
|
||||||
|
defaultDate={customFrom}
|
||||||
|
onDateChange={(date) => {
|
||||||
|
setCustomFrom(date);
|
||||||
|
setFromSelectorOpen(false);
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</Popover.Content>
|
||||||
|
</Popover.Root>{' '}
|
||||||
|
to{' '}
|
||||||
|
<Popover.Root
|
||||||
|
open={toSelectorOpen}
|
||||||
|
onOpenChange={setToSelectorOpen}
|
||||||
|
>
|
||||||
|
<Popover.Trigger>
|
||||||
|
<Link underline="always" href="#">
|
||||||
|
{customTo.toLocaleString(undefined, {
|
||||||
|
month: 'short',
|
||||||
|
day: 'numeric',
|
||||||
|
year: 'numeric',
|
||||||
|
})}
|
||||||
|
</Link>
|
||||||
|
</Popover.Trigger>
|
||||||
|
|
||||||
|
<Popover.Content>
|
||||||
|
<DatePicker
|
||||||
|
defaultDate={customTo}
|
||||||
|
onDateChange={(date) => {
|
||||||
|
setCustomTo(date);
|
||||||
|
setToSelectorOpen(false);
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</Popover.Content>
|
||||||
|
</Popover.Root>
|
||||||
|
</Text>
|
||||||
|
)}
|
||||||
|
</Flex>
|
||||||
|
</Flex>
|
||||||
|
|
||||||
|
<Flex justify="center" py="8" gap="6">
|
||||||
|
<Speedometer
|
||||||
|
value={`${bkniMetric}`}
|
||||||
|
color={bkniColor}
|
||||||
|
fill={bkniFraction}
|
||||||
|
label={
|
||||||
|
<Popover.Root>
|
||||||
<Popover.Trigger>
|
<Popover.Trigger>
|
||||||
<Link underline="always" href="#">
|
<Button
|
||||||
{customFrom.toLocaleString(undefined, {
|
style={{ position: 'relative' }}
|
||||||
month: 'short',
|
color="gray"
|
||||||
day: 'numeric',
|
size="2"
|
||||||
year: 'numeric',
|
variant="ghost"
|
||||||
})}
|
>
|
||||||
</Link>
|
<TimerIcon />
|
||||||
|
{strings.common.bkni_percentile[bkniPercentile]}
|
||||||
|
</Button>
|
||||||
</Popover.Trigger>
|
</Popover.Trigger>
|
||||||
|
|
||||||
<Popover.Content>
|
<Popover.Content align="center">
|
||||||
<DatePicker
|
<Flex
|
||||||
defaultDate={customFrom}
|
direction="column"
|
||||||
onDateChange={(date) => {
|
style={{
|
||||||
setCustomFrom(date);
|
width: 'fit-content',
|
||||||
setFromSelectorOpen(false);
|
|
||||||
}}
|
}}
|
||||||
/>
|
>
|
||||||
</Popover.Content>
|
<Flex direction="column" mb="3" align="center">
|
||||||
</Popover.Root>{' '}
|
<Heading weight="bold" size="5">
|
||||||
to{' '}
|
BkNI
|
||||||
<Popover.Root
|
</Heading>
|
||||||
open={toSelectorOpen}
|
<Text size="2" color="gray" wrap="wrap">
|
||||||
onOpenChange={setToSelectorOpen}
|
A next generation performance metric
|
||||||
>
|
</Text>
|
||||||
<Popover.Trigger>
|
</Flex>
|
||||||
<Link underline="always" href="#">
|
|
||||||
{customTo.toLocaleString(undefined, {
|
|
||||||
month: 'short',
|
|
||||||
day: 'numeric',
|
|
||||||
year: 'numeric',
|
|
||||||
})}
|
|
||||||
</Link>
|
|
||||||
</Popover.Trigger>
|
|
||||||
|
|
||||||
<Popover.Content>
|
{BKNI_PERCENTILES.map(({ percentile, min }, index) => (
|
||||||
<DatePicker
|
<Button
|
||||||
defaultDate={customTo}
|
radius="none"
|
||||||
onDateChange={(date) => {
|
color={BKNI_COLORS[percentile]}
|
||||||
setCustomTo(date);
|
key={percentile}
|
||||||
setToSelectorOpen(false);
|
>
|
||||||
}}
|
<Flex
|
||||||
/>
|
align="center"
|
||||||
|
justify="between"
|
||||||
|
width="100%"
|
||||||
|
gap="2"
|
||||||
|
>
|
||||||
|
<Text>
|
||||||
|
{strings.common.bkni_percentile[percentile]}
|
||||||
|
</Text>
|
||||||
|
<Text size="1">
|
||||||
|
{min} - {BKNI_PERCENTILES[index + 1]?.min ?? 200}
|
||||||
|
</Text>
|
||||||
|
</Flex>
|
||||||
|
</Button>
|
||||||
|
))}
|
||||||
|
</Flex>
|
||||||
</Popover.Content>
|
</Popover.Content>
|
||||||
</Popover.Root>
|
</Popover.Root>
|
||||||
</Text>
|
}
|
||||||
)}
|
/>
|
||||||
|
|
||||||
|
<Speedometer
|
||||||
|
value={
|
||||||
|
<Flex align="center" gap="1">
|
||||||
|
{(winrate * 100).toFixed(0)}
|
||||||
|
<Text size="3" color="gray">
|
||||||
|
%
|
||||||
|
</Text>
|
||||||
|
</Flex>
|
||||||
|
}
|
||||||
|
color="green"
|
||||||
|
fill={0.5 * (Math.cbrt(2 * winrate - 1) + 1)}
|
||||||
|
label={<Text color="gray">Winrate</Text>}
|
||||||
|
/>
|
||||||
|
</Flex>
|
||||||
|
|
||||||
|
<Flex style={{ flex: 1 }} wrap="wrap" gap="2" justify="center">
|
||||||
|
{stats.map((stat) => (
|
||||||
|
<Flex minWidth="96px" direction="column" align="center">
|
||||||
|
<Text size="6">{stat[1]}</Text>
|
||||||
|
<Text color="gray" size="2">
|
||||||
|
{stat[0]}
|
||||||
|
</Text>
|
||||||
|
</Flex>
|
||||||
|
))}
|
||||||
</Flex>
|
</Flex>
|
||||||
</Theme>
|
</Theme>
|
||||||
|
|
||||||
<Box flexGrow="1" />
|
<Box flexGrow="1" />
|
||||||
|
|
||||||
|
{/* <Flex gap="4">
|
||||||
|
<Link href="/tools/tankopedia">
|
||||||
|
<Button variant="ghost" size="1" ml="-1">
|
||||||
|
<ChevronLeftIcon />
|
||||||
|
Back
|
||||||
|
</Button>
|
||||||
|
</Link>
|
||||||
|
</Flex> */}
|
||||||
|
{/* <Flex style={{ flex: 1 }} justify="center">
|
||||||
|
</Flex>
|
||||||
|
|
||||||
|
*/}
|
||||||
|
|
||||||
{/* <PageWrapper
|
{/* <PageWrapper
|
||||||
p="6"
|
p="6"
|
||||||
pt="9"
|
pt="9"
|
||||||
|
|||||||
@@ -1,112 +0,0 @@
|
|||||||
import { TimerIcon } from '@radix-ui/react-icons';
|
|
||||||
import {
|
|
||||||
Button,
|
|
||||||
Flex,
|
|
||||||
FlexProps,
|
|
||||||
Heading,
|
|
||||||
Popover,
|
|
||||||
Text,
|
|
||||||
} from '@radix-ui/themes';
|
|
||||||
import { parseBkni } from '../core/blitzkit/parseBkni';
|
|
||||||
import {
|
|
||||||
BKNI_COLORS,
|
|
||||||
BKNI_PERCENTILES,
|
|
||||||
} from '../core/statistics/getBkniPercentile';
|
|
||||||
import strings from '../lang/en-US.json';
|
|
||||||
|
|
||||||
type BkniIndicatorProps = FlexProps & {
|
|
||||||
bkni: number;
|
|
||||||
};
|
|
||||||
|
|
||||||
export function BkniIndicator({ bkni, ...props }: BkniIndicatorProps) {
|
|
||||||
const { bkniColor, bkniFraction, bkniMetric, bkniPercentile } =
|
|
||||||
parseBkni(bkni);
|
|
||||||
|
|
||||||
return (
|
|
||||||
<Flex direction="column" align="center" {...props}>
|
|
||||||
<svg
|
|
||||||
width={180}
|
|
||||||
height={100}
|
|
||||||
style={{
|
|
||||||
transform: 'scaleX(-1)',
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<circle
|
|
||||||
cx={90}
|
|
||||||
cy={90}
|
|
||||||
r={80}
|
|
||||||
strokeLinecap="round"
|
|
||||||
fill="none"
|
|
||||||
stroke="var(--color-surface)"
|
|
||||||
strokeWidth={20}
|
|
||||||
strokeDasharray={80 * Math.PI}
|
|
||||||
strokeDashoffset={80 * Math.PI}
|
|
||||||
/>
|
|
||||||
<circle
|
|
||||||
cx={90}
|
|
||||||
cy={90}
|
|
||||||
r={80}
|
|
||||||
strokeLinecap="round"
|
|
||||||
fill="none"
|
|
||||||
stroke={`var(--${bkniColor}-indicator)`}
|
|
||||||
strokeWidth={20}
|
|
||||||
strokeDasharray={(160 - 80 * bkniFraction) * Math.PI}
|
|
||||||
strokeDashoffset={(160 - 80 * bkniFraction) * Math.PI}
|
|
||||||
/>
|
|
||||||
</svg>
|
|
||||||
|
|
||||||
<Flex direction="column" align="center" mt="-8">
|
|
||||||
<Text weight="bold" size="8">
|
|
||||||
{bkniMetric}
|
|
||||||
</Text>
|
|
||||||
|
|
||||||
<Popover.Root>
|
|
||||||
<Popover.Trigger>
|
|
||||||
<Button
|
|
||||||
style={{ position: 'relative' }}
|
|
||||||
color="gray"
|
|
||||||
size="2"
|
|
||||||
variant="ghost"
|
|
||||||
>
|
|
||||||
<TimerIcon />
|
|
||||||
{strings.common.bkni_percentile[bkniPercentile]}
|
|
||||||
</Button>
|
|
||||||
</Popover.Trigger>
|
|
||||||
|
|
||||||
<Popover.Content align="center">
|
|
||||||
<Flex
|
|
||||||
direction="column"
|
|
||||||
style={{
|
|
||||||
width: 'fit-content',
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<Flex direction="column" mb="3" align="center">
|
|
||||||
<Heading weight="bold" size="5">
|
|
||||||
BkNI
|
|
||||||
</Heading>
|
|
||||||
<Text size="2" color="gray" wrap="wrap">
|
|
||||||
A next generation performance metric
|
|
||||||
</Text>
|
|
||||||
</Flex>
|
|
||||||
|
|
||||||
{BKNI_PERCENTILES.map(({ percentile, min }, index) => (
|
|
||||||
<Button
|
|
||||||
radius="none"
|
|
||||||
color={BKNI_COLORS[percentile]}
|
|
||||||
key={percentile}
|
|
||||||
>
|
|
||||||
<Flex align="center" justify="between" width="100%" gap="2">
|
|
||||||
<Text>{strings.common.bkni_percentile[percentile]}</Text>
|
|
||||||
<Text size="1">
|
|
||||||
{min} - {BKNI_PERCENTILES[index + 1]?.min ?? 200}
|
|
||||||
</Text>
|
|
||||||
</Flex>
|
|
||||||
</Button>
|
|
||||||
))}
|
|
||||||
</Flex>
|
|
||||||
</Popover.Content>
|
|
||||||
</Popover.Root>
|
|
||||||
</Flex>
|
|
||||||
</Flex>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
@@ -0,0 +1,60 @@
|
|||||||
|
import { Flex, FlexProps, Text } from '@radix-ui/themes';
|
||||||
|
import { ReactNode } from 'react';
|
||||||
|
|
||||||
|
type SpeedometerProps = FlexProps & {
|
||||||
|
value: ReactNode;
|
||||||
|
color: string;
|
||||||
|
fill: number;
|
||||||
|
label: ReactNode;
|
||||||
|
};
|
||||||
|
|
||||||
|
export function Speedometer({
|
||||||
|
value,
|
||||||
|
color,
|
||||||
|
fill,
|
||||||
|
label,
|
||||||
|
...props
|
||||||
|
}: SpeedometerProps) {
|
||||||
|
return (
|
||||||
|
<Flex direction="column" align="center" {...props}>
|
||||||
|
<svg
|
||||||
|
width={180}
|
||||||
|
height={98}
|
||||||
|
style={{
|
||||||
|
transform: 'scaleX(-1)',
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<circle
|
||||||
|
cx={90}
|
||||||
|
cy={90}
|
||||||
|
r={80}
|
||||||
|
strokeLinecap="round"
|
||||||
|
fill="none"
|
||||||
|
stroke={`var(--${color}-3)`}
|
||||||
|
strokeWidth={16}
|
||||||
|
strokeDasharray={80 * Math.PI}
|
||||||
|
strokeDashoffset={80 * Math.PI}
|
||||||
|
/>
|
||||||
|
<circle
|
||||||
|
cx={90}
|
||||||
|
cy={90}
|
||||||
|
r={80}
|
||||||
|
strokeLinecap="round"
|
||||||
|
fill="none"
|
||||||
|
stroke={`var(--${color}-indicator)`}
|
||||||
|
strokeWidth={16}
|
||||||
|
strokeDasharray={(160 - 80 * fill) * Math.PI}
|
||||||
|
strokeDashoffset={(160 - 80 * fill) * Math.PI}
|
||||||
|
/>
|
||||||
|
</svg>
|
||||||
|
|
||||||
|
<Flex direction="column" align="center" mt="-8">
|
||||||
|
<Text weight="bold" size="8" mt="-1">
|
||||||
|
{value}
|
||||||
|
</Text>
|
||||||
|
|
||||||
|
{label}
|
||||||
|
</Flex>
|
||||||
|
</Flex>
|
||||||
|
);
|
||||||
|
}
|
||||||
Reference in new issue
Block a user