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 { TankopediaSkinSwitcher } from "../TankopediaSkinSwitcher";
|
||||||
import { TankSandboxLoader } from "../TankSandboxLoader";
|
import { TankSandboxLoader } from "../TankSandboxLoader";
|
||||||
import { TankStaticArmor, type ThicknessRange } from "../TankStaticArmor";
|
import { TankStaticArmor, type ThicknessRange } from "../TankStaticArmor";
|
||||||
|
import { Thicknesses } from "../Thicknesses";
|
||||||
import styles from "./index.module.css";
|
import styles from "./index.module.css";
|
||||||
|
|
||||||
type TankSandboxProps = MaybeSkeletonComponentProps & {
|
type TankSandboxProps = MaybeSkeletonComponentProps & {
|
||||||
@@ -176,6 +177,7 @@ export function TankopediaSandbox({
|
|||||||
|
|
||||||
<TankSandboxLoader />
|
<TankSandboxLoader />
|
||||||
<TankopediaSkinSwitcher />
|
<TankopediaSkinSwitcher />
|
||||||
|
<Thicknesses thicknessRange={thicknessRange} />
|
||||||
<TankopediaAntagonistControl
|
<TankopediaAntagonistControl
|
||||||
antagonistCharacteristics={antagonistCharacteristics}
|
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