Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Refactor internal frame tracking & time handling #29

Merged
merged 19 commits into from
Sep 29, 2023
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
Next Next commit
rework FrameTracker to consistently trigger on next frame
  • Loading branch information
DavidVollmers committed Sep 27, 2023
commit e289b67cb581fbeb3a1adee16af7ea43183f1eef
38 changes: 22 additions & 16 deletions packages/Ignis.Components/FrameTracker.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,9 @@ internal class FrameTracker
{
private readonly IHostContext _hostContext;

private long _currentFrame;
private long? _frameToExecuteOn;
private IgnisComponentBase? _target;
private Action? _action;

public bool IsPending => _action != null;
Expand All @@ -13,27 +16,30 @@ public FrameTracker(IHostContext hostContext)
_hostContext = hostContext ?? throw new ArgumentNullException(nameof(hostContext));
}

public void ExecuteOnNextFrame(Action action, Action<bool> update)
public void ExecuteOnNextFrame(IgnisComponentBase target, Action action)
{
if (action == null) throw new ArgumentNullException(nameof(action));

_target = target ?? throw new ArgumentNullException(nameof(target));
_action = action ?? throw new ArgumentNullException(nameof(action));

// If we're server-side, we can just execute the action on the next render, otherwise we need to wait for the second render. (WebAssembly)
_action = _hostContext.IsServerSide
? action
: () =>
{
_action = action;

update(obj: false);
};
_frameToExecuteOn = _currentFrame + (_hostContext.IsServerSide ? 1 : 2);
}

public void OnAfterRender()
{
var action = _action;

_action = null;

action?.Invoke();
if (_currentFrame >= _frameToExecuteOn)
{
_frameToExecuteOn = null;

_action?.Invoke();

_action = null;
}
else if (_frameToExecuteOn.HasValue)
{
_target?.Update();
}

++_currentFrame;
}
}
5 changes: 2 additions & 3 deletions packages/Tailwind/Ignis.Components.HeadlessUI/Transition.cs
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,6 @@ public bool Show

[Parameter] public bool Appear { get; set; }

/// <inheritdoc />
[CascadingParameter] public IContentHost? Outlet { get; set; }

[CascadingParameter] public IMenu? Menu { get; set; }
Expand Down Expand Up @@ -221,7 +220,7 @@ private void WatchTransition(bool isEnter, Action? continueWith)
var startedTransitions = new List<ITransitionChild>();
var finishedTransitions = 0;

if (isEnter) base.EnterTransition(() => AggregateDialogs(true, ContinueWith));
if (isEnter) base.EnterTransition(() => AggregateDialogs(open: true, ContinueWith));
else ContinueWith();
return;

Expand All @@ -243,7 +242,7 @@ void ContinueWith()
if (finishedTransitions == startedTransitions.Count + 1)
{
if (isEnter) continueWith?.Invoke();
else AggregateDialogs(false, () => base.LeaveTransition(continueWith));
else AggregateDialogs(open: false, () => base.LeaveTransition(continueWith));
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -120,9 +120,9 @@

_state = state;

if (continueWith != null) FrameTracker.ExecuteOnNextFrame(continueWith, Update);

Check failure on line 123 in packages/Tailwind/Ignis.Components.HeadlessUI/TransitionBase.cs

View workflow job for this annotation

GitHub Actions / Test

Argument 1: cannot convert from 'System.Action' to 'Ignis.Components.IgnisComponentBase'

Check failure on line 123 in packages/Tailwind/Ignis.Components.HeadlessUI/TransitionBase.cs

View workflow job for this annotation

GitHub Actions / Test

Argument 2: cannot convert from 'method group' to 'System.Action'

Update(true);
Update(async: true);
}

/// <inheritdoc />
Expand Down
4 changes: 1 addition & 3 deletions tests/Ignis.Tests.Components.HeadlessUI/DialogTests.razor
Original file line number Diff line number Diff line change
Expand Up @@ -238,7 +238,7 @@
const string openButtonId = "open-button";
const string outletId = "dialog-outlet";

var cut = Render<CustomDialogTest>(@<CustomDialogTest/>);
var cut = Render(@<CustomDialogTest/>);

var outlet = cut.Find($"#{outletId}");
Assert.Empty(outlet.Children);
Expand All @@ -247,15 +247,13 @@
openButton.Click();

await Task.Delay(400);
cut.Render();

var closeButton = cut.Find($"#{closeButtonId}");
Assert.True(outlet.Contains(closeButton));

closeButton.Click();

await Task.Delay(300);
cut.Render();

outlet = cut.Find($"#{outletId}");
Assert.Empty(outlet.Children);
Expand Down
40 changes: 40 additions & 0 deletions tests/Ignis.Tests.Components.HeadlessUI/ListboxInDialog.razor
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
@inherits IgnisRigidComponentBase

<CustomDialog Open="Open" OpenChanged="OpenChanged">
<Listbox @bind-Value="_selectedType">
<ListboxButton Id="listbox-button">
</ListboxButton>
<Transition AsComponent="typeof(Fragment)"
Leave="duration-100"
Context="transition">
<ListboxOptions @attributes="transition.Attributes">
@foreach (var option in _options)
{
<ListboxOption @key="option.Name"
Value="option"
Context="_">
@option.Name
</ListboxOption>
}
</ListboxOptions>
</Transition>
</Listbox>
</CustomDialog>

@code
{
private readonly Type[] _options =
{
typeof(string),
typeof(int),
typeof(bool)
};

private Type? _selectedType;

[Parameter]
public bool Open { get; set; }

[Parameter]
public EventCallback<bool> OpenChanged { get; set; }
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
<ListboxInDialog @bind-Open="_isOpen"></ListboxInDialog>
<button id="open-button" @onclick="@(() => _isOpen = true)"></button>

@code
{
bool _isOpen;
}
22 changes: 22 additions & 0 deletions tests/Ignis.Tests.Components.HeadlessUI/ListboxTests.razor
Original file line number Diff line number Diff line change
Expand Up @@ -43,4 +43,26 @@
var listbox = cut.FindComponent<Listbox<object>>();
Assert.False(listbox.Instance.IsOpen);
}

[Fact]
public async Task ListboxInDialog()
{
Services.AddIgnis();
Services.AddSingleton<IHostContext, TestHostContext>();

JSInterop.Mode = JSRuntimeMode.Loose;

const string openButtonId = "open-button";
const string listboxButtonId = "listbox-button";

var cut = Render(@<ListboxInDialogTest></ListboxInDialogTest>);

var openButton = cut.Find($"#{openButtonId}");
openButton.Click();

await Task.Delay(400);

var listboxButton = cut.Find($"#{listboxButtonId}");
listboxButton.Click();
}
}