组件
滑动输入 Slider
悬浮时展开轨道、数值标签实时跟随的可调输入控件。
基础示例
Loading…
pnpm dlx wui@latest add @wui/slider"use client"
import * as React from "react"
import { Slider as SliderPrimitive } from "radix-ui"
import { cva } from "class-variance-authority"
import { cn } from "@/lib/utils"
const sliderTrackVariants = cva(
"relative h-1 w-full grow cursor-pointer overflow-hidden rounded-full bg-muted transition-[height] duration-200 ease-out motion-reduce:transition-none",
{
variants: {
variant: {
default: "group-hover:h-2 group-focus-within:h-2",
expand: "group-hover:h-5 group-focus-within:h-5",
},
},
defaultVariants: { variant: "default" },
}
)
export interface SliderProps
extends React.ComponentProps<typeof SliderPrimitive.Root> {
/** Track expansion treatment used on hover and focus. @default "default" */
variant?: "default" | "expand"
/** Controls when the value label is visible. @default "hover" */
showValue?: "hover" | "always" | "never"
/** Formats the value displayed above each thumb. */
formatValue?: (value: number) => React.ReactNode
}
/** An adjustable range input with a track that expands on interaction. */
function Slider({
className,
value,
defaultValue = [50],
onValueChange,
min = 0,
max = 100,
variant = "default",
showValue = "hover",
formatValue = (current) => current,
disabled,
...props
}: SliderProps) {
const [internalValue, setInternalValue] = React.useState(defaultValue)
const currentValues = value ?? internalValue
function handleValueChange(next: number[]) {
if (value === undefined) setInternalValue(next)
onValueChange?.(next)
}
return (
<SliderPrimitive.Root
data-slot="slider"
className={cn(
"group relative flex w-full touch-none select-none items-center py-4 outline-none data-[disabled]:cursor-not-allowed data-[disabled]:opacity-50",
className
)}
value={value}
defaultValue={defaultValue}
onValueChange={handleValueChange}
min={min}
max={max}
disabled={disabled}
{...props}
>
<SliderPrimitive.Track
data-slot="slider-track"
data-variant={variant}
className={sliderTrackVariants({ variant })}
>
<SliderPrimitive.Range
data-slot="slider-range"
className="absolute h-full bg-primary"
/>
</SliderPrimitive.Track>
{currentValues.map((current, index) => (
<SliderPrimitive.Thumb
key={index}
data-slot="slider-thumb"
className="group/thumb relative block size-5 cursor-grab rounded-full border border-border/70 bg-background shadow-sm outline-none transition-[box-shadow,transform] duration-150 hover:scale-105 focus-visible:ring-[4px] focus-visible:ring-ring/30 active:scale-95 active:cursor-grabbing disabled:pointer-events-none motion-reduce:transition-none"
>
{showValue !== "never" ? (
<span
data-slot="slider-value"
className={cn(
"pointer-events-none absolute bottom-[calc(100%+9px)] left-1/2 -translate-x-1/2 whitespace-nowrap rounded-md bg-foreground px-2 py-1 text-[11px] font-medium leading-none tabular-nums text-background shadow-sm transition-[opacity,transform] duration-150 motion-reduce:transition-none",
showValue === "always"
? "opacity-100"
: "translate-y-1 opacity-0 group-hover:translate-y-0 group-hover:opacity-100 group-focus-within:translate-y-0 group-focus-within:opacity-100"
)}
>
{formatValue(current)}
</span>
) : null}
</SliderPrimitive.Thumb>
))}
</SliderPrimitive.Root>
)
}
export { Slider, sliderTrackVariants }
组件作用
Slider 用于音量、亮度、缩放和数值范围等连续调整。轨道在悬浮或键盘聚焦时平滑变粗,滑块上方的标签跟随位置并实时显示当前值。
组件属性
| Prop | Type | Default | Description |
|---|---|---|---|
| variant | "default" | "expand" | default | Track expansion treatment used on hover and focus. |
| showValue | "hover" | "always" | "never" | hover | Controls when the value label is visible. |
| formatValue | ((value: number) => ReactNode) | (current) => current | Formats the value displayed above each thumb. |
| disabled | boolean | — | |
| asChild | boolean | — |
事件
onValueChange(values) 在拖动或键盘调整期间连续触发;需要只在操作完成后提交时使用 onValueCommit(values)。组件支持受控与非受控用法。
拓展使用
使用 showValue="hover | always | never" 控制浮动标签,通过 formatValue 添加百分号、单位或自定义内容。传入两个值即可得到范围选择器,方向键会按照 step 调整数值。
满尺寸展开
Loading…
variant="expand" 会在鼠标浮入或键盘聚焦时将轨道扩展到 20px,与滑块圆点保持相同高度;离开交互区域后平滑收回细轨道。