EffectiveConfig: migrate edit activation/commit to shared UiContext

- OptionStateBadge: add FieldKey param, call Ui.ActivateFieldAsync instead of OnActivateEdit
- EffectiveConfigOptionItem: add FieldKey, pass to badge; remove OnActivateEdit
- EffectiveConfigFieldDisplay: add FieldKey, commit only via Ui.CommitFieldAsync; remove ActiveFieldKey, OnActivateEdit, OnEditCommitted
- EffectiveConfigSectionPanel: pass FieldKey to FieldDisplay; remove OnActivateField, OnFieldEditCommitted
- EffectiveConfigPanels: remove OnActivateField, OnFieldEditCommitted params and relay
- EffectiveConfigSection: stop passing callback props to Panels (Session + UiContext drive behavior)
This commit is contained in:
2026-02-28 19:25:25 +10:00
parent 91beb9d181
commit bf1f6a0d82
6 changed files with 21 additions and 27 deletions
@@ -34,7 +34,7 @@
@fragment(Descriptor)
</CascadingValue>
}
<OptionStateBadge AnchorId="@_readonlyBadgeAnchorId" Source="@Descriptor.GetSource()" OptionName="@Descriptor.OptionName" Value="@effectiveValue" IsInteractive="@IsEditMode" ShowEditableBadge="@showEditableBadge" HasPendingChange="@_hasPendingChange" EditableAsButton="true" OnActivateEdit="@OnActivateEdit" />
<OptionStateBadge AnchorId="@_readonlyBadgeAnchorId" Source="@Descriptor.GetSource()" OptionName="@Descriptor.OptionName" Value="@effectiveValue" IsInteractive="@IsEditMode" ShowEditableBadge="@showEditableBadge" HasPendingChange="@_hasPendingChange" EditableAsButton="true" FieldKey="@FieldKey" />
</span>
</li>
}
@@ -54,14 +54,14 @@ else
{
<li class="ec-field" title="@(Descriptor.GetSource()?.GetReadOnlyTooltip())">
<EffectiveConfigOptionItem Label="@Descriptor.OptionName" DisplayValue="@(displayValue ?? "(not set)")" Source="@Descriptor.GetSource()" LabelTooltip="@_labelTooltip" OptionHelpKey="@_optionHelpKey" OptionHelpAnchorId="@_anchorId"
ReadonlyBadgeAnchorId="@_readonlyBadgeAnchorId" RawValue="@value" HasPendingChange="@_hasPendingChange" />
FieldKey="@FieldKey" ReadonlyBadgeAnchorId="@_readonlyBadgeAnchorId" RawValue="@value" HasPendingChange="@_hasPendingChange" />
</li>
}
else
{
<li class="ec-field @(isActiveEditor ? "ec-field-editing" : "")" title="@(Descriptor.GetSource()?.GetReadOnlyTooltip())">
<EffectiveConfigOptionItem Label="@Descriptor.OptionName" DisplayValue="@(displayValue ?? "(not set)")" Source="@Descriptor.GetSource()" LabelTooltip="@_labelTooltip" OptionHelpKey="@_optionHelpKey" OptionHelpAnchorId="@_anchorId"
ShowEditableBadge="true" OnActivateEdit="@OnActivateEdit" IsActive="@IsActive"
ShowEditableBadge="true" FieldKey="@FieldKey" IsActive="@IsActive"
ReadonlyBadgeAnchorId="@_readonlyBadgeAnchorId" RawValue="@value" HasPendingChange="@_hasPendingChange"
DisplayValueChanged="@HandleDefaultValueChanged" OnBlur="@HandleDefaultBlur" />
</li>
@@ -70,11 +70,9 @@ else
@code {
[Parameter] public EffectiveConfigFieldDescriptor Descriptor { get; set; } = null!;
[Parameter] public string FieldKey { get; set; } = "";
[Parameter] public bool IsEditMode { get; set; }
[Parameter] public bool IsActive { get; set; }
[Parameter] public string? ActiveFieldKey { get; set; }
[Parameter] public EventCallback OnActivateEdit { get; set; }
[Parameter] public EventCallback<EffectiveConfigEditCommittedArgs> OnEditCommitted { get; set; }
[CascadingParameter] public IEffectiveConfigEditSession? Session { get; set; }
private readonly string _anchorId = "ec-help-" + Guid.NewGuid().ToString("N")[..8];
@@ -127,8 +125,6 @@ else
var args = new EffectiveConfigEditCommittedArgs(Descriptor.SectionId, Descriptor.OptionName, _valueAtActivation, newValue, source?.FilePath);
if (Ui != null)
await Ui.CommitFieldAsync(args);
else if (OnEditCommitted.HasDelegate)
await OnEditCommitted.InvokeAsync(args);
}
_draftValue = null;
}
@@ -7,14 +7,14 @@
{
<span class="ec-field-value" @onclick:stopPropagation="true">
<input type="text" class="form-control form-control-sm" value="@(_editValue ?? DisplayValue)" @oninput="OnValueInput" @onfocusout="OnInputBlur" />
<OptionStateBadge AnchorId="@(ReadonlyBadgeAnchorId ?? "")" Source="@Source" OptionName="@Label" Value="@(RawValue ?? DisplayValue)" IsInteractive="@(DisplayMode == EffectiveConfigDisplayMode.Edit && !string.IsNullOrEmpty(ReadonlyBadgeAnchorId))" ShowEditableBadge="@ShowEditableBadge" HasPendingChange="@HasPendingChange" EditableAsButton="true" OnActivateEdit="@OnActivateEdit" />
<OptionStateBadge AnchorId="@(ReadonlyBadgeAnchorId ?? "")" Source="@Source" OptionName="@Label" Value="@(RawValue ?? DisplayValue)" IsInteractive="@(DisplayMode == EffectiveConfigDisplayMode.Edit && !string.IsNullOrEmpty(ReadonlyBadgeAnchorId))" ShowEditableBadge="@ShowEditableBadge" HasPendingChange="@HasPendingChange" EditableAsButton="true" FieldKey="@FieldKey" />
</span>
}
else
{
<span class="ec-field-value">
@DisplayValue
<OptionStateBadge AnchorId="@(ReadonlyBadgeAnchorId ?? "")" Source="@Source" OptionName="@Label" Value="@(RawValue ?? DisplayValue)" IsInteractive="@(DisplayMode == EffectiveConfigDisplayMode.Edit && !string.IsNullOrEmpty(ReadonlyBadgeAnchorId))" ShowEditableBadge="@ShowEditableBadge" HasPendingChange="@HasPendingChange" EditableAsButton="true" OnActivateEdit="@OnActivateEdit" />
<OptionStateBadge AnchorId="@(ReadonlyBadgeAnchorId ?? "")" Source="@Source" OptionName="@Label" Value="@(RawValue ?? DisplayValue)" IsInteractive="@(DisplayMode == EffectiveConfigDisplayMode.Edit && !string.IsNullOrEmpty(ReadonlyBadgeAnchorId))" ShowEditableBadge="@ShowEditableBadge" HasPendingChange="@HasPendingChange" EditableAsButton="true" FieldKey="@FieldKey" />
</span>
}
@@ -34,7 +34,8 @@ else
[Parameter] public bool ShowEditableBadge { get; set; }
/// <summary>When true, badge shows "pending" and warning style to indicate this field has an unsaved change.</summary>
[Parameter] public bool HasPendingChange { get; set; }
[Parameter] public EventCallback OnActivateEdit { get; set; }
/// <summary>Field key for activation via context when editable badge is clicked.</summary>
[Parameter] public string FieldKey { get; set; } = "";
/// <summary>When true, add ec-field-editing class and show input when DisplayMode is Edit.</summary>
[Parameter] public bool IsActive { get; set; }
/// <summary>When Edit and not readonly: raised when the user changes the value. Not yet wired to save.</summary>
@@ -23,8 +23,7 @@
var isOpen = hasSearch || IsSectionOpen(view.SectionId);
<EffectiveConfigSectionPanel SectionId="@view.SectionId" Title="@view.Title" Descriptors="@sectionDescriptors" IsOpen="@isOpen" OnToggle="@OnSectionToggle"
DisplayMode="@( IsEditMode ? EffectiveConfigDisplayMode.Edit : EffectiveConfigDisplayMode.View )"
IsEditMode="@IsEditMode" ActiveFieldKey="@ActiveFieldKey"
OnActivateField="@OnActivateField" OnFieldEditCommitted="@OnFieldEditCommitted" />
IsEditMode="@IsEditMode" ActiveFieldKey="@ActiveFieldKey" />
}
</div>
@@ -36,8 +35,6 @@
[Parameter] public EventCallback<string> OnSectionToggle { get; set; }
[Parameter] public bool IsEditMode { get; set; }
[Parameter] public string? ActiveFieldKey { get; set; }
[Parameter] public EventCallback<string> OnActivateField { get; set; }
[Parameter] public EventCallback<EffectiveConfigEditCommittedArgs> OnFieldEditCommitted { get; set; }
private bool IsSectionOpen(string id) => OpenSectionIds.Contains(id);
@@ -22,8 +22,7 @@
<CascadingValue Value="@Session">
<CascadingValue Value="@_uiContext">
<EffectiveConfigPanels Status="@Status" Context="@Context" SearchTerm="@_searchTerm" OpenSectionIds="@_openPanels"
OnSectionToggle="OnSectionToggle" IsEditMode="@Session.IsEditMode" ActiveFieldKey="@Session.ActiveFieldKey"
OnActivateField="ActivateField" OnFieldEditCommitted="HandleFieldEditCommitted" />
OnSectionToggle="OnSectionToggle" IsEditMode="@Session.IsEditMode" ActiveFieldKey="@Session.ActiveFieldKey" />
</CascadingValue>
</CascadingValue>
<EffectiveConfigSaveFlow ShowModal="@_showSaveModal" PendingChanges="@Session.PendingChanges" Status="@Status" Session="@Session"
@@ -14,12 +14,8 @@
@foreach (var f in Descriptors)
{
var fieldKey = $"{SectionId}:{f.OptionName}";
<EffectiveConfigFieldDisplay Descriptor="@f"
IsEditMode="@IsEditMode"
IsActive="@(ActiveFieldKey == fieldKey)"
ActiveFieldKey="@ActiveFieldKey"
OnActivateEdit="@(() => OnActivateField.InvokeAsync(fieldKey))"
OnEditCommitted="@OnFieldEditCommitted" />
<EffectiveConfigFieldDisplay Descriptor="@f" FieldKey="@fieldKey"
IsEditMode="@IsEditMode" IsActive="@(ActiveFieldKey == fieldKey)" />
}
</ul>
</div>
@@ -37,8 +33,6 @@
[Parameter] public EffectiveConfigDisplayMode DisplayMode { get; set; } = EffectiveConfigDisplayMode.View;
[Parameter] public bool IsEditMode { get; set; }
[Parameter] public string? ActiveFieldKey { get; set; }
[Parameter] public EventCallback<string> OnActivateField { get; set; }
[Parameter] public EventCallback<EffectiveConfigEditCommittedArgs> OnFieldEditCommitted { get; set; }
private async Task ToggleSection() => await OnToggle.InvokeAsync(SectionId);
}
@@ -22,7 +22,7 @@ else if (ShowEditableBadge)
{
<button type="button"
class="badge ec-badge-inline border-0 ms-1 @(HasPendingChange ? "bg-warning text-dark" : "bg-primary")"
@onclick="OnActivateEdit"
@onclick="OnEditableClick"
@onclick:stopPropagation="true"
title="@(HasPendingChange ? "Pending change" : "Edit this field")"
aria-label="Edit">
@@ -48,9 +48,16 @@ else if (ShowEditableBadge)
[Parameter] public bool HasPendingChange { get; set; }
/// <summary>When true, editable badge is a button; otherwise a static span.</summary>
[Parameter] public bool EditableAsButton { get; set; } = true;
[Parameter] public EventCallback OnActivateEdit { get; set; }
/// <summary>Field key for activation via context when editable badge is clicked.</summary>
[Parameter] public string? FieldKey { get; set; }
[CascadingParameter] public EffectiveConfigUiContext? Ui { get; set; }
private Task OnEditableClick()
{
if (Ui == null || string.IsNullOrEmpty(FieldKey)) return Task.CompletedTask;
return Ui.ActivateFieldAsync(FieldKey);
}
private Task OnReadonlyClick()
{
if (Ui == null || Source == null) return Task.CompletedTask;