Files
IGP-Fan-Reference/Components/Inputs/CooldownButtonComponent.razor
T
2026-06-04 17:12:17 -04:00

146 lines
4.0 KiB
Plaintext

@implements IDisposable
<div class="cooldown-wrap" style="--cooldown-size: @(Size)px">
<button class="cooldown-btn"
disabled="@_isCooldown"
@onclick="HandleClick"
@onclick:preventDefault="_isCooldown">
<span class="cooldown-btn-text">@ChildContent</span>
</button>
@if (_isCooldown)
{
<div class="cooldown-overlay" style="--angle: @(_elapsedAngle)deg">
<span class="cooldown-label">@_remainingSeconds</span>
</div>
}
</div>
<style>
.cooldown-wrap {
position: relative;
display: inline-flex;
width: var(--cooldown-size, 120px);
height: var(--cooldown-size, 120px);
}
.cooldown-btn {
width: 100%;
height: 100%;
border: 2px solid var(--primary);
border-radius: 12px;
background: var(--paper);
color: var(--text-primary, #eee);
font-weight: 700;
font-size: 0.9rem;
cursor: pointer;
padding: 12px;
display: flex;
align-items: center;
justify-content: center;
transition: background 0.15s, border-color 0.15s;
line-height: 1.3;
font-family: inherit;
}
.cooldown-btn:hover:not(:disabled) {
background: var(--paper-hover);
border-color: var(--primary-hover);
}
.cooldown-btn:active:not(:disabled) {
transform: scale(0.97);
}
.cooldown-btn:disabled {
cursor: default;
}
.cooldown-btn-text {
pointer-events: none;
}
.cooldown-overlay {
position: absolute;
inset: 0;
border-radius: 12px;
display: flex;
align-items: center;
justify-content: center;
pointer-events: none;
user-select: none;
}
.cooldown-overlay::before {
content: '';
position: absolute;
inset: 0;
border-radius: inherit;
background: rgba(22, 22, 24, 0.45);
mask-image: conic-gradient(transparent 0deg, transparent var(--angle), #000 var(--angle), #000 360deg);
-webkit-mask-image: conic-gradient(transparent 0deg, transparent var(--angle), #000 var(--angle), #000 360deg);
}
.cooldown-label {
font-size: 1.6rem;
font-weight: 900;
color: #999;
text-shadow: 0 1px 3px rgba(0, 0, 0, 0.6);
pointer-events: none;
position: relative;
z-index: 1;
}
</style>
@code {
[Parameter] public RenderFragment? ChildContent { get; set; }
[Parameter] public EventCallback OnClick { get; set; }
[Parameter] public int CooldownSeconds { get; set; } = 12;
[Parameter] public int Size { get; set; } = 120;
private bool _isCooldown;
private int _elapsedAngle;
private int _remainingSeconds;
private DateTime _startTime;
private System.Timers.Timer? _timer;
private async Task HandleClick()
{
if (_isCooldown) return;
await OnClick.InvokeAsync(null);
StartCooldown();
}
private void StartCooldown()
{
_isCooldown = true;
_startTime = DateTime.UtcNow;
_elapsedAngle = 0;
_remainingSeconds = CooldownSeconds;
_timer = new System.Timers.Timer(33);
_timer.Elapsed += OnTick;
_timer.AutoReset = true;
_timer.Enabled = true;
}
private void OnTick(object? sender, System.Timers.ElapsedEventArgs e)
{
var elapsed = (DateTime.UtcNow - _startTime).TotalSeconds;
if (elapsed >= CooldownSeconds)
{
_isCooldown = false;
_timer?.Stop();
_timer?.Dispose();
_timer = null;
InvokeAsync(StateHasChanged);
return;
}
_elapsedAngle = (int)(elapsed / CooldownSeconds * 360);
_remainingSeconds = CooldownSeconds - (int)elapsed;
InvokeAsync(StateHasChanged);
}
public void Dispose()
{
if (_timer != null)
{
_timer.Stop();
_timer.Dispose();
_timer = null;
}
}
}