feat: better layout

This commit is contained in:
TrèsAbhi committed 2024-05-04 16:19:27 -05:00
1 parent 299ccef579
commit ab510361b5
6 files changed
+463 -435

No files matched your search

@@ -32,7 +32,7 @@ export function Info({
style={{ width: '100%', paddingLeft: indent ? 24 : 0 }} style={{ width: '100%', paddingLeft: indent ? 24 : 0 }}
gap="4" gap="4"
> >
<Text color={highlight ? 'amber' : undefined}> <Text color={highlight ? 'amber' : 'gray'}>
{name} {name}
{unit != undefined && ( {unit != undefined && (
<> <>
@@ -25,12 +25,14 @@ import {
} from '../../../../../../core/blitzkit/tankDefinitions/constants'; } from '../../../../../../core/blitzkit/tankDefinitions/constants';
import { useEquipment } from '../../../../../../hooks/useEquipment'; import { useEquipment } from '../../../../../../hooks/useEquipment';
import { useFullScreen } from '../../../../../../hooks/useFullScreen'; import { useFullScreen } from '../../../../../../hooks/useFullScreen';
import { useWideFormat } from '../../../../../../hooks/useWideFormat';
import { mutateDuel, useDuel } from '../../../../../../stores/duel'; import { mutateDuel, useDuel } from '../../../../../../stores/duel';
import { useTankopediaTemporary } from '../../../../../../stores/tankopedia'; import { useTankopediaTemporary } from '../../../../../../stores/tankopedia';
import { Info } from './components/Info'; import { Info } from './components/Info';
import { InfoWithDelta } from './components/InfoWithDelta'; import { InfoWithDelta } from './components/InfoWithDelta';
export function Characteristics() { export function Characteristics() {
const wideFormat = useWideFormat(960);
const awaitedEquipmentDefinitions = use(equipmentDefinitions); const awaitedEquipmentDefinitions = use(equipmentDefinitions);
const awaitedModelDefinitions = use(modelDefinitions); const awaitedModelDefinitions = use(modelDefinitions);
const awaitedProvisionDefinitions = use(provisionDefinitions); const awaitedProvisionDefinitions = use(provisionDefinitions);
@@ -128,445 +130,450 @@ export function Characteristics() {
if (isFullScreen) return null; if (isFullScreen) return null;
return ( return (
<Flex direction="column" gap="8"> <Flex direction={wideFormat ? 'row' : 'column'} gap="8" wrap="wrap">
<Flex direction="column" gap="2"> <Flex direction="column" gap="8" style={{ flex: 1 }}>
<Flex align="center" gap="4"> <Flex direction="column" gap="2">
<Heading size="5">Fire</Heading> <Flex align="center" gap="4">
<Heading size="5">Fire</Heading>
<Flex> <Flex>
{gun.shells.map((thisShell, shellIndex) => ( {gun.shells.map((thisShell, shellIndex) => (
<IconButton <IconButton
color={thisShell.id === shell.id ? undefined : 'gray'} color={thisShell.id === shell.id ? undefined : 'gray'}
variant="soft" variant="soft"
key={thisShell.id} key={thisShell.id}
style={{ style={{
borderTopLeftRadius: shellIndex === 0 ? undefined : 0, borderTopLeftRadius: shellIndex === 0 ? undefined : 0,
borderBottomLeftRadius: shellIndex === 0 ? undefined : 0, borderBottomLeftRadius: shellIndex === 0 ? undefined : 0,
borderTopRightRadius: borderTopRightRadius:
shellIndex === gun.shells.length - 1 ? undefined : 0, shellIndex === gun.shells.length - 1 ? undefined : 0,
borderBottomRightRadius: borderBottomRightRadius:
shellIndex === gun.shells.length - 1 ? undefined : 0, shellIndex === gun.shells.length - 1 ? undefined : 0,
marginLeft: shellIndex === 0 ? 0 : -1, marginLeft: shellIndex === 0 ? 0 : -1,
}} }}
onClick={() => { onClick={() => {
mutateDuel((draft) => { mutateDuel((draft) => {
draft.protagonist!.shell = thisShell; draft.protagonist!.shell = thisShell;
}); });
}} }}
>
<img
width={16}
height={16}
src={asset(`icons/shells/${thisShell.icon}.webp`)}
/>
</IconButton>
))}
</Flex>
</Flex>
<Info name="Gun type">{GUN_TYPE_NAMES[gun.type]}</Info>
<InfoWithDelta name="DPM" decimals={0} unit="hp / min">
{stats.dpm}
</InfoWithDelta>
{gun.type === 'autoReloader' && (
<>
<InfoWithDelta decimals={0} indent name="Maximum" unit="hp / min">
{stats.dpmMaximum!}
</InfoWithDelta>
<InfoWithDelta
decimals={0}
indent
name="Effective at 60s"
unit="hp / min"
>
{stats.dpmEffective!}
</InfoWithDelta>
<Info
indent
name={
<a
target="_blank"
href="https://tresabhi.github.io/blitzkit/guide/dpm.html"
> >
What's the difference? <img
</a> width={16}
} height={16}
/> src={asset(`icons/shells/${thisShell.icon}.webp`)}
</> />
)} </IconButton>
{gun.type !== 'regular' && ( ))}
<InfoWithDelta name="Shells">{stats.shells}</InfoWithDelta> </Flex>
)} </Flex>
{gun.type === 'autoReloader' ? ( <Info name="Gun type">{GUN_TYPE_NAMES[gun.type]}</Info>
<> <InfoWithDelta name="DPM" decimals={0} unit="hp / min">
<Info indent name="Most optimal"> {stats.dpm}
{stats.mostOptimalShellIndex} </InfoWithDelta>
</Info> {gun.type === 'autoReloader' && (
<Info name="Shell reloads" unit="s" /> <>
{stats.shellReloads!.map((reload, index) => ( <InfoWithDelta decimals={0} indent name="Maximum" unit="hp / min">
<InfoWithDelta {stats.dpmMaximum!}
key={index}
indent
name={`Shell ${index + 1}`}
decimals={2}
deltaType="lowerIsBetter"
>
{reload}
</InfoWithDelta> </InfoWithDelta>
))} <InfoWithDelta
</> decimals={0}
) : ( indent
<InfoWithDelta name="Effective at 60s"
decimals={2} unit="hp / min"
name="Reload" >
unit="s" {stats.dpmEffective!}
deltaType="lowerIsBetter" </InfoWithDelta>
> <Info
{stats.shellReload!} indent
</InfoWithDelta> name={
)} <a
{(gun.type === 'autoLoader' || gun.type === 'autoReloader') && ( target="_blank"
<InfoWithDelta href="https://tresabhi.github.io/blitzkit/guide/dpm.html"
indent >
decimals={2} What's the difference?
name="Intra-clip" </a>
unit="s" }
deltaType="lowerIsBetter" />
> </>
{stats.intraClip!} )}
</InfoWithDelta> {gun.type !== 'regular' && (
)} <InfoWithDelta name="Shells">{stats.shells}</InfoWithDelta>
<InfoWithDelta decimals={0} name="Penetration" unit="mm"> )}
{stats.penetration} {gun.type === 'autoReloader' ? (
</InfoWithDelta> <>
{typeof shell.penetration !== 'number' && ( <Info indent name="Most optimal">
<> {stats.mostOptimalShellIndex}
</Info>
<Info name="Shell reloads" unit="s" />
{stats.shellReloads!.map((reload, index) => (
<InfoWithDelta
key={index}
indent
name={`Shell ${index + 1}`}
decimals={2}
deltaType="lowerIsBetter"
>
{reload}
</InfoWithDelta>
))}
</>
) : (
<InfoWithDelta
decimals={2}
name="Reload"
unit="s"
deltaType="lowerIsBetter"
>
{stats.shellReload!}
</InfoWithDelta>
)}
{(gun.type === 'autoLoader' || gun.type === 'autoReloader') && (
<InfoWithDelta <InfoWithDelta
indent indent
decimals={0} decimals={2}
name={`At ${penetrationDistance}m`} name="Intra-clip"
unit="s"
deltaType="lowerIsBetter"
> >
{lerp( {stats.intraClip!}
shell.penetration[0],
shell.penetration[1],
(penetrationDistance * penetrationLossOverDistanceCoefficient) /
500,
) * penetrationCoefficient}
</InfoWithDelta> </InfoWithDelta>
<Flex align="center" gap="2" style={{ paddingLeft: 24 }}> )}
<Text>Distance</Text> <InfoWithDelta decimals={0} name="Penetration" unit="mm">
<Slider {stats.penetration}
key={penetrationDistance}
min={0}
max={500}
style={{ flex: 1 }}
defaultValue={[penetrationDistance]}
onValueChange={debounce(([value]) => {
setPenetrationDistance(value);
}, 500)}
/>
<TextField.Root
style={{ width: 64 }}
ref={penetrationDistanceInput}
defaultValue={penetrationDistance}
onBlur={() => {
setPenetrationDistance(
Math.min(
parseInt(penetrationDistanceInput.current!.value),
500,
),
);
}}
onKeyDown={(event) => {
if (event.key === 'Enter') {
event.currentTarget.blur();
}
}}
>
<TextField.Slot side="right">m</TextField.Slot>
</TextField.Root>
</Flex>
</>
)}
<InfoWithDelta name="Damage" unit="hp" decimals={0}>
{stats.damage}
</InfoWithDelta>
<InfoWithDelta name="Module damage" unit="hp" decimals={0}>
{stats.moduleDamage}
</InfoWithDelta>
{isExplosive(shell.type) && (
<InfoWithDelta name="Splash radius" unit="m" decimals={0}>
{stats.explosionRadius!}
</InfoWithDelta> </InfoWithDelta>
)} {typeof shell.penetration !== 'number' && (
<InfoWithDelta name="Caliber" decimals={0} unit="mm"> <>
{stats.caliber}
</InfoWithDelta>
<InfoWithDelta name="Normalization" decimals={0} unit="°">
{stats.shellNormalization}
</InfoWithDelta>
{shell.type !== 'he' && (
<InfoWithDelta
name="Ricochet"
decimals={0}
deltaType="lowerIsBetter"
unit="°"
>
{stats.shellRicochet!}
</InfoWithDelta>
)}
<InfoWithDelta name="Shell velocity" unit="m/s" decimals={0}>
{stats.shellVelocity}
</InfoWithDelta>
<InfoWithDelta
decimals={2}
name="Aim time"
unit="s"
deltaType="lowerIsBetter"
>
{stats.aimTime}
</InfoWithDelta>
<Info name="Dispersion at 100m" />
<InfoWithDelta
decimals={3}
indent
name="Still"
unit="m"
deltaType="lowerIsBetter"
>
{stats.dispersion}
</InfoWithDelta>
<InfoWithDelta
prefix="+ "
decimals={3}
indent
name="Moving"
deltaType="lowerIsBetter"
>
{stats.dispersionMoving}
</InfoWithDelta>
<InfoWithDelta
decimals={3}
prefix="+ "
indent
name="Hull traversing"
deltaType="lowerIsBetter"
>
{stats.dispersionHullTraversing}
</InfoWithDelta>
<InfoWithDelta
decimals={3}
prefix="+ "
indent
name="Turret traversing"
deltaType="lowerIsBetter"
>
{stats.dispersionTurretTraversing}
</InfoWithDelta>
<InfoWithDelta
decimals={3}
prefix="+ "
indent
name="After shooting"
deltaType="lowerIsBetter"
>
{stats.dispersionShooting}
</InfoWithDelta>
<InfoWithDelta
decimals={1}
prefix="x "
indent
name="Gun damaged"
unit="scalar"
deltaType="lowerIsBetter"
>
{stats.dispersionGunDamaged}
</InfoWithDelta>
<Info name="Gun flexibility" unit="°" />
<InfoWithDelta decimals={1} indent name="Depression">
{stats.gunDepression}
</InfoWithDelta>
<InfoWithDelta decimals={1} indent name="Elevation">
{stats.gunElevation}
</InfoWithDelta>
{gunModelDefinition.pitch.front && (
<>
<InfoWithDelta decimals={1} indent name="Frontal depression">
{stats.gunFrontalDepression!}
</InfoWithDelta>
<InfoWithDelta decimals={1} indent name="Frontal elevation">
{stats.gunFrontalElevation!}
</InfoWithDelta>
</>
)}
{gunModelDefinition.pitch.back && (
<>
<InfoWithDelta decimals={1} indent name="Rear depression">
{stats.gunRearDepression!}
</InfoWithDelta>
<InfoWithDelta decimals={1} indent name="Rear elevation">
{stats.gunRearElevation!}
</InfoWithDelta>
</>
)}
{turretModelDefinition.yaw && (
<>
<InfoWithDelta decimals={1} indent name="Azimuth left">
{stats.azimuthLeft!}
</InfoWithDelta>
<InfoWithDelta decimals={1} indent name="Azimuth right">
{stats.azimuthRight!}
</InfoWithDelta>
</>
)}
</Flex>
<Flex direction="column" gap="2">
<Heading size="5">Maneuverability</Heading>
<Info name="Speed" unit="km/hr" />
<InfoWithDelta decimals={0} indent name="Forwards">
{stats.speedForwards}
</InfoWithDelta>
<InfoWithDelta decimals={0} indent name="Backwards">
{stats.speedBackwards}
</InfoWithDelta>
<InfoWithDelta decimals={0} name="Engine power" unit="hp">
{stats.enginePower}
</InfoWithDelta>
<InfoWithDelta
decimals={1}
name="Weight"
unit="tn"
deltaType="lowerIsBetter"
>
{stats.weight}
</InfoWithDelta>
<Info name="Power to weight ratio" unit="hp/tn" />
<InfoWithDelta decimals={1} indent name="On hard terrain">
{stats.powerToWeightRatioHardTerrain}
</InfoWithDelta>
<InfoWithDelta decimals={1} indent name="On medium terrain">
{stats.powerToWeightRatioMediumTerrain}
</InfoWithDelta>
<InfoWithDelta decimals={1} indent name="On soft terrain">
{stats.powerToWeightRatioSoftTerrain}
</InfoWithDelta>
<InfoWithDelta name="Turret traverse speed" unit="°/s" decimals={1}>
{stats.turretTraverseSpeed}
</InfoWithDelta>
<Info name="Hull traverse speed" unit="°/s" />
<InfoWithDelta decimals={1} indent name="On hard terrain">
{stats.hullTraverseHardTerrain}
</InfoWithDelta>
<InfoWithDelta decimals={1} indent name="On medium terrain">
{stats.hullTraverseMediumTerrain}
</InfoWithDelta>
<InfoWithDelta decimals={1} indent name="On soft terrain">
{stats.hullTraverseSoftTerrain}
</InfoWithDelta>
</Flex>
<Flex direction="column" gap="2">
<Heading size="5">Survivability</Heading>
<InfoWithDelta name="Health" unit="hp" decimals={0}>
{stats.health}
</InfoWithDelta>
<InfoWithDelta
name="Fire chance"
unit="%"
deltaType="lowerIsBetter"
decimals={0}
>
{stats.fireChance * 100}
</InfoWithDelta>
<InfoWithDelta name="View range" unit="m" decimals={0}>
{stats.viewRange}
</InfoWithDelta>
<Info name="Camouflage" unit="%" />
<InfoWithDelta indent name="Still" decimals={0}>
{stats.camouflageStill * 100}
</InfoWithDelta>
<InfoWithDelta indent name="Moving" decimals={0}>
{stats.camouflageMoving * 100}
</InfoWithDelta>
<InfoWithDelta indent name="Shooting still" decimals={0}>
{stats.camouflageShootingStill * 100}
</InfoWithDelta>
<InfoWithDelta indent name="Shooting moving" decimals={0}>
{stats.camouflageShootingMoving * 100}
</InfoWithDelta>
<InfoWithDelta indent name="Caught on fire" decimals={0}>
{stats.camouflageCaughtOnFire * 100}
</InfoWithDelta>
<Info name="Width" unit="m" decimals={0} deltaType="lowerIsBetter">
{stats.width}
</Info>
<Info name="Height" unit="m" decimals={0} deltaType="lowerIsBetter">
{stats.height}
</Info>
<Info name="Length" unit="m" decimals={0} deltaType="lowerIsBetter">
{stats.length}
</Info>
<Info name="Volume" unit="m" decimals={0} deltaType="lowerIsBetter">
{stats.volume}
</Info>
</Flex>
<Flex direction="column" gap="2">
<Flex gap="2" align="center">
<Heading size="5">Crew training</Heading>
<Popover.Root>
<Popover.Trigger>
<IconButton variant="ghost">
<InfoCircledIcon />
</IconButton>
</Popover.Trigger>
<Popover.Content>
<Flex gap="1" align="center">
<AccessibilityIcon />
<Text>represents the other roles a crew member can take.</Text>
</Flex>
</Popover.Content>
</Popover.Root>
</Flex>
{tank.crew.map((member) => {
const count = member.count ?? 1;
return (
<Fragment key={member.type}>
<InfoWithDelta <InfoWithDelta
key={`${member.type}-root`} indent
name={`${CREW_MEMBER_NAMES[member.type]}${count > 1 ? ` x ${count}` : ''}`}
unit="%"
decimals={0} decimals={0}
name={`At ${penetrationDistance}m`}
> >
{(member.type === 'commander' {lerp(
? commanderMastery shell.penetration[0],
: crewMemberMastery) * 100} shell.penetration[1],
(penetrationDistance *
penetrationLossOverDistanceCoefficient) /
500,
) * penetrationCoefficient}
</InfoWithDelta> </InfoWithDelta>
{member.substitute && ( <Flex align="center" gap="2" style={{ paddingLeft: 24 }}>
<Info <Text>Distance</Text>
key={`${member.type}-substitute`} <Slider
indent key={penetrationDistance}
name={ min={0}
<> max={500}
<Flex align="center" gap="1"> style={{ flex: 1 }}
<AccessibilityIcon /> defaultValue={[penetrationDistance]}
{member.substitute onValueChange={debounce(([value]) => {
.map((sub, index) => setPenetrationDistance(value);
index === 0 ? CREW_MEMBER_NAMES[sub] : sub, }, 500)}
)
.join(', ')}
</Flex>
</>
}
/> />
)} <TextField.Root
</Fragment> style={{ width: 64 }}
); ref={penetrationDistanceInput}
})} defaultValue={penetrationDistance}
onBlur={() => {
setPenetrationDistance(
Math.min(
parseInt(penetrationDistanceInput.current!.value),
500,
),
);
}}
onKeyDown={(event) => {
if (event.key === 'Enter') {
event.currentTarget.blur();
}
}}
>
<TextField.Slot side="right">m</TextField.Slot>
</TextField.Root>
</Flex>
</>
)}
<InfoWithDelta name="Damage" unit="hp" decimals={0}>
{stats.damage}
</InfoWithDelta>
<InfoWithDelta name="Module damage" unit="hp" decimals={0}>
{stats.moduleDamage}
</InfoWithDelta>
{isExplosive(shell.type) && (
<InfoWithDelta name="Splash radius" unit="m" decimals={0}>
{stats.explosionRadius!}
</InfoWithDelta>
)}
<InfoWithDelta name="Caliber" decimals={0} unit="mm">
{stats.caliber}
</InfoWithDelta>
<InfoWithDelta name="Normalization" decimals={0} unit="°">
{stats.shellNormalization}
</InfoWithDelta>
{shell.type !== 'he' && (
<InfoWithDelta
name="Ricochet"
decimals={0}
deltaType="lowerIsBetter"
unit="°"
>
{stats.shellRicochet!}
</InfoWithDelta>
)}
<InfoWithDelta name="Shell velocity" unit="m/s" decimals={0}>
{stats.shellVelocity}
</InfoWithDelta>
<InfoWithDelta
decimals={2}
name="Aim time"
unit="s"
deltaType="lowerIsBetter"
>
{stats.aimTime}
</InfoWithDelta>
<Info name="Dispersion at 100m" />
<InfoWithDelta
decimals={3}
indent
name="Still"
unit="m"
deltaType="lowerIsBetter"
>
{stats.dispersion}
</InfoWithDelta>
<InfoWithDelta
prefix="+ "
decimals={3}
indent
name="Moving"
deltaType="lowerIsBetter"
>
{stats.dispersionMoving}
</InfoWithDelta>
<InfoWithDelta
decimals={3}
prefix="+ "
indent
name="Hull traversing"
deltaType="lowerIsBetter"
>
{stats.dispersionHullTraversing}
</InfoWithDelta>
<InfoWithDelta
decimals={3}
prefix="+ "
indent
name="Turret traversing"
deltaType="lowerIsBetter"
>
{stats.dispersionTurretTraversing}
</InfoWithDelta>
<InfoWithDelta
decimals={3}
prefix="+ "
indent
name="After shooting"
deltaType="lowerIsBetter"
>
{stats.dispersionShooting}
</InfoWithDelta>
<InfoWithDelta
decimals={1}
prefix="x "
indent
name="Gun damaged"
unit="scalar"
deltaType="lowerIsBetter"
>
{stats.dispersionGunDamaged}
</InfoWithDelta>
<Info name="Gun flexibility" unit="°" />
<InfoWithDelta decimals={1} indent name="Depression">
{stats.gunDepression}
</InfoWithDelta>
<InfoWithDelta decimals={1} indent name="Elevation">
{stats.gunElevation}
</InfoWithDelta>
{gunModelDefinition.pitch.front && (
<>
<InfoWithDelta decimals={1} indent name="Frontal depression">
{stats.gunFrontalDepression!}
</InfoWithDelta>
<InfoWithDelta decimals={1} indent name="Frontal elevation">
{stats.gunFrontalElevation!}
</InfoWithDelta>
</>
)}
{gunModelDefinition.pitch.back && (
<>
<InfoWithDelta decimals={1} indent name="Rear depression">
{stats.gunRearDepression!}
</InfoWithDelta>
<InfoWithDelta decimals={1} indent name="Rear elevation">
{stats.gunRearElevation!}
</InfoWithDelta>
</>
)}
{turretModelDefinition.yaw && (
<>
<InfoWithDelta decimals={1} indent name="Azimuth left">
{stats.azimuthLeft!}
</InfoWithDelta>
<InfoWithDelta decimals={1} indent name="Azimuth right">
{stats.azimuthRight!}
</InfoWithDelta>
</>
)}
</Flex>
<Flex direction="column" gap="2">
<Flex gap="2" align="center">
<Heading size="5">Crew training</Heading>
<Popover.Root>
<Popover.Trigger>
<IconButton variant="ghost">
<InfoCircledIcon />
</IconButton>
</Popover.Trigger>
<Popover.Content>
<Flex gap="1" align="center">
<AccessibilityIcon />
<Text>
represents the other roles a crew member can take.
</Text>
</Flex>
</Popover.Content>
</Popover.Root>
</Flex>
{tank.crew.map((member) => {
const count = member.count ?? 1;
return (
<Fragment key={member.type}>
<InfoWithDelta
key={`${member.type}-root`}
name={`${CREW_MEMBER_NAMES[member.type]}${count > 1 ? ` x ${count}` : ''}`}
unit="%"
decimals={0}
>
{(member.type === 'commander'
? commanderMastery
: crewMemberMastery) * 100}
</InfoWithDelta>
{member.substitute && (
<Info
key={`${member.type}-substitute`}
indent
name={
<>
<Flex align="center" gap="1">
<AccessibilityIcon />
{member.substitute
.map((sub, index) =>
index === 0 ? CREW_MEMBER_NAMES[sub] : sub,
)
.join(', ')}
</Flex>
</>
}
/>
)}
</Fragment>
);
})}
</Flex>
</Flex>
<Flex direction="column" gap="8" style={{ flex: 1 }}>
<Flex direction="column" gap="2">
<Heading size="5">Maneuverability</Heading>
<Info name="Speed" unit="km/hr" />
<InfoWithDelta decimals={0} indent name="Forwards">
{stats.speedForwards}
</InfoWithDelta>
<InfoWithDelta decimals={0} indent name="Backwards">
{stats.speedBackwards}
</InfoWithDelta>
<InfoWithDelta decimals={0} name="Engine power" unit="hp">
{stats.enginePower}
</InfoWithDelta>
<InfoWithDelta
decimals={1}
name="Weight"
unit="tn"
deltaType="lowerIsBetter"
>
{stats.weight}
</InfoWithDelta>
<Info name="Power to weight ratio" unit="hp/tn" />
<InfoWithDelta decimals={1} indent name="On hard terrain">
{stats.powerToWeightRatioHardTerrain}
</InfoWithDelta>
<InfoWithDelta decimals={1} indent name="On medium terrain">
{stats.powerToWeightRatioMediumTerrain}
</InfoWithDelta>
<InfoWithDelta decimals={1} indent name="On soft terrain">
{stats.powerToWeightRatioSoftTerrain}
</InfoWithDelta>
<InfoWithDelta name="Turret traverse speed" unit="°/s" decimals={1}>
{stats.turretTraverseSpeed}
</InfoWithDelta>
<Info name="Hull traverse speed" unit="°/s" />
<InfoWithDelta decimals={1} indent name="On hard terrain">
{stats.hullTraverseHardTerrain}
</InfoWithDelta>
<InfoWithDelta decimals={1} indent name="On medium terrain">
{stats.hullTraverseMediumTerrain}
</InfoWithDelta>
<InfoWithDelta decimals={1} indent name="On soft terrain">
{stats.hullTraverseSoftTerrain}
</InfoWithDelta>
</Flex>
<Flex direction="column" gap="2">
<Heading size="5">Survivability</Heading>
<InfoWithDelta name="Health" unit="hp" decimals={0}>
{stats.health}
</InfoWithDelta>
<InfoWithDelta
name="Fire chance"
unit="%"
deltaType="lowerIsBetter"
decimals={0}
>
{stats.fireChance * 100}
</InfoWithDelta>
<InfoWithDelta name="View range" unit="m" decimals={0}>
{stats.viewRange}
</InfoWithDelta>
<Info name="Camouflage" unit="%" />
<InfoWithDelta indent name="Still" decimals={0}>
{stats.camouflageStill * 100}
</InfoWithDelta>
<InfoWithDelta indent name="Moving" decimals={0}>
{stats.camouflageMoving * 100}
</InfoWithDelta>
<InfoWithDelta indent name="Shooting still" decimals={0}>
{stats.camouflageShootingStill * 100}
</InfoWithDelta>
<InfoWithDelta indent name="Shooting moving" decimals={0}>
{stats.camouflageShootingMoving * 100}
</InfoWithDelta>
<InfoWithDelta indent name="Caught on fire" decimals={0}>
{stats.camouflageCaughtOnFire * 100}
</InfoWithDelta>
<Info name="Width" unit="m" decimals={0} deltaType="lowerIsBetter">
{stats.width}
</Info>
<Info name="Height" unit="m" decimals={0} deltaType="lowerIsBetter">
{stats.height}
</Info>
<Info name="Length" unit="m" decimals={0} deltaType="lowerIsBetter">
{stats.length}
</Info>
<Info name="Volume" unit="m" decimals={0} deltaType="lowerIsBetter">
{stats.volume}
</Info>
</Flex>
</Flex> </Flex>
</Flex> </Flex>
); );
@@ -1,5 +1,4 @@
import { Flex, Heading } from '@radix-ui/themes'; import { Flex, Heading } from '@radix-ui/themes';
import PageWrapper from '../../../../../../components/PageWrapper';
import { useWideFormat } from '../../../../../../hooks/useWideFormat'; import { useWideFormat } from '../../../../../../hooks/useWideFormat';
import { Characteristics } from '../Characteristics'; import { Characteristics } from '../Characteristics';
import { Consumables } from '../Characteristics/components/Consumables'; import { Consumables } from '../Characteristics/components/Consumables';
@@ -10,16 +9,25 @@ import { Provisions } from '../Characteristics/components/Provisions';
import { Skills } from '../Characteristics/components/Skills'; import { Skills } from '../Characteristics/components/Skills';
export function CharacteristicsSection() { export function CharacteristicsSection() {
const wideFormat = useWideFormat(720); const wideFormat = useWideFormat(700);
return ( return (
<PageWrapper > <Flex
align="center"
px="4"
style={{
boxSizing: 'border-box',
}}
direction={wideFormat ? 'row' : 'column'}
mt="4"
>
<Flex <Flex
gap="6" gap="6"
style={{ width: '100%' }} style={{ width: '100%' }}
direction={wideFormat ? 'row' : 'column'} direction={wideFormat ? 'row' : 'column'}
justify="center"
> >
<Flex style={{ flex: 1, maxWidth: 480 }} direction="column" gap="4"> <Flex style={{ flex: 1, maxWidth: 380 }} direction="column" gap="4">
<Heading>Configure</Heading> <Heading>Configure</Heading>
<Modules /> <Modules />
<Equipment /> <Equipment />
@@ -30,7 +38,11 @@ export function CharacteristicsSection() {
</Flex> </Flex>
<Flex <Flex
style={{ flex: 1, width: wideFormat ? undefined : '100%' }} style={{
flex: 1,
maxWidth: wideFormat ? 640 : undefined,
minWidth: 320,
}}
direction="column" direction="column"
gap="4" gap="4"
> >
@@ -38,6 +50,6 @@ export function CharacteristicsSection() {
<Characteristics /> <Characteristics />
</Flex> </Flex>
</Flex> </Flex>
</PageWrapper> </Flex>
); );
} }
@@ -25,7 +25,7 @@ export function HeroSection() {
style={{ style={{
background: `linear-gradient(${wideFormat ? -90 : 180}deg, ${theme.colors.appBackground1}, ${theme.colors.appBackground2})`, background: `linear-gradient(${wideFormat ? -90 : 180}deg, ${theme.colors.appBackground1}, ${theme.colors.appBackground2})`,
position: 'relative', position: 'relative',
height: wideFormat ? undefined : 'calc(87.5vh - 64px)', height: wideFormat ? undefined : 'calc(75vh - 64px)',
}} }}
gap="4" gap="4"
> >
@@ -124,7 +124,12 @@ export function TechTreeSection() {
<PageWrapper <PageWrapper
style={{ style={{
position: 'relative', position: 'relative',
overflow: 'hidden', }}
containerProps={{
style: {
overflow: 'hidden',
marginTop: 16,
},
}} }}
size={tank.ancestors && tank.successors ? 880 : 480} size={tank.ancestors && tank.successors ? 880 : 480}
gap="0" gap="0"
+4
View File
@@ -8,6 +8,7 @@ type PageWrapperProps = FlexProps & {
noPadding?: boolean; noPadding?: boolean;
noMaxWidth?: boolean; noMaxWidth?: boolean;
highlight?: boolean; highlight?: boolean;
containerProps?: ComponentProps<typeof Theme>;
}; };
export default function PageWrapper({ export default function PageWrapper({
@@ -18,6 +19,7 @@ export default function PageWrapper({
noPadding = false, noPadding = false,
noMaxWidth = false, noMaxWidth = false,
highlight = false, highlight = false,
containerProps,
...props ...props
}: PageWrapperProps) { }: PageWrapperProps) {
return ( return (
@@ -30,7 +32,9 @@ export default function PageWrapper({
background: highlight background: highlight
? `linear-gradient(-90deg, ${theme.colors.appBackground1}, ${theme.colors.appBackground2})` ? `linear-gradient(-90deg, ${theme.colors.appBackground1}, ${theme.colors.appBackground2})`
: undefined, : undefined,
...containerProps?.style,
}} }}
{...containerProps}
> >
<Flex <Flex
direction="column" direction="column"