diff --git a/.gitignore b/.gitignore index 8a30d258..c83793c2 100644 --- a/.gitignore +++ b/.gitignore @@ -396,3 +396,4 @@ FodyWeavers.xsd # JetBrains Rider *.sln.iml +/output diff --git a/BlazorExpress.ChartJS.Demo.RCL/Components/Shared/Prerequisites.razor b/BlazorExpress.ChartJS.Demo.RCL/Components/Shared/Prerequisites.razor new file mode 100644 index 00000000..edae0aea --- /dev/null +++ b/BlazorExpress.ChartJS.Demo.RCL/Components/Shared/Prerequisites.razor @@ -0,0 +1,12 @@ +@namespace BlazorExpress.ChartJS.Demo.RCL + +
+ + Refer to the Getting Started guide for setting up charts. + +
+ +@code { + [Parameter] + public string? PageUrl { get; set; } +} diff --git a/BlazorExpress.ChartJS.Demo.RCL/Pages/Demos/BarChart/BarChartDocumentation.razor b/BlazorExpress.ChartJS.Demo.RCL/Pages/Demos/BarChart/BarChartDocumentation.razor index 38de563b..c882e6fa 100644 --- a/BlazorExpress.ChartJS.Demo.RCL/Pages/Demos/BarChart/BarChartDocumentation.razor +++ b/BlazorExpress.ChartJS.Demo.RCL/Pages/Demos/BarChart/BarChartDocumentation.razor @@ -32,6 +32,14 @@ +
+ + Set the OnClick callback to react when a chart data item is selected. The callback receives ChartClickEventArgs with the dataset index and label, data-item index and label, and raw value. + The dedicated demo displays the selected item below the chart. + + +
+
The Combo bar/line demo mixes BarChartDataset and LineChartDataset in the same BarChart. diff --git a/BlazorExpress.ChartJS.Demo.RCL/Pages/Demos/BarChart/BarChart_Demo_01_Examples.razor b/BlazorExpress.ChartJS.Demo.RCL/Pages/Demos/BarChart/BarChart_Demo_01_Examples.razor index c66b5d30..34fc7b61 100644 --- a/BlazorExpress.ChartJS.Demo.RCL/Pages/Demos/BarChart/BarChart_Demo_01_Examples.razor +++ b/BlazorExpress.ChartJS.Demo.RCL/Pages/Demos/BarChart/BarChart_Demo_01_Examples.razor @@ -1,4 +1,11 @@ - + + +@if (selectedChartItem is not null) +{ +

+ Selected: @selectedChartItem.DatasetLabel (dataset @selectedChartItem.DatasetIndex), @selectedChartItem.Label (index @selectedChartItem.Index), value @selectedChartItem.Value +

+}
@@ -12,6 +19,7 @@ private BarChart barChart = default!; private BarChartOptions barChartOptions = default!; private ChartData chartData = default!; + private ChartClickEventArgs? selectedChartItem; private int datasetsCount = 0; private int labelsCount = 0; @@ -33,6 +41,12 @@ await base.OnAfterRenderAsync(firstRender); } + private Task HandleClickAsync(ChartClickEventArgs eventArgs) + { + selectedChartItem = eventArgs; + return Task.CompletedTask; + } + private async Task RandomizeAsync() { if (chartData is null || chartData.Datasets is null || !chartData.Datasets.Any()) return; diff --git a/BlazorExpress.ChartJS.Demo.RCL/Pages/Demos/BarChart/BarChart_Demo_11_Click_Event.razor b/BlazorExpress.ChartJS.Demo.RCL/Pages/Demos/BarChart/BarChart_Demo_11_Click_Event.razor new file mode 100644 index 00000000..60f97778 --- /dev/null +++ b/BlazorExpress.ChartJS.Demo.RCL/Pages/Demos/BarChart/BarChart_Demo_11_Click_Event.razor @@ -0,0 +1,47 @@ + + +@if (selectedChartItem is not null) +{ +

+ Selected: @selectedChartItem.DatasetLabel (dataset @selectedChartItem.DatasetIndex), @selectedChartItem.Label (index @selectedChartItem.Index), value @selectedChartItem.Value +

+} + +@code { + private BarChart barChart = default!; + private BarChartOptions barChartOptions = default!; + private ChartData chartData = default!; + private ChartClickEventArgs? selectedChartItem; + + protected override void OnInitialized() + { + chartData = new ChartData + { + Labels = new List { "January", "February", "March" }, + Datasets = new List + { + new BarChartDataset + { + Label = "Orders", + Data = new List { 45, 68, 54 }, + BackgroundColor = new List { "#36A2EB" }, + }, + }, + }; + barChartOptions = new BarChartOptions { Responsive = true }; + } + + protected override async Task OnAfterRenderAsync(bool firstRender) + { + if (firstRender) + await barChart.InitializeAsync(chartData, barChartOptions); + + await base.OnAfterRenderAsync(firstRender); + } + + private Task HandleClickAsync(ChartClickEventArgs eventArgs) + { + selectedChartItem = eventArgs; + return Task.CompletedTask; + } +} diff --git a/BlazorExpress.ChartJS.Demo.RCL/Pages/Demos/BubbleChart/BubbleChartDocumentation.razor b/BlazorExpress.ChartJS.Demo.RCL/Pages/Demos/BubbleChart/BubbleChartDocumentation.razor index dc7e3963..748e3325 100644 --- a/BlazorExpress.ChartJS.Demo.RCL/Pages/Demos/BubbleChart/BubbleChartDocumentation.razor +++ b/BlazorExpress.ChartJS.Demo.RCL/Pages/Demos/BubbleChart/BubbleChartDocumentation.razor @@ -34,6 +34,13 @@
+
+ + Set the OnClick callback to react when a bubble is selected. The callback receives ChartClickEventArgs with the dataset, point index, and raw value; the demo displays the selected item below the chart. + + +
+ @code { private const string pageUrl = DemoRouteConstants.Demos_BubbleChart; private const string pageTitle = "Bubble Chart"; diff --git a/BlazorExpress.ChartJS.Demo.RCL/Pages/Demos/BubbleChart/BubbleChart_Demo_02_Click_Event.razor b/BlazorExpress.ChartJS.Demo.RCL/Pages/Demos/BubbleChart/BubbleChart_Demo_02_Click_Event.razor new file mode 100644 index 00000000..3180aa8c --- /dev/null +++ b/BlazorExpress.ChartJS.Demo.RCL/Pages/Demos/BubbleChart/BubbleChart_Demo_02_Click_Event.razor @@ -0,0 +1,54 @@ + + +@if (selectedChartItem is not null) +{ +

+ Selected: @selectedChartItem.DatasetLabel (dataset @selectedChartItem.DatasetIndex), @selectedChartItem.Label (index @selectedChartItem.Index), value @selectedChartItem.Value +

+} + +@code { + private BubbleChart bubbleChart = default!; + private BubbleChartOptions bubbleChartOptions = default!; + private ChartData chartData = default!; + private ChartClickEventArgs? selectedChartItem; + + protected override void OnInitialized() + { + chartData = new ChartData + { + Labels = new List { "January", "February", "March" }, + Datasets = new List + { + new BubbleChartDataset + { + Label = "Projects", + Data = new List + { + new(10, 20, 8), + new(15, 12, 12), + new(8, 25, 6), + }, + BackgroundColor = new List { "rgba(255, 99, 132, 0.5)" }, + BorderColor = new List { "rgb(255, 99, 132)" }, + BorderWidth = 1, + }, + }, + }; + bubbleChartOptions = new BubbleChartOptions { Responsive = true }; + } + + protected override async Task OnAfterRenderAsync(bool firstRender) + { + if (firstRender) + await bubbleChart.InitializeAsync(chartData, bubbleChartOptions); + + await base.OnAfterRenderAsync(firstRender); + } + + private Task HandleClickAsync(ChartClickEventArgs eventArgs) + { + selectedChartItem = eventArgs; + return Task.CompletedTask; + } +} diff --git a/BlazorExpress.ChartJS.Demo.RCL/Pages/Demos/DoughnutChart/DoughnutChartDocumentation.razor b/BlazorExpress.ChartJS.Demo.RCL/Pages/Demos/DoughnutChart/DoughnutChartDocumentation.razor index d5a939dd..1228666d 100644 --- a/BlazorExpress.ChartJS.Demo.RCL/Pages/Demos/DoughnutChart/DoughnutChartDocumentation.razor +++ b/BlazorExpress.ChartJS.Demo.RCL/Pages/Demos/DoughnutChart/DoughnutChartDocumentation.razor @@ -50,6 +50,13 @@ +
+ + Set the OnClick callback to react when a doughnut segment is selected. The callback receives ChartClickEventArgs with the dataset, item index, label, and raw value; the demo displays the selected item below the chart. + + +
+ @code { private const string pageUrl = DemoRouteConstants.Demos_DoughnutChart; private const string pageTitle = "Doughnut Chart"; @@ -57,4 +64,4 @@ private const string metaTitle = "Blazor Doughnut Chart Demo & Examples"; private const string metaDescription = "Explore interactive Blazor Doughnut Chart demos with real-world examples. Learn how to visualize data, customize chart options, and implement data labels using the Blazor Doughnut Chart component."; private const string imageUrl = DemoImageSrcConstants.DoughnutChart; -} \ No newline at end of file +} diff --git a/BlazorExpress.ChartJS.Demo.RCL/Pages/Demos/DoughnutChart/DoughnutChart_Demo_03_Click_Event.razor b/BlazorExpress.ChartJS.Demo.RCL/Pages/Demos/DoughnutChart/DoughnutChart_Demo_03_Click_Event.razor new file mode 100644 index 00000000..ff8c8512 --- /dev/null +++ b/BlazorExpress.ChartJS.Demo.RCL/Pages/Demos/DoughnutChart/DoughnutChart_Demo_03_Click_Event.razor @@ -0,0 +1,47 @@ + + +@if (selectedChartItem is not null) +{ +

+ Selected: @selectedChartItem.DatasetLabel (dataset @selectedChartItem.DatasetIndex), @selectedChartItem.Label (index @selectedChartItem.Index), value @selectedChartItem.Value +

+} + +@code { + private DoughnutChart doughnutChart = default!; + private DoughnutChartOptions doughnutChartOptions = default!; + private ChartData chartData = default!; + private ChartClickEventArgs? selectedChartItem; + + protected override void OnInitialized() + { + chartData = new ChartData + { + Labels = new List { "Direct", "Referral", "Social" }, + Datasets = new List + { + new DoughnutChartDataset + { + Label = "Visitors", + Data = new List { 55, 30, 15 }, + BackgroundColor = new List { "#36A2EB", "#FF6384", "#FFCE56" }, + }, + }, + }; + doughnutChartOptions = new DoughnutChartOptions { Responsive = true }; + } + + protected override async Task OnAfterRenderAsync(bool firstRender) + { + if (firstRender) + await doughnutChart.InitializeAsync(chartData, doughnutChartOptions); + + await base.OnAfterRenderAsync(firstRender); + } + + private Task HandleClickAsync(ChartClickEventArgs eventArgs) + { + selectedChartItem = eventArgs; + return Task.CompletedTask; + } +} diff --git a/BlazorExpress.ChartJS.Demo.RCL/Pages/Demos/LineChart/LineChartDocumentation.razor b/BlazorExpress.ChartJS.Demo.RCL/Pages/Demos/LineChart/LineChartDocumentation.razor index e760ad2d..d0bd58b4 100644 --- a/BlazorExpress.ChartJS.Demo.RCL/Pages/Demos/LineChart/LineChartDocumentation.razor +++ b/BlazorExpress.ChartJS.Demo.RCL/Pages/Demos/LineChart/LineChartDocumentation.razor @@ -144,6 +144,13 @@ +
+ + Set the OnClick callback to react when a line data point is selected. The callback receives ChartClickEventArgs with the dataset, point index, label, and raw value; the demo displays the selected item below the chart. + + +
+ @code { private const string pageUrl = DemoRouteConstants.Demos_LineChart; private const string pageTitle = "Line Chart"; diff --git a/BlazorExpress.ChartJS.Demo.RCL/Pages/Demos/LineChart/LineChart_Demo_08_Click_Event.razor b/BlazorExpress.ChartJS.Demo.RCL/Pages/Demos/LineChart/LineChart_Demo_08_Click_Event.razor new file mode 100644 index 00000000..7c6101c7 --- /dev/null +++ b/BlazorExpress.ChartJS.Demo.RCL/Pages/Demos/LineChart/LineChart_Demo_08_Click_Event.razor @@ -0,0 +1,50 @@ + + +@if (selectedChartItem is not null) +{ +

+ Selected: @selectedChartItem.DatasetLabel (dataset @selectedChartItem.DatasetIndex), @selectedChartItem.Label (index @selectedChartItem.Index), value @selectedChartItem.Value +

+} + +@code { + private LineChart lineChart = default!; + private LineChartOptions lineChartOptions = default!; + private ChartData chartData = default!; + private ChartClickEventArgs? selectedChartItem; + + protected override void OnInitialized() + { + chartData = new ChartData + { + Labels = new List { "Monday", "Tuesday", "Wednesday", "Thursday" }, + Datasets = new List + { + new LineChartDataset + { + Label = "Visitors", + Data = new List { 120, 190, 150, 230 }, + BackgroundColor = "rgba(54, 162, 235, 0.2)", + BorderColor = "rgb(54, 162, 235)", + PointRadius = new List { 5 }, + PointHoverRadius = new List { 8 }, + }, + }, + }; + lineChartOptions = new LineChartOptions { Responsive = true }; + } + + protected override async Task OnAfterRenderAsync(bool firstRender) + { + if (firstRender) + await lineChart.InitializeAsync(chartData, lineChartOptions); + + await base.OnAfterRenderAsync(firstRender); + } + + private Task HandleClickAsync(ChartClickEventArgs eventArgs) + { + selectedChartItem = eventArgs; + return Task.CompletedTask; + } +} diff --git a/BlazorExpress.ChartJS.Demo.RCL/Pages/Demos/PieChart/PieChartDocumentation.razor b/BlazorExpress.ChartJS.Demo.RCL/Pages/Demos/PieChart/PieChartDocumentation.razor index fe47d712..fc51bf1a 100644 --- a/BlazorExpress.ChartJS.Demo.RCL/Pages/Demos/PieChart/PieChartDocumentation.razor +++ b/BlazorExpress.ChartJS.Demo.RCL/Pages/Demos/PieChart/PieChartDocumentation.razor @@ -70,6 +70,13 @@ +
+ + Set the OnClick callback to react when a pie slice is selected. The callback receives ChartClickEventArgs with the dataset, item index, label, and raw value; the demo displays the selected item below the chart. + + +
+ @code { private const string pageUrl = DemoRouteConstants.Demos_PieChart; private const string pageTitle = "Pie Chart"; diff --git a/BlazorExpress.ChartJS.Demo.RCL/Pages/Demos/PieChart/PieChart_Demo_04_Click_Event.razor b/BlazorExpress.ChartJS.Demo.RCL/Pages/Demos/PieChart/PieChart_Demo_04_Click_Event.razor new file mode 100644 index 00000000..843abe64 --- /dev/null +++ b/BlazorExpress.ChartJS.Demo.RCL/Pages/Demos/PieChart/PieChart_Demo_04_Click_Event.razor @@ -0,0 +1,47 @@ + + +@if (selectedChartItem is not null) +{ +

+ Selected: @selectedChartItem.DatasetLabel (dataset @selectedChartItem.DatasetIndex), @selectedChartItem.Label (index @selectedChartItem.Index), value @selectedChartItem.Value +

+} + +@code { + private PieChart pieChart = default!; + private PieChartOptions pieChartOptions = default!; + private ChartData chartData = default!; + private ChartClickEventArgs? selectedChartItem; + + protected override void OnInitialized() + { + chartData = new ChartData + { + Labels = new List { "Desktop", "Tablet", "Mobile" }, + Datasets = new List + { + new PieChartDataset + { + Label = "Sessions", + Data = new List { 62, 18, 20 }, + BackgroundColor = new List { "#4BC0C0", "#9966FF", "#FF9F40" }, + }, + }, + }; + pieChartOptions = new PieChartOptions { Responsive = true }; + } + + protected override async Task OnAfterRenderAsync(bool firstRender) + { + if (firstRender) + await pieChart.InitializeAsync(chartData, pieChartOptions); + + await base.OnAfterRenderAsync(firstRender); + } + + private Task HandleClickAsync(ChartClickEventArgs eventArgs) + { + selectedChartItem = eventArgs; + return Task.CompletedTask; + } +} diff --git a/BlazorExpress.ChartJS.Demo.RCL/Pages/Demos/PolarAreaChart/PolarAreaChartDocumentation.razor b/BlazorExpress.ChartJS.Demo.RCL/Pages/Demos/PolarAreaChart/PolarAreaChartDocumentation.razor index b7c24236..22bb743d 100644 --- a/BlazorExpress.ChartJS.Demo.RCL/Pages/Demos/PolarAreaChart/PolarAreaChartDocumentation.razor +++ b/BlazorExpress.ChartJS.Demo.RCL/Pages/Demos/PolarAreaChart/PolarAreaChartDocumentation.razor @@ -33,6 +33,13 @@ +
+ + Set the OnClick callback to react when a polar-area segment is selected. The callback receives ChartClickEventArgs with the dataset, item index, label, and raw value; the demo displays the selected item below the chart. + + +
+ @code { private const string pageUrl = DemoRouteConstants.Demos_PolarAreaChart; private const string pageTitle = "PolarArea Chart"; diff --git a/BlazorExpress.ChartJS.Demo.RCL/Pages/Demos/PolarAreaChart/PolarAreaChart_Demo_02_Click_Event.razor b/BlazorExpress.ChartJS.Demo.RCL/Pages/Demos/PolarAreaChart/PolarAreaChart_Demo_02_Click_Event.razor new file mode 100644 index 00000000..9da1b9f1 --- /dev/null +++ b/BlazorExpress.ChartJS.Demo.RCL/Pages/Demos/PolarAreaChart/PolarAreaChart_Demo_02_Click_Event.razor @@ -0,0 +1,53 @@ + + +@if (selectedChartItem is not null) +{ +

+ Selected: @selectedChartItem.DatasetLabel (dataset @selectedChartItem.DatasetIndex), @selectedChartItem.Label (index @selectedChartItem.Index), value @selectedChartItem.Value +

+} + +@code { + private PolarAreaChart polarAreaChart = default!; + private PolarAreaChartOptions polarAreaChartOptions = default!; + private ChartData chartData = default!; + private ChartClickEventArgs? selectedChartItem; + + protected override void OnInitialized() + { + chartData = new ChartData + { + Labels = new List { "North", "South", "East", "West" }, + Datasets = new List + { + new PolarAreaChartDataset + { + Label = "Sales", + Data = new List { 35, 55, 40, 25 }, + BackgroundColor = new List + { + "rgba(255, 99, 132, 0.5)", + "rgba(54, 162, 235, 0.5)", + "rgba(255, 206, 86, 0.5)", + "rgba(75, 192, 192, 0.5)", + }, + }, + }, + }; + polarAreaChartOptions = new PolarAreaChartOptions { Responsive = true }; + } + + protected override async Task OnAfterRenderAsync(bool firstRender) + { + if (firstRender) + await polarAreaChart.InitializeAsync(chartData, polarAreaChartOptions); + + await base.OnAfterRenderAsync(firstRender); + } + + private Task HandleClickAsync(ChartClickEventArgs eventArgs) + { + selectedChartItem = eventArgs; + return Task.CompletedTask; + } +} diff --git a/BlazorExpress.ChartJS.Demo.RCL/Pages/Demos/RadarChart/RadarChartDocumentation.razor b/BlazorExpress.ChartJS.Demo.RCL/Pages/Demos/RadarChart/RadarChartDocumentation.razor index 8ef6443e..b43a6de2 100644 --- a/BlazorExpress.ChartJS.Demo.RCL/Pages/Demos/RadarChart/RadarChartDocumentation.razor +++ b/BlazorExpress.ChartJS.Demo.RCL/Pages/Demos/RadarChart/RadarChartDocumentation.razor @@ -33,6 +33,13 @@ +
+ + Set the OnClick callback to react when a radar data point is selected. The callback receives ChartClickEventArgs with the dataset, point index, label, and raw value; the demo displays the selected item below the chart. + + +
+ @code { private const string pageUrl = DemoRouteConstants.Demos_RadarChart; private const string pageTitle = "Radar Chart"; diff --git a/BlazorExpress.ChartJS.Demo.RCL/Pages/Demos/RadarChart/RadarChart_Demo_02_Click_Event.razor b/BlazorExpress.ChartJS.Demo.RCL/Pages/Demos/RadarChart/RadarChart_Demo_02_Click_Event.razor new file mode 100644 index 00000000..7f697f74 --- /dev/null +++ b/BlazorExpress.ChartJS.Demo.RCL/Pages/Demos/RadarChart/RadarChart_Demo_02_Click_Event.razor @@ -0,0 +1,50 @@ + + +@if (selectedChartItem is not null) +{ +

+ Selected: @selectedChartItem.DatasetLabel (dataset @selectedChartItem.DatasetIndex), @selectedChartItem.Label (index @selectedChartItem.Index), value @selectedChartItem.Value +

+} + +@code { + private RadarChart radarChart = default!; + private RadarChartOptions radarChartOptions = default!; + private ChartData chartData = default!; + private ChartClickEventArgs? selectedChartItem; + + protected override void OnInitialized() + { + chartData = new ChartData + { + Labels = new List { "Speed", "Reliability", "Comfort", "Safety", "Efficiency" }, + Datasets = new List + { + new RadarChartDataset + { + Label = "Vehicle A", + Data = new List { 80, 72, 65, 90, 78 }, + BackgroundColor = "rgba(153, 102, 255, 0.2)", + BorderColor = "rgba(153, 102, 255, 0.8)", + BorderWidth = 2, + Fill = true, + }, + }, + }; + radarChartOptions = new RadarChartOptions { Responsive = true }; + } + + protected override async Task OnAfterRenderAsync(bool firstRender) + { + if (firstRender) + await radarChart.InitializeAsync(chartData, radarChartOptions); + + await base.OnAfterRenderAsync(firstRender); + } + + private Task HandleClickAsync(ChartClickEventArgs eventArgs) + { + selectedChartItem = eventArgs; + return Task.CompletedTask; + } +} diff --git a/BlazorExpress.ChartJS.Demo.RCL/Pages/Demos/ScatterChart/ScatterChartDocumentation.razor b/BlazorExpress.ChartJS.Demo.RCL/Pages/Demos/ScatterChart/ScatterChartDocumentation.razor index c1431bdc..98f61ff1 100644 --- a/BlazorExpress.ChartJS.Demo.RCL/Pages/Demos/ScatterChart/ScatterChartDocumentation.razor +++ b/BlazorExpress.ChartJS.Demo.RCL/Pages/Demos/ScatterChart/ScatterChartDocumentation.razor @@ -52,6 +52,13 @@ +
+ + Set the OnClick callback to react when a scatter point is selected. The callback receives ChartClickEventArgs with the dataset, point index, and raw coordinates; the demo displays the selected item below the chart. + + +
+ @code { private const string pageUrl = DemoRouteConstants.Demos_ScatterChart; private const string pageTitle = "Scatter Chart"; diff --git a/BlazorExpress.ChartJS.Demo.RCL/Pages/Demos/ScatterChart/ScatterChart_Demo_03_Click_Event.razor b/BlazorExpress.ChartJS.Demo.RCL/Pages/Demos/ScatterChart/ScatterChart_Demo_03_Click_Event.razor new file mode 100644 index 00000000..e3b65ac6 --- /dev/null +++ b/BlazorExpress.ChartJS.Demo.RCL/Pages/Demos/ScatterChart/ScatterChart_Demo_03_Click_Event.razor @@ -0,0 +1,55 @@ + + +@if (selectedChartItem is not null) +{ +

+ Selected: @selectedChartItem.DatasetLabel (dataset @selectedChartItem.DatasetIndex), @(selectedChartItem.Label ?? $"point {selectedChartItem.Index}") (index @selectedChartItem.Index), value @selectedChartItem.Value +

+} + +@code { + private ScatterChart scatterChart = default!; + private ScatterChartOptions scatterChartOptions = default!; + private ChartData chartData = default!; + private ChartClickEventArgs? selectedChartItem; + + protected override void OnInitialized() + { + chartData = new ChartData + { + Labels = new List { "First", "Second", "Third", "Fourth" }, + Datasets = new List + { + new ScatterChartDataset + { + Label = "Measurements", + Data = new List + { + new(10, 25), + new(25, 15), + new(35, 40), + new(50, 30), + }, + BackgroundColor = "rgba(255, 159, 64, 0.6)", + BorderColor = "rgb(255, 159, 64)", + BorderWidth = 2, + }, + }, + }; + scatterChartOptions = new ScatterChartOptions { Responsive = true }; + } + + protected override async Task OnAfterRenderAsync(bool firstRender) + { + if (firstRender) + await scatterChart.InitializeAsync(chartData, scatterChartOptions); + + await base.OnAfterRenderAsync(firstRender); + } + + private Task HandleClickAsync(ChartClickEventArgs eventArgs) + { + selectedChartItem = eventArgs; + return Task.CompletedTask; + } +} diff --git a/BlazorExpress.ChartJS/ChartComponents/BarChart.razor.cs b/BlazorExpress.ChartJS/ChartComponents/BarChart.razor.cs index 13118e35..1951554a 100644 --- a/BlazorExpress.ChartJS/ChartComponents/BarChart.razor.cs +++ b/BlazorExpress.ChartJS/ChartComponents/BarChart.razor.cs @@ -177,7 +177,7 @@ public override async Task InitializeAsync(ChartData chartData, IChartOptions ch { var datasets = BarLineChartSupport.GetSupportedDatasets(chartData); var data = new { chartData.Labels, Datasets = datasets }; - await JSRuntime.InvokeVoidAsync(BarChartInterop.Initialize, Id, GetChartType(), data, (BarChartOptions)chartOptions, plugins); + await JSRuntime.InvokeVoidAsync(BarChartInterop.Initialize, Id, GetChartType(), data, (BarChartOptions)chartOptions, plugins, DotNetObjectReference); } } diff --git a/BlazorExpress.ChartJS/ChartComponents/BubbleChart.razor.cs b/BlazorExpress.ChartJS/ChartComponents/BubbleChart.razor.cs index 1547854e..6d3d770c 100644 --- a/BlazorExpress.ChartJS/ChartComponents/BubbleChart.razor.cs +++ b/BlazorExpress.ChartJS/ChartComponents/BubbleChart.razor.cs @@ -157,7 +157,7 @@ public override async Task InitializeAsync(ChartData chartData, IChartOptions ch var datasets = chartData.Datasets.OfType(); var data = new { chartData.Labels, Datasets = datasets }; - await JSRuntime.InvokeVoidAsync(BubbleChartInterop.Initialize, Id, GetChartType(), data, (BubbleChartOptions)chartOptions, plugins); + await JSRuntime.InvokeVoidAsync(BubbleChartInterop.Initialize, Id, GetChartType(), data, (BubbleChartOptions)chartOptions, plugins, DotNetObjectReference); } /// diff --git a/BlazorExpress.ChartJS/ChartComponents/Core/ChartComponentBase.cs b/BlazorExpress.ChartJS/ChartComponents/Core/ChartComponentBase.cs index ff37ca5c..b9e6859d 100644 --- a/BlazorExpress.ChartJS/ChartComponents/Core/ChartComponentBase.cs +++ b/BlazorExpress.ChartJS/ChartComponents/Core/ChartComponentBase.cs @@ -12,6 +12,8 @@ public abstract class ChartComponentBase : BlazorExpressComponentCore, IDisposab internal ChartType _chartType; + private DotNetObjectReference? dotNetObjectReference; + #endregion #region Methods @@ -76,10 +78,24 @@ public virtual async Task InitializeAsync(ChartData chartData, IChartOptions cha if (chartData is not null && chartData.Datasets is not null && chartData.Datasets.Any()) { var _data = GetChartDataObject(chartData); - await JSRuntime.InvokeVoidAsync(ChartInterop.Initialize, Id, GetChartType(), _data, chartOptions, plugins); + await JSRuntime.InvokeVoidAsync(ChartInterop.Initialize, Id, GetChartType(), _data, chartOptions, plugins, DotNetObjectReference); } } + /// + /// Dispatches a chart-item click to the configured callback. + /// + /// The selected chart data-item metadata. + /// A task that represents the asynchronous callback dispatch operation. + [JSInvokable] + [AddedVersion("1.2.4")] + [Description("Dispatches a chart-item click to the configured callback.")] + public async Task HandleClickAsync(ChartClickEventArgs eventArgs) + { + if (OnClick.HasDelegate) + await OnClick.InvokeAsync(eventArgs); + } + //public async Task Render() { } //public async Task Reset() { } @@ -139,6 +155,9 @@ protected string GetChartType() => protected string ContainerClassNames => ContainerClass!; + protected DotNetObjectReference DotNetObjectReference => + dotNetObjectReference ??= Microsoft.JSInterop.DotNetObjectReference.Create(this); + protected string ContainerStyleNames => BuildStyleNames( ContainerStyle, @@ -241,6 +260,18 @@ private object GetChartDataObject(ChartData chartData) protected bool IsRenderComplete { get; private set; } + /// + /// Gets or sets the callback that is invoked when a chart data item is selected. + /// + /// Default value is . + /// + /// + [AddedVersion("1.2.4")] + [DefaultValue(null)] + [Description("Gets or sets the callback that is invoked when a chart data item is selected.")] + [Parameter] + public EventCallback OnClick { get; set; } + /// /// Gets or sets chart container width. /// The default unit of measure is . @@ -272,6 +303,14 @@ private object GetChartDataObject(ChartData chartData) #region Other + protected override void Dispose(bool disposing) + { + if (disposing) + dotNetObjectReference?.Dispose(); + + base.Dispose(disposing); + } + ~ChartComponentBase() { Dispose(false); diff --git a/BlazorExpress.ChartJS/ChartComponents/DoughnutChart.razor.cs b/BlazorExpress.ChartJS/ChartComponents/DoughnutChart.razor.cs index 1bce4978..573007a1 100644 --- a/BlazorExpress.ChartJS/ChartComponents/DoughnutChart.razor.cs +++ b/BlazorExpress.ChartJS/ChartComponents/DoughnutChart.razor.cs @@ -156,7 +156,7 @@ public override async Task InitializeAsync(ChartData chartData, IChartOptions ch { var datasets = chartData.Datasets.OfType(); var data = new { chartData.Labels, Datasets = datasets }; - await JSRuntime.InvokeVoidAsync(DoughnutChartInterop.Initialize, Id, GetChartType(), data, (DoughnutChartOptions)chartOptions, plugins); + await JSRuntime.InvokeVoidAsync(DoughnutChartInterop.Initialize, Id, GetChartType(), data, (DoughnutChartOptions)chartOptions, plugins, DotNetObjectReference); } } diff --git a/BlazorExpress.ChartJS/ChartComponents/LineChart.razor.cs b/BlazorExpress.ChartJS/ChartComponents/LineChart.razor.cs index 8c9c7f0d..5ef99166 100644 --- a/BlazorExpress.ChartJS/ChartComponents/LineChart.razor.cs +++ b/BlazorExpress.ChartJS/ChartComponents/LineChart.razor.cs @@ -184,7 +184,7 @@ public override async Task InitializeAsync(ChartData chartData, IChartOptions ch var datasets = BarLineChartSupport.GetSupportedDatasets(chartData); var data = new { chartData.Labels, Datasets = datasets }; - await JSRuntime.InvokeVoidAsync(LineChartInterop.Initialize, Id, GetChartType(), data, (LineChartOptions)chartOptions, plugins); + await JSRuntime.InvokeVoidAsync(LineChartInterop.Initialize, Id, GetChartType(), data, (LineChartOptions)chartOptions, plugins, DotNetObjectReference); } /// diff --git a/BlazorExpress.ChartJS/ChartComponents/PieChart.razor.cs b/BlazorExpress.ChartJS/ChartComponents/PieChart.razor.cs index bf303122..2aa9c019 100644 --- a/BlazorExpress.ChartJS/ChartComponents/PieChart.razor.cs +++ b/BlazorExpress.ChartJS/ChartComponents/PieChart.razor.cs @@ -156,7 +156,7 @@ public override async Task InitializeAsync(ChartData chartData, IChartOptions ch { var datasets = chartData.Datasets.OfType(); var data = new { chartData.Labels, Datasets = datasets }; - await JSRuntime.InvokeVoidAsync(PieChartInterop.Initialize, Id, GetChartType(), data, (PieChartOptions)chartOptions, plugins); + await JSRuntime.InvokeVoidAsync(PieChartInterop.Initialize, Id, GetChartType(), data, (PieChartOptions)chartOptions, plugins, DotNetObjectReference); } } diff --git a/BlazorExpress.ChartJS/ChartComponents/PolarAreaChart.razor.cs b/BlazorExpress.ChartJS/ChartComponents/PolarAreaChart.razor.cs index e97ae02a..e258b800 100644 --- a/BlazorExpress.ChartJS/ChartComponents/PolarAreaChart.razor.cs +++ b/BlazorExpress.ChartJS/ChartComponents/PolarAreaChart.razor.cs @@ -150,7 +150,7 @@ public override async Task InitializeAsync(ChartData chartData, IChartOptions ch { var datasets = chartData.Datasets.OfType(); var data = new { chartData.Labels, Datasets = datasets }; - await JSRuntime.InvokeVoidAsync(PolarAreaChartInterop.Initialize, Id, GetChartType(), data, (PolarAreaChartOptions)chartOptions, plugins); + await JSRuntime.InvokeVoidAsync(PolarAreaChartInterop.Initialize, Id, GetChartType(), data, (PolarAreaChartOptions)chartOptions, plugins, DotNetObjectReference); } } diff --git a/BlazorExpress.ChartJS/ChartComponents/RadarChart.razor.cs b/BlazorExpress.ChartJS/ChartComponents/RadarChart.razor.cs index fe27d8da..a92ca63f 100644 --- a/BlazorExpress.ChartJS/ChartComponents/RadarChart.razor.cs +++ b/BlazorExpress.ChartJS/ChartComponents/RadarChart.razor.cs @@ -150,7 +150,7 @@ public override async Task InitializeAsync(ChartData chartData, IChartOptions ch { var datasets = chartData.Datasets.OfType(); var data = new { chartData.Labels, Datasets = datasets }; - await JSRuntime.InvokeVoidAsync(RadarChartInterop.Initialize, Id, GetChartType(), data, (RadarChartOptions)chartOptions, plugins); + await JSRuntime.InvokeVoidAsync(RadarChartInterop.Initialize, Id, GetChartType(), data, (RadarChartOptions)chartOptions, plugins, DotNetObjectReference); } } diff --git a/BlazorExpress.ChartJS/ChartComponents/ScatterChart.razor.cs b/BlazorExpress.ChartJS/ChartComponents/ScatterChart.razor.cs index 2321dcf1..fb68f974 100644 --- a/BlazorExpress.ChartJS/ChartComponents/ScatterChart.razor.cs +++ b/BlazorExpress.ChartJS/ChartComponents/ScatterChart.razor.cs @@ -157,7 +157,7 @@ public override async Task InitializeAsync(ChartData chartData, IChartOptions ch var datasets = chartData.Datasets.OfType(); var data = new { chartData.Labels, Datasets = datasets }; - await JSRuntime.InvokeVoidAsync(ScatterChartInterop.Initialize, Id, GetChartType(), data, (ScatterChartOptions)chartOptions, plugins); + await JSRuntime.InvokeVoidAsync(ScatterChartInterop.Initialize, Id, GetChartType(), data, (ScatterChartOptions)chartOptions, plugins, DotNetObjectReference); } /// diff --git a/BlazorExpress.ChartJS/Models/ChartClickEventArgs.cs b/BlazorExpress.ChartJS/Models/ChartClickEventArgs.cs new file mode 100644 index 00000000..d2f25c0d --- /dev/null +++ b/BlazorExpress.ChartJS/Models/ChartClickEventArgs.cs @@ -0,0 +1,49 @@ +namespace BlazorExpress.ChartJS; + +/// +/// Provides information about a selected chart data item. +/// +public class ChartClickEventArgs +{ + #region Properties, Indexers + + /// + /// Gets or sets the index of the selected dataset. + /// + [AddedVersion("1.2.4")] + [Description("Gets or sets the index of the selected dataset.")] + public int DatasetIndex { get; set; } + + /// + /// Gets or sets the label of the selected dataset. + /// + [AddedVersion("1.2.4")] + [DefaultValue(null)] + [Description("Gets or sets the label of the selected dataset.")] + public string? DatasetLabel { get; set; } + + /// + /// Gets or sets the index of the selected data item. + /// + [AddedVersion("1.2.4")] + [Description("Gets or sets the index of the selected data item.")] + public int Index { get; set; } + + /// + /// Gets or sets the label of the selected data item. + /// + [AddedVersion("1.2.4")] + [DefaultValue(null)] + [Description("Gets or sets the label of the selected data item.")] + public string? Label { get; set; } + + /// + /// Gets or sets the raw value of the selected data item. + /// + [AddedVersion("1.2.4")] + [DefaultValue(null)] + [Description("Gets or sets the raw value of the selected data item.")] + public JsonElement? Value { get; set; } + + #endregion +} diff --git a/BlazorExpress.ChartJS/Usings.cs b/BlazorExpress.ChartJS/Usings.cs index 7f28513a..55872776 100644 --- a/BlazorExpress.ChartJS/Usings.cs +++ b/BlazorExpress.ChartJS/Usings.cs @@ -4,4 +4,5 @@ global using System.ComponentModel; global using System.Drawing; global using System.Globalization; +global using System.Text.Json; global using System.Text.Json.Serialization; diff --git a/BlazorExpress.ChartJS/wwwroot/blazorexpress.chartjs.js b/BlazorExpress.ChartJS/wwwroot/blazorexpress.chartjs.js index c8746fd6..6ba5fdbb 100644 --- a/BlazorExpress.ChartJS/wwwroot/blazorexpress.chartjs.js +++ b/BlazorExpress.ChartJS/wwwroot/blazorexpress.chartjs.js @@ -39,6 +39,26 @@ if (!window.blazorexpress.chartjs.scatter) { } window.blazorexpress.chartjs = { + addClickHandler: (chart, dotNetObjectReference) => { + if (!chart || !chart.canvas || !dotNetObjectReference || chart.blazorExpressClickHandler) return; + + chart.blazorExpressClickHandler = true; + chart.canvas.addEventListener('click', (event) => { + const activeElements = chart.getElementsAtEventForMode(event, 'nearest', { intersect: true }, true); + if (!activeElements || activeElements.length === 0) return; + + const activeElement = activeElements[0]; + const dataset = chart.data.datasets?.[activeElement.datasetIndex]; + + dotNetObjectReference.invokeMethodAsync('HandleClickAsync', { + datasetIndex: activeElement.datasetIndex, + datasetLabel: dataset?.label ?? null, + index: activeElement.index, + label: chart.data.labels?.[activeElement.index] ?? null, + value: dataset?.data?.[activeElement.index] ?? null + }).catch(() => { }); + }); + }, create: (elementId, type, data, options, plugins) => { let chartEl = document.getElementById(elementId); let _plugins = []; @@ -72,11 +92,14 @@ window.blazorexpress.chartjs = { return chart; }, - initialize: (elementId, type, data, options, plugins) => { + initialize: (elementId, type, data, options, plugins, dotNetObjectReference) => { let chart = window.blazorexpress.chartjs.get(elementId); - if (chart) return; - else + if (!chart) { window.blazorexpress.chartjs.create(elementId, type, data, options, plugins); + chart = window.blazorexpress.chartjs.get(elementId); + } + + window.blazorexpress.chartjs.addClickHandler(chart, dotNetObjectReference); }, resize: (elementId, width, height) => { let chart = window.blazorexpress.chartjs.get(elementId); @@ -201,11 +224,14 @@ window.blazorexpress.chartjs.bar = { return chart; }, - initialize: (elementId, type, data, options, plugins) => { + initialize: (elementId, type, data, options, plugins, dotNetObjectReference) => { let chart = window.blazorexpress.chartjs.bar.get(elementId); - if (chart) return; - else + if (!chart) { window.blazorexpress.chartjs.bar.create(elementId, type, data, options, plugins); + chart = window.blazorexpress.chartjs.bar.get(elementId); + } + + window.blazorexpress.chartjs.addClickHandler(chart, dotNetObjectReference); }, resize: (elementId, width, height) => { let chart = window.blazorexpress.chartjs.bar.get(elementId); @@ -313,11 +339,14 @@ window.blazorexpress.chartjs.bubble = { return chart; }, - initialize: (elementId, type, data, options, plugins) => { + initialize: (elementId, type, data, options, plugins, dotNetObjectReference) => { let chart = window.blazorexpress.chartjs.bubble.get(elementId); - if (chart) return; - else + if (!chart) { window.blazorexpress.chartjs.bubble.create(elementId, type, data, options, plugins); + chart = window.blazorexpress.chartjs.bubble.get(elementId); + } + + window.blazorexpress.chartjs.addClickHandler(chart, dotNetObjectReference); }, resize: (elementId, width, height) => { let chart = window.blazorexpress.chartjs.bubble.get(elementId); @@ -431,11 +460,14 @@ window.blazorexpress.chartjs.doughnut = { return chart; }, - initialize: (elementId, type, data, options, plugins) => { + initialize: (elementId, type, data, options, plugins, dotNetObjectReference) => { let chart = window.blazorexpress.chartjs.doughnut.get(elementId); - if (chart) return; - else + if (!chart) { window.blazorexpress.chartjs.doughnut.create(elementId, type, data, options, plugins); + chart = window.blazorexpress.chartjs.doughnut.get(elementId); + } + + window.blazorexpress.chartjs.addClickHandler(chart, dotNetObjectReference); }, resize: (elementId, width, height) => { let chart = window.blazorexpress.chartjs.doughnut.get(elementId); @@ -581,12 +613,14 @@ window.blazorexpress.chartjs.line = { return chart; }, - initialize: (elementId, type, data, options, plugins) => { + initialize: (elementId, type, data, options, plugins, dotNetObjectReference) => { let chart = window.blazorexpress.chartjs.line.get(elementId); - if (chart) - return; - else + if (!chart) { window.blazorexpress.chartjs.line.create(elementId, type, data, options, plugins); + chart = window.blazorexpress.chartjs.line.get(elementId); + } + + window.blazorexpress.chartjs.addClickHandler(chart, dotNetObjectReference); }, resize: (elementId, width, height) => { let chart = window.blazorexpress.chartjs.line.get(elementId); @@ -701,11 +735,14 @@ window.blazorexpress.chartjs.pie = { return chart; }, - initialize: (elementId, type, data, options, plugins) => { + initialize: (elementId, type, data, options, plugins, dotNetObjectReference) => { let chart = window.blazorexpress.chartjs.pie.get(elementId); - if (chart) return; - else + if (!chart) { window.blazorexpress.chartjs.pie.create(elementId, type, data, options, plugins); + chart = window.blazorexpress.chartjs.pie.get(elementId); + } + + window.blazorexpress.chartjs.addClickHandler(chart, dotNetObjectReference); }, resize: (elementId, width, height) => { let chart = window.blazorexpress.chartjs.pie.get(elementId); @@ -820,11 +857,14 @@ window.blazorexpress.chartjs.polarArea = { return chart; }, - initialize: (elementId, type, data, options, plugins) => { + initialize: (elementId, type, data, options, plugins, dotNetObjectReference) => { let chart = window.blazorexpress.chartjs.polarArea.get(elementId); - if (chart) return; - else + if (!chart) { window.blazorexpress.chartjs.polarArea.create(elementId, type, data, options, plugins); + chart = window.blazorexpress.chartjs.polarArea.get(elementId); + } + + window.blazorexpress.chartjs.addClickHandler(chart, dotNetObjectReference); }, resize: (elementId, width, height) => { let chart = window.blazorexpress.chartjs.polarArea.get(elementId); @@ -938,11 +978,14 @@ window.blazorexpress.chartjs.radar = { return chart; }, - initialize: (elementId, type, data, options, plugins) => { + initialize: (elementId, type, data, options, plugins, dotNetObjectReference) => { let chart = window.blazorexpress.chartjs.radar.get(elementId); - if (chart) return; - else + if (!chart) { window.blazorexpress.chartjs.radar.create(elementId, type, data, options, plugins); + chart = window.blazorexpress.chartjs.radar.get(elementId); + } + + window.blazorexpress.chartjs.addClickHandler(chart, dotNetObjectReference); }, resize: (elementId, width, height) => { let chart = window.blazorexpress.chartjs.radar.get(elementId); @@ -1073,11 +1116,14 @@ window.blazorexpress.chartjs.scatter = { return chart; }, - initialize: (elementId, type, data, options, plugins) => { + initialize: (elementId, type, data, options, plugins, dotNetObjectReference) => { let chart = window.blazorexpress.chartjs.scatter.get(elementId); - if (chart) return; - else + if (!chart) { window.blazorexpress.chartjs.scatter.create(elementId, type, data, options, plugins); + chart = window.blazorexpress.chartjs.scatter.get(elementId); + } + + window.blazorexpress.chartjs.addClickHandler(chart, dotNetObjectReference); }, resize: (elementId, width, height) => { let chart = window.blazorexpress.chartjs.scatter.get(elementId); @@ -1121,4 +1167,4 @@ window.blazorexpress.chartjs.scatter = { chart.update(); } } -} \ No newline at end of file +}