some more tankoepdia layout

This commit is contained in:
tresabhi committed 2026-05-28 14:03:27 -05:00
1 parent d2b4cc3e7d
commit 0a186399b6
12 files changed
+234 -8

No files matched your search

+2 -1
View File
@@ -6,6 +6,7 @@
"uasset",
"oodle",
"blitzkit",
"webp"
"webp",
"hocs"
]
}
+14
View File
@@ -202,6 +202,20 @@
},
"tanks": {
"characteristics": {
"titles": {
"meta": "Meta",
"firepower": "Firepower",
"maneuverability": "Maneuverability",
"survivability": "Survivability"
}
},
"navigation": {
"tank": "Tank",
"characteristics": "Characteristics"
},
"loadout": {
"consumables": "Consumables",
"modules": "Modules",
@@ -0,0 +1,56 @@
.characteristics {
padding: var(--space-5);
padding-top: var(--space-6);
display: flex;
flex-direction: column;
gap: var(--space-5);
box-sizing: border-box;
max-height: 100%;
overflow-y: auto;
&[data-visible="false"] {
display: none;
}
.group {
display: flex;
flex-direction: column;
gap: var(--space-4);
.title-wrapper {
display: flex;
align-items: center;
gap: var(--space-4);
.title {
.title-content {
display: flex;
align-items: center;
gap: var(--space-2);
cursor: pointer;
svg {
width: 1em;
height: 1em;
}
}
}
.separator {
height: var(--space-1);
flex: 1;
background-color: var(--gray-5);
}
}
.content {
display: flex;
flex-direction: column;
}
}
}
@@ -0,0 +1,62 @@
import { CaretDownIcon, CaretRightIcon } from "@radix-ui/react-icons";
import {
characteristicsOrder,
type CharacteristicRenderConfig,
type CharacteristicsGroup,
} from "../../tankopedia/characteristicsOrder";
import styles from "./index.module.css";
import { Heading } from "../Heading";
import { Tankopedia, TankopediaTab } from "../../stores/tankopedia";
import { useStrings } from "../../hooks/useStrings";
export function TankopediaCharacteristics() {
const isVisible = Tankopedia.use(
(state) => state.tab === TankopediaTab.Characteristics,
);
return (
<div className={styles.characteristics} data-visible={isVisible}>
{characteristicsOrder.map((group) => (
<Group {...group} />
))}
</div>
);
}
function Group({ group, order }: CharacteristicsGroup) {
const isOpen = Tankopedia.use((state) => state.groups[group]);
const strings = useStrings();
return (
<div className={styles.group}>
<div className={styles["title-wrapper"]}>
<Heading className={styles.title} size="3">
<div className={styles["title-content"]}>
{isOpen ? <CaretDownIcon /> : <CaretRightIcon />}
{strings.tanks.characteristics.titles[group]}
</div>
</Heading>
<div className={styles.separator} />
</div>
<div className={styles.content}>
{order.map((item) =>
"toy" in item ? <Toy {...item} /> : <Item {...item} />,
)}
</div>
</div>
);
}
interface ToyProps {
toy: string;
}
function Toy({ toy }: ToyProps) {
return <span>{toy}</span>;
}
function Item({ name }: CharacteristicRenderConfig) {
return <span>{name}</span>;
}
@@ -0,0 +1,20 @@
.tabs {
display: flex;
position: absolute;
top: 0;
left: 50%;
transform: translateX(-50%);
.tab {
padding: var(--space-3) var(--space-5);
border-top: var(--space-2) solid transparent;
cursor: pointer;
&[data-selected="true"] {
background: linear-gradient(var(--accent-a4), transparent);
border-top: var(--space-2) solid var(--accent-9);
}
}
}
@@ -0,0 +1,37 @@
import { useStrings } from "../../hooks/useStrings";
import { Tankopedia, TankopediaTab } from "../../stores/tankopedia";
import { Text } from "../Text";
import styles from "./index.module.css";
export function TankopediaNavigation() {
const strings = useStrings();
const tab = Tankopedia.use((state) => state.tab);
return (
<div className={styles.tabs}>
<div
className={styles.tab}
data-selected={tab === TankopediaTab.Tank}
onClick={() => {
Tankopedia.mutate((draft) => {
draft.tab = TankopediaTab.Tank;
});
}}
>
<Text>{strings.tanks.navigation.tank}</Text>
</div>
<div
className={styles.tab}
data-selected={tab === TankopediaTab.Characteristics}
onClick={() => {
Tankopedia.mutate((draft) => {
draft.tab = TankopediaTab.Characteristics;
});
}}
>
<Text>{strings.tanks.navigation.characteristics}</Text>
</div>
</div>
);
}
@@ -1,7 +1,11 @@
.sandbox {
position: relative;
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
.canvas {
&[data-visible="false"] {
display: none;
}
}
@@ -2,11 +2,19 @@ import { Canvas, useLoader } from "@react-three/fiber";
import { OrbitControls } from "@react-three/drei";
import styles from "./index.module.css";
import { GLTFLoader } from "three-stdlib";
import { Tankopedia } from "../../stores/tankopedia";
import { TankopediaTab } from "../../stores/tankopedia";
export function TankopediaSandbox() {
const isVisible = Tankopedia.use((state) => state.tab === TankopediaTab.Tank);
return (
<div className={styles.sandbox}>
<Canvas shadows className={styles.canvas}>
<div className={styles.sandbox} data-visible={isVisible}>
<Canvas
shadows
className={styles.canvas}
frameloop={isVisible ? "always" : "never"}
>
{!import.meta.env.SSR && <Content />}
</Canvas>
</div>
@@ -4,6 +4,8 @@
display: flex;
.sandbox {
position: relative;
flex-grow: 1;
background-color: black;
}
@@ -11,6 +11,8 @@ import { characteristicsOrder } from "../../../../tankopedia/characteristicsOrde
import { computeCharacteristics } from "../../../../tankopedia/computeCharacteristics";
import styles from "./_index.module.css";
import { TankopediaSandbox } from "../../../../components/TankopediaSandbox";
import { TankopediaNavigation } from "../../../../components/TankopediaNavigation";
import { TankopediaCharacteristics } from "../../../../components/TankopediaCharacteristics";
interface PageProps {
id: string;
@@ -41,6 +43,8 @@ export const Page = withErrorWrapper(
<div className={styles.page}>
<div className={styles.sandbox}>
<TankopediaSandbox />
<TankopediaCharacteristics />
<TankopediaNavigation />
</div>
<div className={styles.stats}>
@@ -2,9 +2,13 @@ import type { Tank } from "@protos/tank";
import { Varuna } from "varuna";
import { createTankState } from "../tankopedia/createTankState";
import { type TankState } from "../tankopedia/tankState";
import { characteristicsOrder } from "../tankopedia/characteristicsOrder";
interface Tankopedia {
compare: TankopediaCompare;
tab: TankopediaTab;
groups: Record<string, boolean>;
protagonist: TankState;
}
@@ -14,7 +18,21 @@ export enum TankopediaCompare {
All,
}
export enum TankopediaTab {
Tank,
Characteristics,
}
const groups: Record<string, boolean> = {};
for (const { group } of characteristicsOrder) {
groups[group] = true;
}
export const Tankopedia = new Varuna<Tankopedia, Tank>((tank) => ({
compare: TankopediaCompare.TierAndClass,
tab: TankopediaTab.Characteristics,
groups,
protagonist: createTankState(tank),
}));
@@ -29,7 +29,7 @@ export interface CharacteristicRenderConfig {
}) => ReactNode;
}
type CharacteristicsOrder = {
export type CharacteristicsGroup = {
group: string;
order: (
| CharacteristicRenderConfig
@@ -37,9 +37,9 @@ type CharacteristicsOrder = {
toy: string;
}
)[];
}[];
};
export const characteristicsOrder: CharacteristicsOrder = [
export const characteristicsOrder: CharacteristicsGroup[] = [
{
group: "meta",
order: [