diff --git a/BlazorBootstrap.Demo.Hosted/Client/Pages/GettingStarted/server/GettingStartedDocumentation.razor b/BlazorBootstrap.Demo.Hosted/Client/Pages/GettingStarted/server/GettingStartedDocumentation.razor index 9a1c0b414..826419ee9 100644 --- a/BlazorBootstrap.Demo.Hosted/Client/Pages/GettingStarted/server/GettingStartedDocumentation.razor +++ b/BlazorBootstrap.Demo.Hosted/Client/Pages/GettingStarted/server/GettingStartedDocumentation.razor @@ -59,18 +59,36 @@

Either remove or keep the site.css file but make sure you clear it out of any content when the Sidebar component with full layout is used.

+ +
+

Blazor Bootstrap Templates make it easy to create Blazor projects in a minute. Just install the templates with the NuGet package manager and you're good to go!

+

dotnet new install Blazor.Bootstrap.Templates::1.9.1

+

+ Check the Visual Studio project templates GitHub repo here. +

+

+

+ Blazor Bootstrap - Visual Studio project templates +
+

+
+ + + Upgrade the Blazor.Bootstrap NuGet package to the latest available version. + + @code { private string pageUrl = "/getting-started/blazor-server"; private string title = "Getting started with Blazor Bootstrap - Blazor Server Project Setup"; private string description = "High-performance, lightweight, and responsive blazor bootstrap components in a single package from the developers for the developers."; private string imageUrl = "https://i.imgur.com/SCbZVd4.jpg"; - private string version; - [Inject] public IConfiguration Configuration { get; set; } + private string? version; + [Inject] public IConfiguration Configuration { get; set; } = default!; protected override void OnInitialized() { version = $"{Configuration.GetValue("version")}"; // example: v0.6.1 base.OnInitialized(); } -} \ No newline at end of file +} diff --git a/BlazorBootstrap.Demo.Hosted/Client/Pages/GettingStarted/webassembly/GettingStartedDocumentation.razor b/BlazorBootstrap.Demo.Hosted/Client/Pages/GettingStarted/webassembly/GettingStartedDocumentation.razor index 4f95c5d1e..105771274 100644 --- a/BlazorBootstrap.Demo.Hosted/Client/Pages/GettingStarted/webassembly/GettingStartedDocumentation.razor +++ b/BlazorBootstrap.Demo.Hosted/Client/Pages/GettingStarted/webassembly/GettingStartedDocumentation.razor @@ -57,18 +57,36 @@

Either remove or keep the app.css file but make sure you clear it out of any content when the Sidebar component with full layout is used.

+ +
+

Blazor Bootstrap Templates make it easy to create Blazor projects in a minute. Just install the templates with the NuGet package manager and you're good to go!

+

dotnet new install Blazor.Bootstrap.Templates::1.9.1

+

+ Check the Visual Studio project templates GitHub repo here. +

+

+

+ Blazor Bootstrap - Visual Studio project templates +
+

+
+ + + Upgrade the Blazor.Bootstrap NuGet package to the latest available version. + + @code { private string pageUrl = "/getting-started/blazor-webassembly"; private string title = "Getting started with Blazor Bootstrap - Blazor WebAssembly Project Setup"; private string description = "High-performance, lightweight, and responsive blazor bootstrap components in a single package from the developers for the developers."; private string imageUrl = "https://i.imgur.com/SCbZVd4.jpg"; - private string version; - [Inject] public IConfiguration Configuration { get; set; } + private string? version; + [Inject] public IConfiguration Configuration { get; set; } = default!; protected override void OnInitialized() { version = $"{Configuration.GetValue("version")}"; // example: v0.6.1 base.OnInitialized(); } -} \ No newline at end of file +} diff --git a/BlazorBootstrap.Demo.Hosted/Client/Pages/Index.razor b/BlazorBootstrap.Demo.Hosted/Client/Pages/Index.razor index 9d69821af..3b664b3ad 100644 --- a/BlazorBootstrap.Demo.Hosted/Client/Pages/Index.razor +++ b/BlazorBootstrap.Demo.Hosted/Client/Pages/Index.razor @@ -28,9 +28,9 @@ -
+
-

Blazor Bootstrap UI & Data Visualization Components

+

All Components

@@ -172,6 +172,74 @@
+ + +
+
+

Data Visualization Components

+
+ + +
+
This demo website is built using the Blazor Bootstrap library and published on the Azure Static Web App. diff --git a/BlazorBootstrap.Demo.Server/Pages/GettingStarted/server/GettingStartedDocumentation.razor b/BlazorBootstrap.Demo.Server/Pages/GettingStarted/server/GettingStartedDocumentation.razor index 9a1c0b414..826419ee9 100644 --- a/BlazorBootstrap.Demo.Server/Pages/GettingStarted/server/GettingStartedDocumentation.razor +++ b/BlazorBootstrap.Demo.Server/Pages/GettingStarted/server/GettingStartedDocumentation.razor @@ -59,18 +59,36 @@

Either remove or keep the site.css file but make sure you clear it out of any content when the Sidebar component with full layout is used.

+ +
+

Blazor Bootstrap Templates make it easy to create Blazor projects in a minute. Just install the templates with the NuGet package manager and you're good to go!

+

dotnet new install Blazor.Bootstrap.Templates::1.9.1

+

+ Check the Visual Studio project templates GitHub repo here. +

+

+

+ Blazor Bootstrap - Visual Studio project templates +
+

+
+ + + Upgrade the Blazor.Bootstrap NuGet package to the latest available version. + + @code { private string pageUrl = "/getting-started/blazor-server"; private string title = "Getting started with Blazor Bootstrap - Blazor Server Project Setup"; private string description = "High-performance, lightweight, and responsive blazor bootstrap components in a single package from the developers for the developers."; private string imageUrl = "https://i.imgur.com/SCbZVd4.jpg"; - private string version; - [Inject] public IConfiguration Configuration { get; set; } + private string? version; + [Inject] public IConfiguration Configuration { get; set; } = default!; protected override void OnInitialized() { version = $"{Configuration.GetValue("version")}"; // example: v0.6.1 base.OnInitialized(); } -} \ No newline at end of file +} diff --git a/BlazorBootstrap.Demo.Server/Pages/GettingStarted/webassembly/GettingStartedDocumentation.razor b/BlazorBootstrap.Demo.Server/Pages/GettingStarted/webassembly/GettingStartedDocumentation.razor index 4f95c5d1e..105771274 100644 --- a/BlazorBootstrap.Demo.Server/Pages/GettingStarted/webassembly/GettingStartedDocumentation.razor +++ b/BlazorBootstrap.Demo.Server/Pages/GettingStarted/webassembly/GettingStartedDocumentation.razor @@ -57,18 +57,36 @@

Either remove or keep the app.css file but make sure you clear it out of any content when the Sidebar component with full layout is used.

+ +
+

Blazor Bootstrap Templates make it easy to create Blazor projects in a minute. Just install the templates with the NuGet package manager and you're good to go!

+

dotnet new install Blazor.Bootstrap.Templates::1.9.1

+

+ Check the Visual Studio project templates GitHub repo here. +

+

+

+ Blazor Bootstrap - Visual Studio project templates +
+

+
+ + + Upgrade the Blazor.Bootstrap NuGet package to the latest available version. + + @code { private string pageUrl = "/getting-started/blazor-webassembly"; private string title = "Getting started with Blazor Bootstrap - Blazor WebAssembly Project Setup"; private string description = "High-performance, lightweight, and responsive blazor bootstrap components in a single package from the developers for the developers."; private string imageUrl = "https://i.imgur.com/SCbZVd4.jpg"; - private string version; - [Inject] public IConfiguration Configuration { get; set; } + private string? version; + [Inject] public IConfiguration Configuration { get; set; } = default!; protected override void OnInitialized() { version = $"{Configuration.GetValue("version")}"; // example: v0.6.1 base.OnInitialized(); } -} \ No newline at end of file +} diff --git a/BlazorBootstrap.Demo.Server/Pages/Index.razor b/BlazorBootstrap.Demo.Server/Pages/Index.razor index 9d69821af..3b664b3ad 100644 --- a/BlazorBootstrap.Demo.Server/Pages/Index.razor +++ b/BlazorBootstrap.Demo.Server/Pages/Index.razor @@ -28,9 +28,9 @@ -
+
-

Blazor Bootstrap UI & Data Visualization Components

+

All Components

@@ -172,6 +172,74 @@
+ + +
+
+

Data Visualization Components

+
+ + +
+
This demo website is built using the Blazor Bootstrap library and published on the Azure Static Web App. diff --git a/BlazorBootstrap.Demo/Pages/Charts/BarCharts/ChartsDocumentation.razor b/BlazorBootstrap.Demo/Pages/Charts/BarCharts/ChartsDocumentation.razor new file mode 100644 index 000000000..a6c7103cc --- /dev/null +++ b/BlazorBootstrap.Demo/Pages/Charts/BarCharts/ChartsDocumentation.razor @@ -0,0 +1,27 @@ +@page "/charts/bar-chart" + +@title + + + +

Blazor Bar Chart

+
+ A Blazor Bootstrap bar chart component is used to represent data values as vertical bars. It is sometimes used to show trend data and to compare multiple data sets side by side. +
+ + +
In the below example, a categorical 12-color palette is used. Use ColorBuilder.CategoricalTwelveColors to get the 12 colors.
+ + In the below example, a maximum of 12 datasets are allowed. + + + + + + +@code { + private string pageUrl = "/charts/bar-chart"; + private string title = "Blazor Bar Chart"; + private string description = "A Blazor Bootstrap bar chart component is used to represent data values as vertical bars. It is sometimes used to show trend data and to compare multiple data sets side by side."; + private string imageUrl = "https://i.imgur.com/FGgEMp6.jpg"; +} diff --git a/BlazorBootstrap.Demo/Pages/Charts/BarCharts/Charts_Demo_01_Static_Colors.razor b/BlazorBootstrap.Demo/Pages/Charts/BarCharts/Charts_Demo_01_Static_Colors.razor new file mode 100644 index 000000000..013e49341 --- /dev/null +++ b/BlazorBootstrap.Demo/Pages/Charts/BarCharts/Charts_Demo_01_Static_Colors.razor @@ -0,0 +1,170 @@ +@using BlazorBootstrap.Extensions +@using BlazorBootstrap.Utilities; + + + + + + + + + +@code { + private BarChart barChart = default!; + private BarChartOptions barChartOptions = default!; + private ChartData chartData = default!; + + private int datasetsCount = 0; + private int labelsCount = 0; + private string[] months = { "January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December" }; + private Random random = new(); + + protected override void OnInitialized() + { + chartData = new ChartData { Labels = GetDefaultDataLabels(6), Datasets = GetDefaultDataSets(3) }; + barChartOptions = new BarChartOptions { Responsive = true, Interaction = new Interaction { Mode = InteractionMode.Index } }; + } + + protected override async Task OnAfterRenderAsync(bool firstRender) + { + if (firstRender) + { + await barChart.InitializeAsync(chartData, barChartOptions); + } + await base.OnAfterRenderAsync(firstRender); + } + + private async Task RandomizeAsync() + { + if (chartData is null || chartData.Datasets is null || !chartData.Datasets.Any()) return; + + var newDatasets = new List(); + + foreach (var dataset in chartData.Datasets) + { + if (dataset is BarChartDataset barChartDataset + && barChartDataset is not null + && barChartDataset.Data is not null) + { + var count = barChartDataset.Data.Count; + + var newData = new List(); + for (var i = 0; i < count; i++) + { + newData.Add(random.Next(200)); + } + + barChartDataset.Data = newData; + newDatasets.Add(barChartDataset); + } + } + + chartData.Datasets = newDatasets; + + await barChart.UpdateAsync(chartData, barChartOptions); + } + + private async Task AddDatasetAsync() + { + if (chartData is null || chartData.Datasets is null) return; + + if (datasetsCount >= 12) + return; + + var chartDataset = GetRandomBarChartDataset(); + chartData = await barChart.AddDatasetAsync(chartData, chartDataset, barChartOptions); + } + + private async Task AddDataAsync() + { + if (chartData is null || chartData.Datasets is null) + return; + + if (labelsCount >= 12) + return; + + var data = new List(); + foreach (var dataset in chartData.Datasets) + { + if (dataset is BarChartDataset barChartDataset) + data.Add(new ChartDatasetData(barChartDataset.Label, random.Next(200))); + } + + chartData = await barChart.AddDataAsync(chartData, GetNextDataLabel(), data); + } + + private async Task ShowHorizontalBarChartAsync() + { + barChartOptions.IndexAxis = "y"; + await barChart.UpdateAsync(chartData, barChartOptions); + } + + private async Task ShowVerticalBarChartAsync() + { + barChartOptions.IndexAxis = "x"; + await barChart.UpdateAsync(chartData, barChartOptions); + } + + #region Data Preparation + + private List GetDefaultDataSets(int numberOfDatasets) + { + var datasets = new List(); + + for (var index = 0; index < numberOfDatasets; index++) + { + datasets.Add(GetRandomBarChartDataset()); + } + + return datasets; + } + + private BarChartDataset GetRandomBarChartDataset() + { + // random color + var c = ColorBuilder.CategoricalTwelveColors[datasetsCount].ToColor(); + + datasetsCount += 1; + + Console.WriteLine($"Bar Chart: Color Name: {c.Name}, HEX: {c.ToHexString()}, RGB: {c.ToRgbString()}, IsNamedColor: {c.IsNamedColor}"); + + return new BarChartDataset() + { + Label = $"Product {datasetsCount}", + Data = GetRandomData(), + BackgroundColor = new List { c.ToRgbString() }, + BorderColor = new List { c.ToRgbString() }, + BorderWidth = new List { 0 }, + }; + } + + private List GetRandomData() + { + var data = new List(); + for (var index = 0; index < labelsCount; index++) + { + data.Add(random.Next(200)); + } + + return data; + } + + private List GetDefaultDataLabels(int numberOfLabels) + { + var labels = new List(); + for (var index = 0; index < numberOfLabels; index++) + { + labels.Add(GetNextDataLabel()); + } + + return labels; + } + + private string GetNextDataLabel() + { + labelsCount += 1; + return months[labelsCount - 1]; + } + + #endregion Data Preparation +} diff --git a/BlazorBootstrap.Demo/Pages/Charts/BarCharts/Charts_Demo_02_Dynamic_Colors.razor b/BlazorBootstrap.Demo/Pages/Charts/BarCharts/Charts_Demo_02_Dynamic_Colors.razor new file mode 100644 index 000000000..47780dcf6 --- /dev/null +++ b/BlazorBootstrap.Demo/Pages/Charts/BarCharts/Charts_Demo_02_Dynamic_Colors.razor @@ -0,0 +1,166 @@ +@using BlazorBootstrap.Extensions +@using Color = System.Drawing.Color + + + + + + + + + +@code { + private BarChart barChart = default!; + private BarChartOptions barChartOptions = default!; + private ChartData chartData = default!; + + private int datasetsCount = 0; + private int labelsCount = 0; + private string[] months = { "January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December" }; + private Random random = new(); + + protected override void OnInitialized() + { + chartData = new ChartData { Labels = GetDefaultDataLabels(6), Datasets = GetDefaultDataSets(3) }; + barChartOptions = new BarChartOptions { Responsive = true, Interaction = new Interaction { Mode = InteractionMode.Index } }; + } + + protected override async Task OnAfterRenderAsync(bool firstRender) + { + if (firstRender) + { + await barChart.InitializeAsync(chartData, barChartOptions); + } + await base.OnAfterRenderAsync(firstRender); + } + + private async Task RandomizeAsync() + { + if (chartData is null || chartData.Datasets is null || !chartData.Datasets.Any()) return; + + var newDatasets = new List(); + + foreach (var dataset in chartData.Datasets) + { + if (dataset is BarChartDataset barChartDataset + && barChartDataset is not null + && barChartDataset.Data is not null) + { + var count = barChartDataset.Data.Count; + + var newData = new List(); + for (var i = 0; i < count; i++) + { + newData.Add(random.Next(200)); + } + + barChartDataset.Data = newData; + newDatasets.Add(barChartDataset); + } + } + + chartData.Datasets = newDatasets; + + await barChart.UpdateAsync(chartData, barChartOptions); + } + + private async Task AddDatasetAsync() + { + if (chartData is null || chartData.Datasets is null) return; + + var chartDataset = GetRandomBarChartDataset(); + chartData = await barChart.AddDatasetAsync(chartData, chartDataset, barChartOptions); + } + + private async Task AddDataAsync() + { + if (chartData is null || chartData.Datasets is null) + return; + + if (labelsCount >= 12) + return; + + var data = new List(); + foreach (var dataset in chartData.Datasets) + { + if (dataset is BarChartDataset barChartDataset) + data.Add(new ChartDatasetData(barChartDataset.Label, random.Next(200))); + } + + chartData = await barChart.AddDataAsync(chartData, GetNextDataLabel(), data); + } + + private async Task ShowHorizontalBarChartAsync() + { + barChartOptions.IndexAxis = "y"; + await barChart.UpdateAsync(chartData, barChartOptions); + } + + private async Task ShowVerticalBarChartAsync() + { + barChartOptions.IndexAxis = "x"; + await barChart.UpdateAsync(chartData, barChartOptions); + } + + #region Data Preparation + + private List GetDefaultDataSets(int numberOfDatasets) + { + var datasets = new List(); + + for (var index = 0; index < numberOfDatasets; index++) + { + datasets.Add(GetRandomBarChartDataset()); + } + + return datasets; + } + + private BarChartDataset GetRandomBarChartDataset() + { + datasetsCount += 1; + + // random color + var c = Color.FromArgb(random.Next(256), random.Next(256), random.Next(256)); + Console.WriteLine($"Bar Chart: Color Name: {c.Name}, HEX: {c.ToHexString()}, RGB: {c.ToRgbString()}, IsNamedColor: {c.IsNamedColor}"); + + return new BarChartDataset() + { + Label = $"Product {datasetsCount}", + Data = GetRandomData(), + BackgroundColor = new List { c.ToRgbString() }, + BorderColor = new List { c.ToRgbString() }, + BorderWidth = new List { 0 }, + }; + } + + private List GetRandomData() + { + var data = new List(); + for (var index = 0; index < labelsCount; index++) + { + data.Add(random.Next(200)); + } + + return data; + } + + private List GetDefaultDataLabels(int numberOfLabels) + { + var labels = new List(); + for (var index = 0; index < numberOfLabels; index++) + { + labels.Add(GetNextDataLabel()); + } + + return labels; + } + + private string GetNextDataLabel() + { + labelsCount += 1; + return months[labelsCount - 1]; + } + + #endregion Data Preparation +} diff --git a/BlazorBootstrap.Demo/Pages/Charts/ChartsDocumentation.razor b/BlazorBootstrap.Demo/Pages/Charts/ChartsDocumentation.razor index 82989b832..58bb25a8b 100644 --- a/BlazorBootstrap.Demo/Pages/Charts/ChartsDocumentation.razor +++ b/BlazorBootstrap.Demo/Pages/Charts/ChartsDocumentation.razor @@ -15,10 +15,10 @@
At this moment we are supporting four blazor chart types.
    -
  1. Bar Chart
  2. -
  3. Doughnut Chart
  4. -
  5. Line Chart
  6. -
  7. Pie Chart
  8. +
  9. Bar Chart
  10. +
  11. Doughnut Chart
  12. +
  13. Line Chart
  14. +
  15. Pie Chart
@@ -26,26 +26,13 @@ -Refer starter template for charts setup. - - - - - - - - - - - - - -@* -*@ +
+ Refer starter template for charts setup. +
@code { private string pageUrl = "/charts"; private string title = "Blazor Charts"; private string description = "Blazor Bootstrap charts are well-designed chart components on top of Chart.js to visualize data. It contains a rich UI gallery of charts that cater to all charting scenarios. Its high performance helps render large amounts of data quickly."; private string imageUrl = "https://i.imgur.com/FGgEMp6.jpg"; -} \ No newline at end of file +} diff --git a/BlazorBootstrap.Demo/Pages/Charts/Charts_Demo_01_BarChart_Examples.razor b/BlazorBootstrap.Demo/Pages/Charts/Charts_Demo_01_BarChart_Examples.razor deleted file mode 100644 index 0a7d586fb..000000000 --- a/BlazorBootstrap.Demo/Pages/Charts/Charts_Demo_01_BarChart_Examples.razor +++ /dev/null @@ -1,81 +0,0 @@ -@using BlazorBootstrap.Extensions -@using Color = System.Drawing.Color - - - - - - - -@code { - private BarChart barChart; - - private ChartData chartData; - private BarChartOptions chartOptions; - - Random random = new Random(); - - protected override void OnInitialized() - { - chartData = new ChartData - { - Labels = new List { "January", "February", "March", "April", "May", "June", "July" }, - Datasets = new List() - }; - - chartData.Datasets.Add(GetRandomBarChartDataset()); - chartData.Datasets.Add(GetRandomBarChartDataset()); - chartData.Datasets.Add(GetRandomBarChartDataset()); - - chartOptions = new BarChartOptions - { - Responsive = true, - Interaction = new Interaction { Mode = InteractionMode.Index } - }; - } - - protected override async Task OnAfterRenderAsync(bool firstRender) - { - if (firstRender) - { - await barChart.InitializeAsync(chartData, chartOptions); - } - await base.OnAfterRenderAsync(firstRender); - } - - private async Task AddDataAsync() - { - if (chartData is null || chartData.Datasets is null) return; - - chartData.Datasets.Add(GetRandomBarChartDataset()); - await barChart.UpdateAsync(chartData, chartOptions); - } - - private async Task ShowHorizontalBarChartAsync() - { - chartOptions.IndexAxis = "y"; - await barChart.UpdateAsync(chartData, chartOptions); - } - - private async Task ShowVerticalBarChartAsync() - { - chartOptions.IndexAxis = "x"; - await barChart.UpdateAsync(chartData, chartOptions); - } - - private BarChartDataset GetRandomBarChartDataset() - { - // random color - var c = Color.FromArgb(random.Next(256), random.Next(256), random.Next(256)); - Console.WriteLine($"Bar Chart: Color Name: {c.Name}, HEX: {c.ToHexString()}, RGB: {c.ToRgbString()}, IsNamedColor: {c.IsNamedColor}"); - - return new BarChartDataset() - { - Label = $"Bar chart dataset {chartData.Datasets.Count + 1}", - Data = new List { random.Next(120), random.Next(120), random.Next(120), random.Next(120), random.Next(120), random.Next(120), random.Next(120) }, - BackgroundColor = new List { c.ToRgbString() }, - BorderColor = new List { c.ToRgbString() }, - BorderWidth = new List { 0 }, - }; - } -} diff --git a/BlazorBootstrap.Demo/Pages/Charts/Charts_Demo_02_BubbleChart_Examples.razor b/BlazorBootstrap.Demo/Pages/Charts/Charts_Demo_02_BubbleChart_Examples.razor deleted file mode 100644 index 633280344..000000000 --- a/BlazorBootstrap.Demo/Pages/Charts/Charts_Demo_02_BubbleChart_Examples.razor +++ /dev/null @@ -1,5 +0,0 @@ -

Charts_Demo_02_BubbleChart_Examples

- -@code { - -} diff --git a/BlazorBootstrap.Demo/Pages/Charts/Charts_Demo_03_DoughnutChart_Examples.razor b/BlazorBootstrap.Demo/Pages/Charts/Charts_Demo_03_DoughnutChart_Examples.razor deleted file mode 100644 index 19a971815..000000000 --- a/BlazorBootstrap.Demo/Pages/Charts/Charts_Demo_03_DoughnutChart_Examples.razor +++ /dev/null @@ -1,93 +0,0 @@ -@using BlazorBootstrap.Extensions -@using Color = System.Drawing.Color - - - - - -@code { - private DoughnutChart doughnutChart; - - private ChartData chartData; - private DoughnutChartOptions chartOptions; - private List backgroundColors; - - Random random = new Random(); - - protected override void OnInitialized() - { - // prepare background colors - PrepareBackgroundColors(); - - chartData = new ChartData - { - Labels = new List { "Team 1", "Team 2", "Team 3", "Team 4", "Team 5", "Team 6" }, - Datasets = new List() - }; - - chartData.Datasets.Add(GetRandomDoughnutChartDataset()); - - chartOptions = new DoughnutChartOptions - { - Responsive = true, - }; - } - - protected override async Task OnAfterRenderAsync(bool firstRender) - { - if (firstRender) - { - await doughnutChart.InitializeAsync(chartData, chartOptions); - } - await base.OnAfterRenderAsync(firstRender); - } - - private async Task AddDataAsync() - { - if (chartData is null || chartData.Datasets is null) return; - - chartData.Datasets.Add(GetRandomDoughnutChartDataset()); - await doughnutChart.UpdateAsync(chartData, chartOptions); - } - - private DoughnutChartDataset GetRandomDoughnutChartDataset() - { - return new DoughnutChartDataset() - { - Data = new List - { - random.Next(120), - random.Next(120), - random.Next(120), - random.Next(120), - random.Next(120), - random.Next(120) - }, - BackgroundColor = new List - { - backgroundColors[0], - backgroundColors[1], - backgroundColors[2], - backgroundColors[3], - backgroundColors[4], - backgroundColors[5] - }, - }; - } - - private void PrepareBackgroundColors() - { - if (backgroundColors is null) - { - backgroundColors = new List(); - backgroundColors.Add(GetRandomColor().ToRgbString()); - backgroundColors.Add(GetRandomColor().ToRgbString()); - backgroundColors.Add(GetRandomColor().ToRgbString()); - backgroundColors.Add(GetRandomColor().ToRgbString()); - backgroundColors.Add(GetRandomColor().ToRgbString()); - backgroundColors.Add(GetRandomColor().ToRgbString()); - } - } - - private Color GetRandomColor() => Color.FromArgb(random.Next(256), random.Next(256), random.Next(256)); -} diff --git a/BlazorBootstrap.Demo/Pages/Charts/Charts_Demo_04_LineChart_Examples.razor b/BlazorBootstrap.Demo/Pages/Charts/Charts_Demo_04_LineChart_Examples.razor deleted file mode 100644 index defc58adc..000000000 --- a/BlazorBootstrap.Demo/Pages/Charts/Charts_Demo_04_LineChart_Examples.razor +++ /dev/null @@ -1,82 +0,0 @@ -@using BlazorBootstrap.Extensions -@using Color = System.Drawing.Color - - - - - - - -@code { - private LineChart lineChart; - - private ChartData chartData; - private LineChartOptions chartOptions; - - Random random = new Random(); - - protected override void OnInitialized() - { - chartData = new ChartData - { - Labels = new List { "Team 1", "Team 2", "Team 3", "Team 4", "Team 5", "Team 6" }, - Datasets = new List() - }; - - chartData.Datasets.Add(GetRandomLineChartDataset()); - chartData.Datasets.Add(GetRandomLineChartDataset()); - chartData.Datasets.Add(GetRandomLineChartDataset()); - - chartOptions = new LineChartOptions - { - Responsive = true, - Interaction = new Interaction { Mode = InteractionMode.Index } - }; - } - - protected override async Task OnAfterRenderAsync(bool firstRender) - { - await lineChart.UpdateAsync(chartData, chartOptions); - await base.OnAfterRenderAsync(firstRender); - } - - private async Task AddDataAsync() - { - if (chartData is null || chartData.Datasets is null) return; - - chartData.Datasets.Add(GetRandomLineChartDataset()); - await lineChart.UpdateAsync(chartData, chartOptions); - } - - private async Task ShowHorizontalLineChartAsync() - { - chartOptions.IndexAxis = "y"; - await lineChart.UpdateAsync(chartData, chartOptions); - } - - private async Task ShowVerticalLineChartAsync() - { - chartOptions.IndexAxis = "x"; - await lineChart.UpdateAsync(chartData, chartOptions); - } - - private LineChartDataset GetRandomLineChartDataset() - { - // random color - var c = Color.FromArgb(random.Next(256), random.Next(256), random.Next(256)); - Console.WriteLine($"Line Chart: Color Name: {c.Name}, HEX: {c.ToHexString()}, RGB: {c.ToRgbString()}, IsNamedColor: {c.IsNamedColor}"); - - return new LineChartDataset() - { - Label = $"Line chart dataset {chartData.Datasets.Count + 1}", - Data = new List { random.Next(200), random.Next(200), random.Next(200), random.Next(200), random.Next(200), random.Next(200) }, - BackgroundColor = new List { c.ToRgbString() }, - BorderColor = new List { c.ToRgbString() }, - BorderWidth = new List { 2 }, - HoverBorderWidth = new List { 4 }, - PointBackgroundColor = new List { c.ToRgbString() }, - PointRadius = new List { 0 }, // hide points - PointHoverRadius = new List { 4 }, - }; - } -} diff --git a/BlazorBootstrap.Demo/Pages/Charts/Charts_Demo_05_PieChart_Examples.razor b/BlazorBootstrap.Demo/Pages/Charts/Charts_Demo_05_PieChart_Examples.razor deleted file mode 100644 index ec0819e17..000000000 --- a/BlazorBootstrap.Demo/Pages/Charts/Charts_Demo_05_PieChart_Examples.razor +++ /dev/null @@ -1,93 +0,0 @@ -@using BlazorBootstrap.Extensions -@using Color = System.Drawing.Color - - - - - -@code { - private PieChart pieChart; - - private ChartData chartData; - private PieChartOptions chartOptions; - private List backgroundColors; - - Random random = new Random(); - - protected override void OnInitialized() - { - // prepare background colors - PrepareBackgroundColors(); - - chartData = new ChartData - { - Labels = new List { "Team 1", "Team 2", "Team 3", "Team 4", "Team 5", "Team 6" }, - Datasets = new List() - }; - - chartData.Datasets.Add(GetRandomPieChartDataset()); - - chartOptions = new PieChartOptions - { - Responsive = true, - }; - } - - protected override async Task OnAfterRenderAsync(bool firstRender) - { - if (firstRender) - { - await pieChart.InitializeAsync(chartData, chartOptions); - } - await base.OnAfterRenderAsync(firstRender); - } - - private async Task AddDataAsync() - { - if (chartData is null || chartData.Datasets is null) return; - - chartData.Datasets.Add(GetRandomPieChartDataset()); - await pieChart.UpdateAsync(chartData, chartOptions); - } - - private PieChartDataset GetRandomPieChartDataset() - { - return new PieChartDataset() - { - Data = new List - { - random.Next(120), - random.Next(120), - random.Next(120), - random.Next(120), - random.Next(120), - random.Next(120) - }, - BackgroundColor = new List - { - backgroundColors[0], - backgroundColors[1], - backgroundColors[2], - backgroundColors[3], - backgroundColors[4], - backgroundColors[5] - }, - }; - } - - private void PrepareBackgroundColors() - { - if (backgroundColors is null) - { - backgroundColors = new List(); - backgroundColors.Add(GetRandomColor().ToRgbString()); - backgroundColors.Add(GetRandomColor().ToRgbString()); - backgroundColors.Add(GetRandomColor().ToRgbString()); - backgroundColors.Add(GetRandomColor().ToRgbString()); - backgroundColors.Add(GetRandomColor().ToRgbString()); - backgroundColors.Add(GetRandomColor().ToRgbString()); - } - } - - private Color GetRandomColor() => Color.FromArgb(random.Next(256), random.Next(256), random.Next(256)); -} diff --git a/BlazorBootstrap.Demo/Pages/Charts/Charts_Demo_06_PolarAreaChart_Examples.razor b/BlazorBootstrap.Demo/Pages/Charts/Charts_Demo_06_PolarAreaChart_Examples.razor deleted file mode 100644 index 017b8a271..000000000 --- a/BlazorBootstrap.Demo/Pages/Charts/Charts_Demo_06_PolarAreaChart_Examples.razor +++ /dev/null @@ -1,5 +0,0 @@ -

Charts_Demo_06_PolarAreaChart_Examples

- -@code { - -} diff --git a/BlazorBootstrap.Demo/Pages/Charts/Charts_Demo_07_RadarChart_Examples.razor b/BlazorBootstrap.Demo/Pages/Charts/Charts_Demo_07_RadarChart_Examples.razor deleted file mode 100644 index ffb337f0c..000000000 --- a/BlazorBootstrap.Demo/Pages/Charts/Charts_Demo_07_RadarChart_Examples.razor +++ /dev/null @@ -1,5 +0,0 @@ -

Charts_Demo_07_RadarChart_Examples

- -@code { - -} diff --git a/BlazorBootstrap.Demo/Pages/Charts/Charts_Demo_08_ScatterChart_Examples.razor b/BlazorBootstrap.Demo/Pages/Charts/Charts_Demo_08_ScatterChart_Examples.razor deleted file mode 100644 index 02dc7079c..000000000 --- a/BlazorBootstrap.Demo/Pages/Charts/Charts_Demo_08_ScatterChart_Examples.razor +++ /dev/null @@ -1,5 +0,0 @@ -

Charts_Demo_08_ScatterChart_Examples

- -@code { - -} diff --git a/BlazorBootstrap.Demo/Pages/Charts/DoughnutCharts/ChartsDocumentation.razor b/BlazorBootstrap.Demo/Pages/Charts/DoughnutCharts/ChartsDocumentation.razor new file mode 100644 index 000000000..fa6dda65f --- /dev/null +++ b/BlazorBootstrap.Demo/Pages/Charts/DoughnutCharts/ChartsDocumentation.razor @@ -0,0 +1,22 @@ +@page "/charts/doughnut-chart" + +@title + + + +

Blazor Doughnut Chart

+
+ A Blazor Bootstrap donut chart component is a circular chart that shows the proportional values of different categories. + It is similar to a pie chart, but the center of the donut chart is hollow. + This makes it easier to see the individual values of each category. +
+ + + + +@code { + private string pageUrl = "/charts/doughnut-chart"; + private string title = "Blazor Doughnut Charts"; + private string description = "A Blazor donut chart component is a circular chart that shows the proportional values of different categories. It is similar to a pie chart, but the center of the donut chart is hollow. This makes it easier to see the individual values of each category."; + private string imageUrl = "https://i.imgur.com/FGgEMp6.jpg"; +} diff --git a/BlazorBootstrap.Demo/Pages/Charts/DoughnutCharts/Charts_Demo_01_DoughnutChart_Examples.razor b/BlazorBootstrap.Demo/Pages/Charts/DoughnutCharts/Charts_Demo_01_DoughnutChart_Examples.razor new file mode 100644 index 000000000..ab45ee82b --- /dev/null +++ b/BlazorBootstrap.Demo/Pages/Charts/DoughnutCharts/Charts_Demo_01_DoughnutChart_Examples.razor @@ -0,0 +1,169 @@ +@using BlazorBootstrap.Extensions +@using Color = System.Drawing.Color + + + + + + + +@code { + private DoughnutChart doughnutChart = default!; + private DoughnutChartOptions doughnutChartOptions = default!; + private ChartData chartData = default!; + private List backgroundColors = default!; + + private int datasetsCount = 0; + private int dataLabelsCount = 0; + + private Random random = new(); + + protected override void OnInitialized() + { + chartData = new ChartData { Labels = GetDefaultDataLabels(5), Datasets = GetDefaultDataSets(1) }; + doughnutChartOptions = new() { Responsive = true, }; + } + + protected override async Task OnAfterRenderAsync(bool firstRender) + { + if (firstRender) + { + await doughnutChart.InitializeAsync(chartData, doughnutChartOptions); + } + await base.OnAfterRenderAsync(firstRender); + } + + private async Task RandomizeAsync() + { + if (chartData is null || chartData.Datasets is null || !chartData.Datasets.Any()) return; + + var newDatasets = new List(); + + foreach (var dataset in chartData.Datasets) + { + if (dataset is DoughnutChartDataset doughnutChartDataset + && doughnutChartDataset is not null + && doughnutChartDataset.Data is not null) + { + var count = doughnutChartDataset.Data.Count; + + var newData = new List(); + for (var i = 0; i < count; i++) + { + newData.Add(random.Next(0, 100)); + } + + doughnutChartDataset.Data = newData; + newDatasets.Add(doughnutChartDataset); + } + } + + chartData.Datasets = newDatasets; + + await doughnutChart.UpdateAsync(chartData, doughnutChartOptions); + } + + private async Task AddDatasetAsync() + { + if (chartData is null || chartData.Datasets is null) return; + + var chartDataset = GetRandomDoughnutChartDataset(); + chartData = await doughnutChart.AddDatasetAsync(chartData, chartDataset, doughnutChartOptions); + } + + private async Task AddDataAsync() + { + if (chartData is null || chartData.Datasets is null) + return; + + var data = new List(); + foreach (var dataset in chartData.Datasets) + { + if (dataset is DoughnutChartDataset doughnutChartDataset) + data.Add(new ChartDatasetData(doughnutChartDataset.Label, random.Next(0, 100))); + } + + chartData = await doughnutChart.AddDataAsync(chartData, GetNextDataLabel(), data); + } + + #region Data Preparation + + private List GetDefaultDataSets(int numberOfDatasets) + { + var datasets = new List(); + + for (var index = 0; index < numberOfDatasets; index++) + { + datasets.Add(GetRandomDoughnutChartDataset()); + } + + return datasets; + } + + private DoughnutChartDataset GetRandomDoughnutChartDataset() + { + datasetsCount += 1; + return new() { Label = $"Team {datasetsCount}", Data = GetRandomData(), BackgroundColor = GetRandomBackgroundColors() }; + } + + private List GetRandomData() + { + var data = new List(); + for (var index = 0; index < dataLabelsCount; index++) + { + data.Add(random.Next(0, 100)); + } + + return data; + } + + private List GetRandomBackgroundColors() + { + // prepare background colors + PrepareBackgroundColors(); + + var colors = new List(); + for (var index = 0; index < dataLabelsCount; index++) + { + colors.Add(backgroundColors[index]); + } + + return colors; + } + + private List GetDefaultDataLabels(int numberOfLabels) + { + var labels = new List(); + for (var index = 0; index < numberOfLabels; index++) + { + labels.Add(GetNextDataLabel()); + } + + return labels; + } + + private string GetNextDataLabel() + { + dataLabelsCount += 1; + return $"Product {dataLabelsCount}"; + } + + private void PrepareBackgroundColors() + { + backgroundColors ??= new List(); + + if (dataLabelsCount == backgroundColors.Count) + return; + + var backgroundColorsCount = dataLabelsCount - backgroundColors.Count; + + for (int index = 0; index < backgroundColorsCount; index++) + { + backgroundColors.Add(GetRandomColor().ToRgbString()); + } + } + + private Color GetRandomColor() => Color.FromArgb(random.Next(256), random.Next(256), random.Next(256)); + + #endregion Data Preparation +} diff --git a/BlazorBootstrap.Demo/Pages/Charts/LineCharts/ChartsDocumentation.razor b/BlazorBootstrap.Demo/Pages/Charts/LineCharts/ChartsDocumentation.razor new file mode 100644 index 000000000..1d7995c53 --- /dev/null +++ b/BlazorBootstrap.Demo/Pages/Charts/LineCharts/ChartsDocumentation.razor @@ -0,0 +1,23 @@ +@page "/charts/line-chart" + +@title + + + +

Blazor Line Chart

+
+ A Blazor Bootstrap line chart component is a graphical representation of data that uses a series of connected points to show how the data changes over time. + It is a type of x-y chart, where the x-axis represents the independent variable, such as time, and the y-axis represents the dependent variable, such as the value. +
+ + + +
+ + +@code { + private string pageUrl = "/charts/line-chart"; + private string title = "Blazor Line Chart"; + private string description = "A Blazor Bootstrap line chart component is a graphical representation of data that uses a series of connected points to show how the data changes over time. It is a type of x-y chart, where the x-axis represents the independent variable, such as time, and the y-axis represents the dependent variable, such as the value."; + private string imageUrl = "https://i.imgur.com/FGgEMp6.jpg"; +} diff --git a/BlazorBootstrap.Demo/Pages/Charts/LineCharts/Charts_Demo_01_LineChart_Examples.razor b/BlazorBootstrap.Demo/Pages/Charts/LineCharts/Charts_Demo_01_LineChart_Examples.razor new file mode 100644 index 000000000..4ae92f628 --- /dev/null +++ b/BlazorBootstrap.Demo/Pages/Charts/LineCharts/Charts_Demo_01_LineChart_Examples.razor @@ -0,0 +1,167 @@ +@using BlazorBootstrap.Extensions +@using Color = System.Drawing.Color + + + + + + + + + +@code { + private LineChart lineChart = default!; + private LineChartOptions lineChartOptions = default!; + private ChartData chartData = default!; + + private int datasetsCount = 0; + private int labelsCount = 0; + + private Random random = new(); + + protected override void OnInitialized() + { + chartData = new ChartData { Labels = GetDefaultDataLabels(6), Datasets = GetDefaultDataSets(3) }; + lineChartOptions = new() { Responsive = true, Interaction = new Interaction { Mode = InteractionMode.Index } }; + } + + protected override async Task OnAfterRenderAsync(bool firstRender) + { + if (firstRender) + { + await lineChart.InitializeAsync(chartData, lineChartOptions); + } + await base.OnAfterRenderAsync(firstRender); + } + + private async Task RandomizeAsync() + { + if (chartData is null || chartData.Datasets is null || !chartData.Datasets.Any()) return; + + var newDatasets = new List(); + + foreach (var dataset in chartData.Datasets) + { + if (dataset is LineChartDataset lineChartDataset + && lineChartDataset is not null + && lineChartDataset.Data is not null) + { + var count = lineChartDataset.Data.Count; + + var newData = new List(); + for (var i = 0; i < count; i++) + { + newData.Add(random.Next(200)); + } + + lineChartDataset.Data = newData; + newDatasets.Add(lineChartDataset); + } + } + + chartData.Datasets = newDatasets; + + await lineChart.UpdateAsync(chartData, lineChartOptions); + } + + private async Task AddDatasetAsync() + { + if (chartData is null || chartData.Datasets is null) return; + + var chartDataset = GetRandomLineChartDataset(); + chartData = await lineChart.AddDatasetAsync(chartData, chartDataset, lineChartOptions); + } + + private async Task AddDataAsync() + { + if (chartData is null || chartData.Datasets is null) + return; + + var data = new List(); + foreach (var dataset in chartData.Datasets) + { + if (dataset is LineChartDataset lineChartDataset) + data.Add(new ChartDatasetData(lineChartDataset.Label, random.Next(200))); + } + + chartData = await lineChart.AddDataAsync(chartData, GetNextDataLabel(), data); + } + + private async Task ShowHorizontalLineChartAsync() + { + lineChartOptions.IndexAxis = "y"; + await lineChart.UpdateAsync(chartData, lineChartOptions); + } + + private async Task ShowVerticalLineChartAsync() + { + lineChartOptions.IndexAxis = "x"; + await lineChart.UpdateAsync(chartData, lineChartOptions); + } + + #region Data Preparation + + private List GetDefaultDataSets(int numberOfDatasets) + { + var datasets = new List(); + + for (var index = 0; index < numberOfDatasets; index++) + { + datasets.Add(GetRandomLineChartDataset()); + } + + return datasets; + } + + private LineChartDataset GetRandomLineChartDataset() + { + datasetsCount += 1; + + // random color + var c = Color.FromArgb(random.Next(256), random.Next(256), random.Next(256)); + Console.WriteLine($"Line Chart: Color Name: {c.Name}, HEX: {c.ToHexString()}, RGB: {c.ToRgbString()}, IsNamedColor: {c.IsNamedColor}"); + + return new LineChartDataset() + { + Label = $"Team {datasetsCount}", + Data = GetRandomData(), + BackgroundColor = new List { c.ToRgbString() }, + BorderColor = new List { c.ToRgbString() }, + BorderWidth = new List { 2 }, + HoverBorderWidth = new List { 4 }, + PointBackgroundColor = new List { c.ToRgbString() }, + PointRadius = new List { 0 }, // hide points + PointHoverRadius = new List { 4 }, + }; + } + + private List GetRandomData() + { + var data = new List(); + for (var index = 0; index < labelsCount; index++) + { + data.Add(random.Next(200)); + } + + return data; + } + + private List GetDefaultDataLabels(int numberOfLabels) + { + var labels = new List(); + for (var index = 0; index < numberOfLabels; index++) + { + labels.Add(GetNextDataLabel()); + } + + return labels; + } + + private string GetNextDataLabel() + { + labelsCount += 1; + return $"Day {labelsCount}"; + } + + #endregion Data Preparation +} diff --git a/BlazorBootstrap.Demo/Pages/Charts/LineCharts/Charts_Demo_02_LineChart_Examples.razor b/BlazorBootstrap.Demo/Pages/Charts/LineCharts/Charts_Demo_02_LineChart_Examples.razor new file mode 100644 index 000000000..4ec611c15 --- /dev/null +++ b/BlazorBootstrap.Demo/Pages/Charts/LineCharts/Charts_Demo_02_LineChart_Examples.razor @@ -0,0 +1,105 @@ + + + + + +@code { + private LineChart lineChart; + private LineChartOptions lineChartOptions; + private ChartData chartData; + private List labels; + + private List indiaRunsArray = new() { 9, 20, 29, 33, 50, 66, 75, 86, 91, 105, 120, 126, 141, 150, 156, 164, 177, 180, 184, 195 }; + private List englandRunsArray = new() { 1, 1, 8, 19, 24, 26, 39, 47, 56, 66, 75, 88, 95, 100, 109, 114, 124, 129, 140, 142 }; + + private int indiaCurrentOver = 0; + private int englandCurrentOver = 0; + + protected override void OnInitialized() + { + labels = new List { "1", "2", "3", "4", "5", "6", "7", "8", "9", "10", "11", "12", "13", "14", "15", "16", "17", "18", "19", "20" }; + lineChartOptions = GetLineChartOptions(); + chartData = new ChartData { Labels = labels, Datasets = GetDefaultDatasets() }; + } + + protected override async Task OnAfterRenderAsync(bool firstRender) + { + if (firstRender) + await lineChart.UpdateAsync(chartData, lineChartOptions); + } + + private async Task UpdateIndiaNextOverRunsAsync() + { + if (indiaCurrentOver > 0 && indiaCurrentOver > (indiaRunsArray.Count - 1)) + return; + + chartData = await lineChart.AddDataAsync(chartData, $"{indiaCurrentOver + 1}", "India", indiaRunsArray[indiaCurrentOver]); + indiaCurrentOver++; + } + + private async Task UpdateEnglandNextOverRunsAsync() + { + if (englandCurrentOver > 0 && englandCurrentOver > (englandRunsArray.Count - 1)) + return; + + chartData = await lineChart.AddDataAsync(chartData, $"{englandCurrentOver + 1}", "England", englandRunsArray[englandCurrentOver]); + englandCurrentOver++; + } + + private List GetDefaultDatasets() + { + var datasets = new List() + { + new LineChartDataset() + { + Label = "India", + Data = new List(), + BackgroundColor = new List{ "rgb(88, 80, 141)" }, + BorderColor = new List{ "rgb(88, 80, 141)" }, + BorderWidth = new List{2}, + HoverBorderWidth = new List{4}, + PointBackgroundColor = new List{ "rgb(88, 80, 141)" }, + PointBorderColor = new List{ "rgb(88, 80, 141)" }, + PointRadius = new List{0}, // hide points + PointHoverRadius = new List{4}, + }, + new LineChartDataset() + { + Label = "England", + Data = new List(), + BackgroundColor = new List{ "rgb(255, 166, 0)" }, + BorderColor = new List{ "rgb(255, 166, 0)" }, + BorderWidth = new List{2}, + HoverBorderWidth = new List{4}, + PointBackgroundColor = new List{ "rgb(255, 166, 0)" }, + PointBorderColor = new List{ "rgb(255, 166, 0)" }, + PointRadius = new List{0}, // hide points + PointHoverRadius = new List{4}, + } + }; + + return datasets; + } + + private LineChartOptions GetLineChartOptions() + { + var options = new LineChartOptions(); + + options.Interaction.Mode = InteractionMode.Index; + + options.Plugins.Title.Text = "WORM"; + options.Plugins.Title.Display = true; + options.Plugins.Title.Font.Size = 20; + + options.Responsive = true; + + options.Scales.X.Title.Text = "Overs"; + options.Scales.X.Title.Display = true; + + options.Scales.Y.Title.Text = "Runs"; + options.Scales.Y.Title.Display = true; + options.Scales.Y.SuggestedMax = 150; + + return options; + } +} diff --git a/BlazorBootstrap.Demo/Pages/Charts/PieCharts/ChartsDocumentation.razor b/BlazorBootstrap.Demo/Pages/Charts/PieCharts/ChartsDocumentation.razor new file mode 100644 index 000000000..c19e6cbda --- /dev/null +++ b/BlazorBootstrap.Demo/Pages/Charts/PieCharts/ChartsDocumentation.razor @@ -0,0 +1,20 @@ +@page "/charts/pie-chart" + +@title + + + +

Blazor Pie Chart

+
+ A Blazor Bootstrap pie chart component is a circular chart that shows the proportional values of different categories. +
+ + + + +@code { + private string pageUrl = "/charts/pie-chart"; + private string title = "Blazor Pie Chart"; + private string description = "A Blazor Bootstrap pie chart component is a circular chart that shows the proportional values of different categories."; + private string imageUrl = "https://i.imgur.com/FGgEMp6.jpg"; +} diff --git a/BlazorBootstrap.Demo/Pages/Charts/PieCharts/Charts_Demo_01_PieChart_Examples.razor b/BlazorBootstrap.Demo/Pages/Charts/PieCharts/Charts_Demo_01_PieChart_Examples.razor new file mode 100644 index 000000000..4421f2b60 --- /dev/null +++ b/BlazorBootstrap.Demo/Pages/Charts/PieCharts/Charts_Demo_01_PieChart_Examples.razor @@ -0,0 +1,169 @@ +@using BlazorBootstrap.Extensions +@using Color = System.Drawing.Color + + + + + + + +@code { + private PieChart pieChart = default!; + private PieChartOptions pieChartOptions = default!; + private ChartData chartData = default!; + private List backgroundColors = default!; + + private int datasetsCount = 0; + private int dataLabelsCount = 0; + + private Random random = new Random(); + + protected override void OnInitialized() + { + chartData = new ChartData { Labels = GetDefaultDataLabels(5), Datasets = GetDefaultDataSets(1) }; + pieChartOptions = new() { Responsive = true, }; + } + + protected override async Task OnAfterRenderAsync(bool firstRender) + { + if (firstRender) + { + await pieChart.InitializeAsync(chartData, pieChartOptions); + } + await base.OnAfterRenderAsync(firstRender); + } + + private async Task RandomizeAsync() + { + if (chartData is null || chartData.Datasets is null || !chartData.Datasets.Any()) return; + + var newDatasets = new List(); + + foreach (var dataset in chartData.Datasets) + { + if (dataset is PieChartDataset pieChartDataset + && pieChartDataset is not null + && pieChartDataset.Data is not null) + { + var count = pieChartDataset.Data.Count; + + var newData = new List(); + for (var i = 0; i < count; i++) + { + newData.Add(random.Next(0, 100)); + } + + pieChartDataset.Data = newData; + newDatasets.Add(pieChartDataset); + } + } + + chartData.Datasets = newDatasets; + + await pieChart.UpdateAsync(chartData, pieChartOptions); + } + + private async Task AddDatasetAsync() + { + if (chartData is null || chartData.Datasets is null) return; + + var chartDataset = GetRandomPieChartDataset(); + chartData = await pieChart.AddDatasetAsync(chartData, chartDataset, pieChartOptions); + } + + private async Task AddDataAsync() + { + if (chartData is null || chartData.Datasets is null) + return; + + var data = new List(); + foreach (var dataset in chartData.Datasets) + { + if (dataset is PieChartDataset pieChartDataset) + data.Add(new ChartDatasetData(pieChartDataset.Label, random.Next(0, 100))); + } + + chartData = await pieChart.AddDataAsync(chartData, GetNextDataLabel(), data); + } + + #region Data Preparation + + private List GetDefaultDataSets(int numberOfDatasets) + { + var datasets = new List(); + + for (var index = 0; index < numberOfDatasets; index++) + { + datasets.Add(GetRandomPieChartDataset()); + } + + return datasets; + } + + private PieChartDataset GetRandomPieChartDataset() + { + datasetsCount += 1; + return new() { Label = $"Team {datasetsCount}", Data = GetRandomData(), BackgroundColor = GetRandomBackgroundColors() }; + } + + private List GetRandomData() + { + var data = new List(); + for (var index = 0; index < dataLabelsCount; index++) + { + data.Add(random.Next(0, 100)); + } + + return data; + } + + private List GetRandomBackgroundColors() + { + // prepare background colors + PrepareBackgroundColors(); + + var colors = new List(); + for (var index = 0; index < dataLabelsCount; index++) + { + colors.Add(backgroundColors[index]); + } + + return colors; + } + + private List GetDefaultDataLabels(int numberOfLabels) + { + var labels = new List(); + for (var index = 0; index < numberOfLabels; index++) + { + labels.Add(GetNextDataLabel()); + } + + return labels; + } + + private string GetNextDataLabel() + { + dataLabelsCount += 1; + return $"Product {dataLabelsCount}"; + } + + private void PrepareBackgroundColors() + { + backgroundColors ??= new List(); + + if (dataLabelsCount == backgroundColors.Count) + return; + + var backgroundColorsCount = dataLabelsCount - backgroundColors.Count; + + for (int index = 0; index < backgroundColorsCount; index++) + { + backgroundColors.Add(GetRandomColor().ToRgbString()); + } + } + + private Color GetRandomColor() => Color.FromArgb(random.Next(256), random.Next(256), random.Next(256)); + + #endregion Data Preparation +} diff --git a/BlazorBootstrap.Demo/Pages/GettingStarted/server/GettingStartedDocumentation.razor b/BlazorBootstrap.Demo/Pages/GettingStarted/server/GettingStartedDocumentation.razor index 9a1c0b414..826419ee9 100644 --- a/BlazorBootstrap.Demo/Pages/GettingStarted/server/GettingStartedDocumentation.razor +++ b/BlazorBootstrap.Demo/Pages/GettingStarted/server/GettingStartedDocumentation.razor @@ -59,18 +59,36 @@

Either remove or keep the site.css file but make sure you clear it out of any content when the Sidebar component with full layout is used.

+ +
+

Blazor Bootstrap Templates make it easy to create Blazor projects in a minute. Just install the templates with the NuGet package manager and you're good to go!

+

dotnet new install Blazor.Bootstrap.Templates::1.9.1

+

+ Check the Visual Studio project templates GitHub repo here. +

+

+

+ Blazor Bootstrap - Visual Studio project templates +
+

+
+ + + Upgrade the Blazor.Bootstrap NuGet package to the latest available version. + + @code { private string pageUrl = "/getting-started/blazor-server"; private string title = "Getting started with Blazor Bootstrap - Blazor Server Project Setup"; private string description = "High-performance, lightweight, and responsive blazor bootstrap components in a single package from the developers for the developers."; private string imageUrl = "https://i.imgur.com/SCbZVd4.jpg"; - private string version; - [Inject] public IConfiguration Configuration { get; set; } + private string? version; + [Inject] public IConfiguration Configuration { get; set; } = default!; protected override void OnInitialized() { version = $"{Configuration.GetValue("version")}"; // example: v0.6.1 base.OnInitialized(); } -} \ No newline at end of file +} diff --git a/BlazorBootstrap.Demo/Pages/GettingStarted/webassembly/GettingStartedDocumentation.razor b/BlazorBootstrap.Demo/Pages/GettingStarted/webassembly/GettingStartedDocumentation.razor index 4f95c5d1e..105771274 100644 --- a/BlazorBootstrap.Demo/Pages/GettingStarted/webassembly/GettingStartedDocumentation.razor +++ b/BlazorBootstrap.Demo/Pages/GettingStarted/webassembly/GettingStartedDocumentation.razor @@ -57,18 +57,36 @@

Either remove or keep the app.css file but make sure you clear it out of any content when the Sidebar component with full layout is used.

+ +
+

Blazor Bootstrap Templates make it easy to create Blazor projects in a minute. Just install the templates with the NuGet package manager and you're good to go!

+

dotnet new install Blazor.Bootstrap.Templates::1.9.1

+

+ Check the Visual Studio project templates GitHub repo here. +

+

+

+ Blazor Bootstrap - Visual Studio project templates +
+

+
+ + + Upgrade the Blazor.Bootstrap NuGet package to the latest available version. + + @code { private string pageUrl = "/getting-started/blazor-webassembly"; private string title = "Getting started with Blazor Bootstrap - Blazor WebAssembly Project Setup"; private string description = "High-performance, lightweight, and responsive blazor bootstrap components in a single package from the developers for the developers."; private string imageUrl = "https://i.imgur.com/SCbZVd4.jpg"; - private string version; - [Inject] public IConfiguration Configuration { get; set; } + private string? version; + [Inject] public IConfiguration Configuration { get; set; } = default!; protected override void OnInitialized() { version = $"{Configuration.GetValue("version")}"; // example: v0.6.1 base.OnInitialized(); } -} \ No newline at end of file +} diff --git a/BlazorBootstrap.Demo/Pages/Index.razor b/BlazorBootstrap.Demo/Pages/Index.razor index 9d69821af..3b664b3ad 100644 --- a/BlazorBootstrap.Demo/Pages/Index.razor +++ b/BlazorBootstrap.Demo/Pages/Index.razor @@ -28,9 +28,9 @@ -
+
-

Blazor Bootstrap UI & Data Visualization Components

+

All Components

@@ -172,6 +172,74 @@
+ + +
+
+

Data Visualization Components

+
+ + +
+
This demo website is built using the Blazor Bootstrap library and published on the Azure Static Web App. diff --git a/BlazorBootstrap.Demo/Shared/MainLayout.razor.cs b/BlazorBootstrap.Demo/Shared/MainLayout.razor.cs index c6d8d3a80..77115b729 100644 --- a/BlazorBootstrap.Demo/Shared/MainLayout.razor.cs +++ b/BlazorBootstrap.Demo/Shared/MainLayout.razor.cs @@ -1,6 +1,4 @@ -using Microsoft.AspNetCore.Components; - -namespace BlazorBootstrap.Demo.Shared; +namespace BlazorBootstrap.Demo.Shared; public partial class MainLayout : LayoutComponentBase { @@ -54,7 +52,7 @@ private IEnumerable GetNavItems() new (){ Id = "503", Text = "Breadcrumb", Href = "/breadcrumb", IconName = IconName.SegmentedNav, ParentId = "5" }, new (){ Id = "504", Text = "Buttons", Href = "/buttons", IconName = IconName.ToggleOn, ParentId = "5" }, new (){ Id = "505", Text = "Callout", Href = "/callout", IconName = IconName.StickyFill, ParentId = "5" }, - new (){ Id = "506", Text = "Charts", Href = "/charts", IconName = IconName.BarChartLineFill, ParentId = "5" }, + new (){ Id = "506", Text = "Charts", Href = "/charts", IconName = IconName.BarChartLineFill, ParentId = "5", Match = NavLinkMatch.All }, new (){ Id = "507", Text = "Collapse", Href = "/collapse", IconName = IconName.ArrowsCollapse, ParentId = "5" }, new (){ Id = "508", Text = "Confirm Dialog", Href = "/confirm-dialog", IconName = IconName.QuestionDiamondFill, ParentId = "5" }, new (){ Id = "509", Text = "Grid", Href = "/grid", IconName = IconName.Grid, ParentId = "5" }, @@ -70,10 +68,10 @@ private IEnumerable GetNavItems() new (){ Id = "519", Text = "Tooltips", Href = "/tooltips", IconName = IconName.ChatSquareDotsFill, ParentId = "5" }, new (){ Id = "6", Text = "Data Visualization", IconName = IconName.BarChartFill, IconColor = IconColor.Warning }, - new (){ Id = "600", Text = "Bar Chart", Href = "/charts", IconName = IconName.BarChartFill, ParentId = "6" }, - new (){ Id = "601", Text = "Doughnut Chart", Href = "/charts", IconName = IconName.CircleFill, ParentId = "6" }, - new (){ Id = "602", Text = "Line Chart", Href = "/charts", IconName = IconName.GraphUp, ParentId = "6" }, - new (){ Id = "603", Text = "Pie Chart", Href = "/charts", IconName = IconName.PieChartFill, ParentId = "6" }, + new (){ Id = "600", Text = "Bar Chart", Href = "/charts/bar-chart", IconName = IconName.BarChartFill, ParentId = "6", Match = NavLinkMatch.All }, + new (){ Id = "601", Text = "Doughnut Chart", Href = "/charts/doughnut-chart", IconName = IconName.CircleFill, ParentId = "6", Match = NavLinkMatch.All }, + new (){ Id = "602", Text = "Line Chart", Href = "/charts/line-chart", IconName = IconName.GraphUp, ParentId = "6", Match = NavLinkMatch.All }, + new (){ Id = "603", Text = "Pie Chart", Href = "/charts/pie-chart", IconName = IconName.PieChartFill, ParentId = "6", Match = NavLinkMatch.All }, new(){ Id = "7", Text = "Services", IconName = IconName.WrenchAdjustableCircleFill, IconColor = IconColor.Success }, new (){ Id = "700", Text = "Modal Service", Href = "/modal-service", IconName = IconName.WindowStack, ParentId = "7" }, diff --git a/BlazorBootstrap.Demo/Usings.cs b/BlazorBootstrap.Demo/Usings.cs index 6e962a806..5a7a84ee2 100644 --- a/BlazorBootstrap.Demo/Usings.cs +++ b/BlazorBootstrap.Demo/Usings.cs @@ -1,2 +1,4 @@ -global using System.Linq.Expressions; -global using System.Net.Http.Json; \ No newline at end of file +global using Microsoft.AspNetCore.Components; +global using Microsoft.AspNetCore.Components.Routing; +global using System.Linq.Expressions; +global using System.Net.Http.Json; diff --git a/blazorbootstrap/Components/Charts/BarChart.razor.cs b/blazorbootstrap/Components/Charts/BarChart.razor.cs index 3a9f1917d..8191125e3 100644 --- a/blazorbootstrap/Components/Charts/BarChart.razor.cs +++ b/blazorbootstrap/Components/Charts/BarChart.razor.cs @@ -17,6 +17,107 @@ public BarChart() #region Methods + public override async Task AddDataAsync(ChartData chartData, string dataLabel, string datasetLabel, double data) + { + if (chartData is null) + throw new ArgumentNullException(nameof(chartData)); + + if (chartData.Datasets is null) + throw new ArgumentNullException(nameof(chartData.Datasets)); + + if (datasetLabel is null) + throw new ArgumentNullException(nameof(datasetLabel)); + + if (string.IsNullOrWhiteSpace(datasetLabel)) + throw new Exception($"{nameof(datasetLabel)} cannot be empty."); + + if (dataLabel is null) + throw new ArgumentNullException(nameof(datasetLabel)); + + if (string.IsNullOrWhiteSpace(dataLabel)) + throw new Exception($"{nameof(dataLabel)} cannot be empty."); + + foreach (var dataset in chartData.Datasets) + { + if (dataset is BarChartDataset lineChartDataset && lineChartDataset.Label == dataLabel) + { + lineChartDataset.Data?.Add(data); + } + } + + await JS.InvokeVoidAsync("window.blazorChart.bar.addDatasetData", ElementId, dataLabel, datasetLabel, data); + + return chartData; + } + + public override async Task AddDataAsync(ChartData chartData, string dataLabel, List data) + { + if (chartData is null) + throw new ArgumentNullException(nameof(chartData)); + + if (chartData.Datasets is null) + throw new ArgumentNullException(nameof(chartData.Datasets)); + + if (chartData.Labels is null) + throw new ArgumentNullException(nameof(chartData.Labels)); + + if (dataLabel is null) + throw new ArgumentNullException(nameof(dataLabel)); + + if (string.IsNullOrWhiteSpace(dataLabel)) + throw new Exception($"{nameof(dataLabel)} cannot be empty."); + + if (data is null) + throw new ArgumentNullException(nameof(data)); + + if (!data.Any()) + throw new Exception($"{nameof(data)} cannot be empty."); + + if (chartData.Datasets.Count != data.Count) + throw new InvalidDataException("The chart dataset count and the new data points count do not match."); + + if (chartData.Labels.Contains(dataLabel)) + throw new Exception($"{dataLabel} already exists."); + + chartData.Labels.Add(dataLabel); + + foreach (var dataset in chartData.Datasets) + { + if (dataset is BarChartDataset lineChartDataset) + { + var chartDatasetData = data.FirstOrDefault(x => x.DatasetLabel == lineChartDataset.Label); + if (chartDatasetData is null) + continue; + + lineChartDataset.Data?.Add(chartDatasetData.Data); + } + } + + await JS.InvokeVoidAsync("window.blazorChart.bar.addDatasetsData", ElementId, dataLabel, data); + + return chartData; + } + + public override async Task AddDatasetAsync(ChartData chartData, IChartDataset chartDataset, IChartOptions chartOptions) + { + if (chartData is null) + throw new ArgumentNullException(nameof(chartData)); + + if (chartData.Datasets is null) + throw new ArgumentNullException(nameof(chartData.Datasets)); + + if (chartDataset is null) + throw new ArgumentNullException(nameof(chartDataset)); + + if (chartDataset is BarChartDataset) + { + chartData.Datasets.Add(chartDataset); + await JS.InvokeVoidAsync("window.blazorChart.bar.addDataset", ElementId, (BarChartDataset)chartDataset); + } + + return chartData; + } + public override async Task InitializeAsync(ChartData chartData, IChartOptions chartOptions) { if (chartData is not null && chartData.Datasets is not null) diff --git a/blazorbootstrap/Components/Charts/BaseChart.cs b/blazorbootstrap/Components/Charts/BaseChart.cs index 6c4fb4fb7..185ba569e 100644 --- a/blazorbootstrap/Components/Charts/BaseChart.cs +++ b/blazorbootstrap/Components/Charts/BaseChart.cs @@ -51,8 +51,14 @@ public virtual async Task InitializeAsync(ChartData chartData, IChartOptions cha //public async Task ToBase64Image(string type, double quality) { } + public virtual async Task AddDataAsync(ChartData chartData, string dataLabel, List data) => await Task.FromResult(chartData); + + public virtual async Task AddDataAsync(ChartData chartData, string dataLabel, string datasetLabel, double data) => await Task.FromResult(chartData); + + public virtual async Task AddDatasetAsync(ChartData chartData, IChartDataset chartDataset, IChartOptions chartOptions) => await Task.FromResult(chartData); + /// - /// Update Bar Chart. + /// Update chart. /// /// /// diff --git a/blazorbootstrap/Components/Charts/DoughnutChart.razor.cs b/blazorbootstrap/Components/Charts/DoughnutChart.razor.cs index 7e7384bac..99ea6ce00 100644 --- a/blazorbootstrap/Components/Charts/DoughnutChart.razor.cs +++ b/blazorbootstrap/Components/Charts/DoughnutChart.razor.cs @@ -17,6 +17,107 @@ public DoughnutChart() #region Methods + public override async Task AddDataAsync(ChartData chartData, string dataLabel, string datasetLabel, double data) + { + if (chartData is null) + throw new ArgumentNullException(nameof(chartData)); + + if (chartData.Datasets is null) + throw new ArgumentNullException(nameof(chartData.Datasets)); + + if (datasetLabel is null) + throw new ArgumentNullException(nameof(datasetLabel)); + + if (string.IsNullOrWhiteSpace(datasetLabel)) + throw new Exception($"{nameof(datasetLabel)} cannot be empty."); + + if (dataLabel is null) + throw new ArgumentNullException(nameof(datasetLabel)); + + if (string.IsNullOrWhiteSpace(dataLabel)) + throw new Exception($"{nameof(dataLabel)} cannot be empty."); + + foreach (var dataset in chartData.Datasets) + { + if (dataset is DoughnutChartDataset doughnutChartDataset && doughnutChartDataset.Label == dataLabel) + { + doughnutChartDataset.Data?.Add(data); + } + } + + await JS.InvokeVoidAsync("window.blazorChart.doughnut.addDatasetData", ElementId, dataLabel, datasetLabel, data); + + return chartData; + } + + public override async Task AddDataAsync(ChartData chartData, string dataLabel, List data) + { + if (chartData is null) + throw new ArgumentNullException(nameof(chartData)); + + if (chartData.Datasets is null) + throw new ArgumentNullException(nameof(chartData.Datasets)); + + if (chartData.Labels is null) + throw new ArgumentNullException(nameof(chartData.Labels)); + + if (dataLabel is null) + throw new ArgumentNullException(nameof(dataLabel)); + + if (string.IsNullOrWhiteSpace(dataLabel)) + throw new Exception($"{nameof(dataLabel)} cannot be empty."); + + if (data is null) + throw new ArgumentNullException(nameof(data)); + + if (!data.Any()) + throw new Exception($"{nameof(data)} cannot be empty."); + + if (chartData.Datasets.Count != data.Count) + throw new InvalidDataException("The chart dataset count and the new data points count do not match."); + + if (chartData.Labels.Contains(dataLabel)) + throw new Exception($"{dataLabel} already exists."); + + chartData.Labels.Add(dataLabel); + + foreach (var dataset in chartData.Datasets) + { + if (dataset is DoughnutChartDataset doughnutChartDataset) + { + var chartDatasetData = data.FirstOrDefault(x => x.DatasetLabel == doughnutChartDataset.Label); + if (chartDatasetData is null) + continue; + + doughnutChartDataset.Data?.Add(chartDatasetData.Data); + } + } + + await JS.InvokeVoidAsync("window.blazorChart.doughnut.addDatasetsData", ElementId, dataLabel, data); + + return chartData; + } + + public override async Task AddDatasetAsync(ChartData chartData, IChartDataset chartDataset, IChartOptions chartOptions) + { + if (chartData is null) + throw new ArgumentNullException(nameof(chartData)); + + if (chartData.Datasets is null) + throw new ArgumentNullException(nameof(chartData.Datasets)); + + if (chartDataset is null) + throw new ArgumentNullException(nameof(chartDataset)); + + if (chartDataset is DoughnutChartDataset doughnutChartDataset) + { + chartData.Datasets.Add(doughnutChartDataset); + await JS.InvokeVoidAsync("window.blazorChart.doughnut.addDataset", ElementId, doughnutChartDataset); + } + + return chartData; + } + public override async Task InitializeAsync(ChartData chartData, IChartOptions chartOptions) { if (chartData is not null && chartData.Datasets is not null) diff --git a/blazorbootstrap/Components/Charts/LineChart.razor.cs b/blazorbootstrap/Components/Charts/LineChart.razor.cs index 79e9ae798..5ce5cbe90 100644 --- a/blazorbootstrap/Components/Charts/LineChart.razor.cs +++ b/blazorbootstrap/Components/Charts/LineChart.razor.cs @@ -17,24 +17,137 @@ public LineChart() #region Methods - public override async Task InitializeAsync(ChartData chartData, IChartOptions chartOptions) + public override async Task AddDataAsync(ChartData chartData, string dataLabel, string datasetLabel, double data) { - if (chartData is not null && chartData.Datasets is not null) + if (chartData is null) + throw new ArgumentNullException(nameof(chartData)); + + if (chartData.Datasets is null) + throw new ArgumentNullException(nameof(chartData.Datasets)); + + if (datasetLabel is null) + throw new ArgumentNullException(nameof(datasetLabel)); + + if (string.IsNullOrWhiteSpace(datasetLabel)) + throw new Exception($"{nameof(datasetLabel)} cannot be empty."); + + if (dataLabel is null) + throw new ArgumentNullException(nameof(datasetLabel)); + + if (string.IsNullOrWhiteSpace(dataLabel)) + throw new Exception($"{nameof(dataLabel)} cannot be empty."); + + foreach (var dataset in chartData.Datasets) { - var datasets = chartData.Datasets.OfType(); - var data = new { chartData.Labels, Datasets = datasets }; - await JS.InvokeVoidAsync("window.blazorChart.line.initialize", ElementId, GetChartType(), data, (LineChartOptions)chartOptions); + if (dataset is LineChartDataset lineChartDataset && lineChartDataset.Label == dataLabel) + { + lineChartDataset.Data?.Add(data); + } } + + await JS.InvokeVoidAsync("window.blazorChart.line.addDatasetData", ElementId, dataLabel, datasetLabel, data); + + return chartData; } - public override async Task UpdateAsync(ChartData chartData, IChartOptions chartOptions) + public override async Task AddDataAsync(ChartData chartData, string dataLabel, List data) + { + if (chartData is null) + throw new ArgumentNullException(nameof(chartData)); + + if (chartData.Datasets is null) + throw new ArgumentNullException(nameof(chartData.Datasets)); + + if (chartData.Labels is null) + throw new ArgumentNullException(nameof(chartData.Labels)); + + if (dataLabel is null) + throw new ArgumentNullException(nameof(dataLabel)); + + if (string.IsNullOrWhiteSpace(dataLabel)) + throw new Exception($"{nameof(dataLabel)} cannot be empty."); + + if (data is null) + throw new ArgumentNullException(nameof(data)); + + if (!data.Any()) + throw new Exception($"{nameof(data)} cannot be empty."); + + if (chartData.Datasets.Count != data.Count) + throw new InvalidDataException("The chart dataset count and the new data points count do not match."); + + if (chartData.Labels.Contains(dataLabel)) + throw new Exception($"{dataLabel} already exists."); + + chartData.Labels.Add(dataLabel); + + foreach (var dataset in chartData.Datasets) + { + if (dataset is LineChartDataset lineChartDataset) + { + var chartDatasetData = data.FirstOrDefault(x => x.DatasetLabel == lineChartDataset.Label); + if (chartDatasetData is null) + continue; + + lineChartDataset.Data?.Add(chartDatasetData.Data); + } + } + + await JS.InvokeVoidAsync("window.blazorChart.line.addDatasetsData", ElementId, dataLabel, data); + + return chartData; + } + + public override async Task AddDatasetAsync(ChartData chartData, IChartDataset chartDataset, IChartOptions chartOptions) { - if (chartData is not null && chartData.Datasets is not null) + if (chartData is null) + throw new ArgumentNullException(nameof(chartData)); + + if (chartData.Datasets is null) + throw new ArgumentNullException(nameof(chartData.Datasets)); + + if (chartDataset is null) + throw new ArgumentNullException(nameof(chartDataset)); + + if (chartDataset is LineChartDataset) { - var datasets = chartData.Datasets.OfType(); - var data = new { chartData.Labels, Datasets = datasets }; - await JS.InvokeVoidAsync("window.blazorChart.line.update", ElementId, GetChartType(), data, (LineChartOptions)chartOptions); + chartData.Datasets.Add(chartDataset); + await JS.InvokeVoidAsync("window.blazorChart.line.addDataset", ElementId, (LineChartDataset)chartDataset); } + + return chartData; + } + + public override async Task InitializeAsync(ChartData chartData, IChartOptions chartOptions) + { + if (chartData is null) + throw new ArgumentNullException(nameof(chartData)); + + if (chartData.Datasets is null) + throw new ArgumentNullException(nameof(chartData.Datasets)); + + if (chartOptions is null) + throw new ArgumentNullException(nameof(chartOptions)); + + var datasets = chartData.Datasets.OfType(); + var data = new { Labels = chartData.Labels, Datasets = datasets }; + await JS.InvokeVoidAsync("window.blazorChart.line.initialize", ElementId, GetChartType(), data, (LineChartOptions)chartOptions); + } + + public override async Task UpdateAsync(ChartData chartData, IChartOptions chartOptions) + { + if (chartData is null) + throw new ArgumentNullException(nameof(chartData)); + + if (chartData.Datasets is null) + throw new ArgumentNullException(nameof(chartData.Datasets)); + + if (chartOptions is null) + throw new ArgumentNullException(nameof(chartOptions)); + + var datasets = chartData.Datasets.OfType(); + var data = new { Labels = chartData.Labels, Datasets = datasets }; + await JS.InvokeVoidAsync("window.blazorChart.line.update", ElementId, GetChartType(), data, (LineChartOptions)chartOptions); } #endregion Methods diff --git a/blazorbootstrap/Components/Charts/PieChart.razor.cs b/blazorbootstrap/Components/Charts/PieChart.razor.cs index 0da372fe7..b182d7273 100644 --- a/blazorbootstrap/Components/Charts/PieChart.razor.cs +++ b/blazorbootstrap/Components/Charts/PieChart.razor.cs @@ -17,6 +17,127 @@ public PieChart() #region Methods + public override async Task AddDataAsync(ChartData chartData, string dataLabel, string datasetLabel, double data) + { + if (chartData is null) + throw new ArgumentNullException(nameof(chartData)); + + if (chartData.Datasets is null) + throw new ArgumentNullException(nameof(chartData.Datasets)); + + if (datasetLabel is null) + throw new ArgumentNullException(nameof(datasetLabel)); + + if (string.IsNullOrWhiteSpace(datasetLabel)) + throw new Exception($"{nameof(datasetLabel)} cannot be empty."); + + if (dataLabel is null) + throw new ArgumentNullException(nameof(datasetLabel)); + + if (string.IsNullOrWhiteSpace(dataLabel)) + throw new Exception($"{nameof(dataLabel)} cannot be empty."); + + foreach (var dataset in chartData.Datasets) + { + if (dataset is PieChartDataset pieChartDataset && pieChartDataset.Label == dataLabel) + { + pieChartDataset.Data?.Add(data); + } + } + + await JS.InvokeVoidAsync("window.blazorChart.pie.addDatasetData", ElementId, dataLabel, datasetLabel, data); + + return chartData; + } + + public override async Task AddDataAsync(ChartData chartData, string dataLabel, List data) + { + if (chartData is null) + throw new ArgumentNullException(nameof(chartData)); + + if (chartData.Datasets is null) + throw new ArgumentNullException(nameof(chartData.Datasets)); + + if (chartData.Labels is null) + throw new ArgumentNullException(nameof(chartData.Labels)); + + if (dataLabel is null) + throw new ArgumentNullException(nameof(dataLabel)); + + if (string.IsNullOrWhiteSpace(dataLabel)) + throw new Exception($"{nameof(dataLabel)} cannot be empty."); + + if (data is null) + throw new ArgumentNullException(nameof(data)); + + if (!data.Any()) + throw new Exception($"{nameof(data)} cannot be empty."); + + if (chartData.Datasets.Count != data.Count) + throw new InvalidDataException("The chart dataset count and the new data points count do not match."); + + if (chartData.Labels.Contains(dataLabel)) + throw new Exception($"{dataLabel} already exists."); + + chartData.Labels.Add(dataLabel); + + foreach (var dataset in chartData.Datasets) + { + if (dataset is PieChartDataset pieChartDataset) + { + var chartDatasetData = data.FirstOrDefault(x => x.DatasetLabel == pieChartDataset.Label); + if (chartDatasetData is null) + continue; + + pieChartDataset.Data?.Add(chartDatasetData.Data); + } + } + + await JS.InvokeVoidAsync("window.blazorChart.pie.addDatasetsData", ElementId, dataLabel, data); + + return chartData; + } + + public override async Task AddDatasetAsync(ChartData chartData, IChartDataset chartDataset, IChartOptions chartOptions) + { + if (chartData is null) + throw new ArgumentNullException(nameof(chartData)); + + if (chartData.Datasets is null) + throw new ArgumentNullException(nameof(chartData.Datasets)); + + if (chartDataset is null) + throw new ArgumentNullException(nameof(chartDataset)); + + if (chartDataset is PieChartDataset pieChartDataset) + { + chartData.Datasets.Add(pieChartDataset); + await JS.InvokeVoidAsync("window.blazorChart.pie.addDataset", ElementId, pieChartDataset); + } + + return chartData; + } + + public override async Task InitializeAsync(ChartData chartData, IChartOptions chartOptions) + { + if (chartData is not null && chartData.Datasets is not null) + { + var datasets = chartData.Datasets.OfType(); + var data = new { chartData.Labels, Datasets = datasets }; + await JS.InvokeVoidAsync("window.blazorChart.pie.initialize", ElementId, GetChartType(), data, (PieChartOptions)chartOptions); + } + } + + public override async Task UpdateAsync(ChartData chartData, IChartOptions chartOptions) + { + if (chartData is not null && chartData.Datasets is not null) + { + var datasets = chartData.Datasets.OfType(); + var data = new { chartData.Labels, Datasets = datasets }; + await JS.InvokeVoidAsync("window.blazorChart.pie.update", ElementId, GetChartType(), data, (PieChartOptions)chartOptions); + } + } + #endregion Methods #region Properties diff --git a/blazorbootstrap/Extensions/ColorExtensions.cs b/blazorbootstrap/Extensions/ColorExtensions.cs index 836b0135f..a587b7405 100644 --- a/blazorbootstrap/Extensions/ColorExtensions.cs +++ b/blazorbootstrap/Extensions/ColorExtensions.cs @@ -36,4 +36,11 @@ public static class ColorExtensions /// The alpha parameter is a number between 0.0 (fully transparent) and 1.0 (fully opaque). /// RGBA(R, G, B, A) format string public static string ToRgbaString(this Color c, double alpha = 0.2) => $"RGBA({c.R}, {c.G}, {c.B}, {alpha})"; + + /// + /// Converts an Html color representation to a GDI+ . + /// + /// + /// Converts #RRGGBB string to . + public static Color ToColor(this string hex) => System.Drawing.ColorTranslator.FromHtml(hex); } diff --git a/blazorbootstrap/Models/Charts/ChartData.cs b/blazorbootstrap/Models/Charts/ChartData.cs index 0e5e1b5b7..609c9bfb1 100644 --- a/blazorbootstrap/Models/Charts/ChartData.cs +++ b/blazorbootstrap/Models/Charts/ChartData.cs @@ -2,6 +2,9 @@ public class ChartData { + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] public List? Labels { get; set; } + + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] public List? Datasets { get; set; } } \ No newline at end of file diff --git a/blazorbootstrap/Models/Charts/ChartDataset/ChartDataset.cs b/blazorbootstrap/Models/Charts/ChartDataset/ChartDataset.cs index 3144a8478..efd1bde12 100644 --- a/blazorbootstrap/Models/Charts/ChartDataset/ChartDataset.cs +++ b/blazorbootstrap/Models/Charts/ChartDataset/ChartDataset.cs @@ -4,6 +4,16 @@ public interface IChartDataset { } public class ChartDataset : IChartDataset { + public ChartDataset() + { + Oid = Guid.NewGuid(); + } + + /// + /// Get unique object id. + /// + public Guid Oid { get; private set; } + /// /// Get or sets the BackgroundColor. /// diff --git a/blazorbootstrap/Models/Charts/ChartDataset/ChartDatasetData.cs b/blazorbootstrap/Models/Charts/ChartDataset/ChartDatasetData.cs new file mode 100644 index 000000000..f32d53b52 --- /dev/null +++ b/blazorbootstrap/Models/Charts/ChartDataset/ChartDatasetData.cs @@ -0,0 +1,3 @@ +namespace BlazorBootstrap; + +public record ChartDatasetData(string DatasetLabel, double Data); diff --git a/blazorbootstrap/Models/Charts/ChartDataset/DoughnutChartDataset.cs b/blazorbootstrap/Models/Charts/ChartDataset/DoughnutChartDataset.cs index 59cedc9de..267fbe713 100644 --- a/blazorbootstrap/Models/Charts/ChartDataset/DoughnutChartDataset.cs +++ b/blazorbootstrap/Models/Charts/ChartDataset/DoughnutChartDataset.cs @@ -2,4 +2,9 @@ public class DoughnutChartDataset : ChartDataset { + /// + /// The label for the dataset which appears in the legend and tooltips. + /// + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + public string? Label { get; set; } } diff --git a/blazorbootstrap/Models/Charts/ChartDataset/LineChartDataset.cs b/blazorbootstrap/Models/Charts/ChartDataset/LineChartDataset.cs index 2f10f24a5..615c211b0 100644 --- a/blazorbootstrap/Models/Charts/ChartDataset/LineChartDataset.cs +++ b/blazorbootstrap/Models/Charts/ChartDataset/LineChartDataset.cs @@ -35,7 +35,7 @@ public class LineChartDataset : ChartDataset /// The label for the dataset which appears in the legend and tooltips. /// [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] - public string Label { get; set; } + public string? Label { get; set; } /// /// The fill color for points. @@ -133,16 +133,11 @@ public class LineChartDataset : ChartDataset /// The ID of the x axis to plot this dataset on. /// [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] - public string XAxisID { get; set; } + public string? XAxisID { get; set; } /// /// The ID of the y axis to plot this dataset on. /// [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] - public string YAxisID { get; set; } - - public LineChartDataset() - { - Type = "line"; - } + public string? YAxisID { get; set; } } diff --git a/blazorbootstrap/Models/Charts/ChartDataset/PieChartDataset.cs b/blazorbootstrap/Models/Charts/ChartDataset/PieChartDataset.cs index aeb565707..4a1b4037e 100644 --- a/blazorbootstrap/Models/Charts/ChartDataset/PieChartDataset.cs +++ b/blazorbootstrap/Models/Charts/ChartDataset/PieChartDataset.cs @@ -2,4 +2,9 @@ public class PieChartDataset : ChartDataset { + /// + /// The label for the dataset which appears in the legend and tooltips. + /// + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + public string? Label { get; set; } } diff --git a/blazorbootstrap/Models/Charts/ChartOptions/ChartOptions.cs b/blazorbootstrap/Models/Charts/ChartOptions/ChartOptions.cs index e3277836b..ad3c3be99 100644 --- a/blazorbootstrap/Models/Charts/ChartOptions/ChartOptions.cs +++ b/blazorbootstrap/Models/Charts/ChartOptions/ChartOptions.cs @@ -103,10 +103,36 @@ public class Scales public class ChartAxes { - [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] - public Title? Title { get; set; } = new Title(); // Stacked public bool BeginAtZero { get; set; } = true; + + /// + /// User defined maximum number for the scale, overrides maximum value from data. + /// + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + public double? Max { get; set; } + + /// + /// User defined minimum number for the scale, overrides minimum value from data. + /// + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + public double? Min { get; set; } + + /// + /// Adjustment used when calculating the maximum data value. + /// + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + public double? SuggestedMax { get; set; } + + /// + /// Adjustment used when calculating the minimum data value. + /// + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + public double? SuggestedMin { get; set; } + + + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + public Title? Title { get; set; } = new Title(); } /// diff --git a/blazorbootstrap/Utilities/ColorBuilder.cs b/blazorbootstrap/Utilities/ColorBuilder.cs new file mode 100644 index 000000000..5d73e6001 --- /dev/null +++ b/blazorbootstrap/Utilities/ColorBuilder.cs @@ -0,0 +1,42 @@ +namespace BlazorBootstrap.Utilities; + +public static class ColorBuilder +{ + /// + /// Returns 6 categorical #RRGGBB colors. + /// + /// + public static string[] CategoricalSixColors => new string[] { + "#0fb5ae", + "#4046ca", + "#f68511", + "#de3d82", + "#7e84fa", + "#72e06a", + "#147af3", + "#7326d3", + "#e8c600", + "#e8c600", + "#e8c600", + "#e8c600" + }; + + /// + /// Returns 12 categorical #RRGGBB colors. + /// + /// + public static string[] CategoricalTwelveColors => new string[] { + "#0fb5ae", + "#4046ca", + "#f68511", + "#de3d82", + "#7e84fa", + "#72e06a", + "#147af3", + "#7326d3", + "#e8c600", + "#cb5d00", + "#008f5d", + "#bce931" + }; +} \ No newline at end of file diff --git a/blazorbootstrap/wwwroot/blazor.bootstrap.js b/blazorbootstrap/wwwroot/blazor.bootstrap.js index 7f657b5d0..c52afc54d 100644 --- a/blazorbootstrap/wwwroot/blazor.bootstrap.js +++ b/blazorbootstrap/wwwroot/blazor.bootstrap.js @@ -534,11 +534,6 @@ window.blazorChart = { create: (elementId, type, data, options) => { let chartEl = document.getElementById(elementId); - //console.log(elementId); - //console.log(type); - //console.log(data); - //console.log(options); // NOTE: this gives more details in the chrome dev tools - const config = { type: type, data: data, @@ -587,6 +582,50 @@ window.blazorChart = { } window.blazorChart.bar = { + addDatasetData: (elementId, dataLabel, datasetLabel, data) => { + let chart = window.blazorChart.get(elementId); + if (chart) { + const chartData = chart.data; + + if (!chartData.labels.includes(dataLabel)) + chartData.labels.push(dataLabel); + + const chartDatasets = chartData.datasets; + + if (chartDatasets.length > 0) { + let datasetIndex = chartDatasets.findIndex(dataset => dataset.label === datasetLabel); + if (datasetIndex > -1) { + chartDatasets[datasetIndex].data.push(data); + chart.update(); + } + } + } + }, + addDatasetsData: (elementId, dataLabel, data) => { + let chart = window.blazorChart.get(elementId); + if (chart && data) { + const chartData = chart.data; + + if (!chartData.labels.includes(dataLabel)) { + chartData.labels.push(dataLabel); + + if (chartData.datasets.length > 0 && chartData.datasets.length === data.length) { + data.forEach(chartDatasetData => { + let datasetIndex = chartData.datasets.findIndex(dataset => dataset.label === chartDatasetData.datasetLabel); + chartData.datasets[datasetIndex].data.push(chartDatasetData.data); + }); + chart.update(); + } + } + } + }, + addDataset: (elementId, newDataset) => { + let chart = window.blazorChart.get(elementId); + if (chart) { + chart.data.datasets.push(newDataset); + chart.update(); + } + }, create: (elementId, type, data, options) => { let chartEl = document.getElementById(elementId); @@ -627,7 +666,25 @@ window.blazorChart.bar = { update: (elementId, type, data, options) => { let chart = window.blazorChart.bar.get(elementId); if (chart) { - chart.data = data; + const chartData = chart.data; + let updatedDatasets = []; + data.datasets.forEach(newDataset => { + if (chartData.datasets.length > 0) { + let chartDatasetIndex = chartData.datasets.findIndex(chartDataset => chartDataset.oid === newDataset.oid); + if (chartDatasetIndex > -1) { + chart.data.datasets[chartDatasetIndex].data = newDataset.data; + updatedDatasets.push(chart.data.datasets[chartDatasetIndex]); + } + else { + updatedDatasets.push(newDataset); + } + } + else { + updatedDatasets.push(newDataset); + } + chart.data.datasets = updatedDatasets; + }); + chart.options = options; chart.update(); } else { @@ -637,6 +694,50 @@ window.blazorChart.bar = { } window.blazorChart.doughnut = { + addDatasetData: (elementId, dataLabel, datasetLabel, data) => { + let chart = window.blazorChart.get(elementId); + if (chart) { + const chartData = chart.data; + + if (!chartData.labels.includes(dataLabel)) + chartData.labels.push(dataLabel); + + const chartDatasets = chartData.datasets; + + if (chartDatasets.length > 0) { + let datasetIndex = chartDatasets.findIndex(dataset => dataset.label === datasetLabel); + if (datasetIndex > -1) { + chartDatasets[datasetIndex].data.push(data); + chart.update(); + } + } + } + }, + addDatasetsData: (elementId, dataLabel, data) => { + let chart = window.blazorChart.get(elementId); + if (chart && data) { + const chartData = chart.data; + + if (!chartData.labels.includes(dataLabel)) { + chartData.labels.push(dataLabel); + + if (chartData.datasets.length > 0 && chartData.datasets.length === data.length) { + data.forEach(chartDatasetData => { + let datasetIndex = chartData.datasets.findIndex(dataset => dataset.label === chartDatasetData.datasetLabel); + chartData.datasets[datasetIndex].data.push(chartDatasetData.data); + }); + chart.update(); + } + } + } + }, + addDataset: (elementId, newDataset) => { + let chart = window.blazorChart.get(elementId); + if (chart) { + chart.data.datasets.push(newDataset); + chart.update(); + } + }, create: (elementId, type, data, options) => { let chartEl = document.getElementById(elementId); @@ -677,7 +778,25 @@ window.blazorChart.doughnut = { update: (elementId, type, data, options) => { let chart = window.blazorChart.doughnut.get(elementId); if (chart) { - chart.data = data; + const chartData = chart.data; + let updatedDatasets = []; + data.datasets.forEach(newDataset => { + if (chartData.datasets.length > 0) { + let chartDatasetIndex = chartData.datasets.findIndex(chartDataset => chartDataset.oid === newDataset.oid); + if (chartDatasetIndex > -1) { + chart.data.datasets[chartDatasetIndex].data = newDataset.data; + updatedDatasets.push(chart.data.datasets[chartDatasetIndex]); + } + else { + updatedDatasets.push(newDataset); + } + } + else { + updatedDatasets.push(newDataset); + } + chart.data.datasets = updatedDatasets; + }); + chart.options = options; chart.update(); } else { @@ -687,6 +806,50 @@ window.blazorChart.doughnut = { } window.blazorChart.line = { + addDatasetData: (elementId, dataLabel, datasetLabel, data) => { + let chart = window.blazorChart.get(elementId); + if (chart) { + const chartData = chart.data; + + if (!chartData.labels.includes(dataLabel)) + chartData.labels.push(dataLabel); + + const chartDatasets = chartData.datasets; + + if (chartDatasets.length > 0) { + let datasetIndex = chartDatasets.findIndex(dataset => dataset.label === datasetLabel); + if (datasetIndex > -1) { + chartDatasets[datasetIndex].data.push(data); + chart.update(); + } + } + } + }, + addDatasetsData: (elementId, dataLabel, data) => { + let chart = window.blazorChart.get(elementId); + if (chart && data) { + const chartData = chart.data; + + if (!chartData.labels.includes(dataLabel)) { + chartData.labels.push(dataLabel); + + if (chartData.datasets.length > 0 && chartData.datasets.length === data.length) { + data.forEach(chartDatasetData => { + let datasetIndex = chartData.datasets.findIndex(dataset => dataset.label === chartDatasetData.datasetLabel); + chartData.datasets[datasetIndex].data.push(chartDatasetData.data); + }); + chart.update(); + } + } + } + }, + addDataset: (elementId, newDataset) => { + let chart = window.blazorChart.get(elementId); + if (chart) { + chart.data.datasets.push(newDataset); + chart.update(); + } + }, create: (elementId, type, data, options) => { let chartEl = document.getElementById(elementId); @@ -747,7 +910,8 @@ window.blazorChart.line = { }, initialize: (elementId, type, data, options) => { let chart = window.blazorChart.line.get(elementId); - if (chart) return; + if (chart) + return; else window.blazorChart.line.create(elementId, type, data, options); }, @@ -761,16 +925,79 @@ window.blazorChart.line = { update: (elementId, type, data, options) => { let chart = window.blazorChart.line.get(elementId); if (chart) { - chart.data = data; + const chartData = chart.data; + let updatedDatasets = []; + data.datasets.forEach(newDataset => { + if (chartData.datasets.length > 0) { + let chartDatasetIndex = chartData.datasets.findIndex(chartDataset => chartDataset.oid === newDataset.oid); + if (chartDatasetIndex > -1) { + chart.data.datasets[chartDatasetIndex].data = newDataset.data; + updatedDatasets.push(chart.data.datasets[chartDatasetIndex]); + } + else { + updatedDatasets.push(newDataset); + } + } + else { + updatedDatasets.push(newDataset); + } + chart.data.datasets = updatedDatasets; + }); + chart.options = options; chart.update(); - } else { + } + else { window.blazorChart.line.create(elementId, type, data, options); } }, } window.blazorChart.pie = { + addDatasetData: (elementId, dataLabel, datasetLabel, data) => { + let chart = window.blazorChart.get(elementId); + if (chart) { + const chartData = chart.data; + + if (!chartData.labels.includes(dataLabel)) + chartData.labels.push(dataLabel); + + const chartDatasets = chartData.datasets; + + if (chartDatasets.length > 0) { + let datasetIndex = chartDatasets.findIndex(dataset => dataset.label === datasetLabel); + if (datasetIndex > -1) { + chartDatasets[datasetIndex].data.push(data); + chart.update(); + } + } + } + }, + addDatasetsData: (elementId, dataLabel, data) => { + let chart = window.blazorChart.get(elementId); + if (chart && data) { + const chartData = chart.data; + + if (!chartData.labels.includes(dataLabel)) { + chartData.labels.push(dataLabel); + + if (chartData.datasets.length > 0 && chartData.datasets.length === data.length) { + data.forEach(chartDatasetData => { + let datasetIndex = chartData.datasets.findIndex(dataset => dataset.label === chartDatasetData.datasetLabel); + chartData.datasets[datasetIndex].data.push(chartDatasetData.data); + }); + chart.update(); + } + } + } + }, + addDataset: (elementId, newDataset) => { + let chart = window.blazorChart.get(elementId); + if (chart) { + chart.data.datasets.push(newDataset); + chart.update(); + } + }, create: (elementId, type, data, options) => { let chartEl = document.getElementById(elementId); @@ -811,7 +1038,25 @@ window.blazorChart.pie = { update: (elementId, type, data, options) => { let chart = window.blazorChart.pie.get(elementId); if (chart) { - chart.data = data; + const chartData = chart.data; + let updatedDatasets = []; + data.datasets.forEach(newDataset => { + if (chartData.datasets.length > 0) { + let chartDatasetIndex = chartData.datasets.findIndex(chartDataset => chartDataset.oid === newDataset.oid); + if (chartDatasetIndex > -1) { + chart.data.datasets[chartDatasetIndex].data = newDataset.data; + updatedDatasets.push(chart.data.datasets[chartDatasetIndex]); + } + else { + updatedDatasets.push(newDataset); + } + } + else { + updatedDatasets.push(newDataset); + } + chart.data.datasets = updatedDatasets; + }); + chart.options = options; chart.update(); } else {