Repository navigation
Let exit animations be played by custom BitModal containers (#13558) #13767
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
Merged
msynk
merged 4 commits into
develop
from
msynk/13558-blazorui-modal-container-play-exit-animation
Oct 11, 2026
Merged
Changes from all commits
Commits
Show all changes
4 commits
Select commit
Hold shift + click to select a range
b49ae1a
let exit animations be played by custom BitModal containers #13558
msynk 3927dbf
code review
msynk eda1884
Merge branch 'develop' of https://github.com/bitfoundation/bitplatfor…
msynk e12fa8a
Merge branch 'develop' of https://github.com/bitfoundation/bitplatfor…
msynk File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
11 changes: 1 addition & 10 deletions
11
src/BlazorUI/Bit.BlazorUI/Components/Surfaces/Modal/BitModalContainer.razor
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,13 +1,4 @@ | ||
| @namespace Bit.BlazorUI | ||
| @inherits BitModalContainerBase<BitModalReference, BitModalParameters> | ||
|
|
||
| @foreach (var modalReference in _modalRefs) | ||
| { | ||
| <CascadingValue @key="modalReference.Id" Value="modalReference"> | ||
| <CascadingValue Name="@BitModalExit.CascadingName" Value="GetExit(modalReference)" IsFixed="true"> | ||
| <CascadingValue Value="GetMergedParameters(modalReference)"> | ||
| @modalReference.Modal | ||
| </CascadingValue> | ||
| </CascadingValue> | ||
| </CascadingValue> | ||
| } | ||
| @RenderModals() |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
109 changes: 109 additions & 0 deletions
109
src/BlazorUI/Bit.BlazorUI/Components/Surfaces/Modal/BitModalContainerEntry.cs
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,109 @@ | ||
| using Microsoft.AspNetCore.Components.Rendering; | ||
|
|
||
| namespace Bit.BlazorUI; | ||
|
|
||
| /// <summary> | ||
| /// One modal of a container, with everything it expects from its container cascaded to it: its reference, what | ||
| /// it reports its exit through (<see cref="BitModalExit"/>) and its merged parameters. | ||
| /// </summary> | ||
| /// <remarks> | ||
| /// It renders only when something of its own modal changed - the merged parameters, a refresh, the modal closing - | ||
| /// and not on every render of the container. A cascaded value of a class type is handed again to everything | ||
| /// reading it whenever the cascade renders, and with it the previous parameters of each of those components, a | ||
| /// moment before their parent hands them the current ones: rendered with the container, every modal and every | ||
| /// content reading its reference would go through that each time another modal is shown or closed. Rendered only | ||
| /// when its own modal changed, the cascades still hand the content its reference again then, so content reading | ||
| /// the state of the reference (its parameters, <c>IsClosed</c>) stays current. | ||
| /// </remarks> | ||
| internal sealed class BitModalContainerEntry<TReference, TParameters> : IComponent | ||
| where TReference : BitModalReferenceBase<TReference, TParameters> | ||
| where TParameters : class, new() | ||
| { | ||
| internal const string ReferenceName = "Reference"; | ||
| internal const string ParametersName = "Parameters"; | ||
| internal const string ExitName = "Exit"; | ||
| internal const string VersionName = "Version"; | ||
|
|
||
| private readonly RenderFragment _renderFragment; | ||
|
|
||
| private RenderHandle _renderHandle; | ||
| private bool _hasRendered; | ||
|
|
||
| private TReference _reference = default!; | ||
| private TParameters? _parameters; | ||
| private BitModalExit _exit = default!; | ||
| private int _version; | ||
| private bool _isClosed; | ||
|
|
||
|
|
||
|
|
||
| public BitModalContainerEntry() | ||
| { | ||
| _renderFragment = BuildRenderTree; | ||
| } | ||
|
|
||
|
|
||
|
|
||
| public void Attach(RenderHandle renderHandle) | ||
| { | ||
| _renderHandle = renderHandle; | ||
| } | ||
|
|
||
| public Task SetParametersAsync(ParameterView parameters) | ||
| { | ||
| var reference = parameters.GetValueOrDefault<TReference>(ReferenceName)!; | ||
| var merged = parameters.GetValueOrDefault<TParameters?>(ParametersName); | ||
| var exit = parameters.GetValueOrDefault<BitModalExit>(ExitName)!; | ||
| var version = parameters.GetValueOrDefault<int>(VersionName); | ||
|
|
||
| if (_hasRendered && | ||
| ReferenceEquals(reference, _reference) && | ||
| ReferenceEquals(merged, _parameters) && | ||
| ReferenceEquals(exit, _exit) && | ||
| version == _version && | ||
| reference.IsClosed == _isClosed) | ||
| { | ||
| return Task.CompletedTask; | ||
| } | ||
|
|
||
| _reference = reference; | ||
| _parameters = merged; | ||
| _exit = exit; | ||
| _version = version; | ||
| // The modal reads it when it renders (the modal is open until the service closes it), so a modal that has | ||
| // just closed has to render once more to start its exit animation. | ||
| _isClosed = reference.IsClosed; | ||
| _hasRendered = true; | ||
|
|
||
| _renderHandle.Render(_renderFragment); | ||
|
|
||
| return Task.CompletedTask; | ||
| } | ||
|
|
||
|
|
||
|
|
||
| private void BuildRenderTree(RenderTreeBuilder builder) | ||
| { | ||
| // Not fixed: the content is handed its reference again whenever its modal changed, so that content reading | ||
| // its state stays current. | ||
| builder.OpenComponent<CascadingValue<TReference>>(0); | ||
| builder.AddComponentParameter(1, nameof(CascadingValue<TReference>.Value), _reference); | ||
| builder.AddComponentParameter(2, nameof(CascadingValue<TReference>.ChildContent), (RenderFragment)(exitBuilder => | ||
| { | ||
| exitBuilder.OpenComponent<CascadingValue<BitModalExit>>(0); | ||
| exitBuilder.AddComponentParameter(1, nameof(CascadingValue<BitModalExit>.Name), BitModalExit.CascadingName); | ||
| exitBuilder.AddComponentParameter(2, nameof(CascadingValue<BitModalExit>.Value), _exit); | ||
| exitBuilder.AddComponentParameter(3, nameof(CascadingValue<BitModalExit>.IsFixed), true); | ||
| exitBuilder.AddComponentParameter(4, nameof(CascadingValue<BitModalExit>.ChildContent), (RenderFragment)(parametersBuilder => | ||
| { | ||
| // Not fixed: a refresh hands the modal a new set of merged parameters, which a fixed value would never pass on. | ||
| parametersBuilder.OpenComponent<CascadingValue<TParameters?>>(0); | ||
| parametersBuilder.AddComponentParameter(1, nameof(CascadingValue<TParameters?>.Value), _parameters); | ||
| parametersBuilder.AddComponentParameter(2, nameof(CascadingValue<TParameters?>.ChildContent), _reference.Modal); | ||
| parametersBuilder.CloseComponent(); | ||
| })); | ||
| exitBuilder.CloseComponent(); | ||
| })); | ||
| builder.CloseComponent(); | ||
| } | ||
| } | ||
29 changes: 29 additions & 0 deletions
29
src/BlazorUI/Bit.BlazorUI/Components/Surfaces/Modal/BitModalContainerItem.cs
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,29 @@ | ||
| namespace Bit.BlazorUI; | ||
|
|
||
| /// <summary> | ||
| /// One modal of a container that puts markup of its own around each modal it renders: what the template handed to | ||
| /// <see cref="BitModalContainerBase{TReference, TParameters}.RenderModals(RenderFragment{BitModalContainerItem{TReference}})"/> | ||
| /// is rendered with, once per modal. | ||
| /// </summary> | ||
| /// <typeparam name="TReference">The modal reference type of the container.</typeparam> | ||
| public sealed class BitModalContainerItem<TReference> where TReference : class | ||
| { | ||
| internal BitModalContainerItem(TReference reference, RenderFragment modal) | ||
| { | ||
| Reference = reference; | ||
| Modal = modal; | ||
| } | ||
|
|
||
|
|
||
|
|
||
| /// <summary> | ||
| /// The reference of the modal - closed while it plays its exit animation, which is the only time a container | ||
| /// holds a closed one. | ||
| /// </summary> | ||
| public TReference Reference { get; } | ||
|
|
||
| /// <summary> | ||
| /// The modal itself, with everything it expects from its container: render it once, where it goes. | ||
| /// </summary> | ||
| public RenderFragment Modal { get; } | ||
| } |
34 changes: 34 additions & 0 deletions
34
src/BlazorUI/Bit.BlazorUI/Components/Surfaces/Modal/BitModalContainerSlot.cs
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,34 @@ | ||
| namespace Bit.BlazorUI; | ||
|
|
||
| /// <summary> | ||
| /// What a container that puts markup of its own around each modal renders that markup in: a component of no markup | ||
| /// of its own, there to carry the modal's key. A key is only told apart from the keys of its siblings, and the | ||
| /// markup of a template is not one of the container's own frames to key, so the slot is. | ||
| /// </summary> | ||
| /// <remarks> | ||
| /// It renders every time the container does, so the markup around a modal follows the state of the container; the | ||
| /// modal inside it is the <see cref="BitModalContainerEntry{TReference, TParameters}"/>, which renders only when its | ||
| /// own modal changed. | ||
| /// </remarks> | ||
| internal sealed class BitModalContainerSlot : IComponent | ||
| { | ||
| internal const string ChildContentName = "ChildContent"; | ||
|
|
||
| private RenderHandle _renderHandle; | ||
|
|
||
|
|
||
|
|
||
| public void Attach(RenderHandle renderHandle) | ||
| { | ||
| _renderHandle = renderHandle; | ||
| } | ||
|
|
||
| public Task SetParametersAsync(ParameterView parameters) | ||
| { | ||
| var childContent = parameters.GetValueOrDefault<RenderFragment>(ChildContentName); | ||
|
|
||
| _renderHandle.Render(childContent ?? (_ => { })); | ||
|
|
||
| return Task.CompletedTask; | ||
| } | ||
| } |
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.