FractalDataWorks.UI.Web.Abstractions 0.1.0-preview.11

This is a prerelease version of FractalDataWorks.UI.Web.Abstractions.
dotnet add package FractalDataWorks.UI.Web.Abstractions --version 0.1.0-preview.11
                    
NuGet\Install-Package FractalDataWorks.UI.Web.Abstractions -Version 0.1.0-preview.11
                    
This command is intended to be used within the Package Manager Console in Visual Studio, as it uses the NuGet module's version of Install-Package.
<PackageReference Include="FractalDataWorks.UI.Web.Abstractions" Version="0.1.0-preview.11" />
                    
For projects that support PackageReference, copy this XML node into the project file to reference the package.
<PackageVersion Include="FractalDataWorks.UI.Web.Abstractions" Version="0.1.0-preview.11" />
                    
Directory.Packages.props
<PackageReference Include="FractalDataWorks.UI.Web.Abstractions" />
                    
Project file
For projects that support Central Package Management (CPM), copy this XML node into the solution Directory.Packages.props file to version the package.
paket add FractalDataWorks.UI.Web.Abstractions --version 0.1.0-preview.11
                    
#r "nuget: FractalDataWorks.UI.Web.Abstractions, 0.1.0-preview.11"
                    
#r directive can be used in F# Interactive and Polyglot Notebooks. Copy this into the interactive tool or source code of the script to reference the package.
#:package FractalDataWorks.UI.Web.Abstractions@0.1.0-preview.11
                    
#:package directive can be used in C# file-based apps starting in .NET 10 preview 4. Copy this into a .cs file before any lines of code to reference the package.
#addin nuget:?package=FractalDataWorks.UI.Web.Abstractions&version=0.1.0-preview.11&prerelease
                    
Install as a Cake Addin
#tool nuget:?package=FractalDataWorks.UI.Web.Abstractions&version=0.1.0-preview.11&prerelease
                    
Install as a Cake Tool

FractalDataWorks.UI.Web.Abstractions

Framework-agnostic web component abstractions with JavaScript interop capabilities. Provides a metadata-driven rendering system that works with Blazor, React, Vue, Angular, or any JavaScript framework.

Overview

This package extends the pure abstractions from FractalDataWorks.UI.Abstractions to add web-specific capabilities:

  • Component Metadata - Serializable to JSON for JavaScript consumption
  • JavaScript Interop - Generic interface for JS communication
  • HTML Rendering - Framework-agnostic HTML generation
  • Property Metadata - Describes form fields for any UI framework
  • TypeCollections - RenderModes and ComponentTypes for type-safe UI patterns

Target Framework: netstandard2.0, net10.0 Dependencies:

  • FractalDataWorks.UI.Abstractions
  • FractalDataWorks.Collections
  • System.Text.Json

Key Types

IWebComponent

From IWebComponent.cs:1-34:

/// <summary>
/// Base interface for web components that can export metadata.
/// </summary>
public interface IWebComponent
{
    /// <summary>
    /// Gets component metadata for serialization.
    /// </summary>
    ComponentMetadata GetMetadata();

    /// <summary>
    /// Serializes component to JSON.
    /// </summary>
    string ToJson();

    /// <summary>
    /// Renders component to HTML string.
    /// </summary>
    string RenderToHtml();

    /// <summary>
    /// Gets property metadata.
    /// </summary>
    IList<PropertyMetadata> GetPropertyMetadata();

    /// <summary>
    /// Gets child components.
    /// </summary>
    IList<IWebComponent> GetChildComponents();
}

WebComponentBase<TSelf, TModel>

From WebComponentBase.cs:1-77:

/// <summary>
/// CRTP-based web component that can render to ANY JavaScript framework.
/// Exports metadata that can be consumed by Blazor, React, Vue, Angular, etc.
/// </summary>
public abstract class WebComponentBase<TSelf, TModel> : ComponentBase<TSelf, TModel>, IWebComponent
    where TSelf : WebComponentBase<TSelf, TModel>
{
    private static readonly JsonSerializerOptions JsonOptions = new()
    {
        PropertyNamingPolicy = JsonNamingPolicy.CamelCase,
        WriteIndented = true
    };

    /// <summary>
    /// JavaScript interop implementation (set by framework).
    /// </summary>
    public IJavaScriptInterop? JSInterop { get; set; }

    /// <summary>
    /// Render mode ID (reference to RenderModes TypeCollection).
    /// </summary>
    public int RenderModeId { get; set; } = 1; // Default: View

    /// <summary>
    /// Gets metadata that ANY JavaScript framework can understand.
    /// </summary>
    public new virtual ComponentMetadata GetMetadata()
    {
        return new ComponentMetadata
        {
            ComponentType = typeof(TSelf).Name,
            ModelType = typeof(TModel).Name,
            Properties = GetPropertyMetadata(),
            ChildComponents = GetChildComponents()
                .Select(c => c.GetMetadata())
                .ToList(),
            RenderModeId = RenderModeId
        };
    }

    /// <summary>
    /// Serializes component to JSON for JavaScript consumption.
    /// </summary>
    public string ToJson()
    {
        return JsonSerializer.Serialize(GetMetadata(), JsonOptions);
    }

    /// <summary>
    /// Renders component to HTML string (framework-agnostic).
    /// </summary>
    public virtual string RenderToHtml()
    {
        var metadata = GetMetadata();
        return HtmlRenderer.Render(metadata);
    }

    /// <summary>
    /// Gets property metadata for all properties.
    /// Must be implemented by derived classes.
    /// </summary>
    public abstract IList<PropertyMetadata> GetPropertyMetadata();

    /// <summary>
    /// Gets child components (for nested structures).
    /// Must be implemented by derived classes.
    /// </summary>
    public abstract IList<IWebComponent> GetChildComponents();
}

ComponentMetadata

From ComponentMetadata.cs:1-46:

/// <summary>
/// Framework-agnostic component metadata that can be serialized to JSON.
/// Consumed by ANY JavaScript framework (Blazor, React, Vue, Angular, etc.).
/// </summary>
public class ComponentMetadata
{
    /// <summary>
    /// Type name of the component (e.g., "EmailConfigurationWebComponent").
    /// </summary>
    public string ComponentType { get; set; } = "";

    /// <summary>
    /// Type name of the model (e.g., "EmailConfiguration").
    /// </summary>
    public string ModelType { get; set; } = "";

    /// <summary>
    /// Property metadata for all properties.
    /// </summary>
    public IList<PropertyMetadata> Properties { get; set; } = new List<PropertyMetadata>();

    /// <summary>
    /// Child components (for nested structures).
    /// </summary>
    public IList<ComponentMetadata> ChildComponents { get; set; } = new List<ComponentMetadata>();

    /// <summary>
    /// Additional component-level attributes.
    /// </summary>
    public IDictionary<string, object> Attributes { get; set; } = new Dictionary<string, object>(StringComparer.Ordinal);

    /// <summary>
    /// Render mode ID (reference to RenderModes TypeCollection).
    /// </summary>
    public int RenderModeId { get; set; }

    /// <summary>
    /// Gets render mode name for serialization.
    /// </summary>
    public string RenderMode => RenderModes.ById(RenderModeId)?.Name ?? "View";
}

PropertyMetadata

From PropertyMetadata.cs:1-80:

/// <summary>
/// Describes a single property's UI representation.
/// </summary>
public class PropertyMetadata
{
    /// <summary>
    /// Property name (e.g., "SmtpHost").
    /// </summary>
    public string Name { get; set; } = "";

    /// <summary>
    /// C# type name (e.g., "string", "int").
    /// </summary>
    public string PropertyType { get; set; } = "";

    /// <summary>
    /// Component type ID (reference to ComponentTypes TypeCollection).
    /// </summary>
    public int ComponentTypeId { get; set; }

    /// <summary>
    /// Gets component type name for serialization.
    /// </summary>
    public string ComponentType => ComponentTypes.ById(ComponentTypeId)?.Name ?? "TextInput";

    /// <summary>
    /// Current property value.
    /// </summary>
    public object? Value { get; set; }

    /// <summary>
    /// Display label.
    /// </summary>
    public string? Label { get; set; }

    /// <summary>
    /// Help text / description.
    /// </summary>
    public string? HelpText { get; set; }

    /// <summary>
    /// Placeholder text for inputs.
    /// </summary>
    public string? Placeholder { get; set; }

    /// <summary>
    /// Is this field required?
    /// </summary>
    public bool Required { get; set; }

    /// <summary>
    /// Is this field read-only?
    /// </summary>
    public bool ReadOnly { get; set; }

    /// <summary>
    /// Display order (for sorting).
    /// </summary>
    public int DisplayOrder { get; set; }

    /// <summary>
    /// Display group (for grouping).
    /// </summary>
    public string? DisplayGroup { get; set; }

    /// <summary>
    /// Validation rules (pattern, min, max, etc.).
    /// </summary>
    public IDictionary<string, object> ValidationRules { get; set; } = new Dictionary<string, object>(StringComparer.Ordinal);

    /// <summary>
    /// Additional property-level attributes.
    /// </summary>
    public IDictionary<string, object> Attributes { get; set; } = new Dictionary<string, object>(StringComparer.Ordinal);
}

IJavaScriptInterop

From IJavaScriptInterop.cs:1-27:

/// <summary>
/// Generic JavaScript interop interface.
/// Implementations vary by framework (Blazor, Node.js, browser).
/// </summary>
public interface IJavaScriptInterop
{
    /// <summary>
    /// Invokes a JavaScript function and returns a value.
    /// </summary>
    /// <typeparam name="T">The type of value to return from the JavaScript function.</typeparam>
    /// <param name="identifier">The JavaScript function identifier to invoke.</param>
    /// <param name="args">Arguments to pass to the JavaScript function.</param>
    /// <returns>A task that represents the asynchronous operation, containing the result of the JavaScript function.</returns>
    Task<T> Invoke<T>(string identifier, params object[] args);

    /// <summary>
    /// Invokes a JavaScript function without returning a value.
    /// </summary>
    /// <param name="identifier">The JavaScript function identifier to invoke.</param>
    /// <param name="args">Arguments to pass to the JavaScript function.</param>
    /// <returns>A task that represents the asynchronous operation.</returns>
    Task InvokeVoid(string identifier, params object[] args);
}

HtmlRenderer

From HtmlRenderer.cs:1-57:

/// <summary>
/// Renders component metadata to HTML string.
/// </summary>
public static class HtmlRenderer
{
    /// <summary>
    /// Renders component metadata to HTML.
    /// </summary>
    /// <param name="metadata">The component metadata to render.</param>
    /// <returns>HTML string representation of the component.</returns>
    public static string Render(ComponentMetadata metadata)
    {
        var html = new StringBuilder();
        html.Append($"<div class=\"component {metadata.ComponentType.ToLowerInvariant()}\">");
        html.Append($"{Environment.NewLine}");

        foreach (var property in metadata.Properties.OrderBy(p => p.DisplayOrder))
        {
            html.Append($"{RenderProperty(property)}");
        }

        foreach (var child in metadata.ChildComponents)
        {
            html.Append($"{Render(child)}");
        }

        html.Append($"</div>{Environment.NewLine}");
        return html.ToString();
    }

    private static string RenderProperty(PropertyMetadata property)
    {
        var componentType = ComponentTypes.ByName(property.ComponentType);

        if (componentType == null)
        {
            return RenderGenericInput(property);
        }

        // Use TypeCollection to determine rendering
        return componentType.Id switch
        {
            1 => RenderTextInput(property),        // TextInput
            2 => RenderNumericInput(property),     // NumericInput
            3 => RenderTextArea(property),         // TextArea
            4 => RenderSwitch(property),           // Switch
            5 => RenderDatePicker(property),       // DatePicker
            10 => RenderDropdown(property),        // Dropdown
            _ => RenderGenericInput(property)
        };
    }

    // ... additional private rendering methods
}

TypeCollections

RenderModes

From RenderModes.cs:1-89:

/// <summary>
/// Collection of render modes for web components.
/// </summary>
[TypeCollection(typeof(RenderModeBase), typeof(IRenderMode), typeof(RenderModes))]
public abstract partial class RenderModes : TypeCollectionBase<RenderModeBase, IRenderMode>
{
}

/// <summary>
/// Base class for render mode types.
/// </summary>
public abstract class RenderModeBase : TypeOptionBase<int, RenderModeBase>, IRenderMode
{
    protected RenderModeBase(int id, string name, string displayName, string description)
        : base(id, name, $"RenderModes:{name}", displayName, description, "RenderModes")
    {
    }
}

// Available render modes:
// ViewRenderMode (Id=1) - Read-only display
// EditRenderMode (Id=2) - Editable input
// CreateRenderMode (Id=3) - Create new items
// BothRenderMode (Id=4) - View and edit simultaneously

ComponentTypes

From ComponentTypes.cs:1-225:

/// <summary>
/// Collection of UI component types.
/// </summary>
[TypeCollection(typeof(ComponentTypeBase), typeof(IComponentType), typeof(ComponentTypes))]
public abstract partial class ComponentTypes : TypeCollectionBase<ComponentTypeBase, IComponentType>
{
}

/// <summary>
/// Base class for component types.
/// </summary>
public abstract class ComponentTypeBase : TypeOptionBase<int, ComponentTypeBase>, IComponentType
{
    public new string Category { get; }

    protected ComponentTypeBase(int id, string name, string displayName, string category, string description)
        : base(id, name, $"ComponentTypes:{name}", displayName, description, category)
    {
        Category = category;
    }
}

// Available component types:
// Input: TextInput(1), NumericInput(2), TextArea(3), Switch(4), DatePicker(5), DateTimePicker(6), FileUpload(16)
// Selection: Dropdown(7), MultiSelect(8), RadioGroup(9), CheckboxList(10)
// Complex: Collection(11), TypeCollectionDropdown(12), JsonEditor(15)

Architecture

Framework Agnostic Design

The same component metadata works with:

  • Blazor (via RenderTreeBuilder)
  • React (via JSInterop and React components)
  • Vue (via JSInterop and Vue components)
  • Angular (via JSInterop and Angular components)
  • Vanilla JavaScript (via HTML string rendering)

TypeCollection Integration

Components use TypeCollection IDs rather than string constants:

  • RenderModeId references RenderModes TypeCollection
  • ComponentTypeId references ComponentTypes TypeCollection

This enables type-safe lookups via RenderModes.ById() and ComponentTypes.ByName().

Best Practices

  1. Keep Metadata Serializable - Only use JSON-compatible types in metadata
  2. Use TypeCollection IDs - Reference ComponentTypeId and RenderModeId rather than strings
  3. Implement Abstract Methods - Derived classes must implement GetPropertyMetadata() and GetChildComponents()
  4. Framework Independence - Don't couple to specific JavaScript frameworks
  5. Validation in Metadata - Use the ValidationRules dictionary for client-side validation
  6. Ordering Properties - Use DisplayOrder to control property rendering sequence
  7. Grouping Properties - Use DisplayGroup to organize related properties

Dependencies

  • FractalDataWorks.UI.Abstractions - Pure CRTP base classes
  • FractalDataWorks.Collections - TypeCollection infrastructure
  • System.Text.Json - JSON serialization
Product Compatible and additional computed target framework versions.
.NET net5.0 was computed.  net5.0-windows was computed.  net6.0 was computed.  net6.0-android was computed.  net6.0-ios was computed.  net6.0-maccatalyst was computed.  net6.0-macos was computed.  net6.0-tvos was computed.  net6.0-windows was computed.  net7.0 was computed.  net7.0-android was computed.  net7.0-ios was computed.  net7.0-maccatalyst was computed.  net7.0-macos was computed.  net7.0-tvos was computed.  net7.0-windows was computed.  net8.0 was computed.  net8.0-android was computed.  net8.0-browser was computed.  net8.0-ios was computed.  net8.0-maccatalyst was computed.  net8.0-macos was computed.  net8.0-tvos was computed.  net8.0-windows was computed.  net9.0 was computed.  net9.0-android was computed.  net9.0-browser was computed.  net9.0-ios was computed.  net9.0-maccatalyst was computed.  net9.0-macos was computed.  net9.0-tvos was computed.  net9.0-windows was computed.  net10.0 was computed.  net10.0-android was computed.  net10.0-browser was computed.  net10.0-ios was computed.  net10.0-maccatalyst was computed.  net10.0-macos was computed.  net10.0-tvos was computed.  net10.0-windows was computed. 
.NET Core netcoreapp2.0 was computed.  netcoreapp2.1 was computed.  netcoreapp2.2 was computed.  netcoreapp3.0 was computed.  netcoreapp3.1 was computed. 
.NET Standard netstandard2.0 is compatible.  netstandard2.1 was computed. 
.NET Framework net461 was computed.  net462 was computed.  net463 was computed.  net47 was computed.  net471 was computed.  net472 was computed.  net48 was computed.  net481 was computed. 
MonoAndroid monoandroid was computed. 
MonoMac monomac was computed. 
MonoTouch monotouch was computed. 
Tizen tizen40 was computed.  tizen60 was computed. 
Xamarin.iOS xamarinios was computed. 
Xamarin.Mac xamarinmac was computed. 
Xamarin.TVOS xamarintvos was computed. 
Xamarin.WatchOS xamarinwatchos was computed. 
Compatible target framework(s)
Included target framework(s) (in package)
Learn more about Target Frameworks and .NET Standard.

NuGet packages (1)

Showing the top 1 NuGet packages that depend on FractalDataWorks.UI.Web.Abstractions:

Package Downloads
FractalDataWorks.UI.Components.Blazor

Development tools and utilities for the FractalDataWorks ecosystem. Build:

GitHub repositories

This package is not used by any popular GitHub repositories.

Version Downloads Last Updated
0.1.0-preview.11 3 1/12/2026
0.1.0-preview.10 14 1/12/2026
0.1.0-preview.9 34 1/9/2026
0.1.0-preview.7 98 1/7/2026