Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -82,8 +82,10 @@
alt="@ImageAlt"
width="@dimension"
height="@dimension"
srcset="@ImageSrcSet"
class="bit-prs-img @Classes?.Image"
style="display:@(_isLoaded ? "unset" : "none");@Styles?.Image" />
loading="@ImageLoading?.ToString().ToLower()"
style="@($"{(_isLoaded ? null : "opacity:0;")}{Styles?.Image}")" />
}
}
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
namespace Bit.BlazorUI;
using ErrorEventArgs = Microsoft.AspNetCore.Components.Web.ErrorEventArgs;

namespace Bit.BlazorUI;

/// <summary>
/// A BitPersona is a visual representation of a person across products, typically showcasing the image that person has chosen to upload themselves. The control can also be used to show that person's online status.
Expand Down Expand Up @@ -38,6 +40,13 @@ public partial class BitPersona : BitComponentBase
/// </summary>
[Parameter] public RenderFragment? ActionTemplate { get; set; }

/// <summary>
/// If true, automatically generates a coin background color derived from the person's name or initials.
/// When set, this takes effect only when <see cref="CoinColor"/> is not explicitly provided.
/// </summary>
[Parameter, ResetClassBuilder]
public bool AutoCoinColor { get; set; }

/// <summary>
/// Custom CSS classes for different parts of the BitPersona component.
/// </summary>
Expand All @@ -49,12 +58,6 @@ public partial class BitPersona : BitComponentBase
[Parameter, ResetClassBuilder]
public BitColor? CoinColor { get; set; }

/// <summary>
/// The shape of the coin.
/// </summary>
[Parameter, ResetClassBuilder]
public BitPersonaCoinShape? CoinShape { get; set; }

/// <summary>
/// Optional custom persona coin size in pixel.
/// </summary>
Expand Down Expand Up @@ -89,7 +92,13 @@ public partial class BitPersona : BitComponentBase
/// <summary>
/// The user's initials to display in the image area when there is no image.
/// </summary>
[Parameter] public string? ImageInitials { get; set; }
[Parameter, ResetClassBuilder]
public string? ImageInitials { get; set; }

/// <summary>
/// Specifies the loading behavior of the image (e.g., "lazy" or "eager").
/// </summary>
[Parameter] public BitImageLoading? ImageLoading { get; set; }

/// <summary>
/// Optional Custom template for the image overlay.
Expand All @@ -101,6 +110,11 @@ public partial class BitPersona : BitComponentBase
/// </summary>
[Parameter] public string ImageOverlayText { get; set; } = "Edit image";

/// <summary>
/// A set of image source URLs for different display densities or sizes (maps to the img srcset attribute).
/// </summary>
[Parameter] public string? ImageSrcSet { get; set; }

/// <summary>
/// Url to the image to use, should be a square aspect ratio and big enough to fit in the image area.
/// </summary>
Expand All @@ -119,6 +133,16 @@ public partial class BitPersona : BitComponentBase
[Parameter, ResetClassBuilder]
public EventCallback<MouseEventArgs> OnImageClick { get; set; }

/// <summary>
/// Callback for when the image fails to load.
/// </summary>
[Parameter] public EventCallback<ErrorEventArgs> OnImageError { get; set; }

/// <summary>
/// Callback for when the image successfully loads.
/// </summary>
[Parameter] public EventCallback<ProgressEventArgs> OnImageLoad { get; set; }

Comment thread
msynk marked this conversation as resolved.
/// <summary>
/// Optional text to display, usually a custom message set.
/// The optional text will only be shown when using size100.
Expand Down Expand Up @@ -184,7 +208,8 @@ public partial class BitPersona : BitComponentBase
/// <summary>
/// Primary text to display, usually the name of the person.
/// </summary>
[Parameter] public string? PrimaryText { get; set; }
[Parameter, ResetClassBuilder]
public string? PrimaryText { get; set; }

/// <summary>
/// Custom primary text template.
Expand All @@ -206,6 +231,12 @@ public partial class BitPersona : BitComponentBase
/// </summary>
[Parameter] public bool ShowInitialsUntilImageLoads { get; set; }

/// <summary>
/// If true, renders the coin with a square shape instead of the default circular shape.
/// </summary>
[Parameter, ResetClassBuilder]
public bool Squared { get; set; }

/// <summary>
Comment thread
msynk marked this conversation as resolved.
/// If true, show the special coin for unknown persona.
/// It has '?' in place of initials, with static font and background colors.
Expand Down Expand Up @@ -308,15 +339,11 @@ protected override void RegisterCssClasses()
BitColor.PrimaryBorder => "bit-prs-pbr",
BitColor.SecondaryBorder => "bit-prs-sbr",
BitColor.TertiaryBorder => "bit-prs-tbr",
null when AutoCoinColor => GetAutoCoinColorClass(),
_ => "bit-prs-inf"
});

ClassBuilder.Register(() => CoinShape switch
{
BitPersonaCoinShape.Circular => "bit-prs-crl",
BitPersonaCoinShape.Square => "bit-prs-sqr",
_ => "bit-prs-crl"
});
ClassBuilder.Register(() => Squared ? "bit-prs-sqr" : null);
}

protected override void RegisterCssStyles()
Expand Down Expand Up @@ -346,7 +373,7 @@ protected override void RegisterCssStyles()

string? position = null;
var presentationSize = CoinSize.Value / 3D;
if (CoinShape == BitPersonaCoinShape.Square)
if (Squared)
{
var presentationPosition = presentationSize / 3D;
position = FormattableString.Invariant($"right:-{presentationPosition}px;bottom:-{presentationPosition}px;");
Expand Down Expand Up @@ -387,6 +414,23 @@ protected override void RegisterCssStyles()
return $"width:{CoinSize.Value}px;";
}

private static readonly string[] _autoCoinColorClasses = ["bit-prs-pri", "bit-prs-sec", "bit-prs-ter", "bit-prs-suc", "bit-prs-wrn", "bit-prs-err", "bit-prs-inf"];

private string GetAutoCoinColorClass()
{
var text = (ImageInitials.HasValue() ? ImageInitials : PrimaryText)?.Trim() ?? string.Empty;
if (text.HasNoValue()) return "bit-prs-inf";

// Stable DJB2 hash — not affected by .NET's randomized GetHashCode
uint hash = 5381;
foreach (var c in text)
{
hash = ((hash << 5) + hash) + c;
}

return _autoCoinColorClasses[hash % (uint)_autoCoinColorClasses.Length];
}
Comment thread
msynk marked this conversation as resolved.

private string GetInitials()
{
if (ImageInitials.HasValue()) return ImageInitials!;
Expand Down Expand Up @@ -463,21 +507,30 @@ private async Task HandleImageClick(MouseEventArgs e)
await OnImageClick.InvokeAsync(e);
}

private void HandleOnError(Microsoft.AspNetCore.Components.Web.ErrorEventArgs e)
private void HandleOnError(ErrorEventArgs e)
{
_hasError = true;
_isLoaded = true;

StateHasChanged();

OnImageError.InvokeAsync(e);
}

private void HandleOnLoad(ProgressEventArgs e)
{
_isLoaded = true;

StateHasChanged();

OnImageLoad.InvokeAsync(e);
}

private void OnSetImageUrl()
{
_hasError = false;
_isLoaded = false;

StateHasChanged();
}
}
Loading
Loading