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 IModuleService ModuleService
@inject IPageService PageService @inject IPageService PageService
<div class="container-fluid"> <TabStrip>
<div class="form-group"> <TabPanel Name="Pages">
@if (_pages == null)
<ul class="nav nav-tabs" role="tablist"> {
<li class="nav-item"> <br />
<a class="nav-link active" data-toggle="tab" href="#Pages" role="tab"> <p>No Deleted Pages</p>
Pages }
</a> else
</li> {
<li class="nav-item"> <Pager Items="@_pages">
<a class="nav-link" data-toggle="tab" href="#Modules" role="tab"> <Header>
Modules <th>&nbsp;</th>
</a> <th>&nbsp;</th>
</li> <th>Name</th>
</ul> <th>Deleted By</th>
<th>Deleted On</th>
<div class="tab-content"> </Header>
<div id="Pages" class="tab-pane fade show active" role="tabpanel"> <Row>
@if (_pages == null) <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>
<br /> <td>@context.Name</td>
<p>No Deleted Pages</p> <td>@context.DeletedBy</td>
} <td>@context.DeletedOn</td>
else </Row>
{ </Pager>
<Pager Items="@_pages"> }
<Header> </TabPanel>
<th>&nbsp;</th> <TabPanel Name="Modules">
<th>&nbsp;</th> @if (_modules == null)
<th>Name</th> {
<th>Deleted By</th> <br />
<th>Deleted On</th> <p>No Deleted Modules</p>
</Header> }
<Row> else
<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> <Pager Items="@_modules">
<td>@context.Name</td> <Header>
<td>@context.DeletedBy</td> <th>&nbsp;</th>
<td>@context.DeletedOn</td> <th>&nbsp;</th>
</Row> <th>Page</th>
</Pager> <th>Module</th>
} <th>Deleted By</th>
</div> <th>Deleted On</th>
<div id="Modules" class="tab-pane fade" role="tabpanel"> </Header>
@if (_modules == null) <Row>
{ <td><button @onclick="@(() => RestoreModule(context))" class="btn btn-info" title="Restore">Restore</button></td>
<br /> <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>
<p>No Deleted Modules</p> <td>@PageState.Pages.Find(item => item.PageId == context.PageId).Name</td>
} <td>@context.Title</td>
else <td>@context.DeletedBy</td>
{ <td>@context.DeletedOn</td>
<Pager Items="@_modules"> </Row>
<Header> </Pager>
<th>&nbsp;</th> }
<th>&nbsp;</th> </TabPanel>
<th>Page</th> </TabStrip>
<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>
@code { @code {
private List<Page> _pages; private List<Page> _pages;

View File

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

View File

@ -15,7 +15,7 @@ else
private string _closeLabel = "</label>"; private string _closeLabel = "</label>";
[Parameter] [Parameter]
public RenderFragment ChildContent { get; set; } // required - the title of the label public RenderFragment ChildContent { get; set; }
[Parameter] [Parameter]
public string For { get; set; } // optional - the id of the associated input control for accessibility 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 @namespace Oqtane.Modules.Controls
@inherits ModuleBase @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 { @code {
[CascadingParameter] [CascadingParameter]
private TabControl Parent { get; set; } private TabStrip Parent { get; set; }
[Parameter] [Parameter]
public RenderFragment ChildContent { get; set; } public RenderFragment ChildContent { get; set; }
[Parameter] [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() protected override void OnInitialized()
{ {
if (Parent == null)
throw new ArgumentNullException(nameof(Parent), "TabPanel must exist within a TabControl");
base.OnInitialized(); 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;
}
}