mirror of
https://github.com/blitzkit/blitzkit.git
synced 2026-10-21 18:57:03 +02:00
feat: incoporate meta section in hero
This commit is contained in:
1 parent
e0ab00386c
commit
37a50f8bc1
3 files changed
+139
-169
No files matched your search
@@ -1,17 +1,16 @@
|
||||
import { ChevronLeftIcon, MixIcon } from '@radix-ui/react-icons';
|
||||
import { Box, Button, Flex, Heading, Text } from '@radix-ui/themes';
|
||||
import { Box, Button, Flex, Heading } from '@radix-ui/themes';
|
||||
import Link from 'next/link';
|
||||
import { Suspense, use, useEffect, useMemo, useRef, useState } from 'react';
|
||||
import { classIcons } from '../../../../../../components/ClassIcon';
|
||||
import { ScienceIcon } from '../../../../../../components/ScienceIcon';
|
||||
import { ThicknessRange } from '../../../../../../components/StaticArmor';
|
||||
import { resolveNearPenetration } from '../../../../../../core/blitz/resolveNearPenetration';
|
||||
import { tankDefinitions } from '../../../../../../core/blitzkit/tankDefinitions';
|
||||
import { TIER_ROMAN_NUMERALS } from '../../../../../../core/blitzkit/tankDefinitions/constants';
|
||||
import { useFullScreen } from '../../../../../../hooks/useFullScreen';
|
||||
import strings from '../../../../../../lang/en-US.json';
|
||||
import * as App from '../../../../../../stores/app';
|
||||
import * as Duel from '../../../../../../stores/duel';
|
||||
import { Options } from './components/Options';
|
||||
import { MetaSection } from './MetaSection';
|
||||
import { TankSandbox } from './TankSandbox';
|
||||
import { TankSandboxLoader } from './TankSandboxLoader';
|
||||
|
||||
@@ -20,6 +19,7 @@ interface HeroSectionProps {
|
||||
}
|
||||
|
||||
export function HeroSection({ id }: HeroSectionProps) {
|
||||
const developerMode = App.useDeferred(false, (state) => state.developerMode);
|
||||
const canvas = useRef<HTMLCanvasElement>(null);
|
||||
const protagonist = Duel.use((state) => state.protagonist.tank);
|
||||
const antagonist = Duel.use((state) => state.antagonist.tank);
|
||||
@@ -38,6 +38,7 @@ export function HeroSection({ id }: HeroSectionProps) {
|
||||
: undefined;
|
||||
const awaitedTankDefinitions = use(tankDefinitions);
|
||||
const tank = awaitedTankDefinitions[id];
|
||||
const ClassIcon = classIcons[tank.class];
|
||||
const thicknessRange = useMemo(() => {
|
||||
const entries = Object.values(awaitedTankDefinitions);
|
||||
const filtered = entries.filter((thisTank) => thisTank.tier === tank.tier);
|
||||
@@ -66,12 +67,13 @@ export function HeroSection({ id }: HeroSectionProps) {
|
||||
pt={{ initial: '6', md: '0' }}
|
||||
style={{ background: 'var(--color-surface)', position: 'relative' }}
|
||||
height={{ initial: 'calc(75vh - 4rem)', md: undefined }}
|
||||
minHeight="30rem"
|
||||
minHeight={{ initial: '30rem' }}
|
||||
gap="4"
|
||||
>
|
||||
<Flex
|
||||
direction={{ initial: 'column', md: 'row' }}
|
||||
style={{ maxWidth: 1600, flex: 1 }}
|
||||
style={{ maxWidth: '140rem', flex: 1 }}
|
||||
gap={{ initial: '0', md: '6' }}
|
||||
>
|
||||
<Flex
|
||||
align="center"
|
||||
@@ -80,51 +82,14 @@ export function HeroSection({ id }: HeroSectionProps) {
|
||||
flexBasis="0"
|
||||
>
|
||||
<Flex
|
||||
gap={{ initial: '2', md: '4' }}
|
||||
gap="2"
|
||||
direction="column"
|
||||
ml={{ initial: '0', md: '8' }}
|
||||
align={{ initial: 'center', md: 'start' }}
|
||||
justify="center"
|
||||
style={{
|
||||
height: '100%',
|
||||
userSelect: 'none',
|
||||
}}
|
||||
style={{ height: '100%', userSelect: 'none' }}
|
||||
>
|
||||
<Flex gap="4">
|
||||
<Heading size={{ initial: '8', md: '9' }} color={treeColor}>
|
||||
<Icon style={{ width: '0.75em', height: '0.75em' }} />{' '}
|
||||
</Heading>
|
||||
<Heading
|
||||
size={{ initial: '8', md: '9' }}
|
||||
align={{ initial: 'center', md: 'left' }}
|
||||
color={treeColor}
|
||||
>
|
||||
{protagonist.name}
|
||||
</Heading>
|
||||
</Flex>
|
||||
|
||||
<Flex direction="column" ml={{ initial: '0', md: '9' }}>
|
||||
{tank.testing && (
|
||||
<Text color="red">
|
||||
<Flex align="center" gap="1">
|
||||
Tank in testing
|
||||
<ScienceIcon width="1em" height="1em" />
|
||||
</Flex>
|
||||
</Text>
|
||||
)}
|
||||
|
||||
<Text color="gray">
|
||||
Tier {TIER_ROMAN_NUMERALS[protagonist.tier]}{' '}
|
||||
{
|
||||
(strings.common.nations_adjectives as Record<string, string>)[
|
||||
protagonist.nation
|
||||
]
|
||||
}{' '}
|
||||
{strings.common.tank_class_short[protagonist.class]}
|
||||
</Text>
|
||||
</Flex>
|
||||
|
||||
<Flex gap="4" ml={{ initial: '0', md: '9' }} mt="-1">
|
||||
<Link href="/tools/tankopedia">
|
||||
<Button variant="ghost" size="1" ml="-1">
|
||||
<ChevronLeftIcon />
|
||||
@@ -139,6 +104,22 @@ export function HeroSection({ id }: HeroSectionProps) {
|
||||
</Button>
|
||||
</Link>
|
||||
</Flex>
|
||||
|
||||
<Flex gap="4">
|
||||
<Heading size={{ initial: '8', sm: '9' }} color={treeColor}>
|
||||
<Icon style={{ width: '0.75em', height: '0.75em' }} />{' '}
|
||||
</Heading>
|
||||
|
||||
<Heading
|
||||
size={{ initial: '8', sm: '9' }}
|
||||
align={{ initial: 'center', md: 'left' }}
|
||||
color={treeColor}
|
||||
>
|
||||
{protagonist.name}
|
||||
</Heading>
|
||||
</Flex>
|
||||
|
||||
<MetaSection naked />
|
||||
</Flex>
|
||||
</Flex>
|
||||
|
||||
|
||||
@@ -6,7 +6,7 @@ type ListingProps = TextProps & {
|
||||
|
||||
export function Listing({ label, ...props }: ListingProps) {
|
||||
return (
|
||||
<Flex justify="between" gap="4">
|
||||
<Flex minWidth="15rem" justify="between" gap="4">
|
||||
<Text color="gray">{label}</Text>
|
||||
<Text {...props} />
|
||||
</Flex>
|
||||
|
||||
@@ -1,138 +1,127 @@
|
||||
import { Code, Flex, Heading } from '@radix-ui/themes';
|
||||
import { Code, Flex } from '@radix-ui/themes';
|
||||
import { classIcons } from '../../../../../../../components/ClassIcon';
|
||||
import { asset } from '../../../../../../../core/blitzkit/asset';
|
||||
import { tankIcon } from '../../../../../../../core/blitzkit/tankIcon';
|
||||
import strings from '../../../../../../../lang/en-US.json';
|
||||
import * as App from '../../../../../../../stores/app';
|
||||
import * as Duel from '../../../../../../../stores/duel';
|
||||
import { Listing } from './components/Listing';
|
||||
|
||||
export function MetaSection() {
|
||||
interface MetaSectionProps {
|
||||
naked?: boolean;
|
||||
}
|
||||
|
||||
export function MetaSection({ naked }: MetaSectionProps) {
|
||||
const developerMode = App.useDeferred(false, (state) => state.developerMode);
|
||||
const tank = Duel.use((state) => state.protagonist.tank);
|
||||
const ClassIcon = classIcons[tank.class];
|
||||
const content = (
|
||||
<Flex
|
||||
wrap={{ initial: 'nowrap', sm: 'wrap' }}
|
||||
direction={{ initial: 'column', sm: 'row' }}
|
||||
align={{ initial: 'center', sm: 'start' }}
|
||||
gapX="4"
|
||||
width="100%"
|
||||
mt={{ initial: '4', md: '6' }}
|
||||
display={naked ? { initial: 'none', sm: 'flex' } : undefined}
|
||||
maxWidth="calc(2 * 15rem + var(--space-4))"
|
||||
>
|
||||
{tank.nameFull && <Listing label="Full-name">{tank.nameFull}</Listing>}
|
||||
<Listing label="Nation">
|
||||
<Flex align="center" gap="1">
|
||||
<img
|
||||
style={{ width: '1em', height: '1em' }}
|
||||
alt={tank.nation}
|
||||
src={asset(`flags/circle/${tank.nation}.webp`)}
|
||||
/>
|
||||
{
|
||||
strings.common.nations[
|
||||
tank.nation as keyof typeof strings.common.nations
|
||||
]
|
||||
}
|
||||
</Flex>
|
||||
</Listing>
|
||||
<Listing label="Tier">{tank.tier}</Listing>
|
||||
<Listing label="Class">
|
||||
<Flex align="center" gap="1">
|
||||
<ClassIcon width="1em" height="1em" />
|
||||
{strings.common.tank_class_short[tank.class]}
|
||||
</Flex>
|
||||
</Listing>
|
||||
<Listing
|
||||
label="Type"
|
||||
color={
|
||||
tank.treeType === 'collector'
|
||||
? 'blue'
|
||||
: tank.treeType === 'premium'
|
||||
? 'amber'
|
||||
: undefined
|
||||
}
|
||||
>
|
||||
{strings.common.tree_type[tank.treeType]}
|
||||
</Listing>
|
||||
{developerMode && (
|
||||
<Listing label="DEV: ID">
|
||||
<Code>{tank.id}</Code>
|
||||
</Listing>
|
||||
)}
|
||||
{tank.treeType === 'premium' && (
|
||||
<Listing label="Purchase price">
|
||||
<Flex align="center" gap="1">
|
||||
{tank.price.value / 400}
|
||||
<img
|
||||
style={{ width: '1em', height: '1em' }}
|
||||
alt="gold"
|
||||
src={asset('icons/currencies/gold.webp')}
|
||||
/>
|
||||
</Flex>
|
||||
</Listing>
|
||||
)}
|
||||
<Listing
|
||||
label={`${tank.treeType === 'researchable' ? 'Purchase' : 'Restoration'} price`}
|
||||
>
|
||||
<Flex align="center" gap="1">
|
||||
{tank.price.value.toLocaleString()}
|
||||
<img
|
||||
style={{ width: '1em', height: '1em' }}
|
||||
alt={tank.price.type}
|
||||
src={asset(
|
||||
`icons/currencies/${tank.price.type === 'gold' ? 'gold' : 'silver'}.webp`,
|
||||
)}
|
||||
/>
|
||||
</Flex>
|
||||
</Listing>
|
||||
<Listing label="Sale price">
|
||||
<Flex align="center" gap="1">
|
||||
{(tank.price.value / 2).toLocaleString()}
|
||||
<img
|
||||
style={{ width: '1em', height: '1em' }}
|
||||
alt={tank.price.type}
|
||||
src={asset(
|
||||
`icons/currencies/${tank.price.type === 'gold' ? 'gold' : 'silver'}.webp`,
|
||||
)}
|
||||
/>
|
||||
</Flex>
|
||||
</Listing>
|
||||
{tank.xp && (
|
||||
<Listing label="Research XP">
|
||||
<Flex align="center" gap="1">
|
||||
{tank.xp.toLocaleString()}
|
||||
<img
|
||||
style={{ width: '1em', height: '1em' }}
|
||||
alt="xp"
|
||||
src={asset('icons/currencies/xp.webp')}
|
||||
/>
|
||||
</Flex>
|
||||
</Listing>
|
||||
)}
|
||||
</Flex>
|
||||
);
|
||||
|
||||
if (naked) return content;
|
||||
|
||||
return (
|
||||
<Flex justify="center" align="center" px="4">
|
||||
<Flex
|
||||
align="center"
|
||||
justify="center"
|
||||
gap="8"
|
||||
direction={{ initial: 'column', sm: 'row' }}
|
||||
>
|
||||
<img alt="Economics" src={tankIcon(tank.id)} />
|
||||
|
||||
<Flex
|
||||
gap="6"
|
||||
justify="center"
|
||||
direction={{ initial: 'column', sm: 'row' }}
|
||||
>
|
||||
<Flex direction="column" minWidth="15rem">
|
||||
<Heading mb="2" align={{ initial: 'center', sm: 'left' }}>
|
||||
Overview
|
||||
</Heading>
|
||||
|
||||
{tank.nameFull && (
|
||||
<Listing label="Full-name">{tank.nameFull}</Listing>
|
||||
)}
|
||||
<Listing label="Nation">
|
||||
<Flex align="center" gap="1">
|
||||
<img
|
||||
style={{ width: '1em', height: '1em' }}
|
||||
alt={tank.nation}
|
||||
src={asset(`flags/circle/${tank.nation}.webp`)}
|
||||
/>
|
||||
{
|
||||
strings.common.nations[
|
||||
tank.nation as keyof typeof strings.common.nations
|
||||
]
|
||||
}
|
||||
</Flex>
|
||||
</Listing>
|
||||
<Listing label="Tier">{tank.tier}</Listing>
|
||||
<Listing label="Class">
|
||||
<Flex align="center" gap="1">
|
||||
<ClassIcon width="1em" height="1em" />
|
||||
{strings.common.tank_class_short[tank.class]}
|
||||
</Flex>
|
||||
</Listing>
|
||||
<Listing
|
||||
label="Type"
|
||||
color={
|
||||
tank.treeType === 'collector'
|
||||
? 'blue'
|
||||
: tank.treeType === 'premium'
|
||||
? 'amber'
|
||||
: undefined
|
||||
}
|
||||
>
|
||||
{strings.common.tree_type[tank.treeType]}
|
||||
</Listing>
|
||||
{developerMode && (
|
||||
<Listing label="DEV: ID">
|
||||
<Code>{tank.id}</Code>
|
||||
</Listing>
|
||||
)}
|
||||
</Flex>
|
||||
|
||||
<Flex direction="column" minWidth="12rem">
|
||||
<Heading mb="2" align={{ initial: 'center', sm: 'left' }}>
|
||||
Economics
|
||||
</Heading>
|
||||
|
||||
{tank.treeType === 'premium' && (
|
||||
<Listing label="Purchase price">
|
||||
<Flex align="center" gap="1">
|
||||
{tank.price.value / 400}
|
||||
<img
|
||||
style={{ width: '1em', height: '1em' }}
|
||||
alt="gold"
|
||||
src={asset('icons/currencies/gold.webp')}
|
||||
/>
|
||||
</Flex>
|
||||
</Listing>
|
||||
)}
|
||||
<Listing
|
||||
label={`${tank.treeType === 'researchable' ? 'Purchase' : 'Restoration'} price`}
|
||||
>
|
||||
<Flex align="center" gap="1">
|
||||
{tank.price.value.toLocaleString()}
|
||||
<img
|
||||
style={{ width: '1em', height: '1em' }}
|
||||
alt={tank.price.type}
|
||||
src={asset(
|
||||
`icons/currencies/${tank.price.type === 'gold' ? 'gold' : 'silver'}.webp`,
|
||||
)}
|
||||
/>
|
||||
</Flex>
|
||||
</Listing>
|
||||
<Listing label="Sale price">
|
||||
<Flex align="center" gap="1">
|
||||
{(tank.price.value / 2).toLocaleString()}
|
||||
<img
|
||||
style={{ width: '1em', height: '1em' }}
|
||||
alt={tank.price.type}
|
||||
src={asset(
|
||||
`icons/currencies/${tank.price.type === 'gold' ? 'gold' : 'silver'}.webp`,
|
||||
)}
|
||||
/>
|
||||
</Flex>
|
||||
</Listing>
|
||||
{tank.xp && (
|
||||
<Listing label="Research XP">
|
||||
<Flex align="center" gap="1">
|
||||
{tank.xp.toLocaleString()}
|
||||
<img
|
||||
style={{ width: '1em', height: '1em' }}
|
||||
alt="xp"
|
||||
src={asset('icons/currencies/xp.webp')}
|
||||
/>
|
||||
</Flex>
|
||||
</Listing>
|
||||
)}
|
||||
</Flex>
|
||||
</Flex>
|
||||
</Flex>
|
||||
<Flex justify="center" display={{ initial: 'flex', sm: 'none' }}>
|
||||
{content}
|
||||
</Flex>
|
||||
);
|
||||
}
|
||||
Reference in new issue
Block a user