# PanelService
**Also known as:** SheetService, DrawerService
## Description
BitPanelService shows a BitPanel with any content - a component, a Type or markup - from anywhere in the app: a service, a view model, a handler. It is the BitModalService of panels: each Show hands back a BitPanelReference that closes the panel, completes with its answer and says whether the user dismissed it, and a BitPanelParameters customizes the panel around the content with the options of BitPanel - its placement, its size, its snap points - plus a close guard and a navigation policy.
## Parameters
| Name | Type | Default Value | Description |
| :--- | :--- | :------------ | :---------- |
| OnAddModal | `event Func<BitPanelReference, Task>?` | | Raised for every panel shown through this service, whoever showed it. |
| OnCloseModal | `event Func<BitPanelReference, Task>?` | | Raised once for every panel of this service that closes, however it closed. |
| IsContainerAvailable | `bool` | | Whether a BitPanelContainer is currently mounted for this service, i.e. whether a Show call right now would render its panel. |
| OpenModals | `IReadOnlyList<BitPanelReference>` | | A snapshot of the panels this service currently has open, in the order they were opened. |
| GetModal | `BitPanelReference? (string? id)` | | The open panel with the given id, or null when there is none. |
| Close | `Task (BitPanelReference panel, object? result = null)` | | Closes an open panel with the result its Result task completes with. This is the application closing the panel, so the CanClose guard is not asked. |
| TryClose | `Task<bool> (BitPanelReference panel, object? result)` | | Asks a panel to close and reports whether it did: a panel whose CanClose guard turns the close down stays open and this answers false. |
| CloseAll | `Task` | | Closes every panel this service currently has open, each with a null result, the last one opened first. The CanClose guards are not asked. |
| Refresh | `Task (BitPanelReference? panel)` | | Re-renders the open panels, after their parameters were changed in place. Without an argument it refreshes every open panel. |
| Show<T> | `Task<BitPanelReference> (Dictionary<string, object>? parameters, BitPanelParameters? panelParameters, bool persistent)` | | Shows a panel with the component T as its content, given the parameters. A persistent panel outlives the page it was opened from and a remount of its container. Overloads take the parameters as a BitModalContentParameters<T>, a factory handed the panel's reference, or no parameters at all. |
| Show | `Task<BitPanelReference> (Type componentType, ...)` | | The same, with the type of the content known at run time. |
| Show | `Task<BitPanelReference> (RenderFragment content, BitPanelParameters? panelParameters, bool persistent)` | | Shows a panel with markup as its content. An overload takes a RenderFragment<BitPanelReference>, handed the panel's own reference. |
| AriaLabel | `string?` | null | Gets or sets the accessible label for the component, used by assistive technologies. |
| Class | `string?` | null | Gets or sets the CSS class name(s) to apply to the rendered element. |
| Dir | `BitDir?` | null | Gets or sets the text directionality for the component's content. |
| Disabled | `bool` | false | Gets or sets a value indicating whether the component is disabled and cannot respond to user interaction. |
| ForceAnimation | `bool` | false | Gets or sets a value indicating whether the component's animations play at their full duration even when reduced motion is requested. |
| HtmlAttributes | `Dictionary<string, object>` | new Dictionary<string, object>() | Captures additional HTML attributes to be applied to the rendered element, in addition to the component's parameters. An attribute written on the component wins over an entry of the same name. |
| Id | `string?` | null | Gets or sets the unique identifier for the component's root element. |
| Style | `string?` | null | Gets or sets the CSS style string to apply to the rendered element. |
| TabIndex | `string?` | null | Gets or sets the tab order index for the component when navigating with the keyboard. |
| Visibility | `BitVisibility` | BitVisibility.Visible | Gets or sets the visibility state (visible, hidden, or collapsed) of the component. |
## Public Members
| Name | Type | Default Value | Description |
| :--- | :--- | :------------ | :---------- |
| UniqueId | `Guid` | Guid.NewGuid() | Gets the readonly unique identifier for the component's root element, assigned when the component instance is constructed. |
| RootElement | `ElementReference` | | Gets the reference to the root HTML element associated with this component. |
## Enums
### BitVisibility Enum
| Name | Value | Description |
| :--- | :--- | :---------- |
| Visible | 0 | The content of the component is visible. |
| Hidden | 1 | The content of the component is hidden, but the space it takes on the page remains (visibility:hidden). |
| Collapsed | 2 | The component is hidden (display:none). |
### BitDir Enum
| Name | Value | Description |
| :--- | :--- | :---------- |
| Ltr | 0 | Ltr (left to right) is to be used for languages that are written from the left to the right (like English). |
| Rtl | 1 | Rtl (right to left) is to be used for languages that are written from the right to the left (like Arabic). |
| Auto | 2 | Auto lets the user agent decide. It uses a basic algorithm as it parses the characters inside the element until it finds a character with a strong directionality, then applies that directionality to the whole element. |
## Sub Classes
### BitPanelReference Properties
| Name | Type | Default Value | Description |
| :--- | :--- | :------------ | :---------- |
| Id | `string` | | The unique id of the panel. |
| Persistent | `bool` | | Whether the panel outlives the page it was opened from. |
| IsClosed | `bool` | | Whether the panel has been closed. |
| IsDismissed | `bool` | | Whether the panel was closed by the user rather than by the application: the overlay, the Escape key, the close button or a swipe. |
| Content | `object?` | | The instance of the component rendered as the content, once it is rendered. |
| Parameters | `BitPanelParameters?` | | The parameters the panel was shown with. |
| Result | `Task<object?>` | | Completes with what the panel was closed with, or null. |
| Rendered | `Task<bool>` | | Completes with true once a container has rendered the panel, or false when it is closed before that ever happens. |
| Close / CloseWith | `Task (object? result)` | | Closes the panel, with no result or with the given one. The CanClose guard is not asked. |
| TryClose | `Task<bool> (object? result = null)` | | Asks the panel to close, putting it to its CanClose guard first, and reports whether it did. |
| Dismiss | `Task<bool>` | | Closes the panel as the user would, for the content's own cancel action: put to the guard, and reported as dismissed. |
| Update | `Task (BitPanelParameters? parameters) / Task (Action<BitPanelParameters> change)` | | Replaces the parameters of the open panel, or changes the ones it names, and re-renders it. |
| GetResult<T> / GetContentAsync<T> | `Task<T?>` | | The result as a T, and the content once it is rendered as a T. |
### BitPanelContainer Properties
| Name | Type | Default Value | Description |
| :--- | :--- | :------------ | :---------- |
| Service | `BitPanelService?` | null | The service whose panels this container renders, in place of the registered one. |
| ModalParameters | `BitPanelParameters` | new() | The defaults of every panel the container renders: a panel's own parameters win over them, one by one. |
### BitPanelParameters Properties
The options a panel is shown with. Every parameter of BitPanel a panel shown from code can be given - including Class, Style, Dir, AriaLabel and Disabled, and its placement, size and snap points - has a nullable counterpart here (null means "not set": the container's value, then the panel's own default is used), plus the options only a service can offer:
| Name | Type | Default Value | Description |
| :--- | :--- | :------------ | :---------- |
| SnapPoint | `int?` | null | The snap point a bottom sheet opens at. The sheet keeps the one the user moves it to from there on. |
| CanClose | `Func<Task<bool>>?` | null | The guard every dismissal by the user and every TryClose is put to: answering false keeps the panel open. Close is never put to it. |
| CloseOnNavigation | `bool?` | null | Whether the panel closes when the app navigates somewhere else, which it does by default (a persistent panel only closes when this is set to true). Only a change of path counts; a query string or a fragment changed on the same page does not. |
## Examples
\n**Basic**:
```razor
@* in the layout *@
Show
```
```csharp
[AutoInject] private BitPanelService panelService = default!;
private async Task ShowPanel()
{
await panelService.Show(new BitPanelParameters { HeaderText = "Shown by the service", ShowCloseButton = true });
}
```
`PanelContent.razor`:
```razor
This content reaches its own panel through the cascaded BitPanelReference.
Ok
Cancel
@code {
[CascadingParameter] private BitPanelReference panelReference { get; set; } = default!;
}
```
\n**Placement**:
```razor
Start
End
Top
Bottom
```
```csharp
[AutoInject] private BitPanelService panelService = default!;
private async Task ShowAt(BitPlacement placement)
{
await panelService.Show(new BitPanelParameters
{
Placement = placement,
Size = 320,
HeaderText = $"From the {placement.ToString().ToLowerInvariant()}",
ShowCloseButton = true,
});
}
```
`PanelContent.razor`:
```razor
This content reaches its own panel through the cascaded BitPanelReference.
Ok
Cancel
@code {
[CascadingParameter] private BitPanelReference panelReference { get; set; } = default!;
}
```
\n**Bottom sheet**:
```razor
Show sheet
```
```csharp
[AutoInject] private BitPanelService panelService = default!;
private async Task ShowSheet()
{
await panelService.Show(new BitPanelParameters
{
Placement = BitPlacement.Bottom,
SnapPoints = [0.3, 0.6, 0.9],
SnapPoint = 1,
HeaderText = "A bottom sheet",
});
}
```
`PanelContent.razor`:
```razor
This content reaches its own panel through the cascaded BitPanelReference.
Ok
Cancel
@code {
[CascadingParameter] private BitPanelReference panelReference { get; set; } = default!;
}
```
\n**Awaiting a result**:
```razor
Pick a size
Picked: @pickedSize
```
```csharp
[AutoInject] private BitPanelService panelService = default!;
private string? pickedSize;
private async Task PickSize()
{
var panel = await panelService.Show(new BitPanelParameters { HeaderText = "Pick a size", ShowCloseButton = true });
var size = await panel.GetResult();
pickedSize = panel.IsDismissed ? "(dismissed)" : size;
}
```
`PickPanelContent.razor`:
```razor
@foreach (var size in sizes)
{
@size
}
@code {
private static readonly string[] sizes = ["Small", "Medium", "Large"];
[CascadingParameter] private BitPanelReference panelReference { get; set; } = default!;
}
```
\n**Markup as content**:
```razor
Show markup
@code {
// a template taking the panel's own reference, so the markup can close it
private async Task ShowMarkup()
{
await panelService.Show(panel => @Got it ,
new BitPanelParameters { HeaderText = "Markup that closes itself" });
}
}
```
```csharp
[AutoInject] private BitPanelService panelService = default!;
```
\n**Guarding the close**:
```razor
Show
```
```csharp
[AutoInject] private BitPanelService panelService = default!;
private bool hasUnsavedChanges = true;
private async Task ShowGuarded()
{
await panelService.Show(new BitPanelParameters
{
HeaderText = "Guarded",
ShowCloseButton = true,
CanClose = () => Task.FromResult(hasUnsavedChanges is false),
});
}
```
`PanelContent.razor`:
```razor
This content reaches its own panel through the cascaded BitPanelReference.
Ok
Cancel
@code {
[CascadingParameter] private BitPanelReference panelReference { get; set; } = default!;
}
```
\n**Updating an open panel**:
```razor
Show
```
```csharp
[AutoInject] private BitPanelService panelService = default!;
private async Task ShowCountdown()
{
var panel = await panelService.Show(new BitPanelParameters { HeaderText = "Closing in 5", ShowCloseButton = true });
for (var left = 4; left >= 0 && panel.IsClosed is false; left--)
{
await Task.Delay(1000);
if (left == 0)
{
await panel.Close();
}
else
{
await panel.Update(p => p.HeaderText = $"Closing in {left}");
}
}
}
```
`PanelContent.razor`:
```razor
This content reaches its own panel through the cascaded BitPanelReference.
Ok
Cancel
@code {
[CascadingParameter] private BitPanelReference panelReference { get; set; } = default!;
}
```