Files
app/app/components/skin_dialog.tsx

187 lines
8.9 KiB
TypeScript

import { Button, Input, Modal, Surface, Typography } from "@heroui/react";
import {
useEffect,
useState,
type ChangeEvent,
type Dispatch,
type SetStateAction,
} from "react";
import { AgentInfo, WeaponInfo } from "~/types/skin";
import { Float } from "./float";
import { SkinSelector } from "./skin_selector";
export function SkinDialog({
isOpen,
setIsOpen,
skin,
setSkin,
side,
}: {
isOpen: boolean;
setIsOpen: Dispatch<SetStateAction<boolean>>;
skin: WeaponInfo | AgentInfo;
setSkin: Dispatch<SetStateAction<WeaponInfo | AgentInfo>>;
side: "T" | "CT";
}) {
const [selectorOpen, setSelectorOpen] = useState(false);
const [canClose, setCanClose] = useState(true);
const [localSkin, setLocalSkin] = useState(skin);
const acolor = `${localSkin.color}64`;
const handleInputChange = (event: ChangeEvent<HTMLInputElement>) => {
if (localSkin instanceof AgentInfo) return;
const newSkin = new WeaponInfo(localSkin);
newSkin.pattern = event.currentTarget.valueAsNumber;
setLocalSkin(newSkin);
};
const applySkin = (side: "SIDE" | "BOTH") => {
setSkin(localSkin);
}
useEffect(() => {
if (!selectorOpen) {
setTimeout(() => {
setCanClose(true);
}, 100);
}
}, [selectorOpen]);
useEffect(() => {
if (isOpen) {
setLocalSkin(skin);
}
}, [isOpen, skin]);
return (
<>
<Modal isOpen={isOpen}>
<Modal.Backdrop
variant="blur"
shouldCloseOnInteractOutside={(_element) => {
if (canClose) {
setIsOpen(false);
}
return canClose;
}}
>
<Modal.Container size="lg">
<Modal.Dialog>
<Modal.CloseTrigger
onPress={() => {
if (!selectorOpen) {
setIsOpen(false);
}
}}
/>
<Modal.Body>
<div className="flex flex-col gap-2">
<Typography.Heading
level={4}
className="py-1 text-wrap"
>
{localSkin.display}
</Typography.Heading>
<Button
onClick={() => {
setCanClose(false);
setSelectorOpen(true);
}}
className="bg-mist-700 flex flex-col h-full p-0 rounded-none gap-0 hover:bg-indigo-800 active:bg-indigo-900"
>
<div className="flex flex-col w-full h-full">
<div
className="bg-linear-to-b pointer-events-none w-full h-full relative"
style={{
background: `linear-gradient(to bottom, #1f2937, ${acolor})`,
}}
>
<img
src={`/skins/${localSkin.name}_${localSkin instanceof WeaponInfo ? localSkin.paint_index : 0}.png`}
className="pointer-events-none object-contain object-bottom h-full"
alt="Weapon image"
/>
<Surface
className="rounded-full w-fit h-fit p-2 absolute bottom-2 right-2"
variant="secondary"
>
<svg
xmlns="http://www.w3.org/2000/svg"
fill="none"
viewBox="0 0 24 24"
strokeWidth={1.5}
stroke="currentColor"
className="size-6 m-0"
>
<path
strokeLinecap="round"
strokeLinejoin="round"
d="m16.862 4.487 1.687-1.688a1.875 1.875 0 1 1 2.652 2.652L10.582 16.07a4.5 4.5 0 0 1-1.897 1.13L6 18l.8-2.685a4.5 4.5 0 0 1 1.13-1.897l8.932-8.931Zm0 0L19.5 7.125M18 14v4.75A2.25 2.25 0 0 1 15.75 21H5.25A2.25 2.25 0 0 1 3 18.75V8.25A2.25 2.25 0 0 1 5.25 6H10"
/>
</svg>
</Surface>
</div>
<div
className={`h-1 w-full`}
style={{
background: `${localSkin.color}`,
}}
></div>
</div>
</Button>
{localSkin instanceof WeaponInfo && (
<Float
skin={localSkin}
setSkin={setLocalSkin}
acceptInput
hasLabel
/>
)}
{localSkin instanceof WeaponInfo && (
<div className="flex items-center gap-2">
<Typography.Paragraph>
Pattern:{" "}
</Typography.Paragraph>
<Input
className="flex-1"
id="pattern"
type="number"
step={1}
value={localSkin.pattern}
onChange={handleInputChange}
/>
</div>
)}
<Typography.Code>
Transaction id: {localSkin.id}
</Typography.Code>
<div className="flex gap-2 justify-end">
{localSkin instanceof WeaponInfo && (
<Button onClick={() => {applySkin("BOTH")}} variant="secondary">
Apply to Both
</Button>
)}
<Button onClick={() => {applySkin("SIDE")}}>
Apply
{localSkin instanceof WeaponInfo
? ` to ${side}`
: ""}
</Button>
</div>
</div>
</Modal.Body>
</Modal.Dialog>
</Modal.Container>
</Modal.Backdrop>
</Modal>
<SkinSelector
isOpen={selectorOpen}
setIsOpen={setSelectorOpen}
skin={localSkin}
setSkin={setLocalSkin}
side={side}
/>
</>
);
}