Skip to content

Commit

Permalink
+ SortableGrid
Browse files Browse the repository at this point in the history
  • Loading branch information
NicklausBrain committed Dec 10, 2024
1 parent 3cadfc4 commit 90abde4
Show file tree
Hide file tree
Showing 4 changed files with 292 additions and 26 deletions.
46 changes: 46 additions & 0 deletions My1kWordsEe/Components/Layout/SortableGrid/SortableGrid.razor
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
@namespace BlazorBootstrap
@inherits BlazorBootstrapComponentBase
@typeparam TItem

<div @ref="@Element" id="@Id" name="@Name" class="@ClassNames" style="@StyleNames" @attributes="@AdditionalAttributes">
@if (IsLoading)
{
if (LoadingTemplate is not null)
{
<div class="col-4 p-1">@LoadingTemplate</div>
}
else
{
<Spinner Type="SpinnerType.Dots" Color="SpinnerColor.Secondary" />
}
}
else if (Data?.Any() ?? false)
{
@foreach (var item in Data)
{
if (ItemTemplate is not null)
{
var disableItem = DisableItem?.Invoke(item) ?? false;
if (disableItem) // disable item
{
<div class="col-4 p-1 @filter.Replace(".", "") @DisabledItemCssClass">@ItemTemplate(item)</div>
}
else
{
<div class="col-4 p-1">@ItemTemplate(item)</div>
}
}
}
}
else
{
if (EmptyDataTemplate is not null)
{
<div class="col-4 p-1">@EmptyDataTemplate</div>
}
else
{
<div class="col-4 p-1">@EmptyText</div>
}
}
</div>
241 changes: 241 additions & 0 deletions My1kWordsEe/Components/Layout/SortableGrid/SortableGrid.razor.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,241 @@
using Microsoft.AspNetCore.Components;
using Microsoft.JSInterop;

namespace BlazorBootstrap;

/// <summary>
/// Represents a sortable list component.
/// </summary>
/// <typeparam name="TItem">The type of items in the list.</typeparam>
public partial class SortableGrid<TItem> : BlazorBootstrapComponentBase
{
#region Fields and Constants

/// <summary>
/// A cancellation token source for managing asynchronous operations.
/// </summary>
private CancellationTokenSource cancellationTokenSource = default!;

/// <summary>
/// A CSS selector used to filter disabled items.
/// </summary>
private string filter = ".bb-sortable-list-item-disabled";

/// <summary>
/// A DotNetObjectReference that allows JavaScript interop with this component.
/// </summary>
private DotNetObjectReference<SortableGrid<TItem>>? objRef;

#endregion

#region Methods

/// <inheritdoc />
protected override async ValueTask DisposeAsyncCore(bool disposing)
{
if (disposing) Data = null!;

await base.DisposeAsyncCore(disposing);
}

protected override async Task OnAfterRenderAsync(bool firstRender)
{
if (firstRender)
await SortableGridJsInterop.InitializeAsync(Id!, Name!, Handle!, Group!, AllowSorting, Pull.ToSortableListPullMode(), Put.ToSortableListPutMode(), filter, objRef!);

await base.OnAfterRenderAsync(firstRender);
}

protected override async Task OnInitializedAsync()
{
objRef ??= DotNetObjectReference.Create(this);

await base.OnInitializedAsync();
}

[JSInvokable]
public async Task OnAddJS(int oldIndex, int newIndex)
{
if (OnAdd.HasDelegate)
await OnAdd.InvokeAsync(new SortableListEventArgs(oldIndex, newIndex));
}

[JSInvokable]
public async Task OnRemoveJS(int oldIndex, int newIndex, string fromListName, string toListName)
{
if (OnRemove.HasDelegate)
await OnRemove.InvokeAsync(new SortableListEventArgs(oldIndex, newIndex, fromListName, toListName));
}

[JSInvokable]
public async Task OnUpdateJS(int oldIndex, int newIndex)
{
if (OnUpdate.HasDelegate)
await OnUpdate.InvokeAsync(new SortableListEventArgs(oldIndex, newIndex));
}

#endregion

#region Properties, Indexers

protected override string? ClassNames =>
BuildClassNames(Class, ("row", true));

/// <summary>
/// Gets or sets a value indicating whether sorting is allowed for the list.
/// </summary>
/// <remarks>
/// Default value is true.
/// </remarks>
[Parameter]
public bool AllowSorting { get; set; } = true;

/// <summary>
/// Gets or sets the content to be rendered within the component.
/// </summary>
/// <remarks>
/// Default value is null.
/// </remarks>
[Parameter]
public RenderFragment ChildContent { get; set; } = default!;

/// <summary>
/// Gets or sets the items.
/// </summary>
/// <remarks>
/// Default value is null.
/// </remarks>
[Parameter]
public List<TItem> Data { get; set; } = default!;

/// <summary>
/// Gets or sets the CSS class applied to disabled items.
/// </summary>
/// <remarks>
/// Default value is null.
/// </remarks>
[Parameter]
public string? DisabledItemCssClass { get; set; } = default!;

/// <summary>
/// Gets or sets a delegate that determines whether an item should be disabled.
/// </summary>
[Parameter]
public Func<TItem, bool> DisableItem { get; set; } = default!;

/// <summary>
/// Gets or sets the empty data template.
/// </summary>
/// <remarks>
/// Default value is null.
/// </remarks>
[Parameter]
public RenderFragment EmptyDataTemplate { get; set; } = default!;

/// <summary>
/// Gets or sets the text to display when there are no records in the list.
/// </summary>
/// <remarks>
/// Default value is `No records to display`.
/// </remarks>
[Parameter]
public string EmptyText { get; set; } = "No records to display";

/// <summary>
/// Gets or sets the group name associated with the list.
/// </summary>
/// <remarks>
/// Default value is null.
/// </remarks>
[Parameter]
public string? Group { get; set; }

/// <summary>
/// Gets or sets the CSS selector for the drag handle element.
/// </summary>
/// <remarks>
/// Default value is null.
/// </remarks>
[Parameter]
public string? Handle { get; set; }

/// <summary>
/// Gets or sets the loading state.
/// </summary>
/// <remarks>
/// Default value is false.
/// </remarks>
[Parameter]
public bool IsLoading { get; set; }

/// <summary>
/// Gets or sets the template used to render individual items in the list.
/// </summary>
/// <remarks>
/// Default value is null.
/// </remarks>
[Parameter]
public RenderFragment<TItem>? ItemTemplate { get; set; }

/// <summary>
/// Gets or sets the loading template.
/// </summary>
/// <remarks>
/// Default value is null.
/// </remarks>
[Parameter]
public RenderFragment LoadingTemplate { get; set; } = default!;

/// <summary>
/// Gets or sets the name of the <see cref="SortableGrid{TItem}" /> component.
/// </summary>
/// <remarks>
/// Default value is null.
/// </remarks>
[Parameter]
public string? Name { get; set; }

/// <summary>
/// Gets or sets an event callback that fires when an item is added to the list.
/// </summary>
[Parameter]
public EventCallback<SortableListEventArgs> OnAdd { get; set; }

/// <summary>
/// Gets or sets an event callback that fires when an item is removed from the list.
/// </summary>
[Parameter]
public EventCallback<SortableListEventArgs> OnRemove { get; set; }

/// <summary>
/// Gets or sets an event callback that fires when an item is updated in the list.
/// </summary>
[Parameter]
public EventCallback<SortableListEventArgs> OnUpdate { get; set; }

/// <summary>
/// Gets or sets the pull mode for the sortable list.
/// </summary>
/// <remarks>
/// Default value is <see cref="SortableListPullMode.True" />.
/// </remarks>
[Parameter]
public SortableListPullMode Pull { get; set; } = SortableListPullMode.True;

/// <summary>
/// Gets or sets the put mode for the sortable list.
/// </summary>
/// <remarks>
/// Default value is <see cref="SortableListPutMode.True" />.
/// </remarks>
[Parameter]
public SortableListPutMode Put { get; set; } = SortableListPutMode.True;

/// <summary>
/// Provides JavaScript interop functionality for the Sortable List.
/// </summary>
[Inject]
private SortableListJsInterop SortableGridJsInterop { get; set; } = default!;

#endregion
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
::deep .bb-sortable-list-handle {
cursor: grab !important;
}
28 changes: 2 additions & 26 deletions My1kWordsEe/Components/Pages/FavoritesPage.razor
Original file line number Diff line number Diff line change
Expand Up @@ -91,7 +91,7 @@
<Content>
@if (FavoriteWords.Any())
{
<SortableList TItem="SampleWord"
<SortableGrid TItem="SampleWord"
Data="SampleWordsList"
Context="item"
OnUpdate="@OnWordsListUpdate">
Expand All @@ -113,31 +113,7 @@
</CardBody>
</Card>
</ItemTemplate>
</SortableList>

@* foreach (var wordPair in FavoriteWords)
{
var word = wordPair.Value;
<div class="col col-12 col-sm-4 p-1">
<Card>
<CardBody>
<CardTitle Class="row">
<div class="col-1">
<i role="button" @onclick="@(()=> RemoveFromFavorites(word))" class="bi bi-x-lg float-start favorite-active"></i>
</div>
<div class="col-10">@word.EeWord</div>
<div class="col-1">
<a href="@($"/word/{word.EeWord}")" target="_blank">
<i role="link" class="bi bi-box-arrow-up-right float-end"></i>
</a>
</div>
</CardTitle>
<CardSubTitle>@word.EnWord</CardSubTitle>
<CardText>@word.EnExplanation</CardText>
</CardBody>
</Card>
</div>
} *@
</SortableGrid>
}
else
{
Expand Down

0 comments on commit 90abde4

Please sign in to comment.