mirror of
https://github.com/blitzkit/blitzkit.git
synced 2026-10-24 12:09:51 +02:00
feat: nivo graphs
This commit is contained in:
1 parent
f2da3994e4
commit
5e6f0e5d18
13 files changed
+157
-364
No files matched your search
@@ -20,5 +20,8 @@ export default defineConfig({
|
|||||||
'../../node_modules/.prisma/client/index-browser.js',
|
'../../node_modules/.prisma/client/index-browser.js',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
ssr: {
|
||||||
|
noExternal: [/^d3.*$/, /^@nivo.*$/],
|
||||||
|
},
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
@@ -13,6 +13,7 @@
|
|||||||
"@blitzkit/core": "workspace:*",
|
"@blitzkit/core": "workspace:*",
|
||||||
"@nanostores/persistent": "^0.10.2",
|
"@nanostores/persistent": "^0.10.2",
|
||||||
"@nanostores/react": "^0.7.3",
|
"@nanostores/react": "^0.7.3",
|
||||||
|
"@nivo/bar": "^0.87.0",
|
||||||
"@radix-ui/colors": "^3.0.0",
|
"@radix-ui/colors": "^3.0.0",
|
||||||
"@radix-ui/react-icons": "^1.3.0",
|
"@radix-ui/react-icons": "^1.3.0",
|
||||||
"@radix-ui/themes": "^3.1.3",
|
"@radix-ui/themes": "^3.1.3",
|
||||||
|
|||||||
@@ -1,64 +0,0 @@
|
|||||||
import { Box, Flex, Heading, type BoxProps } from '@radix-ui/themes';
|
|
||||||
import type { ECharts, EChartsOption, SetOptionOpts } from 'echarts';
|
|
||||||
import { getInstanceByDom, init } from 'echarts';
|
|
||||||
import type { CSSProperties } from 'react';
|
|
||||||
import { useEffect, useRef } from 'react';
|
|
||||||
import { echartsTheme } from './theme';
|
|
||||||
|
|
||||||
export type EChartProps = BoxProps & {
|
|
||||||
option: EChartsOption;
|
|
||||||
style?: CSSProperties;
|
|
||||||
settings?: SetOptionOpts;
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Thank you Maneet Goyal! Heavily modified.
|
|
||||||
*
|
|
||||||
* https://dev.to/manufac/using-apache-echarts-with-react-and-typescript-353k
|
|
||||||
*/
|
|
||||||
export function EChart({
|
|
||||||
option,
|
|
||||||
settings,
|
|
||||||
title,
|
|
||||||
...props
|
|
||||||
}: EChartProps): JSX.Element {
|
|
||||||
const chartRef = useRef<HTMLDivElement>(null);
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
// Initialize chart
|
|
||||||
let chart: ECharts | undefined;
|
|
||||||
if (chartRef.current !== null) {
|
|
||||||
chart = init(chartRef.current, echartsTheme, { renderer: 'svg' });
|
|
||||||
}
|
|
||||||
|
|
||||||
// Add chart resize listener
|
|
||||||
// ResizeObserver is leading to a bit janky UX
|
|
||||||
function resizeChart() {
|
|
||||||
chart?.resize();
|
|
||||||
}
|
|
||||||
window.addEventListener('resize', resizeChart);
|
|
||||||
|
|
||||||
// Return cleanup function
|
|
||||||
return () => {
|
|
||||||
chart?.dispose();
|
|
||||||
window.removeEventListener('resize', resizeChart);
|
|
||||||
};
|
|
||||||
}, []);
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
if (chartRef.current === null) return undefined;
|
|
||||||
|
|
||||||
const chart = getInstanceByDom(chartRef.current);
|
|
||||||
|
|
||||||
if (chart === undefined) return undefined;
|
|
||||||
|
|
||||||
chart.setOption(option, settings);
|
|
||||||
}, [option, settings]);
|
|
||||||
|
|
||||||
return (
|
|
||||||
<Flex direction="column" align="center">
|
|
||||||
{title && <Heading align="center">{title}</Heading>}
|
|
||||||
<Box {...props} ref={chartRef} width="100%" />
|
|
||||||
</Flex>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
@@ -1,140 +0,0 @@
|
|||||||
import { Var } from '../../core/radix/var';
|
|
||||||
|
|
||||||
export const echartsTheme = {
|
|
||||||
seriesCnt: '3',
|
|
||||||
backgroundColor: Var('color-background'),
|
|
||||||
titleColor: '#ff0000',
|
|
||||||
subtitleColor: '#dddddd',
|
|
||||||
textColorShow: false,
|
|
||||||
textColor: '#333',
|
|
||||||
markTextColor: '#293441',
|
|
||||||
color: [
|
|
||||||
Var('accent-indicator'),
|
|
||||||
'#fc97af',
|
|
||||||
'#87f7cf',
|
|
||||||
'#f7f494',
|
|
||||||
'#72ccff',
|
|
||||||
'#f7c5a0',
|
|
||||||
'#d4a4eb',
|
|
||||||
'#d2f5a6',
|
|
||||||
'#76f2f2',
|
|
||||||
],
|
|
||||||
borderColor: '#ccc',
|
|
||||||
borderWidth: 0,
|
|
||||||
visualMapColor: ['#fc97af', '#87f7cf'],
|
|
||||||
legendTextColor: '#999999',
|
|
||||||
kColor: '#fc97af',
|
|
||||||
kColor0: 'transparent',
|
|
||||||
kBorderColor: '#fc97af',
|
|
||||||
kBorderColor0: '#87f7cf',
|
|
||||||
kBorderWidth: '2',
|
|
||||||
lineWidth: '3',
|
|
||||||
symbolSize: '0',
|
|
||||||
symbol: 'circle',
|
|
||||||
symbolBorderWidth: '4',
|
|
||||||
lineSmooth: true,
|
|
||||||
graphLineWidth: '1',
|
|
||||||
graphLineColor: '#ffffff',
|
|
||||||
mapLabelColor: '#893448',
|
|
||||||
mapLabelColorE: 'rgb(137,52,72)',
|
|
||||||
mapBorderColor: '#999999',
|
|
||||||
mapBorderColorE: '#eb8146',
|
|
||||||
mapBorderWidth: 0.5,
|
|
||||||
mapBorderWidthE: 1,
|
|
||||||
mapAreaColor: '#f3f3f3',
|
|
||||||
mapAreaColorE: 'rgba(255,178,72,1)',
|
|
||||||
axes: [
|
|
||||||
{
|
|
||||||
type: 'all',
|
|
||||||
name: '通用坐标轴',
|
|
||||||
axisLineShow: true,
|
|
||||||
axisLineColor: '#666666',
|
|
||||||
axisTickShow: false,
|
|
||||||
axisTickColor: '#333',
|
|
||||||
axisLabelShow: true,
|
|
||||||
axisLabelColor: '#aaaaaa',
|
|
||||||
splitLineShow: false,
|
|
||||||
splitLineColor: ['#e6e6e6'],
|
|
||||||
splitAreaShow: false,
|
|
||||||
splitAreaColor: ['rgba(250,250,250,0.05)', 'rgba(200,200,200,0.02)'],
|
|
||||||
},
|
|
||||||
{
|
|
||||||
type: 'category',
|
|
||||||
name: '类目坐标轴',
|
|
||||||
axisLineShow: true,
|
|
||||||
axisLineColor: '#333',
|
|
||||||
axisTickShow: true,
|
|
||||||
axisTickColor: '#333',
|
|
||||||
axisLabelShow: true,
|
|
||||||
axisLabelColor: '#333',
|
|
||||||
splitLineShow: false,
|
|
||||||
splitLineColor: ['#ccc'],
|
|
||||||
splitAreaShow: false,
|
|
||||||
splitAreaColor: ['rgba(250,250,250,0.3)', 'rgba(200,200,200,0.3)'],
|
|
||||||
},
|
|
||||||
{
|
|
||||||
type: 'value',
|
|
||||||
name: '数值坐标轴',
|
|
||||||
axisLineShow: true,
|
|
||||||
axisLineColor: '#333',
|
|
||||||
axisTickShow: true,
|
|
||||||
axisTickColor: '#333',
|
|
||||||
axisLabelShow: true,
|
|
||||||
axisLabelColor: '#333',
|
|
||||||
splitLineShow: true,
|
|
||||||
splitLineColor: ['#ccc'],
|
|
||||||
splitAreaShow: false,
|
|
||||||
splitAreaColor: ['rgba(250,250,250,0.3)', 'rgba(200,200,200,0.3)'],
|
|
||||||
},
|
|
||||||
{
|
|
||||||
type: 'log',
|
|
||||||
name: '对数坐标轴',
|
|
||||||
axisLineShow: true,
|
|
||||||
axisLineColor: '#333',
|
|
||||||
axisTickShow: true,
|
|
||||||
axisTickColor: '#333',
|
|
||||||
axisLabelShow: true,
|
|
||||||
axisLabelColor: '#333',
|
|
||||||
splitLineShow: true,
|
|
||||||
splitLineColor: ['#ccc'],
|
|
||||||
splitAreaShow: false,
|
|
||||||
splitAreaColor: ['rgba(250,250,250,0.3)', 'rgba(200,200,200,0.3)'],
|
|
||||||
},
|
|
||||||
{
|
|
||||||
type: 'time',
|
|
||||||
name: '时间坐标轴',
|
|
||||||
axisLineShow: true,
|
|
||||||
axisLineColor: '#333',
|
|
||||||
axisTickShow: true,
|
|
||||||
axisTickColor: '#333',
|
|
||||||
axisLabelShow: true,
|
|
||||||
axisLabelColor: '#333',
|
|
||||||
splitLineShow: true,
|
|
||||||
splitLineColor: ['#ccc'],
|
|
||||||
splitAreaShow: false,
|
|
||||||
splitAreaColor: ['rgba(250,250,250,0.3)', 'rgba(200,200,200,0.3)'],
|
|
||||||
},
|
|
||||||
],
|
|
||||||
axisSeperateSetting: false,
|
|
||||||
toolboxColor: '#999999',
|
|
||||||
toolboxEmphasisColor: '#666666',
|
|
||||||
tooltipAxisColor: '#cccccc',
|
|
||||||
tooltipAxisWidth: 1,
|
|
||||||
timelineLineColor: '#87f7cf',
|
|
||||||
timelineLineWidth: 1,
|
|
||||||
timelineItemColor: '#87f7cf',
|
|
||||||
timelineItemColorE: '#f7f494',
|
|
||||||
timelineCheckColor: '#fc97af',
|
|
||||||
timelineCheckBorderColor: '#fc97af',
|
|
||||||
timelineItemBorderWidth: 1,
|
|
||||||
timelineControlColor: '#87f7cf',
|
|
||||||
timelineControlBorderColor: '#87f7cf',
|
|
||||||
timelineControlBorderWidth: 0.5,
|
|
||||||
timelineLabelColor: '#87f7cf',
|
|
||||||
datazoomBackgroundColor: 'rgba(255,255,255,0)',
|
|
||||||
datazoomDataColor: 'rgba(114,204,255,1)',
|
|
||||||
datazoomFillColor: 'rgba(114,204,255,0.2)',
|
|
||||||
datazoomHandleColor: '#72ccff',
|
|
||||||
datazoomHandleWidth: '100',
|
|
||||||
datazoomLabelColor: '#333333',
|
|
||||||
};
|
|
||||||
@@ -0,0 +1,18 @@
|
|||||||
|
import {
|
||||||
|
ResponsiveBar,
|
||||||
|
type BarDatum,
|
||||||
|
type ResponsiveBarSvgProps,
|
||||||
|
} from '@nivo/bar';
|
||||||
|
import { nivoTheme } from '../../core/nivo/theme';
|
||||||
|
|
||||||
|
export function ThemedBar<RawDatum extends BarDatum>(
|
||||||
|
props: ResponsiveBarSvgProps<RawDatum>,
|
||||||
|
) {
|
||||||
|
return (
|
||||||
|
<ResponsiveBar
|
||||||
|
colors={{ scheme: 'yellow_green_blue' }}
|
||||||
|
theme={nivoTheme}
|
||||||
|
{...props}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
}
|
||||||
-57
@@ -1,57 +0,0 @@
|
|||||||
import {
|
|
||||||
TIERS,
|
|
||||||
TIER_ROMAN_NUMERALS,
|
|
||||||
fetchAverageDefinitions,
|
|
||||||
fetchTankDefinitions,
|
|
||||||
} from '@blitzkit/core';
|
|
||||||
import { TankPerformanceEphemeral } from '../../../../stores/tankPerformanceEphemeral';
|
|
||||||
import { EChart } from '../../../EChart';
|
|
||||||
|
|
||||||
const averageDefinitions = await fetchAverageDefinitions();
|
|
||||||
const tankDefinitions = await fetchTankDefinitions();
|
|
||||||
const averageDefinitionsArray = Object.entries(averageDefinitions.averages);
|
|
||||||
|
|
||||||
export function BattleDistributionAcrossTiers() {
|
|
||||||
const playerCountPeriod = TankPerformanceEphemeral.use(
|
|
||||||
(state) => state.playerCountPeriod,
|
|
||||||
);
|
|
||||||
|
|
||||||
return (
|
|
||||||
<EChart
|
|
||||||
height="25rem"
|
|
||||||
title="Battle distribution across tiers"
|
|
||||||
option={{
|
|
||||||
xAxis: {
|
|
||||||
type: 'category',
|
|
||||||
data: TIERS.map((tier) => TIER_ROMAN_NUMERALS[tier]),
|
|
||||||
},
|
|
||||||
yAxis: {
|
|
||||||
type: 'value',
|
|
||||||
},
|
|
||||||
series: [
|
|
||||||
{
|
|
||||||
data: TIERS.map((tier) => {
|
|
||||||
return averageDefinitionsArray.reduce(
|
|
||||||
(accumulator, [idString, entry]) => {
|
|
||||||
const id = Number(idString);
|
|
||||||
const tank = tankDefinitions.tanks[id];
|
|
||||||
|
|
||||||
if (tank === undefined || tank.tier !== tier) {
|
|
||||||
return accumulator;
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
|
||||||
accumulator +
|
|
||||||
entry.mu.battles * entry.samples[playerCountPeriod]
|
|
||||||
);
|
|
||||||
},
|
|
||||||
0,
|
|
||||||
);
|
|
||||||
}),
|
|
||||||
type: 'bar',
|
|
||||||
},
|
|
||||||
],
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
-65
@@ -1,65 +0,0 @@
|
|||||||
import {
|
|
||||||
TANK_CLASSES,
|
|
||||||
TIERS,
|
|
||||||
TIER_ROMAN_NUMERALS,
|
|
||||||
fetchAverageDefinitions,
|
|
||||||
fetchTankDefinitions,
|
|
||||||
} from '@blitzkit/core';
|
|
||||||
import strings from '@blitzkit/core/lang/en-US.json';
|
|
||||||
import { TankPerformanceEphemeral } from '../../../../stores/tankPerformanceEphemeral';
|
|
||||||
import { EChart } from '../../../EChart';
|
|
||||||
|
|
||||||
const averageDefinitions = await fetchAverageDefinitions();
|
|
||||||
const tankDefinitions = await fetchTankDefinitions();
|
|
||||||
const averageDefinitionsArray = Object.entries(averageDefinitions.averages);
|
|
||||||
|
|
||||||
export function BattleDistributionAcrossTiersByClass() {
|
|
||||||
const playerCountPeriod = TankPerformanceEphemeral.use(
|
|
||||||
(state) => state.playerCountPeriod,
|
|
||||||
);
|
|
||||||
|
|
||||||
return (
|
|
||||||
<EChart
|
|
||||||
height="25rem"
|
|
||||||
title="Battle distribution across tiers by class"
|
|
||||||
option={{
|
|
||||||
xAxis: {
|
|
||||||
type: 'category',
|
|
||||||
data: TIERS.map((tier) => TIER_ROMAN_NUMERALS[tier]),
|
|
||||||
},
|
|
||||||
yAxis: {
|
|
||||||
type: 'value',
|
|
||||||
},
|
|
||||||
legend: {
|
|
||||||
data: TANK_CLASSES.map((c) => strings.common.tank_class_short[c]),
|
|
||||||
},
|
|
||||||
series: TANK_CLASSES.map((c) => ({
|
|
||||||
name: strings.common.tank_class_short[c],
|
|
||||||
data: TIERS.map((tier) => {
|
|
||||||
return averageDefinitionsArray.reduce(
|
|
||||||
(accumulator, [idString, entry]) => {
|
|
||||||
const id = Number(idString);
|
|
||||||
const tank = tankDefinitions.tanks[id];
|
|
||||||
|
|
||||||
if (
|
|
||||||
tank === undefined ||
|
|
||||||
tank.tier !== tier ||
|
|
||||||
tank.class !== c
|
|
||||||
) {
|
|
||||||
return accumulator;
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
|
||||||
accumulator +
|
|
||||||
entry.mu.battles * entry.samples[playerCountPeriod]
|
|
||||||
);
|
|
||||||
},
|
|
||||||
0,
|
|
||||||
);
|
|
||||||
}),
|
|
||||||
type: 'bar',
|
|
||||||
})),
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
+87
@@ -0,0 +1,87 @@
|
|||||||
|
import {
|
||||||
|
fetchAverageDefinitions,
|
||||||
|
fetchTankDefinitions,
|
||||||
|
formatCompact,
|
||||||
|
TANK_CLASSES,
|
||||||
|
TankClass,
|
||||||
|
TIER_ROMAN_NUMERALS,
|
||||||
|
TIERS,
|
||||||
|
} from '@blitzkit/core';
|
||||||
|
import strings from '@blitzkit/core/lang/en-US.json';
|
||||||
|
import { Box, Flex, Heading } from '@radix-ui/themes';
|
||||||
|
import { useAveragesExclusionRatio } from '../../../../hooks/useAveragesExclusionRatio';
|
||||||
|
import { TankPerformanceEphemeral } from '../../../../stores/tankPerformanceEphemeral';
|
||||||
|
import { ThemedBar } from '../../../Nivo/ThemedBar';
|
||||||
|
|
||||||
|
const averageDefinitions = await fetchAverageDefinitions();
|
||||||
|
const tankDefinitions = await fetchTankDefinitions();
|
||||||
|
const averageDefinitionsArray = Object.entries(averageDefinitions.averages);
|
||||||
|
|
||||||
|
export function BattleDistributionAcrossTiers() {
|
||||||
|
const playerCountPeriod = TankPerformanceEphemeral.use(
|
||||||
|
(state) => state.playerCountPeriod,
|
||||||
|
);
|
||||||
|
const ratio = useAveragesExclusionRatio();
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Flex direction="column" gap="4">
|
||||||
|
<Heading align="center">Players by tier & class</Heading>
|
||||||
|
<Box height="25rem">
|
||||||
|
<ThemedBar
|
||||||
|
data={TIERS.map((tier) => {
|
||||||
|
const players: Record<TankClass, number> = {
|
||||||
|
0: 0,
|
||||||
|
1: 0,
|
||||||
|
2: 0,
|
||||||
|
3: 0,
|
||||||
|
};
|
||||||
|
|
||||||
|
averageDefinitionsArray.forEach(([idString, entry]) => {
|
||||||
|
const id = Number(idString);
|
||||||
|
const tank = tankDefinitions.tanks[id];
|
||||||
|
|
||||||
|
if (tank === undefined || tank.tier !== tier) return;
|
||||||
|
|
||||||
|
players[tank.class] += entry.samples[playerCountPeriod] * ratio;
|
||||||
|
});
|
||||||
|
|
||||||
|
const bar: Record<string, number | string> = {
|
||||||
|
tier: TIER_ROMAN_NUMERALS[tier],
|
||||||
|
};
|
||||||
|
|
||||||
|
TANK_CLASSES.forEach((c) => {
|
||||||
|
bar[strings.common.tank_class_short[c]] = players[c];
|
||||||
|
});
|
||||||
|
|
||||||
|
return bar;
|
||||||
|
})}
|
||||||
|
keys={TANK_CLASSES.map((c) => strings.common.tank_class_short[c])}
|
||||||
|
indexBy="tier"
|
||||||
|
margin={{ right: 90, bottom: 50, left: 90 }}
|
||||||
|
axisBottom={{
|
||||||
|
legend: 'Tier',
|
||||||
|
legendPosition: 'middle',
|
||||||
|
legendOffset: 32,
|
||||||
|
}}
|
||||||
|
axisLeft={{
|
||||||
|
legend: 'Players',
|
||||||
|
legendPosition: 'middle',
|
||||||
|
legendOffset: -60,
|
||||||
|
format: formatCompact,
|
||||||
|
}}
|
||||||
|
legends={[
|
||||||
|
{
|
||||||
|
dataFrom: 'keys',
|
||||||
|
anchor: 'bottom-right',
|
||||||
|
direction: 'column',
|
||||||
|
translateX: 120,
|
||||||
|
itemWidth: 100,
|
||||||
|
itemHeight: 20,
|
||||||
|
},
|
||||||
|
]}
|
||||||
|
valueFormat={formatCompact}
|
||||||
|
/>
|
||||||
|
</Box>
|
||||||
|
</Flex>
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -1,13 +1,11 @@
|
|||||||
import { Flex } from '@radix-ui/themes';
|
import { Flex } from '@radix-ui/themes';
|
||||||
import { BattleDistributionAcrossTiers } from './components/BattleDistributionAcrossTiers';
|
import { BattleDistributionAcrossTiers } from './components/BattleDistributionAcrossTiersNivo';
|
||||||
import { BattleDistributionAcrossTiersByClass } from './components/BattleDistributionAcrossTiersByClass';
|
|
||||||
|
|
||||||
export function Charts() {
|
export function Charts() {
|
||||||
return (
|
return (
|
||||||
<Flex justify="center">
|
<Flex justify="center">
|
||||||
<Flex direction="column" maxWidth="45rem" flexGrow="1" py="6" gap="9">
|
<Flex direction="column" maxWidth="45rem" flexGrow="1" py="6" gap="9">
|
||||||
<BattleDistributionAcrossTiers />
|
<BattleDistributionAcrossTiers />
|
||||||
<BattleDistributionAcrossTiersByClass />
|
|
||||||
</Flex>
|
</Flex>
|
||||||
</Flex>
|
</Flex>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import { Flex, Select, Text } from '@radix-ui/themes';
|
import { Flex, Select, Text } from '@radix-ui/themes';
|
||||||
import {
|
import {
|
||||||
PlayerCountPeriod,
|
|
||||||
TankPerformanceEphemeral,
|
TankPerformanceEphemeral,
|
||||||
|
type PlayerCountPeriod,
|
||||||
} from '../../stores/tankPerformanceEphemeral';
|
} from '../../stores/tankPerformanceEphemeral';
|
||||||
|
|
||||||
export function PlayerCountControl() {
|
export function PlayerCountControl() {
|
||||||
@@ -23,24 +23,13 @@ export function PlayerCountControl() {
|
|||||||
>
|
>
|
||||||
<Select.Trigger />
|
<Select.Trigger />
|
||||||
<Select.Content>
|
<Select.Content>
|
||||||
<Select.Item value={PlayerCountPeriod.Past120Days}>
|
<Select.Item value="total">Total</Select.Item>
|
||||||
Past 120 days
|
<Select.Item value="d_120">Past 120 days</Select.Item>
|
||||||
</Select.Item>
|
<Select.Item value="d_90">Past 90 days</Select.Item>
|
||||||
<Select.Item value={PlayerCountPeriod.Past90Days}>
|
<Select.Item value="d_60">Past 60 days</Select.Item>
|
||||||
Past 90 days
|
<Select.Item value="d_30">This month</Select.Item>
|
||||||
</Select.Item>
|
<Select.Item value="d_7">This week</Select.Item>
|
||||||
<Select.Item value={PlayerCountPeriod.Past60Days}>
|
<Select.Item value="d_1">Yesterday</Select.Item>
|
||||||
Past 60 days
|
|
||||||
</Select.Item>
|
|
||||||
<Select.Item value={PlayerCountPeriod.ThisMonth}>
|
|
||||||
This month
|
|
||||||
</Select.Item>
|
|
||||||
<Select.Item value={PlayerCountPeriod.ThisWeek}>
|
|
||||||
This week
|
|
||||||
</Select.Item>
|
|
||||||
<Select.Item value={PlayerCountPeriod.Yesterday}>
|
|
||||||
Yesterday
|
|
||||||
</Select.Item>
|
|
||||||
</Select.Content>
|
</Select.Content>
|
||||||
</Select.Root>
|
</Select.Root>
|
||||||
</Flex>
|
</Flex>
|
||||||
|
|||||||
@@ -1,10 +1,39 @@
|
|||||||
import type { Theme } from '@nivo/core';
|
import type { Theme } from '@nivo/core';
|
||||||
|
import { Var } from '../radix/var';
|
||||||
|
|
||||||
export const nivoTheme = {
|
export const nivoTheme = {
|
||||||
theme: {
|
background: Var('color-background'),
|
||||||
text: { fill: 'var(--gray-11)' },
|
text: {
|
||||||
grid: { line: { stroke: 'var(--gray-3)' } },
|
fill: Var('gray-contrast'),
|
||||||
} satisfies Theme,
|
fontFamily: 'inherit',
|
||||||
borderRadius: 2,
|
},
|
||||||
innerPadding: 1,
|
axis: {
|
||||||
};
|
ticks: {
|
||||||
|
text: {
|
||||||
|
fill: Var('gray-indicator'),
|
||||||
|
},
|
||||||
|
line: {
|
||||||
|
stroke: Var('gray-indicator'),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
grid: {
|
||||||
|
line: {
|
||||||
|
stroke: Var('gray-indicator'),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
tooltip: {
|
||||||
|
container: {
|
||||||
|
background: Var('color-overlay'),
|
||||||
|
backdropFilter: 'blur(4rem)',
|
||||||
|
WebkitBackdropFilter: 'blur(4rem)',
|
||||||
|
color: Var('gray-contrast'),
|
||||||
|
borderRadius: Var('radius-2'),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
labels: {
|
||||||
|
text: {
|
||||||
|
fill: Var('gray-1'),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
} satisfies Theme;
|
||||||
@@ -1,3 +1,4 @@
|
|||||||
|
import type { Samples } from '@blitzkit/core';
|
||||||
import { create } from 'zustand';
|
import { create } from 'zustand';
|
||||||
import { createContextualStore } from '../core/zustand/createContextualStore';
|
import { createContextualStore } from '../core/zustand/createContextualStore';
|
||||||
|
|
||||||
@@ -11,18 +12,11 @@ export enum TankPerformanceMode {
|
|||||||
Charts,
|
Charts,
|
||||||
}
|
}
|
||||||
|
|
||||||
export enum PlayerCountPeriod {
|
export type PlayerCountPeriod = keyof Samples;
|
||||||
Past120Days = 'd_120',
|
|
||||||
Past90Days = 'd_90',
|
|
||||||
Past60Days = 'd_60',
|
|
||||||
ThisMonth = 'd_30',
|
|
||||||
ThisWeek = 'd_7',
|
|
||||||
Yesterday = 'd_1',
|
|
||||||
}
|
|
||||||
|
|
||||||
export const TankPerformanceEphemeral = createContextualStore(() =>
|
export const TankPerformanceEphemeral = createContextualStore(() =>
|
||||||
create<TankPerformanceEphemeral>()(() => ({
|
create<TankPerformanceEphemeral>()(() => ({
|
||||||
playerCountPeriod: PlayerCountPeriod.ThisMonth,
|
playerCountPeriod: 'd_30',
|
||||||
mode: TankPerformanceMode.Charts,
|
mode: TankPerformanceMode.Charts,
|
||||||
})),
|
})),
|
||||||
);
|
);
|
||||||
Reference in new issue
Block a user