}
@for (int i = 0; i < _rows.Count; i++)
{
var index = i;
var row = _rows[index];
@if (Definition.IsCompositeList)
{
@foreach (var field in Definition.Fields)
{
var fieldKey = field.Key;
var fieldDef = field.Value;
var fieldLabel = !string.IsNullOrWhiteSpace(fieldDef.DisplayName) ? fieldDef.DisplayName : fieldKey;
var fieldValue = row.TryGetValue(fieldKey, out var v) ? v : "";
@fieldLabel
}
}
else
{
}
}
@if (Definition.MinItems.HasValue || Definition.MaxItems.HasValue)
{
@(Definition.MinItems.HasValue ? $"min {Definition.MinItems.Value}" : "")@(Definition.MinItems.HasValue && Definition.MaxItems.HasValue ? ", " : "")@(Definition.MaxItems.HasValue ? $"max {Definition.MaxItems.Value}" : "")
}
@code {
const string ValueKey = "__value";
[Parameter] public OptionDefinition Definition { get; set; }
[Parameter] public string Value { get; set; }
[Parameter] public EventCallback ValueChanged { get; set; }
List> _rows = new();
OptionDefinition _scalarItemDefinition;
string _lastSyncedValue;
protected override void OnParametersSet()
{
_scalarItemDefinition = new OptionDefinition
{
Type = string.IsNullOrWhiteSpace(Definition.ItemType) ? "string" : Definition.ItemType
};
// Only reparse Value when it changed externally (not as the result of our own emit).
if (!ReferenceEquals(Value, _lastSyncedValue) && Value != _lastSyncedValue)
{
_rows = ParseValue(Value);
_lastSyncedValue = Value;
}
}
List> ParseValue(string json)
{
var result = new List>();
if (string.IsNullOrWhiteSpace(json))
return result;
JsonElement root;
try
{
using var doc = JsonDocument.Parse(json);
root = doc.RootElement.Clone();
}
catch
{
return result;
}
if (root.ValueKind != JsonValueKind.Array)
return result;
foreach (var item in root.EnumerateArray())
{
if (Definition.IsCompositeList)
{
var dict = new Dictionary();
if (item.ValueKind == JsonValueKind.Object)
{
foreach (var field in Definition.Fields)
{
if (item.TryGetProperty(field.Key, out var fieldEl))
dict[field.Key] = ReadScalar(fieldEl);
}
}
result.Add(dict);
}
else
{
result.Add(new Dictionary { [ValueKey] = ReadScalar(item) });
}
}
return result;
}
static string ReadScalar(JsonElement element) => element.ValueKind switch
{
JsonValueKind.String => element.GetString() ?? "",
JsonValueKind.Null or JsonValueKind.Undefined => "",
_ => element.GetRawText()
};
async Task OnFieldChanged(int index, string fieldKey, string value)
{
if (index < 0 || index >= _rows.Count)
return;
_rows[index][fieldKey] = value;
await EmitChange();
}
async Task AddRow()
{
if (Definition.MaxItems.HasValue && _rows.Count >= Definition.MaxItems.Value)
return;
var row = new Dictionary();
if (Definition.IsCompositeList)
{
foreach (var field in Definition.Fields)
{
var fieldDefault = field.Value?.GetDefaultAsString();
if (!string.IsNullOrWhiteSpace(fieldDefault))
row[field.Key] = fieldDefault;
}
}
else
{
row[ValueKey] = "";
}
_rows.Add(row);
await EmitChange();
}
async Task RemoveRow(int index)
{
if (index < 0 || index >= _rows.Count)
return;
if (_rows.Count <= (Definition.MinItems ?? 0))
return;
_rows.RemoveAt(index);
await EmitChange();
}
async Task EmitChange()
{
var json = Serialize();
_lastSyncedValue = json;
if (ValueChanged.HasDelegate)
await ValueChanged.InvokeAsync(json);
}
string Serialize()
{
if (Definition.IsCompositeList)
{
var items = new List>();
foreach (var row in _rows)
{
var entry = new Dictionary();
foreach (var field in Definition.Fields)
{
entry[field.Key] = row.TryGetValue(field.Key, out var v) ? v : "";
}
items.Add(entry);
}
return JsonSerializer.Serialize(items);
}
else
{
var items = new List();
foreach (var row in _rows)
items.Add(row.TryGetValue(ValueKey, out var v) ? v : "");
return JsonSerializer.Serialize(items);
}
}
}