Sticky Metric

Pinned metric pill that sticks to a canvas object's corner.

Report a bug

Preview

Switch between light and dark to inspect the embedded Storybook preview.

Installation

pnpm dlx shadcn@latest add https://ui.vllnt.ai/r/sticky-metric.json
bash

Storybook

Explore all variants, controls, and accessibility checks in the interactive Storybook playground.

View in Storybook

4 stories available:

Code

"use client";

import {
  type ComponentPropsWithoutRef,
  forwardRef,
  type ReactNode,
} from "react";

import { cn } from "../../lib/utils";

/**
 * Tone of the metric — drives the dot color.
 *
 * @public
 */
export type StickyMetricTone = "danger" | "neutral" | "success" | "warn";

/**
 * Anchor corner relative to the canvas object the metric sticks to.
 *
 * @public
 */
export type StickyMetricAnchor =
  | "bottom-left"
  | "bottom-right"
  | "top-left"
  | "top-right";

const TONE_DOT: Record<StickyMetricTone, string> = {
  danger: "bg-red-500",
  neutral: "bg-muted-foreground",
  success: "bg-emerald-500",
  warn: "bg-amber-500",
};

const ANCHOR_OFFSET: Record<StickyMetricAnchor, { transform: string }> = {
  "bottom-left": { transform: "translate(-100%, 100%)" },
  "bottom-right": { transform: "translate(0%, 100%)" },
  "top-left": { transform: "translate(-100%, -100%)" },
  "top-right": { transform: "translate(0%, -100%)" },
};

/**
 * Localizable strings.
 *
 * @public
 */
export type StickyMetricLabels = {
  /** Aria-label override. Defaults to `"Sticky metric"`. */
  region?: string;
};

const DEFAULT_LABELS = {
  region: "Sticky metric",
} as const satisfies Required<StickyMetricLabels>;

/**
 * Props for {@link StickyMetric}.
 *
 * @public
 */
export type StickyMetricProps = {
  /** Anchor corner. Defaults to `"top-right"`. */
  anchor?: StickyMetricAnchor;
  /** Optional secondary slot rendered after the value (delta, unit). */
  detail?: ReactNode;
  /** Short metric label (e.g. `"errs/min"`). */
  label: ReactNode;
  /** Localizable strings. */
  labels?: StickyMetricLabels;
  /** Tone for the leading dot. Defaults to `"neutral"`. */
  tone?: StickyMetricTone;
  /** Display value (already formatted by host). */
  value: ReactNode;
  /** Anchor X in canvas pixels. */
  x: number;
  /** Anchor Y in canvas pixels. */
  y: number;
} & ComponentPropsWithoutRef<"div">;

/**
 * Pinned metric pill that sticks to a canvas object's corner. Use to
 * overlay a single live value (errors / min, p95 latency, queue depth)
 * on a runtime object without consuming card real-estate. Pure
 * presentation; the host computes the anchor coords from the object's
 * bounding box and the value from the runtime stream.
 *
 * The wrapper is `pointer-events: none` — host gestures pass through.
 *
 * @example
 * ```tsx
 * <div className="relative h-screen w-screen">
 *   <Canvas />
 *   <StickyMetric
 *     x={420} y={180}
 *     anchor="top-right"
 *     label="errs / min"
 *     value="14"
 *     tone="danger"
 *   />
 * </div>
 * ```
 *
 * @public
 */
export const StickyMetric = forwardRef<HTMLDivElement, StickyMetricProps>(
  (props, ref) => {
    const {
      anchor = "top-right",
      className,
      detail,
      label,
      labels,
      tone = "neutral",
      value,
      x,
      y,
      ...rest
    } = props;
    const resolvedLabels = { ...DEFAULT_LABELS, ...labels };
    return (
      <div
        aria-label={resolvedLabels.region}
        className={cn(
          "pointer-events-none absolute z-20 inline-flex items-center gap-1.5 rounded-full border border-border bg-background/90 px-2 py-1 text-[11px] shadow-sm backdrop-blur",
          className,
        )}
        data-sticky-anchor={anchor}
        data-sticky-metric
        data-sticky-tone={tone}
        ref={ref}
        role="status"
        style={{
          left: x,
          top: y,
          transform: ANCHOR_OFFSET[anchor].transform,
        }}
        {...rest}
      >
        <span
          aria-hidden="true"
          className={cn("size-1.5 rounded-full", TONE_DOT[tone])}
          data-sticky-metric-dot
        />
        <span className="font-medium text-muted-foreground">{label}</span>
        <span className="font-semibold text-foreground">{value}</span>
        {detail ? (
          <span
            className="text-[10px] text-muted-foreground"
            data-sticky-metric-detail
          >
            {detail}
          </span>
        ) : null}
      </div>
    );
  },
);
StickyMetric.displayName = "StickyMetric";
typescript

Dependencies

  • @vllnt/ui@^0.2.1