!3647 feat(#I66VTQ): add AspectRatio/Height parameter on Chart component

* Merge branch 'main' into feat_I66VTQ
* Update BootstrapBlazor.Chart.csproj
* feat(#I66VTQ): add AspectRatio/Height parameter on Chart component
This commit is contained in:
alex_zou 2022-12-23 13:41:07 +00:00 committed by Argo
parent 71a0e845fa
commit 3cecfd4801
7 changed files with 159 additions and 33 deletions

View File

@ -3,7 +3,7 @@
<Import Project="..\..\..\bundleconfig.props" />
<PropertyGroup>
<Version>7.0.0</Version>
<Version>7.0.1</Version>
<PackageTags>Bootstrap Blazor WebAssembly wasm UI Components Chart</PackageTags>
<Description>Bootstrap UI components extensions of Chart.js</Description>
</PropertyGroup>

View File

@ -32,15 +32,77 @@ public partial class Chart : BootstrapComponentBase, IDisposable
/// 获得/设置 组件 Style 字符串
/// </summary>
private string? StyleString => CssBuilder.Default()
.AddClass($"height: {Height};", !string.IsNullOrEmpty(Height))
.AddClass($"width: {Width};", !string.IsNullOrEmpty(Width))
.Build();
/// <summary>
/// 获得/设置 组件宽度支持单位
/// 获得/设置 图表标题
/// </summary>
[Parameter]
public string? Title { get; set; }
/// <summary>
/// 获得/设置 组件高度支持单位<para>如: 30% , 30px , 30em , calc(30%)</para>
/// </summary>
[Parameter]
public string? Height { get; set; }
/// <summary>
/// 获得/设置 组件宽度支持单位<para>如: 30% , 30px , 30em , calc(30%)</para>
/// </summary>
[Parameter]
public string? Width { get; set; }
/// <summary>
/// 获得/设置 图表所在canvas是否随其容器大小变化而变化 默认为 true
/// </summary>
[Parameter]
public bool Responsive { get; set; } = true;
/// <summary>
/// 获取/设置 是否 约束图表比例 默认为 true
/// </summary>
[Parameter]
public bool MaintainAspectRatio { get; set; } = true;
/// <summary>
/// 获得/设置 设置canvas的宽高比值为1表示canvas是正方形如果显示定义了canvas的高度则此属性无效 默认为 2
/// </summary>
[Parameter]
public int AspectRatio { get; set; } = 2;
/// <summary>
/// 获得/设置 图表尺寸延迟变化时间 默认为 0
/// </summary>
[Parameter]
public int ResizeDelay { get; set; } = 0;
/// <summary>
/// 获得/设置 Bubble 模式下显示角度 180 为 半圆 360 为正圆
/// </summary>
[Parameter]
public int Angle { get; set; }
/// <summary>
/// 获得/设置 正在加载文本
/// </summary>
[Parameter]
[NotNull]
public string? LoadingText { get; set; }
/// <summary>
/// 获得/设置 图表组件渲染类型 默认为 line 图
/// </summary>
[Parameter]
public ChartType ChartType { get; set; }
/// <summary>
/// 获得/设置 图表组件组件方法 默认为 Update
/// </summary>
[Parameter]
public ChartAction ChartAction { get; set; }
/// <summary>
/// 获得/设置 组件数据初始化委托方法
/// </summary>
@ -59,31 +121,6 @@ public partial class Chart : BootstrapComponentBase, IDisposable
[Parameter]
public Func<ChartAction, Task>? OnAfterUpdateAsync { get; set; }
/// <summary>
/// 获得/设置 Bubble 模式下显示角度 180 为 半圆 360 为正圆
/// </summary>
[Parameter]
public int Angle { get; set; }
/// <summary>
/// 获得/设置 图表组件渲染类型 默认为 line 图
/// </summary>
[Parameter]
public ChartType ChartType { get; set; }
/// <summary>
/// 获得/设置 图表组件渲染类型 默认为 Update
/// </summary>
[Parameter]
public ChartAction ChartAction { get; set; }
/// <summary>
/// 获得/设置 正在加载文本
/// </summary>
[Parameter]
[NotNull]
public string? LoadingText { get; set; }
[Inject]
[NotNull]
private IStringLocalizer<Chart>? Localizer { get; set; }
@ -117,6 +154,16 @@ public partial class Chart : BootstrapComponentBase, IDisposable
Interop ??= new JSInterop<Chart>(JSRuntime);
var ds = await OnInitAsync.Invoke();
ds.Options.Title = ds.Options.Title ?? Title;
ds.Options.Responsive = ds.Options.Responsive ?? Responsive;
ds.Options.MaintainAspectRatio = ds.Options.MaintainAspectRatio ?? MaintainAspectRatio;
ds.Options.AspectRatio = ds.Options.AspectRatio ?? AspectRatio;
ds.Options.ResizeDelay = ds.Options.ResizeDelay ?? ResizeDelay;
if (Height != null && Width != null)
{
//设置了高度和宽度,会自动禁用约束图表比例,图表充满容器
ds.Options.MaintainAspectRatio = false;
}
await Interop.InvokeVoidAsync(this, ChartElement, "bb_chart", nameof(Completed), ds, "", ChartType.ToDescriptionString(), Angle);
}
}
@ -147,6 +194,24 @@ public partial class Chart : BootstrapComponentBase, IDisposable
}
}
/// <summary>
/// 重新加载方法, 强制重新渲染图表
/// </summary>
public async Task Reload()
{
if (OnInitAsync != null)
{
var ds = await OnInitAsync();
await Interop.InvokeVoidAsync(this, ChartElement, "bb_chart", nameof(Completed), ds, ChartAction.Reload.ToDescriptionString(), ChartType.ToDescriptionString(), Angle);
if (OnAfterUpdateAsync != null)
{
await OnAfterUpdateAsync(ChartAction.Reload);
}
}
}
#region Dispose
/// <summary>
/// Dispose 方法

View File

@ -16,29 +16,40 @@ public enum ChartAction
/// </summary>
[Description("update")]
Update,
/// <summary>
/// 增加数据集
/// </summary>
[Description("addDataset")]
AddDataset,
/// <summary>
/// 减少数据集
/// </summary>
[Description("removeDataset")]
RemoveDataset,
/// <summary>
/// 增加数据
/// </summary>
[Description("addData")]
AddData,
/// <summary>
/// 减少数据
/// </summary>
[Description("removeData")]
RemoveData,
/// <summary>
/// 全圆/半圆
/// </summary>
[Description("setAngle")]
SetAngle
SetAngle,
/// <summary>
/// 重新渲染图表
/// </summary>
[Description("reload")]
Reload,
}

View File

@ -2,6 +2,8 @@
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
// Website: https://www.blazor.zone or https://argozhang.github.io/
using Microsoft.AspNetCore.Components;
namespace BootstrapBlazor.Components;
/// <summary>
@ -10,7 +12,7 @@ namespace BootstrapBlazor.Components;
public class ChartOptions
{
/// <summary>
/// 获得/设置 Title
/// 获得/设置 表 Title
/// </summary>
public string? Title { get; set; }
@ -26,14 +28,34 @@ public class ChartOptions
public ChartAxes Y { get; } = new ChartAxes();
/// <summary>
/// 获得/设置 是否 适配移动端 默认为 true
/// 获得/设置 图表所在canvas是否随其容器大小变化而变化 默认为 true
/// </summary>
public bool Responsive { get; set; } = true;
public bool? Responsive { get; set; }
/// <summary>
/// 获取/设置 是否 约束图表比例 默认为 true
/// </summary>
public bool MaintainAspectRatio { get; set; } = true;
public bool? MaintainAspectRatio { get; set; }
/// <summary>
/// 获得/设置 设置canvas的宽高比值为1表示canvas是正方形如果显示定义了canvas的高度则此属性无效 默认为 2
/// </summary>
public int? AspectRatio { get; set; }
/// <summary>
/// 获得/设置 图表尺寸延迟变化时间 默认为 0
/// </summary>
public int? ResizeDelay { get; set; }
/// <summary>
/// 获得/设置 图表canvas高度 默认为空,跟随容器高度<para>如: 300px</para>
/// </summary>
public string? Height { get; set; }
/// <summary>
/// 获得/设置 图表canvas宽度 默认为空,跟随容器高度<para>如: 300px</para>
/// </summary>
public string? Width { get; set; }
/// <summary>
/// 获得/设置 数据显示颜色

View File

@ -17,6 +17,8 @@
options: {
responsive: true,
maintainAspectRatio: true,
aspectRatio: 2,
resizeDelay: 0,
plugins: {
title: {
display: true,
@ -171,6 +173,8 @@
options: {
responsive: option.options.responsive,
maintainAspectRatio: option.options.maintainAspectRatio,
aspectRatio: option.options.aspectRatio,
resizeDelay: option.options.resizeDelay,
plugins: {
title: {
display: option.options.title != null,
@ -193,6 +197,7 @@
stacked: option.options.x.stacked
}
}
}
});
},
@ -236,6 +241,9 @@
}
}
}
else if (option.updateMethod === "reload") {
config.data = option.data;
}
else {
config.data.datasets.forEach((dataset, index) => {
dataset.data = option.data.datasets[index].data;
@ -249,6 +257,12 @@
if (!chart) {
var op = $.getChartOption(option);
$el.data('chart', chart = new Chart(el.getElementsByTagName('canvas'), op));
if (option.options.height !== null) {
chart.canvas.parentNode.style.height = option.options.height;
}
if (option.options.width !== null) {
chart.canvas.parentNode.style.width = option.options.width;
}
$el.removeClass('is-loading').trigger('chart.afterInit');
obj.invokeMethodAsync(method);
}

View File

@ -3,6 +3,8 @@
options: {
responsive: true,
maintainAspectRatio: true,
aspectRatio: 2,
resizeDelay: 0,
plugins: {
title: {
display: true,
@ -157,6 +159,8 @@
options: {
responsive: option.options.responsive,
maintainAspectRatio: option.options.maintainAspectRatio,
aspectRatio: option.options.aspectRatio,
resizeDelay: option.options.resizeDelay,
plugins: {
title: {
display: option.options.title != null,
@ -179,6 +183,7 @@
stacked: option.options.x.stacked
}
}
}
});
},
@ -222,6 +227,9 @@
}
}
}
else if (option.updateMethod === "reload") {
config.data = option.data;
}
else {
config.data.datasets.forEach((dataset, index) => {
dataset.data = option.data.datasets[index].data;
@ -235,6 +243,12 @@
if (!chart) {
var op = $.getChartOption(option);
$el.data('chart', chart = new Chart(el.getElementsByTagName('canvas'), op));
if (option.options.height !== null) {
chart.canvas.parentNode.style.height = option.options.height;
}
if (option.options.width !== null) {
chart.canvas.parentNode.style.width = option.options.width;
}
$el.removeClass('is-loading').trigger('chart.afterInit');
obj.invokeMethodAsync(method);
}

View File

@ -1 +1 @@
(function(n){window.chartOption={options:{responsive:!0,maintainAspectRatio:!0,plugins:{title:{display:!0,text:null}},tooltips:{mode:"index",intersect:!1},hover:{mode:"nearest",intersect:!0},scales:{x:{display:!0,title:{display:!1,text:null}},y:{display:!0,title:{display:!1,text:null}}}}};var t=(n,t)=>n.p0.skip||n.p1.skip?t:undefined,r=(n,t)=>n.p0.parsed.y>n.p1.parsed.y?t:undefined,i={fill:!1,interaction:{intersect:!1},radius:0};n.extend({getChartOption:function(r){var e=[],o,u,f;window.chartColors=r.options.colors;for(o in r.options.colors)e.push(o);return u={},f=null,r.type==="line"?(n.isArray(r.data)&&(n.each(r.data,function(i,u){n.each(u.data,function(n,u){u===null&&(r.data[i].data[n]=NaN,r.data[i].segment={borderColor:n=>t(n,"rgb(0,0,0,0.2)"),borderDash:n=>t(n,[6,6])})})}),r.options=n.extend(!0,r.options,i)),u=n.extend(!0,{},chartOption),f=function(t){var i=chartColors[e.shift()];n.extend(t,{backgroundColor:i,borderColor:i})}):r.type==="bar"?(u=n.extend(!0,{},chartOption),f=function(t){var i=chartColors[e.shift()];n.extend(t,{backgroundColor:Chart.helpers.color(i).alpha(.5).rgbString(),borderColor:i,borderWidth:1})}):r.type==="pie"||r.type==="doughnut"?(u=n.extend(!0,{},chartOption,{options:{scales:{x:{display:!1},y:{display:!1}}}}),f=function(t){n.extend(t,{backgroundColor:e.slice(0,t.data.length).map(function(n){return chartColors[n]})})},r.type==="doughnut"&&n.extend(u.options,{cutoutPercentage:50,animation:{animateScale:!0,animateRotate:!0}})):r.type==="bubble"&&(u=n.extend(!0,{},chartOption,{data:{animation:{duration:1e4}},options:{tooltips:{mode:"point"}}}),f=function(t){var i=chartColors[e.shift()];n.extend(t,{backgroundColor:Chart.helpers.color(i).alpha(.5).rgbString(),borderWidth:1,borderColor:i})}),n.each(r.data,function(){f(this)}),n.extend(!0,u,{type:r.type,data:{labels:r.labels,datasets:r.data},options:{responsive:r.options.responsive,maintainAspectRatio:r.options.maintainAspectRatio,plugins:{title:{display:r.options.title!=null,text:r.options.title}},scales:{x:{title:{display:r.options.x.title!=null,text:r.options.x.title},stacked:r.options.x.stacked},y:{title:{display:r.options.y.title!=null,text:r.options.y.title},stacked:r.options.x.stacked}}}})},updateChart:function(n,t){t.updateMethod==="addDataset"?n.data.datasets.push(t.data.datasets.pop()):t.updateMethod==="removeDataset"?n.data.datasets.pop():t.updateMethod==="addData"?n.data.datasets.length>0&&(n.data.labels.push(t.data.labels.pop()),n.data.datasets.forEach(function(n,i){n.data.push(t.data.datasets[i].data.pop());(t.type==="pie"||t.type==="doughnut")&&n.backgroundColor.push(t.data.datasets[i].backgroundColor.pop())})):t.updateMethod==="removeData"?(n.data.labels.pop(),n.data.datasets.forEach(function(n){n.data.pop();(t.type==="pie"||t.type==="doughnut")&&n.backgroundColor.pop()})):t.updateMethod==="setAngle"?t.type==="doughnut"&&(t.angle===360?(n.options.circumference=360,n.options.rotation=-360):(n.options.circumference=180,n.options.rotation=-90)):n.data.datasets.forEach((n,i)=>{n.data=t.data.datasets[i].data})},bb_chart:function(t,i,r,u,f,e,o){var c=n(t),h,s;u.type=e;h=c.data("chart");h?(s=n.getChartOption(u),s.angle=o,s.updateMethod=f,n.updateChart(h.config,s),h.update()):(s=n.getChartOption(u),c.data("chart",h=new Chart(t.getElementsByTagName("canvas"),s)),c.removeClass("is-loading").trigger("chart.afterInit"),i.invokeMethodAsync(r))}})})(jQuery);
(function(n){window.chartOption={options:{responsive:!0,maintainAspectRatio:!0,aspectRatio:2,resizeDelay:0,plugins:{title:{display:!0,text:null}},tooltips:{mode:"index",intersect:!1},hover:{mode:"nearest",intersect:!0},scales:{x:{display:!0,title:{display:!1,text:null}},y:{display:!0,title:{display:!1,text:null}}}}};var t=(n,t)=>n.p0.skip||n.p1.skip?t:undefined,r=(n,t)=>n.p0.parsed.y>n.p1.parsed.y?t:undefined,i={fill:!1,interaction:{intersect:!1},radius:0};n.extend({getChartOption:function(r){var e=[],o,u,f;window.chartColors=r.options.colors;for(o in r.options.colors)e.push(o);return u={},f=null,r.type==="line"?(n.isArray(r.data)&&(n.each(r.data,function(i,u){n.each(u.data,function(n,u){u===null&&(r.data[i].data[n]=NaN,r.data[i].segment={borderColor:n=>t(n,"rgb(0,0,0,0.2)"),borderDash:n=>t(n,[6,6])})})}),r.options=n.extend(!0,r.options,i)),u=n.extend(!0,{},chartOption),f=function(t){var i=chartColors[e.shift()];n.extend(t,{backgroundColor:i,borderColor:i})}):r.type==="bar"?(u=n.extend(!0,{},chartOption),f=function(t){var i=chartColors[e.shift()];n.extend(t,{backgroundColor:Chart.helpers.color(i).alpha(.5).rgbString(),borderColor:i,borderWidth:1})}):r.type==="pie"||r.type==="doughnut"?(u=n.extend(!0,{},chartOption,{options:{scales:{x:{display:!1},y:{display:!1}}}}),f=function(t){n.extend(t,{backgroundColor:e.slice(0,t.data.length).map(function(n){return chartColors[n]})})},r.type==="doughnut"&&n.extend(u.options,{cutoutPercentage:50,animation:{animateScale:!0,animateRotate:!0}})):r.type==="bubble"&&(u=n.extend(!0,{},chartOption,{data:{animation:{duration:1e4}},options:{tooltips:{mode:"point"}}}),f=function(t){var i=chartColors[e.shift()];n.extend(t,{backgroundColor:Chart.helpers.color(i).alpha(.5).rgbString(),borderWidth:1,borderColor:i})}),n.each(r.data,function(){f(this)}),n.extend(!0,u,{type:r.type,data:{labels:r.labels,datasets:r.data},options:{responsive:r.options.responsive,maintainAspectRatio:r.options.maintainAspectRatio,aspectRatio:r.options.aspectRatio,resizeDelay:r.options.resizeDelay,plugins:{title:{display:r.options.title!=null,text:r.options.title}},scales:{x:{title:{display:r.options.x.title!=null,text:r.options.x.title},stacked:r.options.x.stacked},y:{title:{display:r.options.y.title!=null,text:r.options.y.title},stacked:r.options.x.stacked}}}})},updateChart:function(n,t){t.updateMethod==="addDataset"?n.data.datasets.push(t.data.datasets.pop()):t.updateMethod==="removeDataset"?n.data.datasets.pop():t.updateMethod==="addData"?n.data.datasets.length>0&&(n.data.labels.push(t.data.labels.pop()),n.data.datasets.forEach(function(n,i){n.data.push(t.data.datasets[i].data.pop());(t.type==="pie"||t.type==="doughnut")&&n.backgroundColor.push(t.data.datasets[i].backgroundColor.pop())})):t.updateMethod==="removeData"?(n.data.labels.pop(),n.data.datasets.forEach(function(n){n.data.pop();(t.type==="pie"||t.type==="doughnut")&&n.backgroundColor.pop()})):t.updateMethod==="setAngle"?t.type==="doughnut"&&(t.angle===360?(n.options.circumference=360,n.options.rotation=-360):(n.options.circumference=180,n.options.rotation=-90)):t.updateMethod==="reload"?n.data=t.data:n.data.datasets.forEach((n,i)=>{n.data=t.data.datasets[i].data})},bb_chart:function(t,i,r,u,f,e,o){var c=n(t),s,h;u.type=e;s=c.data("chart");s?(h=n.getChartOption(u),h.angle=o,h.updateMethod=f,n.updateChart(s.config,h),s.update()):(h=n.getChartOption(u),c.data("chart",s=new Chart(t.getElementsByTagName("canvas"),h)),u.options.height!==null&&(s.canvas.parentNode.style.height=u.options.height),u.options.width!==null&&(s.canvas.parentNode.style.width=u.options.width),c.removeClass("is-loading").trigger("chart.afterInit"),i.invokeMethodAsync(r))}})})(jQuery);