You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
155 lines
3.4 KiB
TypeScript
155 lines
3.4 KiB
TypeScript
import { useState, type CSSProperties } from 'react';
|
|
import { useT, t } from '../i18n';
|
|
|
|
// ---- Types ----
|
|
|
|
interface WanderingNPCPopupProps {
|
|
npcName: string;
|
|
message: string;
|
|
cost: number;
|
|
heroGold: number;
|
|
onAccept: () => void;
|
|
onDecline: () => void;
|
|
}
|
|
|
|
// ---- Styles ----
|
|
|
|
const overlayStyle: CSSProperties = {
|
|
position: 'absolute',
|
|
inset: 0,
|
|
zIndex: 800,
|
|
display: 'flex',
|
|
alignItems: 'center',
|
|
justifyContent: 'center',
|
|
pointerEvents: 'auto',
|
|
};
|
|
|
|
const backdropStyle: CSSProperties = {
|
|
position: 'absolute',
|
|
inset: 0,
|
|
backgroundColor: 'rgba(0, 0, 0, 0.55)',
|
|
};
|
|
|
|
const cardStyle: CSSProperties = {
|
|
position: 'relative',
|
|
width: '85%',
|
|
maxWidth: 320,
|
|
backgroundColor: 'rgba(15, 15, 25, 0.96)',
|
|
border: '2px solid rgba(218, 165, 32, 0.4)',
|
|
borderRadius: 16,
|
|
padding: '20px 18px 16px',
|
|
textAlign: 'center',
|
|
animation: 'wander-npc-pop 0.3s ease-out',
|
|
};
|
|
|
|
const titleStyle: CSSProperties = {
|
|
fontSize: 16,
|
|
fontWeight: 700,
|
|
color: '#daa520',
|
|
marginBottom: 8,
|
|
};
|
|
|
|
const messageStyle: CSSProperties = {
|
|
fontSize: 13,
|
|
color: '#ccc',
|
|
lineHeight: 1.5,
|
|
marginBottom: 16,
|
|
};
|
|
|
|
const costStyle: CSSProperties = {
|
|
fontSize: 14,
|
|
fontWeight: 700,
|
|
color: '#ffd700',
|
|
marginBottom: 16,
|
|
};
|
|
|
|
const btnRow: CSSProperties = {
|
|
display: 'flex',
|
|
gap: 10,
|
|
justifyContent: 'center',
|
|
};
|
|
|
|
const btnBase: CSSProperties = {
|
|
flex: 1,
|
|
padding: '10px 0',
|
|
borderRadius: 10,
|
|
fontSize: 13,
|
|
fontWeight: 700,
|
|
border: 'none',
|
|
cursor: 'pointer',
|
|
transition: 'background-color 150ms ease',
|
|
};
|
|
|
|
// ---- Component ----
|
|
|
|
export function WanderingNPCPopup({
|
|
npcName,
|
|
message,
|
|
cost,
|
|
heroGold,
|
|
onAccept,
|
|
onDecline,
|
|
}: WanderingNPCPopupProps) {
|
|
const tr = useT();
|
|
const [pending, setPending] = useState(false);
|
|
const canAfford = heroGold >= cost;
|
|
|
|
const handleAccept = () => {
|
|
if (!canAfford || pending) return;
|
|
setPending(true);
|
|
onAccept();
|
|
};
|
|
|
|
return (
|
|
<>
|
|
<style>{`
|
|
@keyframes wander-npc-pop {
|
|
0% { opacity: 0; transform: scale(0.92); }
|
|
100% { opacity: 1; transform: scale(1); }
|
|
}
|
|
`}</style>
|
|
<div style={overlayStyle}>
|
|
<div style={backdropStyle} onClick={onDecline} />
|
|
<div style={cardStyle}>
|
|
<div style={titleStyle}>{npcName}</div>
|
|
<div style={messageStyle}>{message}</div>
|
|
<div style={costStyle}>
|
|
{t(tr.giveGoldForItem, { cost })}
|
|
</div>
|
|
{!canAfford && (
|
|
<div style={{ fontSize: 11, color: '#ff6666', marginBottom: 10 }}>
|
|
{tr.notEnoughGold} ({heroGold}/{cost})
|
|
</div>
|
|
)}
|
|
<div style={btnRow}>
|
|
<button
|
|
style={{
|
|
...btnBase,
|
|
backgroundColor: canAfford && !pending
|
|
? 'rgba(68, 200, 68, 0.25)'
|
|
: 'rgba(100, 100, 100, 0.15)',
|
|
color: canAfford && !pending ? '#88dd88' : '#666',
|
|
}}
|
|
onClick={handleAccept}
|
|
disabled={!canAfford || pending}
|
|
>
|
|
{pending ? tr.giving : tr.accept}
|
|
</button>
|
|
<button
|
|
style={{
|
|
...btnBase,
|
|
backgroundColor: 'rgba(200, 68, 68, 0.15)',
|
|
color: '#ff8888',
|
|
}}
|
|
onClick={onDecline}
|
|
disabled={pending}
|
|
>
|
|
{tr.decline}
|
|
</button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</>
|
|
);
|
|
}
|