Skip to content

Commit

Permalink
Add MBPopover (#1278)
Browse files Browse the repository at this point in the history
* Add MBPopover

* Update docs table of contents

* Release notes
  • Loading branch information
simonziegler authored Mar 18, 2024
1 parent 97d801b commit eecc417
Show file tree
Hide file tree
Showing 19 changed files with 554 additions and 100 deletions.
94 changes: 94 additions & 0 deletions Material.Blazor.Website/Pages/Popover.razor
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
@page "/popover"

@inject IMBToastService ToastService

<DemonstrationPage ComponentAndPageName="Popover"
MaterialIOPage="mdc-menu-surface#menu-surface"
Title="Popover Demonstration">
<Description>
<p>
Demonstrates a popover. Popovers have the same styling and UX as menu surfaces, but with control similar to dialogs. This
means lazy content rendering (for substantially improved page load speed) and late instantiation of components to ensure
correct dimensions after opening, which is also similar to dialogs. You can observe this in action via the slider in this
demonstration, which would fail to render correctly within a menu surface.
</p>
</Description>

<PageContent>
<div class="mdc-layout-grid__cell--span-4">
<MBCard AutoStyled="true">
<Primary>
<h2 class="mb-card__title mdc-typography mdc-typography--headline6">
Popover Example
</h2>

<h3 class="mb-card__subtitle mdc-typography mdc-typography--subtitle2">
Allows the definition of freeform menus. Click the button to open the menu.
</h3>

<p>
<MBIconButton @onclick="@OpenMenuSurfaceAsync" Icon="account_circle" />
<div class="mdc-menu-surface--anchor">
<MBPopover @ref="PopoverElement">
<div class="mdc-layout-grid__inner" style="padding: 20px; width: 600px;">
<div class="mdc-layout-grid__cell--span-12 mdc-typography mdc-typography--headline6">
Alice Smith
</div>

<div class="mdc-layout-grid__cell--span-12 mdc-typography mdc-typography--subtitle1">
alice.smith@domain.com
</div>

<div class="mdc-layout-grid__cell--span-12">
<MBIconButton Icon="logout" @onclick="@(() => OnClick("logout"))" />
<MBIconButton Icon="settings" @onclick="@(() => OnClick("settings"))" />
</div>

<div class="mdc-layout-grid__cell--span-12" style="margin-top: 24px;">
<p class="mdc-typography mdc-typography--subtitle1">Take Alice's dog for a walk</p>
<p class="mdc-typography mdc-typography--caption">This slider will not render correctly in an <code>MBMenuSurface</code></p>
</div>

<div class="mdc-layout-grid__cell--span-12">
<MBSlider style="margin-left: 0px; margin-right: 0px;"
AriaLabel="The dog's lead'"
@bind-Value="@SliderValue"
DecimalPlaces="0"
SliderType="@MBSliderType.Continuous"
EventType="@MBInputEventType.OnInputThrottled"
ContinuousInputDelay="10"
ValueMin="0"
ValueMax="100" />
</div>

<div class="mdc-layout-grid__cell--span-12">
<span style="position: relative; width: 0px; display: inline-flex; flex-flow: row nowrap; justify-content: center; left: @Left;">
<MBIcon IconName="pets" />
</span>
</div>
</div>
</MBPopover>
</div>
</p>
</Primary>
</MBCard>
</div>
</PageContent>

</DemonstrationPage>

@code {
private MBPopover PopoverElement { get; set; }
private decimal SliderValue { get; set; } = 0;
private string Left => $"{Math.Round(SliderValue, 0)}%";

private async Task OpenMenuSurfaceAsync()
{
await PopoverElement.ShowAsync();
}

private void OnClick(string notification)
{
ToastService.ShowToast(heading: "Button Clicked", message: notification, level: MBToastLevel.Success, showIcon: false);
}
}
3 changes: 2 additions & 1 deletion Material.Blazor.Website/Shared/MainLayout.razor
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@
<MBListItem LeadingIcon="web" Label="Cascading Defaults" @onclick="@(() => ListItemClickHandler("cascadingdefaults"))" />
<MBListItem LeadingIcon="check_box" Label="Checkbox" @onclick="@(() => ListItemClickHandler("checkbox"))" />
<MBListItem LeadingIcon="access_time" Label="Circular Progress" @onclick="@(() => ListItemClickHandler("circularprogress"))" />
<MBListItem LeadingIcon="picture_in_picture" Label="Confirmation Dialog" @onclick="@(() => ListItemClickHandler("confirmationdialog"))" />
<MBListItem LeadingIcon="picture_in_picture_center" Label="Confirmation Dialog" @onclick="@(() => ListItemClickHandler("confirmationdialog"))" />
<MBListItem LeadingIcon="table_chart" Label="Data Table" @onclick="@(() => ListItemClickHandler("datatable"))" />
<MBListItem LeadingIcon="date_range" Label="Date Picker" @onclick="@(() => ListItemClickHandler("datepicker"))" />
<MBListItem LeadingIcon="date_range" Label="DateTime Field" @onclick="@(() => ListItemClickHandler("datetimefield"))" />
Expand All @@ -45,6 +45,7 @@
<MBListItem LeadingIcon="exposure_zero" Label="Numeric Int Field" @onclick="@(() => ListItemClickHandler("numericintfield"))" />
<MBListItem LeadingIcon="import_contacts" Label="Paged Data List" @onclick="@(() => ListItemClickHandler("pageddatalist"))" />
<MBListItem LeadingIcon="import_contacts" Label="Paginator" @onclick="@(() => ListItemClickHandler("paginator"))" />
<MBListItem LeadingIcon="picture_in_picture" Label="Popover" @onclick="@(() => ListItemClickHandler("popover"))" />
<MBListItem LeadingIcon="radio_button_checked" Label="Radio Button" @onclick="@(() => ListItemClickHandler("radiobutton"))" />
<MBListItem LeadingIcon="radio_button_checked" Label="Radio Button Group" @onclick="@(() => ListItemClickHandler("radiobuttongroup"))" />
<MBListItem LeadingIcon="ballot" Label="Segmented Button Multi-Select" @onclick="@(() => ListItemClickHandler("segmentedbuttonmulti"))" />
Expand Down
1 change: 1 addition & 0 deletions Material.Blazor/Articles/CoreComponents.md
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ the markup specification. We term these "Core Components. Material.Blazor also h
| [MBLinearProgress](xref:C.MBLinearProgress) | A [Material Linear Progress bar](https://github.com/material-components/material-components-web/tree/v12.0.0/packages/mdc-linear-progress#linear-progress). |
| [MBList](xref:C.MBList) | A [Material List](https://github.com/material-components/material-components-web/tree/v12.0.0/packages/mdc-list#lists). Implements Material Theme Web Components one and two line lists, plus a Material.Blazor interpretation of a three line list. |
| [MBMenu](xref:C.MBMenu) | A [Material Menu](https://github.com/material-components/material-components-web/tree/v12.0.0/packages/mdc-menu#menus). Does not implement sub menus. May redesign parameterization. |
| [MBMenuSurface](xref:C.MBMenuSurface) | A [Material Menu Surface](https://github.com/material-components/material-components-web/tree/v12.0.0/packages/mdc-menu-surface#menu-surface). Renders content in a render fragment. Does not render contained components well, for which [MBPopover](xref:C.MBPopover) is prefered |
| [MBRadioButton](xref:C.MBRadioButton) | A [Material Radio Button](https://github.com/material-components/material-components-web/tree/v12.0.0/packages/mdc-radio#selection-controls-radio-buttons). |
| [MBPaginator](xref:C.MBPaginator) | An implementation of the [Material paginator specification](https://material.io/components/data-tables#behavior). |
| [MBSegmentedButtonMulti](xref:C.MBSegmentedButtonMulti) | A [Material Multi-Select Segmented Button](https://github.com/material-components/material-components-web/tree/v12.0.0/packages/mdc-segmented-button#segmented-buttons). Implements a multi-select segmented button. |
Expand Down
1 change: 1 addition & 0 deletions Material.Blazor/Articles/PlusComponents.md
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ implements further Blazor/Material Theme hybrid components that we term "Plus Co
| [MBNumericDoubleField](xref:C.MBNumericDoubleField) | A wrapper for `MBNumericDecimalField` for `double` variables. |
| [MBNumericIntField](xref:C.MBNumericIntField) | A wrapper for `MBNumericDecimalField` for `int` variables. |
| [MBPagedDataList](xref:C.MBPagedDataList) | A templated component for paging generic data lists using [MBPaginator](xref:C.MBPaginator) and applying transitions with [MBSlidingContent](xref:C.MBSlidingContent). |
| [MBPopover](xref:C.MBPopover) | A [Material Menu Surface](https://github.com/material-components/material-components-web/tree/v12.0.0/packages/mdc-menu-surface#menu-surface). Renders content in a render fragment with lazy content rendering for improved page load performance, and late instantiation of contained components to ensure correct rendering. This is a prefered component over [MBMenuSurface](xref:C.MBMenuSurface). |
| [MBRadioButtonGroup](xref:C.MBRadioButtonGroup) | A group of [MBRadioButtons](xref:C.MBRadioButton). |
| [MBShield](xref:C.MBShield) | A simple component producing an HTML shield styled after svgs from shield.io (square, flat variety) |
| [MBSlidingContent](xref:C.MBSlidingContent) | A templated component to provide previous/next navigation through a series of pages with light left/right and fade in/out animation. |
Expand Down
11 changes: 4 additions & 7 deletions Material.Blazor/Components/Dialog/MBDialog.razor.cs
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ namespace Material.Blazor;
/// <summary>
/// This is a general purpose Material Theme dialog.
/// </summary>
public partial class MBDialog : ComponentFoundation, IMBDialog
public partial class MBDialog : ComponentFoundation, IMBLayoutParent
{
/// <summary>
/// The dialog title.
Expand Down Expand Up @@ -76,17 +76,14 @@ public partial class MBDialog : ComponentFoundation, IMBDialog


private TaskCompletionSource<string> CloseReasonTaskCompletionSource { get; set; }

private TaskCompletionSource OpenedTaskCompletionSource { get; set; } = new();

internal Task Opened => OpenedTaskCompletionSource.Task;
private bool AfterDialogInitialization { get; set; } = false;
private bool IsOpen { get; set; }
private bool IsOpening { get; set; }


private bool _hasInstantiated = false;
bool IMBDialog.HasInstantiated => _hasInstantiated;
bool IMBLayoutParent.HasInstantiated => _hasInstantiated;


// Would like to use <inheritdoc/> however DocFX cannot resolve to references outside Material.Blazor
Expand Down Expand Up @@ -118,7 +115,7 @@ protected override void Dispose(bool disposing)


/// <inheritdoc/>
void IMBDialog.RegisterLayoutAction(ComponentFoundation child)
void IMBLayoutParent.RegisterLayoutAction(ComponentFoundation child)
{
LayoutChildren.Add(child);
}
Expand Down Expand Up @@ -225,8 +222,8 @@ public async Task NotifyClosed(string reason)
{
_ = (CloseReasonTaskCompletionSource?.TrySetResult(reason));
// Allow enough time for the dialog closing animation before re-rendering
await Task.Delay(150);
IsOpen = false;
await Task.Delay(150);
await InvokeAsync(StateHasChanged);
}
}
19 changes: 10 additions & 9 deletions Material.Blazor/Components/Dialog/MBDialog.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,33 +4,34 @@ export function show(elem, dotNetObject, escapeKeyAction, scrimClickAction): any
if (!elem) {
return;
}

elem._dialog = elem._dialog || MDCDialog.attachTo(elem);
elem._dotNetObject = dotNetObject;

const dialog = elem._dialog;

const openedCallback = () => {
dialog.unlisten('MDCDialog:opened', openedCallback);
elem._dialog.unlisten('MDCDialog:opened', openedCallback);
dotNetObject.invokeMethodAsync('NotifyOpened');
};
dialog.listen('MDCDialog:opened', openedCallback);

dialog.escapeKeyAction = escapeKeyAction;
dialog.scrimClickAction = scrimClickAction;
elem._dialog.listen('MDCDialog:opened', openedCallback);

elem._dialog.escapeKeyAction = escapeKeyAction;
elem._dialog.scrimClickAction = scrimClickAction;

const closingCallback = event => {
dialog.unlisten('MDCDialog:closing', closingCallback);
elem._dialog.unlisten('MDCDialog:closing', closingCallback);
dotNetObject.invokeMethodAsync('NotifyClosed', event.detail.action);
};

dialog.listen('MDCDialog:closing', closingCallback);
dialog.open();
elem._dialog.listen('MDCDialog:closing', closingCallback);
elem._dialog.open();
}

export function hide(elem, dialogAction) {
if (!elem) {
return;
}

if (elem && elem._dialog) {
elem._dialog.close(dialogAction || 'dismissed');
elem._dialog.destroy();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
namespace Material.Blazor;

/// <summary>
/// This is a general purpose Material Theme menu.
/// This is a general purpose Material Theme menu surface.
/// </summary>
public partial class MBMenuSurface : ComponentFoundation
{
Expand Down
2 changes: 2 additions & 0 deletions Material.Blazor/Components/MenuSurface/MBMenuSurface.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ export function init(elem, dotNetObject) {
if (!elem) {
return;
}

elem._menu = MDCMenuSurface.attachTo(elem);

const openedCallback = () => {
Expand Down Expand Up @@ -32,6 +33,7 @@ export function hide(elem) {
if (!elem) {
return;
}

if (elem._menu) {
elem._menu.close();
}
Expand Down
18 changes: 18 additions & 0 deletions Material.Blazor/Components/Popover/MBPopover.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
---
uid: C.MBPopover
title: MBPopover
---
# MBPopover

## Summary

A [Material Menu Surface](https://github.com/material-components/material-components-web/tree/v9.0.0/packages/mdc-menu-surface#menu-surface), where the entire menu content is a render fragment.
Unlike the [MBMenuSurface](xref:Material.Blazor.MBMenuSurface) component, the MBPopover component performs both lazy rendering to improve page load times and also late rendering of componnents within
the menu surface to ensure correct rendering once opened.

&nbsp;

&nbsp;

[![Components](https://img.shields.io/static/v1?label=Components&message=Plus&color=red)](xref:A.PlusComponents)
[![Docs](https://img.shields.io/static/v1?label=API%20Documentation&message=MBMenu&color=brightgreen)](xref:Material.Blazor.MBPopover)
17 changes: 17 additions & 0 deletions Material.Blazor/Components/Popover/MBPopover.razor
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
@namespace Material.Blazor
@inherits ComponentFoundation


<div @ref="ElementReference"
class="mdc-menu-surface @ActiveConditionalClasses @(@class)"
style="@style"
id="@id"
@attributes="@AttributesToSplat()">

<CascadingValue Value="@this" IsFixed="true">
@if (IsOpen && ChildContent != null)
{
@ChildContent
}
</CascadingValue>
</div>
Loading

0 comments on commit eecc417

Please sign in to comment.