f6ae86617c
Co-authored-by: Copilot <copilot@github.com>
196 lines
6.1 KiB
Markdown
196 lines
6.1 KiB
Markdown
# Slider
|
||
|
||
A draggable range control. Use it when you want the user to pick a numeric value within a range — volume, brightness, price range, font size.
|
||
|
||
---
|
||
|
||
## Quick example
|
||
|
||
```csharp
|
||
new Slider(
|
||
id: "volume",
|
||
name: "volume",
|
||
label: "Volume")
|
||
```
|
||
|
||
Defaults to a 0–100 range with a starting value of 50.
|
||
|
||
---
|
||
|
||
## All the options
|
||
|
||
```csharp
|
||
public Slider(
|
||
string id,
|
||
string name = "",
|
||
int min = 0,
|
||
int max = 100,
|
||
int step = 1,
|
||
int value = 50,
|
||
string label = "",
|
||
string description = "",
|
||
string extraClasses = "",
|
||
string hxAttrs = "")
|
||
```
|
||
|
||
| Parameter | What it does |
|
||
|---|---|
|
||
| `id` | The element id. Also used by `<label for="...">`. |
|
||
| `name` | Form field name. |
|
||
| `min` | Lowest selectable value. |
|
||
| `max` | Highest selectable value. |
|
||
| `step` | How much the value changes per tick. |
|
||
| `value` | Starting position of the thumb. |
|
||
| `label` | Visible text label above the slider. |
|
||
| `description` | Small hint text below the slider. |
|
||
| `extraClasses` | Additional Tailwind classes on the `<input>`. |
|
||
| `hxAttrs` | Extra HTML attributes appended verbatim. |
|
||
|
||
---
|
||
|
||
## Real-world examples
|
||
|
||
### Brightness setting (stepped)
|
||
|
||
```csharp
|
||
new Slider(
|
||
id: "brightness",
|
||
name: "brightness",
|
||
min: 10,
|
||
max: 100,
|
||
step: 10,
|
||
value: 70,
|
||
label: "Brightness",
|
||
description: "10 to 100")
|
||
```
|
||
|
||
### Font size with live HTMX update
|
||
|
||
```csharp
|
||
new Slider(
|
||
id: "fontSize",
|
||
name: "fontSize",
|
||
min: 12,
|
||
max: 32,
|
||
value: 16,
|
||
label: "Font size",
|
||
hxAttrs: """hx-post="/settings/font-size" hx-trigger="change"""")
|
||
```
|
||
|
||
### Reading the value in a form handler
|
||
|
||
```csharp
|
||
public record Command([property: FromForm] int Volume);
|
||
// command.Volume == 0..100
|
||
```
|
||
|
||
---
|
||
|
||
## How it works
|
||
|
||
Slider renders a native `<input type="range">`. The thumb and active track colour follow your primary theme colour via `accent-primary`. No JavaScript is needed — the browser handles the drag interaction natively.
|
||
|
||
```csharp
|
||
public record Command([property: FromForm] int Volume);
|
||
|
||
// command.Volume is the slider value at submit time
|
||
```
|
||
|
||
---
|
||
|
||
## Tips and tricks
|
||
|
||
- Display the current numeric value next to the slider by adding a small `<output>` element and a JS `input` event listener: `slider.addEventListener('input', e => output.value = e.target.value)`. This can be added via `hxAttrs: "oninput=\"document.getElementById('vol-val').textContent=this.value\""`.
|
||
- `value` is the **initial** server-rendered position. After the user moves the slider, only the form submission captures the new value.
|
||
- Use `step` to snap to meaningful increments (e.g. `step: 5` for a 0–100 percentage slider).
|
||
- `accent-primary` is supported in all modern browsers and requires no custom CSS.
|
||
- `accent-primary` is supported in all modern browsers and requires no custom CSS.
|
||
|
||
---
|
||
|
||
## Complete page example
|
||
|
||
**`Templates/AudioSettingsPage.htmx`**
|
||
```html
|
||
<div class="max-w-md mx-auto py-10">
|
||
<h1 class="text-2xl font-bold mb-6">Audio settings</h1>
|
||
<form method="post" action="/settings/audio">
|
||
$$AntiforgeryToken$$
|
||
<div class="space-y-6 mb-8">
|
||
$$VolumeSlider$$
|
||
$$BassSlider$$
|
||
$$TrebleSlider$$
|
||
</div>
|
||
$$SaveBtn$$
|
||
</form>
|
||
$$SuccessAlert$$
|
||
</div>
|
||
```
|
||
|
||
**`Templates/AudioSettingsPage.htmx.cs`**
|
||
```csharp
|
||
namespace Htmx.ApiDemo.Templates;
|
||
|
||
public sealed class AudioSettingsPage : AudioSettingsPageBase
|
||
{
|
||
private readonly IHtmxComponent _volume;
|
||
private readonly IHtmxComponent _bass;
|
||
private readonly IHtmxComponent _treble;
|
||
private readonly IHtmxComponent _save;
|
||
private readonly IHtmxComponent _success;
|
||
private readonly byte[] _afToken;
|
||
|
||
public AudioSettingsPage(
|
||
IAntiforgery af,
|
||
HttpContext ctx,
|
||
AudioPrefs? prefs = null,
|
||
bool saved = false)
|
||
{
|
||
var tokens = af.GetAndStoreTokens(ctx);
|
||
_afToken = $"""<input type="hidden" name="{tokens.FormFieldName}" value="{tokens.RequestToken}">""".ToUtf8Bytes();
|
||
|
||
_volume = new Components.Slider(id: "volume", name: "volume", label: "Volume", value: prefs?.Volume ?? 70, description: "0 – 100");
|
||
_bass = new Components.Slider(id: "bass", name: "bass", label: "Bass", value: prefs?.Bass ?? 50, min: -10, max: 10, step: 1);
|
||
_treble = new Components.Slider(id: "treble", name: "treble", label: "Treble", value: prefs?.Treble ?? 50, min: -10, max: 10, step: 1);
|
||
_save = new Components.Button("Save", type: "submit");
|
||
_success = saved ? new Components.Alert(title: "Audio settings saved.") : HtmxEmpty.Instance;
|
||
}
|
||
|
||
protected override void RenderAntiforgeryToken(HtmxRenderContext ctx) => ctx.Writer.WriteUtf8(_afToken);
|
||
protected override void RenderVolumeSlider(HtmxRenderContext ctx) => _volume.Render(ctx.Next());
|
||
protected override void RenderBassSlider(HtmxRenderContext ctx) => _bass.Render(ctx.Next());
|
||
protected override void RenderTrebleSlider(HtmxRenderContext ctx) => _treble.Render(ctx.Next());
|
||
protected override void RenderSaveBtn(HtmxRenderContext ctx) => _save.Render(ctx.Next());
|
||
protected override void RenderSuccessAlert(HtmxRenderContext ctx) => _success.Render(ctx.Next());
|
||
}
|
||
```
|
||
|
||
**POST handler**
|
||
```csharp
|
||
[Handler]
|
||
[MapPost("/settings/audio")]
|
||
public static partial class PostAudioSettingsHandler
|
||
{
|
||
public record Command(
|
||
[property: FromForm] int Volume,
|
||
[property: FromForm] int Bass,
|
||
[property: FromForm] int Treble);
|
||
|
||
private static Task<IResult> HandleAsync(
|
||
[AsParameters] Command cmd, HttpContext ctx, IAntiforgery af, CancellationToken ct)
|
||
{
|
||
var prefs = new AudioPrefs(cmd.Volume, cmd.Bass, cmd.Treble);
|
||
// Persist prefs…
|
||
return ctx.WriteHtmxPage(
|
||
new AudioSettingsPage(af, ctx, prefs, saved: true), title: "Audio settings");
|
||
}
|
||
}
|
||
|
||
public record AudioPrefs(int Volume, int Bass, int Treble);
|
||
```
|
||
|
||
**`AppJsonSerializerContext.cs`**
|
||
```csharp
|
||
[JsonSerializable(typeof(PostAudioSettingsHandler.Command), TypeInfoPropertyName = "AudioSettingsCommand")]
|
||
```
|