{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "order-book",
  "type": "registry:component",
  "title": "Order Book",
  "description": "Level II bid/ask depth ladder with cumulative size bars and spread readout.",
  "dependencies": [
    "@vllnt/ui@^0.2.1"
  ],
  "registryDependencies": [],
  "files": [
    {
      "path": "registry/default/order-book/order-book.tsx",
      "content": "import * as React from \"react\";\n\nimport { cn } from \"@vllnt/ui\";\n\nexport type OrderBookLevel = {\n  price: number;\n  size: number;\n  total?: number;\n};\n\nexport type OrderBookProps = {\n  asks: OrderBookLevel[];\n  bids: OrderBookLevel[];\n  precision?: number;\n} & React.HTMLAttributes<HTMLDivElement>;\n\nfunction withCumulativeTotal(levels: OrderBookLevel[]) {\n  let runningTotal = 0;\n  return levels.map((level) => {\n    runningTotal += level.total ?? level.size;\n    return {\n      ...level,\n      total: level.total ?? runningTotal,\n    };\n  });\n}\n\nfunction formatNumber(value: number, precision = 2) {\n  return value.toLocaleString(undefined, {\n    maximumFractionDigits: precision,\n    minimumFractionDigits: precision,\n  });\n}\n\nfunction BookSide({\n  accent,\n  levels,\n  precision,\n  title,\n}: {\n  accent: \"ask\" | \"bid\";\n  levels: OrderBookLevel[];\n  precision: number;\n  title: string;\n}) {\n  const maxTotal = Math.max(...levels.map((level) => level.total ?? 0), 1);\n  const barClassName =\n    accent === \"ask\"\n      ? \"bg-rose-500/12 border-rose-500/15\"\n      : \"bg-emerald-500/12 border-emerald-500/15\";\n  const priceClassName =\n    accent === \"ask\"\n      ? \"text-rose-600 dark:text-rose-400\"\n      : \"text-emerald-600 dark:text-emerald-400\";\n\n  return (\n    <div className=\"space-y-2\">\n      <div className=\"flex items-center justify-between\">\n        <h3 className=\"text-sm font-semibold uppercase tracking-[0.24em] text-muted-foreground\">\n          {title}\n        </h3>\n        <span className=\"text-xs text-muted-foreground\">\n          Depth by total size\n        </span>\n      </div>\n      <div className=\"rounded-2xl border border-border/70 bg-background/60 p-2\">\n        <div className=\"grid grid-cols-[1.2fr_1fr_1fr] gap-3 px-3 py-2 text-[11px] font-medium uppercase tracking-[0.2em] text-muted-foreground\">\n          <span>Price</span>\n          <span className=\"text-right\">Size</span>\n          <span className=\"text-right\">Total</span>\n        </div>\n        <div className=\"space-y-1\">\n          {levels.map((level) => {\n            const width = `${((level.total ?? 0) / maxTotal) * 100}%`;\n            return (\n              <div\n                className=\"relative overflow-hidden rounded-xl border border-transparent px-3 py-2\"\n                key={`${accent}-${level.price}-${level.size}`}\n              >\n                <div\n                  className={cn(\n                    \"absolute inset-y-0 right-0 rounded-xl border\",\n                    barClassName,\n                  )}\n                  style={{ width }}\n                />\n                <div className=\"relative grid grid-cols-[1.2fr_1fr_1fr] gap-3 text-sm tabular-nums\">\n                  <span className={cn(\"font-medium\", priceClassName)}>\n                    {formatNumber(level.price, precision)}\n                  </span>\n                  <span className=\"text-right text-foreground\">\n                    {formatNumber(level.size, 3)}\n                  </span>\n                  <span className=\"text-right text-muted-foreground\">\n                    {formatNumber(level.total ?? 0, 3)}\n                  </span>\n                </div>\n              </div>\n            );\n          })}\n        </div>\n      </div>\n    </div>\n  );\n}\n\nexport const OrderBook = React.forwardRef<HTMLDivElement, OrderBookProps>(\n  ({ asks, bids, className, precision = 2, ...props }, reference) => {\n    if (asks.length === 0 && bids.length === 0) {\n      return null;\n    }\n\n    const askLevels = withCumulativeTotal(asks);\n    const bidLevels = withCumulativeTotal(bids);\n    const bestAsk = askLevels[0];\n    const bestBid = bidLevels[0];\n    const spread =\n      bestAsk && bestBid ? Math.max(bestAsk.price - bestBid.price, 0) : 0;\n\n    return (\n      <div\n        className={cn(\n          \"rounded-2xl border border-border bg-card/80 p-4 shadow-sm\",\n          className,\n        )}\n        ref={reference}\n        {...props}\n      >\n        <div className=\"mb-4 flex flex-wrap items-start justify-between gap-3\">\n          <div>\n            <p className=\"text-xs font-medium uppercase tracking-[0.28em] text-muted-foreground\">\n              Level II\n            </p>\n            <h2 className=\"text-lg font-semibold text-foreground\">\n              Order book\n            </h2>\n          </div>\n          <div className=\"rounded-full border border-border bg-background/70 px-3 py-1 text-sm text-muted-foreground tabular-nums\">\n            Spread {formatNumber(spread, precision)}\n          </div>\n        </div>\n        <div className=\"grid gap-4 lg:grid-cols-2\">\n          <BookSide\n            accent=\"ask\"\n            levels={askLevels}\n            precision={precision}\n            title=\"Asks\"\n          />\n          <BookSide\n            accent=\"bid\"\n            levels={bidLevels}\n            precision={precision}\n            title=\"Bids\"\n          />\n        </div>\n      </div>\n    );\n  },\n);\n\nOrderBook.displayName = \"OrderBook\";\n",
      "type": "registry:component"
    }
  ],
  "version": "0.2.1",
  "stability": "stable"
}
