153 lines
6.7 KiB
TypeScript
153 lines
6.7 KiB
TypeScript
'use client';
|
|
|
|
import { ActionButton } from '@/components/ui/action-button';
|
|
import { Badge } from '@/components/ui/badge';
|
|
import { ScrollArea } from '@/components/ui/scroll-area';
|
|
import { Separator } from '@/components/ui/separator';
|
|
import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from '@/components/ui/tooltip';
|
|
import { AlertCircle, Wand2, Plus, Minus } from 'lucide-react';
|
|
import { ENCHANTMENT_EFFECTS, calculateEffectCapacityCost } from '@/lib/game/data/enchantment-effects';
|
|
import type { EffectSelectorProps } from './types';
|
|
import { DebugName } from '@/components/game/debug/debug-context';
|
|
|
|
export function EffectSelector({
|
|
selectedEquipmentType,
|
|
selectedEffects,
|
|
availableEffects,
|
|
incompatibleEffects,
|
|
enchantingLevel,
|
|
efficiencyBonus,
|
|
designProgress,
|
|
addEffect,
|
|
removeEffect,
|
|
getIncompatibilityReason,
|
|
}: EffectSelectorProps) {
|
|
return (
|
|
<DebugName name="EffectSelector">
|
|
<>
|
|
{enchantingLevel < 1 ? (
|
|
<div className="text-center text-[var(--text-muted)] py-8">
|
|
<Wand2 className="w-12 h-12 mx-auto mb-2 opacity-50 text-[var(--text-disabled)]" />
|
|
<p>Learn Enchanting skill to design enchantments</p>
|
|
</div>
|
|
) : designProgress ? (
|
|
<div className="space-y-2">
|
|
<div className="text-sm text-[var(--text-secondary)]">Design in progress...</div>
|
|
{designProgress.effects.map(eff => {
|
|
const def = ENCHANTMENT_EFFECTS[eff.effectId];
|
|
return (
|
|
<div key={eff.effectId} className="flex justify-between text-sm text-[var(--text-primary)]">
|
|
<span>{def?.name} x{eff.stacks}</span>
|
|
<span className="text-[var(--text-muted)]">{eff.capacityCost} cap</span>
|
|
</div>
|
|
);
|
|
})}
|
|
</div>
|
|
) : !selectedEquipmentType ? (
|
|
<div className="text-center text-[var(--text-muted)] py-8">
|
|
Select an equipment type first
|
|
</div>
|
|
) : (
|
|
<>
|
|
<ScrollArea className="h-48 mb-4">
|
|
<div className="space-y-2">
|
|
{/* Compatible Effects */}
|
|
{availableEffects.map(effect => {
|
|
const selected = selectedEffects.find(e => e.effectId === effect.id);
|
|
const _cost = calculateEffectCapacityCost(effect.id, (selected?.stacks || 0) + 1, efficiencyBonus);
|
|
|
|
return (
|
|
<div
|
|
key={effect.id}
|
|
className={`p-2 rounded border transition-all
|
|
${selected
|
|
? 'border-[var(--mana-stellar)] bg-[var(--mana-stellar)]/10'
|
|
: 'border-[var(--border-default)] bg-[var(--bg-sunken)]/50'
|
|
}`}
|
|
>
|
|
<div className="flex justify-between items-start">
|
|
<div className="flex-1">
|
|
<div className="text-sm font-semibold text-[var(--text-primary)]">{effect.name}</div>
|
|
<div className="text-xs text-[var(--text-muted)]">{effect.description}</div>
|
|
<div className="text-xs text-[var(--text-disabled)] mt-1">
|
|
Cost: {effect.baseCapacityCost} cap | Max: {effect.maxStacks}
|
|
</div>
|
|
</div>
|
|
<div className="flex gap-1">
|
|
{selected && (
|
|
<ActionButton
|
|
size="sm"
|
|
variant="ghost"
|
|
className="h-6 w-6 p-0"
|
|
onClick={() => removeEffect(effect.id)}
|
|
>
|
|
<Minus className="w-3 h-3" />
|
|
</ActionButton>
|
|
)}
|
|
<ActionButton
|
|
size="sm"
|
|
variant="ghost"
|
|
className="h-6 w-6 p-0"
|
|
onClick={() => addEffect(effect.id)}
|
|
disabled={!selected && selectedEffects.length >= 5}
|
|
>
|
|
<Plus className="w-3 h-3" />
|
|
</ActionButton>
|
|
</div>
|
|
</div>
|
|
{selected && (
|
|
<Badge variant="outline" className="mt-1 text-xs border-[var(--mana-stellar)] text-[var(--mana-stellar)]">
|
|
{selected.stacks}/{effect.maxStacks}
|
|
</Badge>
|
|
)}
|
|
</div>
|
|
);
|
|
})}
|
|
|
|
{/* Incompatible Effects - Requirement: greyed-out "Unavailable" section with tooltips */}
|
|
{incompatibleEffects.length > 0 && (
|
|
<>
|
|
<Separator className="bg-[var(--border-subtle)] my-2" />
|
|
<div className="text-xs font-semibold text-[var(--text-disabled)] uppercase tracking-wider mb-2">
|
|
Unavailable
|
|
</div>
|
|
{incompatibleEffects.map(effect => {
|
|
const reason = getIncompatibilityReason(effect);
|
|
|
|
return (
|
|
<TooltipProvider key={effect.id}>
|
|
<Tooltip>
|
|
<TooltipTrigger asChild>
|
|
<div
|
|
className="p-2 rounded border border-[var(--border-subtle)] bg-[var(--bg-sunken)]/30 opacity-50 cursor-not-allowed"
|
|
>
|
|
<div className="flex justify-between items-start">
|
|
<div className="flex-1">
|
|
<div className="text-sm font-semibold text-[var(--text-disabled)]">{effect.name}</div>
|
|
<div className="text-xs text-[var(--text-disabled)]">{effect.description}</div>
|
|
</div>
|
|
<AlertCircle size={14} className="text-[var(--text-disabled)]" />
|
|
</div>
|
|
</div>
|
|
</TooltipTrigger>
|
|
<TooltipContent className="bg-[var(--bg-elevated)] border-[var(--border-default)] text-[var(--text-primary)]">
|
|
<p className="font-semibold">Incompatible Effect</p>
|
|
<p className="text-xs text-[var(--text-muted)] mt-1">{reason}</p>
|
|
</TooltipContent>
|
|
</Tooltip>
|
|
</TooltipProvider>
|
|
);
|
|
})}
|
|
</>
|
|
)}
|
|
</div>
|
|
</ScrollArea>
|
|
</>
|
|
)}
|
|
</>
|
|
</DebugName>
|
|
);
|
|
}
|
|
|
|
EffectSelector.displayName = 'EffectSelector';
|