< Summary

Information
Class: Chronicis.Client.Components.Articles.ArticleHeader
Assembly: Chronicis.Client
File(s): /home/runner/work/chronicis/chronicis/src/Chronicis.Client/Components/Articles/ArticleHeader.razor
Line coverage
0%
Covered lines: 0
Uncovered lines: 36
Coverable lines: 36
Total lines: 162
Line coverage: 0%
Branch coverage
0%
Covered branches: 0
Total branches: 6
Branch coverage: 0%
Method coverage

Feature is only available for sponsors

Upgrade to PRO version

Metrics

File(s)

/home/runner/work/chronicis/chronicis/src/Chronicis.Client/Components/Articles/ArticleHeader.razor

#LineLine coverage
 1@* ArticleHeader.razor - Header section for article detail view *@
 2@* Includes breadcrumbs, metadata toggle, icon picker, editable title, and divider *@
 3
 4@using Chronicis.Client.Components.Shared
 5@inject IJSRuntime JSRuntime
 6
 7<!-- Breadcrumbs with Metadata Toggle -->
 8<ChroniclsBreadcrumbs Items="Breadcrumbs" UseCustomLinks="true">
 9    <MudTooltip Text="Metadata">
 10        <MudIconButton Color="Color.Inherit"
 11                       OnClick="OnMetadataToggleClick"
 12                       Icon="@Icons.Material.Filled.ChromeReaderMode"
 13                       Class="mt-0"
 14                       Edge="Edge.End" />
 15    </MudTooltip>
 16</ChroniclsBreadcrumbs>
 17
 18<!-- Title Row with Icon Picker -->
 19<div class="d-flex align-center mb-3">
 20    <!-- Icon Picker Button -->
 21    <IconPickerButton
 22        CurrentIcon="@IconEmoji"
 23        OnIconChanged="OnIconChangedInternal" />
 24
 25    <!-- Title (Always Editable) -->
 026    <MudTextField @ref="_titleField"
 027                  @bind-Value="Title"
 028                  @bind-Value:after="OnTitleChangedInternal"
 029                  Variant="Variant.Text"
 030                  Placeholder="Untitled Article"
 031                  Class="chronicis-article-title flex-grow-1"
 32                  Style="font-size: 2rem; font-family: var(--chronicis-font-heading);"
 33                  Immediate="true"
 34                  @onkeydown="OnTitleKeyDownInternal"
 35                  Underline="false" />
 36</div>
 37
 38<!-- Divider -->
 39<div class="chronicis-rune-divider mb-3"></div>
 40
 41@code {
 42    private MudTextField<string>? _titleField;
 43
 44    #region Parameters
 45
 46    /// <summary>
 47    /// The breadcrumb items to display for navigation.
 48    /// </summary>
 49    [Parameter]
 050    public List<BreadcrumbItem>? Breadcrumbs { get; set; }
 51
 52    /// <summary>
 53    /// The current title value (two-way bound).
 54    /// </summary>
 55    [Parameter]
 056    public string Title { get; set; } = string.Empty;
 57
 58    /// <summary>
 59    /// Callback when title changes (for two-way binding).
 60    /// </summary>
 61    [Parameter]
 062    public EventCallback<string> TitleChanged { get; set; }
 63
 64    /// <summary>
 65    /// The current icon emoji (e.g., "fa-solid fa-dragon").
 66    /// </summary>
 67    [Parameter]
 068    public string? IconEmoji { get; set; }
 69
 70    /// <summary>
 71    /// Callback when the icon is changed or cleared.
 72    /// </summary>
 73    [Parameter]
 074    public EventCallback<string?> OnIconChanged { get; set; }
 75
 76    /// <summary>
 77    /// Callback when title content is edited (for marking unsaved changes).
 78    /// </summary>
 79    [Parameter]
 080    public EventCallback OnTitleEdited { get; set; }
 81
 82    /// <summary>
 83    /// Callback when Enter key is pressed in title field (for triggering save).
 84    /// </summary>
 85    [Parameter]
 086    public EventCallback OnEnterPressed { get; set; }
 87
 88    /// <summary>
 89    /// Callback when the metadata toggle button is clicked.
 90    /// </summary>
 91    [Parameter]
 092    public EventCallback OnMetadataToggle { get; set; }
 93
 94    /// <summary>
 95    /// When true, the title field will be focused on next render.
 96    /// Component will set this to false after focusing.
 97    /// </summary>
 98    [Parameter]
 099    public bool ShouldFocusTitle { get; set; }
 100
 101    /// <summary>
 102    /// Callback to notify parent that focus has been handled.
 103    /// </summary>
 104    [Parameter]
 0105    public EventCallback<bool> ShouldFocusTitleChanged { get; set; }
 106
 107    #endregion
 108
 109    #region Lifecycle
 110
 111    protected override async Task OnAfterRenderAsync(bool firstRender)
 112    {
 0113        if (ShouldFocusTitle && _titleField != null)
 114        {
 0115            await Task.Delay(100);
 116            try
 117            {
 0118                await _titleField.FocusAsync();
 119                // Also try JS focus as backup
 0120                await JSRuntime.InvokeVoidAsync("eval",
 0121                    "document.querySelector('.chronicis-article-title input')?.focus();");
 0122            }
 0123            catch
 124            {
 125                // Ignore focus errors
 0126            }
 127
 128            // Notify parent that focus has been handled
 0129            await ShouldFocusTitleChanged.InvokeAsync(false);
 130        }
 0131    }
 132
 133    #endregion
 134
 135    #region Event Handlers
 136
 137    private async Task OnTitleChangedInternal()
 138    {
 0139        await TitleChanged.InvokeAsync(Title);
 0140        await OnTitleEdited.InvokeAsync();
 0141    }
 142
 143    private async Task OnTitleKeyDownInternal(KeyboardEventArgs e)
 144    {
 0145        if (e.Key == "Enter")
 146        {
 0147            await OnEnterPressed.InvokeAsync();
 148        }
 0149    }
 150
 151    private async Task OnIconChangedInternal(string? newIcon)
 152    {
 0153        await OnIconChanged.InvokeAsync(newIcon);
 0154    }
 155
 156    private async Task OnMetadataToggleClick()
 157    {
 0158        await OnMetadataToggle.InvokeAsync();
 0159    }
 160
 161    #endregion
 162}