mirror of
https://github.com/blitzkit/blitzkit.git
synced 2026-11-10 11:16:10 +01:00
thicknesses
This commit is contained in:
4 files changed
+200
-204
No files matched your search
@@ -1,204 +0,0 @@
|
||||
import { literals } from "@blitzkit/i18n/src/literals";
|
||||
import { EyeClosedIcon, EyeOpenIcon, ReloadIcon } from "@radix-ui/react-icons";
|
||||
import { Button, Checkbox, Flex, Text } from "@radix-ui/themes";
|
||||
import { Suspense } from "react";
|
||||
import { useLocale } from "../../../hooks/useLocale";
|
||||
import { Tankopedia } from "../../../stores/tankopedia";
|
||||
import { TankopediaPersistent } from "../../../stores/tankopediaPersistent";
|
||||
import { TankopediaDisplay } from "../../../stores/tankopediaPersistent/constants";
|
||||
import type { MaybeSkeletonComponentProps } from "../../../types/maybeSkeletonComponentProps";
|
||||
import type { ThicknessRange } from "../../TankStaticArmor";
|
||||
import { DynamicArmorSwitcher } from "./DynamicArmorSwitcher";
|
||||
|
||||
type ThicknessesProps = MaybeSkeletonComponentProps & {
|
||||
thicknessRange: ThicknessRange;
|
||||
};
|
||||
|
||||
export function Thicknesses({ thicknessRange, skeleton }: ThicknessesProps) {
|
||||
const display = Tankopedia.use((state) => state.display);
|
||||
const showExternalModules = TankopediaPersistent.use(
|
||||
(state) => state.showExternalModules,
|
||||
);
|
||||
const showSpacedArmor = TankopediaPersistent.use(
|
||||
(state) => state.showSpacedArmor,
|
||||
);
|
||||
const showPrimaryArmor = TankopediaPersistent.use(
|
||||
(state) => state.showPrimaryArmor,
|
||||
);
|
||||
const editStatic = Tankopedia.use((state) => state.editStatic);
|
||||
const { strings } = useLocale();
|
||||
|
||||
return (
|
||||
<Flex
|
||||
position="absolute"
|
||||
right="0"
|
||||
top="50%"
|
||||
mr="4"
|
||||
style={{
|
||||
transform: `translate(${display === TankopediaDisplay.StaticArmor ? "0" : "calc(100% + var(--space-4))"}, -50%)`,
|
||||
userSelect: "none",
|
||||
transitionDuration: "200ms",
|
||||
}}
|
||||
direction="column"
|
||||
gap="5"
|
||||
align="end"
|
||||
>
|
||||
<Flex direction="column" align="end" gap="3">
|
||||
<Flex
|
||||
height="64px"
|
||||
gap="2"
|
||||
style={{ opacity: showPrimaryArmor ? 1 : 0.5, cursor: "pointer" }}
|
||||
onClick={() => {
|
||||
TankopediaPersistent.mutate((draft) => {
|
||||
draft.showPrimaryArmor = !draft.showPrimaryArmor;
|
||||
});
|
||||
Tankopedia.mutate((draft) => {
|
||||
draft.highlightArmor = undefined;
|
||||
});
|
||||
}}
|
||||
>
|
||||
<Flex direction="column" align="end" justify="between">
|
||||
<Text color="gray" size="1">
|
||||
{literals(strings.common.units.mm, {
|
||||
value: (thicknessRange.value * 1.5).toFixed(0),
|
||||
})}
|
||||
</Text>
|
||||
<Text size="1">
|
||||
{strings.website.tools.tankopedia.sandbox.static.primary}
|
||||
</Text>
|
||||
<Text color="gray" size="1">
|
||||
{literals(strings.common.units.mm, { value: 0 })}
|
||||
</Text>
|
||||
</Flex>
|
||||
|
||||
<Flex
|
||||
align="center"
|
||||
width="1rem"
|
||||
style={{
|
||||
borderRadius: "var(--radius-full)",
|
||||
overflow: "hidden",
|
||||
background: `linear-gradient(rgb(64, 0, 0), rgb(255, 0, 0) 33%, rgb(255, 255, 0) 66%, rgb(0, 255, 0))`,
|
||||
color: "black",
|
||||
}}
|
||||
>
|
||||
{showPrimaryArmor ? <EyeOpenIcon /> : <EyeClosedIcon />}
|
||||
</Flex>
|
||||
</Flex>
|
||||
|
||||
<Flex
|
||||
height="64px"
|
||||
gap="2"
|
||||
style={{ opacity: showSpacedArmor ? 1 : 0.5, cursor: "pointer" }}
|
||||
onClick={() => {
|
||||
TankopediaPersistent.mutate((draft) => {
|
||||
draft.showSpacedArmor = !draft.showSpacedArmor;
|
||||
});
|
||||
Tankopedia.mutate((draft) => {
|
||||
draft.highlightArmor = undefined;
|
||||
});
|
||||
}}
|
||||
>
|
||||
<Flex direction="column" align="end" justify="between">
|
||||
<Text color="gray" size="1">
|
||||
{literals(strings.common.units.mm, {
|
||||
value: thicknessRange.value.toFixed(0),
|
||||
})}
|
||||
</Text>
|
||||
<Text size="1">
|
||||
{strings.website.tools.tankopedia.sandbox.static.spaced}
|
||||
</Text>
|
||||
<Text color="gray" size="1">
|
||||
{literals(strings.common.units.mm, { value: 0 })}
|
||||
</Text>
|
||||
</Flex>
|
||||
|
||||
<Flex
|
||||
align="center"
|
||||
width="1rem"
|
||||
style={{
|
||||
borderRadius: "var(--radius-full)",
|
||||
overflow: "hidden",
|
||||
background: `linear-gradient(rgb(32, 0, 225), rgb(255, 0, 255))`,
|
||||
}}
|
||||
>
|
||||
{showSpacedArmor ? <EyeOpenIcon /> : <EyeClosedIcon />}
|
||||
</Flex>
|
||||
</Flex>
|
||||
|
||||
<Flex
|
||||
gap="2"
|
||||
style={{ opacity: showExternalModules ? 1 : 0.5, cursor: "pointer" }}
|
||||
onClick={() => {
|
||||
TankopediaPersistent.mutate((draft) => {
|
||||
draft.showExternalModules = !draft.showExternalModules;
|
||||
});
|
||||
Tankopedia.mutate((draft) => {
|
||||
draft.highlightArmor = undefined;
|
||||
});
|
||||
}}
|
||||
>
|
||||
<Flex direction="column" align="end" justify="between">
|
||||
<Text size="1">
|
||||
{strings.website.tools.tankopedia.sandbox.static.external}
|
||||
</Text>
|
||||
</Flex>
|
||||
|
||||
<Flex
|
||||
justify="center"
|
||||
width="1rem"
|
||||
height="1rem"
|
||||
style={{
|
||||
borderRadius: "var(--radius-full)",
|
||||
overflow: "hidden",
|
||||
backgroundColor: "#c0c0c0",
|
||||
color: "black",
|
||||
}}
|
||||
>
|
||||
{showExternalModules ? <EyeOpenIcon /> : <EyeClosedIcon />}
|
||||
</Flex>
|
||||
</Flex>
|
||||
</Flex>
|
||||
|
||||
<Flex direction="column" align="end" gap="2">
|
||||
<Flex align="center" gap="2">
|
||||
<Flex
|
||||
gap="2"
|
||||
style={{ cursor: "pointer" }}
|
||||
onClick={() => {
|
||||
Tankopedia.mutate((draft) => {
|
||||
draft.editStatic = !draft.editStatic;
|
||||
draft.highlightArmor = undefined;
|
||||
});
|
||||
}}
|
||||
>
|
||||
<Text size="1">
|
||||
{strings.website.tools.tankopedia.sandbox.static.edit.label}
|
||||
</Text>
|
||||
<Checkbox variant="classic" size="2" checked={editStatic} />
|
||||
</Flex>
|
||||
</Flex>
|
||||
|
||||
<Button
|
||||
color="red"
|
||||
size="1"
|
||||
variant="ghost"
|
||||
onClick={() => {
|
||||
Tankopedia.mutate((draft) => {
|
||||
draft.editStatic = false;
|
||||
draft.model = Tankopedia.initial.model;
|
||||
});
|
||||
}}
|
||||
>
|
||||
<ReloadIcon />
|
||||
{strings.website.tools.tankopedia.sandbox.static.edit.reset}
|
||||
</Button>
|
||||
</Flex>
|
||||
|
||||
{!skeleton && (
|
||||
<Suspense>
|
||||
<DynamicArmorSwitcher />
|
||||
</Suspense>
|
||||
)}
|
||||
</Flex>
|
||||
);
|
||||
}
|
||||
@@ -26,6 +26,7 @@ import { TankopediaOptions } from "../TankopediaOptions";
|
||||
import { TankopediaSkinSwitcher } from "../TankopediaSkinSwitcher";
|
||||
import { TankSandboxLoader } from "../TankSandboxLoader";
|
||||
import { TankStaticArmor, type ThicknessRange } from "../TankStaticArmor";
|
||||
import { Thicknesses } from "../Thicknesses";
|
||||
import styles from "./index.module.css";
|
||||
|
||||
type TankSandboxProps = MaybeSkeletonComponentProps & {
|
||||
@@ -176,6 +177,7 @@ export function TankopediaSandbox({
|
||||
|
||||
<TankSandboxLoader />
|
||||
<TankopediaSkinSwitcher />
|
||||
<Thicknesses thicknessRange={thicknessRange} />
|
||||
<TankopediaAntagonistControl
|
||||
antagonistCharacteristics={antagonistCharacteristics}
|
||||
/>
|
||||
|
||||
@@ -0,0 +1,67 @@
|
||||
.thicknesses {
|
||||
pointer-events: auto;
|
||||
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: flex-end;
|
||||
gap: var(--space-4);
|
||||
|
||||
position: absolute;
|
||||
right: var(--space-4);
|
||||
top: 50%;
|
||||
|
||||
user-select: none;
|
||||
transition: var(--duration-brief);
|
||||
|
||||
/* slides off the right edge when the static armor view isn't active */
|
||||
transform: translate(calc(100% + var(--space-4)), -50%);
|
||||
|
||||
&[data-visible="true"] {
|
||||
transform: translate(0, -50%);
|
||||
}
|
||||
}
|
||||
|
||||
.toggle {
|
||||
display: flex;
|
||||
gap: var(--space-3);
|
||||
|
||||
padding: 0;
|
||||
border: none;
|
||||
background: none;
|
||||
font: inherit;
|
||||
|
||||
cursor: pointer;
|
||||
opacity: 0.5;
|
||||
|
||||
&[data-visible="true"] {
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
&[data-scaled="true"] {
|
||||
height: 4rem;
|
||||
}
|
||||
}
|
||||
|
||||
.labels {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: flex-end;
|
||||
justify-content: space-between;
|
||||
}
|
||||
|
||||
.swatch {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
|
||||
width: 1rem;
|
||||
border-radius: var(--radius-max);
|
||||
overflow: hidden;
|
||||
|
||||
color: black;
|
||||
|
||||
/* the unscaled swatch is just an icon-sized dot */
|
||||
.toggle:not([data-scaled="true"]) & {
|
||||
height: 1rem;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,131 @@
|
||||
import { literals } from "@blitzkit/i18n/src/literals";
|
||||
import { EyeClosedIcon, EyeOpenIcon } from "@radix-ui/react-icons";
|
||||
import { useCallback, type ReactNode } from "react";
|
||||
import { useStrings } from "../../hooks/useStrings";
|
||||
import { Tankopedia } from "../../stores/tankopedia";
|
||||
import { TankopediaPersistent } from "../../stores/tankopediaPersistent";
|
||||
import { TankopediaDisplay } from "../../stores/tankopediaPersistent/constants";
|
||||
import type { ThicknessRange } from "../TankStaticArmor";
|
||||
import { Text } from "../Text";
|
||||
import styles from "./index.module.css";
|
||||
|
||||
interface LayerToggleProps {
|
||||
visible: boolean;
|
||||
label: ReactNode;
|
||||
/** Thickness labels for the top and bottom of the scale */
|
||||
range?: [ReactNode, ReactNode];
|
||||
swatch: string;
|
||||
onToggle(): void;
|
||||
}
|
||||
|
||||
function LayerToggle({
|
||||
visible,
|
||||
label,
|
||||
range,
|
||||
swatch,
|
||||
onToggle,
|
||||
}: LayerToggleProps) {
|
||||
return (
|
||||
<button
|
||||
className={styles.toggle}
|
||||
data-visible={visible}
|
||||
data-scaled={range !== undefined}
|
||||
onClick={onToggle}
|
||||
>
|
||||
<div className={styles.labels}>
|
||||
{range && (
|
||||
<Text size="minor" lowContrast>
|
||||
{range[0]}
|
||||
</Text>
|
||||
)}
|
||||
|
||||
<Text size="minor">{label}</Text>
|
||||
|
||||
{range && (
|
||||
<Text size="minor" lowContrast>
|
||||
{range[1]}
|
||||
</Text>
|
||||
)}
|
||||
</div>
|
||||
|
||||
<div className={styles.swatch} style={{ background: swatch }}>
|
||||
{visible ? <EyeOpenIcon /> : <EyeClosedIcon />}
|
||||
</div>
|
||||
</button>
|
||||
);
|
||||
}
|
||||
|
||||
export interface ThicknessesProps {
|
||||
thicknessRange: ThicknessRange;
|
||||
}
|
||||
|
||||
export function Thicknesses({ thicknessRange }: ThicknessesProps) {
|
||||
const strings = useStrings();
|
||||
const display = Tankopedia.use((state) => state.loaders.display.loaded);
|
||||
const showControls = Tankopedia.use((state) => state.showControls);
|
||||
const showPrimaryArmor = TankopediaPersistent.use(
|
||||
(state) => state.showPrimaryArmor,
|
||||
);
|
||||
const showSpacedArmor = TankopediaPersistent.use(
|
||||
(state) => state.showSpacedArmor,
|
||||
);
|
||||
const showExternalModules = TankopediaPersistent.use(
|
||||
(state) => state.showExternalModules,
|
||||
);
|
||||
const { element } = strings.tanks.shot_card;
|
||||
const mm = useCallback(
|
||||
(value: number) =>
|
||||
literals(strings.common.units.mm, { value: value.toFixed(0) }),
|
||||
[strings],
|
||||
);
|
||||
const clearHighlight = useCallback(() => {
|
||||
Tankopedia.mutate((draft) => {
|
||||
draft.highlightArmor = undefined;
|
||||
});
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<div
|
||||
className={styles.thicknesses}
|
||||
data-visible={showControls && display === TankopediaDisplay.StaticArmor}
|
||||
>
|
||||
<LayerToggle
|
||||
visible={showPrimaryArmor}
|
||||
label={element.primary}
|
||||
range={[mm(thicknessRange.value * 1.5), mm(0)]}
|
||||
swatch="linear-gradient(rgb(64, 0, 0), rgb(255, 0, 0) 33%, rgb(255, 255, 0) 66%, rgb(0, 255, 0))"
|
||||
onToggle={() => {
|
||||
TankopediaPersistent.mutate((draft) => {
|
||||
draft.showPrimaryArmor = !draft.showPrimaryArmor;
|
||||
});
|
||||
clearHighlight();
|
||||
}}
|
||||
/>
|
||||
|
||||
<LayerToggle
|
||||
visible={showSpacedArmor}
|
||||
label={element.spaced}
|
||||
range={[mm(thicknessRange.value), mm(0)]}
|
||||
swatch="linear-gradient(rgb(32, 0, 225), rgb(255, 0, 255))"
|
||||
onToggle={() => {
|
||||
TankopediaPersistent.mutate((draft) => {
|
||||
draft.showSpacedArmor = !draft.showSpacedArmor;
|
||||
});
|
||||
clearHighlight();
|
||||
}}
|
||||
/>
|
||||
|
||||
<LayerToggle
|
||||
visible={showExternalModules}
|
||||
label={element.external}
|
||||
swatch="#c0c0c0"
|
||||
onToggle={() => {
|
||||
TankopediaPersistent.mutate((draft) => {
|
||||
draft.showExternalModules = !draft.showExternalModules;
|
||||
});
|
||||
clearHighlight();
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
Reference in new issue
Block a user