Rask
Rask
v0.23.1-alpha.0.478

Components

Slider

A value chosen along a track, or a range between two thumbs. Each thumb is a real range input, so dragging, a press on the track and the keys are the browser's own.

Drawn and behaving as Flux UI's slider does, measured against its live docs.

Slider

SliderExample.cs
namespace Rask.Site.Features.UiDocs.Examples;

public sealed partial class SliderExample : Component
{
    private int Amount { get; set; } = 50;

    protected override Component? Render() =>
        Ui.Slider.Bind(() => Amount).Class("max-w-sm");
}
Live result

Min/max/step

SliderStepExample.cs
namespace Rask.Site.Features.UiDocs.Examples;

public sealed partial class SliderStepExample : Component
{
    private int _amount = 50;

    protected override Component? Render() =>
        Ui.Slider.Value(_amount).Min(0).Max(100).Step(10).OnChange(amount => { _amount = amount; }).Class("max-w-sm");
}
Live result

Displaying value

The value follows the thumb while it moves, so a label beside it can show the number.

SliderValueExample.cs
using System.Globalization;

namespace Rask.Site.Features.UiDocs.Examples;

public sealed partial class SliderValueExample : Component
{
    private int Amount { get; set; } = 50;

    protected override Component? Render() =>
        Ui.Field.Class("max-w-sm")[
            Ui.Label.Trailing(Span.Class("tabular-nums").Data("testid", "ui-slider-amount")[Amount.ToString(CultureInfo.InvariantCulture)])[
                "Corner radius"
            ],
            Ui.Slider.Bind(() => Amount).Live()
        ];
}
Live result

With input

A number input beside the slider, both bound to the same member, in one field.

SliderInputExample.cs
namespace Rask.Site.Features.UiDocs.Examples;

public sealed partial class SliderInputExample : Component
{
    private int Amount { get; set; } = 25;

    protected override Component? Render() =>
        Ui.Field.Class("max-w-sm")[
            Ui.Label["Corner radius"],
            Div.Class("-mt-2 flex items-center gap-4")[
                Ui.Slider.Bind(() => Amount).Live(),
                Ui.Input.Bind(() => Amount).Live().Type(InputType.Number).Sm.Class("max-w-18")
            ]
        ];
}
Live result

Big steps

Hold Shift and press an arrow key to move by the big step; Page Up and Page Down move by it too.

SliderBigStepExample.cs
using System.Globalization;

namespace Rask.Site.Features.UiDocs.Examples;

public sealed partial class SliderBigStepExample : Component
{
    private int _amount = 50;

    protected override Component? Render() =>
        Div.Class("max-w-sm space-y-3")[
            Ui.Slider.Value(_amount).Step(1).BigStep(10).OnChange(amount => { _amount = amount; }),
            Ui.Text[
                "Amount: ",
                Span.Class("tabular-nums").Data("testid", "ui-slider-big-value")[_amount.ToString(CultureInfo.InvariantCulture)]
            ]
        ];
}
Live result

Amount: 50

Step marks

SliderMarksExample.cs
namespace Rask.Site.Features.UiDocs.Examples;

public sealed partial class SliderMarksExample : Component
{
    private int _step = 3;

    protected override Component? Render() =>
        Ui.Slider.Value(_step).Min(1).Max(5).OnChange(step => { _step = step; }).Class("max-w-sm")[
            Enumerable.Range(1, 5).Select(step => Ui.SliderTick.Key(step).Value(step))
        ];
}
Live result

Step dots

Inside puts the ticks on the track, and Dot draws each as a dot.

SliderDotsExample.cs
namespace Rask.Site.Features.UiDocs.Examples;

public sealed partial class SliderDotsExample : Component
{
    private int _step = 4;

    protected override Component? Render() =>
        Ui.Slider.Value(_step).Inside.Min(1).Max(5).TrackClass("h-5").ThumbClass("size-6").OnChange(step => { _step = step; })
            .Class("max-w-sm")[
            Enumerable.Range(1, 5).Select(step => Ui.SliderTick.Key(step).Value(step).Dot)
        ];
}
Live result

Numbered steps

SliderNumbersExample.cs
using System.Globalization;

namespace Rask.Site.Features.UiDocs.Examples;

public sealed partial class SliderNumbersExample : Component
{
    private int _step = 3;

    protected override Component? Render() =>
        Ui.Slider.Value(_step).Min(1).Max(5).OnChange(step => { _step = step; }).Class("max-w-sm")[
            Enumerable.Range(1, 5).Select(step => Ui.SliderTick.Key(step).Value(step)[step.ToString(CultureInfo.InvariantCulture)])
        ];
}
Live result
1
2
3
4
5

Custom steps

A tick's children are its label. Pressing one moves the thumb there.

SliderCustomStepsExample.cs
namespace Rask.Site.Features.UiDocs.Examples;

public sealed partial class SliderCustomStepsExample : Component
{
    private int _level = 3;

    protected override Component? Render() =>
        Ui.Slider.Value(_level).Min(1).Max(5).OnChange(level => { _level = level; }).Class("max-w-sm")[
            Ui.SliderTick.Value(1)["Low"],
            Ui.SliderTick.Value(3)["Mid"],
            Ui.SliderTick.Value(5)["High"]
        ];
}
Live result
Low
Mid
High

Range slider

Two thumbs over an array of two: int[] Range = [20, 80].

SliderRangeExample.cs
namespace Rask.Site.Features.UiDocs.Examples;

public sealed partial class SliderRangeExample : Component
{
    private int[] Range { get; set; } = [20, 80];

    protected override Component? Render() =>
        Ui.Slider.Bind(() => Range).Range().Class("max-w-sm");
}
Live result

Min steps between

The thumbs never come closer than ten steps.

SliderMinStepsExample.cs
namespace Rask.Site.Features.UiDocs.Examples;

public sealed partial class SliderMinStepsExample : Component
{
    private int[] _range = [40, 60];

    protected override Component? Render() =>
        Ui.Slider.Value(_range).Range().Step(1).MinStepsBetween(10).OnChange(range => { _range = range; }).Class("max-w-sm");
}
Live result

Displaying values

SliderRangeValuesExample.cs
using System.Globalization;

namespace Rask.Site.Features.UiDocs.Examples;

public sealed partial class SliderRangeValuesExample : Component
{
    private int[] Range { get; set; } = [200, 800];

    protected override Component? Render() =>
        Div.Class("relative max-w-sm")[
            Ui.Field[
                Ui.Label.Trailing(Span.Data("testid", "ui-slider-price")[
                    "$", Span.Class("tabular-nums")[Number(Range[0])], " – $", Span.Class("tabular-nums")[Number(Range[1])]
                ])["Price range"],
                Ui.Slider.Bind(() => Range).Live().Range().Min(0).Max(990).Step(10).MinStepsBetween(10).BigStep(100)
            ]
        ];

    private static string Number(int value) => value.ToString(CultureInfo.InvariantCulture);
}
Live result

Custom styles

TrackClass and ThumbClass style the two parts.

SliderStylesExample.cs
namespace Rask.Site.Features.UiDocs.Examples;

public sealed partial class SliderStylesExample : Component
{
    private int _amount = 50;

    protected override Component? Render() =>
        Ui.Slider.Value(_amount).TrackClass("h-5").ThumbClass("size-5").OnChange(amount => { _amount = amount; }).Class("max-w-sm");
}
Live result

Behaviour

The value follows the thumb

A slider binds a number: an int, long, float, double or decimal. The bound member (or OnChange) hears every step while the thumb is dragged, which is what lets a label beside it show the number; a slider the page owns draws the Value it was given, so keep it in state.

A range is the same slider over an array of two (int[], double[], decimal[]), and every change writes a new array. The thumbs do not cross, MinStepsBetween keeps them that many steps apart, and a press on the track moves the nearer one.

The browser does the moving

Each thumb holds a real <input type="range">, invisible and laid over the stretch of track that thumb can reach. A range's two inputs say aria-valuetext="200 start range" and "800 end range", and carry their neighbour as their own max and min. BigStep is the one thing a range input cannot do alone, so the input carries data-rask-big-step and the runtime steps it.

Key
What happens
ArrowRight, ArrowUpOne step up. With Shift, the big step.
ArrowLeft, ArrowDownOne step down. With Shift, the big step.
Page Up, Page DownThe big step, or a tenth of the track when there is none.
Home, EndThe two ends, or the other thumb less the steps kept between them.

A label is the field's

Flux's slider takes no label of its own, and neither does this one: put it in a Ui.Field with a Ui.Label (and a Ui.Description), and every thumb is named by that label (aria-labelledby) and described by the rest. A bound slider outside a field still shows its own message.

Ticks

Ui.SliderTick.Value(n) children mark values: a line, a .Dot, or whatever you put inside. They sit under the track, or on it with .Inside (TickPosition). Each carries data-active while the fill reaches it and data-current while a thumb is on it, and pressing one moves the thumb there. A size-6 in ThumbClass is also where the fill and the ticks learn the thumb's size (--ui-slider-thumb, 1rem unless set).

Reference

Ui.Slider

flux:slider

A value along a track, or a range between two thumbs. Its children are its ticks.

Step
Takes
What it does
.Bind(…)() => a number, or an array of twoThe model's number it reads and writes: () => m.Amount, or an array of two for a range.
.Value(…)a number, or an array of twoThe value, for a page that holds it itself.
.OnChange(…)a handler given the new valueRuns with every step the thumb moves.
.AfterBind(…)a handler given the new valueRuns after Bind has written the model.
.Validate(…)a validatorA rule the value is checked by when it is written.
.Live()boolWrites the model as the thumb moves, not with the next action.
.Range()boolTwo thumbs, over an array of two.
.Min(…)DoubleThe lowest value. 0 when unset.
.Max(…)DoubleThe highest value. 100 when unset.
.Step(…)DoubleHow far one step moves the thumb. 1 when unset.
.BigStep(…)DoubleHow far Shift with an arrow, and Page Up or Page Down, move it.
.MinStepsBetween(…)intHow many steps a range's two thumbs keep between them.
.TickPosition(…)
Ui.SliderTickPosition
Below
Inside

Each is a step of its own too, unless an HTML tag has its name.

Where the ticks sit: under the track when unset, or Inside it.
.TrackClass(…)stringClasses for the track: h-5.
.ThumbClass(…)stringClasses for the thumbs: size-5.
.Disabled()boolDims it and takes it out of reach.
.ShowValidation()boolFalse leaves the member's message to a Ui.Error placed elsewhere.

Ui.SliderTick

flux:slider.tick

A mark at a value. Its children are its label; a line when it has none.

Step
Takes
What it does
.Value(…)DoubleThe value it marks. Required.
.Variant(…)
Ui.SliderTickVariant
Line
Dot

Each is a step of its own too, unless an HTML tag has its name.

A line when unset, or a Dot.
Confirm