new basic layout

This commit is contained in:
tresabhi committed 2026-06-01 13:48:16 -05:00
1 parent 6aa14d2faf
commit 73de6e3521
6 files changed
+254 -186

No files matched your search

@@ -1,24 +1,37 @@
.characteristics {
--cell-width: 2.5rem;
--group-span: 7;
--inner-padding: var(--space-4);
display: grid;
grid-auto-rows: var(--cell-width);
grid-template-columns: repeat(auto-fill, minmax(var(--cell-width), 1fr));
grid-auto-flow: dense;
padding: var(--space-5);
gap: var(--space-4);
display: flex;
flex-direction: column;
gap: var(--space-5);
box-sizing: border-box;
.group {
display: flex;
flex-direction: column;
display: grid;
grid-template-columns: 1fr;
gap: var(--space-4);
grid-column: span var(--group-span);
gap: inherit;
.title-wrapper {
background-color: var(--amber-a3);
grid-row: span 1;
padding: 0 var(--inner-padding);
display: flex;
align-items: center;
gap: var(--space-4);
gap: var(--space-3);
.title {
.title-content {
.content {
display: flex;
align-items: center;
gap: var(--space-2);
@@ -31,67 +44,60 @@
}
}
}
.separator {
height: var(--space-1);
flex: 1;
background-color: var(--gray-5);
}
}
.content {
--cell-width: 2rem;
--toy-span: 6;
.item {
padding: 0 var(--inner-padding);
display: grid;
grid-auto-rows: var(--cell-width);
grid-template-columns: repeat(auto-fill, minmax(var(--cell-width), 1fr));
grid-auto-flow: row dense;
gap: var(--space-4);
grid-row: span 1;
.item {
grid-row: span 1;
grid-column: span var(--toy-span);
display: flex;
flex-direction: column;
justify-content: center;
gap: var(--space-2);
.value {
display: flex;
flex-direction: column;
justify-content: center;
gap: var(--space-2);
justify-content: space-between;
}
.value {
display: flex;
justify-content: space-between;
}
.ranking {
display: flex;
align-items: center;
gap: var(--space-3);
.ranking {
display: flex;
align-items: center;
gap: var(--space-3);
.bar-wrapper {
flex: 1;
height: var(--space-1);
.bar-wrapper {
flex: 1;
height: var(--space-1);
background-color: var(--gray-3);
border-radius: var(--radius-max);
overflow: hidden;
background-color: var(--gray-3);
border-radius: var(--radius-max);
overflow: hidden;
.bar {
background-color: var(--jade-9);
width: 75%;
height: 100%;
}
.bar {
background-color: var(--jade-9);
width: 75%;
height: 100%;
}
}
}
}
.toy {
grid-row: span var(--toy-span);
grid-column: span var(--toy-span);
.toy {
grid-row: span var(--group-span);
padding: var(--inner-padding);
background-color: var(--gray-3);
border: var(--gray-5) solid var(--space-1);
.content {
background-color: green;
width: 100%;
height: 100%;
display: flex;
align-items: center;
justify-content: center;
background-color: var(--gray-2);
outline: var(--gray-5) solid var(--space-1);
border-radius: var(--space-3);
}
@@ -1,7 +1,6 @@
import { CaretDownIcon, CaretRightIcon } from "@radix-ui/react-icons";
import { GearIcon } from "@radix-ui/react-icons";
import { useCharacteristicRenderer } from "../../hooks/useCharacteristicRenderer";
import { useStrings } from "../../hooks/useStrings";
import { Tankopedia } from "../../stores/tankopedia";
import type { CharacteristicOutput } from "../../tankopedia/characteristics";
import {
characteristicsOrder,
@@ -9,7 +8,6 @@ import {
type CharacteristicsGroup,
} from "../../tankopedia/characteristicsOrder";
import type { ComputedCharacteristics } from "../../tankopedia/computeCharacteristics";
import { Heading } from "../Heading";
import { Text } from "../Text";
import styles from "./index.module.css";
@@ -35,35 +33,35 @@ interface GroupProps {
}
function Group({ group, characteristics }: GroupProps) {
const isOpen = Tankopedia.use((state) => state.groups[group.group]);
const strings = useStrings();
const toys = group.order.filter((item) => "toy" in item).length;
const items = group.order.filter((item) => "name" in item).length;
const span = `calc(${toys} * var(--group-span) + ${items} + 1)`;
return (
<div className={styles.group}>
<div
className={styles.group}
style={{
gridRow: `span ${span}`,
gridTemplateRows: `repeat(${span}, 1fr)`,
}}
>
<div className={styles["title-wrapper"]}>
<Heading className={styles.title} size="3">
<div className={styles["title-content"]}>
{isOpen ? <CaretDownIcon /> : <CaretRightIcon />}
{
(strings.tanks.characteristics.titles as Record<string, string>)[
group.group
]
}
<Text className={styles.title}>
<div className={styles.content}>
{group.group}
<GearIcon />
</div>
</Heading>
<div className={styles.separator} />
</Text>
</div>
<div className={styles.content}>
{group.order.map((item) =>
"toy" in item ? (
<Toy {...item} />
) : (
<Item config={item} value={characteristics[item.name]} />
),
)}
</div>
{group.order.map((item) =>
"toy" in item ? (
<Toy {...item} />
) : (
<Item config={item} value={characteristics[item.name]} />
),
)}
</div>
);
}
@@ -80,13 +78,7 @@ function Item({ config, value }: ItemProps) {
return (
<span className={styles.item}>
<div className={styles.value}>
<Text lowContrast>
{
(strings.tanks.characteristics.items as Record<string, string>)[
config.name
]
}
</Text>
<Text lowContrast>{config.name}</Text>
<Text>{renderCharacteristic(value, config)}</Text>
</div>
@@ -108,5 +100,11 @@ interface ToyProps {
}
function Toy({ toy }: ToyProps) {
return <span className={styles.toy}></span>;
return (
<div className={styles.toy}>
<div className={styles.content}>
<Text lowContrast>{toy} toy</Text>
</div>
</div>
);
}
@@ -1,4 +1,4 @@
import { Fragment, useMemo } from "react";
import { useMemo } from "react";
import { api } from "../../../../api/dynamic";
import { TankopediaCharacteristics } from "../../../../components/TankopediaCharacteristics";
import { TankopediaLoadout } from "../../../../components/TankopediaLoadout";
@@ -6,10 +6,8 @@ import { TankopediaSandbox } from "../../../../components/TankopediaSandbox";
import { withErrorWrapper } from "../../../../hocs/withErrorWrapper";
import { withLocale } from "../../../../hocs/withLocale";
import { useAwait } from "../../../../hooks/useAwait";
import { useCharacteristicRenderer } from "../../../../hooks/useCharacteristicRenderer";
import { useEquipment } from "../../../../hooks/useEquipment";
import { Tankopedia } from "../../../../stores/tankopedia";
import { characteristicsOrder } from "../../../../tankopedia/characteristicsOrder";
import { computeCharacteristics } from "../../../../tankopedia/computeCharacteristics";
import styles from "./_index.module.css";
@@ -50,62 +48,3 @@ export const Page = withErrorWrapper(
);
}),
);
export const _Page = withErrorWrapper(
withLocale<PageProps>(({ id }) => {
const protagonistTank = useAwait(() => api.tank(id), `tank-${id}`);
Tankopedia.useInitialization(protagonistTank);
const renderCharacteristic = useCharacteristicRenderer();
const protagonist = Tankopedia.use((state) => state.protagonist);
const protagonistEquipment = useEquipment(protagonistTank.tank!);
const { characteristics } = useMemo(
() =>
computeCharacteristics(
protagonistTank,
protagonistEquipment,
protagonist,
),
[protagonist],
);
return (
<>
<h1>{protagonist.id}</h1>
<br />
<TankopediaLoadout characteristics={characteristics} />
<h2>characteristics</h2>
{characteristicsOrder.map((group) => {
return (
<Fragment key={group.group}>
<h3>{group.group}</h3>
{group.order.map((item) => {
if ("toy" in item) {
return <span key={`toy-${item.toy}`}>toy: {item.toy}</span>;
}
const value = characteristics[item.name];
if (value == null) return null;
return (
<span key={`characteristic-${item.name}`}>
{item.name}: {renderCharacteristic(value, item)}
</span>
);
})}
</Fragment>
);
})}
</>
);
}),
);
@@ -23,6 +23,7 @@ const { id, locale } = Astro.props;
<ColorScale color="red" />
<ColorScale color="jade" />
<ColorScale color="amber" />
<ColorScale color="amber" alpha />
<Page {locale} {id} client:load />
</BaseLayout>
@@ -419,4 +419,60 @@ export const characteristics = {
compensation({ tank }) {
return tank.compensation!.compensation?.currency_reward?.amount ?? null;
},
purchase_price() {
return -Infinity;
},
upgrade_price() {
return -Infinity;
},
research_level() {
return -Infinity;
},
crew_xp_multiplier() {
return -Infinity;
},
clipping_potential() {
return -Infinity;
},
burst_size() {
return -Infinity;
},
intra_burst() {
return -Infinity;
},
dispersion_angle() {
return -Infinity;
},
gun_yaw_range() {
return -Infinity;
},
brake_force() {
return -Infinity;
},
gun_traverse_speed() {
return -Infinity;
},
fire_health_burn() {
return -Infinity;
},
ramming_resistance() {
return -Infinity;
},
width() {
return -Infinity;
},
} satisfies Record<string, Characteristic>;
@@ -29,19 +29,25 @@ export interface CharacteristicRenderConfig {
}) => ReactNode;
}
export interface ToyRenderConfig {
toy: string;
}
export type CharacteristicsGroup = {
group: string;
order: (
| CharacteristicRenderConfig
| {
toy: string;
}
)[];
render: CharacteristicsGroupRender;
order: (CharacteristicRenderConfig | ToyRenderConfig)[];
};
export enum CharacteristicsGroupRender {
Flashy,
Statistical,
}
export const characteristicsOrder: CharacteristicsGroup[] = [
{
group: "meta",
render: CharacteristicsGroupRender.Flashy,
order: [
{
name: "name",
@@ -80,15 +86,29 @@ export const characteristicsOrder: CharacteristicsGroup[] = [
return strings.tiers[name as keyof typeof strings.tiers];
},
},
{ name: "compensation" },
{ name: "id" },
{ name: "slug" },
],
},
{
group: "firepower",
group: "economics",
render: CharacteristicsGroupRender.Statistical,
order: [
{ name: "purchase_price" },
{ name: "compensation" },
{ name: "upgrade_price" },
{ name: "research_level" },
{ name: "crew_xp_multiplier" },
// TODO: investigate ATTRIBUTE_NAME_CREW_XP_MULTIPLIER
],
},
{
group: "gun",
render: CharacteristicsGroupRender.Statistical,
order: [
{ toy: "reload" },
{
name: "gun_type",
render({ output }) {
@@ -96,75 +116,123 @@ export const characteristicsOrder: CharacteristicsGroup[] = [
},
},
{ name: "dpm", decimals: 0, units: "hp_min" },
{ name: "damage", units: "hp" },
{ name: "module_damage", units: "hp" },
{ name: "reload", decimals: 2, units: "s" },
{ name: "reloads", decimals: 2, units: "s" },
{ name: "damage", units: "hp" },
{ name: "module_damage", units: "hp" },
{ toy: "reload" },
],
},
{
group: "clip",
render: CharacteristicsGroupRender.Statistical,
order: [
{ name: "clipping_potential" },
{ name: "clip_size" },
{ name: "optimal_shell" },
{ name: "intra_clip", decimals: 2, units: "s" },
// { name: "burst_size" }, // TODO: implement burst size
// { name: 'intra_burst', decimals: 2, units: "s" }, // TODO: implement intra burst
{ name: "burst_size" },
{ name: "intra_burst", decimals: 2, units: "s" },
// TODO: investigate ATTRIBUTE_NAME_SHELLS_SLOTS
// TODO: investigate ATTRIBUTE_NAME_RELOAD_SPEED_SCALE
// TODO: investigate ATTRIBUTE_NAME_AVERAGE_THICKNESS
// TODO: investigate ATTRIBUTE_NAME_TRACER_SHELLS_OBSERVATION_TIMEOUT
],
},
// { toy: "ricochet" },
{
group: "shell",
render: CharacteristicsGroupRender.Statistical,
order: [
{ name: "penetration", units: "mm" },
{ name: "shell_velocity", units: "m_s" },
{ name: "shell_range", units: "m" },
{ name: "shell_capacity" },
{ name: "caliber", units: "mm" },
{ name: "normalization", units: "deg" },
{ name: "ricochet", units: "deg" },
{ name: "shell_velocity", units: "m_s" },
{ name: "shell_range", units: "m" },
{ name: "shell_capacity" },
// { toy: "aim_time" },
{ name: "aim_time", decimals: 1, units: "s" },
{ name: "dispersion", decimals: 3, units: "m" },
// { name: "dispersion_angle", decimals: 3, units: "m" }, // TODO: implement dispersion angle
// { toy: "flexibility" },
{ name: "gun_depression", decimals: 0, units: "deg" },
{ name: "gun_elevation", decimals: 0, units: "deg" },
// { name: "gun_yaw_range", decimals: 0, units: "deg" }, // TODO: implement gun yaw range
{ toy: "ricochet" },
],
},
{
group: "accuracy",
render: CharacteristicsGroupRender.Statistical,
order: [
{ name: "aim_time", decimals: 1, units: "s" },
{ name: "dispersion", decimals: 3, units: "m" },
{ name: "dispersion_angle", decimals: 3, units: "m" },
{ toy: "aim_time" },
],
},
{
group: "flexibility",
render: CharacteristicsGroupRender.Statistical,
order: [
{ name: "gun_depression", decimals: 0, units: "deg" },
{ name: "gun_elevation", decimals: 0, units: "deg" },
{ name: "gun_yaw_range", decimals: 0, units: "deg" },
{ toy: "flexibility" },
],
},
{
group: "maneuverability",
render: CharacteristicsGroupRender.Statistical,
order: [
// { toy: "traverse" },
{ name: "speed_forward", units: "km_h" },
{ name: "speed_backward", units: "km_h" },
{ name: "engine_power", units: "bhp" },
// { name: "brake_force", units: "N" }, // TODO: implement brake force
{ name: "brake_force", units: "N" },
{ name: "weight", decimals: 1, units: "t" },
{ name: "terrain_coefficient" },
{ name: "power_to_weight", decimals: 1, units: "bhp_t" },
{ name: "hull_traverse_speed", units: "deg_s" },
{ name: "turret_traverse_speed", units: "deg_s" },
// { name: "gun_traverse_speed", units: "deg_s" }, // TODO: implement gun traverse speed
{ name: "gun_traverse_speed", units: "deg_s" },
{ toy: "traverse" },
// TODO: investigate ATTRIBUTE_NAME_TERRAIN_SIDE_FRICTION
// TODO: investigate ATTRIBUTE_NAME_TERRAIN_FORWARD_FRICTION
// TODO: investigate ATTRIBUTE_NAME_RESERVE_TRACK_BACKWARD_SPEED
],
},
{
group: "survivability",
render: CharacteristicsGroupRender.Statistical,
order: [
{ name: "health", units: "hp" },
{ name: "fire_chance", units: "%" },
// { name: "fire_health_burn", units: "hp_s" }, // TODO: implement fire health burn
// { name: "ramming_resistance", units: "%" }, // TODO: implement ramming resistance
{ name: "fire_health_burn", units: "hp_s" },
{ name: "ramming_resistance", units: "%" },
// { toy: "view_range" },
{ toy: "fire_health_burn" },
// TODO: investigate ATTRIBUTE_NAME_MAX_FIRE_DURATION
],
},
{
group: "concealment",
render: CharacteristicsGroupRender.Statistical,
order: [
{ name: "view_range", units: "m" },
{ name: "camouflage", decimals: 0, units: "%" },
// { name: "consumable_cooldown", decimals: 0, units: "%" }, // TODO: implement consumable cooldown
// { name: "consumable_duration", decimals: 0, units: "s" }, // TODO: implement consumable duration
// { name: "consumable_reload", decimals: 0, units: "s" }, // TODO: implement consumable reload
{ toy: "view_range" },
// { name: "width", units: "m" }, // TODO: implement width
{ name: "width", units: "m" },
{ name: "height", units: "m" },
{ name: "length", units: "m" },
{ name: "volume", units: "m" },