mirror of
https://github.com/blitzkit/blitzkit.git
synced 2026-10-22 19:19:52 +02:00
feat: module selection
This commit is contained in:
1 parent
6a280982d0
commit
581d889e9b
1 file changed
+138
-52
@@ -1,16 +1,20 @@
|
||||
import { asset } from "@blitzkit/core";
|
||||
import { CaretRightIcon, UpdateIcon } from "@radix-ui/react-icons";
|
||||
import { asset, TIER_ROMAN_NUMERALS } from "@blitzkit/core";
|
||||
import { literals } from "@blitzkit/i18n";
|
||||
import { CaretRightIcon, CaretUpIcon, UpdateIcon } from "@radix-ui/react-icons";
|
||||
import {
|
||||
Box,
|
||||
Button,
|
||||
Dialog,
|
||||
Flex,
|
||||
IconButton,
|
||||
Popover,
|
||||
Skeleton,
|
||||
Text,
|
||||
type ButtonProps,
|
||||
} from "@radix-ui/themes";
|
||||
import { useRef, useState } from "react";
|
||||
import { MixerScene } from "../../../components/MixerScene";
|
||||
import { ModuleButton } from "../../../components/ModuleButtons/ModuleButton";
|
||||
import { PageWrapper } from "../../../components/PageWrapper";
|
||||
import { ScreenshotButton } from "../../../components/ScreenshotButton";
|
||||
import {
|
||||
@@ -18,6 +22,7 @@ import {
|
||||
type TankSearchProps,
|
||||
} from "../../../components/TankSearch";
|
||||
import { awaitableModelDefinitions } from "../../../core/awaitables/modelDefinitions";
|
||||
import { awaitableTankDefinitions } from "../../../core/awaitables/tankDefinitions";
|
||||
import { curateMixer } from "../../../core/blitzkit/curateMixer";
|
||||
import {
|
||||
LocaleProvider,
|
||||
@@ -31,6 +36,7 @@ import type { MaybeSkeletonComponentProps } from "../../../types/maybeSkeletonCo
|
||||
const modelDefinition = await awaitableModelDefinitions.then(
|
||||
({ models }) => models[1]
|
||||
);
|
||||
const tankDefinitions = await awaitableTankDefinitions;
|
||||
|
||||
export function Page({
|
||||
locale,
|
||||
@@ -50,57 +56,131 @@ export function Page({
|
||||
);
|
||||
}
|
||||
|
||||
function ModuleButton({
|
||||
skeleton,
|
||||
tank,
|
||||
onSelect,
|
||||
...props
|
||||
}: Omit<ButtonProps, "onSelect"> &
|
||||
type ModuleButtonProps = Omit<ButtonProps, "onSelect"> &
|
||||
MaybeSkeletonComponentProps & {
|
||||
tank: number;
|
||||
onSelect: TankSearchProps["onSelect"];
|
||||
}) {
|
||||
turret?: number;
|
||||
gun?: number;
|
||||
};
|
||||
|
||||
function ModuleChooser({
|
||||
skeleton,
|
||||
tank,
|
||||
onSelect,
|
||||
turret,
|
||||
gun,
|
||||
...props
|
||||
}: ModuleButtonProps) {
|
||||
const { strings } = useLocale();
|
||||
const [open, setOpen] = useState(false);
|
||||
const tankDefinition = tankDefinitions.tanks[tank];
|
||||
const turretDefinition = tankDefinition.turrets.find((t) => t.id === turret);
|
||||
const gunDefinition = turretDefinition?.guns.find((g) => g.id === gun);
|
||||
const subSelector =
|
||||
turret !== undefined &&
|
||||
(tankDefinition.turrets.length > 1 ||
|
||||
(gun !== undefined && turretDefinition!.guns.length > 1));
|
||||
|
||||
return (
|
||||
<Dialog.Root open={open} onOpenChange={setOpen}>
|
||||
<Dialog.Trigger>
|
||||
<Button
|
||||
variant="surface"
|
||||
size="4"
|
||||
style={{ overflow: "clip" }}
|
||||
{...props}
|
||||
>
|
||||
{skeleton ? (
|
||||
<Skeleton width="4em" height="2em" />
|
||||
) : (
|
||||
<img
|
||||
<Flex>
|
||||
<Dialog.Root open={open} onOpenChange={setOpen}>
|
||||
<Dialog.Trigger>
|
||||
<Button
|
||||
variant="surface"
|
||||
size={{ initial: "3", md: "4" }}
|
||||
style={{
|
||||
overflow: "clip",
|
||||
borderTopRightRadius: subSelector ? 0 : undefined,
|
||||
borderBottomRightRadius: subSelector ? 0 : undefined,
|
||||
}}
|
||||
{...props}
|
||||
>
|
||||
{skeleton ? (
|
||||
<Skeleton width="4em" height="2em" />
|
||||
) : (
|
||||
<img
|
||||
style={{
|
||||
width: "4em",
|
||||
height: "4em",
|
||||
objectFit: "contain",
|
||||
}}
|
||||
src={asset(`icons/tanks/big/${tank}.webp`)}
|
||||
/>
|
||||
)}
|
||||
</Button>
|
||||
</Dialog.Trigger>
|
||||
|
||||
<Dialog.Content>
|
||||
<Dialog.Title>
|
||||
{strings.website.common.tank_search.select_dialog_title}
|
||||
</Dialog.Title>
|
||||
|
||||
<TankSearch
|
||||
compact
|
||||
onSelect={(tank) => {
|
||||
onSelect?.(tank);
|
||||
setOpen(false);
|
||||
}}
|
||||
/>
|
||||
</Dialog.Content>
|
||||
</Dialog.Root>
|
||||
|
||||
{subSelector && (
|
||||
<Popover.Root>
|
||||
<Popover.Trigger>
|
||||
<IconButton
|
||||
variant="surface"
|
||||
size={{ initial: "3", md: "4" }}
|
||||
style={{
|
||||
width: "4em",
|
||||
height: "4em",
|
||||
objectFit: "contain",
|
||||
overflow: "clip",
|
||||
borderTopLeftRadius: 0,
|
||||
borderBottomLeftRadius: 0,
|
||||
marginLeft: -1,
|
||||
}}
|
||||
src={asset(`icons/tanks/big/${tank}.webp`)}
|
||||
/>
|
||||
)}
|
||||
</Button>
|
||||
</Dialog.Trigger>
|
||||
{...props}
|
||||
>
|
||||
<CaretUpIcon />
|
||||
</IconButton>
|
||||
</Popover.Trigger>
|
||||
|
||||
<Dialog.Content>
|
||||
<Dialog.Title>
|
||||
{strings.website.common.tank_search.select_dialog_title}
|
||||
</Dialog.Title>
|
||||
<Popover.Content>
|
||||
<Flex direction="column" gap="2">
|
||||
{tankDefinition.turrets.length > 1 && (
|
||||
<Flex gap="2">
|
||||
{tankDefinition.turrets.map((turret) => (
|
||||
<ModuleButton
|
||||
key={turret.id}
|
||||
module="turret"
|
||||
discriminator={TIER_ROMAN_NUMERALS[turret.tier]}
|
||||
/>
|
||||
))}
|
||||
</Flex>
|
||||
)}
|
||||
|
||||
<TankSearch
|
||||
compact
|
||||
onSelect={(tank) => {
|
||||
onSelect?.(tank);
|
||||
setOpen(false);
|
||||
}}
|
||||
/>
|
||||
</Dialog.Content>
|
||||
</Dialog.Root>
|
||||
{turretDefinition && turretDefinition.guns.length > 1 && (
|
||||
<Flex gap="2">
|
||||
{turretDefinition.guns.map((gun) => (
|
||||
<ModuleButton
|
||||
key={gun.id}
|
||||
module="gun"
|
||||
discriminator={TIER_ROMAN_NUMERALS[gun.tier]}
|
||||
secondaryDiscriminator={
|
||||
<Text style={{ fontSize: "0.75em" }}>
|
||||
{literals(strings.common.units.mm, {
|
||||
value: gun.shells[0].caliber.toFixed(0),
|
||||
})}
|
||||
</Text>
|
||||
}
|
||||
/>
|
||||
))}
|
||||
</Flex>
|
||||
)}
|
||||
</Flex>
|
||||
</Popover.Content>
|
||||
</Popover.Root>
|
||||
)}
|
||||
</Flex>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -113,9 +193,12 @@ function ModuleSeparator() {
|
||||
}
|
||||
|
||||
function Content({ skeleton }: MaybeSkeletonComponentProps) {
|
||||
const hull = Mixer.use((state) => state.hull);
|
||||
const turretId = Mixer.use((state) => state.turret.tank.id);
|
||||
const gunId = Mixer.use((state) => state.gun.tank.id);
|
||||
const hullTankId = Mixer.use((state) => state.hull);
|
||||
const turretTankId = Mixer.use((state) => state.turret.tank.id);
|
||||
const turretTurretId = Mixer.use((state) => state.turret.turret.id);
|
||||
const gunTankId = Mixer.use((state) => state.gun.tank.id);
|
||||
const gunTurretId = Mixer.use((state) => state.gun.turret.id);
|
||||
const gunGunId = Mixer.use((state) => state.gun.gun.id);
|
||||
const canvas = useRef<HTMLCanvasElement>(null!);
|
||||
const { strings } = useLocale();
|
||||
|
||||
@@ -136,7 +219,7 @@ function Content({ skeleton }: MaybeSkeletonComponentProps) {
|
||||
>
|
||||
<Flex gap="2">
|
||||
<ScreenshotButton
|
||||
name={hull.slug}
|
||||
name={hullTankId.slug}
|
||||
canvas={canvas}
|
||||
highContrast
|
||||
variant="surface"
|
||||
@@ -160,9 +243,9 @@ function Content({ skeleton }: MaybeSkeletonComponentProps) {
|
||||
</Flex>
|
||||
|
||||
<Flex gap="2" align="center">
|
||||
<ModuleButton
|
||||
<ModuleChooser
|
||||
skeleton={skeleton}
|
||||
tank={hull.id}
|
||||
tank={hullTankId.id}
|
||||
onSelect={(tank) => {
|
||||
Mixer.mutate((draft) => {
|
||||
draft.hull = tank;
|
||||
@@ -170,9 +253,10 @@ function Content({ skeleton }: MaybeSkeletonComponentProps) {
|
||||
}}
|
||||
/>
|
||||
<ModuleSeparator />
|
||||
<ModuleButton
|
||||
<ModuleChooser
|
||||
skeleton={skeleton}
|
||||
tank={turretId}
|
||||
tank={turretTankId}
|
||||
turret={turretTurretId}
|
||||
onSelect={(tank) => {
|
||||
Mixer.mutate((draft) => {
|
||||
draft.turret = {
|
||||
@@ -183,9 +267,11 @@ function Content({ skeleton }: MaybeSkeletonComponentProps) {
|
||||
}}
|
||||
/>
|
||||
<ModuleSeparator />
|
||||
<ModuleButton
|
||||
<ModuleChooser
|
||||
skeleton={skeleton}
|
||||
tank={gunId}
|
||||
tank={gunTankId}
|
||||
turret={gunTurretId}
|
||||
gun={gunGunId}
|
||||
onSelect={(tank) => {
|
||||
Mixer.mutate((draft) => {
|
||||
draft.gun = {
|
||||
|
||||
Reference in new issue
Block a user