module wrapping

This commit is contained in:
tresabhi committed 2026-05-19 13:20:54 -05:00
1 parent 402d054b30
commit d66d25a261
7 files changed
+87 -43

No files matched your search

@@ -1,5 +1,6 @@
.link { .link {
text-decoration-color: currentColor; text-decoration-color: currentColor;
cursor: pointer;
display: inline; display: inline;
align-items: center; align-items: center;
@@ -1,11 +1,14 @@
.price { .price {
font-size: var(--font-size-minor);
display: flex; display: flex;
gap: var(--space-3); gap: var(--space-3);
height: 1em;
.entry { .entry {
display: block; display: block;
line-height: 1; line-height: 1;
font-size: inherit;
.wrapper { .wrapper {
display: flex; display: flex;
@@ -17,6 +17,12 @@ export function Price({ price, className, ...props }: PriceProps) {
{price.price_list.map((entry, index) => ( {price.price_list.map((entry, index) => (
<PriceEntry key={index} entry={entry} /> <PriceEntry key={index} entry={entry} />
))} ))}
{price.price_list.length === 0 && (
<Text className={styles.entry} lowContrast>
Free
</Text>
)}
</div> </div>
); );
} }
@@ -27,8 +33,6 @@ interface PriceEntryProps {
function PriceEntry({ entry }: PriceEntryProps) { function PriceEntry({ entry }: PriceEntryProps) {
if (entry.premium_time_price !== undefined) { if (entry.premium_time_price !== undefined) {
console.log(entry);
throw new Error("Unsupported price type"); throw new Error("Unsupported price type");
} }
@@ -60,7 +64,7 @@ function PriceInternal({ icon, value }: PriceInternalProps) {
const locale = useLocale(); const locale = useLocale();
return ( return (
<Text weight="light" size="minor" lowContrast className={styles.entry}> <Text weight="light" lowContrast className={styles.entry}>
<div className={styles.wrapper}> <div className={styles.wrapper}>
<img className={styles.icon} src={icon} /> <img className={styles.icon} src={icon} />
{formatCompact(locale, value)} {formatCompact(locale, value)}
@@ -60,40 +60,57 @@
.line { .line {
display: flex; display: flex;
flex-direction: column;
gap: var(--space-3); gap: var(--space-3);
.wrapper { .line-wrap {
display: flex; display: flex;
flex-direction: column;
align-items: center; align-items: center;
gap: var(--space-2);
.stage { gap: var(--space-3);
position: relative;
padding: var(--space-3); .wrap-marker {
padding-bottom: var(--font-size-minor);
font-size: 2em; svg {
.icon {
width: 1em; width: 1em;
height: 1em; height: 1em;
transform: translate(var(--space-1), var(--space-1));
} }
}
.tier { .wrapper {
position: absolute; display: flex;
left: var(--space-2); flex-direction: column;
top: var(--space-2); align-items: center;
} gap: var(--space-2);
.price { .stage {
position: absolute; position: relative;
left: 50%;
bottom: var(--space-2); padding: var(--space-3);
transform: translateX(-50%);
font-size: 2em;
.icon {
width: 1em;
height: 1em;
transform: translate(var(--space-1), var(--space-1));
}
.tier {
position: absolute;
left: var(--space-2);
top: var(--space-2);
}
.price {
position: absolute;
left: 50%;
bottom: var(--space-2);
transform: translateX(-50%);
}
} }
} }
} }
@@ -1,10 +1,14 @@
import { romanize } from "@blitzkit/core"; import { romanize } from "@blitzkit/core";
import type { BlitzStaticEquipmentPresetComponent_EquipmentSlot } from "@protos/blitz_static_equipment_preset_component"; import type { BlitzStaticEquipmentPresetComponent_EquipmentSlot } from "@protos/blitz_static_equipment_preset_component";
import { Grade } from "@protos/blitz_static_standard_grades_enum"; import { Grade } from "@protos/blitz_static_standard_grades_enum";
import type { StandardPrice } from "@protos/blitz_static_standard_price"; import { StandardPrice } from "@protos/blitz_static_standard_price";
import type { UpgradeLine } from "@protos/blitz_static_tank_upgrade_line"; import type { UpgradeLine } from "@protos/blitz_static_tank_upgrade_line";
import type { StageParameters } from "@protos/blitz_static_tank_upgrade_single_stage"; import type { StageParameters } from "@protos/blitz_static_tank_upgrade_single_stage";
import { DoubleArrowDownIcon, DoubleArrowUpIcon } from "@radix-ui/react-icons"; import {
CornerBottomLeftIcon,
DoubleArrowLeftIcon,
DoubleArrowRightIcon,
} from "@radix-ui/react-icons";
import { chunk } from "lodash-es"; import { chunk } from "lodash-es";
import { useMemo } from "react"; import { useMemo } from "react";
import { equipmentColumns } from "../../config/equipment"; import { equipmentColumns } from "../../config/equipment";
@@ -12,6 +16,7 @@ import { catImages } from "../../config/images";
import { import {
alternativeLines, alternativeLines,
isAlternativeLine, isAlternativeLine,
maxModulesPerRow,
originalLineName, originalLineName,
} from "../../config/modules"; } from "../../config/modules";
import { useEquipment } from "../../hooks/useEquipment"; import { useEquipment } from "../../hooks/useEquipment";
@@ -21,7 +26,7 @@ import { useUpgradePreset } from "../../hooks/useUpgradePreset";
import { Tankopedia } from "../../stores/tankopedia"; import { Tankopedia } from "../../stores/tankopedia";
import { Button } from "../Button"; import { Button } from "../Button";
import { Heading } from "../Heading"; import { Heading } from "../Heading";
import { IconButton } from "../IconButton"; import { Link } from "../Link";
import { Price } from "../Price"; import { Price } from "../Price";
import { Section } from "../Section"; import { Section } from "../Section";
import { Text } from "../Text"; import { Text } from "../Text";
@@ -167,7 +172,7 @@ function Modules() {
<div className={styles.header}> <div className={styles.header}>
<Heading size="4">{strings.tanks.loadout.modules}</Heading> <Heading size="4">{strings.tanks.loadout.modules}</Heading>
<IconButton <Link
onClick={() => { onClick={() => {
Tankopedia.mutate((draft) => { Tankopedia.mutate((draft) => {
for (const line of tank.tank!.upgrade_lines) { for (const line of tank.tank!.upgrade_lines) {
@@ -178,13 +183,12 @@ function Modules() {
} }
}); });
}} }}
color="tomato" color="gray"
variant="ghost"
> >
<DoubleArrowDownIcon /> <DoubleArrowLeftIcon />
</IconButton> </Link>
<IconButton <Link
onClick={() => { onClick={() => {
Tankopedia.mutate((draft) => { Tankopedia.mutate((draft) => {
for (const line of tank.tank!.upgrade_lines) { for (const line of tank.tank!.upgrade_lines) {
@@ -197,11 +201,10 @@ function Modules() {
} }
}); });
}} }}
color="jade" color="gray"
variant="ghost"
> >
<DoubleArrowUpIcon /> <DoubleArrowRightIcon />
</IconButton> </Link>
</div> </div>
<div className={styles.lines}> <div className={styles.lines}>
@@ -254,8 +257,23 @@ function Line({ name, lines }: LineProps) {
function LineInner({ name, lines }: LineProps) { function LineInner({ name, lines }: LineProps) {
const line = lines.find((line) => line.name === name)!; const line = lines.find((line) => line.name === name)!;
return line.stages.map((stage, index) => ( return chunk(line.stages, maxModulesPerRow).map((chunk, wrapIndex) => (
<LineElement key={index} index={index} lineName={name} stage={stage} /> <div className={styles["line-wrap"]}>
{wrapIndex > 0 && (
<Text size="minor" className={styles["wrap-marker"]} lowContrast>
<CornerBottomLeftIcon />
</Text>
)}
{chunk.map((stage, elementIndex) => (
<LineElement
key={elementIndex}
index={wrapIndex * maxModulesPerRow + elementIndex}
lineName={name}
stage={stage}
/>
))}
</div>
)); ));
} }
@@ -297,6 +315,8 @@ function LineElement({ index, lineName, stage }: LineElementProps) {
price = override.price; price = override.price;
} }
price ??= StandardPrice.create();
let isSelected: boolean; let isSelected: boolean;
if (isAlternativeLine(lineName)) { if (isAlternativeLine(lineName)) {
@@ -395,7 +415,7 @@ function LineElement({ index, lineName, stage }: LineElementProps) {
</Text> </Text>
</Button> </Button>
{price && <Price className={styles.price} price={price} />} {<Price className={styles.price} price={price} />}
</div> </div>
); );
} }
@@ -1,3 +1,5 @@
export const maxModulesPerRow = 5;
export const alternativeLines: Record<string, string> = { export const alternativeLines: Record<string, string> = {
guns: "alternative_guns", guns: "alternative_guns",
}; };
@@ -2,7 +2,6 @@
import "@radix-ui/colors/violet-dark.css"; import "@radix-ui/colors/violet-dark.css";
import { api } from "../../../../api/dynamic"; import { api } from "../../../../api/dynamic";
import { mixStaticPaths } from "../../../../astro/mixStaticPaths"; import { mixStaticPaths } from "../../../../astro/mixStaticPaths";
import ColorScale from "../../../../components/ColorScale.astro";
import BaseLayout from "../../../../layouts/BaseLayout.astro"; import BaseLayout from "../../../../layouts/BaseLayout.astro";
import { getStaticPaths as _getStaticPaths } from "../index.astro"; import { getStaticPaths as _getStaticPaths } from "../index.astro";
import { Page } from "./_index"; import { Page } from "./_index";
@@ -20,8 +19,6 @@ const { id, locale } = Astro.props;
--- ---
<BaseLayout {locale} accent="violet"> <BaseLayout {locale} accent="violet">
<ColorScale color="tomato" />
<ColorScale color="jade" />
<Page {locale} {id} client:load /> <Page {locale} {id} client:load />
</BaseLayout> </BaseLayout>