Removed old architecture. Added new base project structure
This commit is contained in:
@@ -0,0 +1,61 @@
|
||||
using Moonlight.Client.Interfaces;
|
||||
using Moonlight.Client.Models;
|
||||
|
||||
namespace Moonlight.Client.Implementations;
|
||||
|
||||
public class DefaultSidebarItemProvider : ISidebarItemProvider
|
||||
{
|
||||
public SidebarItem[] Get()
|
||||
{
|
||||
return
|
||||
[
|
||||
// User
|
||||
new SidebarItem()
|
||||
{
|
||||
Icon = "bi bi-columns",
|
||||
Name = "Overview",
|
||||
Path = "/",
|
||||
Priority = 0,
|
||||
RequiresExactMatch = true
|
||||
},
|
||||
|
||||
// Admin
|
||||
new SidebarItem()
|
||||
{
|
||||
Icon = "bi bi-columns",
|
||||
Name = "Overview",
|
||||
Group = "Admin",
|
||||
Path = "/admin",
|
||||
Priority = 0,
|
||||
RequiresExactMatch = true
|
||||
},
|
||||
new SidebarItem()
|
||||
{
|
||||
Icon = "bi bi-people",
|
||||
Name = "Users",
|
||||
Group = "Admin",
|
||||
Path = "/admin/users",
|
||||
Priority = 1,
|
||||
RequiresExactMatch = false
|
||||
},
|
||||
new SidebarItem()
|
||||
{
|
||||
Icon = "bi bi-key",
|
||||
Name = "API",
|
||||
Group = "Admin",
|
||||
Path = "/admin/api",
|
||||
Priority = 2,
|
||||
RequiresExactMatch = false
|
||||
},
|
||||
new SidebarItem()
|
||||
{
|
||||
Icon = "bi bi-gear",
|
||||
Name = "System",
|
||||
Group = "Admin",
|
||||
Path = "/admin/system",
|
||||
Priority = 3,
|
||||
RequiresExactMatch = false
|
||||
},
|
||||
];
|
||||
}
|
||||
}
|
||||
8
Moonlight.Client/Interfaces/ISidebarItemProvider.cs
Normal file
8
Moonlight.Client/Interfaces/ISidebarItemProvider.cs
Normal file
@@ -0,0 +1,8 @@
|
||||
using Moonlight.Client.Models;
|
||||
|
||||
namespace Moonlight.Client.Interfaces;
|
||||
|
||||
public interface ISidebarItemProvider
|
||||
{
|
||||
public SidebarItem[] Get();
|
||||
}
|
||||
11
Moonlight.Client/Models/SidebarItem.cs
Normal file
11
Moonlight.Client/Models/SidebarItem.cs
Normal file
@@ -0,0 +1,11 @@
|
||||
namespace Moonlight.Client.Models;
|
||||
|
||||
public class SidebarItem
|
||||
{
|
||||
public string Icon { get; set; }
|
||||
public string Name { get; set; }
|
||||
public string? Group { get; set; }
|
||||
public string Path { get; set; }
|
||||
public int Priority { get; set; }
|
||||
public bool RequiresExactMatch { get; set; } = false;
|
||||
}
|
||||
33
Moonlight.Client/Moonlight.Client.csproj
Normal file
33
Moonlight.Client/Moonlight.Client.csproj
Normal file
@@ -0,0 +1,33 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk.BlazorWebAssembly">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net8.0</TargetFramework>
|
||||
<Nullable>enable</Nullable>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<ServiceWorkerAssetsManifest>service-worker-assets.js</ServiceWorkerAssetsManifest>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.AspNetCore.Components.WebAssembly" Version="8.0.6"/>
|
||||
<PackageReference Include="Microsoft.AspNetCore.Components.WebAssembly.DevServer" Version="8.0.6" PrivateAssets="all"/>
|
||||
<PackageReference Include="MoonCore" Version="1.5.7" />
|
||||
<PackageReference Include="MoonCore.Blazor.Tailwind" Version="1.0.1" />
|
||||
<PackageReference Include="MoonCore.PluginFramework" Version="1.0.0" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ServiceWorker Include="wwwroot\service-worker.js" PublishedContent="wwwroot\service-worker.published.js"/>
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\Moonlight.Shared\Moonlight.Shared.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Folder Include="Helpers\" />
|
||||
<Folder Include="Services\" />
|
||||
<Folder Include="UI\Components\" />
|
||||
<Folder Include="wwwroot\css\" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
60
Moonlight.Client/Program.cs
Normal file
60
Moonlight.Client/Program.cs
Normal file
@@ -0,0 +1,60 @@
|
||||
using Microsoft.AspNetCore.Components.Web;
|
||||
using Microsoft.AspNetCore.Components.WebAssembly.Hosting;
|
||||
using MoonCore.Blazor.Tailwind.Extensions;
|
||||
using MoonCore.Extensions;
|
||||
using MoonCore.Helpers;
|
||||
using MoonCore.PluginFramework.Services;
|
||||
using Moonlight.Client.Implementations;
|
||||
using Moonlight.Client.Interfaces;
|
||||
using Moonlight.Client.UI;
|
||||
|
||||
// Build pre run logger
|
||||
var providers = LoggerBuildHelper.BuildFromConfiguration(configuration =>
|
||||
{
|
||||
configuration.Console.Enable = true;
|
||||
configuration.Console.EnableAnsiMode = true;
|
||||
configuration.FileLogging.Enable = false;
|
||||
});
|
||||
|
||||
using var loggerFactory = new LoggerFactory(providers);
|
||||
var logger = loggerFactory.CreateLogger("Startup");
|
||||
|
||||
// Fancy start console output... yes very fancy :>
|
||||
Console.Write("Running ");
|
||||
|
||||
var rainbow = new Crayon.Rainbow(0.5);
|
||||
foreach (var c in "Moonlight")
|
||||
{
|
||||
Console.Write(
|
||||
rainbow
|
||||
.Next()
|
||||
.Bold()
|
||||
.Text(c.ToString())
|
||||
);
|
||||
}
|
||||
|
||||
Console.WriteLine();
|
||||
|
||||
// Building app
|
||||
var builder = WebAssemblyHostBuilder.CreateDefault(args);
|
||||
|
||||
// Configure application logging
|
||||
builder.Logging.ClearProviders();
|
||||
builder.Logging.AddProviders(providers);
|
||||
|
||||
builder.RootComponents.Add<App>("#app");
|
||||
builder.RootComponents.Add<HeadOutlet>("head::after");
|
||||
|
||||
builder.Services.AddScoped(sp => new HttpClient { BaseAddress = new Uri(builder.HostEnvironment.BaseAddress) });
|
||||
builder.Services.AddScoped(sp => new HttpApiClient(sp.GetRequiredService<HttpClient>()));
|
||||
|
||||
builder.Services.AddMoonCoreBlazorTailwind();
|
||||
|
||||
// Implementation service
|
||||
var implementationService = new ImplementationService();
|
||||
|
||||
implementationService.Register<ISidebarItemProvider, DefaultSidebarItemProvider>();
|
||||
|
||||
builder.Services.AddSingleton(implementationService);
|
||||
|
||||
await builder.Build().RunAsync();
|
||||
14
Moonlight.Client/Properties/launchSettings.json
Normal file
14
Moonlight.Client/Properties/launchSettings.json
Normal file
@@ -0,0 +1,14 @@
|
||||
{
|
||||
"profiles": {
|
||||
"http": {
|
||||
"commandName": "Project",
|
||||
"dotnetRunMessages": true,
|
||||
"launchBrowser": false,
|
||||
"inspectUri": "{wsProtocol}://{url.hostname}:{url.port}/_framework/debug/ws-proxy?browser={browserInspectUri}",
|
||||
"applicationUrl": "http://localhost:5165",
|
||||
"environmentVariables": {
|
||||
"ASPNETCORE_ENVIRONMENT": "Development"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
80
Moonlight.Client/Styles/additions/buttons.css
Normal file
80
Moonlight.Client/Styles/additions/buttons.css
Normal file
@@ -0,0 +1,80 @@
|
||||
/* Buttons */
|
||||
|
||||
.btn,
|
||||
.btn-lg,
|
||||
.btn-sm,
|
||||
.btn-xs {
|
||||
@apply font-medium text-sm inline-flex items-center justify-center border border-transparent rounded-lg leading-5 shadow-sm transition;
|
||||
}
|
||||
|
||||
.btn {
|
||||
@apply px-3 py-2;
|
||||
}
|
||||
|
||||
.btn-lg {
|
||||
@apply px-4 py-3;
|
||||
}
|
||||
|
||||
.btn-sm {
|
||||
@apply px-2 py-1;
|
||||
}
|
||||
|
||||
.btn-xs {
|
||||
@apply px-2 py-0.5;
|
||||
}
|
||||
|
||||
/* Colors */
|
||||
|
||||
.btn-primary {
|
||||
@apply bg-primary-600 hover:bg-primary-500 focus-visible:outline-primary-600;
|
||||
}
|
||||
|
||||
.btn-secondary {
|
||||
@apply bg-secondary-800 hover:bg-secondary-700 focus-visible:outline-secondary-800;
|
||||
}
|
||||
|
||||
.btn-tertiary {
|
||||
@apply bg-tertiary-600 hover:bg-tertiary-500 focus-visible:outline-tertiary-600;
|
||||
}
|
||||
|
||||
.btn-danger {
|
||||
@apply bg-danger-600 hover:bg-danger-500 focus-visible:outline-danger-600;
|
||||
}
|
||||
|
||||
.btn-warning {
|
||||
@apply bg-warning-500 hover:bg-warning-400 focus-visible:outline-warning-500;
|
||||
}
|
||||
|
||||
.btn-info {
|
||||
@apply bg-info-600 hover:bg-info-500 focus-visible:outline-info-600;
|
||||
}
|
||||
|
||||
.btn-success {
|
||||
@apply bg-success-600 hover:bg-success-500 focus-visible:outline-success-600;
|
||||
}
|
||||
|
||||
/* Outline */
|
||||
|
||||
.btn-outline-primary {
|
||||
@apply bg-gray-800 hover:border-gray-600 text-primary-500;
|
||||
}
|
||||
|
||||
.btn-outline-tertiary {
|
||||
@apply bg-gray-800 hover:border-gray-600 text-tertiary-500;
|
||||
}
|
||||
|
||||
.btn-outline-danger {
|
||||
@apply bg-gray-800 hover:border-gray-600 text-danger-500;
|
||||
}
|
||||
|
||||
.btn-outline-warning {
|
||||
@apply bg-gray-800 hover:border-gray-600 text-warning-400;
|
||||
}
|
||||
|
||||
.btn-outline-info {
|
||||
@apply bg-gray-800 hover:border-gray-600 text-info-500;
|
||||
}
|
||||
|
||||
.btn-outline-success {
|
||||
@apply bg-gray-800 hover:border-gray-600 text-success-500;
|
||||
}
|
||||
19
Moonlight.Client/Styles/additions/cards.css
Normal file
19
Moonlight.Client/Styles/additions/cards.css
Normal file
@@ -0,0 +1,19 @@
|
||||
.card {
|
||||
@apply flex flex-col bg-gray-800 shadow-sm rounded-xl;
|
||||
}
|
||||
|
||||
.card-header {
|
||||
@apply px-5 py-4 border-b border-gray-700/60 flex items-center;
|
||||
}
|
||||
|
||||
.card-title {
|
||||
@apply font-semibold text-gray-100;
|
||||
}
|
||||
|
||||
.card-body {
|
||||
@apply px-5 py-5;
|
||||
}
|
||||
|
||||
.card-footer {
|
||||
@apply pt-3 pb-3 border-t border-gray-700/60 mt-auto;
|
||||
}
|
||||
3
Moonlight.Client/Styles/additions/fonts.css
Normal file
3
Moonlight.Client/Styles/additions/fonts.css
Normal file
@@ -0,0 +1,3 @@
|
||||
@import url('https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&display=fallback');
|
||||
@import url('https://fonts.googleapis.com/css2?family=Source+Code+Pro:ital,wght@0,200..900;1,200..900&display=swap');
|
||||
@import url("https://cdn.jsdelivr.net/npm/bootstrap-icons@1.11.3/font/bootstrap-icons.min.css");
|
||||
77
Moonlight.Client/Styles/additions/forms.css
Normal file
77
Moonlight.Client/Styles/additions/forms.css
Normal file
@@ -0,0 +1,77 @@
|
||||
/* Forms */
|
||||
input[type="search"]::-webkit-search-decoration,
|
||||
input[type="search"]::-webkit-search-cancel-button,
|
||||
input[type="search"]::-webkit-search-results-button,
|
||||
input[type="search"]::-webkit-search-results-decoration {
|
||||
-webkit-appearance: none;
|
||||
}
|
||||
|
||||
.form-input,
|
||||
.form-textarea,
|
||||
.form-multiselect,
|
||||
.form-select,
|
||||
.form-checkbox,
|
||||
.form-radio {
|
||||
@apply bg-gray-700/60 border-2 focus:ring-0 focus:ring-offset-0 disabled:bg-gray-700/30 disabled:border-gray-700 disabled:hover:border-gray-700;
|
||||
}
|
||||
|
||||
.form-checkbox {
|
||||
@apply rounded;
|
||||
}
|
||||
|
||||
.form-input,
|
||||
.form-textarea,
|
||||
.form-multiselect,
|
||||
.form-select {
|
||||
@apply text-sm text-gray-100 leading-5 py-2 px-3 border-gray-700 focus:border-primary-500 shadow-sm rounded-lg;
|
||||
}
|
||||
|
||||
.form-input,
|
||||
.form-textarea {
|
||||
@apply placeholder-gray-700;
|
||||
}
|
||||
|
||||
.form-select {
|
||||
@apply pr-10;
|
||||
}
|
||||
|
||||
.form-checkbox,
|
||||
.form-radio {
|
||||
@apply text-primary-500 checked:bg-primary-500 checked:border-transparent border border-gray-700/60 focus:border-primary-500/50;
|
||||
}
|
||||
|
||||
/* Switch element */
|
||||
.form-switch {
|
||||
@apply relative select-none;
|
||||
width: 44px;
|
||||
}
|
||||
|
||||
.form-switch label {
|
||||
@apply block overflow-hidden cursor-pointer h-6 rounded-full;
|
||||
}
|
||||
|
||||
.form-switch label > span:first-child {
|
||||
@apply absolute block rounded-full;
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
top: 2px;
|
||||
left: 2px;
|
||||
right: 50%;
|
||||
transition: all .15s ease-out;
|
||||
}
|
||||
|
||||
.form-switch input[type="checkbox"]:checked + label {
|
||||
@apply bg-primary-600;
|
||||
}
|
||||
|
||||
.form-switch input[type="checkbox"]:checked + label > span:first-child {
|
||||
left: 22px;
|
||||
}
|
||||
|
||||
.form-switch input[type="checkbox"]:disabled + label {
|
||||
@apply cursor-not-allowed bg-gray-700/20 border border-gray-700/60;
|
||||
}
|
||||
|
||||
.form-switch input[type="checkbox"]:disabled + label > span:first-child {
|
||||
@apply bg-gray-600;
|
||||
}
|
||||
25
Moonlight.Client/Styles/additions/progress.css
Normal file
25
Moonlight.Client/Styles/additions/progress.css
Normal file
@@ -0,0 +1,25 @@
|
||||
.progress {
|
||||
@apply bg-gray-800 rounded-full overflow-hidden;
|
||||
}
|
||||
|
||||
.progress-bar {
|
||||
@apply bg-primary-500 rounded-full h-3;
|
||||
transition: width 0.6s ease;
|
||||
}
|
||||
|
||||
.progress-bar.progress-intermediate {
|
||||
animation: progress-animation 1s infinite linear;
|
||||
transform-origin: 0 50%
|
||||
}
|
||||
|
||||
@keyframes progress-animation {
|
||||
0% {
|
||||
transform: translateX(0) scaleX(0);
|
||||
}
|
||||
40% {
|
||||
transform: translateX(0) scaleX(0.4);
|
||||
}
|
||||
100% {
|
||||
transform: translateX(100%) scaleX(0.5);
|
||||
}
|
||||
}
|
||||
9
Moonlight.Client/Styles/additions/scrollbar.css
Normal file
9
Moonlight.Client/Styles/additions/scrollbar.css
Normal file
@@ -0,0 +1,9 @@
|
||||
* {
|
||||
scrollbar-width: thin;
|
||||
scrollbar-color: #64748b transparent;
|
||||
}
|
||||
|
||||
.no-scrollbar {
|
||||
scrollbar-width: none;
|
||||
scrollbar-color: transparent transparent;
|
||||
}
|
||||
1
Moonlight.Client/Styles/build.bat
Normal file
1
Moonlight.Client/Styles/build.bat
Normal file
@@ -0,0 +1 @@
|
||||
npx tailwindcss -i style.css -o ../wwwroot/css/style.min.css --watch
|
||||
2
Moonlight.Client/Styles/build.sh
Normal file
2
Moonlight.Client/Styles/build.sh
Normal file
@@ -0,0 +1,2 @@
|
||||
#! /bin/bash
|
||||
npx tailwindcss -i style.css -o ../wwwroot/css/style.min.css --watch
|
||||
403
Moonlight.Client/Styles/mappings/mooncore.json
Normal file
403
Moonlight.Client/Styles/mappings/mooncore.json
Normal file
@@ -0,0 +1,403 @@
|
||||
[
|
||||
"-m-1",
|
||||
"-m-1.5",
|
||||
"-m-2.5",
|
||||
"-m-3",
|
||||
"-mx-2",
|
||||
"-mx-4",
|
||||
"-translate-x-1/2",
|
||||
"-translate-x-full",
|
||||
"absolute",
|
||||
"animate-spin",
|
||||
"backdrop-blur",
|
||||
"bg-danger-200",
|
||||
"bg-danger-600",
|
||||
"bg-gradient-to-t",
|
||||
"bg-gray-100",
|
||||
"bg-gray-200",
|
||||
"bg-gray-400",
|
||||
"bg-gray-50",
|
||||
"bg-gray-700",
|
||||
"bg-gray-750",
|
||||
"bg-gray-800",
|
||||
"bg-gray-800/60",
|
||||
"bg-gray-800/80",
|
||||
"bg-gray-900",
|
||||
"bg-gray-950",
|
||||
"bg-info-100",
|
||||
"bg-opacity-50",
|
||||
"bg-opacity-75",
|
||||
"bg-red-50",
|
||||
"bg-slate-900",
|
||||
"bg-success-100",
|
||||
"bg-tertiary-500",
|
||||
"bg-transparent",
|
||||
"bg-warning-100",
|
||||
"bg-warning-400",
|
||||
"bg-white",
|
||||
"block",
|
||||
"border",
|
||||
"border-0",
|
||||
"border-b-2",
|
||||
"border-gray-100/10",
|
||||
"border-gray-700",
|
||||
"border-gray-700/60",
|
||||
"border-primary-500",
|
||||
"border-red-600",
|
||||
"border-slate-700",
|
||||
"border-t",
|
||||
"border-transparent",
|
||||
"bottom-0",
|
||||
"bottom-full",
|
||||
"col-span-1",
|
||||
"col-span-3",
|
||||
"cursor-default",
|
||||
"cursor-not-allowed",
|
||||
"dark:bg-gray-700",
|
||||
"dark:bg-gray-700/60",
|
||||
"dark:disabled:bg-gray-800",
|
||||
"dark:disabled:border-gray-700",
|
||||
"dark:disabled:placeholder:text-gray-600",
|
||||
"dark:disabled:text-gray-600",
|
||||
"dark:group-hover:text-gray-400",
|
||||
"dark:text-gray-100",
|
||||
"dark:text-gray-500",
|
||||
"disabled:bg-gray-100",
|
||||
"disabled:bg-gray-800",
|
||||
"disabled:border-gray-200",
|
||||
"disabled:border-gray-700",
|
||||
"disabled:cursor-not-allowed",
|
||||
"disabled:text-gray-400",
|
||||
"disabled:text-gray-600",
|
||||
"divide-gray-700/60",
|
||||
"divide-y",
|
||||
"duration-100",
|
||||
"duration-200",
|
||||
"duration-300",
|
||||
"duration-75",
|
||||
"ease-in",
|
||||
"ease-in-out",
|
||||
"ease-linear",
|
||||
"ease-out",
|
||||
"fill-current",
|
||||
"fill-primary-600",
|
||||
"filter",
|
||||
"first:pl-4",
|
||||
"fixed",
|
||||
"flex",
|
||||
"flex-1",
|
||||
"flex-col",
|
||||
"flex-nowrap",
|
||||
"flex-row",
|
||||
"flex-shrink-0",
|
||||
"flex-wrap",
|
||||
"focus:outline-none",
|
||||
"focus:ring-0",
|
||||
"focus:ring-2",
|
||||
"focus:ring-indigo-500",
|
||||
"focus:ring-indigo-600",
|
||||
"focus:ring-offset-0",
|
||||
"focus:ring-offset-2",
|
||||
"font-bold",
|
||||
"font-inter",
|
||||
"font-medium",
|
||||
"font-normal",
|
||||
"font-scp",
|
||||
"font-semibold",
|
||||
"form-checkbox",
|
||||
"form-input",
|
||||
"form-radio",
|
||||
"form-select",
|
||||
"from-gray-700",
|
||||
"from-primary-700",
|
||||
"gap-2",
|
||||
"gap-5",
|
||||
"gap-x-3",
|
||||
"gap-x-4",
|
||||
"gap-x-6",
|
||||
"gap-y-5",
|
||||
"gap-y-7",
|
||||
"gap-y-8",
|
||||
"grid",
|
||||
"grid-cols-1",
|
||||
"grid-cols-3",
|
||||
"grid-flow-col",
|
||||
"group-hover:text-gray-500",
|
||||
"group-hover:text-white",
|
||||
"grow",
|
||||
"h-10",
|
||||
"h-12",
|
||||
"h-16",
|
||||
"h-20",
|
||||
"h-4",
|
||||
"h-5",
|
||||
"h-6",
|
||||
"h-8",
|
||||
"h-px",
|
||||
"hidden",
|
||||
"hover:bg-gray-700",
|
||||
"hover:bg-gray-800",
|
||||
"hover:bg-primary-600",
|
||||
"hover:border-b-2",
|
||||
"hover:border-gray-600",
|
||||
"hover:border-primary-500",
|
||||
"hover:text-gray-100",
|
||||
"hover:text-gray-500",
|
||||
"hover:text-info-400",
|
||||
"hover:text-white",
|
||||
"inline",
|
||||
"inline-flex",
|
||||
"inset-0",
|
||||
"italic",
|
||||
"items-center",
|
||||
"items-end",
|
||||
"items-start",
|
||||
"justify-between",
|
||||
"justify-center",
|
||||
"justify-end",
|
||||
"justify-start",
|
||||
"justify-stretch",
|
||||
"last:mr-0",
|
||||
"last:pr-4",
|
||||
"leading-5",
|
||||
"leading-6",
|
||||
"leading-7",
|
||||
"left-1/2",
|
||||
"left-auto",
|
||||
"left-full",
|
||||
"lg:-mx-8",
|
||||
"lg:bg-gray-900/10",
|
||||
"lg:block",
|
||||
"lg:first:pl-8",
|
||||
"lg:fixed",
|
||||
"lg:flex",
|
||||
"lg:flex-col",
|
||||
"lg:gap-x-6",
|
||||
"lg:h-6",
|
||||
"lg:hidden",
|
||||
"lg:inset-y-0",
|
||||
"lg:items-center",
|
||||
"lg:last:pr-8",
|
||||
"lg:max-w-5xl",
|
||||
"lg:pl-72",
|
||||
"lg:px-8",
|
||||
"lg:w-72",
|
||||
"lg:w-px",
|
||||
"lg:z-50",
|
||||
"list-disc",
|
||||
"m-1",
|
||||
"m-3",
|
||||
"max-h-56",
|
||||
"max-w-sm",
|
||||
"max-w-xs",
|
||||
"mb-1",
|
||||
"mb-2",
|
||||
"mb-3",
|
||||
"mb-4",
|
||||
"mb-5",
|
||||
"mb-6",
|
||||
"mb-8",
|
||||
"md:flex-row",
|
||||
"md:grid-cols-3",
|
||||
"md:items-center",
|
||||
"md:space-y-0",
|
||||
"md:text-3xl",
|
||||
"me-1",
|
||||
"me-2",
|
||||
"min-h-full",
|
||||
"min-w-60",
|
||||
"ml-2",
|
||||
"ml-3",
|
||||
"ml-4",
|
||||
"ml-auto",
|
||||
"mr-16",
|
||||
"mr-2",
|
||||
"mr-3",
|
||||
"mr-6",
|
||||
"ms-0.5",
|
||||
"ms-1",
|
||||
"ms-3",
|
||||
"mt-1",
|
||||
"mt-10",
|
||||
"mt-2",
|
||||
"mt-2.5",
|
||||
"mt-3",
|
||||
"mt-5",
|
||||
"mt-8",
|
||||
"mt-auto",
|
||||
"mx-5",
|
||||
"mx-auto",
|
||||
"my-1",
|
||||
"my-3",
|
||||
"my-4",
|
||||
"my-8",
|
||||
"opacity-0",
|
||||
"opacity-100",
|
||||
"origin-top-right",
|
||||
"overflow-auto",
|
||||
"overflow-hidden",
|
||||
"overflow-x-auto",
|
||||
"overflow-x-scroll",
|
||||
"overflow-y-auto",
|
||||
"p-0",
|
||||
"p-1.5",
|
||||
"p-2",
|
||||
"p-2.5",
|
||||
"p-4",
|
||||
"p-5",
|
||||
"pb-1",
|
||||
"pb-3",
|
||||
"pb-4",
|
||||
"pl-12",
|
||||
"pl-2",
|
||||
"pl-3",
|
||||
"pl-5",
|
||||
"pl-9",
|
||||
"pointer-events-auto",
|
||||
"pointer-events-none",
|
||||
"pr-3",
|
||||
"pr-8",
|
||||
"pt-0.5",
|
||||
"pt-4",
|
||||
"pt-5",
|
||||
"pt-6",
|
||||
"px-2",
|
||||
"px-3",
|
||||
"px-4",
|
||||
"px-5",
|
||||
"px-6",
|
||||
"py-1",
|
||||
"py-10",
|
||||
"py-2",
|
||||
"py-3",
|
||||
"py-6",
|
||||
"py-8",
|
||||
"relative",
|
||||
"right-0",
|
||||
"right-auto",
|
||||
"ring-1",
|
||||
"ring-black",
|
||||
"ring-gray-900/5",
|
||||
"ring-opacity-5",
|
||||
"ring-white/10",
|
||||
"rounded",
|
||||
"rounded-b-lg",
|
||||
"rounded-full",
|
||||
"rounded-lg",
|
||||
"rounded-md",
|
||||
"rounded-t-lg",
|
||||
"scale-100",
|
||||
"scale-95",
|
||||
"select-none",
|
||||
"shadow-lg",
|
||||
"shadow-none",
|
||||
"shadow-sm",
|
||||
"shadow-xl",
|
||||
"shrink-0",
|
||||
"sm:-mx-6",
|
||||
"sm:auto-cols-max",
|
||||
"sm:col-span-1",
|
||||
"sm:col-span-2",
|
||||
"sm:col-span-3",
|
||||
"sm:col-span-4",
|
||||
"sm:col-span-5",
|
||||
"sm:col-span-6",
|
||||
"sm:first:pl-6",
|
||||
"sm:flex",
|
||||
"sm:gap-x-6",
|
||||
"sm:grid-cols-6",
|
||||
"sm:items-center",
|
||||
"sm:items-end",
|
||||
"sm:justify-between",
|
||||
"sm:justify-end",
|
||||
"sm:last:pr-6",
|
||||
"sm:max-w-lg",
|
||||
"sm:mb-0",
|
||||
"sm:mt-5",
|
||||
"sm:mt-6",
|
||||
"sm:my-8",
|
||||
"sm:p-0",
|
||||
"sm:p-6",
|
||||
"sm:pb-4",
|
||||
"sm:px-6",
|
||||
"sm:text-sm",
|
||||
"sm:w-full",
|
||||
"space-x-1",
|
||||
"space-x-2",
|
||||
"space-x-5",
|
||||
"space-y-1",
|
||||
"space-y-2",
|
||||
"space-y-3",
|
||||
"space-y-4",
|
||||
"space-y-8",
|
||||
"sr-only",
|
||||
"static",
|
||||
"sticky",
|
||||
"table",
|
||||
"table-auto",
|
||||
"text-2xl",
|
||||
"text-4xl",
|
||||
"text-[0.625rem]",
|
||||
"text-base",
|
||||
"text-center",
|
||||
"text-danger-500",
|
||||
"text-gray-100",
|
||||
"text-gray-200",
|
||||
"text-gray-300",
|
||||
"text-gray-400",
|
||||
"text-gray-500",
|
||||
"text-gray-600",
|
||||
"text-gray-700",
|
||||
"text-gray-800",
|
||||
"text-gray-900",
|
||||
"text-green-500",
|
||||
"text-indigo-600",
|
||||
"text-info-400",
|
||||
"text-info-500",
|
||||
"text-left",
|
||||
"text-lg",
|
||||
"text-primary-500",
|
||||
"text-red-400",
|
||||
"text-red-500",
|
||||
"text-red-700",
|
||||
"text-red-800",
|
||||
"text-slate-400",
|
||||
"text-slate-600",
|
||||
"text-sm",
|
||||
"text-success-400",
|
||||
"text-success-500",
|
||||
"text-warning-400",
|
||||
"text-white",
|
||||
"text-xs",
|
||||
"to-gray-800",
|
||||
"to-primary-600",
|
||||
"top-0",
|
||||
"transform",
|
||||
"transition",
|
||||
"transition-all",
|
||||
"transition-opacity",
|
||||
"translate-x-0",
|
||||
"translate-y-0",
|
||||
"translate-y-2",
|
||||
"truncate",
|
||||
"uppercase",
|
||||
"w-0",
|
||||
"w-10",
|
||||
"w-12",
|
||||
"w-16",
|
||||
"w-20",
|
||||
"w-24",
|
||||
"w-32",
|
||||
"w-4",
|
||||
"w-5",
|
||||
"w-6",
|
||||
"w-8",
|
||||
"w-auto",
|
||||
"w-full",
|
||||
"w-px",
|
||||
"w-screen",
|
||||
"whitespace-nowrap",
|
||||
"z-10",
|
||||
"z-40",
|
||||
"z-50"
|
||||
]
|
||||
1294
Moonlight.Client/Styles/package-lock.json
generated
Normal file
1294
Moonlight.Client/Styles/package-lock.json
generated
Normal file
File diff suppressed because it is too large
Load Diff
8
Moonlight.Client/Styles/package.json
Normal file
8
Moonlight.Client/Styles/package.json
Normal file
@@ -0,0 +1,8 @@
|
||||
{
|
||||
"devDependencies": {
|
||||
"tailwindcss": "^3.4.11"
|
||||
},
|
||||
"dependencies": {
|
||||
"@tailwindcss/forms": "^0.5.9"
|
||||
}
|
||||
}
|
||||
15
Moonlight.Client/Styles/style.css
Normal file
15
Moonlight.Client/Styles/style.css
Normal file
@@ -0,0 +1,15 @@
|
||||
@import "tailwindcss/base";
|
||||
@import "tailwindcss/components";
|
||||
|
||||
@import "additions/fonts.css";
|
||||
@import "additions/buttons.css";
|
||||
@import "additions/cards.css";
|
||||
@import "additions/forms.css";
|
||||
@import "additions/progress.css";
|
||||
@import "additions/scrollbar.css";
|
||||
|
||||
@import "tailwindcss/utilities";
|
||||
|
||||
#blazor-error-ui {
|
||||
display: none;
|
||||
}
|
||||
124
Moonlight.Client/Styles/tailwind.config.js
Normal file
124
Moonlight.Client/Styles/tailwind.config.js
Normal file
@@ -0,0 +1,124 @@
|
||||
/** @type {import('tailwindcss').Config} */
|
||||
module.exports = {
|
||||
content: [
|
||||
'../**/*.razor',
|
||||
'mappings/*.json'
|
||||
],
|
||||
theme: {
|
||||
extend: {
|
||||
fontFamily: {
|
||||
inter: ['Inter', 'sans-serif'],
|
||||
scp: ['Source Code Pro', 'mono'],
|
||||
},
|
||||
colors: {
|
||||
primary: {
|
||||
50: '#eef2ff',
|
||||
100: '#e0e7ff',
|
||||
200: '#c7d2fe',
|
||||
300: '#a5b4fc',
|
||||
400: '#818cf8',
|
||||
500: '#6366f1',
|
||||
600: '#4f46e5',
|
||||
700: '#4338ca',
|
||||
800: '#3730a3',
|
||||
900: '#312e81',
|
||||
950: '#1e1b4b'
|
||||
},
|
||||
secondary: {
|
||||
100: '#F9F9F9',
|
||||
200: '#F1F1F2',
|
||||
300: '#DBDFE9',
|
||||
400: '#B5B5C3',
|
||||
500: '#99A1B7',
|
||||
600: '#78829D',
|
||||
700: '#4B5675',
|
||||
800: '#252F4A',
|
||||
900: '#071437',
|
||||
950: '#030712',
|
||||
},
|
||||
tertiary: {
|
||||
50: '#f5f3ff',
|
||||
100: '#ede9fe',
|
||||
200: '#ddd6fe',
|
||||
300: '#c4b5fd',
|
||||
400: '#a78bfa',
|
||||
500: '#8b5cf6',
|
||||
600: '#7c3aed',
|
||||
700: '#6d28d9',
|
||||
800: '#5b21b6',
|
||||
900: '#4c1d95',
|
||||
950: '#2e1065'
|
||||
},
|
||||
warning: {
|
||||
50: '#fefce8',
|
||||
100: '#fef9c3',
|
||||
200: '#fef08a',
|
||||
300: '#fde047',
|
||||
400: '#facc15',
|
||||
500: '#eab308',
|
||||
600: '#ca8a04',
|
||||
700: '#a16207',
|
||||
800: '#854d0e',
|
||||
900: '#713f12',
|
||||
950: '#422006'
|
||||
},
|
||||
danger: {
|
||||
50: '#fef2f2',
|
||||
100: '#fee2e2',
|
||||
200: '#fecaca',
|
||||
300: '#fca5a5',
|
||||
400: '#f87171',
|
||||
500: '#ef4444',
|
||||
600: '#dc2626',
|
||||
700: '#b91c1c',
|
||||
800: '#991b1b',
|
||||
900: '#7f1d1d',
|
||||
950: '#450a0a'
|
||||
},
|
||||
success: {
|
||||
50: '#f0fdf4',
|
||||
100: '#dcfce7',
|
||||
200: '#bbf7d0',
|
||||
300: '#86efac',
|
||||
400: '#4ade80',
|
||||
500: '#22c55e',
|
||||
600: '#16a34a',
|
||||
700: '#15803d',
|
||||
800: '#166534',
|
||||
900: '#14532d',
|
||||
950: '#052e16'
|
||||
},
|
||||
info: {
|
||||
50: '#eff6ff',
|
||||
100: '#dbeafe',
|
||||
200: '#bfdbfe',
|
||||
300: '#93c5fd',
|
||||
400: '#60a5fa',
|
||||
500: '#3b82f6',
|
||||
600: '#2563eb',
|
||||
700: '#1d4ed8',
|
||||
800: '#1e40af',
|
||||
900: '#1e3a8a',
|
||||
950: '#172554'
|
||||
},
|
||||
gray: {
|
||||
100: '#F9F9F9',
|
||||
200: '#F1F1F2',
|
||||
300: '#DBDFE9',
|
||||
400: '#B5B5C3',
|
||||
500: '#99A1B7',
|
||||
600: '#78829D',
|
||||
700: '#4B5675',
|
||||
750: '#323c59',
|
||||
800: '#252F4A',
|
||||
900: '#071437',
|
||||
950: '#030b1f',
|
||||
}
|
||||
}
|
||||
},
|
||||
},
|
||||
plugins: [
|
||||
require('@tailwindcss/forms')
|
||||
],
|
||||
}
|
||||
|
||||
13
Moonlight.Client/UI/App.razor
Normal file
13
Moonlight.Client/UI/App.razor
Normal file
@@ -0,0 +1,13 @@
|
||||
@using Moonlight.Client.UI.Layouts
|
||||
|
||||
<Router AppAssembly="@typeof(App).Assembly">
|
||||
<Found Context="routeData">
|
||||
<RouteView RouteData="@routeData" DefaultLayout="@typeof(MainLayout)"/>
|
||||
</Found>
|
||||
<NotFound>
|
||||
<PageTitle>Not found</PageTitle>
|
||||
<LayoutView Layout="@typeof(MainLayout)">
|
||||
<p role="alert">Sorry, there's nothing at this address.</p>
|
||||
</LayoutView>
|
||||
</NotFound>
|
||||
</Router>
|
||||
35
Moonlight.Client/UI/Layouts/MainLayout.razor
Normal file
35
Moonlight.Client/UI/Layouts/MainLayout.razor
Normal file
@@ -0,0 +1,35 @@
|
||||
@using MoonCore.Helpers
|
||||
@using Moonlight.Client.UI.Partials
|
||||
|
||||
@inherits LayoutComponentBase
|
||||
|
||||
<div>
|
||||
<AppSidebar Layout="this" />
|
||||
|
||||
<div class="lg:pl-72">
|
||||
<AppHeader Layout="this"/>
|
||||
|
||||
<ErrorHandler>
|
||||
<main class="py-10">
|
||||
<div class="px-4 sm:px-6 lg:px-8">
|
||||
@Body
|
||||
</div>
|
||||
</main>
|
||||
</ErrorHandler>
|
||||
|
||||
<ToastLauncher />
|
||||
<ModalLauncher />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@code
|
||||
{
|
||||
public event Func<Task> OnStateChanged;
|
||||
public bool ShowMobileNavigation { get; private set; } = false;
|
||||
|
||||
public async Task ToggleMobileNavigation()
|
||||
{
|
||||
ShowMobileNavigation = !ShowMobileNavigation;
|
||||
await OnStateChanged();
|
||||
}
|
||||
}
|
||||
82
Moonlight.Client/UI/Partials/AppHeader.razor
Normal file
82
Moonlight.Client/UI/Partials/AppHeader.razor
Normal file
@@ -0,0 +1,82 @@
|
||||
@using Moonlight.Client.UI.Layouts
|
||||
|
||||
<div class="sticky top-0 z-40 flex h-16 shrink-0 items-center gap-x-4 bg-gray-800/60 backdrop-blur px-4 shadow-sm sm:gap-x-6 sm:px-6 lg:px-8">
|
||||
@if (Layout.ShowMobileNavigation)
|
||||
{
|
||||
<button @onclick="Layout.ToggleMobileNavigation" type="button" class="-m-2.5 p-2.5 text-gray-700 lg:hidden">
|
||||
<span class="sr-only">Close sidebar</span>
|
||||
<i class="bi bi-x-lg text-xl"></i>
|
||||
</button>
|
||||
}
|
||||
else
|
||||
{
|
||||
<button @onclick="Layout.ToggleMobileNavigation" type="button" class="-m-2.5 p-2.5 text-gray-700 lg:hidden">
|
||||
<span class="sr-only">Open sidebar</span>
|
||||
<i class="bi bi-list text-xl"></i>
|
||||
</button>
|
||||
}
|
||||
|
||||
<div class="h-6 w-px bg-gray-800 lg:hidden" aria-hidden="true"></div>
|
||||
|
||||
<div class="flex justify-between gap-x-4 lg:gap-x-6 w-full">
|
||||
<div></div>
|
||||
<div class="flex items-center gap-x-4 lg:gap-x-6">
|
||||
@*
|
||||
<button type="button" class="-m-2.5 p-2.5 text-gray-200 hover:text-gray-100">
|
||||
<span class="sr-only">View notifications</span>
|
||||
<svg class="h-6 w-6" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" aria-hidden="true">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" d="M14.857 17.082a23.848 23.848 0 005.454-1.31A8.967 8.967 0 0118 9.75v-.7V9A6 6 0 006 9v.75a8.967 8.967 0 01-2.312 6.022c1.733.64 3.56 1.085 5.455 1.31m5.714 0a24.255 24.255 0 01-5.714 0m5.714 0a3 3 0 11-5.714 0"/>
|
||||
</svg>
|
||||
</button>
|
||||
*@
|
||||
|
||||
<!-- Separator -->
|
||||
<div class="hidden lg:block lg:h-6 lg:w-px lg:bg-gray-900/10" aria-hidden="true"></div>
|
||||
|
||||
<!-- Profile dropdown -->
|
||||
<div class="relative">
|
||||
<button type="button" class="-m-1.5 flex items-center p-1.5" id="user-menu-button" aria-expanded="false" aria-haspopup="true">
|
||||
<span class="sr-only">Open user menu</span>
|
||||
<img class="h-8 w-8 rounded-full" src="https://masuowo.xyz/assets/images/avatar.png" alt="">
|
||||
<span class="hidden lg:flex lg:items-center">
|
||||
<span class="ml-4 text-sm font-semibold leading-6 text-gray-100" aria-hidden="true">
|
||||
@@masuowo
|
||||
</span>
|
||||
<svg class="ml-2 h-5 w-5 text-gray-400" viewBox="0 0 20 20" fill="currentColor" aria-hidden="true">
|
||||
<path fill-rule="evenodd" d="M5.23 7.21a.75.75 0 011.06.02L10 11.168l3.71-3.938a.75.75 0 111.08 1.04l-4.25 4.5a.75.75 0 01-1.08 0l-4.25-4.5a.75.75 0 01.02-1.06z" clip-rule="evenodd"/>
|
||||
</svg>
|
||||
</span>
|
||||
</button>
|
||||
|
||||
<!--
|
||||
Dropdown menu, show/hide based on menu state.
|
||||
|
||||
Entering: "transition ease-out duration-100"
|
||||
From: "transform opacity-0 scale-95"
|
||||
To: "transform opacity-100 scale-100"
|
||||
Leaving: "transition ease-in duration-75"
|
||||
From: "transform opacity-100 scale-100"
|
||||
To: "transform opacity-0 scale-95"
|
||||
-->
|
||||
<div class="hidden absolute right-0 z-10 mt-2.5 w-32 origin-top-right rounded-md bg-white py-2 shadow-lg ring-1 ring-gray-900/5 focus:outline-none" role="menu" aria-orientation="vertical" aria-labelledby="user-menu-button" tabindex="-1">
|
||||
<!-- Active: "bg-gray-50", Not Active: "" -->
|
||||
<a href="#" class="block px-3 py-1 text-sm leading-6 text-gray-900" role="menuitem" tabindex="-1" id="user-menu-item-0">Your profile</a>
|
||||
<a href="#" class="block px-3 py-1 text-sm leading-6 text-gray-900" role="menuitem" tabindex="-1" id="user-menu-item-1">Sign out</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@code
|
||||
{
|
||||
[Parameter] public MainLayout Layout { get; set; }
|
||||
|
||||
protected override async Task OnAfterRenderAsync(bool firstRender)
|
||||
{
|
||||
if(!firstRender)
|
||||
return;
|
||||
|
||||
Layout.OnStateChanged += () => InvokeAsync(StateHasChanged);
|
||||
}
|
||||
}
|
||||
194
Moonlight.Client/UI/Partials/AppSidebar.razor
Normal file
194
Moonlight.Client/UI/Partials/AppSidebar.razor
Normal file
@@ -0,0 +1,194 @@
|
||||
@using MoonCore.PluginFramework.Services
|
||||
@using Moonlight.Client.Interfaces
|
||||
@using Moonlight.Client.Models
|
||||
@using Moonlight.Client.UI.Layouts
|
||||
|
||||
@inject NavigationManager Navigation
|
||||
@inject ImplementationService ImplementationService
|
||||
|
||||
@{
|
||||
var url = new Uri(Navigation.Uri);
|
||||
}
|
||||
|
||||
<div class="relative z-40 lg:hidden transition-opacity @(Layout.ShowMobileNavigation ? "opacity-100" : "opacity-0")" role="dialog" aria-modal="true">
|
||||
<div class="fixed inset-0 bg-gray-800/80"></div>
|
||||
|
||||
<div class="fixed inset-0 flex justify-center bg-gray-900">
|
||||
<!--
|
||||
Off-canvas menu, show/hide based on off-canvas menu state.
|
||||
|
||||
Entering: "transition ease-in-out duration-300 transform"
|
||||
From: "-translate-x-full"
|
||||
To: "translate-x-0"
|
||||
Leaving: "transition ease-in-out duration-300 transform"
|
||||
From: "translate-x-0"
|
||||
To: "-translate-x-full"
|
||||
-->
|
||||
<div class="relative flex w-full max-w-xs flex-1">
|
||||
<!--
|
||||
Close button, show/hide based on off-canvas menu state.
|
||||
|
||||
Entering: "ease-in-out duration-300"
|
||||
From: "opacity-0"
|
||||
To: "opacity-100"
|
||||
Leaving: "ease-in-out duration-300"
|
||||
From: "opacity-100"
|
||||
To: "opacity-0"
|
||||
-->
|
||||
|
||||
<!-- Sidebar component, swap this element with another sidebar if you like -->
|
||||
<div class="flex grow flex-col gap-y-5 overflow-y-auto bg-gray-900 px-6 pb-4">
|
||||
<div class="flex h-16 shrink-0 items-center">
|
||||
<img class="h-8 w-auto" src="https://tailwindui.com/img/logos/mark.svg?color=indigo&shade=500" alt="Your Company">
|
||||
</div>
|
||||
<nav class="flex flex-1 flex-col">
|
||||
<ul role="list" class="flex flex-1 flex-col gap-y-7">
|
||||
@foreach (var group in Items)
|
||||
{
|
||||
<li>
|
||||
@if (!string.IsNullOrEmpty(group.Key))
|
||||
{
|
||||
<div class="text-xs font-semibold leading-6 text-gray-400">
|
||||
@group.Key
|
||||
</div>
|
||||
}
|
||||
|
||||
<ul role="list" class="-mx-2 space-y-1">
|
||||
@foreach (var item in group.Value)
|
||||
{
|
||||
var isMatch = item.RequiresExactMatch
|
||||
? url.LocalPath == item.Path
|
||||
: url.LocalPath.StartsWith(item.Path);
|
||||
|
||||
<li>
|
||||
@if (isMatch)
|
||||
{
|
||||
<a href="@item.Path" class="bg-gray-800 text-white group flex gap-x-3 rounded-md p-2 text-sm leading-6 items-center">
|
||||
<i class="ms-1 text-lg shrink-0 @item.Icon"></i>
|
||||
@item.Name
|
||||
</a>
|
||||
}
|
||||
else
|
||||
{
|
||||
<a href="@item.Path" class="text-gray-300 hover:text-white hover:bg-gray-800 group flex gap-x-3 rounded-md p-2 text-sm leading-6 items-center">
|
||||
<i class="ms-1 text-lg shrink-0 @item.Icon"></i>
|
||||
@item.Name
|
||||
</a>
|
||||
}
|
||||
</li>
|
||||
}
|
||||
</ul>
|
||||
</li>
|
||||
}
|
||||
|
||||
<li class="mt-auto">
|
||||
<a href="#" class="group -mx-2 flex gap-x-3 rounded-md p-2 text-sm font-semibold leading-6 text-gray-400 hover:bg-gray-800 hover:text-white">
|
||||
<svg class="h-6 w-6 shrink-0" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" aria-hidden="true">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" d="M9.594 3.94c.09-.542.56-.94 1.11-.94h2.593c.55 0 1.02.398 1.11.94l.213 1.281c.063.374.313.686.645.87.074.04.147.083.22.127.324.196.72.257 1.075.124l1.217-.456a1.125 1.125 0 011.37.49l1.296 2.247a1.125 1.125 0 01-.26 1.431l-1.003.827c-.293.24-.438.613-.431.992a6.759 6.759 0 010 .255c-.007.378.138.75.43.99l1.005.828c.424.35.534.954.26 1.43l-1.298 2.247a1.125 1.125 0 01-1.369.491l-1.217-.456c-.355-.133-.75-.072-1.076.124a6.57 6.57 0 01-.22.128c-.331.183-.581.495-.644.869l-.213 1.28c-.09.543-.56.941-1.11.941h-2.594c-.55 0-1.02-.398-1.11-.94l-.213-1.281c-.062-.374-.312-.686-.644-.87a6.52 6.52 0 01-.22-.127c-.325-.196-.72-.257-1.076-.124l-1.217.456a1.125 1.125 0 01-1.369-.49l-1.297-2.247a1.125 1.125 0 01.26-1.431l1.004-.827c.292-.24.437-.613.43-.992a6.932 6.932 0 010-.255c.007-.378-.138-.75-.43-.99l-1.004-.828a1.125 1.125 0 01-.26-1.43l1.297-2.247a1.125 1.125 0 011.37-.491l1.216.456c.356.133.751.072 1.076-.124.072-.044.146-.087.22-.128.332-.183.582-.495.644-.869l.214-1.281z"/>
|
||||
<path stroke-linecap="round" stroke-linejoin="round" d="M15 12a3 3 0 11-6 0 3 3 0 016 0z"/>
|
||||
</svg>
|
||||
Settings
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
</nav>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Static sidebar for desktop -->
|
||||
<div class="hidden lg:fixed lg:inset-y-0 lg:z-50 lg:flex lg:w-72 lg:flex-col">
|
||||
<!-- Sidebar component, swap this element with another sidebar if you like -->
|
||||
<div class="flex grow flex-col gap-y-5 overflow-y-auto bg-gray-800/60 px-6 pb-4">
|
||||
<div class="flex h-16 shrink-0 items-center">
|
||||
<img class="h-8 w-auto" src="https://gamecp.masuowo.xyz/api/core/asset/Core/svg/logo.svg" alt="Your Company">
|
||||
</div>
|
||||
<nav class="flex flex-1 flex-col">
|
||||
<ul role="list" class="flex flex-1 flex-col gap-y-7">
|
||||
@foreach (var group in Items)
|
||||
{
|
||||
<li>
|
||||
@if (!string.IsNullOrEmpty(group.Key))
|
||||
{
|
||||
<div class="text-xs font-semibold leading-6 text-gray-400">
|
||||
@group.Key
|
||||
</div>
|
||||
}
|
||||
|
||||
<ul role="list" class="-mx-2 space-y-1">
|
||||
@foreach (var item in group.Value)
|
||||
{
|
||||
var isMatch = item.RequiresExactMatch
|
||||
? url.LocalPath == item.Path
|
||||
: url.LocalPath.StartsWith(item.Path);
|
||||
|
||||
<li>
|
||||
@if (isMatch)
|
||||
{
|
||||
<a href="@item.Path" class="bg-gray-800 text-white group flex gap-x-3 rounded-md p-2 text-sm leading-6 items-center">
|
||||
<i class="ms-1 text-lg shrink-0 @item.Icon"></i>
|
||||
@item.Name
|
||||
</a>
|
||||
}
|
||||
else
|
||||
{
|
||||
<a href="@item.Path" class="text-gray-300 hover:text-white hover:bg-gray-800 group flex gap-x-3 rounded-md p-2 text-sm leading-6 items-center">
|
||||
<i class="ms-1 text-lg shrink-0 @item.Icon"></i>
|
||||
@item.Name
|
||||
</a>
|
||||
}
|
||||
</li>
|
||||
}
|
||||
</ul>
|
||||
</li>
|
||||
}
|
||||
|
||||
<li class="mt-auto">
|
||||
<a href="#" class="group -mx-2 flex gap-x-3 rounded-md p-2 text-sm font-semibold leading-6 text-gray-400 hover:bg-gray-800 hover:text-white">
|
||||
<svg class="h-6 w-6 shrink-0" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" aria-hidden="true">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" d="M9.594 3.94c.09-.542.56-.94 1.11-.94h2.593c.55 0 1.02.398 1.11.94l.213 1.281c.063.374.313.686.645.87.074.04.147.083.22.127.324.196.72.257 1.075.124l1.217-.456a1.125 1.125 0 011.37.49l1.296 2.247a1.125 1.125 0 01-.26 1.431l-1.003.827c-.293.24-.438.613-.431.992a6.759 6.759 0 010 .255c-.007.378.138.75.43.99l1.005.828c.424.35.534.954.26 1.43l-1.298 2.247a1.125 1.125 0 01-1.369.491l-1.217-.456c-.355-.133-.75-.072-1.076.124a6.57 6.57 0 01-.22.128c-.331.183-.581.495-.644.869l-.213 1.28c-.09.543-.56.941-1.11.941h-2.594c-.55 0-1.02-.398-1.11-.94l-.213-1.281c-.062-.374-.312-.686-.644-.87a6.52 6.52 0 01-.22-.127c-.325-.196-.72-.257-1.076-.124l-1.217.456a1.125 1.125 0 01-1.369-.49l-1.297-2.247a1.125 1.125 0 01.26-1.431l1.004-.827c.292-.24.437-.613.43-.992a6.932 6.932 0 010-.255c.007-.378-.138-.75-.43-.99l-1.004-.828a1.125 1.125 0 01-.26-1.43l1.297-2.247a1.125 1.125 0 011.37-.491l1.216.456c.356.133.751.072 1.076-.124.072-.044.146-.087.22-.128.332-.183.582-.495.644-.869l.214-1.281z"/>
|
||||
<path stroke-linecap="round" stroke-linejoin="round" d="M15 12a3 3 0 11-6 0 3 3 0 016 0z"/>
|
||||
</svg>
|
||||
Settings
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
</nav>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@code
|
||||
{
|
||||
[Parameter] public MainLayout Layout { get; set; }
|
||||
|
||||
private Dictionary<string, SidebarItem[]> Items = new();
|
||||
|
||||
protected override void OnInitialized()
|
||||
{
|
||||
Items = ImplementationService.Get<ISidebarItemProvider>()
|
||||
.SelectMany(x => x.Get())
|
||||
//.Where(x => x.Permission == null || (x.Permission != null && IdentityService.HasPermission(x.Permission)))
|
||||
.GroupBy(x => x.Group ?? "")
|
||||
.OrderByDescending(x => string.IsNullOrEmpty(x.Key))
|
||||
.ToDictionary(x => x.Key, x => x.OrderBy(y => y.Priority).ToArray());
|
||||
}
|
||||
|
||||
protected override Task OnAfterRenderAsync(bool firstRender)
|
||||
{
|
||||
if (!firstRender)
|
||||
return Task.CompletedTask;
|
||||
|
||||
Layout.OnStateChanged += () => InvokeAsync(StateHasChanged);
|
||||
|
||||
Navigation.LocationChanged += async (_, _) =>
|
||||
{
|
||||
if (!Layout.ShowMobileNavigation)
|
||||
return;
|
||||
|
||||
await Layout.ToggleMobileNavigation();
|
||||
};
|
||||
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
}
|
||||
3
Moonlight.Client/UI/Views/Admin/Index.razor
Normal file
3
Moonlight.Client/UI/Views/Admin/Index.razor
Normal file
@@ -0,0 +1,3 @@
|
||||
@page "/admin"
|
||||
|
||||
<h1>Elo testy</h1>
|
||||
5
Moonlight.Client/UI/Views/Index.razor
Normal file
5
Moonlight.Client/UI/Views/Index.razor
Normal file
@@ -0,0 +1,5 @@
|
||||
@page "/"
|
||||
|
||||
<h1>Hello, world!</h1>
|
||||
|
||||
Welcome to your new app.
|
||||
18
Moonlight.Client/_Imports.razor
Normal file
18
Moonlight.Client/_Imports.razor
Normal file
@@ -0,0 +1,18 @@
|
||||
@using System.Net.Http
|
||||
@using System.Net.Http.Json
|
||||
@using Microsoft.AspNetCore.Components.Forms
|
||||
@using Microsoft.AspNetCore.Components.Routing
|
||||
@using Microsoft.AspNetCore.Components.Web
|
||||
@using Microsoft.AspNetCore.Components.Web.Virtualization
|
||||
@using Microsoft.AspNetCore.Components.WebAssembly.Http
|
||||
@using Microsoft.JSInterop
|
||||
@using Moonlight.Client
|
||||
|
||||
@using MoonCore.Blazor.Tailwind.Components
|
||||
@using MoonCore.Blazor.Tailwind.Alerts
|
||||
@using MoonCore.Blazor.Tailwind.Crud
|
||||
@using MoonCore.Blazor.Tailwind.Forms
|
||||
@using MoonCore.Blazor.Tailwind.Helpers
|
||||
@using MoonCore.Blazor.Tailwind.Modals
|
||||
@using MoonCore.Blazor.Tailwind.Services
|
||||
@using MoonCore.Blazor.Tailwind.Toasts
|
||||
3222
Moonlight.Client/wwwroot/css/style.min.css
vendored
Normal file
3222
Moonlight.Client/wwwroot/css/style.min.css
vendored
Normal file
File diff suppressed because it is too large
Load Diff
BIN
Moonlight.Client/wwwroot/icon-192.png
Normal file
BIN
Moonlight.Client/wwwroot/icon-192.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 2.6 KiB |
BIN
Moonlight.Client/wwwroot/icon-512.png
Normal file
BIN
Moonlight.Client/wwwroot/icon-512.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 6.2 KiB |
32
Moonlight.Client/wwwroot/index.html
Normal file
32
Moonlight.Client/wwwroot/index.html
Normal file
@@ -0,0 +1,32 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<title>Moonlight.Client</title>
|
||||
<base href="/" />
|
||||
<link rel="stylesheet" href="/css/style.min.css" />
|
||||
<link href="manifest.webmanifest" rel="manifest" />
|
||||
<link rel="apple-touch-icon" sizes="512x512" href="icon-512.png" />
|
||||
<link rel="apple-touch-icon" sizes="192x192" href="icon-192.png" />
|
||||
</head>
|
||||
|
||||
<body class="bg-gray-950 text-white font-inter">
|
||||
<div id="app">
|
||||
|
||||
|
||||
|
||||
</div>
|
||||
|
||||
<div id="blazor-error-ui">
|
||||
An unhandled error has occurred.
|
||||
<a href="" class="reload">Reload</a>
|
||||
<a class="dismiss">🗙</a>
|
||||
</div>
|
||||
|
||||
<script src="/_framework/blazor.webassembly.js"></script>
|
||||
<script>navigator.serviceWorker.register('service-worker.js');</script>
|
||||
</body>
|
||||
|
||||
</html>
|
||||
22
Moonlight.Client/wwwroot/manifest.webmanifest
Normal file
22
Moonlight.Client/wwwroot/manifest.webmanifest
Normal file
@@ -0,0 +1,22 @@
|
||||
{
|
||||
"name": "Moonlight.Client",
|
||||
"short_name": "Moonlight.Client",
|
||||
"id": "./",
|
||||
"start_url": "./",
|
||||
"display": "standalone",
|
||||
"background_color": "#ffffff",
|
||||
"theme_color": "#03173d",
|
||||
"prefer_related_applications": false,
|
||||
"icons": [
|
||||
{
|
||||
"src": "icon-512.png",
|
||||
"type": "image/png",
|
||||
"sizes": "512x512"
|
||||
},
|
||||
{
|
||||
"src": "icon-192.png",
|
||||
"type": "image/png",
|
||||
"sizes": "192x192"
|
||||
}
|
||||
]
|
||||
}
|
||||
4
Moonlight.Client/wwwroot/service-worker.js
Normal file
4
Moonlight.Client/wwwroot/service-worker.js
Normal file
@@ -0,0 +1,4 @@
|
||||
// In development, always fetch from the network and do not enable offline support.
|
||||
// This is because caching would make development more difficult (changes would not
|
||||
// be reflected on the first load after each change).
|
||||
self.addEventListener('fetch', () => { });
|
||||
55
Moonlight.Client/wwwroot/service-worker.published.js
Normal file
55
Moonlight.Client/wwwroot/service-worker.published.js
Normal file
@@ -0,0 +1,55 @@
|
||||
// Caution! Be sure you understand the caveats before publishing an application with
|
||||
// offline support. See https://aka.ms/blazor-offline-considerations
|
||||
|
||||
self.importScripts('./service-worker-assets.js');
|
||||
self.addEventListener('install', event => event.waitUntil(onInstall(event)));
|
||||
self.addEventListener('activate', event => event.waitUntil(onActivate(event)));
|
||||
self.addEventListener('fetch', event => event.respondWith(onFetch(event)));
|
||||
|
||||
const cacheNamePrefix = 'offline-cache-';
|
||||
const cacheName = `${cacheNamePrefix}${self.assetsManifest.version}`;
|
||||
const offlineAssetsInclude = [ /\.dll$/, /\.pdb$/, /\.wasm/, /\.html/, /\.js$/, /\.json$/, /\.css$/, /\.woff$/, /\.png$/, /\.jpe?g$/, /\.gif$/, /\.ico$/, /\.blat$/, /\.dat$/ ];
|
||||
const offlineAssetsExclude = [ /^service-worker\.js$/ ];
|
||||
|
||||
// Replace with your base path if you are hosting on a subfolder. Ensure there is a trailing '/'.
|
||||
const base = "/";
|
||||
const baseUrl = new URL(base, self.origin);
|
||||
const manifestUrlList = self.assetsManifest.assets.map(asset => new URL(asset.url, baseUrl).href);
|
||||
|
||||
async function onInstall(event) {
|
||||
console.info('Service worker: Install');
|
||||
|
||||
// Fetch and cache all matching items from the assets manifest
|
||||
const assetsRequests = self.assetsManifest.assets
|
||||
.filter(asset => offlineAssetsInclude.some(pattern => pattern.test(asset.url)))
|
||||
.filter(asset => !offlineAssetsExclude.some(pattern => pattern.test(asset.url)))
|
||||
.map(asset => new Request(asset.url, { integrity: asset.hash, cache: 'no-cache' }));
|
||||
await caches.open(cacheName).then(cache => cache.addAll(assetsRequests));
|
||||
}
|
||||
|
||||
async function onActivate(event) {
|
||||
console.info('Service worker: Activate');
|
||||
|
||||
// Delete unused caches
|
||||
const cacheKeys = await caches.keys();
|
||||
await Promise.all(cacheKeys
|
||||
.filter(key => key.startsWith(cacheNamePrefix) && key !== cacheName)
|
||||
.map(key => caches.delete(key)));
|
||||
}
|
||||
|
||||
async function onFetch(event) {
|
||||
let cachedResponse = null;
|
||||
if (event.request.method === 'GET') {
|
||||
// For all navigation requests, try to serve index.html from cache,
|
||||
// unless that request is for an offline resource.
|
||||
// If you need some URLs to be server-rendered, edit the following check to exclude those URLs
|
||||
const shouldServeIndexHtml = event.request.mode === 'navigate'
|
||||
&& !manifestUrlList.some(url => url === event.request.url);
|
||||
|
||||
const request = shouldServeIndexHtml ? 'index.html' : event.request;
|
||||
const cache = await caches.open(cacheName);
|
||||
cachedResponse = await cache.match(request);
|
||||
}
|
||||
|
||||
return cachedResponse || fetch(event.request);
|
||||
}
|
||||
Reference in New Issue
Block a user