arikchakma

Unit Field

Component 1

About

Headless, composable numeric input field with drag-to-scrub, keyboard stepping, format/parse controls, and committed-value events for building any unit field.

Built with React and Tailwind. Copy the prompt and paste it into Claude Code, Cursor or Lovable: your agent adds Unit Field to your project and fits it to your theme.

Code

How Unit Field is used: the demo's code. The component's own source comes with a free account.

Usage.tsx

"use client";

import * as React from "react";
import { UnitField } from "@/components/ui/unit-field";

const INPUT =
  "w-full bg-transparent pr-2.5 text-right font-mono text-sm text-gray-700 tabular-nums outline-none placeholder:text-gray-300";

const DRAG_AREA =
  "flex aspect-square w-9 shrink-0 cursor-ew-resize items-center justify-center text-[12px] font-medium text-gray-400 select-none";

const ROOT =
  "relative flex h-9 w-full items-stretch border border-gray-200 transition-colors data-[focused=true]:border-gray-400";

function pctFormat(v: number) {
  return `${v}%`;
}

function pctParse(v: string) {
  const n = parseInt(v.replace("%", ""), 10);
  return isNaN(n) ? null : n;
}

export default function UnitFieldCommittedDemo() {
  const [value, setValue] = React.useState(50);
  const [committed, setCommitted] = React.useState(50);

  const handleCommitted = React.useCallback((v: number) => {
    setCommitted(v);
  }, []);

  return (
    <div className="mx-auto w-full max-w-sm bg-white p-6 text-gray-700">
      <header className="mb-3">
        <div className="flex items-center gap-2">
          <span className="font-mono text-[11px] text-gray-400">03</span>
          <h3 className="text-[14px] font-medium text-black">
            Committed value
          </h3>
        </div>
        <p className="mt-1 text-[13px] text-gray-500">
          Live value updates on drag, committed value fires on release.
        </p>
      </header>

      <UnitField.Root
        className={ROOT}
        format={pctFormat}
        parse={pctParse}
        value={value}
        onValueChange={setValue}
        onValueCommitted={handleCommitted}
        min={0}
        max={100}
      >
        <UnitField.DragArea className={DRAG_AREA}>%</UnitField.DragArea>
        <UnitField.Input className={INPUT} />
      </UnitField.Root>

      <div className="mt-3 space-y-1.5">
        <div className="flex items-center justify-between border border-gray-200 px-3 py-2">
          <span className="text-[12px] text-gray-400">Live</span>
          <span className="font-mono text-[13px] text-gray-600 tabular-nums">
            {value}%
          </span>
        </div>
        <div className="flex items-center justify-between border border-gray-200 px-3 py-2">
          <span className="text-[12px] text-gray-400">Committed</span>
          <span className="font-mono text-[13px] text-gray-600 tabular-nums">
            {committed}%
          </span>
        </div>
      </div>

      <div className="mt-3 border border-gray-200">
        <div
          className="h-1.5 bg-gray-800 transition-all duration-75"
          style={{ width: `${value}%` }}
        />
      </div>
    </div>
  );
}

More by arikchakma

View all
  • Cromia Color Picker

    Cromia Color Picker

    42
  • Cromia Compact

    Cromia Compact

    18
  • Cromia Channels

    Cromia Channels

    14
  • Cromia Swatches

    Cromia Swatches

    10