Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
40 changes: 37 additions & 3 deletions src/LumexUI/Components/Popover/LumexPopover.razor.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@
// LumexUI licenses this file to you under the MIT license
// See the license here https://github.com/LumexUI/lumexui/blob/main/LICENSE

using System.Threading.Tasks;

using LumexUI.Common;
using LumexUI.Extensions;
using LumexUI.Styles;
Expand Down Expand Up @@ -99,8 +101,10 @@ public partial class LumexPopover : LumexComponentBase, ISlotComponent<PopoverSl
/// </summary>
[Parameter] public PopoverSlots? Classes { get; set; }

internal bool IsVisible { get; private set; }
internal bool IsTooltip { get; private set; }
internal PopoverOptions Options { get; private set; }
internal PopoverState State { get; private set; } = PopoverState.Closed;
internal Dictionary<string, ComponentSlot> Slots { get; private set; } = [];

private readonly PopoverContext _context;
Expand All @@ -126,6 +130,11 @@ protected override void OnParametersSet()
IsTooltip = value is "tooltip";
}

if (Open && State is PopoverState.Closing or PopoverState.Closed )
{
State = PopoverState.Opening;
}

Options = new PopoverOptions( this );

var popover = Popover.Style( TwMerge );
Expand All @@ -135,6 +144,7 @@ protected override void OnParametersSet()
[nameof( Color )] = Color.ToString(),
[nameof( Radius )] = Radius.ToString(),
[nameof( Shadow )] = Shadow.ToString(),
[nameof( State )] = State.ToString(),
} );
}

Expand All @@ -151,21 +161,40 @@ internal async Task TriggerAsync()
await CloseAsync();
}

StateHasChanged();
await InvokeAsync( StateHasChanged );
}

internal Task OpenAsync()
internal async Task OpenAsync()
{
State = PopoverState.Opening;
await InvokeAsync( StateHasChanged );

await Task.Delay( 100 ); // <-- I don't know how to avoid this. The animation works from the second time forward
Copy link
Contributor Author

@Denny09310 Denny09310 Oct 16, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please see this comment. I don't know how to solve it


Open = true;
return OpenChanged.InvokeAsync( true );
await OpenChanged.InvokeAsync( true );
}

internal Task CloseAsync()
{
State = PopoverState.Closing;

Open = false;
return OpenChanged.InvokeAsync( false );
}

internal void SetTransitionState()
{
if( State is PopoverState.Opening )
{
State = PopoverState.Opened;
}
else if( State is PopoverState.Closing )
{
State = PopoverState.Closed;
}
}

/// <summary>
/// Represents the configuration options for a <see cref="LumexPopover"/> component.
/// </summary>
Expand All @@ -177,4 +206,9 @@ internal readonly struct PopoverOptions( LumexPopover popover )
public bool MatchRefWidth { get; } = popover.MatchRefWidth;
public string Placement { get; } = popover.Placement.ToDescription();
}

internal enum PopoverState
{
Closed, Opening, Opened, Closing
}
}
11 changes: 8 additions & 3 deletions src/LumexUI/Components/Popover/LumexPopoverContent.razor
Original file line number Diff line number Diff line change
@@ -1,17 +1,22 @@
@namespace LumexUI
@inherits LumexComponentBase

@if( Popover.Open )
@using PopoverState = LumexPopover.PopoverState;

@if( Popover.State is not PopoverState.Closed )
{
<PopoverWrapper Class="@Popover.Slots["Wrapper"]()">
<PopoverWrapper Class="@Popover.Slots["Wrapper"]()"
data-popover-open="@Popover.Open.ToAttributeValue()"
data-popover-wrapper
@ontransitionend="HandleTransitionEnd">
<div role="dialog"
class="@Popover.Slots["Base"]( Popover.Classes?.Base, Popover.Class )"
tabindex="-1"
data-slot="base"
data-open="@Popover.Open.ToAttributeValue()"
@attributes="@Popover.AdditionalAttributes">
<LumexComponent As="@As"
Class="@Popover.Slots["Content"](Popover.Classes?.Content, Class)"
Class="@Popover.Slots["Content"]( Popover.Classes?.Content, Class )"
Style="@RootStyle"
data-slot="content"
@attributes="@AdditionalAttributes">
Expand Down
12 changes: 11 additions & 1 deletion src/LumexUI/Components/Popover/LumexPopoverContent.razor.cs
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,8 @@
/// </summary>
[Parameter] public RenderFragment? ChildContent { get; set; }

[Parameter] public EventCallback OnTransitionEnd { get; set; }

Check warning on line 22 in src/LumexUI/Components/Popover/LumexPopoverContent.razor.cs

View workflow job for this annotation

GitHub Actions / build-test

Missing XML comment for publicly visible type or member 'LumexPopoverContent.OnTransitionEnd'

[CascadingParameter] internal PopoverContext Context { get; set; } = default!;

private LumexPopover Popover => Context.Owner;
Expand All @@ -28,4 +30,12 @@
{
ContextNullException.ThrowIfNull( Context, nameof( LumexPopoverContent ) );
}
}

private async Task HandleTransitionEnd( EventArgs e )
{
Console.WriteLine("Transition end: {0}", Popover.State);

Popover.SetTransitionState();
await OnTransitionEnd.InvokeAsync();
}
}
3 changes: 2 additions & 1 deletion src/LumexUI/Infrastructure/EventHandlers.cs
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ namespace LumexUI.Infrastructure;
/// Configures event handlers for the components.
/// </summary>
[EventHandler( "onclickoutside", typeof( EventArgs ), enableStopPropagation: true, enablePreventDefault: true )]
[EventHandler( "ontransitionend", typeof( EventArgs ), enableStopPropagation: true, enablePreventDefault: true )]
public static class EventHandlers
{
}
}
3 changes: 2 additions & 1 deletion src/LumexUI/Styles/Popover.cs
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,8 @@ public static ComponentVariant Style( TwMerge twMerge )
.ToString(),

["Wrapper"] = new ElementClass()
.Add( "animate-enter" )
.Add( "transition-[opacity,scale] duration-300 ease opacity-0 scale-85" )
.Add( "data-[popover-open=true]:opacity-100 data-[popover-open=true]:scale-100" )
.ToString(),

[nameof( PopoverSlots.Content )] = new ElementClass()
Expand Down
13 changes: 12 additions & 1 deletion src/LumexUI/wwwroot/js/LumexUI.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,4 +8,15 @@ export const Lumex = {
elementReference
};

window['Lumex'] = Lumex
window['Lumex'] = Lumex

Blazor.registerCustomEventType('animationend', {
browserEventName: 'animationend',
createEventArgs: event => {
return {
AnimationName: event.animationName,
ElapsedTime: event.elapsedTime,
PseudoElement: event.pseudoElement
};
}
})
11 changes: 9 additions & 2 deletions tests/LumexUI.Tests/Components/Dropdown/DropdownTests.razor
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
@namespace LumexUI.Tests.Components
@using LumexUI.Infrastructure
@inherits TestContext

@code {
Expand Down Expand Up @@ -99,7 +100,7 @@
</LumexDropdownMenu>
</LumexDropdown>
</text>
);
);

var trigger = cut.Find( "[data-popovertarget]" );
trigger.Click();
Expand Down Expand Up @@ -133,7 +134,13 @@
var menuItems = cut.FindAll( "li" );
menuItems[0].Click();

var menu = cut.FindByTestId( "menu-test" );
var popover = cut.Find("[data-popover-wrapper]");
popover.TriggerEvent("onanimationend", new AnimationEventArgs

Check failure on line 138 in tests/LumexUI.Tests/Components/Dropdown/DropdownTests.razor

View workflow job for this annotation

GitHub Actions / build-test

The type or namespace name 'AnimationEventArgs' could not be found (are you missing a using directive or an assembly reference?)

Check failure on line 138 in tests/LumexUI.Tests/Components/Dropdown/DropdownTests.razor

View workflow job for this annotation

GitHub Actions / build-test

The type or namespace name 'AnimationEventArgs' could not be found (are you missing a using directive or an assembly reference?)
{
AnimationName = "animate-exit"
});

var menu = cut.FindByTestId("menu-test");
menu.Should().BeNull();
}

Expand Down
Loading