Skip to content

Commit ba7b328

Browse files
committed
Merge branch 'main' into copilot/fix-030e1e49-e9e3-4284-bfb2-1455f503ce53
2 parents 42c4136 + 7051bf1 commit ba7b328

File tree

7 files changed

+135
-4
lines changed

7 files changed

+135
-4
lines changed

.github/workflows/pack.yml

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,9 @@ on:
1010
jobs:
1111
pack:
1212
runs-on: ubuntu-latest
13+
permissions:
14+
id-token: write
15+
contents: read
1316

1417
steps:
1518
- uses: actions/checkout@v4
@@ -19,9 +22,15 @@ jobs:
1922
with:
2023
dotnet-version: 9.0.x
2124

25+
- name: NuGet login
26+
uses: NuGet/login@v1
27+
id: login
28+
with:
29+
user: ${{ secrets.NUGET_USER }}
30+
2231
- name: Publish to Nuget
2332
env:
24-
NUGET_API_KEY: ${{secrets.NUGET_API_KEY}}
33+
NUGET_API_KEY: ${{steps.login.outputs.NUGET_API_KEY}}
2534
Bundle: True
2635

2736
run: |

BootstrapBlazor.slnx

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,11 @@
11
<Solution>
2+
<Folder Name="/actions/">
3+
<File Path=".github/workflows/auto-pull-request-checks.yml" />
4+
<File Path=".github/workflows/build.yml" />
5+
<File Path=".github/workflows/docker.yml" />
6+
<File Path=".github/workflows/pack.yml" />
7+
<File Path=".github/workflows/publish.yml" />
8+
</Folder>
29
<Folder Name="/configuration/">
310
<File Path=".editorconfig" />
411
<File Path=".gitignore" />

src/BootstrapBlazor/Components/AutoComplete/AutoComplete.razor.cs

Lines changed: 40 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -85,6 +85,8 @@ public partial class AutoComplete
8585
[NotNull]
8686
private RenderTemplate? _dropdown = null;
8787

88+
private string? _clientValue;
89+
8890
private string? ClassString => CssBuilder.Default("auto-complete")
8991
.AddClass("is-clearable", IsClearable)
9092
.Build();
@@ -130,13 +132,36 @@ protected override void OnParametersSet()
130132
PlaceHolder ??= Localizer[nameof(PlaceHolder)];
131133
Icon ??= IconTheme.GetIconByKey(ComponentIcons.AutoCompleteIcon);
132134
LoadingIcon ??= IconTheme.GetIconByKey(ComponentIcons.LoadingIcon);
135+
136+
_clientValue = Value;
133137
}
134138

135139
/// <summary>
136140
/// <inheritdoc/>
137141
/// </summary>
142+
/// <param name="firstRender"></param>
138143
/// <returns></returns>
139-
protected override Task InvokeInitAsync() => InvokeVoidAsync("init", Id, Interop, Value);
144+
protected override async Task OnAfterRenderAsync(bool firstRender)
145+
{
146+
await base.OnAfterRenderAsync(firstRender);
147+
148+
if (!firstRender)
149+
{
150+
if (Value != _clientValue)
151+
{
152+
_clientValue = Value;
153+
await InvokeVoidAsync("setValue", Id, _clientValue);
154+
}
155+
}
156+
}
157+
158+
/// <summary>
159+
/// <inheritdoc/>
160+
/// </summary>
161+
/// <returns></returns>
162+
protected override Task InvokeInitAsync() => InvokeVoidAsync("init", Id, Interop, Value, GetChangedEventCallbackName());
163+
164+
private string? GetChangedEventCallbackName() => (OnValueChanged != null || ValueChanged.HasDelegate) ? nameof(TriggerChange) : null;
140165

141166
/// <summary>
142167
/// Gets whether show the clear button.
@@ -199,6 +224,20 @@ public async Task TriggerFilter(string val)
199224
_dropdown.Render();
200225
}
201226

227+
/// <summary>
228+
/// 支持双向绑定 由客户端 JavaScript 触发
229+
/// </summary>
230+
/// <param name="v"></param>
231+
/// <returns></returns>
232+
[JSInvokable]
233+
public Task TriggerChange(string v)
234+
{
235+
_clientValue = v;
236+
CurrentValueAsString = v;
237+
238+
return Task.CompletedTask;
239+
}
240+
202241
private List<string> GetFilterItemsByValue(string val)
203242
{
204243
var comparison = IgnoreCase ? StringComparison.OrdinalIgnoreCase : StringComparison.Ordinal;

src/BootstrapBlazor/Components/AutoComplete/AutoComplete.razor.js

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,13 +5,15 @@ import EventHandler from "../../modules/event-handler.js"
55
import Input from "../../modules/input.js"
66
import Popover from "../../modules/base-popover.js"
77

8-
export function init(id, invoke) {
8+
export function init(id, invoke, value, changedEventCallback) {
99
const el = document.getElementById(id)
1010
const menu = el.querySelector('.dropdown-menu')
1111
const input = document.getElementById(`${id}_input`)
1212
const ac = { el, invoke, menu, input }
1313
Data.set(id, ac)
1414

15+
input.value = value;
16+
1517
const isPopover = input.getAttribute('data-bs-toggle') === 'bb.dropdown';
1618
if (isPopover) {
1719
ac.popover = Popover.init(el, { toggleClass: '[data-bs-toggle="bb.dropdown"]' });
@@ -62,6 +64,12 @@ export function init(id, invoke) {
6264
}
6365
});
6466

67+
if (changedEventCallback) {
68+
EventHandler.on(input, 'change', e => {
69+
invoke.invokeMethodAsync(changedEventCallback, e.target.value);
70+
});
71+
}
72+
6573
let filterDuration = duration;
6674
if (filterDuration === 0) {
6775
filterDuration = 200;

src/BootstrapBlazor/Components/TimePicker/TimePickerCell.razor.cs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,8 @@
33
// See the LICENSE file in the project root for more information.
44
// Maintainer: Argo Zhang([email protected]) Website: https://www.blazor.zone
55

6+
using System.Globalization;
7+
68
namespace BootstrapBlazor.Components;
79

810
/// <summary>
@@ -51,7 +53,7 @@ public partial class TimePickerCell
5153
/// 获得 组件单元数据样式
5254
/// </summary>
5355
private string? StyleName => CssBuilder.Default()
54-
.AddClass($"transform: translateY({CalcTranslateY()}px);")
56+
.AddClass($"transform: translateY({CalcTranslateY().ToString(CultureInfo.InvariantCulture)}px);")
5557
.Build();
5658

5759
private string? UpIconString => CssBuilder.Default("time-spinner-arrow time-up")

test/UnitTest/Components/AutoCompleteTest.cs

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,37 @@ public void Value_Ok()
5454
Assert.Equal(2, menus.Count);
5555
}
5656

57+
[Fact]
58+
public async Task BindValue_Ok()
59+
{
60+
// 由于设置了双向绑定 Value 改变后触发 change 事件
61+
var clientValue = "";
62+
var cut = Context.RenderComponent<AutoComplete>(pb =>
63+
{
64+
pb.Add(a => a.Items, new List<string>() { "test1", "test12", "test123", "test1234" });
65+
pb.Add(a => a.Value, "test12");
66+
pb.Add(a => a.OnValueChanged, v =>
67+
{
68+
clientValue = v;
69+
return Task.CompletedTask;
70+
});
71+
});
72+
73+
await cut.InvokeAsync(() => cut.Instance.TriggerChange("test4"));
74+
Assert.Equal("test4", clientValue);
75+
76+
cut.SetParametersAndRender(pb =>
77+
{
78+
pb.Add(a => a.OnValueChanged, null);
79+
pb.Add(a => a.ValueChanged, v =>
80+
{
81+
clientValue = v;
82+
});
83+
});
84+
await cut.InvokeAsync(() => cut.Instance.TriggerChange("test5"));
85+
Assert.Equal("test5", clientValue);
86+
}
87+
5788
[Fact]
5889
public void IsClearable_Ok()
5990
{

test/UnitTest/Components/TimePickerTest.cs

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,8 @@
33
// See the LICENSE file in the project root for more information.
44
// Maintainer: Argo Zhang([email protected]) Website: https://www.blazor.zone
55

6+
using System.Globalization;
7+
68
namespace UnitTest.Components;
79

810
public class TimePickerTest : BootstrapBlazorTestBase
@@ -98,4 +100,37 @@ public async Task OnClickConfirm_Ok()
98100
await cut.InvokeAsync(() => btn.Click());
99101
Assert.True(confirm);
100102
}
103+
104+
[Fact]
105+
public async Task TimePickerCell_StyleName_CultureInvariant()
106+
{
107+
// 保存老的 Culture 设置
108+
var originalCulture = CultureInfo.CurrentCulture;
109+
var originalUICulture = CultureInfo.CurrentUICulture;
110+
111+
// 设置为土耳其文化环境 小数点使用逗号
112+
var trCulture = new CultureInfo("tr-TR");
113+
CultureInfo.CurrentCulture = trCulture;
114+
CultureInfo.CurrentUICulture = trCulture;
115+
116+
var cut = Context.RenderComponent<TimePickerCell>(pb =>
117+
{
118+
pb.Add(a => a.ViewMode, TimePickerCellViewMode.Hour);
119+
pb.Add(a => a.Value, TimeSpan.FromHours(2.5));
120+
});
121+
122+
// 调用 OnHeightCallback 方法设置高度
123+
await cut.InvokeAsync(() => cut.Instance.OnHeightCallback(12.25));
124+
cut.SetParametersAndRender();
125+
126+
// 检查高度样式是否正确生成应该是用点而不是逗号
127+
var styleElement = cut.Find("ul.time-spinner-list");
128+
var style = styleElement.GetAttribute("style");
129+
130+
Assert.Contains("-24.5px", style);
131+
132+
// 恢复当前线程文化设置
133+
CultureInfo.CurrentCulture = originalCulture;
134+
CultureInfo.CurrentUICulture = originalUICulture;
135+
}
101136
}

0 commit comments

Comments
 (0)