Files
radzen-blazor/Radzen.Blazor/RadzenSpiderSeries.razor
Atanas Korchev 126b2d1efa RadzenSpiderChart (#2417)
* spider chart added

* code improved

* EventConsole added

* Pastel is the default color scheme

* Fix color schemes in Spider chart

* Update SpiderChart styles

---------

Co-authored-by: Ehab Hussein <me@ehabhussein.com>
Co-authored-by: Vladimir Enchev <vladimir.enchev@gmail.com>
Co-authored-by: yordanov <vasil@yordanov.info>
2026-01-22 14:27:01 +02:00

244 lines
7.1 KiB
Plaintext

@typeparam TItem
@inherits RadzenComponent
@implements IDisposable
@implements Radzen.Blazor.IRadzenSpiderSeries
@using Radzen.Blazor.Rendering
@using Microsoft.AspNetCore.Components.Rendering
@using Microsoft.AspNetCore.Components.Web
@code {
/// <summary>
/// Gets or sets the data for this series.
/// </summary>
[Parameter]
public IEnumerable<TItem> Data { get; set; } = Enumerable.Empty<TItem>();
/// <summary>
/// Gets or sets the property that provides the category values for this series.
/// </summary>
[Parameter]
public string CategoryProperty { get; set; } = string.Empty;
/// <summary>
/// Gets or sets the property that provides the values for this series.
/// </summary>
[Parameter]
public string ValueProperty { get; set; } = string.Empty;
/// <summary>
/// Gets or sets the series title.
/// </summary>
[Parameter]
public string Title { get; set; } = string.Empty;
/// <summary>
/// Gets or sets the value formatter function.
/// </summary>
[Parameter]
public Func<double, string> ValueFormatter { get; set; } = value => value.ToString("F1");
/// <summary>
/// Gets or sets the format string for values.
/// </summary>
[Parameter]
public string FormatString { get; set; } = string.Empty;
/// <summary>
/// Gets or sets the stroke width for this series.
/// </summary>
[Parameter]
public double StrokeWidth { get; set; } = 2;
/// <summary>
/// Gets or sets whether markers are visible for this series.
/// </summary>
[Parameter]
public bool MarkersVisible { get; set; } = true;
/// <summary>
/// Gets or sets the marker size for this series.
/// </summary>
[Parameter]
public double MarkerSize { get; set; } = 6;
[CascadingParameter]
public RadzenSpiderChart? Chart { get; set; }
private bool _registered;
/// <inheritdoc />
protected override async Task OnInitializedAsync()
{
await base.OnInitializedAsync();
if (Chart != null && !_registered)
{
_registered = true;
Chart.AddSeries(this);
}
}
/// <summary>
/// Disposes the component and removes it from the parent chart.
/// </summary>
public new void Dispose()
{
if (Chart != null && _registered)
{
_registered = false;
Chart.RemoveSeries(this);
}
base.Dispose();
}
/// <summary>
/// Gets whether the series is visible.
/// </summary>
public bool IsVisible { get; set; } = true;
/// <summary>
/// Gets the series index in the chart.
/// </summary>
internal int Index { get; set; }
int IRadzenSpiderSeries.Index { get => Index; set => Index = value; }
string IRadzenSpiderSeries.Title => Title;
bool IRadzenSpiderSeries.IsVisible { get => IsVisible; set => IsVisible = value; }
bool IRadzenSpiderSeries.MarkersVisible => MarkersVisible;
double IRadzenSpiderSeries.MarkerSize => MarkerSize;
double IRadzenSpiderSeries.StrokeWidth => StrokeWidth;
/// <summary>
/// Measures the legend text width for this series.
/// </summary>
public double MeasureLegend()
{
if (string.IsNullOrEmpty(Title))
{
return 0;
}
return TextMeasurer.TextWidth(Title);
}
double IRadzenSpiderSeries.MeasureLegend() => MeasureLegend();
IEnumerable<string> IRadzenSpiderSeries.GetCategories()
{
if (Data == null || string.IsNullOrEmpty(CategoryProperty))
{
return Enumerable.Empty<string>();
}
var categoryGetter = PropertyAccess.Getter<TItem, string>(CategoryProperty);
return Data.Select(item => categoryGetter(item))
.Where(c => !string.IsNullOrEmpty(c))
.Distinct()
.ToList();
}
IEnumerable<double> IRadzenSpiderSeries.GetValues()
{
if (Data == null || string.IsNullOrEmpty(ValueProperty))
{
return Enumerable.Empty<double>();
}
var valueGetter = PropertyAccess.Getter<TItem, double>(ValueProperty);
return Data.Select(item => valueGetter(item)).ToList();
}
double IRadzenSpiderSeries.GetValue(string category)
{
if (Data == null || string.IsNullOrEmpty(CategoryProperty) || string.IsNullOrEmpty(ValueProperty))
{
return 0;
}
var categoryGetter = PropertyAccess.Getter<TItem, string>(CategoryProperty);
var valueGetter = PropertyAccess.Getter<TItem, double>(ValueProperty);
var item = Data.FirstOrDefault(d => categoryGetter(d) == category);
return item != null ? valueGetter(item) : 0;
}
object? IRadzenSpiderSeries.GetData(string category)
{
if (Data == null || string.IsNullOrEmpty(CategoryProperty))
{
return null;
}
var categoryGetter = PropertyAccess.Getter<TItem, string>(CategoryProperty);
return Data.FirstOrDefault(d => categoryGetter(d) == category);
}
string IRadzenSpiderSeries.FormatValue(double value)
{
if (ValueFormatter != null)
{
return ValueFormatter(value);
}
if (!string.IsNullOrEmpty(FormatString))
{
return value.ToString(FormatString, System.Globalization.CultureInfo.InvariantCulture);
}
return value.ToString("F1", System.Globalization.CultureInfo.InvariantCulture);
}
RenderFragment IRadzenSpiderSeries.RenderLegendItem() => RenderLegendItem();
void IRadzenSpiderSeries.ForceUpdate() => ForceUpdate();
/// <summary>
/// Renders the legend item for this series.
/// </summary>
public RenderFragment RenderLegendItem() => RenderLegendItem(true);
/// <summary>
/// Renders the legend item for this series.
/// </summary>
protected virtual RenderFragment RenderLegendItem(bool clickable) => builder =>
{
var style = new List<string>();
if (!IsVisible)
{
style.Add("text-decoration: line-through");
style.Add("opacity: 0.5");
}
builder.OpenComponent<LegendItem>(0);
builder.AddAttribute(1, nameof(LegendItem.Index), Index);
builder.AddAttribute(2, nameof(LegendItem.MarkerType), MarkerType.Circle);
builder.AddAttribute(3, nameof(LegendItem.Style), string.Join(";", style));
builder.AddAttribute(4, nameof(LegendItem.MarkerSize), MarkerSize);
builder.AddAttribute(5, nameof(LegendItem.Text), Title);
builder.AddAttribute(6, nameof(LegendItem.Click), EventCallback.Factory.Create(this, OnLegendItemClick));
builder.AddAttribute(7, nameof(LegendItem.Clickable), clickable);
builder.CloseComponent();
};
/// <summary>
/// Handles legend item click.
/// </summary>
private async Task OnLegendItemClick()
{
if (Chart != null)
{
IsVisible = !IsVisible;
await Chart.Refresh();
}
}
/// <summary>
/// Forces the series to update its display.
/// </summary>
internal void ForceUpdate()
{
StateHasChanged();
}
}