Skip to content
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
33 changes: 23 additions & 10 deletions src/BootstrapBlazor/Components/ContextMenu/ContextMenuTrigger.cs
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ namespace BootstrapBlazor.Components;
/// <summary>
/// ContextMenuTrigger 组件
/// </summary>
public class ContextMenuTrigger : BootstrapComponentBase
public class ContextMenuTrigger(IOptionsMonitor<BootstrapBlazorOptions> options) : BootstrapComponentBase
{
/// <summary>
/// 获得/设置 子组件
Expand All @@ -35,9 +35,20 @@ public class ContextMenuTrigger : BootstrapComponentBase
[NotNull]
private ContextMenuZone? ContextMenuZone { get; set; }

/// <summary>
/// The timeout duration for touch events to trigger the context menu (in milliseconds).
/// Default is <see cref="ContextMenuOptions.OnTouchDelay"/> milliseconds. Must be greater than 0.
/// </summary>
[Parameter]
public int OnTouchDelay
{
get;
set { if(value > 0) field = value; }
Comment thread
peereflits marked this conversation as resolved.
Outdated
Comment thread
peereflits marked this conversation as resolved.
Outdated
} = options.CurrentValue.ContextMenuOptions.OnTouchDelay;

private string? ClassString => CssBuilder.Default()
.AddClassFromAttributes(AdditionalAttributes)
.Build();
.AddClassFromAttributes(AdditionalAttributes)
.Build();

/// <summary>
/// <inheritdoc/>
Expand Down Expand Up @@ -65,7 +76,7 @@ protected override void BuildRenderTree(RenderTreeBuilder builder)
/// <summary>
/// 是否触摸
/// </summary>
private bool TouchStart { get; set; }
public bool IsTouchStarted { get; private set; }

/// <summary>
/// 触摸定时器工作指示
Expand All @@ -77,11 +88,12 @@ private async Task OnTouchStart(TouchEventArgs e)
if (!IsBusy)
{
IsBusy = true;
TouchStart = true;
IsTouchStarted = true;

// 延时保持 TouchStart 状态
await Task.Delay(200);
if (TouchStart)
// Delay to maintain TouchStart state
await Task.Delay(OnTouchDelay);
if (IsTouchStarted)
{
var args = new MouseEventArgs()
{
Expand All @@ -93,15 +105,16 @@ private async Task OnTouchStart(TouchEventArgs e)
// 弹出关联菜单
await OnContextMenu(args);

//延时防止重复激活菜单功能
await Task.Delay(200);
// 延时防止重复激活菜单功能
// Delay to prevent repeated activation of menu functions
await Task.Delay(OnTouchDelay);
}
IsBusy = false;
}
}

private void OnTouchEnd()
{
TouchStart = false;
IsTouchStarted = false;
}
}
49 changes: 49 additions & 0 deletions test/UnitTest/Components/ContextMenuTest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
// See the LICENSE file in the project root for more information.
// Maintainer: Argo Zhang(argo@live.ca) Website: https://www.blazor.zone

using System.Diagnostics;
using AngleSharp.Dom;
using Microsoft.AspNetCore.Components.Web;
using Microsoft.Extensions.Localization;
Expand Down Expand Up @@ -106,6 +107,54 @@ public async Task ContextMenu_Ok()
Assert.True(clicked);
}

[Fact]
public async Task ContextMenu_TouchWithTimeout_Ok()
{
const int Delay = 300; // ms

var localizer = Context.Services.GetRequiredService<IStringLocalizer<Foo>>();
var foo = Foo.Generate(localizer);

var sut = Context.Render<ContextMenuZone>(x =>
{
x.AddChildContent<ContextMenuTrigger>(y =>
{
y.Add(z => z.ContextItem, foo);
y.Add(z => z.OnTouchDelay, Delay);
y.AddChildContent(z =>
{
z.OpenElement(0, "div");
z.AddAttribute(1, "class", "context-trigger");
z.AddContent(2, foo.Name);
z.CloseElement();
});
});
x.AddChildContent<ContextMenu>(y =>
{
y.AddChildContent<ContextMenuItem>(z =>
{
z.Add(a => a.Icon, "fa fa-test");
z.Add(a => a.Text, "Test");
z.Add(a => a.OnClick, (_, _) => Task.CompletedTask);
});
});
});


var element = sut.Find(".context-trigger");
var sw = Stopwatch.StartNew();
await element.TouchStartAsync(new TouchEventArgs()
{ Detail = 0
, Touches = [new TouchPoint() { ClientX = 10, ClientY = 10, ScreenX = 10, ScreenY = 10 }]
});
Comment thread
peereflits marked this conversation as resolved.
Outdated
Assert.True(sut.FindComponent<ContextMenuTrigger>().Instance.IsTouchStarted);
await element.TouchEndAsync();
sw.Stop();

Assert.True(sw.ElapsedMilliseconds >= Delay * 2);
Comment thread
peereflits marked this conversation as resolved.
Assert.False(sut.FindComponent<ContextMenuTrigger>().Instance.IsTouchStarted);
}

[Theory]
[InlineData(TableRenderMode.Table)]
[InlineData(TableRenderMode.CardView)]
Expand Down