Files
IGP-Fan-Reference/docs/cooldown-button.md
T

112 lines
4.6 KiB
Markdown
Raw Blame History

This file contains invisible Unicode characters
This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Cooldown Button Component
A square Blazor button with a 12-second cooldown animation: when clicked, the button greys out and a circular transparency wedge dials open clockwise, progressively revealing the normal button state underneath.
---
## Visual Design
The button has two visual states:
**Idle state** A solid square button with the project's standard `--paper` background and `--primary` border. Hover inverts or brightens the paper. Click triggers a brief scale-down.
**Cooldown state** The native button fades to `opacity: 0` while an absolutely-positioned overlay covers it. The overlay uses a `conic-gradient` CSS mask to create a "dialling open" effect. A number in the centre shows the remaining seconds.
---
## Core Technique: Conic-Gradient Mask
The cooldown reveal is accomplished with a **conic-gradient mask** applied to the overlay `<div>`:
```
mask-image: conic-gradient(
transparent 0deg,
transparent {angle}deg,
#000 {angle}deg,
#000 360deg
);
```
- **`transparent`** lets the button underneath show through (revealed area)
- **`#000` (black)** fully masks the overlay, making it visible (greyed-out area)
At `angle = 0deg`, transparent covers nothing and the mask is entirely black → the overlay is fully opaque (button completely greyed out).
At `angle = 360deg`, transparent covers the full circle and black covers nothing → the overlay is fully transparent (button completely visible).
The angle animates linearly from 0 to 360 over the cooldown duration. Because `conic-gradient` starts at the 12 o'clock position and sweeps clockwise, the reveal begins at the top of the button and rotates around, like a clock hand or a dial opening.
---
## Implementation Architecture
### Component Parameters
| Parameter | Type | Default | Description |
|------------------|-------------------|---------|------------------------------------|
| `ChildContent` | `RenderFragment` | `null` | Text or content inside the button |
| `OnClick` | `EventCallback` | — | Fired when the button is clicked |
| `CooldownSeconds`| `int` | `12` | Duration of the cooldown in seconds|
| `Size` | `int` | `120` | Width and height in pixels (square)|
### Timer Loop
A `System.Timers.Timer` fires every ~33ms (≈30fps) during the cooldown:
```
OnTick:
elapsed = UtcNow - startTime
if elapsed >= CooldownSeconds → end cooldown, dispose timer
_elapsedAngle = (elapsed / CooldownSeconds) * 360
_remainingSeconds = CooldownSeconds - (int)elapsed
InvokeAsync(StateHasChanged)
```
On each tick, `_elapsedAngle` is written into the overlay's inline `style` attribute, causing Blazor to re-render the `mask-image`. The timer is disposed in `Dispose()` to prevent leaks.
### Disposal
The component implements `IDisposable` to clean up the timer when the component is removed from the render tree. This follows the same pattern used by `SearchDialogComponent` and `BuildChartComponent` elsewhere in the codebase.
---
## CSS Masking Details
Two vendor-prefixed properties are set to ensure cross-browser support:
```
mask-image: conic-gradient(...);
-webkit-mask-image: conic-gradient(...);
```
The overlay uses `pointer-events: none` and `user-select: none` so that interaction passes through to the button underneath (which is disabled and transparent).
An `rgba(22, 22, 24, 0.82)` semi-transparent background on the overlay produces the greyed-out appearance. The mask controls *where* this background is visible.
---
## Usage on the Home Page
The component is added to `Pages/Pages/Home/HomePage.razor` inside the first `PaperComponent`:
```razor
<CooldownButtonComponent CooldownSeconds="12"
Size="120"
OnClick="OnCooldownClick">
Click Me
</CooldownButtonComponent>
```
The `OnCooldownClick` handler in the page's `@code` block currently returns `Task.CompletedTask` (a no-op). This is the extension point where real work (e.g. triggering a game action, calling an API, showing a toast) would go.
---
## Adapting the Component
- **Change cooldown duration** set `CooldownSeconds` to any positive integer.
- **Change button size** set `Size` to any pixel dimension (button remains square).
- **Custom content** pass any Blazor markup as `ChildContent` (text, icons, spinners).
- **Handle the click** attach a handler to `OnClick` that returns `Task` or `void`.
The `--cooldown-size` CSS custom property is set inline on the wrapper so that the label, overlay, and button all scale together.