refactor(central-ui): move script Trigger section into the tabbed panel

The Add/Edit Script modal's Trigger configuration (trigger editor + Min
time between runs) moves out of the always-visible header area and into
the tab strip as a new first tab: Trigger | Code | Parameters | Return
type. Trigger is the default selected tab.

Name and Locked remain above the tabs. The Trigger panel toggles via
display:none like the others, so the trigger expression's Monaco editor
stays mounted across tab switches. Markup-only — no logic change; verified
in the browser. CentralUI suite 316 green.
This commit is contained in:
Joseph Doherty
2026-05-18 16:51:58 -04:00
parent 01509a045f
commit e1a4ce4de8

View File

@@ -104,7 +104,7 @@
private string? _scriptReturn; private string? _scriptReturn;
private bool _scriptIsLocked; private bool _scriptIsLocked;
private string? _scriptFormError; private string? _scriptFormError;
private string _scriptModalTab = "code"; // "code" | "parameters" | "return" private string _scriptModalTab = "trigger"; // "trigger" | "code" | "parameters" | "return"
private MonacoEditor? _scriptEditor; private MonacoEditor? _scriptEditor;
private IReadOnlyList<ScadaLink.CentralUI.ScriptAnalysis.DiagnosticMarker> _scriptMarkers private IReadOnlyList<ScadaLink.CentralUI.ScriptAnalysis.DiagnosticMarker> _scriptMarkers
= Array.Empty<ScadaLink.CentralUI.ScriptAnalysis.DiagnosticMarker>(); = Array.Empty<ScadaLink.CentralUI.ScriptAnalysis.DiagnosticMarker>();
@@ -875,54 +875,6 @@
<label class="form-label">Name</label> <label class="form-label">Name</label>
<input type="text" class="form-control" @bind="_scriptName" readonly="@editingScript" /> <input type="text" class="form-control" @bind="_scriptName" readonly="@editingScript" />
</div> </div>
<div class="col-12">
<label class="form-label">Trigger</label>
<ScriptTriggerEditor TriggerType="@_scriptTriggerType"
TriggerConfig="@_scriptTriggerConfig"
Changed="@OnScriptTriggerChanged"
AvailableAttributes="@BuildAlarmAttributeChoices()" />
</div>
@if (ScriptTriggerConfigCodec.SupportsMinTimeBetweenRuns(_scriptTriggerType))
{
<div class="col-12">
<label class="form-label">Min time between runs</label>
<div class="row g-2" style="max-width: 420px;">
<div class="col-7">
<input type="number" min="1" step="1" class="form-control"
placeholder="(optional)"
@bind="_scriptMinTimeValue" @bind:event="oninput" />
</div>
<div class="col-5">
<select class="form-select" @bind="_scriptMinTimeUnit">
<option value="ms">milliseconds</option>
<option value="sec">seconds</option>
<option value="min">minutes</option>
</select>
</div>
</div>
@if (ScriptTriggerIsWhileTrue())
{
<div class="form-text">
This is the re-fire interval for the
<strong>WhileTrue</strong> trigger above.
</div>
@if (DurationInput.Compose(_scriptMinTimeValue, _scriptMinTimeUnit) is null)
{
<div class="alert alert-warning py-1 px-2 small mt-1 mb-0">
The WhileTrue trigger has no interval set — the script
will fire only once. Set a value here to make it re-fire.
</div>
}
}
else
{
<div class="form-text">
Optional throttle — skips trigger invocations that fire
sooner than this.
</div>
}
</div>
}
<div class="col-12"> <div class="col-12">
<div class="form-check"> <div class="form-check">
<input class="form-check-input" type="checkbox" @bind="_scriptIsLocked" id="scriptLocked" /> <input class="form-check-input" type="checkbox" @bind="_scriptIsLocked" id="scriptLocked" />
@@ -931,10 +883,17 @@
</div> </div>
</div> </div>
@* Tabs: Code, Parameters, Return. Both editor panels stay @* Tabs: Trigger, Code, Parameters, Return. All panels stay
mounted (toggled via display:none) so Monaco and the mounted (toggled via display:none) so Monaco editors and the
JSONJoy React island don't tear down on tab switch. *@ JSONJoy React island don't tear down on tab switch. *@
<ul class="nav nav-tabs" role="tablist"> <ul class="nav nav-tabs" role="tablist">
<li class="nav-item" role="presentation">
<button type="button"
class="nav-link @(_scriptModalTab == "trigger" ? "active" : "")"
role="tab"
aria-selected="@(_scriptModalTab == "trigger" ? "true" : "false")"
@onclick='() => _scriptModalTab = "trigger"'>Trigger</button>
</li>
<li class="nav-item" role="presentation"> <li class="nav-item" role="presentation">
<button type="button" <button type="button"
class="nav-link @(_scriptModalTab == "code" ? "active" : "")" class="nav-link @(_scriptModalTab == "code" ? "active" : "")"
@@ -958,6 +917,53 @@
</li> </li>
</ul> </ul>
<div class="border border-top-0 rounded-bottom p-3"> <div class="border border-top-0 rounded-bottom p-3">
<div style="display: @(_scriptModalTab == "trigger" ? "block" : "none")">
<ScriptTriggerEditor TriggerType="@_scriptTriggerType"
TriggerConfig="@_scriptTriggerConfig"
Changed="@OnScriptTriggerChanged"
AvailableAttributes="@BuildAlarmAttributeChoices()" />
@if (ScriptTriggerConfigCodec.SupportsMinTimeBetweenRuns(_scriptTriggerType))
{
<div class="mt-3">
<label class="form-label">Min time between runs</label>
<div class="row g-2" style="max-width: 420px;">
<div class="col-7">
<input type="number" min="1" step="1" class="form-control"
placeholder="(optional)"
@bind="_scriptMinTimeValue" @bind:event="oninput" />
</div>
<div class="col-5">
<select class="form-select" @bind="_scriptMinTimeUnit">
<option value="ms">milliseconds</option>
<option value="sec">seconds</option>
<option value="min">minutes</option>
</select>
</div>
</div>
@if (ScriptTriggerIsWhileTrue())
{
<div class="form-text">
This is the re-fire interval for the
<strong>WhileTrue</strong> trigger above.
</div>
@if (DurationInput.Compose(_scriptMinTimeValue, _scriptMinTimeUnit) is null)
{
<div class="alert alert-warning py-1 px-2 small mt-1 mb-0">
The WhileTrue trigger has no interval set — the script
will fire only once. Set a value here to make it re-fire.
</div>
}
}
else
{
<div class="form-text">
Optional throttle — skips trigger invocations that fire
sooner than this.
</div>
}
</div>
}
</div>
<div style="display: @(_scriptModalTab == "code" ? "block" : "none")"> <div style="display: @(_scriptModalTab == "code" ? "block" : "none")">
<MonacoEditor @ref="_scriptEditor" Value="@_scriptCode" ValueChanged="@(v => _scriptCode = v)" <MonacoEditor @ref="_scriptEditor" Value="@_scriptCode" ValueChanged="@(v => _scriptCode = v)"
Language="csharp" Height="360px" Language="csharp" Height="360px"
@@ -1530,7 +1536,7 @@
_scriptParameters = null; _scriptParameters = null;
_scriptReturn = null; _scriptReturn = null;
_scriptIsLocked = false; _scriptIsLocked = false;
_scriptModalTab = "code"; _scriptModalTab = "trigger";
ResetScriptTestRun(); ResetScriptTestRun();
} }
@@ -1547,7 +1553,7 @@
_scriptParameters = script.ParameterDefinitions; _scriptParameters = script.ParameterDefinitions;
_scriptReturn = script.ReturnDefinition; _scriptReturn = script.ReturnDefinition;
_scriptIsLocked = script.IsLocked; _scriptIsLocked = script.IsLocked;
_scriptModalTab = "code"; _scriptModalTab = "trigger";
ResetScriptTestRun(); ResetScriptTestRun();
} }