Skip to main content
0.78.0
View Zag.js on Github
Join the Discord server

Angle Slider

An angle slider is a circular dial that allows users to select an angle, typically in degrees, within a 360° range. It provides an intuitive way to control rotations or orientations, offering accessibility features.

0 degrees
Properties

Features

  • Fully managed keyboard navigation.
  • Supports touch or click on track to update value.
  • Supports Right-to-Left directionality.

Installation

To use the angle slider machine in your project, run the following command in your command line:

npm install @zag-js/angle-slider @zag-js/react # or yarn add @zag-js/angle-slider @zag-js/react

This command will install the framework agnostic angle slider logic and the reactive utilities for your framework of choice.

Anatomy

To set up the angle slider correctly, you'll need to understand its anatomy and how we name its parts.

Each part includes a data-part attribute to help identify them in the DOM.

No anatomy available for angle-slider

Usage

First, import the angle-slider package into your project

import * as angleSlider from "@zag-js/angle-slider"

The angle slider package exports two key functions:

  • machine — The state machine logic for the angle slider widget as described in the WAI-ARIA spec.
  • connect — The function that translates the machine's state to JSX attributes and event handlers.

You'll need to provide a unique id to the useMachine hook. This is used to ensure that every part has a unique identifier.

Next, import the required hooks and functions for your framework and use the angle slider machine in your project 🔥

import * as angleSlider from "@zag-js/angle-slider" import { normalizeProps, useMachine } from "@zag-js/react" export function AngleSlider() { const [state, send] = useMachine(angleSlider.machine({ id: "1" })) const api = angleSlider.connect(state, send, normalizeProps) return ( <div {...api.getRootProps()}> <label {...api.getLabelProps()}>Wind direction</label> <div {...api.getControlProps()}> <div {...api.getThumbProps()}></div> <div {...api.getMarkerGroupProps()}> {[0, 45, 90, 135, 180, 225, 270, 315].map((value) => ( <div key={value} {...api.getMarkerProps({ value })}></div> ))} </div> </div> <div {...api.getValueTextProps()}>{api.value} degrees</div> <input {...api.getHiddenInputProps()} /> </div> ) }

Setting the initial value

const [state, send] = useMachine( angleSlider.machine({ value: 45, }), )

Setting the value's granularity

By default, the granularity, is 1, meaning that the value is always an integer. You can change the step attribute to control the granularity.

For example, If you need a value between 5 and 10, accurate to two decimal places, you should set the value of step to 0.01:

const [state, send] = useMachine( angleSlider.machine({ step: 0.01, }), )

Listening for changes

When the angle slider value changes, the onValueChange and onValueChangeEnd callbacks are invoked. You can use this to setup custom behaviors in your app.

const [state, send] = useMachine( angleSlider.machine({ onValueChange(details) { console.log("value is changing to:", details) }, onValueChangeEnd(details) { console.log("value has changed to:", details) }, }), )

Usage within forms

To use angle slider within forms, use the exposed hiddenInputProps from the connect function and ensure you pass name value to the machine's context. It will render a hidden input and ensure the value changes get propagated to the form correctly.

const [state, send] = useMachine( angleSlider.machine({ name: "wind-direction", }), )

Using angle slider marks

To show marks or ticks along the angle slider track, use the exposed api.getMarkerProps() method to position the angle slider marks at desired angles.

//... <div {...api.getRootProps()}> <label {...api.getLabelProps()}>Wind direction</label> <div {...api.getControlProps()}> <div {...api.getThumbProps()}></div> <div {...api.getMarkerGroupProps()}> {[0, 45, 90, 135, 180, 225, 270, 315].map((value) => ( <div key={value} {...api.getMarkerProps({ value })}></div> ))} </div> </div> <div {...api.getValueTextProps()}>{api.value} degrees</div> <input {...api.getHiddenInputProps()} /> </div> //...

Styling guide

Earlier, we mentioned that each angle slider part has a data-part attribute added to them to select and style them in the DOM.

Disabled State

When the angle slider is disabled, the data-disabled attribute is added to the root, label, control, thumb and marker.

[data-part="root"][data-disabled] { /* styles for root disabled state */ } [data-part="label"][data-disabled] { /* styles for label disabled state */ } [data-part="control"][data-disabled] { /* styles for control disabled state */ } [data-part="thumb"][data-disabled] { /* styles for thumb disabled state */ } [data-part="range"][data-disabled] { /* styles for thumb disabled state */ }

Invalid State

When the slider is invalid, the data-invalid attribute is added to the root, track, range, label, and thumb parts.

[data-part="root"][data-invalid] { /* styles for root invalid state */ } [data-part="label"][data-invalid] { /* styles for label invalid state */ } [data-part="control"][data-invalid] { /* styles for control invalid state */ } [data-part="valueText"][data-invalid] { /* styles for output invalid state */ } [data-part="thumb"][data-invalid] { /* styles for thumb invalid state */ } [data-part="marker"][data-invalid] { /* styles for marker invalid state */ }

Styling the markers

[data-part="marker"][data-state="(at|under|over)-value"] { /* styles for when the value exceeds the marker's value */ }

Methods and Properties

Machine Context

The slider machine exposes the following context properties:

  • idsPartial<ElementIds>The ids of the elements in the machine. Useful for composition.
  • stepnumberThe step value for the slider.
  • valuenumberThe value of the slider.
  • onValueChange(details: ValueChangeDetails) => voidThe callback function for when the value changes.
  • onValueChangeEnd(details: ValueChangeDetails) => voidThe callback function for when the value changes ends.
  • disabledbooleanWhether the slider is disabled.
  • readOnlybooleanWhether the slider is read-only.
  • invalidbooleanWhether the slider is invalid.
  • namestringThe name of the slider. Useful for form submission.
  • dir"ltr" | "rtl"The document's text/writing direction.
  • idstringThe unique identifier of the machine.
  • getRootNode() => ShadowRoot | Node | DocumentA root node to correctly resolve document in custom environments. E.x.: Iframes, Electron.

Machine API

The slider api exposes the following methods:

  • valuenumberThe current value of the angle slider
  • valueAsDegreestringThe current value as a degree string
  • setValue(value: number) => voidSets the value of the angle slider
  • draggingbooleanWhether the slider is being dragged.

Data Attributes

Root
data-scope
angle-slider
data-part
root
data-disabled
Present when disabled
data-invalid
Present when invalid
data-readonly
Present when read-only
Label
data-scope
angle-slider
data-part
label
data-disabled
Present when disabled
data-invalid
Present when invalid
data-readonly
Present when read-only
Control
data-scope
angle-slider
data-part
control
data-disabled
Present when disabled
data-invalid
Present when invalid
data-readonly
Present when read-only
Thumb
data-scope
angle-slider
data-part
thumb
data-disabled
Present when disabled
data-invalid
Present when invalid
data-readonly
Present when read-only
Marker
data-scope
angle-slider
data-part
marker
data-value
The value of the item
data-disabled
Present when disabled

Keyboard Interactions

  • ArrowRight
    Increments the angle slider based on defined step
  • ArrowLeft
    Decrements the angle slider based on defined step
  • ArrowUp
    Decreases the value by the step amount.
  • ArrowDown
    Increases the value by the step amount.
  • Shift + ArrowUp
    Decreases the value by a larger step
  • Shift + ArrowDown
    Increases the value by a larger step
  • Home
    Sets the value to 0 degrees.
  • End
    Sets the value to 360 degrees.

Edit this page on GitHub

Proudly made in🇳🇬by Segun Adebayo

Copyright © 2024
On this page