Code/SectionOutlet.razor.cs
using System;
using System.Collections.Generic;
using System.Linq;
using Sandbox.Diagnostics;

namespace BetterUI;

/// <summary>
/// A panel that acts as an outlet for sections.
/// </summary>
/// <remarks>
/// The <see cref="SectionOutlet"/> is a panel that can be used as a placeholder for
/// sections that are defined elsewhere in code. It is used in conjunction with the
/// <see cref="SectionContent"/> panel to define sections of content that can be
/// rendered in multiple places in the UI.
/// </remarks>
public sealed partial class SectionOutlet : Panel
{
	/// <summary>
	/// The name of the section to render.
	/// </summary>
	public string SectionName { get; set; } = null!;

	protected override void OnAfterTreeRender( bool firstTime )
	{
		Assert.NotNull( SectionName, "SectionName must be set" );
		IterateChildren( FindRootPanel().Descendants );
	}
	
	/// <summary>
	/// Iterates over the provided panels and sets the child content
	/// for each section outlet that matches the section name.
	/// </summary>
	/// <param name="panels">The panels to iterate over.</param>
	private void IterateChildren( IEnumerable<Panel> panels )
	{
		foreach ( var panel in panels )
		{
			var sections = FindSectionOutlets( panel );
			
			foreach ( var section in sections )
				section.ChildContent = ChildContent;
		}
	}

	/// <summary>
	/// Finds all section outlets within the panel that match the section name.
	/// </summary>
	/// <param name="panel">The panel to search within.</param>
	/// <returns>A list of matching section outlets.</returns>
	private List<SectionContent> FindSectionOutlets( Panel panel ) =>
		panel.Descendants.OfType<SectionContent>().Where( x => x.SectionName == SectionName ).ToList();
	
	protected override int BuildHash() => HashCode.Combine( SectionName );
}