added classes to all theme controls, added mobile support to Oqtane theme

This commit is contained in:
Shaun Walker 2020-05-08 17:30:35 -04:00
parent aaf2c96374
commit c2ed71ab0d
18 changed files with 278 additions and 230 deletions

View File

@ -85,8 +85,8 @@
private ElementReference _toolBar; private ElementReference _toolBar;
private bool _filemanagervisible = false; private bool _filemanagervisible = false;
private FileManager _fileManager; private FileManager _fileManager;
private string _original = string.Empty;
private string _content = string.Empty; private string _content = string.Empty;
private string _original = string.Empty;
private string _message = string.Empty; private string _message = string.Empty;
[Parameter] [Parameter]
@ -110,8 +110,7 @@
protected override void OnInitialized() protected override void OnInitialized()
{ {
_original = Content; _content = Content; // raw HTML
_content = _original;
} }
protected override async Task OnAfterRenderAsync(bool firstRender) protected override async Task OnAfterRenderAsync(bool firstRender)
@ -129,45 +128,54 @@
await RichTextEditorInterop.LoadEditorContent( await RichTextEditorInterop.LoadEditorContent(
JsRuntime, JsRuntime,
_editorElement, _original); _editorElement, Content);
}
}
public async Task<string> GetText() // preserve a copy of the rich text content ( Quill sanitizes content so we need to retrieve it from the editor )
{ _original = await RichTextEditorInterop.GetHtml(
return await RichTextEditorInterop.GetText(
JsRuntime,
_editorElement);
}
public async Task<string> GetHtml()
{
if (_original != _content)
{
// raw html content changed
return _content;
}
else
{
// return rich text content
return await RichTextEditorInterop.GetHtml(
JsRuntime, JsRuntime,
_editorElement); _editorElement);
} }
} }
public async Task<string> GetContent() public void CloseFileManager()
{ {
return await RichTextEditorInterop.GetContent( _filemanagervisible = false;
JsRuntime, _message = string.Empty;
_editorElement); StateHasChanged();
} }
public async Task EnableEditor(bool mode) public async Task RefreshRichText()
{ {
await RichTextEditorInterop.EnableEditor( await RichTextEditorInterop.LoadEditorContent(
JsRuntime, JsRuntime,
_editorElement, mode); _editorElement, _content);
}
public async Task RefreshRawHtml()
{
_content = await RichTextEditorInterop.GetHtml(
JsRuntime,
_editorElement);
StateHasChanged();
}
public async Task<string> GetHtml()
{
// get rich text content
string content = await RichTextEditorInterop.GetHtml(
JsRuntime,
_editorElement);
if (_original != content)
{
// rich text content has changed - return it
return content;
}
else
{
// return raw html content
return _content;
}
} }
public async Task InsertImage() public async Task InsertImage()
@ -196,25 +204,25 @@
StateHasChanged(); StateHasChanged();
} }
public void CloseFileManager() // other rich text editor methods which can be used by developers
public async Task<string> GetText()
{ {
_filemanagervisible = false; return await RichTextEditorInterop.GetText(
_message = string.Empty;
StateHasChanged();
}
public async Task RefreshRichText()
{
await RichTextEditorInterop.LoadEditorContent(
JsRuntime,
_editorElement, _content);
}
public async Task RefreshRawHtml()
{
_content = await RichTextEditorInterop.GetHtml(
JsRuntime, JsRuntime,
_editorElement); _editorElement);
StateHasChanged(); }
public async Task<string> GetContent()
{
return await RichTextEditorInterop.GetContent(
JsRuntime,
_editorElement);
}
public async Task EnableEditor(bool mode)
{
await RichTextEditorInterop.EnableEditor(
JsRuntime,
_editorElement, mode);
} }
} }

View File

@ -7,15 +7,7 @@
<div class="sidebar"> <div class="sidebar">
<nav class="navbar"> <nav class="navbar">
<Logo /> <Logo /><Menu Orientation="Vertical" />
<button class="navbar-toggler" aria-expanded="false" aria-controls="navbarSupportedContent"
aria-label="Toggle navigation" type="button" data-toggle="collapse"
data-target="#navbarSupportedContent">
<span class="navbar-toggler-icon"></span>
</button>
<div class="collapse navbar-collapse" id="navbarSupportedContent">
<Menu Orientation="Vertical" />
</div>
</nav> </nav>
</div> </div>

View File

@ -4,24 +4,30 @@
@if (BreadCrumbPages.Any()) @if (BreadCrumbPages.Any())
{ {
<ol class="breadcrumb"> <span class="app-breadcrumbs">
@foreach (var p in BreadCrumbPages) <ol class="breadcrumb">
{ @foreach (var p in BreadCrumbPages)
<li class="breadcrumb-item @ActiveClass(p)"> {
<a href="@NavigateUrl(p.Path)">@p.Name</a> if (p.PageId == PageState.Page.PageId)
</li> {
} <li class="breadcrumb-item active">
</ol> <a href="@NavigateUrl(p.Path)">@p.Name</a>
</li>
}
else
{
<li class="breadcrumb-item">
<a href="@NavigateUrl(p.Path)">@p.Name</a>
</li>
}
}
</ol>
</span>
} }
@code { @code {
protected IEnumerable<Page> BreadCrumbPages => GetBreadCrumbPages().Reverse().ToList(); protected IEnumerable<Page> BreadCrumbPages => GetBreadCrumbPages().Reverse().ToList();
protected string ActiveClass(Page page)
{
return (page.PageId == PageState.Page.PageId) ? " active" : string.Empty;
}
private IEnumerable<Page> GetBreadCrumbPages() private IEnumerable<Page> GetBreadCrumbPages()
{ {

View File

@ -1,5 +1,4 @@
@namespace Oqtane.Themes.Controls @namespace Oqtane.Themes.Controls
@using Oqtane.Enums
@inherits ThemeControlBase @inherits ThemeControlBase
@attribute [OqtaneIgnore] @attribute [OqtaneIgnore]
@inject NavigationManager NavigationManager @inject NavigationManager NavigationManager

View File

@ -2,14 +2,16 @@
@inherits LoginBase @inherits LoginBase
@attribute [OqtaneIgnore] @attribute [OqtaneIgnore]
<AuthorizeView> <span class="app-login">
<Authorizing> <AuthorizeView>
<text>...</text> <Authorizing>
</Authorizing> <text>...</text>
<Authorized> </Authorizing>
<button type="button" class="btn btn-primary" @onclick="LogoutUser">Logout</button> <Authorized>
</Authorized> <button type="button" class="btn btn-primary" @onclick="LogoutUser">Logout</button>
<NotAuthorized> </Authorized>
<button type="button" class="btn btn-primary" @onclick="LoginUser">Login</button> <NotAuthorized>
</NotAuthorized> <button type="button" class="btn btn-primary" @onclick="LoginUser">Login</button>
</AuthorizeView> </NotAuthorized>
</AuthorizeView>
</span>

View File

@ -1,22 +1,13 @@
@namespace Oqtane.Themes.Controls @namespace Oqtane.Themes.Controls
@inherits ThemeControlBase @inherits ThemeControlBase
@attribute [OqtaneIgnore] @attribute [OqtaneIgnore]
@inject NavigationManager NavigationManager
@if (PageState.Site.LogoFileId != null) @if (PageState.Site.LogoFileId != null)
{ {
<a href="@Href"> <span class="app-logo">
<img class="img-fluid" src="@ContentUrl(PageState.Site.LogoFileId.Value)" alt="@PageState.Site.Name"/> <a href="@PageState.Alias.Path">
</a> <img class="img-fluid" src="@ContentUrl(PageState.Site.LogoFileId.Value)" alt="@PageState.Site.Name" />
</a>
</span>
} }
@code {
string Href
{
get
{
var uri = new Uri(NavigationManager.Uri);
return $"{uri.Scheme}://{uri.Authority}";
}
}
}

View File

@ -3,11 +3,10 @@
@attribute [OqtaneIgnore] @attribute [OqtaneIgnore]
@if (MenuPages.Any()) @if (MenuPages.Any())
{ {
<button class="navbar-toggler" type="button" data-toggle="collapse" data-target="#Menu" aria-controls="Menu" aria-expanded="false" aria-label="Toggle Navigation">
<span class="navbar-toggler-icon"></span>
</button>
<div class="app-menu"> <div class="app-menu">
<button class="navbar-toggler" type="button" data-toggle="collapse" data-target="#Menu" aria-controls="Menu" aria-expanded="false" aria-label="Toggle navigation">
<span class="navbar-toggler-icon"></span>
</button>
<div class="collapse navbar-collapse" id="Menu"> <div class="collapse navbar-collapse" id="Menu">
<ul class="navbar-nav mr-auto"> <ul class="navbar-nav mr-auto">
@foreach (var p in MenuPages) @foreach (var p in MenuPages)

View File

@ -1,28 +1,32 @@
@namespace Oqtane.Themes.Controls @namespace Oqtane.Themes.Controls
@inherits MenuBase @inherits MenuBase
@attribute [OqtaneIgnore] @attribute [OqtaneIgnore]
@if (MenuPages.Any()) @if (MenuPages.Any())
{ {
<button class="navbar-toggler" type="button" data-toggle="collapse" data-target="#Menu" aria-controls="Menu" aria-expanded="false" aria-label="Toggle Navigation">
<span class="navbar-toggler-icon"></span>
</button>
<div class="app-menu"> <div class="app-menu">
<ul class="nav flex-column"> <div class="collapse navbar-collapse" id="Menu">
@foreach (var p in MenuPages) <ul class="nav flex-column">
{ @foreach (var p in MenuPages)
<li class="nav-item px-3"> {
<a href="@GetUrl(p)" class="nav-link" style="padding-left:@((p.Level + 1) * 15)px !important;" target="@GetTarget(p)"> <li class="nav-item px-3">
<a href="@GetUrl(p)" class="nav-link" style="padding-left:@((p.Level + 1) * 15)px !important;" target="@GetTarget(p)">
@if (p.HasChildren) @if (p.HasChildren)
{ {
<i class="oi oi-chevron-right"></i> <i class="oi oi-chevron-right"></i>
} }
@if (p.Icon != string.Empty) @if (p.Icon != string.Empty)
{ {
<span class="oi oi-@p.Icon" aria-hidden="true"></span> <span class="oi oi-@p.Icon" aria-hidden="true"></span>
} }
@p.Name @p.Name
</a>
</a> </li>
</li> }
} </ul>
</ul> </div>
</div> </div>
} }

View File

@ -4,18 +4,20 @@
@if (PageState.EditMode && !PageState.Page.EditMode && UserSecurity.IsAuthorized(PageState.User,PermissionNames.Edit, ModuleState.Permissions)) @if (PageState.EditMode && !PageState.Page.EditMode && UserSecurity.IsAuthorized(PageState.User,PermissionNames.Edit, ModuleState.Permissions))
{ {
<a class="nav-link dropdown-toggle" data-toggle="dropdown" role="button" aria-haspopup="true" aria-expanded="false"></a> <div class="app-moduleactions">
<div class="dropdown-menu" x-placement="bottom-start" style="position: absolute; will-change: transform; top: 0px; left: 0px; transform: translate3d(0px, 37px, 0px);"> <a class="nav-link dropdown-toggle" data-toggle="dropdown" role="button" aria-haspopup="true" aria-expanded="false"></a>
@foreach (var action in Actions) <div class="dropdown-menu" x-placement="bottom-start" style="position: absolute; will-change: transform; top: 0px; left: 0px; transform: translate3d(0px, 37px, 0px);">
{ @foreach (var action in Actions)
if (string.IsNullOrEmpty(action.Name)) {
{ if (string.IsNullOrEmpty(action.Name))
<div class="dropdown-divider"></div> {
} <div class="dropdown-divider"></div>
else }
{ else
<a class="dropdown-item" @onclick="(async () => await ModuleAction(action))">@action.Name</a> {
} <a class="dropdown-item" @onclick="(async () => await ModuleAction(action))">@action.Name</a>
} }
}
</div>
</div> </div>
} }

View File

@ -2,7 +2,9 @@
@inherits ContainerBase @inherits ContainerBase
@attribute [OqtaneIgnore] @attribute [OqtaneIgnore]
@((MarkupString)title) <span class="app-moduletitle">
@((MarkupString)title)
</span>
@code { @code {
private string title = ""; private string title = "";

View File

@ -3,21 +3,22 @@
@attribute [OqtaneIgnore] @attribute [OqtaneIgnore]
@inject NavigationManager NavigationManager @inject NavigationManager NavigationManager
<AuthorizeView> <span class="app-profile">
<Authorizing> <AuthorizeView>
<text>...</text> <Authorizing>
</Authorizing> <text>...</text>
<Authorized> </Authorizing>
<button type="button" class="btn btn-primary" @onclick="UpdateProfile">@context.User.Identity.Name</button> <Authorized>
</Authorized> <button type="button" class="btn btn-primary" @onclick="UpdateProfile">@context.User.Identity.Name</button>
<NotAuthorized> </Authorized>
@if (PageState.Site.AllowRegistration) <NotAuthorized>
{ @if (PageState.Site.AllowRegistration)
<button type="button" class="btn btn-primary" @onclick="RegisterUser">Register</button> {
} <button type="button" class="btn btn-primary" @onclick="RegisterUser">Register</button>
</NotAuthorized> }
</AuthorizeView> </NotAuthorized>
</AuthorizeView>
</span>
@code { @code {

View File

@ -3,7 +3,10 @@
<main role="main"> <main role="main">
<nav class="navbar navbar-expand-md navbar-dark bg-primary fixed-top"> <nav class="navbar navbar-expand-md navbar-dark bg-primary fixed-top">
<Logo /><Menu Orientation="Horizontal" /><div class="ml-md-auto"><UserProfile /> <Login /> <ControlPanel /></div> <Logo /><Menu Orientation="Horizontal" />
<div class="controls ml-md-auto">
<div class="controls-group"><UserProfile /> <Login /> <ControlPanel /></div>
</div>
</nav> </nav>
<div class="container"> <div class="container">
<PaneLayout /> <PaneLayout />

View File

@ -1,4 +1,6 @@
@namespace Oqtane.UI @using System.Diagnostics.CodeAnalysis
@using System.Runtime.InteropServices
@namespace Oqtane.UI
@inject AuthenticationStateProvider AuthenticationStateProvider @inject AuthenticationStateProvider AuthenticationStateProvider
@inject SiteState SiteState @inject SiteState SiteState
@inject NavigationManager NavigationManager @inject NavigationManager NavigationManager
@ -11,9 +13,6 @@
@inject IModuleService ModuleService @inject IModuleService ModuleService
@inject IModuleDefinitionService ModuleDefinitionService @inject IModuleDefinitionService ModuleDefinitionService
@inject ILogService LogService @inject ILogService LogService
@using System.Diagnostics.CodeAnalysis
@using Oqtane.Enums
@using System.Runtime.InteropServices
@implements IHandleAfterRender @implements IHandleAfterRender
@DynamicComponent @DynamicComponent
@ -89,7 +88,7 @@
// get path // get path
var path = uri.LocalPath.Substring(1); var path = uri.LocalPath.Substring(1);
// parse querystring // parse querystring
var querystring = ParseQueryString(uri.Query); var querystring = ParseQueryString(uri.Query);
// the reload parameter is used during user login/logout // the reload parameter is used during user login/logout
@ -237,7 +236,7 @@
} }
// check if user is authorized to view page // check if user is authorized to view page
if (UserSecurity.IsAuthorized(user,PermissionNames.View, page.Permissions)) if (UserSecurity.IsAuthorized(user, PermissionNames.View, page.Permissions))
{ {
page = await ProcessPage(page, site, user); page = await ProcessPage(page, site, user);

View File

@ -17,4 +17,5 @@
@using Oqtane.Shared @using Oqtane.Shared
@using Oqtane.Themes @using Oqtane.Themes
@using Oqtane.Themes.Controls @using Oqtane.Themes.Controls
@using Oqtane.UI @using Oqtane.UI
@using Oqtane.Enums

View File

@ -197,11 +197,8 @@ namespace Oqtane.Repository
|| Attribute.IsDefined(modulecontroltype, typeof(OqtaneIgnoreAttribute)) || Attribute.IsDefined(modulecontroltype, typeof(OqtaneIgnoreAttribute))
) continue; ) continue;
string[] typename = modulecontroltype.AssemblyQualifiedName?.Split(',').Select(item => item.Trim()).ToArray(); string moduleNamespace = modulecontroltype.Namespace;
string[] segments = typename[0].Split('.'); string qualifiedModuleType = moduleNamespace + ", " + modulecontroltype.Assembly.GetName().Name;
Array.Resize(ref segments, segments.Length - 1);
string moduleType = string.Join(".", segments);
string qualifiedModuleType = moduleType + ", " + typename[1];
int index = moduledefinitions.FindIndex(item => item.ModuleDefinitionName == qualifiedModuleType); int index = moduledefinitions.FindIndex(item => item.ModuleDefinitionName == qualifiedModuleType);
if (index == -1) if (index == -1)
@ -210,7 +207,7 @@ namespace Oqtane.Repository
Type moduletype = assembly Type moduletype = assembly
.GetTypes() .GetTypes()
.Where(item => item.Namespace != null) .Where(item => item.Namespace != null)
.Where(item => item.Namespace.StartsWith(moduleType)) .Where(item => item.Namespace.StartsWith(moduleNamespace))
.FirstOrDefault(item => item.GetInterfaces().Contains(typeof(IModule))); .FirstOrDefault(item => item.GetInterfaces().Contains(typeof(IModule)));
if (moduletype != null) if (moduletype != null)
{ {
@ -223,8 +220,8 @@ namespace Oqtane.Repository
// set default property values // set default property values
moduledefinition = new ModuleDefinition moduledefinition = new ModuleDefinition
{ {
Name = moduleType.Substring(moduleType.LastIndexOf(".") + 1), Name = moduleNamespace.Substring(moduleNamespace.LastIndexOf(".") + 1),
Description = "Manage " + moduleType.Substring(moduleType.LastIndexOf(".") + 1), Description = "Manage " + moduleNamespace.Substring(moduleNamespace.LastIndexOf(".") + 1),
Categories = ((qualifiedModuleType.StartsWith("Oqtane.Modules.Admin.")) ? "Admin" : "") Categories = ((qualifiedModuleType.StartsWith("Oqtane.Modules.Admin.")) ? "Admin" : "")
}; };
} }
@ -232,7 +229,7 @@ namespace Oqtane.Repository
// set internal properties // set internal properties
moduledefinition.ModuleDefinitionName = qualifiedModuleType; moduledefinition.ModuleDefinitionName = qualifiedModuleType;
moduledefinition.Version = ""; // will be populated from database moduledefinition.Version = ""; // will be populated from database
moduledefinition.ControlTypeTemplate = moduleType + "." + Constants.ActionToken + ", " + typename[1]; moduledefinition.ControlTypeTemplate = moduleNamespace + "." + Constants.ActionToken + ", " + modulecontroltype.Assembly.GetName().Name;
moduledefinition.AssemblyName = assembly.GetName().Name; moduledefinition.AssemblyName = assembly.GetName().Name;
if (string.IsNullOrEmpty(moduledefinition.Categories)) if (string.IsNullOrEmpty(moduledefinition.Categories))
@ -269,7 +266,7 @@ namespace Oqtane.Repository
{ {
foreach (string action in actions.Split(',')) foreach (string action in actions.Split(','))
{ {
moduledefinition.ControlTypeRoutes += (action + "=" + modulecontroltype.FullName + ", " + typename[1] + ";"); moduledefinition.ControlTypeRoutes += (action + "=" + modulecontroltype.FullName + ", " + modulecontroltype.Assembly.GetName().Name + ";");
} }
} }

View File

@ -3,6 +3,7 @@ using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Reflection; using System.Reflection;
using Oqtane.Models; using Oqtane.Models;
using Oqtane.Shared;
using Oqtane.Themes; using Oqtane.Themes;
namespace Oqtane.Repository namespace Oqtane.Repository
@ -46,76 +47,77 @@ namespace Oqtane.Repository
Type[] themeControlTypes = assembly.GetTypes().Where(item => item.GetInterfaces().Contains(typeof(IThemeControl))).ToArray(); Type[] themeControlTypes = assembly.GetTypes().Where(item => item.GetInterfaces().Contains(typeof(IThemeControl))).ToArray();
foreach (Type themeControlType in themeControlTypes) foreach (Type themeControlType in themeControlTypes)
{ {
if (themeControlType.Name != "ThemeBase") // Check if type should be ignored
if (themeControlType.Name == "ThemeBase"
|| themeControlType.IsGenericType
|| Attribute.IsDefined(themeControlType, typeof(OqtaneIgnoreAttribute))
) continue;
string themeNamespace = themeControlType.Namespace;
string qualifiedModuleType = themeNamespace + ", " + themeControlType.Assembly.GetName().Name;
int index = themes.FindIndex(item => item.ThemeName == themeNamespace);
if (index == -1)
{ {
string[] typename = themeControlType.AssemblyQualifiedName.Split(',').Select(item => item.Trim()).ToList().ToArray(); // determine if this theme implements ITheme
string[] segments = typename[0].Split('.'); Type themetype = assembly.GetTypes()
Array.Resize(ref segments, segments.Length - 1);
string @namespace = string.Join(".", segments);
int index = themes.FindIndex(item => item.ThemeName == @namespace);
if (index == -1)
{
// determine if this theme implements ITheme
Type themetype = assembly.GetTypes()
.Where(item => item.Namespace != null)
.Where(item => item.Namespace.StartsWith(@namespace))
.Where(item => item.GetInterfaces().Contains(typeof(ITheme))).FirstOrDefault();
if (themetype != null)
{
var themeobject = Activator.CreateInstance(themetype);
theme = (Theme)themetype.GetProperty("Theme").GetValue(themeobject);
}
else
{
theme = new Theme
{
Name = themeControlType.Name,
Version = new Version(1, 0, 0).ToString()
};
}
// set internal properties
theme.ThemeName = @namespace;
theme.ThemeControls = "";
theme.PaneLayouts = "";
theme.ContainerControls = "";
theme.AssemblyName = assembly.FullName.Split(",")[0];
themes.Add(theme);
index = themes.FindIndex(item => item.ThemeName == @namespace);
}
theme = themes[index];
theme.ThemeControls += (themeControlType.FullName + ", " + typename[1] + ";");
// layouts
Type[] layouttypes = assembly.GetTypes()
.Where(item => item.Namespace != null) .Where(item => item.Namespace != null)
.Where(item => item.Namespace.StartsWith(@namespace)) .Where(item => item.Namespace.StartsWith(themeNamespace))
.Where(item => item.GetInterfaces().Contains(typeof(ILayoutControl))).ToArray(); .Where(item => item.GetInterfaces().Contains(typeof(ITheme))).FirstOrDefault();
foreach (Type layouttype in layouttypes) if (themetype != null)
{ {
string panelayout = layouttype.FullName + ", " + typename[1] + ";"; var themeobject = Activator.CreateInstance(themetype);
if (!theme.PaneLayouts.Contains(panelayout)) theme = (Theme)themetype.GetProperty("Theme").GetValue(themeobject);
{
theme.PaneLayouts += panelayout;
}
} }
else
// containers
Type[] containertypes = assembly.GetTypes()
.Where(item => item.Namespace != null)
.Where(item => item.Namespace.StartsWith(@namespace))
.Where(item => item.GetInterfaces().Contains(typeof(IContainerControl))).ToArray();
foreach (Type containertype in containertypes)
{ {
string container = containertype.FullName + ", " + typename[1] + ";"; theme = new Theme
if (!theme.ContainerControls.Contains(container))
{ {
theme.ContainerControls += container; Name = themeControlType.Name,
} Version = new Version(1, 0, 0).ToString()
};
} }
// set internal properties
themes[index] = theme; theme.ThemeName = themeNamespace;
theme.ThemeControls = "";
theme.PaneLayouts = "";
theme.ContainerControls = "";
theme.AssemblyName = assembly.FullName.Split(",")[0];
themes.Add(theme);
index = themes.FindIndex(item => item.ThemeName == themeNamespace);
} }
theme = themes[index];
theme.ThemeControls += (themeControlType.FullName + ", " + themeControlType.Assembly.GetName().Name + ";");
// layouts
Type[] layouttypes = assembly.GetTypes()
.Where(item => item.Namespace != null)
.Where(item => item.Namespace.StartsWith(themeNamespace))
.Where(item => item.GetInterfaces().Contains(typeof(ILayoutControl))).ToArray();
foreach (Type layouttype in layouttypes)
{
string panelayout = layouttype.FullName + ", " + themeControlType.Assembly.GetName().Name + ";";
if (!theme.PaneLayouts.Contains(panelayout))
{
theme.PaneLayouts += panelayout;
}
}
// containers
Type[] containertypes = assembly.GetTypes()
.Where(item => item.Namespace != null)
.Where(item => item.Namespace.StartsWith(themeNamespace))
.Where(item => item.GetInterfaces().Contains(typeof(IContainerControl))).ToArray();
foreach (Type containertype in containertypes)
{
string container = containertype.FullName + ", " + themeControlType.Assembly.GetName().Name + ";";
if (!theme.ContainerControls.Contains(container))
{
theme.ContainerControls += container;
}
}
themes[index] = theme;
} }
return themes; return themes;
} }

View File

@ -83,6 +83,7 @@
.navbar-toggler { .navbar-toggler {
background-color: rgba(255, 255, 255, 0.1); background-color: rgba(255, 255, 255, 0.1);
margin-left: auto;
} }
@media (max-width: 767.98px) { @media (max-width: 767.98px) {

View File

@ -4,6 +4,13 @@ body {
padding-top: 7rem; padding-top: 7rem;
} }
.controls {
z-index: 2000;
padding-top: 15px;
padding-bottom: 15px;
margin-right: 10px;
}
.app-menu .nav-item { .app-menu .nav-item {
font-size: 0.9rem; font-size: 0.9rem;
padding-bottom: 0.5rem; padding-bottom: 0.5rem;
@ -33,3 +40,35 @@ body {
vertical-align: text-top; vertical-align: text-top;
top: -2px; top: -2px;
} }
.navbar-toggler {
background-color: rgba(255, 255, 255, 0.1);
margin-left: auto;
}
@media (max-width: 767px) {
.navbar {
position: fixed;
top: 60px;
width: 100%;
}
.controls {
height: 60px;
top: 15px;
position: fixed;
top: 0px;
width: 100%;
background-color: rgb(0, 0, 0);
}
.controls-group {
float: right;
margin-right: 25px;
}
.container {
position: relative;
top: 30px;
}
}