Skip to content

Commit

Permalink
Make features of entity picker configurable (#211)
Browse files Browse the repository at this point in the history
  • Loading branch information
ThomasBleijendaal authored Jan 23, 2024
1 parent 67aeae8 commit 34fc135
Show file tree
Hide file tree
Showing 7 changed files with 102 additions and 15 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -153,7 +153,9 @@ public static void AddPersonCollection(this ICmsConfig config)
// so this field will allow the user to select an entity that is one level deeper in the person-tree
section.AddField(x => x.FavouriteChildId)
.SetName("Favorite child")
.SetType(EditorType.Select)
.SetType(EditorType.EntityPicker)
// use the Picker configuration class to configure EntityPicker and EntitiesPicker editors
.SetConfiguration(new Picker(PageSize: 3))
.VisibleWhen((person, state) => state == EntityState.IsExisting)
.SetCollectionRelation<Person>("person", config =>
{
Expand Down
7 changes: 7 additions & 0 deletions src/RapidCMS.Core/Models/Configuration/Picker.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
namespace RapidCMS.Core.Models.Configuration;

public record Picker(
bool EnableSelectAll = false,
bool EnableUnselectAll = false,
bool EnableReset = false,
int PageSize = 25);
11 changes: 8 additions & 3 deletions src/RapidCMS.Core/Providers/CollectionDataProvider.cs
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,6 @@
using RapidCMS.Core.Forms;
using RapidCMS.Core.Models.Data;
using RapidCMS.Core.Models.EventArgs.Mediators;
using RapidCMS.Core.Models.Setup;

namespace RapidCMS.Core.Providers;

Expand Down Expand Up @@ -156,13 +155,19 @@ public async Task<IReadOnlyList<IElement>> GetRelatedElementsAsync()
return elements.Where(x => _relatedIds.Contains(x.Id)).ToList();
}

public void AddElement(object id) => _relatedIds.Add(id);
public void AddElement(object id)
{
if (!_relatedIds.Contains(id))
{
_relatedIds.Add(id);
}
}

public void RemoveElement(object id) => _relatedIds.Remove(id);

public bool IsRelated(object id) => _relatedIds.Any(x => x.Equals(id));

public IReadOnlyList<object> GetCurrentRelatedElementIds() => _relatedIds;
public IReadOnlyList<object> GetCurrentRelatedElementIds() => _relatedIds.ToList();

public Type GetRelatedEntityType() => _relatedEntityType ?? typeof(object);

Expand Down
61 changes: 58 additions & 3 deletions src/RapidCMS.UI/Components/Editors/BasePicker.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,14 @@
using Microsoft.AspNetCore.Components;
using Microsoft.JSInterop;
using RapidCMS.Core.Abstractions.Data;
using RapidCMS.Core.Abstractions.UI;
using RapidCMS.Core.Models.Configuration;
using RapidCMS.Core.Models.Data;
using RapidCMS.UI.Extensions;

namespace RapidCMS.UI.Components.Editors;

public abstract class BasePicker : BaseDataEditor
public abstract class BasePicker : BaseDataEditor, IWantConfiguration<Picker>
{
protected string? _searchTerm;
protected int _currentPage = 1;
Expand All @@ -25,15 +28,19 @@ public abstract class BasePicker : BaseDataEditor

protected virtual bool IsMultiple { get; set; }

protected Picker Config { get; set; }

Check warning on line 31 in src/RapidCMS.UI/Components/Editors/BasePicker.cs

View workflow job for this annotation

GitHub Actions / build

Non-nullable property 'Config' must contain a non-null value when exiting constructor. Consider declaring the property as nullable.

Check warning on line 31 in src/RapidCMS.UI/Components/Editors/BasePicker.cs

View workflow job for this annotation

GitHub Actions / build

Non-nullable property 'Config' must contain a non-null value when exiting constructor. Consider declaring the property as nullable.

Check warning on line 31 in src/RapidCMS.UI/Components/Editors/BasePicker.cs

View workflow job for this annotation

GitHub Actions / build

Non-nullable property 'Config' must contain a non-null value when exiting constructor. Consider declaring the property as nullable.

Check warning on line 31 in src/RapidCMS.UI/Components/Editors/BasePicker.cs

View workflow job for this annotation

GitHub Actions / build

Non-nullable property 'Config' must contain a non-null value when exiting constructor. Consider declaring the property as nullable.

[Inject]
private IJSRuntime JsRuntime { get; set; } = null!;

private IRelationDataCollection RelationDataCollection
=> DataCollection as IRelationDataCollection
=> DataCollection as IRelationDataCollection
?? throw new InvalidOperationException("Incorrect DataCollection assigned to Entity/iesPicker");

protected override async Task OnInitializedAsync()
{
Config = await this.GetConfigAsync() ?? new();

if (DataCollection != null)
{
DataCollection.OnDataChange += UpdateOptionsAsync;
Expand Down Expand Up @@ -83,14 +90,62 @@ protected async Task ResetViewAsync()
await UpdateOptionsAsync();
}

protected async Task SelectAllAsync()
{
if (RelationDataCollection == null)
{
return;
}

var page = 1;
do
{
var view = View.Create(Config.PageSize, page, null, null);
var data = await RelationDataCollection.GetAvailableElementsAsync(view);

foreach (var item in data)
{
RelationDataCollection.AddElement(item.Id);
}

if (data.Count < Config.PageSize)
{
break;
}
else
{
page++;
}
}
while (true);

StateHasChanged();
}

protected async Task UnselectAllAsync()

Check warning on line 125 in src/RapidCMS.UI/Components/Editors/BasePicker.cs

View workflow job for this annotation

GitHub Actions / build

This async method lacks 'await' operators and will run synchronously. Consider using the 'await' operator to await non-blocking API calls, or 'await Task.Run(...)' to do CPU-bound work on a background thread.

Check warning on line 125 in src/RapidCMS.UI/Components/Editors/BasePicker.cs

View workflow job for this annotation

GitHub Actions / build

This async method lacks 'await' operators and will run synchronously. Consider using the 'await' operator to await non-blocking API calls, or 'await Task.Run(...)' to do CPU-bound work on a background thread.

Check warning on line 125 in src/RapidCMS.UI/Components/Editors/BasePicker.cs

View workflow job for this annotation

GitHub Actions / build

This async method lacks 'await' operators and will run synchronously. Consider using the 'await' operator to await non-blocking API calls, or 'await Task.Run(...)' to do CPU-bound work on a background thread.

Check warning on line 125 in src/RapidCMS.UI/Components/Editors/BasePicker.cs

View workflow job for this annotation

GitHub Actions / build

This async method lacks 'await' operators and will run synchronously. Consider using the 'await' operator to await non-blocking API calls, or 'await Task.Run(...)' to do CPU-bound work on a background thread.
{
if (RelationDataCollection == null)
{
return;
}

var items = RelationDataCollection.GetCurrentRelatedElementIds();
foreach (var item in items)
{
RelationDataCollection.RemoveElement(item);
}

StateHasChanged();
}

private async Task UpdateOptionsAsync()
{
if (DataCollection == null)
{
return;
}

var view = View.Create(25, _currentPage, _searchTerm, default);
var view = View.Create(Config.PageSize, _currentPage, _searchTerm, default);
_options = await DataCollection.GetAvailableElementsAsync(view);

if (view.MoreDataAvailable)
Expand Down
4 changes: 2 additions & 2 deletions src/RapidCMS.UI/Components/Editors/EntitiesPicker.razor
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
@inherits BaseMultiplePickerEditor
@attribute [Relation(RelationType.Many)]

@if (_options != null)
@if (_options != null && Config != null)
{
var index = 0;

<div class="form-control form-control-select-list @(CssHelper.GetDisplayModifier(DisplayType)) @(CssHelper.GetValidationClass(State))" id=@ElementId>
<SearchBar OnResetView="ResetViewAsync" OnSearch="SearchAsync" />
<SearchBar OnResetView="ResetViewAsync" OnSelectAll="SelectAllAsync" OnUnselectAll="UnselectAllAsync" OnSearch="SearchAsync" Config="Config" />

@foreach (var option in _options)
{
Expand Down
4 changes: 2 additions & 2 deletions src/RapidCMS.UI/Components/Editors/EntityPicker.razor
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
@inherits BasePicker
@attribute [Relation(RelationType.One)]

@if (_options != null)
@if (_options != null && Config != null)
{
var index = 0;

<div class="form-control form-control-select-list @(CssHelper.GetDisplayModifier(DisplayType)) @(CssHelper.GetValidationClass(State))" id=@ElementId>
<SearchBar OnResetView="ResetViewAsync" OnSearch="SearchAsync" />
<SearchBar OnResetView="ResetViewAsync" OnSearch="SearchAsync" Config="Config" />

@foreach (var option in _options)
{
Expand Down
26 changes: 22 additions & 4 deletions src/RapidCMS.UI/Components/Editors/Parts/SearchBar.razor
Original file line number Diff line number Diff line change
@@ -1,7 +1,22 @@
<ul class="nav nav-tabs">
<li class="nav-item">
<button type="button" class="btn btn-light" @onclick="@(async (x) => await OnResetView.InvokeAsync())"><Icon Name="Refresh" /> Reset</button>
</li>
@if (Config.EnableSelectAll)
{
<li class="nav-item">
<button type="button" class="btn btn-light" @onclick="@(async (x) => await OnSelectAll.InvokeAsync())"><Icon Name="MultiSelect" /> Select all</button>
</li>
}
@if (Config.EnableUnselectAll)
{
<li class="nav-item">
<button type="button" class="btn btn-light" @onclick="@(async (x) => await OnUnselectAll.InvokeAsync())"><Icon Name="RemoveFilter" /> Unselect all</button>
</li>
}
@if (Config.EnableReset)
{
<li class="nav-item">
<button type="button" class="btn btn-light" @onclick="@(async (x) => await OnResetView.InvokeAsync())"><Icon Name="Refresh" /> Reset</button>
</li>
}

<li class="nav-item search">
<div class="input-group">
Expand All @@ -24,5 +39,8 @@
private string? _searchTerm;

[Parameter] public EventCallback OnResetView { get; set; } = default!;
[Parameter] public EventCallback OnSelectAll { get; set; } = default!;
[Parameter] public EventCallback OnUnselectAll { get; set; } = default!;
[Parameter] public EventCallback<string> OnSearch { get; set; } = default!;
}
[Parameter] public Picker Config { get; set; } = default!;
}

0 comments on commit 34fc135

Please sign in to comment.