Mats Bosson 796ccab838 Slider component
Implements headless Slider with Root, Track, Range, and Thumb parts. Supports controlled/uncontrolled value, keyboard navigation (Arrow, Page, Home, End), clamping, step, orientation, and disabled state. 10 tests added, all 152 suite tests pass.
2026-03-29 08:30:31 +07:00

68 lines
1.8 KiB
TypeScript

import type { JSX } from "solid-js";
import { splitProps } from "solid-js";
import { useSliderContext } from "./slider-context";
/** Props for Slider.Thumb. */
export interface SliderThumbProps extends JSX.HTMLAttributes<HTMLSpanElement> {
/** Accessible label for the thumb. */
"aria-label"?: string;
}
/** The draggable thumb element. Handles keyboard navigation. */
export function SliderThumb(props: SliderThumbProps): JSX.Element {
const [local, rest] = splitProps(props, ["onKeyDown"]);
const ctx = useSliderContext();
const handleKeyDown: JSX.EventHandler<HTMLSpanElement, KeyboardEvent> = (e) => {
if (typeof local.onKeyDown === "function") local.onKeyDown(e);
if (ctx.disabled()) return;
const current = ctx.value();
const s = ctx.step();
switch (e.key) {
case "ArrowRight":
case "ArrowUp":
e.preventDefault();
ctx.setValue(current + s);
break;
case "ArrowLeft":
case "ArrowDown":
e.preventDefault();
ctx.setValue(current - s);
break;
case "PageUp":
e.preventDefault();
ctx.setValue(current + s * 10);
break;
case "PageDown":
e.preventDefault();
ctx.setValue(current - s * 10);
break;
case "Home":
e.preventDefault();
ctx.setValue(ctx.min());
break;
case "End":
e.preventDefault();
ctx.setValue(ctx.max());
break;
}
};
return (
<span
role="slider"
aria-valuemin={ctx.min()}
aria-valuemax={ctx.max()}
aria-valuenow={ctx.value()}
aria-orientation={ctx.orientation()}
aria-disabled={ctx.disabled() || undefined}
data-disabled={ctx.disabled() || undefined}
tabIndex={ctx.disabled() ? -1 : 0}
onKeyDown={handleKeyDown}
{...rest}
/>
);
}