mirror of
https://github.com/blitzkit/blitzkit.git
synced 2026-10-21 18:57:03 +02:00
1 parent
3a60535564
commit
44e7cd7505
16 files changed
+206
-14
No files matched your search
@@ -193,6 +193,10 @@
|
||||
},
|
||||
|
||||
"tanks": {
|
||||
"loadout": {
|
||||
"modules": "Modules"
|
||||
},
|
||||
|
||||
"search": {
|
||||
"placeholder": "Search tanks..."
|
||||
}
|
||||
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 5.0 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 5.1 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 4.0 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 4.5 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 5.7 KiB |
@@ -90,3 +90,23 @@ import "@fontsource-variable/roboto-flex";
|
||||
--tank-card-min-width: 8rem;
|
||||
}
|
||||
</style>
|
||||
|
||||
<style is:global>
|
||||
.composable-radius {
|
||||
&[data-radius="1"] {
|
||||
border-radius: var(--space-3);
|
||||
}
|
||||
|
||||
&[data-radius="2"] {
|
||||
border-radius: var(--space-4);
|
||||
}
|
||||
|
||||
&[data-radius="3"] {
|
||||
border-radius: var(--space-5);
|
||||
}
|
||||
|
||||
&[data-radius="max"] {
|
||||
border-radius: var(--max-size);
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -7,7 +7,6 @@
|
||||
border-radius: var(--max-size);
|
||||
padding: var(--space-3) var(--space-4);
|
||||
|
||||
background-color: var(--accent-9);
|
||||
border: none;
|
||||
|
||||
font-family: var(--font-family-regular);
|
||||
@@ -23,4 +22,14 @@
|
||||
&[data-light-text="true"] {
|
||||
color: var(--white-a12);
|
||||
}
|
||||
|
||||
&[data-variant="solid"] {
|
||||
background-color: var(--accent-8);
|
||||
}
|
||||
|
||||
&[data-variant="surface"] {
|
||||
background-color: var(--accent-3);
|
||||
|
||||
outline: var(--space-1) solid var(--accent-4);
|
||||
}
|
||||
}
|
||||
@@ -5,7 +5,8 @@ import styles from "./index.module.css";
|
||||
|
||||
export interface ButtonProps extends ComponentProps<"button"> {
|
||||
color?: Color;
|
||||
variant?: "solid";
|
||||
radius?: `${1 | 2 | 3}` | "max";
|
||||
variant?: "solid" | "surface";
|
||||
}
|
||||
|
||||
export const LIGHT_TEXT_COLORS = new Set<Color>([
|
||||
@@ -28,14 +29,16 @@ export function Button({
|
||||
color,
|
||||
variant = "solid",
|
||||
style,
|
||||
radius = "max",
|
||||
...props
|
||||
}: ButtonProps) {
|
||||
return (
|
||||
<button
|
||||
className={classNames(styles.button, className)}
|
||||
className={classNames("composable-radius", styles.button, className)}
|
||||
data-color={color}
|
||||
data-variant={variant}
|
||||
data-light-text={variant === "solid"}
|
||||
data-radius={radius}
|
||||
style={{
|
||||
backgroundColor: color ? `var(--${color}-9)` : undefined,
|
||||
...style,
|
||||
|
||||
@@ -0,0 +1,7 @@
|
||||
.icon-button {
|
||||
padding: var(--space-3);
|
||||
|
||||
aspect-ratio: 1 / 1;
|
||||
|
||||
font-size: 2rem;
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
import { classNames } from "../../core/ui/classNames";
|
||||
import type { Color } from "../../core/ui/color";
|
||||
import { Button, type ButtonProps } from "../Button";
|
||||
import styles from "./index.module.css";
|
||||
|
||||
export interface IconButtonProps extends ButtonProps {
|
||||
color?: Color;
|
||||
variant?: "solid";
|
||||
}
|
||||
|
||||
export function IconButton({ className, ...props }: ButtonProps) {
|
||||
return (
|
||||
<Button
|
||||
className={classNames(className, styles["icon-button"])}
|
||||
{...props}
|
||||
/>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
.lines {
|
||||
display: flex;
|
||||
|
||||
gap: var(--space-3);
|
||||
|
||||
.line {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
|
||||
gap: var(--space-2);
|
||||
|
||||
.stage {
|
||||
img {
|
||||
width: 1em;
|
||||
height: 1em;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,110 @@
|
||||
import type { UpgradeLine } from "@blitzkit/closed/protos/game/proto/legacy/blitz_static_tank_upgrade_line";
|
||||
import { CaretDownIcon } from "@radix-ui/react-icons";
|
||||
import { api } from "../../core/api/dynamic";
|
||||
import { useAwait } from "../../hooks/useAwait";
|
||||
import { useStrings } from "../../hooks/useStrings";
|
||||
import { Tankopedia } from "../../stores/tankopedia";
|
||||
import { Heading } from "../Heading";
|
||||
import { IconButton } from "../IconButton";
|
||||
import { Section } from "../Section";
|
||||
import styles from "./index.module.css";
|
||||
|
||||
export function TankopediaLoadout() {
|
||||
const strings = useStrings();
|
||||
|
||||
const id = Tankopedia.use((state) => state.protagonist.id);
|
||||
const tank = useAwait(() => api.tank(id), `tank-${id}`);
|
||||
|
||||
return (
|
||||
<Section>
|
||||
<Heading size="3">{strings.tanks.loadout.modules}</Heading>
|
||||
|
||||
<div className={styles.lines}>
|
||||
{tank.tank!.upgrade_lines.map((line) => (
|
||||
<Line line={line} />
|
||||
))}
|
||||
</div>
|
||||
</Section>
|
||||
);
|
||||
}
|
||||
|
||||
interface LineProps {
|
||||
line: UpgradeLine;
|
||||
}
|
||||
|
||||
function Line({ line }: LineProps) {
|
||||
const upgrades = Tankopedia.use((state) => state.protagonist.upgrades);
|
||||
const id = Tankopedia.use((state) => state.protagonist.id);
|
||||
const tank = useAwait(() => api.tank(id), `tank-${id}`);
|
||||
|
||||
return (
|
||||
<div className={styles.line}>
|
||||
{line.stages.map((stage, index) => {
|
||||
const isSelected = upgrades[line.name] === index;
|
||||
|
||||
return (
|
||||
<>
|
||||
{index !== 0 && <CaretDownIcon width="1em" height="1em" />}
|
||||
|
||||
<IconButton
|
||||
variant={isSelected ? "solid" : "surface"}
|
||||
radius="1"
|
||||
className={styles.stage}
|
||||
onClick={() => {
|
||||
Tankopedia.mutate((draft) => {
|
||||
draft.protagonist.upgrades[line.name] = index;
|
||||
|
||||
for (const required of stage.required_upgrades) {
|
||||
for (const line of tank.tank!.upgrade_lines) {
|
||||
let i = 0;
|
||||
|
||||
for (const stage of line.stages) {
|
||||
if (stage.tech_name === required) {
|
||||
draft.protagonist.upgrades[line.name] = Math.max(
|
||||
draft.protagonist.upgrades[line.name],
|
||||
i,
|
||||
);
|
||||
}
|
||||
|
||||
i++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (const line of tank.tank!.upgrade_lines) {
|
||||
let i = draft.protagonist.upgrades[line.name];
|
||||
|
||||
while (i > 0) {
|
||||
const currentStage = line.stages[i];
|
||||
const valid = currentStage.required_upgrades.every(
|
||||
(required) => {
|
||||
for (const line of tank.tank!.upgrade_lines) {
|
||||
const idx = draft.protagonist.upgrades[line.name];
|
||||
|
||||
if (line.stages[idx]?.tech_name === required) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
},
|
||||
);
|
||||
|
||||
if (valid) break;
|
||||
|
||||
i--;
|
||||
}
|
||||
|
||||
draft.protagonist.upgrades[line.name] = i;
|
||||
}
|
||||
});
|
||||
}}
|
||||
>
|
||||
<img src={`/api/tanks/modules/${stage.stage_type}.webp`} />
|
||||
</IconButton>
|
||||
</>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -46,7 +46,7 @@ export abstract class AbstractAPI {
|
||||
return supported;
|
||||
}
|
||||
|
||||
@Cache<[string, string, boolean]>((...args) => args.join("-"))
|
||||
@Cache()
|
||||
async groupedGameStrings(locale: string, group: string, prefix: boolean) {
|
||||
this.assertLocale(locale);
|
||||
|
||||
@@ -64,9 +64,7 @@ export abstract class AbstractAPI {
|
||||
}
|
||||
}
|
||||
|
||||
export function Cache<Arguments extends unknown[]>(
|
||||
discriminator: (...args: Arguments) => string = (...args) => args.join("-"),
|
||||
) {
|
||||
export function Cache<Arguments extends unknown[]>(disableInDev = false) {
|
||||
const cache = new WeakMap<object, Map<string, Promise<unknown>>>();
|
||||
|
||||
return function (
|
||||
@@ -77,7 +75,7 @@ export function Cache<Arguments extends unknown[]>(
|
||||
const original = descriptor.value;
|
||||
|
||||
descriptor.value = function (...args: Arguments) {
|
||||
const key = discriminator(...args);
|
||||
const key = args.join("-");
|
||||
|
||||
let thisCache = cache.get(this);
|
||||
|
||||
@@ -86,7 +84,9 @@ export function Cache<Arguments extends unknown[]>(
|
||||
cache.set(this, thisCache);
|
||||
}
|
||||
|
||||
if (thisCache.has(key)) {
|
||||
const disabled = disableInDev && import.meta.env.DEV;
|
||||
|
||||
if (thisCache.has(key) && !disabled) {
|
||||
return thisCache.get(key);
|
||||
}
|
||||
|
||||
|
||||
@@ -186,7 +186,7 @@ export class ServerAPI extends AbstractAPI {
|
||||
return tiers;
|
||||
}
|
||||
|
||||
@Cache()
|
||||
@Cache(true)
|
||||
async strings(locale: string) {
|
||||
const localized = (await globbedStrings[
|
||||
`../../../../i18n/strings/${locale}.json`
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
import { Grade } from "@blitzkit/closed/protos/game/proto/legacy/blitz_static_standard_grades_enum";
|
||||
import { StageParameters_StageType } from "@blitzkit/closed/protos/game/proto/legacy/blitz_static_tank_upgrade_single_stage";
|
||||
import { useMemo } from "react";
|
||||
import { TankopediaLoadout } from "../../../../components/TankopediaLoadout";
|
||||
import { api } from "../../../../core/api/dynamic";
|
||||
import { characteristicsOrder } from "../../../../core/tankopedia/characteristicsOrder";
|
||||
import { computeCharacteristics } from "../../../../core/tankopedia/computeCharacteristics";
|
||||
@@ -48,7 +47,9 @@ function Content() {
|
||||
|
||||
<br />
|
||||
|
||||
<h2>upgrades</h2>
|
||||
<TankopediaLoadout />
|
||||
|
||||
{/* <h2>upgrades</h2>
|
||||
|
||||
{protagonistTank.tank!.upgrade_lines.map((line) => {
|
||||
return (
|
||||
@@ -145,7 +146,7 @@ function Content() {
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
})}
|
||||
})} */}
|
||||
|
||||
<h2>characteristics</h2>
|
||||
|
||||
|
||||
Reference in new issue
Block a user