Documentations added

Co-authored-by: Copilot <copilot@github.com>
This commit is contained in:
2026-05-04 19:57:48 +05:00
parent 40a7d9018c
commit ee8797c142
35 changed files with 6655 additions and 0 deletions
+169
View File
@@ -0,0 +1,169 @@
# Avatar
A circular user avatar. Shows an image when a `src` URL is provided; falls back to a text/initials span otherwise.
---
## HTML structure
```
span.relative.flex.{size classes}.shrink-0.overflow-hidden.rounded-full
img[src, alt, class] ← when src is provided
span.flex.items-center... ← fallback when no src
{fallback text}
```
---
## CSS mechanics
| Class | Effect |
|---|---|
| `rounded-full overflow-hidden` | Clips content to a circle |
| `aspect-square h-full w-full object-cover` | Image fills the circle without distortion |
| `bg-muted text-muted-foreground` | Neutral background for the initials fallback |
| Size `h-8 w-8` / `h-10 w-10` / `h-14 w-14` / `h-20 w-20` | sm / default / lg / xl |
---
## Constructor signature
```csharp
public Avatar(
string fallback,
string? src = null,
string size = "default")
```
| Parameter | Description |
|---|---|
| `fallback` | Text shown when no `src` is given; also used as `alt` text on the image |
| `src` | Optional image URL |
| `size` | `"sm"` / `"default"` / `"lg"` / `"xl"` |
---
## Usage examples
### Initials avatar
```csharp
new Avatar(fallback: "JD")
new Avatar(fallback: "JD", size: "lg")
```
### Image avatar with fallback
```csharp
new Avatar(fallback: "Jane Doe", src: "/avatars/jane.jpg", size: "default")
```
### Sizes
```csharp
new Avatar(fallback: "SM", size: "sm") // 32×32
new Avatar(fallback: "DF", size: "default") // 40×40
new Avatar(fallback: "LG", size: "lg") // 56×56
new Avatar(fallback: "XL", size: "xl") // 80×80
```
### Inside a user card
```csharp
var avatar = new Avatar(fallback: user.Initials, src: user.AvatarUrl, size: "lg");
// In a page's RenderUserCard override:
protected override void RenderUserAvatar(HtmxRenderContext ctx)
=> avatar.Render(ctx.Next());
```
---
## Tips and tricks
- Compute initials before constructing the Avatar — the component does not extract them from a full name. See `MainLayout`'s `GetInitials` helper for a reference implementation.
- Always provide `fallback` even when you also provide `src` — it serves as the `alt` attribute for accessibility.
- The Avatar does not handle image load errors. If you need a graceful image fallback on 404, add an `onerror="this.style.display='none'"` attribute by embedding it in the `src` or use `hxAttrs` in a subclassed version.
- For a group of overlapping avatars (avatar stack), wrap several Avatars in a flex container with negative margin: `<div class="flex -space-x-2">`.
---
## Complete page example
**`Templates/ProfilePage.htmx`**
```html
<div class="max-w-lg mx-auto py-10">
<div class="flex items-center gap-4 mb-6">
$$UserAvatar$$
<div>
<h1 class="text-xl font-bold">$$DisplayName$$</h1>
<p class="text-sm text-muted-foreground">$$Email$$</p>
</div>
</div>
<p class="text-sm">Member since $$JoinDate$$</p>
</div>
```
**`Templates/ProfilePage.htmx.cs`**
```csharp
namespace Htmx.ApiDemo.Templates;
public sealed class ProfilePage : ProfilePageBase
{
private readonly IHtmxComponent _avatar;
private readonly byte[] _displayName;
private readonly byte[] _email;
private readonly byte[] _joinDate;
public ProfilePage(AppUser user)
{
_avatar = new Components.Avatar(
fallback: GetInitials(user.DisplayName),
size: "lg");
_displayName = (user.DisplayName ?? "Unknown").ToUtf8Bytes();
_email = user.Email.ToUtf8Bytes();
_joinDate = user.CreatedAt.ToString("MMMM yyyy").ToUtf8Bytes();
}
private static string GetInitials(string? name)
{
if (string.IsNullOrWhiteSpace(name)) return "?";
var parts = name.Split(' ', StringSplitOptions.RemoveEmptyEntries);
return parts.Length >= 2
? $"{parts[0][0]}{parts[^1][0]}"
: name[..1].ToUpperInvariant();
}
protected override void RenderUserAvatar(HtmxRenderContext ctx)
=> _avatar.Render(ctx.Next());
protected override void RenderDisplayName(HtmxRenderContext ctx)
=> ctx.Writer.WriteUtf8(_displayName);
protected override void RenderEmail(HtmxRenderContext ctx)
=> ctx.Writer.WriteUtf8(_email);
protected override void RenderJoinDate(HtmxRenderContext ctx)
=> ctx.Writer.WriteUtf8(_joinDate);
}
```
**GET handler**
```csharp
[Handler]
[MapGet("/profile")]
public static partial class GetProfileHandler
{
public record Query();
private static async Task<IResult> HandleAsync(
Query _,
HttpContext ctx,
MongoDbService db,
CancellationToken ct)
{
var email = ctx.User.FindFirst(ClaimTypes.Email)?.Value ?? "";
var user = await db.FindByNormalizedEmailAsync(email.ToUpperInvariant(), ct);
if (user is null) return Results.Redirect("/login");
return await ctx.WriteHtmxPage(new ProfilePage(user), title: "Profile");
}
}
```