Chat

The MudChat component is used to house one or more MudChatBubble components, with optional components such as MudAvatar, MudChatHeader, and MudChatFooter.

Warning: This component is currently under development.

Breaking changes such as updates to the API, look and feel, or CSS classes, may occur even in minor patch releases. Please use it only if you are prepared to adapt your code accordingly and provide feedback or contribute code.

Basic

MudChat can be used to display one or more MudChatBubble components. The bubbles can be aligned at the start (left for Left-to-Right, right for Right-to-Left) or at the end.

It's over Anakin
I have the high ground.
You underestimate my power!
<MudChat ChatPosition="ChatBubblePosition.Start">
    <MudChatBubble>
        It's over Anakin
    </MudChatBubble>
    <MudChatBubble>
        I have the high ground.
    </MudChatBubble>
</MudChat>
<MudChat ChatPosition="ChatBubblePosition.End">
    <MudChatBubble>
        You underestimate my power!
    </MudChatBubble>
</MudChat>
With Avatar

MudChat can include an avatar, which works seamlessly with the MudAvatar component. The avatar will always appear on the first MudChatBubble inside a MudChat.

It was said that you would, destroy the Sith, not join them.
It was you who would bring balance to the Force
Not leave it in Darkness
<MudChat ChatPosition="ChatBubblePosition.Start">
    <MudAvatar>OK</MudAvatar>
    <MudChatBubble>
        It was said that you would, destroy the Sith, not join them.
    </MudChatBubble>
    <MudChatBubble>
        It was you who would bring balance to the Force
    </MudChatBubble>
</MudChat>

<MudChat ChatPosition="ChatBubblePosition.Start">
    <MudAvatar>
        <MudImage Src="images/jonny.jpg" />
    </MudAvatar>
    <MudChatBubble>
        Not leave it in Darkness
    </MudChatBubble>
</MudChat>
Obi-Wan Kenobi
You were my brother Anakin.
Obi-Wan Kenobi
I loved you.
<MudChat ChatPosition="ChatBubblePosition.Start">
    <MudChatHeader Name="Obi-Wan Kenobi" Time="2 hours ago" />
    <MudChatBubble>You were my brother Anakin.</MudChatBubble>
    <MudChatFooter Text="Seen" />
</MudChat>

<MudChat ChatPosition="ChatBubblePosition.Start">
    <MudChatHeader>
        <MudAlert Severity="Severity.Info" Dense="true">Obi-Wan Kenobi</MudAlert>
    </MudChatHeader>
    <MudChatBubble>I loved you.</MudChatBubble>
    <MudChatFooter>
        <MudAlert Severity="Severity.Info" Dense="true">Seen</MudAlert>
    </MudChatFooter>
</MudChat>
ChatBubble OnClick Parameter

You can define the OnClick and/or OnContextClick parameter for the MudChatBubble component to handle click events.

Obi-Wan Kenobi
You were my brother Anakin.
I loved you.
Anakin Skywalker
I'm sorry.
@inject ISnackbar Snackbar

@{
    var orderedGroups = messages
        .OrderByDescending(m => m.Time)
        .GroupBy(m => m.Name)
        .ToList();

    for (int i = 0; i < orderedGroups.Count; i++)
    {
        var group = orderedGroups[i];
        var position = i == 0 ? ChatBubblePosition.Start : ChatBubblePosition.End;

        <MudChat ChatPosition="@position">
            <MudAvatar>@group.First().Initials</MudAvatar>
            <MudChatHeader Name="@group.Key" Time="@group.First().Time" />
            @foreach (var message in group.OrderByDescending(m => m.Time))
            {
                <MudChatBubble OnClick="(MouseEventArgs args) => ClickMessage(args, message)" 
                               OnContextClick="(MouseEventArgs args) => RightClickMessage(args, message)">
                    @message.Text
                </MudChatBubble>
            }
        </MudChat>
    }
}

<MudMenu PositionAtCursor="true" @ref="_contextMenu" id="_contextMenu">
    <MudMenuItem Icon="@Icons.Material.Filled.Block" OnClick="@BanUser">
        Ban @_selectedMessage?.Name
    </MudMenuItem>
    <MudMenuItem Icon="@Icons.Material.Filled.Info" OnClick="@ShowHiddenInfo">
        View Details for @_selectedMessage?.Name
    </MudMenuItem>
</MudMenu>
@code {
    #nullable enable
    private List<Message> messages = new();
    private Message? _selectedMessage;
    private MudMenu? _contextMenu;

    protected override void OnInitialized()
    {
        messages.Add(new Message("Obi-Wan Kenobi", "OK", "You were my brother Anakin.", "2 hours ago"));
        messages.Add(new Message("Obi-Wan Kenobi", "OK", "I loved you.", "2 hours ago"));
        messages.Add(new Message("Anakin Skywalker", "AS", "I'm sorry.", "1 hour ago"));
    }

    private void ShowHiddenInfo()
    {
        if (_selectedMessage is not null)
        {
            Snackbar.Add($"Hidden information for {_selectedMessage.Name}", Severity.Info);
        }
    }

    private void BanUser()
    {
        if (_selectedMessage is not null)
        {
            Snackbar.Add($"{_selectedMessage.Name} has been banned!", Severity.Error);
        }
    }

    private async Task RightClickMessage(MouseEventArgs args, Message message)
    {
        _selectedMessage = message;
        if (_contextMenu != null)
            await _contextMenu.OpenMenuAsync(args);
    }

    private async Task ClickMessage(MouseEventArgs args, Message message)
    {
        _selectedMessage = message;
        Snackbar.Add("Message clicked: " + message.Text, Severity.Info);
        await Task.CompletedTask;
    }


    private record Message(string Name, string Initials, string Text, string Time);
}
Additional Chat Bubble Options

MudChat can be customized with the following properties:

  • Dense: Reduces the vertical margins of the chat bubbles.
  • Square: Makes the chat bubbles square.
  • Elevation: The elevation of the chat bubbles. Defaults to 0.
  • ArrowPosition: The position of the arrow on the first chat bubble. Defaults to Top.
  • *Color: The color of the chat bubbles. Defaults to Color.Default.
  • *Variant: The variant of the chat bubbles. Defaults to Variant.Text.
* These properties are passed down to the MudChatBubble components, and can be overridden individually.

Elevation: 5

Anakin
What kind of nonsense is this
Put me on the Council and not make me a Master!??
That's never been done in the history of the Jedi. It's insulting!
Obi-Wan Kenobi
Calm down, Anakin. You have been given a great honor.
To be on the Council at your age.
It's never happened before.
<MudGrid>
    <MudItem xs="12" Class="justify-center">
        <MudStack>
            <MudStack Row>
                <MudCheckBox Label="Dense" @bind-Value="_dense" />
                <MudCheckBox Label="Square" @bind-Value="_square" />
            </MudStack>
            <MudRadioGroup @bind-Value="_variant">
                <MudRadio Value="@Variant.Text" Label="Text" />
                <MudRadio Value="@Variant.Outlined" Label="Outlined" />
                <MudRadio Value="@Variant.Filled" Label="Filled" />
            </MudRadioGroup>
            <MudStack Row Class="ml-4" Style="max-width:600px;">
                <MudSelect Class="mr-1" T="MudBlazor.Color" Label="Chat Color" @bind-Value="_selectedColor">
                    @foreach (MudBlazor.Color color in Enum.GetValues(typeof(MudBlazor.Color)))
                    {
                        <MudSelectItem Value="@color">@color.ToString()</MudSelectItem>
                    }
                </MudSelect>
                <MudSelect T="MudBlazor.ChatArrowPosition" Label="Chat Arrow" @bind-Value="_selectedArrowPosition">
                    @foreach (MudBlazor.ChatArrowPosition pos in Enum.GetValues(typeof(MudBlazor.ChatArrowPosition)))
                    {
                        <MudSelectItem Value="@pos">@pos.ToString()</MudSelectItem>
                    }
                </MudSelect>
            </MudStack>
            <MudSlider @bind-Value="_elevation" Min="0" Max="25" Color="Color.Info" Class="ml-4 mb-6" Style="max-width:600px;">Elevation: @_elevation.ToString()</MudSlider>
        </MudStack>
    </MudItem>
</MudGrid>

<MudChat Color="_selectedColor" Dense="@_dense" Elevation="@_elevation" Variant="@_variant" Square="_square" ArrowPosition="_selectedArrowPosition" ChatPosition="ChatBubblePosition.Start">
    <MudChatHeader Name="Anakin" Time="12:46" />
    <MudAvatar Size="@(_dense ? Size.Small : Size.Medium)">
        <MudImage Src="images/toiletvisit.jpg" />
    </MudAvatar>
    <MudChatBubble>What kind of nonsense is this</MudChatBubble>
    <MudChatBubble>Put me on the Council and not make me a Master!??</MudChatBubble>
    <MudChatBubble>That's never been done in the history of the Jedi. It's insulting!</MudChatBubble>
    <MudChatFooter Text="Seen at 12:46" />
</MudChat>

<MudChat Color="_selectedColor" Dense="@_dense" Elevation="@_elevation" Variant="@_variant" Square="_square" ArrowPosition="_selectedArrowPosition" ChatPosition="ChatBubblePosition.End">
    <MudChatHeader Name="Obi-Wan Kenobi" Time="12:45" />
    <MudAvatar Size="@(_dense ? Size.Small : Size.Medium)">
        <MudImage Src="images/jonny.jpg" />
    </MudAvatar>
    <MudChatBubble>Calm down, Anakin. You have been given a great honor. <br/> To be on the Council at your age.</MudChatBubble>
    <MudChatBubble Color="Color.Primary">It's never happened before.</MudChatBubble>
    <MudChatFooter Text="Delivered" />
</MudChat>
@code {
    private bool _dense = true;
    private bool _square = false;
    private int _elevation = 5;
    private Variant _variant = Variant.Text;
    private Color _selectedColor = Color.Default;
    private ChatArrowPosition _selectedArrowPosition = ChatArrowPosition.Top;
}
Adding a Hover Event

MudChat and MudChatBubble can be customized with any html classes, styles, and normal events. Here is an example of adding a hovering like/reply center.

Obi-Wan Kenobi
You were my brother Anakin.
I loved you.
Anakin Skywalker
I'm sorry.
@inject ISnackbar Snackbar
@inject IScrollListenerFactory ScrollListenerFactory

@{
    var orderedGroups = messages
        .OrderByDescending(m => m.Time)
        .GroupBy(m => m.Name)
        .ToList();

    for (int i = 0; i < orderedGroups.Count; i++)
    {
        var group = orderedGroups[i];
        var position = i == 0 ? ChatBubblePosition.Start : ChatBubblePosition.End;

        <MudChat ChatPosition="@position">
            <MudAvatar>@group.First().Initials</MudAvatar>
            <MudChatHeader Name="@group.Key" Time="@group.First().Time" />
            @foreach (var message in group.OrderByDescending(m => m.Time))
            {
                <MudChatBubble OnClick="@((args) => ClickMessage(args, message))"
                OnContextClick="@((args) => RightClickMessage(args, message))"
                @onmouseenter="@((args) => HoverStart(args, message))"
                @onmouseleave="@(() => HoverStop("bubble"))">
                    @message.Text
                    <MudPopover Open="@(Hovering && message.Equals(_hoverMessage))" Class="hoverarea"
                    AnchorOrigin="Origin.CenterRight"
                    TransformOrigin="Origin.BottomRight"
                    @onscroll="@(() => HoverStop("scroll"))"
                    @onmouseenter="@(() => _paperHovering = true)"
                    @onmouseleave="@(() => HoverStop("paper"))">
                        <div class="d-flex" style="width: 100%;" @onclick:stopPropagation="true" @onclick:preventDefault="true">
                            <span class="emoji" @onclick="@(() => MessageAction("liked"))">👍</span>
                            <span class="emoji" @onclick="@(() => MessageAction("cried"))">😭</span>
                            <span class="emoji" @onclick="@(() => MessageAction("angered"))">👿</span>
                            <MudDivider Class="mx-1" Vertical="true" FlexItem="true" />
                            <MudTooltip Text="Reply">
                                <span class="emoji" @onclick="@Reply">↩️</span>
                            </MudTooltip>
                        </div>
                    </MudPopover>
                </MudChatBubble>
                <MudChatFooter>
                    <MudPaper Class="actionarea" Elevation="0">
                        <div class="d-flex" style="width: 100%;" @onclick:stopPropagation="true" @onclick:preventDefault="true">
                            @if (message.Likes > 0)
                            {
                                <span class="emoji" @onclick="@(() => MessageAction("liked"))">👍</span>
                                @message.Likes
                            }
                            @if (message.Cries > 0)
                            {
                                <span class="emoji" @onclick="@(() => MessageAction("cried"))">😭</span>
                                @message.Cries
                            }
                            @if (message.Angers > 0)
                            {
                                <span class="emoji" @onclick="@(() => MessageAction("angered"))">👿</span>
                                @message.Angers
                            }
                        </div>
                    </MudPaper>
                </MudChatFooter>
            }
        </MudChat>
    }
}

<MudMenu PositionAtCursor="true" @ref="_contextMenu" id="_contextMenu">
    <MudMenuItem Icon="@Icons.Material.Filled.Block" OnClick="@BanUser">
        Ban @_selectedMessage?.Name
    </MudMenuItem>
    <MudMenuItem Icon="@Icons.Material.Filled.Info" OnClick="@ShowHiddenInfo">
        View Details for @_selectedMessage?.Name
    </MudMenuItem>
</MudMenu>

<style>
    .hoverarea {
        cursor: pointer !important;
        background-color: var(--mud-palette-appbar-background);
        color: var(--mud-palette-appbar-text);
        transition: opacity 0.3s ease;
        opacity: .8;
    }

    .actionarea {
        background-color: var(--mud-palette-appbar-background);
        color: var(--mud-palette-appbar-text);
    }

    .actionarea:focus {
        opacity: 1; 
    }

    span.emoji {
        font-size: 12px;
        padding: 1px 1px;
        align-self: center;
    }
</style>
@code {
    #nullable enable
    private IScrollListener? _scrollListener;
    private List<Message> messages = new();
    private Message? _selectedMessage;
    private MudMenu? _contextMenu;

    private bool Hovering => _paperHovering || _bubbleHovering;
    private bool _bubbleHovering;
    private bool _paperHovering;

    private Message? _hoverMessage;

    protected override void OnInitialized()
    {
        _scrollListener = ScrollListenerFactory.Create(null);
        _scrollListener.OnScroll += OnScrollAsync;
        messages.Add(new Message("Obi-Wan Kenobi", "OK", "You were my brother Anakin.", "2 hours ago"));
        messages.Add(new Message("Obi-Wan Kenobi", "OK", "I loved you.", "2 hours ago"));
        messages.Add(new Message("Anakin Skywalker", "AS", "I'm sorry.", "1 hour ago"));
    }

    private void OnScrollAsync(object? sender, ScrollEventArgs e)
    {
        HoverStop("scroll");
    }

    public void Dispose()
    {
        if (_scrollListener != null)
            _scrollListener.OnScroll -= OnScrollAsync;
    }

    private void HoverStart(MouseEventArgs args, Message message)
    {
        _bubbleHovering = true;
        _hoverMessage = message;
    }

    private void HoverStop(string typeOfStop)
    {
        try
        {
            switch (typeOfStop)
            {
                case "paper":
                    _paperHovering = false;
                    break;
                case "bubble":
                    _bubbleHovering = false;
                    break;
                case "scroll":
                    _paperHovering = false;
                    _bubbleHovering = false;
                    StateHasChanged();
                    break;
                default:
                    break;
            }
        }
        catch { }
    }

    private void MessageAction(string actionType)
    {
        switch (actionType)
        {
            case "liked":
                _hoverMessage!.Likes++;
                break;
            case "cried":
                _hoverMessage!.Cries++;
                break;
            case "angered":
                _hoverMessage!.Angers++;
                break;
            default:
                break;
        }
    }

    private void Reply()
    {
        if (_hoverMessage is null)
        {
            Snackbar.Add("No message available!", Severity.Warning);
        }
        else
        {
            Snackbar.Add($"Simulate Reply: To: {_hoverMessage.Name}", Severity.Success);
        }
    }

    private void ShowHiddenInfo()
    {
        if (_selectedMessage is not null)
        {
            Snackbar.Add($"Hidden information for {_selectedMessage.Name}", Severity.Info);
        }
    }

    private void BanUser()
    {
        if (_selectedMessage is not null)
        {
            Snackbar.Add($"{_selectedMessage.Name} has been banned!", Severity.Error);
        }
    }

    private async Task RightClickMessage(MouseEventArgs args, Message message)
    {
        _selectedMessage = message;
        if (_contextMenu != null)
            await _contextMenu.OpenMenuAsync(args);
    }

    private async Task ClickMessage(MouseEventArgs args, Message message)
    {
        _selectedMessage = message;
        Snackbar.Add("Message clicked: " + message.Text, Severity.Info);
        await Task.CompletedTask;
    }

    private record Message(
        string Name,
        string Initials,
        string Text,
        string Time,
        int Likes = 0,
        int Cries = 0,
        int Angers = 0)
    {
        public int Likes { get; set; } = Likes;
        public int Cries { get; set; } = Cries;
        public int Angers { get; set; } = Angers;
    }
}
An unhandled error has occurred. Reload 🗙