Added theme loading. Improved theme editor. Updated app theme model

This commit is contained in:
2025-07-22 21:08:03 +02:00
parent a480ae9c50
commit 7dde1d86f8
8 changed files with 339 additions and 165 deletions

View File

@@ -224,9 +224,11 @@ grid
grid-cols-1
grid-cols-12
grid-cols-2
grid-cols-3
grid-flow-col
grow
grow-0
h-0
h-12
h-14
h-2
@@ -399,6 +401,7 @@ overlay-open:duration-50
overlay-open:opacity-100
p-0.5
p-1
p-1.5
p-2
p-2.5
p-3

View File

@@ -1,83 +0,0 @@
@using System.Drawing
@using MoonCore.Helpers
<div class="card card-body py-2 px-4 flex flex-row items-center justify-between">
<span>
@PrettyIdentifier
</span>
<div class="flex flex-row items-center gap-x-1">
@{
var currentValue = Value ?? DefaultValue;
var currentHex = ColorToHex(currentValue);
var elementId = $"colorSelect{GetHashCode()}";
}
<input id="@elementId" @onchange="OnColorChanged" type="color" hidden="" value="@(currentHex)"/>
<label for="@elementId" class="btn w-32 justify-between" style="background-color: @(currentHex)">
<i class="icon-palette mix-blend-exclusion"></i>
<span class="ms-2 mix-blend-exclusion">@(currentHex)</span>
</label>
<button @onclick="Reset" class="btn btn-error">
<i class="icon-rotate-ccw"></i>
</button>
</div>
</div>
@code
{
[Parameter] public string? Value { get; set; }
[Parameter] public string Identifier { get; set; }
[Parameter] public string DefaultValue { get; set; }
[Parameter] public Func<string, Task> OnChanged { get; set; }
private string PrettyIdentifier;
protected override void OnInitialized()
{
var parts = Identifier
.Split("-")
.Select(Formatter.CapitalizeFirstCharacter);
PrettyIdentifier = string.Join(" ", parts);
}
private async Task Save()
{
await OnChanged.Invoke(Value ?? DefaultValue);
}
private async Task Reset()
{
Value = DefaultValue;
await Save();
}
private async Task OnColorChanged(ChangeEventArgs eventArgs)
{
var strVal = eventArgs.Value?.ToString() ?? null;
if (strVal == null)
Value = DefaultValue;
else
Value = HexToColor(strVal);
await Save();
}
private string ColorToHex(string str)
{
var colorParts = str.Split(" ");
var r = int.Parse(colorParts[0]);
var g = int.Parse(colorParts[1]);
var b = int.Parse(colorParts[2]);
return ColorTranslator.ToHtml(Color.FromArgb(r, g, b));
}
private string HexToColor(string str)
{
var color = ColorTranslator.FromHtml(str);
return $"{color.R} {color.G} {color.B}";
}
}

View File

@@ -1,132 +1,322 @@
@using Moonlight.Shared.Misc
<div class="grid grid-cols-3 gap-3">
<div class="col-span-1 flex flex-col gap-3">
<div class="card card-body flex-row grow-0 items-center p-1.5 text-base-content">
<div class="grid grid-cols-1 lg:grid-cols-2 xl:grid-cols-3 gap-3">
<div class="col-span-1 flex flex-col gap-y-3 card card-body">
<div class="text-lg text-base-content mb-2.5 flex items-center">
<i class="icon-paint-bucket me-2"></i>
<span>Base Colors</span>
</div>
<div class="flex flex-row items-center">
<ColorSelector @bind-Value="Theme.ColorBackground"/>
<span class="ms-1">Background</span>
<span class="ms-2.5">Background</span>
</div>
<div class="card card-body flex-row grow-0 items-center p-1.5 text-base-content">
<div class="flex flex-row items-center">
<ColorSelector @bind-Value="Theme.ColorBaseContent"/>
<span class="ms-1">Base Content</span>
<span class="ms-2.5">Base Content</span>
</div>
<div class="card card-body flex-row grow-0 items-center p-1.5 text-base-content">
<div class="flex flex-row items-center">
<ColorSelector @bind-Value="Theme.ColorBase100"/>
<span class="ms-1">Base 100</span>
<span class="ms-2.5">Base 100</span>
</div>
<div class="card card-body flex-row grow-0 items-center p-1.5 text-base-content">
<div class="flex flex-row items-center">
<ColorSelector @bind-Value="Theme.ColorBase150"/>
<span class="ms-1">Base 150</span>
<span class="ms-2.5">Base 150</span>
</div>
<div class="card card-body flex-row grow-0 items-center p-1.5 text-base-content">
<div class="flex flex-row items-center">
<ColorSelector @bind-Value="Theme.ColorBase200"/>
<span class="ms-1">Base 200</span>
<span class="ms-2.5">Base 200</span>
</div>
<div class="card card-body flex-row grow-0 items-center p-1.5 text-base-content">
<div class="flex flex-row items-center">
<ColorSelector @bind-Value="Theme.ColorBase250"/>
<span class="ms-1">Base 250</span>
<span class="ms-2.5">Base 250</span>
</div>
<div class="card card-body flex-row grow-0 items-center p-1.5 text-base-content">
<div class="flex flex-row items-center">
<ColorSelector @bind-Value="Theme.ColorBase300"/>
<span class="ms-1">Base 300</span>
<span class="ms-2.5">Base 300</span>
</div>
</div>
<div class="col-span-2 grid grid-cols-2 gap-3">
<div class="col-span-1 flex flex-col gap-y-3">
<div class="card card-body flex-row grow-0 items-center p-1.5 text-base-content">
<div class="col-span-1 flex flex-col gap-y-3 card card-body">
<div class="text-lg text-base-content mb-2.5 flex items-center">
<i class="icon-paint-bucket me-2"></i>
<span>Colors</span>
</div>
<div class="grid grid-cols-2 gap-y-1.5">
<div class="col-span-1 flex flex-row items-center">
<ColorSelector @bind-Value="Theme.ColorPrimary"/>
<span class="ms-1">Primary</span>
<span class="ms-2.5">Primary</span>
</div>
<div class="card card-body flex-row grow-0 items-center p-1.5 text-base-content">
<div class="col-span-1 flex flex-row items-center">
<ColorSelector Icon="icon-type" @bind-Value="Theme.ColorPrimaryContent"/>
<span class="ms-1">Primary Content</span>
<span class="ms-2.5">Primary Content</span>
</div>
</div>
<div class="col-span-1 flex flex-col gap-y-3">
<div class="card card-body flex-row grow-0 items-center p-1.5 text-base-content">
<div class="col-span-1 flex flex-row items-center">
<ColorSelector @bind-Value="Theme.ColorSecondary"/>
<span class="ms-1">Secondary</span>
<span class="ms-2.5">Secondary</span>
</div>
<div class="card card-body flex-row grow-0 items-center p-1.5 text-base-content">
<div class="col-span-1 flex flex-row items-center">
<ColorSelector Icon="icon-type" @bind-Value="Theme.ColorSecondaryContent"/>
<span class="ms-1">Secondary Content</span>
<span class="ms-2.5">Secondary Content</span>
</div>
</div>
<div class="col-span-1 flex flex-col gap-y-3">
<div class="card card-body flex-row grow-0 items-center p-1.5 text-base-content">
<div class="col-span-1 flex flex-row items-center">
<ColorSelector @bind-Value="Theme.ColorAccent"/>
<span class="ms-1">Accent</span>
<span class="ms-2.5">Accent</span>
</div>
<div class="card card-body flex-row grow-0 items-center p-1.5 text-base-content">
<div class="col-span-1 flex flex-row items-center">
<ColorSelector Icon="icon-type" @bind-Value="Theme.ColorAccentContent"/>
<span class="ms-1">Accent Content</span>
<span class="ms-2.5">Accent Content</span>
</div>
</div>
<div class="col-span-1 flex flex-col gap-y-3">
<div class="card card-body flex-row grow-0 items-center p-1.5 text-base-content">
<div class="col-span-1 flex flex-row items-center">
<ColorSelector @bind-Value="Theme.ColorInfo"/>
<span class="ms-1">Info</span>
<span class="ms-2.5">Info</span>
</div>
<div class="card card-body flex-row grow-0 items-center p-1.5 text-base-content">
<div class="col-span-1 flex flex-row items-center">
<ColorSelector Icon="icon-type" @bind-Value="Theme.ColorInfoContent"/>
<span class="ms-1">Info Content</span>
<span class="ms-2.5">Info Content</span>
</div>
</div>
<div class="col-span-1 flex flex-col gap-y-3">
<div class="card card-body flex-row grow-0 items-center p-1.5 text-base-content">
<div class="col-span-1 flex flex-row items-center">
<ColorSelector @bind-Value="Theme.ColorSuccess"/>
<span class="ms-1">Success</span>
<span class="ms-2.5">Success</span>
</div>
<div class="card card-body flex-row grow-0 items-center p-1.5 text-base-content">
<div class="col-span-1 flex flex-row items-center">
<ColorSelector Icon="icon-type" @bind-Value="Theme.ColorSuccessContent"/>
<span class="ms-1">Success Content</span>
<span class="ms-2.5">Success Content</span>
</div>
</div>
<div class="col-span-1 flex flex-col gap-y-3">
<div class="card card-body flex-row grow-0 items-center p-1.5 text-base-content">
<div class="col-span-1 flex flex-row items-center">
<ColorSelector @bind-Value="Theme.ColorWarning"/>
<span class="ms-1">Warning</span>
<span class="ms-2.5">Warning</span>
</div>
<div class="card card-body flex-row grow-0 items-center p-1.5 text-base-content">
<div class="col-span-1 flex flex-row items-center">
<ColorSelector Icon="icon-type" @bind-Value="Theme.ColorWarningContent"/>
<span class="ms-1">Warning Content</span>
<span class="ms-2.5">Warning Content</span>
</div>
</div>
<div class="col-span-1 flex flex-col gap-y-3">
<div class="card card-body flex-row grow-0 items-center p-1.5 text-base-content">
<div class="col-span-1 flex flex-row items-center">
<ColorSelector @bind-Value="Theme.ColorError"/>
<span class="ms-1">Error</span>
<span class="ms-2.5">Error</span>
</div>
<div class="card card-body flex-row grow-0 items-center p-1.5 text-base-content">
<div class="col-span-1 flex flex-row items-center">
<ColorSelector Icon="icon-type" @bind-Value="Theme.ColorErrorContent"/>
<span class="ms-1">Error Content</span>
<span class="ms-2.5">Error Content</span>
</div>
</div>
</div>
<div class="col-span-1 flex flex-col gap-3 card card-body">
<div class="text-lg text-base-content mb-2.5 flex items-center">
<i class="icon-square-round-corner me-2"></i>
<span>Radius</span>
</div>
<div>
<div class="mb-1 text-base-content">Boxes</div>
<div class="flex w-full items-start gap-3 flex-wrap sm:flex-nowrap">
@foreach (var possibleValue in RadiusValues)
{
<label class="custom-option flex sm:w-1/2 flex-row items-start gap-3 p-2">
@if (Math.Abs(Theme.RadiusBox - possibleValue) == 0)
{
<input type="radio" name="radius-box" class="radio hidden" checked="checked"/>
}
else
{
<input @onclick="_ => UpdateRadiusBox(possibleValue)" type="radio" name="radius-box" class="radio hidden"/>
}
<span class="label-text w-full">
<div class="pe-1.5 pt-1.5" aria-hidden="true">
<div class="bg-base-200 h-6 w-8 border-e-2 border-t-2 border-base-content/60"
style="border-start-end-radius: @(possibleValue)rem">
</div>
</div>
</span>
</label>
}
</div>
</div>
<div>
<div class="mb-1 text-base-content">Fields</div>
<div class="flex w-full items-start gap-3 flex-wrap sm:flex-nowrap">
@foreach (var possibleValue in RadiusValues)
{
<label class="custom-option flex sm:w-1/2 flex-row items-start gap-3 p-2">
@if (Math.Abs(Theme.RadiusField - possibleValue) == 0)
{
<input type="radio" name="radius-field" class="radio hidden" checked="checked"/>
}
else
{
<input @onclick="_ => UpdateRadiusField(possibleValue)" type="radio" name="radius-field" class="radio hidden"/>
}
<span class="label-text w-full">
<div class="pe-1.5 pt-1.5" aria-hidden="true">
<div class="bg-base-200 h-6 w-8 border-e-2 border-t-2 border-base-content/60"
style="border-start-end-radius: @(possibleValue)rem">
</div>
</div>
</span>
</label>
}
</div>
</div>
<div>
<div class="mb-1 text-base-content">Selectors</div>
<div class="flex w-full items-start gap-3 flex-wrap sm:flex-nowrap">
@foreach (var possibleValue in RadiusValues)
{
<label class="custom-option flex sm:w-1/2 flex-row items-start gap-3 p-2">
@if (Math.Abs(Theme.RadiusSelector - possibleValue) == 0)
{
<input type="radio" name="radius-selector" class="radio hidden" checked="checked"/>
}
else
{
<input @onclick="_ => UpdateRadiusSelector(possibleValue)" type="radio" name="radius-selector" class="radio hidden"/>
}
<span class="label-text w-full">
<div class="pe-1.5 pt-1.5" aria-hidden="true">
<div class="bg-base-200 h-6 w-8 border-e-2 border-t-2 border-base-content/60"
style="border-start-end-radius: @(possibleValue)rem">
</div>
</div>
</span>
</label>
}
</div>
</div>
</div>
<div class="col-span-1 flex flex-col gap-3 card card-body">
<div class="text-lg text-base-content mb-2.5 flex items-center">
<i class="icon-star me-2"></i>
<span>Effects</span>
</div>
<div class="flex items-center gap-1">
<input @bind="InputDepth" type="checkbox" class="switch" id="depthEffect" />
<label class="label-text text-base" for="depthEffect">
Depth Effect
</label>
</div>
<div class="flex items-center gap-1">
<input @bind="InputNoise" type="checkbox" class="switch" id="noiseEffect" />
<label class="label-text text-base" for="noiseEffect">
Noise Effect
</label>
</div>
</div>
<div class="col-span-1 flex flex-col gap-3 card card-body">
<div class="text-lg text-base-content mb-2.5 flex items-center">
<i class="icon-ruler me-2"></i>
<span>Sizes</span>
</div>
<div>
<div class="mb-1 text-base-content">Fields</div>
<input @bind="Theme.SizeField" type="range" min="3" max="5" class="range" step="0.5" aria-label="range"/>
<div class="w-full flex justify-between text-xs px-2">
<span>3</span>
<span>3.5</span>
<span>4</span>
<span>4.5</span>
<span>5</span>
</div>
</div>
<div>
<div class="mb-1 text-base-content">Selector</div>
<input @bind="Theme.SizeSelector" type="range" min="3" max="5" class="range" step="0.5" aria-label="range"/>
<div class="w-full flex justify-between text-xs px-2">
<span>3</span>
<span>3.5</span>
<span>4</span>
<span>4.5</span>
<span>5</span>
</div>
</div>
<div>
<div class="mb-1 text-base-content">Border Width</div>
<input @bind="Theme.Border" type="range" min="0.5" max="2" class="range" step="0.5" aria-label="range"/>
<div class="w-full flex justify-between text-xs px-2">
<span>0.5</span>
<span>1</span>
<span>1.5</span>
<span>2</span>
</div>
</div>
</div>
</div>
@code
{
[Parameter] public ApplicationTheme Theme { get; set; }
private float[] RadiusValues =
[
0,
0.25f,
0.5f,
1f,
2f
];
private bool InputDepth
{
get => Theme.Depth != 0;
set => Theme.Depth = value ? 1 : 0;
}
private bool InputNoise
{
get => Theme.Noise != 0;
set => Theme.Noise = value ? 1 : 0;
}
private async Task UpdateRadiusBox(float value)
{
Theme.RadiusBox = value;
await InvokeAsync(StateHasChanged);
}
private async Task UpdateRadiusField(float value)
{
Theme.RadiusField = value;
await InvokeAsync(StateHasChanged);
}
private async Task UpdateRadiusSelector(float value)
{
Theme.RadiusSelector = value;
await InvokeAsync(StateHasChanged);
}
}

View File

@@ -109,8 +109,8 @@
SizeField = 0.25f,
Border = 1f,
Depth = 0f,
Noise = 0f
Depth = 0,
Noise = 0
};
}
}