Slider
A native range control.
<slider.Slider name="volume" value={50} label="Volume" />Installation
Run the command in the app module.
gx add sliderThe command writes these files.
| File | Path in the app |
|---|---|
Slider.gx |
ui/slider/Slider.gx |
Slider.fixtures.go |
ui/slider/Slider.fixtures.go |
styles.go |
ui/slider/styles.go |
Copy each file to its path in the app. Change each import of a registry package to the path of that package in the app.
package slider
props {
// Orientation sets the direction: Horizontal or Vertical.
Orientation Orientation = Horizontal
// Name is the form field name.
Name string = ""
// Min is the minimum value.
Min int = 0
// Max is the maximum value. A zero value is 100.
Max int = 100
// Value is the value of the first render.
Value int = 0
// Step is the size of one step. A zero value is 1.
Step int = 1
// Label is the accessible name of the slider.
Label string = ""
// Disabled disables the slider.
Disabled bool = false
// Class adds classes to the root element.
Class string = ""
// Attrs adds HTML attributes to the root element.
Attrs gx.Attrs = nil
}
<input type="range" name={p.Name} min={p.min()} max={p.max()} value={p.Value} step={p.step()} aria-label={p.Label} disabled={p.Disabled} data-gx-behavior style={p.fill()} class={p.class()} {...p.Attrs} />package slider
import "github.com/alternayte/gx"
var SliderFixtures = gx.Fixtures[SliderProps]{
"Half": {Name: "volume", Value: 50, Label: "Volume"},
"Full": {Name: "volume", Value: 100, Label: "Volume"},
"Empty": {Name: "volume", Value: 0, Label: "Volume"},
"Range": {Name: "price", Min: 10, Max: 200, Value: 80, Step: 10, Label: "Price"},
"Disabled": {Name: "volume", Value: 50, Label: "Volume", Disabled: true},
"Vertical": {Name: "level", Value: 50, Label: "Level", Orientation: Vertical, Class: "h-44"},
}package slider
import (
"strconv"
"github.com/alternayte/gx"
)
// Orientation is the direction of a slider.
type Orientation string
// The orientations of slider.Slider.
const (
Horizontal Orientation = "horizontal"
Vertical Orientation = "vertical"
)
// The track and the thumb of a native range input are pseudo-elements with
// one name per engine. WebKit and Blink have no pseudo-element for the range,
// so the track paints it with a gradient up to --gx-fill; Gecko has
// ::-moz-range-progress.
var orientationClass = gx.Enum[Orientation]{
Horizontal: "h-1.5 w-full [&::-webkit-slider-runnable-track]:h-1.5 [&::-webkit-slider-runnable-track]:bg-[linear-gradient(to_right,var(--primary)_var(--gx-fill),var(--muted)_var(--gx-fill))] [&::-webkit-slider-thumb]:-mt-[5px] [&::-moz-range-track]:h-1.5 [&::-moz-range-progress]:h-1.5",
Vertical: "h-full min-h-44 w-1.5 [direction:rtl] [writing-mode:vertical-lr] [&::-webkit-slider-runnable-track]:w-1.5 [&::-webkit-slider-runnable-track]:bg-[linear-gradient(to_top,var(--primary)_var(--gx-fill),var(--muted)_var(--gx-fill))] [&::-webkit-slider-thumb]:-ml-[5px] [&::-moz-range-track]:w-1.5 [&::-moz-range-progress]:w-1.5",
}
// class returns the classes of one slider.
func (p SliderProps) class() string {
const base = "block touch-none appearance-none bg-transparent outline-none select-none disabled:pointer-events-none disabled:opacity-50 [&::-webkit-slider-runnable-track]:rounded-full [&::-webkit-slider-thumb]:box-border [&::-webkit-slider-thumb]:size-4 [&::-webkit-slider-thumb]:appearance-none [&::-webkit-slider-thumb]:rounded-full [&::-webkit-slider-thumb]:border [&::-webkit-slider-thumb]:border-primary [&::-webkit-slider-thumb]:bg-white [&::-webkit-slider-thumb]:shadow-sm [&::-webkit-slider-thumb]:ring-ring/50 [&::-webkit-slider-thumb]:transition-[color,box-shadow] [&::-webkit-slider-thumb]:hover:ring-4 focus-visible:[&::-webkit-slider-thumb]:ring-4 motion-reduce:[&::-webkit-slider-thumb]:transition-none [&::-moz-range-track]:rounded-full [&::-moz-range-track]:bg-muted [&::-moz-range-progress]:rounded-full [&::-moz-range-progress]:bg-primary [&::-moz-range-thumb]:box-border [&::-moz-range-thumb]:size-4 [&::-moz-range-thumb]:rounded-full [&::-moz-range-thumb]:border [&::-moz-range-thumb]:border-primary [&::-moz-range-thumb]:bg-white [&::-moz-range-thumb]:shadow-sm [&::-moz-range-thumb]:ring-ring/50 [&::-moz-range-thumb]:transition-[color,box-shadow] [&::-moz-range-thumb]:hover:ring-4 focus-visible:[&::-moz-range-thumb]:ring-4 motion-reduce:[&::-moz-range-thumb]:transition-none"
orientation := p.Orientation
if orientation == "" {
orientation = Horizontal
}
return gx.Cx(base, orientationClass[orientation], p.Class)
}
// min returns the minimum value.
func (p SliderProps) min() int {
return p.Min
}
// max returns the maximum value; a zero value is 100.
func (p SliderProps) max() int {
if p.Max == 0 {
return 100
}
return p.Max
}
// step returns the step; a zero value is 1.
func (p SliderProps) step() int {
if p.Step == 0 {
return 1
}
return p.Step
}
// fill returns the custom property that paints the range of the first
// render. The behaviour runtime updates it on input.
func (p SliderProps) fill() gx.Style {
span := p.max() - p.min()
v := p.Value - p.min()
if span <= 0 || v < 0 {
v, span = 0, 1
}
if v > span {
v = span
}
return gx.Style("--gx-fill: " + strconv.Itoa(v*100/span) + "%")
}The theme must define these tokens: --muted, --primary, --ring.
Usage
<slider.Slider name="volume" value={p.Volume} label="Volume" />
<slider.Slider name="level" value={p.Level} label="Level" orientation={slider.Vertical} />The component is a styled native <input type="range">.
The server paints the range for the first render. The behaviour runtime repaints it on input in Chrome and Safari. Firefox paints the range itself.
A slider has one thumb.
Examples
Half
<slider.Slider name="volume" value={50} label="Volume" />Full
<slider.Slider name="volume" value={100} label="Volume" />Empty
<slider.Slider name="volume" value={0} label="Volume" />Range
<slider.Slider name="price" min={10} max={200} value={80} step={10} label="Price" />Disabled
<slider.Slider name="volume" value={50} label="Volume" disabled />Vertical
<slider.Slider name="level" value={50} label="Level" orientation={slider.Vertical} class="h-44" />API reference
A tag sets a prop by its name with a lower-case first letter: Class is class.
slider.Slider
| Prop | Type | Default | Description |
|---|---|---|---|
Orientation |
Orientation |
Horizontal |
Orientation sets the direction: Horizontal or Vertical. |
Name |
string |
"" |
Name is the form field name. |
Min |
int |
0 |
Min is the minimum value. |
Max |
int |
100 |
Max is the maximum value. A zero value is 100. |
Value |
int |
0 |
Value is the value of the first render. |
Step |
int |
1 |
Step is the size of one step. A zero value is 1. |
Label |
string |
"" |
Label is the accessible name of the slider. |
Disabled |
bool |
false |
Disabled disables the slider. |
Class |
string |
"" |
Class adds classes to the root element. |
Attrs |
gx.Attrs |
nil |
Attrs adds HTML attributes to the root element. |
Do and do not
Do
- Set
Min,MaxandStepfor the range. - Give the slider a
Label.
Don't
- Do not use a slider for an exact number. Use an input.
- Do not omit the label.
Keyboard
| Key | Action |
|---|---|
| Tab | Moves focus to the slider. |
| Arrow keys | Changes the value by one step. |
| Home, End | Moves to the minimum or maximum. |