Merge remote-tracking branch 'oqtane/dev' into dev
This commit is contained in:
commit
79fc03bb0f
153
Oqtane.Client/Modules/Controls/AutoComplete.razor
Normal file
153
Oqtane.Client/Modules/Controls/AutoComplete.razor
Normal 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;
|
||||||
|
}
|
||||||
|
}
|
|
@ -8,16 +8,16 @@
|
||||||
@if ((Toolbar == "Top" || Toolbar == "Both") && _pages > 0 && Items.Count() > _maxItems)
|
@if ((Toolbar == "Top" || Toolbar == "Both") && _pages > 0 && Items.Count() > _maxItems)
|
||||||
{
|
{
|
||||||
<ul class="pagination justify-content-center my-2">
|
<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>
|
<a class="page-link" @onclick=@(async () => UpdateList(1))><span class="oi oi-media-step-backward" title="start" aria-hidden="true"></span></a>
|
||||||
</li>
|
</li>
|
||||||
@if (_pages > _displayPages && _displayPages > 1)
|
@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>
|
<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>
|
||||||
}
|
}
|
||||||
<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>
|
<a class="page-link" @onclick=@(async () => NavigateToPage("previous"))><span class="oi oi-chevron-left" title="previous" aria-hidden="true"></span></a>
|
||||||
</li>
|
</li>
|
||||||
@for (int i = _startPage; i <= _endPage; i++)
|
@for (int i = _startPage; i <= _endPage; i++)
|
||||||
|
@ -25,27 +25,27 @@
|
||||||
var pager = i;
|
var pager = i;
|
||||||
if (pager == _page)
|
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>
|
<a class="page-link" @onclick=@(async () => UpdateList(pager))>@pager</a>
|
||||||
</li>
|
</li>
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
<li class="page-item page-pointer">
|
<li class="page-item app-pager-pointer">
|
||||||
<a class="page-link" @onclick=@(async () => UpdateList(pager))>@pager</a>
|
<a class="page-link" @onclick=@(async () => UpdateList(pager))>@pager</a>
|
||||||
</li>
|
</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>
|
<a class="page-link" @onclick=@(async () => NavigateToPage("next"))><span class="oi oi-chevron-right" title="next" aria-hidden="true"></span></a>
|
||||||
</li>
|
</li>
|
||||||
@if (_pages > _displayPages && _displayPages > 1)
|
@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>
|
<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>
|
||||||
}
|
}
|
||||||
<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>
|
<a class="page-link" @onclick=@(async () => UpdateList(_pages))><span class="oi oi-media-step-forward" title="end" aria-hidden="true"></span></a>
|
||||||
</li>
|
</li>
|
||||||
<li class="page-item disabled">
|
<li class="page-item disabled">
|
||||||
|
@ -119,16 +119,16 @@
|
||||||
@if ((Toolbar == "Bottom" || Toolbar == "Both") && _pages > 0 && Items.Count() > _maxItems)
|
@if ((Toolbar == "Bottom" || Toolbar == "Both") && _pages > 0 && Items.Count() > _maxItems)
|
||||||
{
|
{
|
||||||
<ul class="pagination justify-content-center my-2">
|
<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>
|
<a class="page-link" @onclick=@(async () => UpdateList(1))><span class="oi oi-media-step-backward" title="start" aria-hidden="true"></span></a>
|
||||||
</li>
|
</li>
|
||||||
@if (_pages > _displayPages && _displayPages > 1)
|
@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>
|
<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>
|
||||||
}
|
}
|
||||||
<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>
|
<a class="page-link" @onclick=@(async () => NavigateToPage("previous"))><span class="oi oi-chevron-left" title="previous" aria-hidden="true"></span></a>
|
||||||
</li>
|
</li>
|
||||||
@for (int i = _startPage; i <= _endPage; i++)
|
@for (int i = _startPage; i <= _endPage; i++)
|
||||||
|
@ -136,27 +136,27 @@
|
||||||
var pager = i;
|
var pager = i;
|
||||||
if (pager == _page)
|
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>
|
<a class="page-link" @onclick=@(async () => UpdateList(pager))>@pager</a>
|
||||||
</li>
|
</li>
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
<li class="page-item page-pointer">
|
<li class="page-item app-pager-pointer">
|
||||||
<a class="page-link" @onclick=@(async () => UpdateList(pager))>@pager</a>
|
<a class="page-link" @onclick=@(async () => UpdateList(pager))>@pager</a>
|
||||||
</li>
|
</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>
|
<a class="page-link" @onclick=@(async () => NavigateToPage("next"))><span class="oi oi-chevron-right" title="next" aria-hidden="true"></span></a>
|
||||||
</li>
|
</li>
|
||||||
@if (_pages > _displayPages && _displayPages > 1)
|
@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>
|
<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>
|
||||||
}
|
}
|
||||||
<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>
|
<a class="page-link" @onclick=@(async () => UpdateList(_pages))><span class="oi oi-media-step-forward" title="end" aria-hidden="true"></span></a>
|
||||||
</li>
|
</li>
|
||||||
<li class="page-item disabled">
|
<li class="page-item disabled">
|
||||||
|
|
|
@ -2,6 +2,7 @@
|
||||||
@inherits ModuleControlBase
|
@inherits ModuleControlBase
|
||||||
@inject IRoleService RoleService
|
@inject IRoleService RoleService
|
||||||
@inject IUserService UserService
|
@inject IUserService UserService
|
||||||
|
@inject IUserRoleService UserRoleService
|
||||||
@inject IStringLocalizer<PermissionGrid> Localizer
|
@inject IStringLocalizer<PermissionGrid> Localizer
|
||||||
@inject IStringLocalizer<SharedResources> SharedLocalizer
|
@inject IStringLocalizer<SharedResources> SharedLocalizer
|
||||||
|
|
||||||
|
@ -77,23 +78,16 @@
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="col">
|
<div class="col-11">
|
||||||
<table class="table table-borderless">
|
<AutoComplete OnSearch="GetUsers" Placeholder="@Localizer["Username.Enter"]" @ref="_user" />
|
||||||
<tbody>
|
</div>
|
||||||
<tr>
|
<div class="col-1">
|
||||||
<td class="input-group">
|
<button type="button" class="btn btn-primary" @onclick="AddUser">@SharedLocalizer["Add"]</button>
|
||||||
<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>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="col">
|
<div class="col">
|
||||||
<ModuleMessage Type="MessageType.Error" Message="@_message" />
|
<ModuleMessage Type="MessageType.Warning" Message="@_message" />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -104,7 +98,7 @@
|
||||||
private List<Role> _roles;
|
private List<Role> _roles;
|
||||||
private List<PermissionString> _permissions;
|
private List<PermissionString> _permissions;
|
||||||
private List<User> _users = new List<User>();
|
private List<User> _users = new List<User>();
|
||||||
private string _username = string.Empty;
|
private AutoComplete _user;
|
||||||
private string _message = string.Empty;
|
private string _message = string.Empty;
|
||||||
|
|
||||||
[Parameter]
|
[Parameter]
|
||||||
|
@ -191,25 +185,28 @@
|
||||||
private bool GetPermissionDisabled(string roleName)
|
private bool GetPermissionDisabled(string roleName)
|
||||||
=> (roleName == RoleNames.Admin && !UserSecurity.IsAuthorized(PageState.User, RoleNames.Host)) ? true : false;
|
=> (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()
|
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);
|
_users.Add(user);
|
||||||
if (user != null)
|
|
||||||
{
|
|
||||||
_users.Add(user);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
catch
|
|
||||||
{
|
|
||||||
_message = Localizer["Message.Username.DontExist"];
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
else
|
||||||
_username = string.Empty;
|
{
|
||||||
|
_message = Localizer["Message.Username.DontExist"];
|
||||||
|
}
|
||||||
|
_user.Clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void PermissionChanged(bool? value, string permissionName, string securityId)
|
private void PermissionChanged(bool? value, string permissionName, string securityId)
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<root>
|
<root>
|
||||||
<!--
|
<!--
|
||||||
Microsoft ResX Schema
|
Microsoft ResX Schema
|
||||||
|
@ -124,9 +124,9 @@
|
||||||
<value>User</value>
|
<value>User</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="Username.Enter" xml:space="preserve">
|
<data name="Username.Enter" xml:space="preserve">
|
||||||
<value>Enter Username</value>
|
<value>Enter User's Name</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="Message.Username.DontExist" xml:space="preserve">
|
<data name="Message.Username.DontExist" xml:space="preserve">
|
||||||
<value>Username Does Not Exist</value>
|
<value>User Does Not Exist With Name Specified</value>
|
||||||
</data>
|
</data>
|
||||||
</root>
|
</root>
|
|
@ -8,7 +8,7 @@ using [Owner].[Module].Repository;
|
||||||
namespace [Owner].[Module].Migrations
|
namespace [Owner].[Module].Migrations
|
||||||
{
|
{
|
||||||
[DbContext(typeof([Module]Context))]
|
[DbContext(typeof([Module]Context))]
|
||||||
[Migration("[Module].01.00.00.00")]
|
[Migration("[Owner].[Module].01.00.00.00")]
|
||||||
public class InitializeModule : MultiDatabaseMigration
|
public class InitializeModule : MultiDatabaseMigration
|
||||||
{
|
{
|
||||||
public InitializeModule(IDatabase database) : base(database)
|
public InitializeModule(IDatabase database) : base(database)
|
||||||
|
|
|
@ -217,6 +217,6 @@ app {
|
||||||
/* Oqtane Conrol Styles */
|
/* Oqtane Conrol Styles */
|
||||||
|
|
||||||
/* Pager */
|
/* Pager */
|
||||||
.page-pointer {
|
.app-pager-pointer {
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
}
|
}
|
BIN
oqtane.png
BIN
oqtane.png
Binary file not shown.
Before Width: | Height: | Size: 92 KiB After Width: | Height: | Size: 62 KiB |
Loading…
Reference in New Issue
Block a user