UNPKG

@zag-js/slider

Version:

Core logic for the slider widget implemented as a state machine

327 lines (324 loc) • 9.85 kB
import { Machine, EventObject, Service } from '@zag-js/core'; import { PropTypes, RequiredBy, DirectionProperty, CommonProperties } from '@zag-js/types'; type ThumbCollisionBehavior = "none" | "push" | "swap"; type ThumbAlignment = "contain" | "center"; interface ValueChangeDetails { value: number[]; } interface FocusChangeDetails { focusedIndex: number; value: number[]; } interface ValueTextDetails { value: number; index: number; } type ElementIds = Partial<{ root: string; thumb: (index: number) => string; hiddenInput: (index: number) => string; control: string; track: string; range: string; label: string; valueText: string; marker: (index: number) => string; }>; interface SliderProps extends DirectionProperty, CommonProperties { /** * The ids of the elements in the slider. Useful for composition. */ ids?: ElementIds | undefined; /** * The aria-label of each slider thumb. Useful for providing an accessible name to the slider */ "aria-label"?: string[] | undefined; /** * The `id` of the elements that labels each slider thumb. Useful for providing an accessible name to the slider */ "aria-labelledby"?: string[] | undefined; /** * The name associated with each slider thumb (when used in a form) */ name?: string | undefined; /** * The associate form of the underlying input element. */ form?: string | undefined; /** * The controlled value of the slider */ value?: number[] | undefined; /** * The initial value of the slider when rendered. * Use when you don't need to control the value of the slider. */ defaultValue?: number[] | undefined; /** * Whether the slider is disabled */ disabled?: boolean | undefined; /** * Whether the slider is read-only */ readOnly?: boolean | undefined; /** * Whether the slider is invalid */ invalid?: boolean | undefined; /** * Function invoked when the value of the slider changes */ onValueChange?: ((details: ValueChangeDetails) => void) | undefined; /** * Function invoked when the slider value change is done */ onValueChangeEnd?: ((details: ValueChangeDetails) => void) | undefined; /** * Function invoked when the slider's focused index changes */ onFocusChange?: ((details: FocusChangeDetails) => void) | undefined; /** * Function that returns a human readable value for the slider thumb */ getAriaValueText?: ((details: ValueTextDetails) => string) | undefined; /** * The minimum value of the slider * @default 0 */ min?: number | undefined; /** * The maximum value of the slider * @default 100 */ max?: number | undefined; /** * The step value of the slider * @default 1 */ step?: number | undefined; /** * The step value of the slider when the `Shift` key is held, or the * `PageUp`/`PageDown` keys are used. * @default 10 * step */ largeStep?: number | undefined; /** * The minimum permitted steps between multiple thumbs. * * `minStepsBetweenThumbs` * `step` should reflect the gap between the thumbs. * * - `step: 1` and `minStepsBetweenThumbs: 10` => gap is `10` * - `step: 10` and `minStepsBetweenThumbs: 2` => gap is `20` * * @default 0 */ minStepsBetweenThumbs?: number | undefined; /** * The orientation of the slider * @default "horizontal" */ orientation?: "vertical" | "horizontal" | undefined; /** * The origin of the slider range. The track is filled from the origin * to the thumb for single values. * - "start": Useful when the value represents an absolute value * - "center": Useful when the value represents an offset (relative) * - "end": Useful when the value represents an offset from the end * * @default "start" */ origin?: "start" | "center" | "end" | undefined; /** * The alignment of the slider thumb relative to the track * - `center`: the thumb will extend beyond the bounds of the slider track. * - `contain`: the thumb will be contained within the bounds of the track. * * @default "contain" */ thumbAlignment?: "contain" | "center" | undefined; /** * The slider thumbs dimensions */ thumbSize?: { width: number; height: number; } | undefined; /** * Controls how thumbs behave when they collide during pointer interactions. * - `none` (default): Thumbs cannot move past each other; excess movement is ignored. * - `push`: Thumbs push each other without restoring their previous positions when dragged back. * - `swap`: Thumbs swap places when dragged past each other. * * @default "none" */ thumbCollisionBehavior?: "none" | "push" | "swap" | undefined; } type PropsWithDefault = "dir" | "min" | "max" | "step" | "largeStep" | "orientation" | "defaultValue" | "origin" | "thumbAlignment" | "minStepsBetweenThumbs" | "thumbCollisionBehavior"; type Computed = Readonly<{ /** * @computed * Whether the slider thumb has been measured */ hasMeasuredThumbSize: boolean; /** * @computed * Whether the slider is interactive */ isInteractive: boolean; /** * @computed * Whether the slider is vertical */ isVertical: boolean; /** * @computed * Whether the slider is horizontal */ isHorizontal: boolean; /** * @computed * Whether the slider is in RTL mode */ isRtl: boolean; /** * @computed * The percentage of the slider value relative to the slider min/max */ valuePercent: number[]; /** * @computed * Whether the slider is disabled */ isDisabled: boolean; }>; interface Context { /** * The active index of the range slider. This represents * the currently dragged/focused thumb. */ focusedIndex: number; /** * The value of the range slider */ value: number[]; /** * The size of the slider thumbs */ thumbSize: Size | null; } interface SliderSchema { state: "idle" | "dragging" | "focus"; props: RequiredBy<SliderProps, PropsWithDefault>; context: Context; refs: { /** * The offset from the thumb center when pointer down occurs. * Used to maintain constant offset during drag. */ thumbDragOffset: { x: number; y: number; } | null; /** * The values when a thumb drag starts. * Used for swap collision behavior to determine swap direction. */ thumbDragStartValue: number[] | null; }; computed: Computed; event: EventObject; action: string; effect: string; guard: string; } type SliderService = Service<SliderSchema>; type SliderMachine = Machine<SliderSchema>; interface Size { width: number; height: number; } interface MarkerProps { value: number; } interface ThumbProps { index: number; name?: string | undefined; } interface DraggingIndicatorProps { index: number; } interface SliderApi<T extends PropTypes = PropTypes> { /** * The value of the slider. */ value: number[]; /** * Whether the slider is being dragged. */ dragging: boolean; /** * Whether the slider is focused. */ focused: boolean; /** * Function to set the value of the slider. */ setValue: (value: number[]) => void; /** * Returns the value of the thumb at the given index. */ getThumbValue: (index: number) => number; /** * Sets the value of the thumb at the given index. */ setThumbValue: (index: number, value: number) => void; /** * Returns the percent of the thumb at the given index. */ getValuePercent: (value: number) => number; /** * Returns the value of the thumb at the given percent. */ getPercentValue: (percent: number) => number; /** * Returns the percent of the thumb at the given index. */ getThumbPercent: (index: number) => number; /** * Sets the percent of the thumb at the given index. */ setThumbPercent: (index: number, percent: number) => void; /** * Returns the min value of the thumb at the given index. */ getThumbMin: (index: number) => number; /** * Returns the max value of the thumb at the given index. */ getThumbMax: (index: number) => number; /** * Function to increment the value of the slider at the given index. */ increment: (index: number) => void; /** * Function to decrement the value of the slider at the given index. */ decrement: (index: number) => void; /** * Function to focus the slider. This focuses the first thumb. */ focus: VoidFunction; getLabelProps: () => T["label"]; getRootProps: () => T["element"]; getValueTextProps: () => T["element"]; getTrackProps: () => T["element"]; getThumbProps: (props: ThumbProps) => T["element"]; getHiddenInputProps: (props: ThumbProps) => T["input"]; getRangeProps: () => T["element"]; getControlProps: () => T["element"]; getMarkerGroupProps: () => T["element"]; getMarkerProps: (props: MarkerProps) => T["element"]; getDraggingIndicatorProps: (props: DraggingIndicatorProps) => T["element"]; } export type { DraggingIndicatorProps, ElementIds, FocusChangeDetails, MarkerProps, SliderApi, SliderMachine, SliderProps, SliderSchema, SliderService, ThumbAlignment, ThumbCollisionBehavior, ThumbProps, ValueChangeDetails, ValueTextDetails };