feat(ui/design): Monaco editor for script code fields
Vendors Monaco 0.55.1 min/vs/ (~15 MB) at wwwroot/lib/monaco/vs/. No CDN dependency; works on air-gapped deployments. Loaded lazily on first script-edit via the AMD loader. wwwroot/js/monaco-init.js exposes window.MonacoBlazor with createEditor / setValue / getValue / setMarkers / dispose. Handles loader bootstrap, DotNet round-trip on content change, and marker sets for later diagnostic wiring. Components/Shared/MonacoEditor.razor is a Blazor wrapper with Value / ValueChanged / Language / Height / ReadOnly parameters and IAsyncDisposable teardown. Bidirectional binding tracks _lastSentValue to avoid push/pull loops. Replaces the plain textareas in SharedScriptForm, TemplateEdit's Add-Script form, and ApiMethodForm. Default height 320px ≈ the previous rows=10. Build / tests / dialog flow unaffected. Wave 1 of three. Roslyn-backed completions and SCADA-specific extensions follow in subsequent commits.
This commit is contained in:
@@ -38,7 +38,7 @@
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label class="form-label">Script</label>
|
||||
<textarea class="form-control font-monospace" rows="10" @bind="_script" style="font-size: 0.85rem;"></textarea>
|
||||
<MonacoEditor Value="@_script" ValueChanged="@(v => _script = v)" Language="csharp" Height="320px" />
|
||||
</div>
|
||||
|
||||
@if (_formError != null)
|
||||
|
||||
@@ -39,8 +39,7 @@
|
||||
</div>
|
||||
<div class="mb-2">
|
||||
<label class="form-label small">Code</label>
|
||||
<textarea class="form-control form-control-sm font-monospace" rows="10" @bind="_formCode"
|
||||
style="font-size: 0.8rem;"></textarea>
|
||||
<MonacoEditor Value="@_formCode" ValueChanged="@(v => _formCode = v)" Language="csharp" Height="320px" />
|
||||
</div>
|
||||
@if (_formError != null)
|
||||
{
|
||||
|
||||
@@ -654,8 +654,7 @@
|
||||
</div>
|
||||
<div class="col-12">
|
||||
<label class="form-label">Code</label>
|
||||
<textarea class="form-control font-monospace" rows="10" @bind="_scriptCode"
|
||||
style="font-size: 0.85rem;"></textarea>
|
||||
<MonacoEditor Value="@_scriptCode" ValueChanged="@(v => _scriptCode = v)" Language="csharp" Height="320px" />
|
||||
</div>
|
||||
@if (_scriptFormError != null)
|
||||
{
|
||||
|
||||
69
src/ScadaLink.CentralUI/Components/Shared/MonacoEditor.razor
Normal file
69
src/ScadaLink.CentralUI/Components/Shared/MonacoEditor.razor
Normal file
@@ -0,0 +1,69 @@
|
||||
@namespace ScadaLink.CentralUI.Components.Shared
|
||||
@implements IAsyncDisposable
|
||||
@inject IJSRuntime JS
|
||||
|
||||
<div @ref="_hostRef" class="monaco-editor-host"
|
||||
style="height: @Height; border: 1px solid var(--bs-border-color); border-radius: 0.25rem; overflow: hidden;"></div>
|
||||
|
||||
@code {
|
||||
[Parameter] public string Value { get; set; } = "";
|
||||
[Parameter] public EventCallback<string> ValueChanged { get; set; }
|
||||
[Parameter] public string Language { get; set; } = "csharp";
|
||||
[Parameter] public string Height { get; set; } = "320px";
|
||||
[Parameter] public bool ReadOnly { get; set; } = false;
|
||||
|
||||
private ElementReference _hostRef;
|
||||
private DotNetObjectReference<MonacoEditor>? _dotNetRef;
|
||||
private readonly string _id = Guid.NewGuid().ToString("N");
|
||||
private string _lastSentValue = "";
|
||||
private bool _initialized;
|
||||
|
||||
protected override async Task OnAfterRenderAsync(bool firstRender)
|
||||
{
|
||||
if (firstRender)
|
||||
{
|
||||
_dotNetRef = DotNetObjectReference.Create(this);
|
||||
_lastSentValue = Value ?? "";
|
||||
try
|
||||
{
|
||||
await JS.InvokeVoidAsync(
|
||||
"MonacoBlazor.createEditor",
|
||||
_id,
|
||||
_hostRef,
|
||||
new
|
||||
{
|
||||
value = Value ?? "",
|
||||
language = Language,
|
||||
readOnly = ReadOnly
|
||||
},
|
||||
_dotNetRef);
|
||||
_initialized = true;
|
||||
}
|
||||
catch
|
||||
{
|
||||
// Prerendering or JS not ready — swallow; subsequent render will retry.
|
||||
}
|
||||
}
|
||||
else if (_initialized && (Value ?? "") != _lastSentValue)
|
||||
{
|
||||
_lastSentValue = Value ?? "";
|
||||
try { await JS.InvokeVoidAsync("MonacoBlazor.setValue", _id, _lastSentValue); } catch { }
|
||||
}
|
||||
}
|
||||
|
||||
[JSInvokable]
|
||||
public async Task OnValueChanged(string newValue)
|
||||
{
|
||||
_lastSentValue = newValue ?? "";
|
||||
await ValueChanged.InvokeAsync(_lastSentValue);
|
||||
}
|
||||
|
||||
public async ValueTask DisposeAsync()
|
||||
{
|
||||
if (_initialized)
|
||||
{
|
||||
try { await JS.InvokeVoidAsync("MonacoBlazor.dispose", _id); } catch { }
|
||||
}
|
||||
_dotNetRef?.Dispose();
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user