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:
		@ -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> </th>
 | 
			
		||||
                            <th> </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> </th>
 | 
			
		||||
                            <th> </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> </th>
 | 
			
		||||
                    <th> </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> </th>
 | 
			
		||||
                    <th> </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;
 | 
			
		||||
 | 
			
		||||
@ -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> 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> 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>
 | 
			
		||||
 | 
			
		||||
@ -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
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										44
									
								
								Oqtane.Client/Modules/Controls/Section.razor
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										44
									
								
								Oqtane.Client/Modules/Controls/Section.razor
									
									
									
									
									
										Normal 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> 
 | 
			
		||||
        </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";
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@ -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;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@ -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);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										56
									
								
								Oqtane.Client/Modules/Controls/TabStrip.razor
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										56
									
								
								Oqtane.Client/Modules/Controls/TabStrip.razor
									
									
									
									
									
										Normal 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;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
		Reference in New Issue
	
	Block a user