final layout

This commit is contained in:
tresabhi committed 2026-06-02 12:27:32 -05:00
1 parent 73de6e3521
commit 9b8d4c4e3c
5 files changed
+183 -110

No files matched your search

+7 -3
View File
@@ -204,10 +204,14 @@
"tanks": { "tanks": {
"characteristics": { "characteristics": {
"titles": { "titles": {
"meta": "Meta", "gun": "Gun",
"firepower": "Firepower", "clip": "Clip",
"shell": "Shell",
"accuracy": "Accuracy",
"flexibility": "Flexibility",
"maneuverability": "Maneuverability", "maneuverability": "Maneuverability",
"survivability": "Survivability" "survivability": "Survivability",
"concealment": "Concealment"
}, },
"items": { "items": {
@@ -1,43 +1,54 @@
.characteristics { .characteristics-wrapper {
--cell-width: 2.5rem; margin: 0 var(--space-4);
--base-cell-width: 20rem;
--group-span: 7; --group-span: 7;
--inner-padding: var(--space-4);
container-type: inline-size;
.characteristics {
--N: round(down, 100cqw / var(--base-cell-width));
--cell-width: calc(100cqw / var(--N));
--cell-height: calc(var(--cell-width) / var(--group-span));
display: grid; display: grid;
grid-auto-rows: var(--cell-width); grid-template-columns: repeat(
grid-template-columns: repeat(auto-fill, minmax(var(--cell-width), 1fr)); auto-fill,
minmax(var(--base-cell-width), 1fr)
);
grid-auto-rows: var(--cell-height);
grid-auto-flow: dense; grid-auto-flow: dense;
padding: var(--space-5); gap: var(--cell-gap);
gap: var(--space-4);
box-sizing: border-box; box-sizing: border-box;
.group { .group {
--gradient-size: min(100%, var(--space-8));
grid-column: span 1;
display: grid; display: grid;
grid-template-columns: 1fr; grid-template-columns: inherit;
grid-auto-rows: inherit;
grid-column: span var(--group-span); .title {
gap: inherit;
.title-wrapper {
background-color: var(--amber-a3);
grid-row: span 1; grid-row: span 1;
padding: 0 var(--inner-padding);
display: flex; display: flex;
align-items: center; align-items: center;
gap: var(--space-3); margin: 0 calc(var(--space-4));
.title { .center {
.content { flex: 1;
padding: var(--space-3);
.text {
.align {
display: flex; display: flex;
align-items: center; align-items: center;
gap: var(--space-2); gap: var(--space-2);
cursor: pointer;
svg { svg {
width: 1em; width: 1em;
height: 1em; height: 1em;
@@ -45,11 +56,22 @@
} }
} }
} }
}
.content {
margin: 0 var(--space-4);
padding: var(--space-0);
gap: inherit;
height: 100%;
display: flex;
flex-direction: column;
.item { .item {
padding: 0 var(--inner-padding); padding: 0 var(--space-3);
grid-row: span 1; flex: 1;
display: flex; display: flex;
flex-direction: column; flex-direction: column;
@@ -75,8 +97,6 @@
overflow: hidden; overflow: hidden;
.bar { .bar {
background-color: var(--jade-9);
width: 75%;
height: 100%; height: 100%;
} }
} }
@@ -84,11 +104,10 @@
} }
.toy { .toy {
grid-row: span var(--group-span); flex: var(--group-span);
padding: var(--inner-padding); padding: var(--space-4) 0;
.content { .sandbox {
background-color: green;
width: 100%; width: 100%;
height: 100%; height: 100%;
@@ -98,9 +117,18 @@
background-color: var(--gray-2); background-color: var(--gray-2);
outline: var(--gray-5) solid var(--space-1); outline: var(--gray-5) solid var(--space-1);
border-radius: var(--space-3); border-radius: var(--space-3);
} }
} }
.dead {
grid-row: span 1;
}
}
.dead {
grid-row: span 1;
}
}
} }
} }
@@ -1,4 +1,3 @@
import { GearIcon } from "@radix-ui/react-icons";
import { useCharacteristicRenderer } from "../../hooks/useCharacteristicRenderer"; import { useCharacteristicRenderer } from "../../hooks/useCharacteristicRenderer";
import { useStrings } from "../../hooks/useStrings"; import { useStrings } from "../../hooks/useStrings";
import type { CharacteristicOutput } from "../../tankopedia/characteristics"; import type { CharacteristicOutput } from "../../tankopedia/characteristics";
@@ -8,6 +7,7 @@ import {
type CharacteristicsGroup, type CharacteristicsGroup,
} from "../../tankopedia/characteristicsOrder"; } from "../../tankopedia/characteristicsOrder";
import type { ComputedCharacteristics } from "../../tankopedia/computeCharacteristics"; import type { ComputedCharacteristics } from "../../tankopedia/computeCharacteristics";
import { Heading } from "../Heading";
import { Text } from "../Text"; import { Text } from "../Text";
import styles from "./index.module.css"; import styles from "./index.module.css";
@@ -19,11 +19,13 @@ export function TankopediaCharacteristics({
characteristics, characteristics,
}: TankopediaCharacteristicsProps) { }: TankopediaCharacteristicsProps) {
return ( return (
<div className={styles["characteristics-wrapper"]}>
<div className={styles.characteristics}> <div className={styles.characteristics}>
{characteristicsOrder.map((group) => ( {characteristicsOrder.map((group) => (
<Group group={group} characteristics={characteristics} /> <Group group={group} characteristics={characteristics} />
))} ))}
</div> </div>
</div>
); );
} }
@@ -36,25 +38,38 @@ function Group({ group, characteristics }: GroupProps) {
const strings = useStrings(); const strings = useStrings();
const toys = group.order.filter((item) => "toy" in item).length; const toys = group.order.filter((item) => "toy" in item).length;
const items = group.order.filter((item) => "name" in item).length; const items = group.order.filter((item) => "name" in item).length;
const span = `calc(${toys} * var(--group-span) + ${items} + 1)`; const groupSpan = `calc(${toys} * var(--group-span) + ${items} + 2)`;
const contentSpan = `calc(${toys} * var(--group-span) + ${items})`;
return ( return (
<div <div
className={styles.group} className={styles.group}
style={{ style={{
gridRow: `span ${span}`, gridRow: `span ${groupSpan}`,
gridTemplateRows: `repeat(${span}, 1fr)`, gridTemplateRows: `repeat(${groupSpan}, 1fr)`,
}} }}
> >
<div className={styles["title-wrapper"]}> <div className={styles.title}>
<Text className={styles.title}> <div className={styles.center}>
<div className={styles.content}> <Heading size="3" className={styles.text}>
{group.group} <div className={styles.align}>
<GearIcon /> {
(
strings.tanks.characteristics.titles as Record<string, string>
)[group.group]
}
{/*<GearIcon />*/}
</div>
</Heading>
</div> </div>
</Text>
</div> </div>
<div
className={styles.content}
style={{
gridRow: `span ${contentSpan}`,
}}
>
{group.order.map((item) => {group.order.map((item) =>
"toy" in item ? ( "toy" in item ? (
<Toy {...item} /> <Toy {...item} />
@@ -62,6 +77,9 @@ function Group({ group, characteristics }: GroupProps) {
<Item config={item} value={characteristics[item.name]} /> <Item config={item} value={characteristics[item.name]} />
), ),
)} )}
<div className={styles.dead} />
</div>
</div> </div>
); );
} }
@@ -83,9 +101,7 @@ function Item({ config, value }: ItemProps) {
</div> </div>
<div className={styles.ranking}> <div className={styles.ranking}>
<div className={styles["bar-wrapper"]}> <Bar value={Math.random()} />
<div className={styles.bar} />
</div>
<Text lowContrast size="minor" className={styles.number}> <Text lowContrast size="minor" className={styles.number}>
1 / 132 1 / 132
@@ -95,6 +111,35 @@ function Item({ config, value }: ItemProps) {
); );
} }
interface BarProps {
value: number;
}
const barColors = ["red", "orange", "amber", "jade"];
function Bar({ value }: BarProps) {
const index = (barColors.length - 1) * value;
const iA = Math.floor(index);
const iB = Math.ceil(index);
const A = barColors[iA];
const B = barColors[iB];
const x = index % 1;
const p = (1 - x) * 100;
const v = 7;
return (
<div className={styles["bar-wrapper"]}>
<div
className={styles.bar}
style={{
backgroundColor: `color-mix(in srgb, var(--${A}-${v}) ${p}%, var(--${B}-${v}))`,
width: `${value * 100}%`,
}}
/>
</div>
);
}
interface ToyProps { interface ToyProps {
toy: string; toy: string;
} }
@@ -102,7 +147,7 @@ interface ToyProps {
function Toy({ toy }: ToyProps) { function Toy({ toy }: ToyProps) {
return ( return (
<div className={styles.toy}> <div className={styles.toy}>
<div className={styles.content}> <div className={styles.sandbox}>
<Text lowContrast>{toy} toy</Text> <Text lowContrast>{toy} toy</Text>
</div> </div>
</div> </div>
@@ -20,10 +20,12 @@ const { id, locale } = Astro.props;
--- ---
<BaseLayout {locale} accent="violet"> <BaseLayout {locale} accent="violet">
<ColorScale color="red" />
<ColorScale color="jade" />
<ColorScale color="amber" />
<ColorScale color="amber" alpha /> <ColorScale color="amber" alpha />
<ColorScale color="red" />
<ColorScale color="orange" />
<ColorScale color="amber" />
<ColorScale color="jade" />
<Page {locale} {id} client:load /> <Page {locale} {id} client:load />
</BaseLayout> </BaseLayout>
@@ -1,11 +1,5 @@
import type { Strings } from "@blitzkit/i18n"; import type { Strings } from "@blitzkit/i18n";
import {
Nation,
TankClass,
TankType,
} from "@protos/blitz_static_tank_component";
import type { ReactNode } from "react"; import type { ReactNode } from "react";
import { VintageIcon } from "../icons/Vintage";
import { import {
GunType, GunType,
type CharacteristicName, type CharacteristicName,
@@ -45,65 +39,65 @@ export enum CharacteristicsGroupRender {
} }
export const characteristicsOrder: CharacteristicsGroup[] = [ export const characteristicsOrder: CharacteristicsGroup[] = [
{ // {
group: "meta", // group: "meta",
render: CharacteristicsGroupRender.Flashy, // render: CharacteristicsGroupRender.Flashy,
order: [ // order: [
{ // {
name: "name", // name: "name",
strings: "TankEntity", // strings: "TankEntity",
render({ output, gameStrings }) { // render({ output, gameStrings }) {
return gameStrings[output as string]; // return gameStrings[output as string];
}, // },
}, // },
{ // {
name: "class", // name: "class",
render({ output, strings }) { // render({ output, strings }) {
return strings.classes.full[output as TankClass]; // return strings.classes.full[output as TankClass];
}, // },
}, // },
{ // {
name: "type", // name: "type",
render({ output, strings }) { // render({ output, strings }) {
return strings.types[output as TankType]; // return strings.types[output as TankType];
}, // },
}, // },
{ // {
name: "nation", // name: "nation",
render({ output, strings }) { // render({ output, strings }) {
return strings.nations[output as Nation]; // return strings.nations[output as Nation];
}, // },
}, // },
{ // {
name: "tier", // name: "tier",
render({ output, strings }) { // render({ output, strings }) {
const name = output as string; // const name = output as string;
if (name == "vintage") { // if (name == "vintage") {
return <VintageIcon />; // return <VintageIcon />;
} // }
return strings.tiers[name as keyof typeof strings.tiers]; // return strings.tiers[name as keyof typeof strings.tiers];
}, // },
}, // },
{ name: "id" }, // { name: "id" },
{ name: "slug" }, // { name: "slug" },
], // ],
}, // },
{ // {
group: "economics", // group: "economics",
render: CharacteristicsGroupRender.Statistical, // render: CharacteristicsGroupRender.Statistical,
order: [ // order: [
{ name: "purchase_price" }, // { name: "purchase_price" },
{ name: "compensation" }, // { name: "compensation" },
{ name: "upgrade_price" }, // { name: "upgrade_price" },
{ name: "research_level" }, // { name: "research_level" },
{ name: "crew_xp_multiplier" }, // { name: "crew_xp_multiplier" },
// TODO: investigate ATTRIBUTE_NAME_CREW_XP_MULTIPLIER // // TODO: investigate ATTRIBUTE_NAME_CREW_XP_MULTIPLIER
], // ],
}, // },
{ {
group: "gun", group: "gun",