import { Button, Gauge } from "@ai-ui/ui"; import type { Meta, StoryObj } from "@storybook/react"; import { useState } from "react"; function GaugePanel({ description, label, shape = "dial", tone = "default", value, variant = "default" }: { description: string; label: string; shape?: "dial" | "semi"; tone?: "default" | "subtle" | "accent"; value: number | null; variant?: "default" | "success" | "warning" | "destructive"; }) { return (
); } const meta = { title: "Components/Gauge", component: Gauge, args: { description: "Lead routing stays stable enough for this week's forecast handoff.", label: "Forecast confidence", shape: "dial", size: "md", tickCount: 0, tone: "default", value: 72, variant: "default" }, argTypes: { className: { control: false }, description: { control: "text" }, label: { control: "text" }, shape: { control: "radio", options: ["dial", "semi"] }, size: { control: "radio", options: ["sm", "md", "lg"] }, tickCount: { control: { type: "range", min: 0, max: 24, step: 1 } }, tone: { control: "radio", options: ["default", "subtle", "accent"] }, value: { control: { type: "range", min: 0, max: 100, step: 1 } }, valueFormatter: { control: false }, variant: { control: "radio", options: ["default", "success", "warning", "destructive"] } }, parameters: { docs: { description: { component: "Gauge is the system's radial meter for current measurements inside a known range. Use it for capacity, forecast confidence, health scores, and KPI thresholds where the UI should communicate the current level rather than the progress of an ongoing task. The component now stages its readout with a restrained sweep and count-up on entry, while reduced or static motion snaps directly to the final state." } }, layout: "centered" }, tags: ["autodocs"] } satisfies Meta; export default meta; type Story = StoryObj; export const Playground: Story = { render: (args) => (

Capacity Watch

Use Gauge for the current level of a metric, not for task completion.

This component is a meter. It reads like a dashboard instrument and belongs beside KPI cards, score panels, and health indicators rather than uploads or async job states.

) }; export const Shapes: Story = { render: () => (
) }; export const Anatomy: Story = { render: () => (

Gauge anatomy

Gauge keeps the public structure small: one canvas, one SVG ring system, one value plate, and optional framing copy below the visual.

data-slot="canvas" owns the responsive gauge frame, while data-slot="svg"{" "} holds the radial drawing primitives.

data-slot="track" is the full meter range, and data-slot="indicator"{" "} is the active measured arc. If a denser dashboard wants calibration marks, opt into them with tickCount, which exposes data-slot="tick".

data-slot="value", data-slot="label", and data-slot="description" keep center readout and supporting copy stable for theming, docs, and tests.

) }; function GaugeMotionShowcase() { const [replayKey, setReplayKey] = useState(0); return (

Motion review

Gauge should arrive like an instrument waking up, not a number teleporting in.

The indicator sweeps in once, the center value counts up with it, and reduced/static motion still resolves instantly.

); } export const Motion: Story = { parameters: { docs: { description: { story: "Gauge uses a one-time staged startup: the ring sweeps to the target, ticks wake up with the same progress, and the center value counts into place. The effect is intentionally calm and should still feel correct when motion is reduced." } } }, render: () => }; export const Accessibility: Story = { parameters: { docs: { description: { story: "Gauge uses the ARIA meter pattern, not progressbar. Use it for a current reading inside a bounded range, provide a concrete label, and keep the center number or supporting copy readable enough that color is never the only signal." } } }, render: () => (

Review guidance

Choose Gauge for current measurement. Choose Progress for ongoing completion.

Always give the meter a concrete label such as Forecast confidence.

Keep the numeric readout or description meaningful enough that color is supplemental.

) };