Added editor functionallity + propper enum rendering
This commit is contained in:
@@ -23,7 +23,8 @@ builder.Services.AddHopFrame(config => {
|
||||
table.SetDescription("The user dataset. It contains all information for the users of the application.");
|
||||
|
||||
table.Property(u => u.Password)
|
||||
.Listable(false);
|
||||
.Listable(false)
|
||||
.SetType(PropertyType.Password);
|
||||
|
||||
table.SetPreferredProperty(u => u.Username);
|
||||
});
|
||||
|
||||
@@ -46,8 +46,10 @@ internal static class ConfigurationHelper {
|
||||
Table = table
|
||||
};
|
||||
|
||||
if (property.CustomAttributes.Any(a => a.AttributeType == typeof(KeyAttribute)))
|
||||
if (property.CustomAttributes.Any(a => a.AttributeType == typeof(KeyAttribute))) {
|
||||
table.PreferredProperty = config.Identifier;
|
||||
config.Editable = false;
|
||||
}
|
||||
|
||||
return config;
|
||||
}
|
||||
|
||||
@@ -6,11 +6,18 @@ namespace HopFrame.Core.Services;
|
||||
public interface IEntityAccessor {
|
||||
|
||||
/// <summary>
|
||||
/// Returns the formatted content of the property, ready to be displayed
|
||||
/// Returns the formatted value of the property, ready to be displayed
|
||||
/// </summary>
|
||||
/// <param name="model">The model to pull the property from</param>
|
||||
/// <param name="property">The property that shall be extracted</param>
|
||||
public string? GetValue(object model, PropertyConfig property);
|
||||
|
||||
/// <summary>
|
||||
/// Returns the real value of the property, ready to be displayed
|
||||
/// </summary>
|
||||
/// <param name="model">The model to pull the property from</param>
|
||||
/// <param name="property">The property that shall be extracted</param>
|
||||
public object? GetValueRaw(object model, PropertyConfig property);
|
||||
|
||||
/// <summary>
|
||||
/// Formats the property to be displayed properly
|
||||
@@ -25,7 +32,7 @@ public interface IEntityAccessor {
|
||||
/// <param name="model">The model to save the property to</param>
|
||||
/// <param name="property">The property that shall be modified</param>
|
||||
/// <param name="value">The new value of the property</param>
|
||||
public void SetValue(object model, PropertyConfig property, object value);
|
||||
public void SetValue(object model, PropertyConfig property, object? value);
|
||||
|
||||
/// <summary>
|
||||
/// Sorts the provided dataset by the specified property
|
||||
|
||||
@@ -7,12 +7,19 @@ namespace HopFrame.Core.Services.Implementation;
|
||||
internal class EntityAccessor(IConfigAccessor accessor) : IEntityAccessor {
|
||||
|
||||
public string? GetValue(object model, PropertyConfig property) {
|
||||
var value = GetValueRaw(model, property);
|
||||
if (value is null)
|
||||
return null;
|
||||
|
||||
return FormatValue(value, property);
|
||||
}
|
||||
|
||||
public object? GetValueRaw(object model, PropertyConfig property) {
|
||||
var prop = model.GetType().GetProperty(property.Identifier);
|
||||
if (prop is null)
|
||||
return null;
|
||||
|
||||
var value = prop.GetValue(model);
|
||||
return FormatValue(value, property);
|
||||
return prop.GetValue(model);
|
||||
}
|
||||
|
||||
public string? FormatValue(object? value, PropertyConfig property) {
|
||||
@@ -34,12 +41,12 @@ internal class EntityAccessor(IConfigAccessor accessor) : IEntityAccessor {
|
||||
return value.ToString();
|
||||
}
|
||||
|
||||
public void SetValue(object model, PropertyConfig property, object value) {
|
||||
public void SetValue(object model, PropertyConfig property, object? value) {
|
||||
var prop = model.GetType().GetProperty(property.Identifier);
|
||||
if (prop is null)
|
||||
return;
|
||||
|
||||
if (value.GetType() != property.Type)
|
||||
if (value?.GetType() != property.Type)
|
||||
value = Convert.ChangeType(value, property.Type);
|
||||
|
||||
prop.SetValue(model, value);
|
||||
|
||||
@@ -18,13 +18,19 @@
|
||||
}
|
||||
</MudText>
|
||||
|
||||
<MudStack Spacing="5" Style="overflow-y: auto">
|
||||
<MudFocusTrap>
|
||||
@foreach (var property in GetProperties()) {
|
||||
<PropertyInput Config="property" Variant="Variant.Filled" />
|
||||
}
|
||||
</MudFocusTrap>
|
||||
</MudStack>
|
||||
@if (Entry is not null) {
|
||||
<MudStack Spacing="5" Style="overflow-y: auto">
|
||||
<MudFocusTrap>
|
||||
@foreach (var property in GetProperties()) {
|
||||
<PropertyInput
|
||||
Config="property"
|
||||
Variant="Variant.Filled"
|
||||
Value="@(GetPropertyValue(property))"
|
||||
ValueChanged="@(v => OnPropertyUpdated(property, v))"/>
|
||||
}
|
||||
</MudFocusTrap>
|
||||
</MudStack>
|
||||
}
|
||||
|
||||
<MudStack Row="true" Style="margin-top: auto">
|
||||
<MudButton Color="Color.Primary" OnClick="@(Submit)">Save</MudButton>
|
||||
|
||||
@@ -1,10 +1,11 @@
|
||||
using HopFrame.Core.Configuration;
|
||||
using HopFrame.Core.Services;
|
||||
using Microsoft.AspNetCore.Components;
|
||||
using MudBlazor;
|
||||
|
||||
namespace HopFrame.Web.Components.Components;
|
||||
|
||||
public partial class Editor(IDialogService dialogs) : ComponentBase {
|
||||
public partial class Editor(IDialogService dialogs, IEntityAccessor accessor) : ComponentBase {
|
||||
|
||||
private enum EditorMode {
|
||||
Editor,
|
||||
@@ -15,17 +16,20 @@ public partial class Editor(IDialogService dialogs) : ComponentBase {
|
||||
public required TableConfig Config { get; set; }
|
||||
|
||||
private bool IsVisible { get; set; }
|
||||
|
||||
|
||||
private object? Entry { get; set; }
|
||||
|
||||
private EditorMode Mode { get; set; }
|
||||
|
||||
private TaskCompletionSource<object?> Completion { get; set; } = null!;
|
||||
|
||||
private Dictionary<string, object?> UpdatedValues { get; set; } = null!;
|
||||
|
||||
public Task<object?> Present(object? entry) {
|
||||
Completion = new ();
|
||||
Mode = entry is null ? EditorMode.Creator : EditorMode.Editor;
|
||||
Entry = entry ?? Activator.CreateInstance(Config.TableType);
|
||||
Entry = entry ?? Activator.CreateInstance(Config.TableType)!;
|
||||
UpdatedValues = new();
|
||||
StateHasChanged();
|
||||
IsVisible = true;
|
||||
return Completion.Task;
|
||||
@@ -56,8 +60,19 @@ public partial class Editor(IDialogService dialogs) : ComponentBase {
|
||||
return query.OrderBy(p => p.OrderIndex);
|
||||
}
|
||||
|
||||
private object? GetPropertyValue(PropertyConfig property) {
|
||||
return accessor.GetValueRaw(Entry!, property);
|
||||
}
|
||||
|
||||
private void OnPropertyUpdated(PropertyConfig property, object? value) {
|
||||
UpdatedValues[property.Identifier] = value;
|
||||
}
|
||||
|
||||
private void ApplyChanges() {
|
||||
|
||||
foreach (var propUpdate in UpdatedValues) {
|
||||
var property = Config.Properties.First(p => p.Identifier == propUpdate.Key);
|
||||
accessor.SetValue(Entry!, property, propUpdate.Value);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,11 +1,14 @@
|
||||
@using HopFrame.Core.Configuration
|
||||
@using System.Collections
|
||||
@using HopFrame.Core.Configuration
|
||||
|
||||
@switch ((PropertyType)((byte)Config.PropertyType & 0x0F)) {
|
||||
case PropertyType.Numeric:
|
||||
<MudNumericField
|
||||
T="double"
|
||||
Value="@(Convert.ToDouble(Value))"
|
||||
ValueChanged="@(v => OnValueChanged(v))"
|
||||
Label="@Config.DisplayName"
|
||||
Required="@((Config.PropertyType & PropertyType.Nullable) != 0)"
|
||||
Required="@((Config.PropertyType & PropertyType.Nullable) == 0)"
|
||||
Disabled="@(!Config.Editable)"
|
||||
Variant="Variant"/>
|
||||
break;
|
||||
@@ -13,8 +16,9 @@
|
||||
case PropertyType.Boolean:
|
||||
<MudSwitch
|
||||
T="bool"
|
||||
Value="@((bool)(Value ?? false))"
|
||||
ValueChanged="@(v => OnValueChanged(v))"
|
||||
Label="@Config.DisplayName"
|
||||
Required="@((Config.PropertyType & PropertyType.Nullable) != 0)"
|
||||
Disabled="@(!Config.Editable)"/>
|
||||
break;
|
||||
|
||||
@@ -22,14 +26,18 @@
|
||||
<MudField Label="@Config.DisplayName" Variant="Variant.Outlined" Style="display: flex">
|
||||
<MudStack Row="true">
|
||||
<MudDatePicker
|
||||
Date="_date"
|
||||
DateChanged="@(v => OnValueChanged(v))"
|
||||
Label="Date"
|
||||
Required="@((Config.PropertyType & PropertyType.Nullable) != 0)"
|
||||
Required="@((Config.PropertyType & PropertyType.Nullable) == 0)"
|
||||
Disabled="@(!Config.Editable)"
|
||||
Variant="Variant"/>
|
||||
|
||||
<MudTimePicker
|
||||
Time="_time"
|
||||
TimeChanged="@(v => OnValueChanged(v))"
|
||||
Label="Time"
|
||||
Required="@((Config.PropertyType & PropertyType.Nullable) != 0)"
|
||||
Required="@((Config.PropertyType & PropertyType.Nullable) == 0)"
|
||||
Disabled="@(!Config.Editable)"
|
||||
Variant="Variant"/>
|
||||
</MudStack>
|
||||
@@ -38,16 +46,20 @@
|
||||
|
||||
case PropertyType.DateOnly:
|
||||
<MudDatePicker
|
||||
Date="_date"
|
||||
DateChanged="@(v => OnValueChanged(v))"
|
||||
Label="@Config.DisplayName"
|
||||
Required="@((Config.PropertyType & PropertyType.Nullable) != 0)"
|
||||
Required="@((Config.PropertyType & PropertyType.Nullable) == 0)"
|
||||
Disabled="@(!Config.Editable)"
|
||||
Variant="Variant"/>
|
||||
break;
|
||||
|
||||
case PropertyType.TimeOnly:
|
||||
<MudTimePicker
|
||||
Time="_time"
|
||||
TimeChanged="@(v => OnValueChanged(v))"
|
||||
Label="@Config.DisplayName"
|
||||
Required="@((Config.PropertyType & PropertyType.Nullable) != 0)"
|
||||
Required="@((Config.PropertyType & PropertyType.Nullable) == 0)"
|
||||
Disabled="@(!Config.Editable)"
|
||||
Variant="Variant"/>
|
||||
break;
|
||||
@@ -55,9 +67,11 @@
|
||||
case PropertyType.Email:
|
||||
<MudTextField
|
||||
T="string"
|
||||
Value="Value?.ToString()"
|
||||
ValueChanged="@(v => OnValueChanged(v))"
|
||||
InputType="InputType.Email"
|
||||
Label="@Config.DisplayName"
|
||||
Required="@((Config.PropertyType & PropertyType.Nullable) != 0)"
|
||||
Required="@((Config.PropertyType & PropertyType.Nullable) == 0)"
|
||||
Disabled="@(!Config.Editable)"
|
||||
Variant="Variant"/>
|
||||
break;
|
||||
@@ -65,28 +79,61 @@
|
||||
case PropertyType.Password:
|
||||
<MudTextField
|
||||
T="string"
|
||||
InputType="InputType.Password"
|
||||
Value="Value?.ToString()"
|
||||
ValueChanged="@(v => OnValueChanged(v))"
|
||||
InputType="@_passwordInputType"
|
||||
Label="@Config.DisplayName"
|
||||
Required="@((Config.PropertyType & PropertyType.Nullable) != 0)"
|
||||
Required="@((Config.PropertyType & PropertyType.Nullable) == 0)"
|
||||
Disabled="@(!Config.Editable)"
|
||||
Variant="Variant"/>
|
||||
Variant="Variant"
|
||||
Adornment="Adornment.End"
|
||||
AdornmentIcon="@_passwordIcon"
|
||||
OnAdornmentClick="@(OnPasswordIconClick)"/>
|
||||
break;
|
||||
|
||||
case PropertyType.PhoneNumber:
|
||||
<MudTextField
|
||||
T="string"
|
||||
Value="Value?.ToString()"
|
||||
ValueChanged="@(v => OnValueChanged(v))"
|
||||
InputType="InputType.Telephone"
|
||||
Label="@Config.DisplayName"
|
||||
Required="@((Config.PropertyType & PropertyType.Nullable) != 0)"
|
||||
Required="@((Config.PropertyType & PropertyType.Nullable) == 0)"
|
||||
Disabled="@(!Config.Editable)"
|
||||
Variant="Variant"/>
|
||||
break;
|
||||
|
||||
case PropertyType.Enum:
|
||||
<MudSelect
|
||||
T="object"
|
||||
Value="Value?.ToString()"
|
||||
ValueChanged="@(v => OnValueChanged(v))"
|
||||
MultiSelection="@((Config.PropertyType & PropertyType.List) != 0)"
|
||||
SelectedValuesChanged="@(v => OnValueChanged(v))"
|
||||
Label="@Config.DisplayName"
|
||||
Clearable="@((Config.PropertyType & (PropertyType.Nullable | PropertyType.List)) != 0)"
|
||||
Disabled="@(!Config.Editable)"
|
||||
Variant="Variant">
|
||||
@if ((Config.PropertyType & PropertyType.List) != 0) {
|
||||
@foreach (var value in Enum.GetValues(Config.Type.GenericTypeArguments[0])) {
|
||||
<MudSelectItem Value="value">@Enum.GetName(Config.Type.GenericTypeArguments[0], value)</MudSelectItem>
|
||||
}
|
||||
}
|
||||
else {
|
||||
@foreach (var value in Enum.GetValues(Config.Type)) {
|
||||
<MudSelectItem Value="value">@Enum.GetName(Config.Type, value)</MudSelectItem>
|
||||
}
|
||||
}
|
||||
</MudSelect>
|
||||
break;
|
||||
|
||||
default:
|
||||
<MudTextField
|
||||
T="string"
|
||||
Value="Value?.ToString()"
|
||||
ValueChanged="@(v => OnValueChanged(v))"
|
||||
Label="@Config.DisplayName"
|
||||
Required="@((Config.PropertyType & PropertyType.Nullable) != 0)"
|
||||
Required="@((Config.PropertyType & PropertyType.Nullable) == 0)"
|
||||
Disabled="@(!Config.Editable)"
|
||||
Variant="Variant"/>
|
||||
break;
|
||||
@@ -95,12 +142,90 @@
|
||||
@code {
|
||||
|
||||
[Parameter]
|
||||
public object? Value { get; set; }
|
||||
public object? Value {
|
||||
get;
|
||||
set {
|
||||
field = value;
|
||||
|
||||
if (value is null)
|
||||
return;
|
||||
|
||||
switch ((PropertyType)((byte)Config.PropertyType & 0x0F)) {
|
||||
case PropertyType.DateTime:
|
||||
_date = (DateTime)value;
|
||||
_time = TimeOnly.FromDateTime((DateTime)value).ToTimeSpan();
|
||||
break;
|
||||
|
||||
case PropertyType.DateOnly:
|
||||
_date = ((DateOnly)value).ToDateTime(TimeOnly.MinValue);
|
||||
break;
|
||||
|
||||
case PropertyType.TimeOnly:
|
||||
_time = ((TimeOnly)value).ToTimeSpan();
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
[Parameter]
|
||||
public required PropertyConfig Config { get; set; }
|
||||
|
||||
[Parameter]
|
||||
public Variant Variant { get; set; }
|
||||
|
||||
|
||||
[Parameter]
|
||||
public EventCallback<object?> ValueChanged { get; set; }
|
||||
|
||||
private DateTime _date;
|
||||
private TimeSpan _time;
|
||||
|
||||
private InputType _passwordInputType = InputType.Password;
|
||||
private string _passwordIcon = Icons.Material.Filled.VisibilityOff;
|
||||
|
||||
private async Task OnValueChanged(object? value) {
|
||||
if (value is DateTime dt) {
|
||||
_date = dt;
|
||||
switch ((PropertyType)((byte)Config.PropertyType & 0x0F)) {
|
||||
case PropertyType.DateOnly:
|
||||
value = DateOnly.FromDateTime(dt);
|
||||
break;
|
||||
|
||||
case PropertyType.DateTime:
|
||||
value = DateOnly.FromDateTime(dt).ToDateTime(TimeOnly.FromTimeSpan(_time));
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (value is TimeSpan ts) {
|
||||
_time = ts;
|
||||
switch ((PropertyType)((byte)Config.PropertyType & 0x0F)) {
|
||||
case PropertyType.DateOnly:
|
||||
value = TimeOnly.FromTimeSpan(ts);
|
||||
break;
|
||||
|
||||
case PropertyType.DateTime:
|
||||
value = DateOnly.FromDateTime(_date).ToDateTime(TimeOnly.FromTimeSpan(ts));
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if ((PropertyType)((byte)Config.PropertyType & 0x0F) == PropertyType.Enum && (Config.PropertyType & PropertyType.List) != 0) {
|
||||
var list = value as IReadOnlyCollection<object?>;
|
||||
var newValue = Activator.CreateInstance(Config.Type) as IList;
|
||||
foreach (var entry in list!) {
|
||||
newValue!.Add(entry);
|
||||
}
|
||||
|
||||
value = newValue;
|
||||
}
|
||||
|
||||
if (ValueChanged.HasDelegate)
|
||||
await ValueChanged.InvokeAsync(value);
|
||||
}
|
||||
|
||||
private void OnPasswordIconClick() {
|
||||
_passwordIcon = _passwordInputType == InputType.Password ? Icons.Material.Filled.VisibilityOff : Icons.Material.Filled.Visibility;
|
||||
_passwordInputType = _passwordInputType == InputType.Password ? InputType.Text : InputType.Password;
|
||||
}
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user