@using System.Globalization
@if (_isDragging)
{
}
@code {
[Parameter] public double Value { get; set; }
[Parameter] public EventCallback ValueChanged { get; set; }
[Parameter] public double Min { get; set; } = 0;
[Parameter] public double Max { get; set; } = 100;
[Parameter] public double Step { get; set; } = 1;
[Parameter] public string Label { get; set; } = "";
[Parameter] public int Size { get; set; } = 50;
[Parameter] public MudBlazor.Color Color { get; set; } = MudBlazor.Color.Primary;
[Parameter] public bool HoldValue { get; set; } = false;
private bool _isDragging = false;
private double _lastMouseY = 0;
private double _dragValue = 0;
private double GetNormalizedValue()
{
// Use drag value during dragging if HoldValue is enabled, otherwise use current Value
double currentValue = (_isDragging && HoldValue) ? _dragValue : Value;
return Math.Max(0, Math.Min(1, (currentValue - Min) / (Max - Min)));
}
private string GetDisplayLabel()
{
// Show drag value during dragging if HoldValue is enabled, otherwise use provided Label
if (_isDragging && HoldValue)
{
return _dragValue.ToString("F0");
}
return Label;
}
private string GetKnobColor()
{
return Color switch
{
MudBlazor.Color.Primary => "var(--mud-palette-primary)",
MudBlazor.Color.Secondary => "var(--mud-palette-secondary)",
MudBlazor.Color.Success => "var(--mud-palette-success)",
MudBlazor.Color.Warning => "var(--mud-palette-warning)",
MudBlazor.Color.Error => "var(--mud-palette-error)",
MudBlazor.Color.Info => "var(--mud-palette-info)",
_ => "var(--mud-palette-primary)"
};
}
private string GetBackgroundStrokeDashArray()
{
double circumference = 2 * Math.PI * 35; // radius = 35
double maxArc = circumference * 0.75; // 270 degrees
return $"{maxArc} {circumference}";
}
private string GetStrokeDashArray()
{
double circumference = 2 * Math.PI * 35; // radius = 35
double maxArc = circumference * 0.75; // 270 degrees
double valueArc = maxArc * GetNormalizedValue();
return $"{valueArc} {circumference}";
}
private string GetStrokeDashOffset()
{
// No offset needed - arc should start from the beginning and grow
return "0";
}
private string GetPointerX()
{
double angle = -225 + (270 * GetNormalizedValue()); // -225 to +45 degrees (centered on vertical)
double radians = angle * Math.PI / 180;
double x = 50 + (25 * Math.Cos(radians)); // radius = 25 for pointer
return x.ToString("F1", CultureInfo.InvariantCulture);
}
private string GetPointerY()
{
double angle = -225 + (270 * GetNormalizedValue());
double radians = angle * Math.PI / 180;
double y = 50 + (25 * Math.Sin(radians));
return y.ToString("F1", CultureInfo.InvariantCulture);
}
private async Task OnMouseDown(MouseEventArgs e)
{
_isDragging = true;
_lastMouseY = e.ClientY;
_dragValue = Value; // Initialize drag value with current value
// Add global mouse event handlers using Blazor's event handling
StateHasChanged();
}
private async Task OnGlobalMouseMove(MouseEventArgs e)
{
if (_isDragging)
{
await UpdateValueFromMouse(e);
}
}
private async Task OnGlobalMouseUp(MouseEventArgs e)
{
if (_isDragging && HoldValue)
{
// If HoldValue is enabled, emit the final value now
if (Math.Abs(_dragValue - Value) > 0.001)
{
await ValueChanged.InvokeAsync(_dragValue);
}
}
_isDragging = false;
StateHasChanged();
}
private async Task UpdateValueFromMouse(MouseEventArgs e)
{
// Calculate vertical delta from last mouse position
double deltaY = _lastMouseY - e.ClientY; // Inverted: up = positive, down = negative
_lastMouseY = e.ClientY;
// Sensitivity factor (pixels to move for full range)
double sensitivity = 100.0;
// Calculate change in normalized value based on vertical movement
double normalizedDelta = deltaY / sensitivity;
double currentNormalized = GetNormalizedValue();
double newNormalized = Math.Max(0, Math.Min(1, currentNormalized + normalizedDelta));
// Convert to actual value
double newValue = Min + (newNormalized * (Max - Min));
// Apply step
if (Step > 0)
{
newValue = Math.Round(newValue / Step) * Step;
}
if (Math.Abs(newValue - (HoldValue ? _dragValue : Value)) > 0.001) // Avoid unnecessary updates
{
if (HoldValue)
{
// Update drag value only, don't emit ValueChanged yet
_dragValue = newValue;
StateHasChanged(); // Update visual only
}
else
{
// Original behavior - emit ValueChanged immediately
Value = newValue;
await ValueChanged.InvokeAsync(Value);
}
}
}
}