187 lines
8.9 KiB
TypeScript
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 acolor = `${skin.color}64`;
|
|
const [selectorOpen, setSelectorOpen] = useState(false);
|
|
const [canClose, setCanClose] = useState(true);
|
|
const [localSkin, setLocalSkin] = useState(skin);
|
|
|
|
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}
|
|
/>
|
|
</>
|
|
);
|
|
}
|