Redesign critical cell editor
This commit is contained in:
@@ -533,6 +533,10 @@ Acceptance criteria:
|
|||||||
|
|
||||||
### Phase 3: Redesign the cell editor
|
### Phase 3: Redesign the cell editor
|
||||||
|
|
||||||
|
Status:
|
||||||
|
|
||||||
|
- implemented in the web app on March 15, 2026
|
||||||
|
|
||||||
Scope:
|
Scope:
|
||||||
|
|
||||||
- add live result preview
|
- add live result preview
|
||||||
|
|||||||
@@ -1,22 +1,29 @@
|
|||||||
|
@using System
|
||||||
|
@using System.Collections.Generic
|
||||||
|
@using System.Linq
|
||||||
|
@using RolemasterDb.App.Domain
|
||||||
|
@using RolemasterDb.App.Features
|
||||||
|
|
||||||
<div class="critical-editor-backdrop" @onclick="HandleBackdropClicked">
|
<div class="critical-editor-backdrop" @onclick="HandleBackdropClicked">
|
||||||
<div class="critical-editor-dialog" @onclick:stopPropagation="true">
|
<div class="critical-editor-dialog" @onclick:stopPropagation="true">
|
||||||
<header class="critical-editor-header">
|
<header class="critical-editor-header">
|
||||||
<div>
|
<div>
|
||||||
<span class="eyebrow">Manual Curation</span>
|
|
||||||
@if (Model is not null)
|
@if (Model is not null)
|
||||||
{
|
{
|
||||||
<h3 class="panel-title">Edit @Model.TableName</h3>
|
<h3 class="panel-title">Edit Result Card</h3>
|
||||||
<p class="muted critical-editor-meta">
|
<p class="muted critical-editor-meta">
|
||||||
Roll band <strong>@Model.RollBand</strong>, column <strong>@Model.ColumnLabel</strong>
|
<strong>@Model.TableName</strong>
|
||||||
|
<span> · Roll band <strong>@Model.RollBand</strong></span>
|
||||||
|
<span> · Severity <strong>@Model.ColumnLabel</strong></span>
|
||||||
@if (!string.IsNullOrWhiteSpace(Model.GroupLabel))
|
@if (!string.IsNullOrWhiteSpace(Model.GroupLabel))
|
||||||
{
|
{
|
||||||
<span>, group <strong>@Model.GroupLabel</strong></span>
|
<span> · Variant <strong>@Model.GroupLabel</strong></span>
|
||||||
}
|
}
|
||||||
</p>
|
</p>
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
<h3 class="panel-title">Critical Cell Editor</h3>
|
<h3 class="panel-title">Edit Result Card</h3>
|
||||||
}
|
}
|
||||||
</div>
|
</div>
|
||||||
<button type="button" class="btn btn-link critical-editor-close" @onclick="OnClose">Close</button>
|
<button type="button" class="btn btn-link critical-editor-close" @onclick="OnClose">Close</button>
|
||||||
@@ -44,49 +51,80 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
<section class="critical-editor-section">
|
<section class="critical-editor-section">
|
||||||
<h4>Base Cell</h4>
|
<div class="critical-editor-section-header">
|
||||||
<div class="field-shell">
|
<div>
|
||||||
<label>Raw Cell Text</label>
|
<h4>Result Preview</h4>
|
||||||
<InputTextArea class="input-shell critical-editor-textarea tall" @bind-Value="Model.RawCellText" />
|
<p class="muted">This is the card the table browser will show.</p>
|
||||||
</div>
|
</div>
|
||||||
<div class="field-shell">
|
|
||||||
<label>Description / Prose</label>
|
|
||||||
<InputText class="input-shell" @bind-Value="Model.DescriptionText" />
|
|
||||||
</div>
|
</div>
|
||||||
<div class="field-shell">
|
<div class="result-card critical-editor-preview-card">
|
||||||
<label>Affix Text</label>
|
<div class="result-stats">
|
||||||
<InputText class="input-shell" @bind-Value="Model.RawAffixText" />
|
<span class="stat-pill">Severity: @Model.ColumnLabel</span>
|
||||||
</div>
|
<span class="stat-pill">Roll band: @Model.RollBand</span>
|
||||||
<div class="form-grid">
|
@if (!string.IsNullOrWhiteSpace(Model.GroupLabel))
|
||||||
<div class="field-shell">
|
{
|
||||||
<label>Parse Status</label>
|
<span class="stat-pill">Variant: @Model.GroupLabel</span>
|
||||||
<InputText class="input-shell" @bind-Value="Model.ParseStatus" />
|
}
|
||||||
</div>
|
|
||||||
<div class="field-shell">
|
|
||||||
<label>Parsed Json</label>
|
|
||||||
<InputTextArea class="input-shell critical-editor-textarea json" @bind-Value="Model.ParsedJson" />
|
|
||||||
</div>
|
</div>
|
||||||
|
<CompactCriticalCell
|
||||||
|
Description="@Model.DescriptionText"
|
||||||
|
Effects="@BuildPreviewEffects(Model.Effects)"
|
||||||
|
Branches="@BuildPreviewBranches(Model.Branches)" />
|
||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
<section class="critical-editor-section">
|
<section class="critical-editor-section">
|
||||||
<div class="critical-editor-section-header">
|
<div class="critical-editor-section-header">
|
||||||
|
<div>
|
||||||
|
<h4>Raw Text</h4>
|
||||||
|
<p class="muted">Update the source text, then adjust the visible card fields below.</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="field-shell">
|
||||||
|
<label>Raw Cell Text</label>
|
||||||
|
<InputTextArea class="input-shell critical-editor-textarea tall" @bind-Value="Model.RawCellText" />
|
||||||
|
</div>
|
||||||
|
<div class="field-shell">
|
||||||
|
<label>Result Text Override</label>
|
||||||
|
<InputTextArea class="input-shell critical-editor-textarea compact" @bind-Value="Model.DescriptionText" />
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<section class="critical-editor-section">
|
||||||
|
<div class="critical-editor-section-header">
|
||||||
|
<div>
|
||||||
<h4>Base Effects</h4>
|
<h4>Base Effects</h4>
|
||||||
|
<p class="muted">These chips appear on the main result.</p>
|
||||||
|
</div>
|
||||||
<button type="button" class="btn-ritual" @onclick="AddBaseEffect">Add Effect</button>
|
<button type="button" class="btn-ritual" @onclick="AddBaseEffect">Add Effect</button>
|
||||||
</div>
|
</div>
|
||||||
@if (Model.Effects.Count == 0)
|
@if (Model.Effects.Count == 0)
|
||||||
{
|
{
|
||||||
<p class="muted">No normalized base effects for this cell.</p>
|
<p class="muted">No base effects on this result yet.</p>
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
<div class="critical-editor-chip-list">
|
||||||
@for (var index = 0; index < Model.Effects.Count; index++)
|
@for (var index = 0; index < Model.Effects.Count; index++)
|
||||||
{
|
{
|
||||||
var effect = Model.Effects[index];
|
var effect = Model.Effects[index];
|
||||||
<div class="critical-editor-card">
|
var effectIndex = index;
|
||||||
|
<div class="critical-editor-chip-card">
|
||||||
|
<div class="critical-editor-chip-preview">
|
||||||
|
<AffixBadgeList Effects="@BuildSinglePreviewEffect(effect)" />
|
||||||
|
<span class="critical-editor-chip-name">@GetEffectLabel(effect)</span>
|
||||||
|
</div>
|
||||||
|
<button type="button" class="btn btn-link" @onclick="() => RemoveBaseEffect(effectIndex)">Remove</button>
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
@for (var index = 0; index < Model.Effects.Count; index++)
|
||||||
|
{
|
||||||
|
var effect = Model.Effects[index];
|
||||||
|
<div class="critical-editor-card nested">
|
||||||
<div class="critical-editor-card-header">
|
<div class="critical-editor-card-header">
|
||||||
<strong>Effect @(index + 1)</strong>
|
<strong>@GetEffectLabel(effect)</strong>
|
||||||
<button type="button" class="btn btn-link" @onclick="() => RemoveBaseEffect(index)">Remove</button>
|
|
||||||
</div>
|
</div>
|
||||||
@EffectFields(effect)
|
@EffectFields(effect)
|
||||||
</div>
|
</div>
|
||||||
@@ -96,12 +134,15 @@
|
|||||||
|
|
||||||
<section class="critical-editor-section">
|
<section class="critical-editor-section">
|
||||||
<div class="critical-editor-section-header">
|
<div class="critical-editor-section-header">
|
||||||
<h4>Branches</h4>
|
<div>
|
||||||
<button type="button" class="btn-ritual" @onclick="AddBranch">Add Branch</button>
|
<h4>Conditions</h4>
|
||||||
|
<p class="muted">Use condition cards for alternate outcomes.</p>
|
||||||
|
</div>
|
||||||
|
<button type="button" class="btn-ritual" @onclick="AddBranch">Add Condition</button>
|
||||||
</div>
|
</div>
|
||||||
@if (Model.Branches.Count == 0)
|
@if (Model.Branches.Count == 0)
|
||||||
{
|
{
|
||||||
<p class="muted">No branch records on this cell.</p>
|
<p class="muted">No alternate condition cards on this result yet.</p>
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@@ -110,68 +151,56 @@
|
|||||||
var branch = Model.Branches[index];
|
var branch = Model.Branches[index];
|
||||||
<div class="critical-editor-card branch-card-editor">
|
<div class="critical-editor-card branch-card-editor">
|
||||||
<div class="critical-editor-card-header">
|
<div class="critical-editor-card-header">
|
||||||
<strong>Branch @(index + 1)</strong>
|
<div>
|
||||||
|
<strong>@GetBranchTitle(branch, index)</strong>
|
||||||
|
<p class="muted critical-editor-inline-copy">Shown when this condition applies.</p>
|
||||||
|
</div>
|
||||||
<button type="button" class="btn btn-link" @onclick="() => RemoveBranch(index)">Remove</button>
|
<button type="button" class="btn btn-link" @onclick="() => RemoveBranch(index)">Remove</button>
|
||||||
</div>
|
</div>
|
||||||
<div class="form-grid">
|
|
||||||
<div class="field-shell">
|
<div class="field-shell">
|
||||||
<label>Branch Kind</label>
|
<label>Condition</label>
|
||||||
<InputText class="input-shell" @bind-Value="branch.BranchKind" />
|
|
||||||
</div>
|
|
||||||
<div class="field-shell">
|
|
||||||
<label>Condition Key</label>
|
|
||||||
<InputText class="input-shell" @bind-Value="branch.ConditionKey" />
|
|
||||||
</div>
|
|
||||||
<div class="field-shell">
|
|
||||||
<label>Sort Order</label>
|
|
||||||
<InputNumber TValue="int" class="input-shell" @bind-Value="branch.SortOrder" />
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="field-shell">
|
|
||||||
<label>Condition Text</label>
|
|
||||||
<InputText class="input-shell" @bind-Value="branch.ConditionText" />
|
<InputText class="input-shell" @bind-Value="branch.ConditionText" />
|
||||||
</div>
|
</div>
|
||||||
<div class="field-shell">
|
<div class="field-shell">
|
||||||
<label>Raw Text</label>
|
<label>Outcome Text</label>
|
||||||
<InputTextArea class="input-shell critical-editor-textarea" @bind-Value="branch.RawText" />
|
<InputTextArea class="input-shell critical-editor-textarea compact" @bind-Value="branch.DescriptionText" />
|
||||||
</div>
|
|
||||||
<div class="field-shell">
|
|
||||||
<label>Description / Prose</label>
|
|
||||||
<InputText class="input-shell" @bind-Value="branch.DescriptionText" />
|
|
||||||
</div>
|
|
||||||
<div class="field-shell">
|
|
||||||
<label>Affix Text</label>
|
|
||||||
<InputText class="input-shell" @bind-Value="branch.RawAffixText" />
|
|
||||||
</div>
|
|
||||||
<div class="form-grid">
|
|
||||||
<div class="field-shell">
|
|
||||||
<label>Condition Json</label>
|
|
||||||
<InputTextArea class="input-shell critical-editor-textarea compact" @bind-Value="branch.ConditionJson" />
|
|
||||||
</div>
|
|
||||||
<div class="field-shell">
|
|
||||||
<label>Parsed Json</label>
|
|
||||||
<InputTextArea class="input-shell critical-editor-textarea json" @bind-Value="branch.ParsedJson" />
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="critical-editor-subsection">
|
<div class="critical-editor-subsection">
|
||||||
<div class="critical-editor-section-header">
|
<div class="critical-editor-section-header">
|
||||||
<h5>Branch Effects</h5>
|
<div>
|
||||||
|
<h5>Condition Effects</h5>
|
||||||
|
<p class="muted">These chips only appear when the condition is met.</p>
|
||||||
|
</div>
|
||||||
<button type="button" class="btn-ritual" @onclick="() => AddBranchEffect(branch)">Add Effect</button>
|
<button type="button" class="btn-ritual" @onclick="() => AddBranchEffect(branch)">Add Effect</button>
|
||||||
</div>
|
</div>
|
||||||
@if (branch.Effects.Count == 0)
|
@if (branch.Effects.Count == 0)
|
||||||
{
|
{
|
||||||
<p class="muted">No normalized branch effects.</p>
|
<p class="muted">No effects on this condition card yet.</p>
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
<div class="critical-editor-chip-list">
|
||||||
|
@for (var effectIndex = 0; effectIndex < branch.Effects.Count; effectIndex++)
|
||||||
|
{
|
||||||
|
var effect = branch.Effects[effectIndex];
|
||||||
|
var localEffectIndex = effectIndex;
|
||||||
|
<div class="critical-editor-chip-card">
|
||||||
|
<div class="critical-editor-chip-preview">
|
||||||
|
<AffixBadgeList Effects="@BuildSinglePreviewEffect(effect)" />
|
||||||
|
<span class="critical-editor-chip-name">@GetEffectLabel(effect)</span>
|
||||||
|
</div>
|
||||||
|
<button type="button" class="btn btn-link" @onclick="() => RemoveBranchEffect(branch, localEffectIndex)">Remove</button>
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
</div>
|
||||||
|
|
||||||
@for (var effectIndex = 0; effectIndex < branch.Effects.Count; effectIndex++)
|
@for (var effectIndex = 0; effectIndex < branch.Effects.Count; effectIndex++)
|
||||||
{
|
{
|
||||||
var effect = branch.Effects[effectIndex];
|
var effect = branch.Effects[effectIndex];
|
||||||
<div class="critical-editor-card nested">
|
<div class="critical-editor-card nested">
|
||||||
<div class="critical-editor-card-header">
|
<div class="critical-editor-card-header">
|
||||||
<strong>Branch Effect @(effectIndex + 1)</strong>
|
<strong>@GetEffectLabel(effect)</strong>
|
||||||
<button type="button" class="btn btn-link" @onclick="() => RemoveBranchEffect(branch, effectIndex)">Remove</button>
|
|
||||||
</div>
|
</div>
|
||||||
@EffectFields(effect)
|
@EffectFields(effect)
|
||||||
</div>
|
</div>
|
||||||
@@ -229,7 +258,7 @@
|
|||||||
|
|
||||||
private void AddBaseEffect()
|
private void AddBaseEffect()
|
||||||
{
|
{
|
||||||
Model?.Effects.Add(new CriticalEffectEditorModel());
|
Model?.Effects.Add(CreateDefaultEffectModel());
|
||||||
}
|
}
|
||||||
|
|
||||||
private void RemoveBaseEffect(int index)
|
private void RemoveBaseEffect(int index)
|
||||||
@@ -251,6 +280,7 @@
|
|||||||
|
|
||||||
Model.Branches.Add(new CriticalBranchEditorModel
|
Model.Branches.Add(new CriticalBranchEditorModel
|
||||||
{
|
{
|
||||||
|
ConditionText = $"Condition {Model.Branches.Count + 1}",
|
||||||
SortOrder = Model.Branches.Count + 1
|
SortOrder = Model.Branches.Count + 1
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -267,7 +297,7 @@
|
|||||||
|
|
||||||
private static void AddBranchEffect(CriticalBranchEditorModel branch)
|
private static void AddBranchEffect(CriticalBranchEditorModel branch)
|
||||||
{
|
{
|
||||||
branch.Effects.Add(new CriticalEffectEditorModel());
|
branch.Effects.Add(CreateDefaultEffectModel());
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void RemoveBranchEffect(CriticalBranchEditorModel branch, int index)
|
private static void RemoveBranchEffect(CriticalBranchEditorModel branch, int index)
|
||||||
@@ -279,55 +309,167 @@
|
|||||||
|
|
||||||
branch.Effects.RemoveAt(index);
|
branch.Effects.RemoveAt(index);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static CriticalEffectEditorModel CreateDefaultEffectModel() =>
|
||||||
|
new()
|
||||||
|
{
|
||||||
|
EffectCode = CriticalEffectCodes.DirectHits,
|
||||||
|
SourceType = "symbol"
|
||||||
|
};
|
||||||
|
|
||||||
|
private static string GetBranchTitle(CriticalBranchEditorModel branch, int index) =>
|
||||||
|
string.IsNullOrWhiteSpace(branch.ConditionText)
|
||||||
|
? $"Condition {index + 1}"
|
||||||
|
: branch.ConditionText;
|
||||||
|
|
||||||
|
private static string GetEffectLabel(CriticalEffectEditorModel effect)
|
||||||
|
{
|
||||||
|
if (AffixDisplayMap.TryGet(effect.EffectCode, out var info))
|
||||||
|
{
|
||||||
|
return info.Label;
|
||||||
|
}
|
||||||
|
|
||||||
|
return string.IsNullOrWhiteSpace(effect.EffectCode) ? "Custom Effect" : effect.EffectCode;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static IEnumerable<KeyValuePair<string, string>> GetEffectOptions(string? currentCode)
|
||||||
|
{
|
||||||
|
if (!string.IsNullOrWhiteSpace(currentCode) && !AffixDisplayMap.Entries.ContainsKey(currentCode))
|
||||||
|
{
|
||||||
|
yield return new KeyValuePair<string, string>(currentCode, currentCode);
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach (var entry in AffixDisplayMap.Entries.OrderBy(item => item.Value.Label))
|
||||||
|
{
|
||||||
|
yield return new KeyValuePair<string, string>(entry.Key, entry.Value.Label);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void HandleEffectCodeChanged(CriticalEffectEditorModel effect, string? newCode)
|
||||||
|
{
|
||||||
|
var normalizedCode = newCode?.Trim() ?? string.Empty;
|
||||||
|
if (string.Equals(effect.EffectCode, normalizedCode, StringComparison.Ordinal))
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
effect.EffectCode = normalizedCode;
|
||||||
|
effect.Target = null;
|
||||||
|
effect.ValueInteger = null;
|
||||||
|
effect.ValueDecimal = null;
|
||||||
|
effect.ValueExpression = null;
|
||||||
|
effect.DurationRounds = null;
|
||||||
|
effect.PerRound = null;
|
||||||
|
effect.Modifier = null;
|
||||||
|
effect.BodyPart = null;
|
||||||
|
effect.IsPermanent = false;
|
||||||
|
effect.SourceText = null;
|
||||||
|
effect.SourceType = AffixDisplayMap.TryGet(effect.EffectCode, out _) ? "symbol" : "manual";
|
||||||
|
}
|
||||||
|
|
||||||
|
private static IReadOnlyList<CriticalEffectLookupResponse> BuildPreviewEffects(IEnumerable<CriticalEffectEditorModel> effects) =>
|
||||||
|
effects.Select(CreatePreviewEffect).ToList();
|
||||||
|
|
||||||
|
private static IReadOnlyList<CriticalEffectLookupResponse> BuildSinglePreviewEffect(CriticalEffectEditorModel effect) =>
|
||||||
|
[CreatePreviewEffect(effect)];
|
||||||
|
|
||||||
|
private static IReadOnlyList<CriticalBranchLookupResponse> BuildPreviewBranches(IEnumerable<CriticalBranchEditorModel> branches) =>
|
||||||
|
branches
|
||||||
|
.OrderBy(branch => branch.SortOrder)
|
||||||
|
.Select(branch => new CriticalBranchLookupResponse(
|
||||||
|
branch.BranchKind,
|
||||||
|
branch.ConditionKey,
|
||||||
|
branch.ConditionText,
|
||||||
|
branch.DescriptionText,
|
||||||
|
branch.RawAffixText,
|
||||||
|
BuildPreviewEffects(branch.Effects),
|
||||||
|
branch.RawText,
|
||||||
|
branch.SortOrder))
|
||||||
|
.ToList();
|
||||||
|
|
||||||
|
private static CriticalEffectLookupResponse CreatePreviewEffect(CriticalEffectEditorModel effect) =>
|
||||||
|
new(
|
||||||
|
effect.EffectCode,
|
||||||
|
effect.Target,
|
||||||
|
effect.ValueInteger,
|
||||||
|
effect.ValueExpression,
|
||||||
|
effect.DurationRounds,
|
||||||
|
effect.PerRound,
|
||||||
|
effect.Modifier,
|
||||||
|
effect.BodyPart,
|
||||||
|
effect.IsPermanent,
|
||||||
|
effect.SourceType,
|
||||||
|
effect.SourceText);
|
||||||
}
|
}
|
||||||
|
|
||||||
@functions {
|
@functions {
|
||||||
private RenderFragment EffectFields(CriticalEffectEditorModel effect) => @<div>
|
private RenderFragment EffectFields(CriticalEffectEditorModel effect) => @<div class="critical-editor-effect-fields">
|
||||||
<div class="form-grid">
|
<div class="form-grid critical-editor-effect-grid">
|
||||||
<div class="field-shell">
|
<div class="field-shell">
|
||||||
<label>Effect Code</label>
|
<label>Effect</label>
|
||||||
<InputText class="input-shell" @bind-Value="effect.EffectCode" />
|
<select class="input-shell" value="@effect.EffectCode" @onchange="args => HandleEffectCodeChanged(effect, args.Value?.ToString())">
|
||||||
|
@foreach (var option in GetEffectOptions(effect.EffectCode))
|
||||||
|
{
|
||||||
|
<option value="@option.Key">@option.Value</option>
|
||||||
|
}
|
||||||
|
</select>
|
||||||
</div>
|
</div>
|
||||||
|
@switch (effect.EffectCode)
|
||||||
|
{
|
||||||
|
case CriticalEffectCodes.DirectHits:
|
||||||
<div class="field-shell">
|
<div class="field-shell">
|
||||||
<label>Target</label>
|
<label>Hits</label>
|
||||||
<InputText class="input-shell" @bind-Value="effect.Target" />
|
|
||||||
</div>
|
|
||||||
<div class="field-shell">
|
|
||||||
<label>Source Type</label>
|
|
||||||
<InputText class="input-shell" @bind-Value="effect.SourceType" />
|
|
||||||
</div>
|
|
||||||
<div class="field-shell">
|
|
||||||
<label>Source Text</label>
|
|
||||||
<InputText class="input-shell" @bind-Value="effect.SourceText" />
|
|
||||||
</div>
|
|
||||||
<div class="field-shell">
|
|
||||||
<label>Value Integer</label>
|
|
||||||
<InputNumber TValue="int?" class="input-shell" @bind-Value="effect.ValueInteger" />
|
<InputNumber TValue="int?" class="input-shell" @bind-Value="effect.ValueInteger" />
|
||||||
</div>
|
</div>
|
||||||
|
break;
|
||||||
|
case CriticalEffectCodes.StunnedRounds:
|
||||||
|
case CriticalEffectCodes.MustParryRounds:
|
||||||
|
case CriticalEffectCodes.NoParryRounds:
|
||||||
<div class="field-shell">
|
<div class="field-shell">
|
||||||
<label>Value Decimal</label>
|
<label>Rounds</label>
|
||||||
<InputNumber TValue="decimal?" class="input-shell" @bind-Value="effect.ValueDecimal" step="0.01" />
|
|
||||||
</div>
|
|
||||||
<div class="field-shell">
|
|
||||||
<label>Value Expression</label>
|
|
||||||
<InputText class="input-shell" @bind-Value="effect.ValueExpression" />
|
|
||||||
</div>
|
|
||||||
<div class="field-shell">
|
|
||||||
<label>Duration Rounds</label>
|
|
||||||
<InputNumber TValue="int?" class="input-shell" @bind-Value="effect.DurationRounds" />
|
<InputNumber TValue="int?" class="input-shell" @bind-Value="effect.DurationRounds" />
|
||||||
</div>
|
</div>
|
||||||
|
break;
|
||||||
|
case CriticalEffectCodes.BleedPerRound:
|
||||||
<div class="field-shell">
|
<div class="field-shell">
|
||||||
<label>Per Round</label>
|
<label>Bleed / Round</label>
|
||||||
<InputNumber TValue="int?" class="input-shell" @bind-Value="effect.PerRound" />
|
<InputNumber TValue="int?" class="input-shell" @bind-Value="effect.PerRound" />
|
||||||
</div>
|
</div>
|
||||||
|
break;
|
||||||
|
case CriticalEffectCodes.FoePenalty:
|
||||||
|
case CriticalEffectCodes.AttackerBonusNextRound:
|
||||||
<div class="field-shell">
|
<div class="field-shell">
|
||||||
<label>Modifier</label>
|
<label>Modifier</label>
|
||||||
<InputNumber TValue="int?" class="input-shell" @bind-Value="effect.Modifier" />
|
<InputNumber TValue="int?" class="input-shell" @bind-Value="effect.Modifier" />
|
||||||
</div>
|
</div>
|
||||||
|
break;
|
||||||
|
case CriticalEffectCodes.PowerPointModifier:
|
||||||
|
<div class="field-shell">
|
||||||
|
<label>Expression</label>
|
||||||
|
<InputText class="input-shell" @bind-Value="effect.ValueExpression" />
|
||||||
|
</div>
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
<div class="field-shell">
|
||||||
|
<label>Display Text</label>
|
||||||
|
<InputText class="input-shell" @bind-Value="effect.SourceText" />
|
||||||
|
</div>
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
@if (!string.IsNullOrWhiteSpace(effect.BodyPart))
|
||||||
|
{
|
||||||
<div class="field-shell">
|
<div class="field-shell">
|
||||||
<label>Body Part</label>
|
<label>Body Part</label>
|
||||||
<InputText class="input-shell" @bind-Value="effect.BodyPart" />
|
<InputText class="input-shell" @bind-Value="effect.BodyPart" />
|
||||||
</div>
|
</div>
|
||||||
|
}
|
||||||
|
@if (!string.IsNullOrWhiteSpace(effect.Target))
|
||||||
|
{
|
||||||
|
<div class="field-shell">
|
||||||
|
<label>Target</label>
|
||||||
|
<InputText class="input-shell" @bind-Value="effect.Target" />
|
||||||
|
</div>
|
||||||
|
}
|
||||||
</div>
|
</div>
|
||||||
<label class="critical-editor-checkbox">
|
<label class="critical-editor-checkbox">
|
||||||
<InputCheckbox class="form-check-input" @bind-Value="effect.IsPermanent" />
|
<InputCheckbox class="form-check-input" @bind-Value="effect.IsPermanent" />
|
||||||
|
|||||||
@@ -728,6 +728,12 @@ textarea {
|
|||||||
margin: 0;
|
margin: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.critical-editor-section-header > div,
|
||||||
|
.critical-editor-card-header > div {
|
||||||
|
display: grid;
|
||||||
|
gap: 0.2rem;
|
||||||
|
}
|
||||||
|
|
||||||
.critical-editor-section-header,
|
.critical-editor-section-header,
|
||||||
.critical-editor-card-header {
|
.critical-editor-card-header {
|
||||||
display: flex;
|
display: flex;
|
||||||
@@ -749,6 +755,15 @@ textarea {
|
|||||||
background: rgba(252, 248, 238, 0.96);
|
background: rgba(252, 248, 238, 0.96);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.critical-editor-preview-card {
|
||||||
|
display: grid;
|
||||||
|
gap: 0.85rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.critical-editor-preview-card .critical-cell-description {
|
||||||
|
font-size: 1.05rem;
|
||||||
|
}
|
||||||
|
|
||||||
.critical-editor-textarea {
|
.critical-editor-textarea {
|
||||||
min-height: 7rem;
|
min-height: 7rem;
|
||||||
}
|
}
|
||||||
@@ -773,6 +788,52 @@ textarea {
|
|||||||
border: 1px solid rgba(141, 43, 30, 0.18);
|
border: 1px solid rgba(141, 43, 30, 0.18);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.critical-editor-inline-copy {
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.critical-editor-chip-list {
|
||||||
|
display: grid;
|
||||||
|
gap: 0.65rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.critical-editor-chip-card {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: space-between;
|
||||||
|
gap: 0.75rem;
|
||||||
|
padding: 0.65rem 0.8rem;
|
||||||
|
border-radius: 14px;
|
||||||
|
background: rgba(255, 255, 255, 0.7);
|
||||||
|
border: 1px solid rgba(127, 96, 55, 0.12);
|
||||||
|
}
|
||||||
|
|
||||||
|
.critical-editor-chip-preview {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 0.55rem;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
min-width: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.critical-editor-chip-preview .affix-badge-list {
|
||||||
|
margin-top: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.critical-editor-chip-name {
|
||||||
|
font-family: var(--font-heading);
|
||||||
|
color: #5b4327;
|
||||||
|
}
|
||||||
|
|
||||||
|
.critical-editor-effect-fields {
|
||||||
|
display: grid;
|
||||||
|
gap: 0.75rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.critical-editor-effect-grid {
|
||||||
|
grid-template-columns: repeat(auto-fit, minmax(180px, 1fr));
|
||||||
|
}
|
||||||
|
|
||||||
.critical-editor-checkbox {
|
.critical-editor-checkbox {
|
||||||
display: inline-flex;
|
display: inline-flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
@@ -810,4 +871,11 @@ textarea {
|
|||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
align-items: stretch;
|
align-items: stretch;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.critical-editor-chip-card,
|
||||||
|
.critical-editor-card-header,
|
||||||
|
.critical-editor-section-header {
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: stretch;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user