mirror of
https://github.com/ramvignesh-b/pi-ku.git
synced 2026-05-04 08:56:52 +00:00
107 lines
3.5 KiB
TypeScript
107 lines
3.5 KiB
TypeScript
import { CampfireIcon, FlameIcon, XCircleIcon } from "@phosphor-icons/react";
|
|
import { useEffect, useState } from "react";
|
|
|
|
interface BurnModalProps {
|
|
burnLetter: () => void;
|
|
isBurning: boolean;
|
|
setShowBurnModal: (show: boolean) => void;
|
|
setRevealState: (state: "sealed" | "revealed" | "burning" | "burned") => void;
|
|
}
|
|
|
|
export function BurnModal({
|
|
burnLetter,
|
|
isBurning,
|
|
setShowBurnModal,
|
|
setRevealState,
|
|
}: BurnModalProps) {
|
|
const [flameOn, setFlameOn] = useState(0);
|
|
const [rotate, setRotate] = useState(0);
|
|
const [burnClicked, setBurnClicked] = useState(false);
|
|
useEffect(() => {
|
|
if (!burnClicked) return;
|
|
if (flameOn === 100) {
|
|
setRevealState("sealed");
|
|
burnLetter();
|
|
}
|
|
const interval = setInterval(() => {
|
|
setFlameOn((prev) => prev + 1);
|
|
setRotate(Math.random() * 4 - 2);
|
|
}, 100);
|
|
return () => clearInterval(interval);
|
|
}, [burnClicked, flameOn, setRevealState, burnLetter]);
|
|
|
|
const burnStyle = flameOn < 30 ? "" : `contrast(${flameOn / 30})`;
|
|
|
|
return (
|
|
<div className="modal modal-open modal-middle bg-base-100/20 backdrop-blur-md">
|
|
<div
|
|
className={`modal-box flex flex-col items-center gap-4 py-8 text-center transition-all duration-200 ease-in-out ${burnClicked ? "animate-[pulse_15s_linear_infinite]" : ""}`}
|
|
style={
|
|
{
|
|
transform: `rotate(${rotate}deg)`,
|
|
} as React.CSSProperties
|
|
}
|
|
>
|
|
<button
|
|
type="button"
|
|
className="btn btn-sm btn-circle btn-ghost absolute right-2 top-2"
|
|
onClick={() => setShowBurnModal(false)}
|
|
aria-label="Close"
|
|
>
|
|
<XCircleIcon size={18} weight="bold" />
|
|
</button>
|
|
<CampfireIcon
|
|
size={48}
|
|
weight="duotone"
|
|
className="text-error animate-pulse"
|
|
/>
|
|
<h3 className="font-serif text-2xl">
|
|
Are you ready to burn this letter?
|
|
</h3>
|
|
<p className="text-sm font-sans text-base-content/80 mt-4">
|
|
Some words are meant to be unsaid, but they don't have to linger
|
|
forever.
|
|
<br />
|
|
Let the echoes of your unsaid be finally released.
|
|
</p>
|
|
<div className="mt-4 font-sans text-sm">
|
|
<span className="text-error">Press</span> and{" "}
|
|
<span className="text-error">hold</span> the{" "}
|
|
<span className="text-amber-300">flame</span> to proceed.
|
|
</div>
|
|
<div className="modal-action w-full justify-center gap-3 mt-2">
|
|
<div
|
|
className="absolute -mt-2 w-28 h-28 radial-progress pointer-events-none text-amber-200/60"
|
|
style={
|
|
{ "--value": flameOn, filter: burnStyle } as React.CSSProperties
|
|
}
|
|
role="progressbar"
|
|
></div>
|
|
<button
|
|
type="button"
|
|
className={`btn btn-error btn-dashed btn-circle w-24 h-24`}
|
|
style={
|
|
{
|
|
filter: burnStyle,
|
|
cursor: burnClicked ? "grabbing" : "grab",
|
|
} as React.CSSProperties
|
|
}
|
|
onMouseDown={() => setBurnClicked(true)}
|
|
onMouseUp={() => {
|
|
setFlameOn(0);
|
|
setBurnClicked(false);
|
|
}}
|
|
disabled={isBurning}
|
|
>
|
|
{isBurning ? (
|
|
<span className="loading loading-spinner loading-xs" />
|
|
) : (
|
|
<FlameIcon size={54} weight="duotone" />
|
|
)}
|
|
</button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
);
|
|
}
|