Components based on Bootstrap4 for Sections and TabStrip to increase productivity and promote uniformity in Module UIs (#333)

* upgrade to .NET Core 3.2 Preview 3 and fixes for issues created by #314

* Components based on Bootstrap4 for Sections and  TabStrip to increase productivity and promote uniformity in Module UIs
This commit is contained in:
Shaun Walker 2020-04-03 15:04:25 -04:00 committed by GitHub
parent c38dff5e7c
commit d8b15e7a4e
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 177 additions and 139 deletions

View File

@ -5,80 +5,62 @@
@inject IModuleService ModuleService
@inject IPageService PageService
<div class="container-fluid">
<div class="form-group">
<ul class="nav nav-tabs" role="tablist">
<li class="nav-item">
<a class="nav-link active" data-toggle="tab" href="#Pages" role="tab">
Pages
</a>
</li>
<li class="nav-item">
<a class="nav-link" data-toggle="tab" href="#Modules" role="tab">
Modules
</a>
</li>
</ul>
<div class="tab-content">
<div id="Pages" class="tab-pane fade show active" role="tabpanel">
@if (_pages == null)
{
<br />
<p>No Deleted Pages</p>
}
else
{
<Pager Items="@_pages">
<Header>
<th>&nbsp;</th>
<th>&nbsp;</th>
<th>Name</th>
<th>Deleted By</th>
<th>Deleted On</th>
</Header>
<Row>
<td><button @onclick="@(() => RestorePage(context))" class="btn btn-info" title="Restore">Restore</button></td>
<td><ActionDialog Header="Delete Page" Message="@("Are You Sure You Wish To Permanently Delete The " + context.Name + " Page?")" Action="Delete" Security="SecurityAccessLevel.Admin" Class="btn btn-danger" OnClick="@(async () => await DeletePage(context))" /></td>
<td>@context.Name</td>
<td>@context.DeletedBy</td>
<td>@context.DeletedOn</td>
</Row>
</Pager>
}
</div>
<div id="Modules" class="tab-pane fade" role="tabpanel">
@if (_modules == null)
{
<br />
<p>No Deleted Modules</p>
}
else
{
<Pager Items="@_modules">
<Header>
<th>&nbsp;</th>
<th>&nbsp;</th>
<th>Page</th>
<th>Module</th>
<th>Deleted By</th>
<th>Deleted On</th>
</Header>
<Row>
<td><button @onclick="@(() => RestoreModule(context))" class="btn btn-info" title="Restore">Restore</button></td>
<td><ActionDialog Header="Delete Module" Message="@("Are You Sure You Wish To Permanently Delete The " + context.Title + " Module?")" Action="Delete" Security="SecurityAccessLevel.Admin" Class="btn btn-danger" OnClick="@(async () => await DeleteModule(context))" /></td>
<td>@PageState.Pages.Find(item => item.PageId == context.PageId).Name</td>
<td>@context.Title</td>
<td>@context.DeletedBy</td>
<td>@context.DeletedOn</td>
</Row>
</Pager>
}
</div>
</div>
</div>
</div>
<TabStrip>
<TabPanel Name="Pages">
@if (_pages == null)
{
<br />
<p>No Deleted Pages</p>
}
else
{
<Pager Items="@_pages">
<Header>
<th>&nbsp;</th>
<th>&nbsp;</th>
<th>Name</th>
<th>Deleted By</th>
<th>Deleted On</th>
</Header>
<Row>
<td><button @onclick="@(() => RestorePage(context))" class="btn btn-info" title="Restore">Restore</button></td>
<td><ActionDialog Header="Delete Page" Message="@("Are You Sure You Wish To Permanently Delete The " + context.Name + " Page?")" Action="Delete" Security="SecurityAccessLevel.Admin" Class="btn btn-danger" OnClick="@(async () => await DeletePage(context))" /></td>
<td>@context.Name</td>
<td>@context.DeletedBy</td>
<td>@context.DeletedOn</td>
</Row>
</Pager>
}
</TabPanel>
<TabPanel Name="Modules">
@if (_modules == null)
{
<br />
<p>No Deleted Modules</p>
}
else
{
<Pager Items="@_modules">
<Header>
<th>&nbsp;</th>
<th>&nbsp;</th>
<th>Page</th>
<th>Module</th>
<th>Deleted By</th>
<th>Deleted On</th>
</Header>
<Row>
<td><button @onclick="@(() => RestoreModule(context))" class="btn btn-info" title="Restore">Restore</button></td>
<td><ActionDialog Header="Delete Module" Message="@("Are You Sure You Wish To Permanently Delete The " + context.Title + " Module?")" Action="Delete" Security="SecurityAccessLevel.Admin" Class="btn btn-danger" OnClick="@(async () => await DeleteModule(context))" /></td>
<td>@PageState.Pages.Find(item => item.PageId == context.PageId).Name</td>
<td>@context.Title</td>
<td>@context.DeletedBy</td>
<td>@context.DeletedOn</td>
</Row>
</Pager>
}
</TabPanel>
</TabStrip>
@code {
private List<Page> _pages;

View File

@ -123,10 +123,7 @@
</tr>
</table>
<a data-toggle="collapse" class="app-link-unstyled" href="#SMTP" aria-expanded="false" aria-controls="SMTP">
<h5><i class="oi oi-chevron-bottom"></i>&nbsp;SMTP Settings</h5><hr class="app-rule" />
</a>
<div class="collapse" id="SMTP">
<Section Name="SMTP" Heading="SMTP Settings">
<table class="table table-borderless">
<tr>
<td>
@ -169,12 +166,8 @@
</td>
</tr>
</table>
</div>
<a data-toggle="collapse" class="app-link-unstyled" href="#PWA" aria-expanded="false" aria-controls="PWA">
<h5><i class="oi oi-chevron-bottom"></i>&nbsp;Progressive Web Application Settings</h5><hr class="app-rule" />
</a>
<div class="collapse" id="PWA">
</Section>
<Section Name="PWA" Heading="Progressive Web Application Settings">
<table class="table table-borderless">
<tr>
<td>
@ -203,9 +196,8 @@
<FileManager FileId="@_pwasplashiconfileid.ToString()" Filter="png" @ref="_pwasplashiconfilemanager" />
</td>
</tr>
</table>
</div>
</Section>
<br />
<button type="button" class="btn btn-success" @onclick="SaveSite">Save</button>

View File

@ -15,7 +15,7 @@ else
private string _closeLabel = "</label>";
[Parameter]
public RenderFragment ChildContent { get; set; } // required - the title of the label
public RenderFragment ChildContent { get; set; }
[Parameter]
public string For { get; set; } // optional - the id of the associated input control for accessibility

View File

@ -0,0 +1,44 @@
@namespace Oqtane.Modules.Controls
@inherits ModuleBase
<div class="d-flex">
<div>
<a data-toggle="collapse" class="app-link-unstyled" href="#@Name" aria-expanded="@_expanded" aria-controls="@Name">
<h5>@_heading</h5>
</a>
</div>
<div class="ml-auto">
<a data-toggle="collapse" class="app-link-unstyled float-right" href="#@Name" aria-expanded="@_expanded" aria-controls="@Name">
<i class="oi oi-chevron-bottom"></i>&nbsp;
</a>
</div>
</div>
<div class="d-flex">
<hr class="app-rule" />
</div>
<div class="collapse" id="@Name">
@ChildContent
</div>
@code {
private string _heading = string.Empty;
private string _expanded = string.Empty;
[Parameter]
public RenderFragment ChildContent { get; set; }
[Parameter]
public string Name { get; set; } // required - the name of the section
[Parameter]
public string Heading { get; set; } // optional - will default to Name if not provided
[Parameter]
public string Expanded { get; set; } // optional - will default to false if not provided
protected override void OnInitialized()
{
_heading = (!string.IsNullOrEmpty(Heading)) ? Heading : Name;
_expanded = (!string.IsNullOrEmpty(Expanded)) ? Expanded : "false";
}
}

View File

@ -1,44 +0,0 @@
@namespace Oqtane.Modules.Controls
@inherits ModuleBase
<CascadingValue Value="this">
<div>
@foreach (TabPanel tabPanel in _tabPanels)
{
<button type="button"
class="btn @GetButtonClass(tabPanel)"
@onclick=@( () => ActivateTabPanel(tabPanel) )>
@tabPanel.Text
</button>
}
</div>
@ChildContent
</CascadingValue>
@code {
private List<TabPanel> _tabPanels = new List<TabPanel>();
// Next line is needed so we are able to add <TabPanel> components inside
[Parameter]
public RenderFragment ChildContent { get; set; }
public TabPanel ActiveTabPanel { get; set; }
internal void AddTabPanel(TabPanel tabPanel)
{
_tabPanels.Add(tabPanel);
if (_tabPanels.Count == 1)
ActiveTabPanel = tabPanel;
StateHasChanged();
}
private string GetButtonClass(TabPanel tabPanel)
=> tabPanel == ActiveTabPanel
? "btn-primary"
: "btn-secondary";
private void ActivateTabPanel(TabPanel tabPanel)
{
ActiveTabPanel = tabPanel;
}
}

View File

@ -1,27 +1,35 @@
@namespace Oqtane.Modules.Controls
@inherits ModuleBase
@if (Parent.ActiveTabPanel == (TabPanel)(object)this)
@if (Name == Parent.ActiveTab)
{
@ChildContent
<div id="@Name" class="tab-pane fade show active" role="tabpanel">
@ChildContent
</div>
}
else
{
<div id="@Name" class="tab-pane fade" role="tabpanel">
@ChildContent
</div>
}
@code {
[CascadingParameter]
private TabControl Parent { get; set; }
private TabStrip Parent { get; set; }
[Parameter]
public RenderFragment ChildContent { get; set; }
[Parameter]
public string Text { get; set; }
public string Name { get; set; } // required - name of the TabPanel
[Parameter]
public string Heading { get; set; } // optional - defaults to name if not specified
protected override void OnInitialized()
{
if (Parent == null)
throw new ArgumentNullException(nameof(Parent), "TabPanel must exist within a TabControl");
base.OnInitialized();
Parent.AddTabPanel((TabPanel)(object)this);
Parent.AddTabPanel((TabPanel)this);
}
}

View File

@ -0,0 +1,56 @@
@namespace Oqtane.Modules.Controls
@inherits ModuleBase
<CascadingValue Value="this">
<div class="container-fluid">
<div class="form-group">
<ul class="nav nav-tabs" role="tablist">
@foreach (TabPanel tabPanel in _tabPanels)
{
<li class="nav-item">
@if (tabPanel.Name == ActiveTab)
{
<a class="nav-link active" data-toggle="tab" href="#@tabPanel.Name" role="tab">
@DisplayHeading(tabPanel.Name, tabPanel.Heading)
</a>
}
else
{
<a class="nav-link" data-toggle="tab" href="#@tabPanel.Name" role="tab">
@DisplayHeading(tabPanel.Name, tabPanel.Heading)
</a>
}
</li>
}
</ul>
<div class="tab-content">
<br />
@ChildContent
</div>
</div>
</div>
</CascadingValue>
@code {
private List<TabPanel> _tabPanels = new List<TabPanel>();
[Parameter]
public RenderFragment ChildContent { get; set; } // contains the TabPanels
[Parameter]
public string ActiveTab { get; set; } // optional - defaults to first TabPanel if not specified
internal void AddTabPanel(TabPanel tabPanel)
{
_tabPanels.Add(tabPanel);
if (string.IsNullOrEmpty(ActiveTab))
{
ActiveTab = tabPanel.Name;
}
}
private string DisplayHeading(string Name, string Heading)
{
return (string.IsNullOrEmpty(Heading)) ? Name : Heading;
}
}