Files
Daybreak/Daybreak.Core/Views/Components/FocusView/BuildComponent.razor
T

151 lines
5.0 KiB
Plaintext

<div class="container backdrop-panel" @ref="this.containerElement" @onmouseleave="this.CloseSnippet">
@if (this.Context is not null)
{
<div class="component-title" @onmouseenter="this.CloseSnippet">
<div class="title-left">
<div class="title">
Builds
</div>
</div>
<div class="title-right">
<button class="load-button" title="Load current player build" @onclick="this.LoadPlayerSingleBuildClicked">
<FluentIcon Value="@(new Microsoft.FluentUI.AspNetCore.Components.Icons.Regular.Size16.Person())" />
</button>
<button class="load-button" title="Load current team build" @onclick="this.LoadPlayerTeamBuildClicked">
<FluentIcon Value="@(new Microsoft.FluentUI.AspNetCore.Components.Icons.Regular.Size16.PeopleTeam())" />
</button>
</div>
</div>
<div class="builds-list @((this.Context.IsInOutpost ? string.Empty : "read-only"))">
<Virtualize Items="this.Context.AvailableBuilds"
Context="build"
ItemSize="50">
<ItemContent>
<div class="build-entry"
@onclick="() => this.BuildClicked(build)"
@onmouseenter="(e) => this.OnMouseEnter(build, e)"
@onmousemove="this.OnMouseMove">
<div class="build-name">
@build.Name
</div>
</div>
</ItemContent>
</Virtualize>
</div>
@if (this.hoveredBuild is not null && this.showBuildSnippet)
{
<div class="build-snippet-overlay" style="top: @(this.snippetTop)px; left: @(this.snippetLeft)px;">
<BuildSnippet BuildEntry="this.hoveredBuild" />
</div>
}
}
else
{
<SpinnerWidget IsLoading="true" />
}
</div>
@using Daybreak.Shared.Models
@using Daybreak.Shared.Models.Builds
@using Microsoft.AspNetCore.Components.Web
@inject IJSRuntime JSRuntime
@code {
private bool showBuildSnippet = false;
private IBuildEntry? hoveredBuild;
private DotNetObjectReference<BuildComponent>? dotNetRef;
private ElementReference containerElement;
private ClientBoundingRect boundingRect = new ClientBoundingRect();
private double snippetTop = 0;
private double snippetLeft = 0;
[Parameter]
public required BuildComponentContext? Context { get; init; }
[Parameter]
public required Action<IBuildEntry> OnBuildClicked { get; init; }
[Parameter]
public required Action OnLoadPlayerSingleBuildClicked { get; init; }
[Parameter]
public required Action OnLoadPlayerTeamBuildClicked { get; init; }
protected override void OnInitialized()
{
this.dotNetRef = DotNetObjectReference.Create(this);
}
[JSInvokable]
public void HoverComplete()
{
if (this.hoveredBuild is not null)
{
this.showBuildSnippet = true;
this.StateHasChanged();
}
}
private void BuildClicked(IBuildEntry build)
{
this.OnBuildClicked(build);
}
private void LoadPlayerSingleBuildClicked()
{
this.OnLoadPlayerSingleBuildClicked();
}
private void LoadPlayerTeamBuildClicked()
{
this.OnLoadPlayerTeamBuildClicked();
}
private async void OnMouseEnter(IBuildEntry build, MouseEventArgs e)
{
if (this.dotNetRef is null)
{
return;
}
// Calculate position relative to the container
this.boundingRect = await this.JSRuntime.GetBoundingClientRect(this.containerElement);
this.snippetTop = e.ClientY - this.boundingRect.Top + 20;
this.snippetLeft = e.ClientX - this.boundingRect.Left + 10;
this.showBuildSnippet = false;
this.hoveredBuild = null;
await this.JSRuntime.HoverDelayStop();
await this.InvokeAsync(this.StateHasChanged);
this.showBuildSnippet = false;
this.hoveredBuild = build;
await this.JSRuntime.HoverDelayStart(this.dotNetRef, nameof(this.HoverComplete));
await this.InvokeAsync(this.StateHasChanged);
}
private void OnMouseMove(MouseEventArgs e)
{
// Reposition the snippet while hovering so that it pops up at the current mouse position
if (!this.showBuildSnippet)
{
this.snippetTop = e.ClientY - this.boundingRect.Top + 20;
this.snippetLeft = e.ClientX - this.boundingRect.Left + 10;
}
}
private async void CloseSnippet()
{
this.showBuildSnippet = false;
this.hoveredBuild = null;
await this.JSRuntime.HoverDelayStop();
}
public void Dispose()
{
if (this.dotNetRef is not null && this.showBuildSnippet)
{
this.showBuildSnippet = false;
this.hoveredBuild = null;
}
this.dotNetRef?.Dispose();
}
}