DynamicPanel.razor.cs
using System;
using System.Collections.Generic;
using Sandbox.Diagnostics;

namespace BetterUI;

/// <summary>
/// A panel that can be dynamically created and configured from a type and attributes.
/// </summary>
public partial class DynamicPanel : Panel
{
	/// <summary>
	/// The type of the panel to create.
	/// </summary>
	public Type Type { get; set; } = null!;

	/// <summary>
	/// The attributes to set on the panel.
	/// </summary>
	public Dictionary<string, object> Attributes { get; set; } = null!;

	protected override void OnAfterTreeRender( bool firstTime )
	{
		Assert.NotNull( Type, $"{nameof(DynamicPanel)} must have a type" );
		Assert.NotNull( Attributes, $"{nameof(DynamicPanel)} must have attributes" );
		Assert.False(Type.IsAssignableFrom( typeof( Panel ) ), $"{nameof(Type)} must inherit from {nameof(Panel)}");

		DeleteChildren( true );

		var panel = TypeLibrary.Create<Panel>( Type );
		if ( panel is null ) return;

		AddChild( panel );

		foreach ( var attribute in Attributes )
			TypeLibrary.SetProperty( panel, attribute.Key, attribute.Value );
	}

	protected override int BuildHash() => HashCode.Combine( Type, Attributes, Attributes.Count );
}