wui
图表

图表范围选择 Chart Brush

复用 Slider 为趋势图提供受控范围选择和缩放窗口。

基础用法

Loading…
pnpm dlx @wui-design/cli@latest add @wui/chart-brush
components/charts/chart-brush.tsx
"use client"

import * as React from "react"

import { Slider } from "@/components/ui/slider"

import { cn } from "@/lib/utils"

import type { ChartDatum } from "./chart-core"

export interface ChartBrushProps extends Omit<
  React.HTMLAttributes<HTMLDivElement>,
  "defaultValue"
> {
  /** 与主图横轴顺序一致的数据。 */
  data: ChartDatum[]
  /** 用于显示选区边界的标签字段。 */
  labelKey: string
  /** 受控的起止索引。 */
  value?: [number, number]
  /** 非受控的初始起止索引。 */
  defaultValue?: [number, number]
  /** 选区索引变化时触发。 */
  onValueChange?: (value: [number, number]) => void
  /** 两个边界之间至少保留的数据点数量。 @default 2 */
  minPoints?: number
  /** 自定义边界标签。 */
  labelFormatter?: (
    value: string | number,
    datum: ChartDatum
  ) => React.ReactNode
}

/** 复用 Slider 提供图表范围选择,可用于缩放或时间窗口过滤。 */
function ChartBrush({
  data,
  labelKey,
  value,
  defaultValue = [0, data.length - 1],
  onValueChange,
  minPoints = 2,
  labelFormatter = (next) => String(next),
  className,
  ...props
}: ChartBrushProps) {
  const [internalValue, setInternalValue] =
    React.useState<[number, number]>(defaultValue)
  const current = value ?? internalValue

  function handleValueChange(next: number[]) {
    const range: [number, number] = [next[0], next[1]]
    if (!value) setInternalValue(range)
    onValueChange?.(range)
  }

  return (
    <div
      data-slot="chart-brush"
      className={cn(
        "mt-4 grid grid-cols-[auto_1fr_auto] items-center gap-3",
        className
      )}
      {...props}
    >
      <span className="text-muted-foreground min-w-10 text-xs">
        {labelFormatter(
          data[current[0]][labelKey] as string | number,
          data[current[0]]
        )}
      </span>
      <Slider
        value={current}
        min={0}
        max={data.length - 1}
        step={1}
        minStepsBetweenThumbs={minPoints - 1}
        showValue="never"
        variant="expand"
        aria-label="图表显示范围"
        onValueChange={handleValueChange}
      />
      <span className="text-muted-foreground min-w-10 text-right text-xs">
        {labelFormatter(
          data[current[1]][labelKey] as string | number,
          data[current[1]]
        )}
      </span>
    </div>
  )
}

export { ChartBrush }
export type { ChartDatum }

使用场景

ChartBrush 直接复用现有 Slider 的双 Thumb、键盘和触摸交互,只负责返回起止索引。主图的数据切片和请求范围由业务状态控制, 因此它可以与折线、面积、时间序列或其他有序图表组合。

属性

属性类型默认值说明
data *ChartDatum[]—与主图横轴顺序一致的数据。
labelKey *string—用于显示选区边界的标签字段。
value[number, number]—受控的起止索引。
defaultValue[number, number][0, data.length - 1]非受控的初始起止索引。
onValueChange((value: [number, number]) => void)—选区索引变化时触发。
minPointsnumber2两个边界之间至少保留的数据点数量。
labelFormatter((value: string | number, datum: ChartDatum) => ReactNode)(next) => String(next)自定义边界标签。

属性名后的 * 表示必填。