Merge remote-tracking branch 'oqtane/dev' into dev

This commit is contained in:
Leigh Pointer 2023-01-04 21:27:31 +01:00
commit 79fc03bb0f
7 changed files with 198 additions and 48 deletions

View File

@ -0,0 +1,153 @@
@namespace Oqtane.Modules.Controls
@inherits LocalizableComponent
<div class="app-autocomplete">
<input class="form-control" value="@Value" @oninput="OnInput" @onkeyup="OnKeyUp" placeholder="@Placeholder" autocomplete="off" />
@if (_results != null)
{
<select class="form-select" style="position: relative;" value="@Value" size="@Rows" @onkeyup="OnKeyUp" @onchange="(e => OnChange(e))">
@if (_results.Any())
{
@foreach (var result in _results)
{
if (result.Value == Value)
{
<option selected>@result.Value</option>
}
else
{
<option>@result.Value</option>
}
}
}
else
{
<option disabled>No Results</option>
}
</select>
}
</div>
@code {
Dictionary<string, string> _results;
[Parameter]
public Func<string, Task<Dictionary<string, string>>> OnSearch { get; set; } // required - an async delegate method which accepts a filter string parameter and returns a dictionary
[Parameter]
public int Characters { get; set; } = 3; // optional - number of characters before search is initiated
[Parameter]
public int Rows { get; set; } = 3; // optional - number of result rows to display
[Parameter]
public string Placeholder { get; set; } // optional - placeholder input text
[Parameter]
public string Value { get; set; } // value of item selected
[Parameter]
public string Key { get; set; } // key of item selected
private async Task OnInput(ChangeEventArgs e)
{
Value = e.Value?.ToString();
if (Value?.Length >= Characters)
{
_results = await OnSearch?.Invoke(Value);
}
else
{
_results = null;
}
SetKey();
}
private async Task OnKeyUp(KeyboardEventArgs e)
{
var index = -1;
switch (e.Key)
{
case "ArrowDown":
if (_results == null)
{
if (Value?.Length >= Characters)
{
_results = await OnSearch?.Invoke(Value);
}
}
else
{
index = GetIndex();
if (index < _results.Count - 1)
{
Value = _results.ElementAt(index + 1).Value;
Key = _results.ElementAt(index + 1).Key;
}
}
break;
case "ArrowUp":
index = GetIndex();
if (index > 0)
{
Value = _results.ElementAt(index - 1).Value;
Key = _results.ElementAt(index - 1).Key;
}
break;
case "ArrowRight":
case "Tab":
_results = null;
break;
case "Enter": // note within a form the enter key submits the entire form
case "NumpadEnter":
_results = null;
break;
case "Escape":
Value = "";
_results = null;
break;
}
}
private void OnChange(ChangeEventArgs e)
{
Value = (string)e.Value;
SetKey();
_results = null;
}
private int GetIndex()
{
if (_results != null)
{
for (int index = 0; index < _results.Count; index++)
{
if (_results.ElementAt(index).Value == Value)
{
return index;
}
}
}
return -1;
}
private void SetKey()
{
var index = GetIndex();
if (index != -1)
{
Key = _results.ElementAt(index).Key;
}
else
{
Key = "";
}
}
public void Clear()
{
Value = "";
Key = "";
_results = null;
}
}

View File

@ -8,16 +8,16 @@
@if ((Toolbar == "Top" || Toolbar == "Both") && _pages > 0 && Items.Count() > _maxItems)
{
<ul class="pagination justify-content-center my-2">
<li class="page-item@((_page > 1) ? " page-pointer" : " disabled")">
<li class="page-item@((_page > 1) ? " app-pager-pointer" : " disabled")">
<a class="page-link" @onclick=@(async () => UpdateList(1))><span class="oi oi-media-step-backward" title="start" aria-hidden="true"></span></a>
</li>
@if (_pages > _displayPages && _displayPages > 1)
{
<li class="page-item@((_page > _displayPages) ? " page-pointer" : " disabled")">
<li class="page-item@((_page > _displayPages) ? " app-pager-pointer" : " disabled")">
<a class="page-link" @onclick=@(async () => SkipPages("back"))><span class="oi oi-media-skip-backward" title="skip back" aria-hidden="true"></span></a>
</li>
}
<li class="page-item@((_page > 1) ? " page-pointer" : " disabled")">
<li class="page-item@((_page > 1) ? " app-pager-pointer" : " disabled")">
<a class="page-link" @onclick=@(async () => NavigateToPage("previous"))><span class="oi oi-chevron-left" title="previous" aria-hidden="true"></span></a>
</li>
@for (int i = _startPage; i <= _endPage; i++)
@ -25,27 +25,27 @@
var pager = i;
if (pager == _page)
{
<li class="page-item page-pointer active">
<li class="page-item app-pager-pointer active">
<a class="page-link" @onclick=@(async () => UpdateList(pager))>@pager</a>
</li>
}
else
{
<li class="page-item page-pointer">
<li class="page-item app-pager-pointer">
<a class="page-link" @onclick=@(async () => UpdateList(pager))>@pager</a>
</li>
}
}
<li class="page-item@((_page < _pages) ? " page-pointer" : " disabled")">
<li class="page-item@((_page < _pages) ? " app-pager-pointer" : " disabled")">
<a class="page-link" @onclick=@(async () => NavigateToPage("next"))><span class="oi oi-chevron-right" title="next" aria-hidden="true"></span></a>
</li>
@if (_pages > _displayPages && _displayPages > 1)
{
<li class="page-item@((_endPage < _pages) ? " page-pointer" : " disabled")">
<li class="page-item@((_endPage < _pages) ? " app-pager-pointer" : " disabled")">
<a class="page-link" @onclick=@(async () => SkipPages("forward"))><span class="oi oi-media-skip-forward" title="skip forward" aria-hidden="true"></span></a>
</li>
}
<li class="page-item@((_page < _pages) ? " page-pointer" : " disabled")">
<li class="page-item@((_page < _pages) ? " app-pager-pointer" : " disabled")">
<a class="page-link" @onclick=@(async () => UpdateList(_pages))><span class="oi oi-media-step-forward" title="end" aria-hidden="true"></span></a>
</li>
<li class="page-item disabled">
@ -119,16 +119,16 @@
@if ((Toolbar == "Bottom" || Toolbar == "Both") && _pages > 0 && Items.Count() > _maxItems)
{
<ul class="pagination justify-content-center my-2">
<li class="page-item@((_page > 1) ? " page-pointer" : " disabled")">
<li class="page-item@((_page > 1) ? " app-pager-pointer" : " disabled")">
<a class="page-link" @onclick=@(async () => UpdateList(1))><span class="oi oi-media-step-backward" title="start" aria-hidden="true"></span></a>
</li>
@if (_pages > _displayPages && _displayPages > 1)
{
<li class="page-item@((_page > _displayPages) ? " page-pointer" : " disabled")">
<li class="page-item@((_page > _displayPages) ? " app-pager-pointer" : " disabled")">
<a class="page-link" @onclick=@(async () => SkipPages("back"))><span class="oi oi-media-skip-backward" title="skip back" aria-hidden="true"></span></a>
</li>
}
<li class="page-item@((_page > 1) ? " page-pointer" : " disabled")">
<li class="page-item@((_page > 1) ? " app-pager-pointer" : " disabled")">
<a class="page-link" @onclick=@(async () => NavigateToPage("previous"))><span class="oi oi-chevron-left" title="previous" aria-hidden="true"></span></a>
</li>
@for (int i = _startPage; i <= _endPage; i++)
@ -136,27 +136,27 @@
var pager = i;
if (pager == _page)
{
<li class="page-item page-pointer active">
<li class="page-item app-pager-pointer active">
<a class="page-link" @onclick=@(async () => UpdateList(pager))>@pager</a>
</li>
}
else
{
<li class="page-item page-pointer">
<li class="page-item app-pager-pointer">
<a class="page-link" @onclick=@(async () => UpdateList(pager))>@pager</a>
</li>
}
}
<li class="page-item@((_page < _pages) ? " page-pointer" : " disabled")">
<li class="page-item@((_page < _pages) ? " app-pager-pointer" : " disabled")">
<a class="page-link" @onclick=@(async () => NavigateToPage("next"))><span class="oi oi-chevron-right" title="next" aria-hidden="true"></span></a>
</li>
@if (_pages > _displayPages && _displayPages > 1)
{
<li class="page-item@((_endPage < _pages) ? " page-pointer" : " disabled")">
<li class="page-item@((_endPage < _pages) ? " app-pager-pointer" : " disabled")">
<a class="page-link" @onclick=@(async () => SkipPages("forward"))><span class="oi oi-media-skip-forward" title="skip forward" aria-hidden="true"></span></a>
</li>
}
<li class="page-item@((_page < _pages) ? " page-pointer" : " disabled")">
<li class="page-item@((_page < _pages) ? " app-pager-pointer" : " disabled")">
<a class="page-link" @onclick=@(async () => UpdateList(_pages))><span class="oi oi-media-step-forward" title="end" aria-hidden="true"></span></a>
</li>
<li class="page-item disabled">

View File

@ -2,6 +2,7 @@
@inherits ModuleControlBase
@inject IRoleService RoleService
@inject IUserService UserService
@inject IUserRoleService UserRoleService
@inject IStringLocalizer<PermissionGrid> Localizer
@inject IStringLocalizer<SharedResources> SharedLocalizer
@ -77,23 +78,16 @@
</div>
</div>
<div class="row">
<div class="col">
<table class="table table-borderless">
<tbody>
<tr>
<td class="input-group">
<input type="text" name="Username" class="form-control" placeholder="@Localizer["Username.Enter"]" @bind="@_username" />
<button type="button" class="btn btn-primary" @onclick="AddUser">@SharedLocalizer["Add"]</button>
</td>
</tr>
</tbody>
</table>
<br />
<div class="col-11">
<AutoComplete OnSearch="GetUsers" Placeholder="@Localizer["Username.Enter"]" @ref="_user" />
</div>
<div class="col-1">
<button type="button" class="btn btn-primary" @onclick="AddUser">@SharedLocalizer["Add"]</button>
</div>
</div>
<div class="row">
<div class="col">
<ModuleMessage Type="MessageType.Error" Message="@_message" />
<ModuleMessage Type="MessageType.Warning" Message="@_message" />
</div>
</div>
</div>
@ -104,7 +98,7 @@
private List<Role> _roles;
private List<PermissionString> _permissions;
private List<User> _users = new List<User>();
private string _username = string.Empty;
private AutoComplete _user;
private string _message = string.Empty;
[Parameter]
@ -191,25 +185,28 @@
private bool GetPermissionDisabled(string roleName)
=> (roleName == RoleNames.Admin && !UserSecurity.IsAuthorized(PageState.User, RoleNames.Host)) ? true : false;
private async Task<Dictionary<string, string>> GetUsers(string filter)
{
var users = await UserRoleService.GetUserRolesAsync(PageState.Site.SiteId, RoleNames.Registered);
return users.Where(item => item.User.DisplayName.Contains(filter, StringComparison.OrdinalIgnoreCase))
.ToDictionary(item => item.UserId.ToString(), item => item.User.DisplayName);
}
private async Task AddUser()
{
if (_users.Where(item => item.Username == _username).FirstOrDefault() == null)
if (!string.IsNullOrEmpty(_user.Key))
{
try
var user = await UserService.GetUserAsync(int.Parse(_user.Key), ModuleState.SiteId);
if (user != null && !_users.Any(item => item.UserId == user.UserId))
{
var user = await UserService.GetUserAsync(_username, ModuleState.SiteId);
if (user != null)
{
_users.Add(user);
}
}
catch
{
_message = Localizer["Message.Username.DontExist"];
_users.Add(user);
}
}
_username = string.Empty;
else
{
_message = Localizer["Message.Username.DontExist"];
}
_user.Clear();
}
private void PermissionChanged(bool? value, string permissionName, string securityId)

View File

@ -1,4 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<?xml version="1.0" encoding="utf-8"?>
<root>
<!--
Microsoft ResX Schema
@ -124,9 +124,9 @@
<value>User</value>
</data>
<data name="Username.Enter" xml:space="preserve">
<value>Enter Username</value>
<value>Enter User's Name</value>
</data>
<data name="Message.Username.DontExist" xml:space="preserve">
<value>Username Does Not Exist</value>
<value>User Does Not Exist With Name Specified</value>
</data>
</root>

View File

@ -8,7 +8,7 @@ using [Owner].[Module].Repository;
namespace [Owner].[Module].Migrations
{
[DbContext(typeof([Module]Context))]
[Migration("[Module].01.00.00.00")]
[Migration("[Owner].[Module].01.00.00.00")]
public class InitializeModule : MultiDatabaseMigration
{
public InitializeModule(IDatabase database) : base(database)

View File

@ -217,6 +217,6 @@ app {
/* Oqtane Conrol Styles */
/* Pager */
.page-pointer {
.app-pager-pointer {
cursor: pointer;
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 92 KiB

After

Width:  |  Height:  |  Size: 62 KiB