Componentmedia0.7 KB gzipZero dependencies

Inventory Grid

Tappable slot matrix with fill states.

Preview

Live Preview

Installation

Tokens guide →

Add to your project via CLI (zero dependencies, code you own):

npx bigbullui add inventory-grid

Or install the full npm package:

npm install bigbullui
import { InventoryGrid } from "@/components/ui/inventory-grid";

<InventoryGrid />

Usage guidance

Use Inventory Grid when a ticket stub surface needs Tappable slot matrix with fill states. Keep props minimal and prefer composition with sibling stubs.

Avoid Inventory Grid for long form flows or dense data grids where a dedicated layout block fits better.

Source code

inventory-grid.tsx

Zero dependencies (only React + utils). Copy and paste directly into your project:

"use client";

import * as React from "react";
import { cn } from "./lib/utils";

export interface InventoryGridProps extends React.HTMLAttributes<HTMLDivElement> {
  slots?: number;
  filled?: number;
  onPick?: (index: number) => void;
}

/** Inventory grid: tappable slot matrix with fill states. */
export function InventoryGrid({ slots = 12, filled = 7, onPick, className, ...props }: InventoryGridProps) {
  return (
    <div role="grid" aria-label={`Inventory ${filled} of ${slots} filled`} className={cn("grid touch-none grid-cols-4 gap-1.5 select-none", className)} style={{ touchAction: "none" }} {...props}>
      {Array.from({ length: slots }).map((_, i) => (
        <button
          key={i}
          type="button"
          role="gridcell"
          aria-label={i < filled ? `Slot ${i + 1} occupied` : `Slot ${i + 1} empty`}
          onClick={() => onPick?.(i)}
          className={cn(
            "flex min-h-11 min-w-11 touch-none items-center justify-center rounded-md border-2 border-dashed font-mono text-[10px] font-bold focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring",
            i < filled ? "border-foreground bg-secondary text-foreground" : "border-border bg-card text-muted-foreground"
          )}
          style={{ touchAction: "none" }}
        >
          {i < filled ? `×${(i % 9) + 1}` : "—"}
        </button>
      ))}
    </div>
  );
}