Skip to content

Commit

Permalink
Add Feature ConfirmationProtection as PropertyEdit extension "WithEdi…
Browse files Browse the repository at this point in the history
…tConfirmation"
  • Loading branch information
Florian Gilde committed Jan 1, 2025
1 parent 78998f5 commit 91d8a63
Show file tree
Hide file tree
Showing 25 changed files with 645 additions and 30 deletions.
2 changes: 1 addition & 1 deletion MudBlazor.Extensions/Components/MudExPromptDialog.razor
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
</TitleContent>
<DialogContent>
<MudText>@Message</MudText>
<MudExTextField AutoFocus="true" OnKeyUp="@OnKeyUp" Immediate="@Immediate" @bind-Value="@Value"></MudExTextField>
<MudExTextField AutoFocus="true" HelperText="@HelperText" OnKeyUp="@OnKeyUp" Immediate="@Immediate" @bind-Value="@Value"></MudExTextField>
</DialogContent>
<DialogActions>
<MudButton OnClick="Cancel">@TryLocalize(CancelText)</MudButton>
Expand Down
6 changes: 6 additions & 0 deletions MudBlazor.Extensions/Components/MudExPromptDialog.razor.cs
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,12 @@ public partial class MudExPromptDialog
[Parameter, SafeCategory("Behavior")]
public string Value { get; set; }

/// <summary>
/// The Helper text
/// </summary>
[Parameter, SafeCategory("Behavior")]
public string HelperText { get; set; }

/// <summary>
/// The message to be displayed in the component.
/// </summary>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -827,6 +827,18 @@ protected virtual async Task CreateMetaIfNotExists(bool? reconfigure = null)

UpdateConditions();
}

foreach (var item in MetaInformation.AllProperties)
{
item.UpdateRequired -= OnMetaUpdateRequired;
item.UpdateRequired += OnMetaUpdateRequired;
}

}

private void OnMetaUpdateRequired(ObjectEditPropertyMeta meta)
{
CallStateHasChanged();
}

private async Task OnResetClick(MouseEventArgs arg)
Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
using System.Linq.Expressions;
using System.Reflection;
using Microsoft.AspNetCore.Components;
using Microsoft.AspNetCore.Components.Web;
using Microsoft.Extensions.Localization;
using MudBlazor.Extensions.Components.ObjectEdit.Options;
using MudBlazor.Extensions.Helper;
using MudBlazor.Extensions.Helper.Internal;
using Nextended.Core.Extensions;

Expand Down Expand Up @@ -678,4 +680,29 @@ public static ObjectEditPropertyMeta OnRendered<TComponent>(this ObjectEditPrope
///</summary>
public static ObjectEditPropertyMeta WithAdditionalAttributes<TComponent>(this ObjectEditPropertyMeta meta, TComponent instanceForAttributes, bool overwriteExisting = false) where TComponent : new()
=> meta?.WithAdditionalAttributes(PropertyHelper.ValuesDictionary(instanceForAttributes, true), overwriteExisting);

/// <summary>
/// Protects the property from being edited by the user until a confirmation is received.
/// </summary>
public static ObjectEditPropertyMeta WithEditConfirmation(this ObjectEditPropertyMeta meta, string message,
AdditionalComponentRenderPosition position = AdditionalComponentRenderPosition.After) => meta.WithEditConfirmation(ConfirmationProtection.CheckBox(message), position);

/// <summary>
/// Protects the property from being edited by the user until a confirmation is received.
/// </summary>
public static ObjectEditPropertyMeta WithEditConfirmation(this ObjectEditPropertyMeta meta, IConfirmationProtection protection,
AdditionalComponentRenderPosition position = AdditionalComponentRenderPosition.After)
{
void ConfirmFunc(bool confirmed)
{
meta?.RenderData?.AddAttributes(true, new KeyValuePair<string, object>(nameof(MudBaseInput<string>.Disabled), !confirmed));
meta?.ForceUpdate();
}

meta?.RenderData.AddAttributes(true, new KeyValuePair<string, object>(nameof(MudBaseInput<string>.Disabled), true));
protection.ConfirmationCallback = ConfirmFunc;
meta?.RenderData.WithAdditionalComponent(protection.AdditionalRenderData, position);

return meta;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -551,4 +551,21 @@ public static ObjectEditPropertyMetaOf<TModel> WithAttributesIf<TModel>(this Obj
public static ObjectEditPropertyMetaOf<TModel> WithAttributesIf<TModel>(this ObjectEditPropertyMetaOf<TModel> meta, Func<TModel, bool> condition, Dictionary<string, object> attributes)
=> meta?.WithAttributesIf(condition, attributes.ToArray());

/// <summary>
/// Protects the property from being edited by the user until a confirmation is received.
/// </summary>
public static ObjectEditPropertyMetaOf<TModel> WithEditConfirmation<TModel>(this ObjectEditPropertyMetaOf<TModel> meta, string message,
AdditionalComponentRenderPosition position = AdditionalComponentRenderPosition.After) => meta.WithEditConfirmation(ConfirmationProtection.CheckBox(message), position);

/// <summary>
/// Protects the property from being edited by the user until a confirmation is received.
/// </summary>
public static ObjectEditPropertyMetaOf<TModel> WithEditConfirmation<TModel>(this ObjectEditPropertyMetaOf<TModel> meta, IConfirmationProtection protection,
AdditionalComponentRenderPosition position = AdditionalComponentRenderPosition.After)
{
var m = meta as ObjectEditPropertyMeta;
m.WithEditConfirmation(protection, position);
return meta;
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -590,4 +590,18 @@ public static IEnumerable<ObjectEditPropertyMeta> IgnoreOnExportAndImport<TModel
/// </summary>
public static IEnumerable<ObjectEditPropertyMeta> AsDisabledIf<TModel>(this IEnumerable<ObjectEditPropertyMeta> metas, Func<TModel, bool> condition)
=> metas.Apply(m => m.AsDisabledIf(condition));

/// <summary>
/// Protects the property from being edited by the user until a confirmation is received.
/// </summary>
public static IEnumerable<ObjectEditPropertyMeta> WithEditConfirmation(this IEnumerable<ObjectEditPropertyMeta> metas, string message,
AdditionalComponentRenderPosition position = AdditionalComponentRenderPosition.After) => metas.WithEditConfirmation(ConfirmationProtection.CheckBox(message), position);

/// <summary>
/// Protects the property from being edited by the user until a confirmation is received.
/// </summary>
public static IEnumerable<ObjectEditPropertyMeta> WithEditConfirmation(this IEnumerable<ObjectEditPropertyMeta> metas, IConfirmationProtection protection,
AdditionalComponentRenderPosition position = AdditionalComponentRenderPosition.After)
=> metas.Apply(m => m.WithEditConfirmation(protection, position));

}
Original file line number Diff line number Diff line change
Expand Up @@ -560,4 +560,18 @@ public static IEnumerable<ObjectEditPropertyMetaOf<TModel>> IgnoreOnExportAndImp
/// </summary>
public static IEnumerable<ObjectEditPropertyMetaOf<TModel>> AsDisabledIf<TModel>(this IEnumerable<ObjectEditPropertyMetaOf<TModel>> metas, Func<TModel, bool> condition)
=> metas.Apply(m => m.AsDisabledIf(condition));

/// <summary>
/// Protects the property from being edited by the user until a confirmation is received.
/// </summary>
public static IEnumerable<ObjectEditPropertyMetaOf<TModel>> WithEditConfirmation<TModel>(this IEnumerable<ObjectEditPropertyMetaOf<TModel>> metas, string message,
AdditionalComponentRenderPosition position = AdditionalComponentRenderPosition.After) => metas.WithEditConfirmation(ConfirmationProtection.CheckBox(message), position);

/// <summary>
/// Protects the property from being edited by the user until a confirmation is received.
/// </summary>
public static IEnumerable<ObjectEditPropertyMetaOf<TModel>> WithEditConfirmation<TModel>(this IEnumerable<ObjectEditPropertyMetaOf<TModel>> metas, IConfirmationProtection protection,
AdditionalComponentRenderPosition position = AdditionalComponentRenderPosition.After)
=> metas.Apply(m => m.WithEditConfirmation(protection, position));

}
Original file line number Diff line number Diff line change
Expand Up @@ -13,12 +13,8 @@ public static partial class MudExObjectEditExtensions
/// <param name="options">A set of actions to configure the wrapper component.</param>
/// <returns>The modified render data wrapped inside the specified component.</returns>
/// </summary>
public static IRenderData WrapIn<TWrapperComponent>(this IRenderData renderData, params Action<TWrapperComponent>[] options) where TWrapperComponent : new()
{
if (renderData != null)
renderData.Wrapper = RenderData.For(typeof(TWrapperComponent), PropertyHelper.ValuesDictionary(true, options));
return renderData?.Wrapper;
}
public static IRenderData WrapIn<TWrapperComponent>(this IRenderData renderData, params Action<TWrapperComponent>[] options) where TWrapperComponent : new()
=> renderData?.WrapIn(RenderData.For(typeof(TWrapperComponent), PropertyHelper.ValuesDictionary(true, options)));

/// <summary>
/// Wraps the current render data with another provided render data as its wrapper.
Expand All @@ -29,7 +25,9 @@ public static partial class MudExObjectEditExtensions
public static IRenderData WrapIn(this IRenderData renderData, IRenderData wrappingRenderData)
{
if (renderData != null)
{
renderData.Wrapper = wrappingRenderData;
}
return wrappingRenderData;
}

Expand Down Expand Up @@ -85,6 +83,27 @@ public static IRenderData WithAdditionalComponent(this IRenderData renderData, I
return additionalRenderData;
}

/// <summary>
/// Adds an additional component (either before or after) to the current render data.
/// <param name="renderData">The current render data to which the additional component is to be added.</param>
/// <param name="additionalRenderData">The additional render data to add.</param>
/// <param name="position">Enum indicating whether the additional component should be rendered after or before the current render data.</param>
/// <returns>The added render data component.</returns>
/// </summary>
public static IRenderData WithAdditionalComponent(this IRenderData renderData, IRenderData additionalRenderData, AdditionalComponentRenderPosition position)
=> renderData.WithAdditionalComponent(additionalRenderData, position == AdditionalComponentRenderPosition.After);

/// <summary>
/// Adds a component of type <typeparamref name="TComponent"/> at the specified position to the current render data, allowing for additional configuration through options.
/// <typeparam name="TComponent">The component type to add.</typeparam>
/// <param name="renderData">The current render data before which the new component is to be added.</param>
/// <param name="options">A set of actions to configure the added component.</param>
/// <returns>The newly added component's render data.</returns>
/// </summary>
public static IRenderData WithAdditionalComponent<TComponent>(this IRenderData renderData, AdditionalComponentRenderPosition position, params Action<TComponent>[] options) where TComponent : new()
=> renderData.WithAdditionalComponent(RenderData.For(typeof(TComponent), PropertyHelper.ValuesDictionary(true, options)), position);


/// <summary>
/// Wraps the current render data within a MudItem component, allowing for additional configuration through options.
/// <param name="renderData">The current render data to be wrapped.</param>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,14 +14,17 @@

private void Render(RenderTreeBuilder __builder)
{
@RenderWrapper(PropertyMeta?.RenderData?.Wrapper);
@RenderWrapper(PropertyMeta?.RenderData?.Wrapper)
;
}

private RenderFragment RenderWrapper(IRenderData wrapper, bool renderEditorAfter = true)
{
{
if (wrapper == null)
return renderEditorAfter ? RenderEditor() : null;
return @<DynamicComponent Type="@wrapper.ComponentType" Parameters="@wrapper.Attributes.AddOrUpdate("ChildContent", RenderWrapper(wrapper.Wrapper, renderEditorAfter))"></DynamicComponent>;
if (wrapper is not RenderData wrapperRenderData)
return @<DynamicComponent Type="@wrapper.ComponentType" Parameters="@wrapper.Attributes.AddOrUpdate("ChildContent", RenderWrapper(wrapper.Wrapper, renderEditorAfter))"></DynamicComponent>;
return @<DynamicComponent @ref="wrapperRenderData.ComponentReference" Type="@wrapper.ComponentType" Parameters="@wrapper.Attributes.AddOrUpdate("ChildContent", RenderWrapper(wrapper.Wrapper, renderEditorAfter))"></DynamicComponent>;
}

private RenderFragment RenderEditor()
Expand Down Expand Up @@ -86,7 +89,7 @@

if (PropertyMeta.Settings.ValidationComponent || PropertyMeta.RenderData?.RenderDataBeforeComponent?.Any() == true || PropertyMeta.RenderData?.RenderDataAfterComponent?.Any() == true)
{
return @<div class="@($"mud-ex-property-component-wrapper mud-ex-property-component-wrapper-for-{PropertyMeta.PropertyName.Replace(".", "-")}")">
return @<div style="@($"{PropertyMeta.Style}")" class="@($"mud-ex-property-component-wrapper mud-ex-property-component-wrapper-for-{PropertyMeta.PropertyName.Replace(".", "-")} {PropertyMeta.Class}")">
@foreach (var data in PropertyMeta.RenderData?.RenderDataBeforeComponent ?? Enumerable.Empty<IRenderData>())
{ @RenderWrapper(data, false)}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
namespace MudBlazor.Extensions.Components.ObjectEdit.Options;

/// <summary>
/// The position at which to render an additional component.
/// </summary>
public enum AdditionalComponentRenderPosition
{
/// <summary>
/// Render the additional component before the current component.
/// </summary>
Before,

/// <summary>
/// Render the additional component after the current component.
/// </summary>
After
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
using Microsoft.AspNetCore.Components;

namespace MudBlazor.Extensions.Components.ObjectEdit.Options;

/// <summary>
/// Base class for a boolean input protection instance, providing a set of properties and methods to configure the boolean input protection.
/// </summary>
public abstract class BooleanInputProtectionBase<TComponent> : IConfirmationProtection where TComponent : MudBooleanInput<bool>, new()
{
/// <summary>
/// Constructor for a boolean input protection instance.
/// </summary>
/// <param name="tooltip"></param>
/// <param name="configure"></param>
protected BooleanInputProtectionBase(string tooltip = null, Action<TComponent>? configure = null)
{
AdditionalRenderData = RenderData.For<TComponent>(cb =>
{
cb.ValueChanged = EventCallback.Factory.Create<bool>(new object(), b => ConfirmationCallback?.Invoke(b));
configure?.Invoke(cb);
});
if (!string.IsNullOrWhiteSpace(tooltip))
{
AdditionalRenderData.OnRendered<TComponent>(cb => cb.UserAttributes.Add("title", tooltip));
}
}
/// <inheritdoc />
public IRenderData AdditionalRenderData { get; set; }

/// <inheritdoc />
public Action<bool>? ConfirmationCallback { get; set; }
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
namespace MudBlazor.Extensions.Components.ObjectEdit.Options;

/// <inheritdoc />
public class CheckBoxConfirmationProtection: BooleanInputProtectionBase<MudCheckBox<bool>>
{
public CheckBoxConfirmationProtection(string tooltip = null, Action<MudCheckBox<bool>> configure = null) : base(tooltip, configure)
{}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
using MudBlazor.Extensions.Options;

namespace MudBlazor.Extensions.Components.ObjectEdit.Options;

/// <summary>
/// Provides a set of static factory options to protect a component with a confirmation.
/// </summary>
public static class ConfirmationProtection
{
public static SwitchConfirmationProtection Switch() => Switch(null, box => { box.UncheckedColor = Color.Default; box.Color = Color.Primary; });
public static SwitchConfirmationProtection Switch(string tooltip) => Switch(tooltip, box => { box.UncheckedColor = Color.Default; box.Color = Color.Primary; });
public static SwitchConfirmationProtection Switch(string tooltip, Action<MudSwitch<bool>> configure) => new(tooltip, configure);

public static CheckBoxConfirmationProtection CheckBox() => CheckBox(null, box => { box.UncheckedColor = Color.Default; box.Color = Color.Primary;});
public static CheckBoxConfirmationProtection CheckBox(string tooltip) => CheckBox(tooltip, box => { box.UncheckedColor = Color.Default; box.Color = Color.Primary;});
public static CheckBoxConfirmationProtection CheckBox(string tooltip, Action<MudCheckBox<bool>> configure) => new (tooltip, configure);

public static ToggleButtonConfirmationProtection ToggleButton() => ToggleButton(null, null);
public static ToggleButtonConfirmationProtection ToggleButton(string tooltip) => ToggleButton(tooltip, null);
public static ToggleButtonConfirmationProtection ToggleButton(string tooltip, Action<MudToggleIconButton> configure) => new(tooltip, configure);


public static DialogConfirmationProtection ConfirmDialog(IDialogService service) => ConfirmDialog(service, "Confirmation", "Press Unlock to confirm edit", null, null);
public static DialogConfirmationProtection ConfirmDialog(IDialogService service, string title, string message,
Action<MudToggleIconButton> configure = null,
Action<MudExMessageDialog> dialogConfigure = null)
=> new(service, title, message, configure, dialogConfigure);

public static DialogConfirmationProtection ConfirmDialog(IDialogService service, string title, string message,
DialogOptionsEx dialogOptions,
Action<MudToggleIconButton> configure = null,
Action<MudExMessageDialog> dialogConfigure = null)
=> new(service, title, message, configure, dialogConfigure) { DialogOptions = dialogOptions };

public static DialogConfirmationProtection ConfirmDialog(IDialogService service, string title, string message, string okText, string cancelText,
DialogOptionsEx dialogOptions,
Action<MudToggleIconButton> configure = null,
Action<MudExMessageDialog> dialogConfigure = null)
=> new(service, title, message, configure, dialogConfigure) { DialogOptions = dialogOptions, OkText = okText, CancelText = cancelText };



public static DialogConfirmationProtection PromptDialog(IDialogService service) => PromptDialog(service, "Confirmation", "confirm", "Please confirm to unlock edit", "Please type {0} to confirm", null, null);

public static DialogConfirmationProtection PromptDialog(IDialogService service, string title, string confirmationWord, string message, string helperText,
Action<MudToggleIconButton> configure = null,
Action<MudExPromptDialog> dialogConfigure = null)
=> new(service, title, confirmationWord, message, helperText, configure, dialogConfigure);

public static DialogConfirmationProtection PromptDialog(IDialogService service, string title, string confirmationWord, string message, string helperText,
DialogOptionsEx dialogOptions,
Action<MudToggleIconButton> configure = null,
Action<MudExPromptDialog> dialogConfigure = null)
=> new(service, title, confirmationWord, message, helperText, configure, dialogConfigure) { DialogOptions = dialogOptions };

public static DialogConfirmationProtection PromptDialog(IDialogService service, string title, string confirmationWord, string message, string helperText, string okText, string cancelText,
DialogOptionsEx dialogOptions,
Action<MudToggleIconButton> configure = null,
Action<MudExPromptDialog> dialogConfigure = null)
=> new(service, title, confirmationWord, message, helperText, configure, dialogConfigure) { DialogOptions = dialogOptions, OkText = okText, CancelText = cancelText };

}
Loading

0 comments on commit 91d8a63

Please sign in to comment.