Files
Nexus.Reader/src/NexusReader.UI.Shared/Components/Organisms/MobileReaderToolbar.razor
T

156 lines
6.0 KiB
Plaintext

@using NexusReader.UI.Shared.Components.Atoms
@using NexusReader.UI.Shared.Services
@using NexusReader.Application.Utilities
@namespace NexusReader.UI.Shared.Components.Organisms
@inject IReaderInteractionService InteractionService
<div class="nexus-unified-mobile-toolbar">
<!-- LEFT SLOT: Progress & Section Checkpoints -->
<div class="toolbar-slot left-slot" @onclick="ToggleCheckpoints" title="Rozdziały i checkpoints">
<div class="progress-ring-wrapper">
<svg class="progress-ring" width="38" height="38">
<circle class="progress-ring-track" stroke="rgba(255,255,255,0.06)" stroke-width="2.5" fill="transparent" r="16" cx="19" cy="19" />
<circle class="progress-ring-indicator" stroke="var(--nexus-neon, #00FF99)" stroke-width="2.5" fill="transparent" r="16" cx="19" cy="19"
stroke-dasharray="100.53" stroke-dashoffset="@GetDashOffset()" />
</svg>
<span class="progress-text">@ScrollPercentage%</span>
</div>
<div class="progress-info">
<span class="slot-label">Postęp</span>
<span class="slot-desc">Checkpoints</span>
</div>
</div>
<!-- CENTER SLOT: Global AI Assistant Glowing Trigger -->
<div class="toolbar-slot center-slot">
<button class="btn-nexus-ai-core" @onclick="HandleAssistantClick" aria-label="Asystent AI">
<div class="pulse-ring"></div>
<div class="pulse-ring-outer"></div>
<NexusIcon Name="robot" Size="22" Class="ai-core-icon" />
</button>
</div>
<!-- RIGHT SLOT: Context View Toggles -->
<div class="toolbar-slot right-slot">
<button class="nav-toggle-btn @(ActiveTab == MobileReaderTab.Reader ? "active" : "")"
@onclick="() => ChangeTab(MobileReaderTab.Reader)"
aria-label="Tekst">
<NexusIcon Name="book-open" Size="18" />
<span>Tekst</span>
</button>
<button class="nav-toggle-btn @(ActiveTab == MobileReaderTab.Graph ? "active" : "")"
@onclick="() => ChangeTab(MobileReaderTab.Graph)"
aria-label="Graf">
<NexusIcon Name="share-2" Size="18" />
<span>Graf</span>
</button>
<button class="nav-toggle-btn @(ActiveTab == MobileReaderTab.Concepts ? "active" : "")"
@onclick="() => ChangeTab(MobileReaderTab.Concepts)"
aria-label="Mapa">
<NexusIcon Name="map" Size="18" />
<span>Mapa</span>
</button>
</div>
</div>
<!-- SECTION CHECKPOINTS OVERLAY -->
<div class="checkpoints-overlay @(IsCheckpointsOpen ? "is-open" : "")">
<div class="checkpoints-backdrop" @onclick="ToggleCheckpoints"></div>
<div class="checkpoints-sheet">
<div class="sheet-drag-handle"></div>
<header class="checkpoints-header">
<h4>Checkpoints Sekcji</h4>
<button class="close-checkpoints-btn" @onclick="ToggleCheckpoints">
<NexusIcon Name="close" Size="16" />
</button>
</header>
<div class="checkpoints-body">
@if (Checkpoints == null || !Checkpoints.Any())
{
<div class="empty-checkpoints">
<NexusIcon Name="info" Size="20" />
<p>Brak punktów kontrolnych w tym rozdziale.</p>
</div>
}
else
{
<div class="checkpoints-list">
@foreach (var cp in Checkpoints)
{
var isCurrent = cp == InteractionService.CurrentBlockId;
<div class="checkpoint-item @(isCurrent ? "active" : "")" @onclick="() => SelectCheckpoint(cp)">
<div class="checkpoint-indicator">
<div class="indicator-dot"></div>
<div class="indicator-line"></div>
</div>
<div class="checkpoint-details">
<span class="checkpoint-id">@cp.ToUpper()</span>
<span class="checkpoint-label">@(isCurrent ? "Aktualna sekcja" : "Przejdź do sekcji")</span>
</div>
<NexusIcon Name="chevron-right" Size="14" Class="arrow-icon" />
</div>
}
</div>
}
</div>
</div>
</div>
@code {
[Parameter] public int ScrollPercentage { get; set; }
[Parameter] public MobileReaderTab ActiveTab { get; set; }
[Parameter] public EventCallback<MobileReaderTab> OnTabChanged { get; set; }
[Parameter] public EventCallback OnAssistantClick { get; set; }
[Parameter] public List<string> Checkpoints { get; set; } = new();
private bool IsCheckpointsOpen { get; set; }
public enum MobileReaderTab
{
Reader,
Graph,
Concepts
}
private double GetDashOffset()
{
// Circumference of r=16 is 2 * pi * 16 = 100.53
double circumference = 100.53;
double progress = Math.Clamp(ScrollPercentage, 0, 100);
return circumference - (progress / 100.0) * circumference;
}
private void ToggleCheckpoints()
{
IsCheckpointsOpen = !IsCheckpointsOpen;
}
private async Task SelectCheckpoint(string checkpointId)
{
IsCheckpointsOpen = false;
// Scroll to the targeted block
await InteractionService.RequestScrollToBlock(checkpointId);
// Ensure user is on the text reading tab to see the scroll happen
if (ActiveTab != MobileReaderTab.Reader)
{
await ChangeTab(MobileReaderTab.Reader);
}
}
private async Task ChangeTab(MobileReaderTab tab)
{
if (OnTabChanged.HasDelegate)
{
await OnTabChanged.InvokeAsync(tab);
}
}
private async Task HandleAssistantClick()
{
if (OnAssistantClick.HasDelegate)
{
await OnAssistantClick.InvokeAsync();
}
}
}