Skip to content
2 changes: 1 addition & 1 deletion src/BootstrapBlazor/BootstrapBlazor.csproj
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
<Project Sdk="Microsoft.NET.Sdk.Razor">

<PropertyGroup>
<Version>10.5.1-beta08</Version>
<Version>10.5.1-beta10</Version>
</PropertyGroup>

<ItemGroup>
Expand Down
69 changes: 51 additions & 18 deletions src/BootstrapBlazor/Components/BaseComponents/DynamicElement.cs
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ namespace BootstrapBlazor.Components;
/// <para lang="zh">动态元素组件</para>
/// <para lang="en">Dynamic element component</para>
/// </summary>
public class DynamicElement : BootstrapComponentBase, IAsyncDisposable
public class DynamicElement : BootstrapComponentBase
{
/// <summary>
/// <para lang="zh">获得/设置 TagName 属性 默认为 div</para>
Expand Down Expand Up @@ -78,6 +78,34 @@ public class DynamicElement : BootstrapComponentBase, IAsyncDisposable
[Parameter]
public bool TriggerContextMenu { get; set; }

/// <summary>
/// <para lang="zh">获得/设置 OnTouchStart 回调委托</para>
/// <para lang="en">Gets or sets the OnTouchStart callback delegate</para>
/// </summary>
[Parameter]
public Func<TouchEventArgs, Task>? OnTouchStart { get; set; }

/// <summary>
/// <para lang="zh">获得/设置 OnTouchEnd 回调委托</para>
/// <para lang="en">Gets or sets the OnTouchEnd callback delegate</para>
/// </summary>
[Parameter]
public Func<TouchEventArgs, Task>? OnTouchEnd { get; set; }

/// <summary>
/// <para lang="zh">获得/设置 是否触发 OnTouchStart 事件 默认 false</para>
/// <para lang="en">Gets or sets whether to trigger OnTouchStart events. Default is false</para>
/// </summary>
[Parameter]
public bool TriggerTouchStart { get; set; }

/// <summary>
/// <para lang="zh">获得/设置 是否触发 OnTouchEnd 事件 默认 false</para>
/// <para lang="en">Gets or sets whether to trigger OnTouchEnd events. Default is false</para>
/// </summary>
[Parameter]
public bool TriggerTouchEnd { get; set; }

/// <summary>
/// <para lang="zh">获得/设置 内容组件</para>
/// <para lang="en">Gets or sets the child content</para>
Expand Down Expand Up @@ -139,6 +167,16 @@ protected override void BuildRenderTree(RenderTreeBuilder builder)
builder.AddEventPreventDefaultAttribute(9, "oncontextmenu", true);
}

if (IsTriggerTouchStart())
{
builder.AddAttribute(11, "ontouchstart", EventCallback.Factory.Create<TouchEventArgs>(this, OnTriggerTouchStart));
}

if (IsTriggerTouchEnd())
{
builder.AddAttribute(12, "ontouchend", EventCallback.Factory.Create<TouchEventArgs>(this, OnTriggerTouchEnd));
}

builder.AddContent(10, ChildContent);

if (GenerateElement || IsTriggerClick() || IsTriggerDoubleClick())
Expand All @@ -153,6 +191,10 @@ protected override void BuildRenderTree(RenderTreeBuilder builder)

private bool IsTriggerContextMenu() => TriggerContextMenu && OnContextMenu != null;

private bool IsTriggerTouchStart() => TriggerTouchStart && OnTouchStart != null;

private bool IsTriggerTouchEnd() => TriggerTouchEnd && OnTouchEnd != null;

private async Task OnTriggerClick()
{
if (OnClick != null)
Expand All @@ -177,28 +219,19 @@ private async Task OnTriggerContextMenu(MouseEventArgs e)
}
}

/// <summary>
/// <para lang="zh">异步释放资源</para>
/// <para lang="en">Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources asynchronously</para>
/// </summary>
/// <param name="disposing"></param>
protected virtual async ValueTask DisposeAsync(bool disposing)
private async Task OnTriggerTouchStart(TouchEventArgs e)
{
if (disposing)
if (OnTouchStart != null)
{
OnClick = null;
OnDoubleClick = null;
OnContextMenu = null;
ChildContent = null;
await OnTouchStart(e);
}
}

/// <summary>
/// <inheritdoc/>
/// </summary>
public async ValueTask DisposeAsync()
private async Task OnTriggerTouchEnd(TouchEventArgs e)
{
await DisposeAsync(true);
GC.SuppressFinalize(this);
if (OnTouchEnd != null)
{
await OnTouchEnd(e);
}
}
}
10 changes: 0 additions & 10 deletions src/BootstrapBlazor/Components/Button/ButtonBase.cs
Original file line number Diff line number Diff line change
Expand Up @@ -334,16 +334,6 @@ protected override async ValueTask DisposeAsync(bool disposing)
{
if (disposing)
{
if (OnClick.HasDelegate)
{
OnClick = EventCallback<MouseEventArgs>.Empty;
}

if (OnClickWithoutRender != null)
{
OnClickWithoutRender = null;
}

if (IsAsync && ValidateForm != null)
{
ValidateForm.UnregisterAsyncSubmitButton(this);
Expand Down
9 changes: 4 additions & 5 deletions src/BootstrapBlazor/Components/Select/Select.razor
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
@namespace BootstrapBlazor.Components
@namespace BootstrapBlazor.Components
@using Microsoft.AspNetCore.Components.Web.Virtualization
@typeparam TValue
@inherits SimpleSelectBase<TValue>
Expand All @@ -22,8 +22,7 @@
}
else
{
<input type="text" id="@InputId" disabled="@Disabled" placeholder="@PlaceHolder" class="@InputClassString"
value="@SelectedRow?.Text" @onchange="@_onChangeEventCallback" readonly="@ReadonlyString" />
<input type="text" id="@InputId" disabled="@Disabled" placeholder="@PlaceHolder" class="@InputClassString" value="@SelectedRow?.Text" @onchange="OnChange" readonly="@ReadonlyString" />
}
<span class="@AppendClassString"><i class="@DropdownIcon"></i></span>
</div>
Expand Down Expand Up @@ -93,7 +92,7 @@

@code {
RenderFragment<SelectedItem> RenderRow => item =>
@<DynamicElement class="@ActiveItem(item)" OnClick="() => OnClickItem(item)">
@<div class="@ActiveItem(item)" @onclick="() => OnClickItem(item)">
@if (ItemTemplate != null)
{
@ItemTemplate(item)
Expand All @@ -106,7 +105,7 @@
{
@item.Text
}
</DynamicElement>;
</div>;

RenderFragment<PlaceholderContext> RenderPlaceHolderRow => context =>
@<div class="dropdown-item">
Expand Down
23 changes: 0 additions & 23 deletions src/BootstrapBlazor/Components/Select/Select.razor.cs
Original file line number Diff line number Diff line change
Expand Up @@ -202,8 +202,6 @@ public bool IsUseActiveWhenValueIsNull

private string _defaultVirtualizedItemText = "";

private EventCallback<ChangeEventArgs> _onChangeEventCallback = EventCallback<ChangeEventArgs>.Empty;

private SelectedItem? SelectedItem { get; set; }

private SelectedItem? SelectedRow
Expand Down Expand Up @@ -236,10 +234,6 @@ protected override void OnParametersSet()
NoSearchDataText ??= Localizer[nameof(NoSearchDataText)];
DropdownIcon ??= IconTheme.GetIconByKey(ComponentIcons.SelectDropdownIcon);
ClearIcon ??= IconTheme.GetIconByKey(ComponentIcons.SelectClearIcon);

_onChangeEventCallback = IsEditable
? EventCallback.Factory.Create<ChangeEventArgs>(this, OnChange)
: EventCallback<ChangeEventArgs>.Empty;
}

/// <summary>
Expand Down Expand Up @@ -395,8 +389,6 @@ private async Task OnClickItem(SelectedItem item)
_defaultVirtualizedItemText = item.Text;
await SelectedItemChanged(item);
}

StateHasChanged();
}

private async Task SelectedItemChanged(SelectedItem item)
Expand Down Expand Up @@ -509,19 +501,4 @@ private async Task OnChange(ChangeEventArgs args)
?? allItems.Find(i => !i.IsDisabled);
return item;
}

/// <summary>
/// <inheritdoc/>
/// </summary>
/// <param name="disposing"></param>
/// <returns></returns>
protected override ValueTask DisposeAsync(bool disposing)
{
if (disposing)
{
_onChangeEventCallback = EventCallback<ChangeEventArgs>.Empty;
}

return base.DisposeAsync(disposing);
}
}
7 changes: 4 additions & 3 deletions src/BootstrapBlazor/Components/Table/Table.razor
Original file line number Diff line number Diff line change
Expand Up @@ -270,8 +270,8 @@
class="@GetRowClassString(item, "table-row")" style="@GetRowStyleString(item)"
TriggerContextMenu="ContextMenuZone != null"
OnContextMenu="e => OnContextMenu(e, item)"
@ontouchstart="e => OnTouchStart(e, item)"
@ontouchend="OnTouchEnd"
TriggerTouchStart="true" OnTouchStart="e => OnTouchStart(e, item)"
TriggerTouchEnd="true" OnTouchEnd="OnTouchEnd"
TriggerClick="@(ClickToSelect || OnClickRowCallback != null)"
OnClick="() => ClickRow(item)">
@if (IsMultipleSelect)
Expand Down Expand Up @@ -734,7 +734,8 @@
@<DynamicElement @key="GetKeyByITem(item)" TagName="tr"
class="@GetRowClassString(item)" style="@GetRowStyleString(item)"
TriggerContextMenu="ContextMenuZone != null" OnContextMenu="e => OnContextMenu(e, item)"
@ontouchstart="e => OnTouchStart(e, item)" @ontouchend="OnTouchEnd"
TriggerTouchStart="true" OnTouchStart="e => OnTouchStart(e, item)"
TriggerTouchEnd="true" OnTouchEnd="OnTouchEnd"
TriggerClick="@(ClickToSelect || OnClickRowCallback != null)" OnClick="() => ClickRow(item)"
TriggerDoubleClick="@(DoubleClickToEdit || OnDoubleClickRowCallback != null)"
OnDoubleClick="() => DoubleClickRow(item)">
Expand Down
3 changes: 2 additions & 1 deletion src/BootstrapBlazor/Components/Table/Table.razor.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2000,9 +2000,10 @@ private async Task OnTouchStart(TouchEventArgs e, TItem item)
}
}

private void OnTouchEnd()
private Task OnTouchEnd(TouchEventArgs e)
{
TouchStart = false;
return Task.CompletedTask;
}

private object? GetKeyByITem(TItem item) => SortableList != null ? item : null; //OnGetRowKey?.Invoke(item);
Expand Down
43 changes: 43 additions & 0 deletions test/UnitTest/Components/DynamicElementTest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@
// See the LICENSE file in the project root for more information.
// Maintainer: Argo Zhang(argo@live.ca) Website: https://www.blazor.zone

using Microsoft.AspNetCore.Components.Web;

namespace UnitTest.Components;

public class DynamicElementTest
Expand All @@ -18,4 +20,45 @@ public void Key_OK()

Assert.Equal("<div></div>", cut.Markup);
}

[Fact]
public void TouchEvents_Ok()
{
var context = new BunitContext();
TouchEventArgs? touchStartArgs = null;
TouchEventArgs? touchEndArgs = null;
var touchStartEventArgs = new TouchEventArgs
{
Touches = [new TouchPoint { ClientX = 10, ClientY = 20, ScreenX = 30, ScreenY = 40 }]
};
var touchEndEventArgs = new TouchEventArgs
{
ChangedTouches = [new TouchPoint { ClientX = 11, ClientY = 21, ScreenX = 31, ScreenY = 41 }]
};

var cut = context.Render<DynamicElement>(pb =>
{
pb.Add(a => a.TagName, "span");
pb.Add(a => a.TriggerTouchStart, true);
pb.Add(a => a.OnTouchStart, e =>
{
touchStartArgs = e;
return Task.CompletedTask;
});
pb.Add(a => a.TriggerTouchEnd, true);
pb.Add(a => a.OnTouchEnd, e =>
{
touchEndArgs = e;
return Task.CompletedTask;
});
pb.AddChildContent("Touch");
});

var element = cut.Find("span");
element.TriggerEvent("ontouchstart", touchStartEventArgs);
element.TriggerEvent("ontouchend", touchEndEventArgs);

Assert.Same(touchStartEventArgs, touchStartArgs);
Assert.Same(touchEndEventArgs, touchEndArgs);
}
}
Loading