From 878a7fededeb4430c87019277b721599b5b9152c Mon Sep 17 00:00:00 2001 From: Alexander Shabarshov Date: Mon, 7 Apr 2025 10:06:02 +0100 Subject: [PATCH] Add ExpandoTree for hierarchical expandable nodes --- BlazorOpenApi/Controls/Expander.razor | 61 ++++++++++++------- BlazorOpenApi/Controls/OperationControl.razor | 29 ++++++--- .../Controls/RequestBodyControl.razor | 2 +- BlazorOpenApi/ExpandoTree.cs | 54 ++++++++++++++++ BlazorOpenApi/IExpandoTree.cs | 26 ++++++++ BlazorOpenApi/OpenAPIUIControl.razor | 40 +++++++----- 6 files changed, 165 insertions(+), 47 deletions(-) create mode 100644 BlazorOpenApi/ExpandoTree.cs create mode 100644 BlazorOpenApi/IExpandoTree.cs diff --git a/BlazorOpenApi/Controls/Expander.razor b/BlazorOpenApi/Controls/Expander.razor index 2390a02..7372dd2 100644 --- a/BlazorOpenApi/Controls/Expander.razor +++ b/BlazorOpenApi/Controls/Expander.razor @@ -1,27 +1,29 @@ -
-
- @Title -
- @if (Collapsed) - { - - - - } - else - { - - - - } + +
+
+ @Title +
+ @if (Collapsed) + { + + + + } + else + { + + + + } +
-
- @if (!Collapsed && ChildContent != null) - { - @ChildContent - } -
+ @if (!Collapsed && ChildContent != null) + { + @ChildContent + } +
+ @code { [Parameter] @@ -35,9 +37,24 @@ [Parameter] public string Class { get; set; } = ""; + [CascadingParameter(Name = "OpenAPIUI_ExpandoTree")] public IExpandoTree Tree { get; set; } = null!; + [CascadingParameter(Name = "OpenAPIUI_ExpandoTree_Parent")] public string Parent { get; set; } = ""; + + private string _anchor = $"anc{Random.Shared.Next():X8}"; + + protected override void OnAfterRender(bool firstRender) + { + if (!firstRender) + return; + + Tree.Add(Title, _anchor, Parent, Collapsed); + Collapsed = Tree.IsCollapsed(Title); + } + void Toggle() { Collapsed = !Collapsed; + Tree.Collapse(_anchor, Collapsed); StateHasChanged(); } } \ No newline at end of file diff --git a/BlazorOpenApi/Controls/OperationControl.razor b/BlazorOpenApi/Controls/OperationControl.razor index cbce0e6..10b6247 100644 --- a/BlazorOpenApi/Controls/OperationControl.razor +++ b/BlazorOpenApi/Controls/OperationControl.razor @@ -48,10 +48,18 @@ -
-

Example Data

-
@GenerateExampleData()
-
+ @if (true) + { + var example = GenerateExampleData(); + if ( !string.IsNullOrWhiteSpace(example) ) + { +
+

Example Data

+
@GenerateExampleData()
+
+ } + + } }
} @@ -85,7 +93,7 @@ var exampleData = new Dictionary(); // Generate example data for parameters - foreach (var parameter in Value.Parameters) + foreach (var parameter in Value.Parameters.Where(x => x.In == ParameterLocation.Query)) { if (parameter.Example != null) { @@ -121,7 +129,10 @@ } } - return JsonSerializer.Serialize(exampleData, new JsonSerializerOptions { WriteIndented = true }); + return exampleData.Count == 0 + ? "" + : JsonSerializer.Serialize(exampleData, new JsonSerializerOptions { WriteIndented = true }) + ; } private object GenerateExampleFromSchema(OpenApiSchema? schema) @@ -153,11 +164,11 @@ { return type switch { - "string" => "string", + "string" => "string", "integer" => 0, - "number" => 0.0, + "number" => 0.0, "boolean" => false, - _ => new object() + _ => new object() }; } } diff --git a/BlazorOpenApi/Controls/RequestBodyControl.razor b/BlazorOpenApi/Controls/RequestBodyControl.razor index 7a38752..6217c1f 100644 --- a/BlazorOpenApi/Controls/RequestBodyControl.razor +++ b/BlazorOpenApi/Controls/RequestBodyControl.razor @@ -3,7 +3,7 @@
Request body
- + @foreach ( var media in Value.Content) { diff --git a/BlazorOpenApi/ExpandoTree.cs b/BlazorOpenApi/ExpandoTree.cs new file mode 100644 index 0000000..2c81807 --- /dev/null +++ b/BlazorOpenApi/ExpandoTree.cs @@ -0,0 +1,54 @@ +using System; +using System.Collections.Generic; +using System.Linq; + +namespace BlazorOpenApi; + +internal class ExpandoTree : IExpandoTree +{ + private readonly Dictionary _nodes = new(); + private readonly List _order = new(); + + public void Add(string name, string anchor, string parentAnchor, bool collapsed) + { + if (_nodes.ContainsKey(anchor)) + { + throw new InvalidOperationException($"A node with anchor '{anchor}' already exists."); + } + + var node = new ExpandoTreeNode + { + Name = name, + Anchor = anchor, + ParentAnchor = parentAnchor, + Collapsed = collapsed + }; + + _nodes[anchor] = node; + _order.Add(anchor); + } + + public void Collapse(string anchor, bool collapsed) + { + if (_nodes.TryGetValue(anchor, out var node)) + { + node.Collapsed = collapsed; + } + else + { + throw new KeyNotFoundException($"No node found with anchor '{anchor}'."); + } + } + + public bool IsCollapsed(string anchor) + { + return _nodes.TryGetValue(anchor, out var node) && node.Collapsed; + } + + public bool Exists(string anchor) => _nodes.ContainsKey(anchor); + + public ExpandoTreeNode[] GetChildren(string anchor) => _order + .Where(x => _nodes[x].ParentAnchor == anchor) + .Select(x => _nodes[x]) + .ToArray(); +} diff --git a/BlazorOpenApi/IExpandoTree.cs b/BlazorOpenApi/IExpandoTree.cs new file mode 100644 index 0000000..54d45e8 --- /dev/null +++ b/BlazorOpenApi/IExpandoTree.cs @@ -0,0 +1,26 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace BlazorOpenApi; + +public class ExpandoTreeNode +{ + public string Name { get; set; } = string.Empty; + public string Anchor { get; set; } = string.Empty; + public string ParentAnchor { get; set; } = string.Empty; + public bool Collapsed { get; set; } +} + +public interface IExpandoTree +{ + void Add(string name, string anchor, string parentAnchor, bool collapsed); + void Collapse(string anchor, bool collapsed); + bool IsCollapsed(string anchor); + bool Exists(string anchor); + ExpandoTreeNode[] GetChildren(string anchor); +} + +//implement IExpandoTree \ No newline at end of file diff --git a/BlazorOpenApi/OpenAPIUIControl.razor b/BlazorOpenApi/OpenAPIUIControl.razor index 5d54315..34f9567 100644 --- a/BlazorOpenApi/OpenAPIUIControl.razor +++ b/BlazorOpenApi/OpenAPIUIControl.razor @@ -2,31 +2,36 @@ @using Microsoft.OpenApi.Models @using Microsoft.OpenApi.Readers + @if (Palette != null ) { @PaletteStr }
- - - - @if (_api.Paths?.Count > 0) - { -

Endpoints

- foreach (var path in _api.Paths) + + + + + + @if (_api.Paths?.Count > 0) { - if (!string.IsNullOrWhiteSpace(path.Key) || path.Value != null) +

Endpoints

+ foreach (var path in _api.Paths) { - + if (!string.IsNullOrWhiteSpace(path.Key) || path.Value != null) + { + + } } } - } - @if (_api.Components != null) - { -

Components

- - } + @if (_api.Components != null) + { +

Components

+ + } + +
@@ -35,8 +40,11 @@ [Parameter] public string Json { get; set; } = ""; [Parameter] public OpenApiUiPalette? Palette { get; set; } + private string _loadedFor = ""; + private OpenApiDocument _api = new(); + private IExpandoTree _tree = new ExpandoTree(); private MarkupString PaletteStr => Palette?.AsMarkupString ?? new(); @@ -98,4 +106,6 @@ _api = new(); } } + + }