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
+176
View File
@@ -0,0 +1,176 @@
# Progress
A horizontal progress bar. Value is clamped to 0100. Three sizes control the bar height.
---
## HTML structure
```
div.w-full.bg-secondary.rounded-full.overflow-hidden.{size class}
div.bg-primary.rounded-full.h-full.transition-all[style="width: {value}%"]
```
---
## CSS mechanics
| Class | Effect |
|---|---|
| `bg-secondary` | Neutral track color |
| `bg-primary` | Filled indicator color |
| `rounded-full overflow-hidden` | Pill-shaped track; fills also become pill-shaped |
| `transition-all` | Smooth animation when `width` changes |
**Size classes applied to the outer track:**
| Size | Class | Height |
|---|---|---|
| `sm` | `h-1.5` | 6 px |
| `default` | `h-2.5` | 10 px |
| `lg` | `h-4` | 16 px |
---
## Constructor signature
```csharp
public Progress(int value, string size = "default")
```
| Parameter | Description |
|---|---|
| `value` | Fill percentage; clamped to 0100 |
| `size` | `"sm"` / `"default"` / `"lg"` |
---
## Usage examples
### Inline usage
```csharp
new Progress(value: 72)
new Progress(value: 40, size: "sm")
new Progress(value: 100, size: "lg")
```
### Inside a Card
```csharp
new Card(
title: "Disk usage",
content: $"""
<div class="mb-2 flex justify-between text-sm">
<span>Used</span>
<span>{used} GB / {total} GB</span>
</div>
{progressHtml}
""")
```
(Pre-render the `Progress` to a string using `HtmxRenderContext` and `ArrayBufferWriter<byte>`.)
### HTMX live update
```html
<div id="progress-bar"
hx-get="/job/42/progress"
hx-trigger="every 1s"
hx-swap="outerHTML">
$$ProgressBar$$
</div>
```
The endpoint returns a partial re-render of this fragment with the updated `value`.
---
## Tips and tricks
- Values below 0 are treated as 0; values above 100 are treated as 100 — no manual clamping needed.
- Use `size: "sm"` for compact UI areas such as table rows.
- To animate progress smoothly, let `transition-all` do the work: re-render the component via HTMX on a polling interval or push updates via SSE.
- For an indeterminate spinner, use `Skeleton` instead (it has `animate-pulse` built in).
- For an indeterminate spinner, use `Skeleton` instead (it has `animate-pulse` built in).
---
## Complete page example
**`Templates/JobStatusPage.htmx`**
```html
<div class="max-w-md mx-auto py-10">
<h1 class="text-2xl font-bold mb-2">Processing</h1>
<p class="text-sm text-muted-foreground mb-6">$$StatusText$$</p>
<div class="mb-2 flex justify-between text-sm">
<span>Progress</span>
<span>$$ProgressLabel$$</span>
</div>
$$ProgressBar$$
$$DoneAlert$$
</div>
```
**`Templates/JobStatusPage.htmx.cs`**
```csharp
namespace Htmx.ApiDemo.Templates;
public sealed class JobStatusPage : JobStatusPageBase
{
private readonly byte[] _statusText;
private readonly byte[] _progressLabel;
private readonly IHtmxComponent _progressBar;
private readonly IHtmxComponent _doneAlert;
public JobStatusPage(int percent, string statusText)
{
_statusText = System.Net.WebUtility.HtmlEncode(statusText).ToUtf8Bytes();
_progressLabel = $"{percent}%".ToUtf8Bytes();
_progressBar = new Components.Progress(value: percent);
_doneAlert = percent >= 100
? new Components.Alert(title: "Complete!", description: "Your export is ready.")
: HtmxEmpty.Instance;
}
protected override void RenderStatusText(HtmxRenderContext ctx) => ctx.Writer.WriteUtf8(_statusText);
protected override void RenderProgressLabel(HtmxRenderContext ctx) => ctx.Writer.WriteUtf8(_progressLabel);
protected override void RenderProgressBar(HtmxRenderContext ctx) => _progressBar.Render(ctx.Next());
protected override void RenderDoneAlert(HtmxRenderContext ctx) => _doneAlert.Render(ctx.Next());
}
```
**GET handler with HTMX polling**
```csharp
[Handler]
[MapGet("/jobs/{jobId}/status")]
public static partial class GetJobStatusHandler
{
public record Query([property: FromRoute] string JobId);
private static async Task<IResult> HandleAsync(
Query q, HttpContext ctx, JobQueue jobs, CancellationToken ct)
{
var job = await jobs.GetAsync(q.JobId, ct);
if (job is null) return Results.NotFound();
var page = new JobStatusPage(job.PercentComplete, job.StatusText);
// If polling (HTMX partial), only return the progress fragment
if (ctx.Request.Headers.ContainsKey("HX-Request"))
{
// Stop polling when done
if (job.PercentComplete >= 100)
ctx.Response.Headers.Append("HX-Trigger", "jobComplete");
return await ctx.WriteHtmxPage(page, title: "Processing");
}
// Full page load — include polling trigger
ctx.Response.Headers.Append("HX-Trigger-After-Settle",
"""{"startPolling": {"interval": 1000, "target": "#progress-region"}}""");
return await ctx.WriteHtmxPage(page, title: "Processing");
}
}
```