File: Components\Resize\GridColumnManager.cs
Web Access
Project: src\src\Aspire.Dashboard\Aspire.Dashboard.csproj (Aspire.Dashboard)
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
 
using System.Text;
using Aspire.Dashboard.Model;
using Microsoft.AspNetCore.Components;
using Microsoft.AspNetCore.Components.Rendering;
 
namespace Aspire.Dashboard.Components.Resize;
 
public class GridColumnManager : ComponentBase, IDisposable
{
    private Dictionary<string, GridColumn> _columnById = null!;
    private float _availableFraction = 1;
    private ViewportInformation? _gridViewportInformation;
 
    [Inject]
    public required DimensionManager DimensionManager { get; init; }
 
    [Parameter]
    public required IList<GridColumn> Columns { get; set; }
 
    [Parameter]
    public RenderFragment? ChildContent { get; set; }
 
    public ViewportInformation ViewportInformation => _gridViewportInformation ?? DimensionManager.ViewportInformation;
 
    protected override void OnInitialized()
    {
        DimensionManager.OnViewportSizeChanged += OnViewportSizeChanged;
        _columnById = Columns.ToDictionary(c => c.Name, StringComparers.GridColumn);
    }
 
    protected override void BuildRenderTree(RenderTreeBuilder builder)
    {
        if (ChildContent is { } c)
        {
            c(builder);
        }
    }
 
    private void OnViewportSizeChanged(object sender, ViewportSizeChangedEventArgs e)
    {
        SetViewportInformation(e.ViewportSize);
    }
 
    private void SetViewportInformation(ViewportSize viewportSize)
    {
        if (_availableFraction == 1)
        {
            _gridViewportInformation = null;
        }
        else
        {
            var calculatedViewportSize = new ViewportSize(
                Convert.ToInt32(viewportSize.Width * _availableFraction),
                viewportSize.Height);
            var newViewportInformation = ViewportInformation.GetViewportInformation(calculatedViewportSize);
 
            if (_gridViewportInformation != newViewportInformation)
            {
                _gridViewportInformation = newViewportInformation;
                _ = InvokeAsync(StateHasChanged);
            }
        }
    }
 
    public void SetWidthFraction(float fraction)
    {
        _availableFraction = fraction;
        SetViewportInformation(DimensionManager.ViewportSize);
    }
 
    /// <summary>
    /// Gets whether the column is known, visible, and has a width for the current viewport.
    /// </summary>
    public bool IsColumnVisible(string columnName)
    {
        return _columnById.TryGetValue(columnName, out var column) // Is a known column.
            && GetColumnWidth(column) is not null                  // Has width for current viewport.
            && column.IsVisible?.Invoke() is null or true;         // Is visible.
    }
 
    /// <summary>
    /// Gets a string that can be used as the value for the grid-template-columns CSS property.
    /// For example, <c>1fr 2fr 1fr</c>.
    /// </summary>
    /// <returns></returns>
    public string GetGridTemplateColumns()
    {
        var sb = new StringBuilder();
 
        foreach (var (_, column) in _columnById)
        {
            if (column.IsVisible?.Invoke() is null or true &&
                GetColumnWidth(column) is string width)
            {
                if (sb.Length > 0)
                {
                    sb.Append(' ');
                }
 
                sb.Append(width);
            }
        }
 
        return sb.ToString();
    }
 
    private string? GetColumnWidth(GridColumn column)
    {
        var viewportInformation = _gridViewportInformation ?? DimensionManager.ViewportInformation;
 
        return viewportInformation.IsDesktop
            ? column.DesktopWidth
            : column.MobileWidth;
    }
 
    public void Dispose()
    {
        DimensionManager.OnViewportSizeChanged -= OnViewportSizeChanged;
    }
}