Local service (#6)
* * registered page level services instead scoped * * warning handling Co-authored-by: Santiago Cattaneo <santiago@rd-its.com>
This commit is contained in:
parent
ae38664643
commit
844ae5a2d6
5 changed files with 114 additions and 52 deletions
|
@ -3,15 +3,16 @@
|
|||
<PageTitle>Blazor Reorder Example</PageTitle>
|
||||
|
||||
<h1>Callbacks</h1>
|
||||
<div>@callback: @data</div>
|
||||
<p>Callbacks and page level service</p>
|
||||
<div>@callback @data</div>
|
||||
|
||||
<div class="row">
|
||||
<div class="col-4">
|
||||
<div class="card">
|
||||
<Reorder Items="list1" TItem="ItemList"
|
||||
OnStart='(i) => { callback = "OnStart"; data = i.ToString(); }'
|
||||
OnChange='(i) => { callback = "OnChange"; data = i.ToString(); }'
|
||||
OnFinish='(i) => { callback = "OnFinish"; data = i.ToString(); }'>
|
||||
<Reorder Items="list1" TItem="ItemList" ReorderService="rs"
|
||||
OnStart='(i) => { callback = "OnStart"; data = i.ToString() ?? ""; }'
|
||||
OnChange='(i) => { callback = "OnChange"; data = i.ToString() ?? ""; }'
|
||||
OnFinish='(i) => { callback = "OnFinish"; data = i.ToString() ?? ""; }'>
|
||||
<div class="mb-2 mx-2">
|
||||
<h5>@context.title</h5>
|
||||
<p>@context.details</p>
|
||||
|
@ -21,7 +22,7 @@
|
|||
</div>
|
||||
<div class="col-4">
|
||||
<div class="card">
|
||||
<Reorder Items="list2" TItem="ItemList"
|
||||
<Reorder Items="list2" TItem="ItemList" ReorderService="rs"
|
||||
OnStart='(i) => i.domClass = "bg-danger"'
|
||||
OnChange='(i) => i.domClass = "bg-info"'
|
||||
OnFinish='(i) => i.domClass = "bg-success"'>
|
||||
|
@ -34,7 +35,7 @@
|
|||
</div>
|
||||
<div class="col-4">
|
||||
<div class="card">
|
||||
<Reorder Items="list3" TItem="ItemList">
|
||||
<Reorder Items="list3" TItem="ItemList" ReorderService="rs">
|
||||
<div class="mb-2 mx-2">
|
||||
<h5>@context.title</h5>
|
||||
<p>@context.details</p>
|
||||
|
@ -48,6 +49,25 @@
|
|||
private string callback = "";
|
||||
private string data = "";
|
||||
|
||||
// You can declare the service at page level (without registering on Progam.cs)
|
||||
// but yo must include the service in every component you use in the page in order make it work
|
||||
ReorderService<ItemList> rs = new();
|
||||
|
||||
// In this example we are registering a new class inline
|
||||
public class ItemList
|
||||
{
|
||||
public string title { get; set; }
|
||||
public string details { get; set; }
|
||||
public string domClass { get; set; }
|
||||
|
||||
public ItemList(string a, string c)
|
||||
{
|
||||
title = a;
|
||||
domClass = "";
|
||||
details = c;
|
||||
}
|
||||
}
|
||||
|
||||
public List<ItemList> list1 = new()
|
||||
{
|
||||
new ItemList("Google", "Again looking for a bug ..."),
|
||||
|
|
|
@ -1,29 +1,15 @@
|
|||
using Microsoft.AspNetCore.Components.Web;
|
||||
using Microsoft.AspNetCore.Components.WebAssembly.Hosting;
|
||||
using BlazorReorderExample;
|
||||
using BlazorReorderList;
|
||||
|
||||
var builder = WebAssemblyHostBuilder.CreateDefault(args);
|
||||
builder.RootComponents.Add<App>("#app");
|
||||
builder.RootComponents.Add<HeadOutlet>("head::after");
|
||||
|
||||
builder.Services.AddScoped(sp => new HttpClient { BaseAddress = new Uri(builder.HostEnvironment.BaseAddress) });
|
||||
builder.Services.AddScoped<BlazorReorderList.ReorderService<ListItem>>();
|
||||
builder.Services.AddScoped<BlazorReorderList.ReorderService<ItemList>>();
|
||||
builder.Services.AddScoped<IReorderService<ListItem>, ReorderService<ListItem>>();
|
||||
|
||||
await builder.Build().RunAsync();
|
||||
|
||||
public record ListItem(string title, string url, string details);
|
||||
|
||||
public class ItemList
|
||||
{
|
||||
public string title { get; set; }
|
||||
public string details { get; set; }
|
||||
public string domClass { get; set; }
|
||||
|
||||
public ItemList(string a, string c)
|
||||
{
|
||||
title = a;
|
||||
domClass = "";
|
||||
details = c;
|
||||
}
|
||||
}
|
25
BlazorReorderList/IReorderService.cs
Normal file
25
BlazorReorderList/IReorderService.cs
Normal file
|
@ -0,0 +1,25 @@
|
|||
using Microsoft.AspNetCore.Components;
|
||||
using Microsoft.AspNetCore.Components.Web;
|
||||
using Microsoft.JSInterop;
|
||||
|
||||
namespace BlazorReorderList
|
||||
{
|
||||
public interface IReorderService<TItem> : IAsyncDisposable
|
||||
{
|
||||
bool isDragging { get; set; }
|
||||
TItem? selected { get; set; }
|
||||
List<TItem>? originItems { get; set; }
|
||||
int elemIndex { get; set; }
|
||||
point elemClickPosition { get; set; }
|
||||
ValueTask<clientRect> getClientRect(ElementReference el);
|
||||
ValueTask<point> getPoint(double PageX, double PageY, double ClientX, double ClientY);
|
||||
ValueTask<point> getPosition(ElementReference el);
|
||||
ValueTask<int> getWidth(ElementReference el);
|
||||
ValueTask initEvents(DotNetObjectReference<Reorder<TItem>> dotNetInstance);
|
||||
ReorderService<TItem> InitService(IJSRuntime jsRuntime);
|
||||
bool isCopy();
|
||||
ValueTask removeEvents(DotNetObjectReference<Reorder<TItem>> dotNetInstance);
|
||||
void Reset();
|
||||
void Set(List<TItem> list, TItem item, int index, point clickPoint, bool copy);
|
||||
}
|
||||
}
|
|
@ -1,11 +1,12 @@
|
|||
@typeparam TItem
|
||||
@using Microsoft.AspNetCore.Components.Routing
|
||||
@using Microsoft.JSInterop
|
||||
@inject ReorderService<TItem> rs
|
||||
@inject IServiceProvider ServiceProvider
|
||||
@inject NavigationManager nav
|
||||
@inject IJSRuntime JS;
|
||||
@implements IDisposable
|
||||
|
||||
@if(rs.isDragging && elemPosition.x != -1000)
|
||||
@if(rs != null && rs.selected != null && rs.isDragging && elemPosition.x != -1000)
|
||||
{
|
||||
<div class="dragging item"
|
||||
style="width: @(elemWidth)px; left: @(elemPosition.x)px; top: @(elemPosition.y)px; transform: translateX(@(ghostTrans.x)px) translateY(@(ghostTrans.y)px )">
|
||||
|
@ -13,7 +14,7 @@
|
|||
</div>
|
||||
}
|
||||
|
||||
@if (itemElem != null)
|
||||
@if (rs != null && rs.selected != null && itemElem != null)
|
||||
{
|
||||
<div class="sortable" @ref="sortable">
|
||||
@foreach (var item in Items.Select((v, i) => (v, i)))
|
||||
|
@ -30,7 +31,7 @@
|
|||
{
|
||||
[Parameter, EditorRequired] public RenderFragment<TItem> ChildContent { get; set; } = null!;
|
||||
[Parameter, EditorRequired] public List<TItem> Items { get; set; } = null!;
|
||||
[Parameter] public Func<TItem, TItem> Copy { get; set; }
|
||||
[Parameter] public Func<TItem, TItem>? Copy { get; set; }
|
||||
[Parameter] public EventCallback<TItem> OnStart { get; set; }
|
||||
[Parameter] public EventCallback<TItem> OnChange { get; set; }
|
||||
[Parameter] public EventCallback<TItem> OnFinish { get; set; }
|
||||
|
@ -38,16 +39,18 @@
|
|||
[Parameter] public bool DisableDrop { get; set; } = false;
|
||||
[Parameter] public bool DisableDrag { get; set; } = false;
|
||||
[Parameter] public bool WithShadow { get; set; } = true;
|
||||
[Parameter] public ReorderService<TItem>? ReorderService { get; set; }
|
||||
|
||||
private bool shouldRender = true; // cancel re-rendering
|
||||
private DotNetObjectReference<Reorder<TItem>>? dotNetHelper; //js-interop 2-ways
|
||||
Dictionary<int, ElementReference> itemElem = new();
|
||||
ElementReference? sortable;
|
||||
ElementReference sortable;
|
||||
point elemPosition = new point(-1000, 0);
|
||||
point ghostTrans = new point(0, 0);
|
||||
point clickPosition = new point(0, 0);
|
||||
int elemWidth = 0;
|
||||
int newElemIndex = -1;
|
||||
private IReorderService<TItem>? rs;
|
||||
|
||||
protected override void OnInitialized()
|
||||
{
|
||||
|
@ -61,7 +64,7 @@
|
|||
|
||||
public void HandleLocationChanged(object? sender, LocationChangedEventArgs e)
|
||||
{
|
||||
if (dotNetHelper == null) return;
|
||||
if (dotNetHelper == null || rs == null) return;
|
||||
base.InvokeAsync(async () =>
|
||||
{
|
||||
await rs.removeEvents(dotNetHelper);
|
||||
|
@ -73,15 +76,22 @@
|
|||
{
|
||||
if (firstRender)
|
||||
{
|
||||
// set if is using scoped service or page service
|
||||
var provider = ServiceProvider.GetService(typeof(IReorderService<TItem>));
|
||||
if (provider != null)
|
||||
rs = (IReorderService<TItem>)provider;
|
||||
if (rs == null && ReorderService != null) rs = ReorderService.InitService(JS);
|
||||
if (rs == null) return;
|
||||
dotNetHelper = DotNetObjectReference.Create(this);
|
||||
await rs.initEvents(dotNetHelper);
|
||||
StateHasChanged();
|
||||
}
|
||||
}
|
||||
|
||||
public async Task onPress(MouseEventArgs m, TItem item, int index)
|
||||
{
|
||||
if (Disabled || DisableDrag) return;
|
||||
if (itemElem == null) return;
|
||||
if (itemElem == null || rs == null) return;
|
||||
|
||||
shouldRender = false; // Because the method triggers re-render, the click propagation is canceled, if you have a link/or any click event inside it's going to stop working
|
||||
var ghostElem = itemElem[index];
|
||||
|
@ -96,7 +106,7 @@
|
|||
public async Task onTouch(TouchEventArgs m, TItem item, int index)
|
||||
{
|
||||
if (Disabled || DisableDrag) return;
|
||||
if (itemElem == null) return;
|
||||
if (itemElem == null || rs == null) return;
|
||||
|
||||
shouldRender = false; // Because the method triggers re-render, the click propagation is canceled, if you have a link/or any click event inside it's going to stop working
|
||||
var ghostElem = itemElem[index];
|
||||
|
@ -112,6 +122,7 @@
|
|||
[JSInvokable]
|
||||
public async Task onRelease(MouseEventArgs m)
|
||||
{
|
||||
if (rs == null) return;
|
||||
elemPosition = new point(-1000, 0);
|
||||
if (rs.isDragging && rs.originItems == Items)
|
||||
{
|
||||
|
@ -134,7 +145,7 @@
|
|||
[JSInvokable]
|
||||
public async Task onMove(point pos)
|
||||
{
|
||||
if (itemElem == null) return;
|
||||
if (itemElem == null || rs == null || rs.originItems == null || rs.selected == null) return;
|
||||
if (rs.isDragging)
|
||||
{
|
||||
shouldRender = false;
|
||||
|
@ -179,6 +190,7 @@
|
|||
// checks if mouse x/y is on top of an item
|
||||
async Task<bool> isOnTop(ElementReference item, point pos)
|
||||
{
|
||||
if (rs == null) return false;
|
||||
var box = await rs.getClientRect(item);
|
||||
if (box.width < 0) return false;
|
||||
var isx = (pos.x > box.left && pos.x < (box.left + box.width));
|
||||
|
|
|
@ -4,22 +4,33 @@ using Microsoft.JSInterop;
|
|||
|
||||
namespace BlazorReorderList;
|
||||
|
||||
public class ReorderService<TItem> : IAsyncDisposable
|
||||
public class ReorderService<TItem> : IReorderService<TItem>
|
||||
{
|
||||
private readonly Lazy<Task<IJSObjectReference>> moduleTask;
|
||||
public List<TItem>? originItems;
|
||||
public int elemIndex = -1;
|
||||
public TItem selected = default(TItem);
|
||||
public point elemClickPosition = new point(0, 0);
|
||||
public bool isDragging = false;
|
||||
public bool isDragging { get; set; } = false;
|
||||
public TItem? selected { get; set; } = default;
|
||||
public List<TItem>? originItems { get; set; }
|
||||
public int elemIndex { get; set; } = -1;
|
||||
public point elemClickPosition { get; set; } = new point(0, 0);
|
||||
|
||||
private Lazy<Task<IJSObjectReference>>? _moduleTask;
|
||||
private bool _copy = false;
|
||||
|
||||
public ReorderService(IJSRuntime jsRuntime)
|
||||
{
|
||||
moduleTask = new(() => jsRuntime.InvokeAsync<IJSObjectReference>(
|
||||
_moduleTask = new(() => jsRuntime.InvokeAsync<IJSObjectReference>(
|
||||
"import", "./_content/BlazorReorderList/ReorderJsInterop.js").AsTask());
|
||||
}
|
||||
|
||||
public ReorderService() { }
|
||||
|
||||
public ReorderService<TItem> InitService(IJSRuntime jsRuntime)
|
||||
{
|
||||
_moduleTask = new(() => jsRuntime.InvokeAsync<IJSObjectReference>(
|
||||
"import", "./_content/BlazorReorderList/ReorderJsInterop.js").AsTask());
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
public void Set(List<TItem> list, TItem item, int index, point clickPoint, bool copy)
|
||||
{
|
||||
isDragging = true;
|
||||
|
@ -41,54 +52,62 @@ public class ReorderService<TItem> : IAsyncDisposable
|
|||
public void Reset()
|
||||
{
|
||||
isDragging = false;
|
||||
originItems = default(List<TItem>);
|
||||
selected = default(TItem);
|
||||
originItems = default;
|
||||
selected = default;
|
||||
elemClickPosition = new point(0, 0);
|
||||
_copy = false;
|
||||
}
|
||||
|
||||
public async ValueTask initEvents(DotNetObjectReference<Reorder<TItem>> dotNetInstance)
|
||||
{
|
||||
var module = await moduleTask.Value;
|
||||
if (_moduleTask == null) throw new Exception("Reorder Component: JS module not initializated");
|
||||
var module = await _moduleTask.Value;
|
||||
await module.InvokeVoidAsync("initEvents", dotNetInstance);
|
||||
}
|
||||
|
||||
public async ValueTask removeEvents(DotNetObjectReference<Reorder<TItem>> dotNetInstance)
|
||||
{
|
||||
var module = await moduleTask.Value;
|
||||
if (_moduleTask == null) throw new Exception("Reorder Component: JS module not initializated");
|
||||
var module = await _moduleTask.Value;
|
||||
await module.InvokeVoidAsync("removeEvents", dotNetInstance);
|
||||
}
|
||||
|
||||
public async ValueTask<int> getWidth(ElementReference el)
|
||||
{
|
||||
var module = await moduleTask.Value;
|
||||
if (_moduleTask == null) throw new Exception("Reorder Component: JS module not initializated");
|
||||
var module = await _moduleTask.Value;
|
||||
return await module.InvokeAsync<int>("getWidth", el);
|
||||
}
|
||||
|
||||
public async ValueTask<point> getPosition(ElementReference el)
|
||||
{
|
||||
var module = await moduleTask.Value;
|
||||
if (_moduleTask == null) throw new Exception("Reorder Component: JS module not initializated");
|
||||
var module = await _moduleTask.Value;
|
||||
return await module.InvokeAsync<point>("getPosition", el);
|
||||
}
|
||||
|
||||
public async ValueTask<point> getPoint(double PageX, double PageY, double ClientX, double ClientY)
|
||||
public async ValueTask<point> getPoint(double pageX, double pageY, double clientX, double clientY)
|
||||
{
|
||||
var module = await moduleTask.Value;
|
||||
return await module.InvokeAsync<point>("getPoint", PageX, PageY, ClientX, ClientY);
|
||||
if (_moduleTask == null) throw new Exception("Reorder Component: JS module not initializated");
|
||||
var module = await _moduleTask.Value;
|
||||
return await module.InvokeAsync<point>("getPoint", pageX, pageY, clientX, clientY);
|
||||
}
|
||||
|
||||
public async ValueTask<clientRect> getClientRect(ElementReference el)
|
||||
{
|
||||
var module = await moduleTask.Value;
|
||||
if (_moduleTask == null) throw new Exception("Reorder Component: JS module not initializated");
|
||||
var module = await _moduleTask.Value;
|
||||
return await module.InvokeAsync<clientRect>("getClientRect", el);
|
||||
}
|
||||
|
||||
public async ValueTask DisposeAsync()
|
||||
{
|
||||
if (moduleTask.IsValueCreated)
|
||||
if (_moduleTask == null) throw new Exception("Reorder Component: JS module not initializated");
|
||||
if (_moduleTask.IsValueCreated)
|
||||
{
|
||||
var module = await moduleTask.Value;
|
||||
var module = await _moduleTask.Value;
|
||||
await module.DisposeAsync();
|
||||
GC.SuppressFinalize(this);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue