{
  "name": "product-grid",
  "title": "ProductGrid",
  "description": "Responsive CSS grid that renders ProductCards.",
  "type": "component",
  "registryDependencies": [
    "product-card",
    "cn"
  ],
  "files": [
    {
      "path": "product-grid.tsx",
      "content": "\"use client\";\n\nimport React from \"react\";\nimport type { Product } from \"@cimplify/sdk\";\nimport { ProductCard } from \"@cimplify/sdk/react\";\nimport { ImagePriorityContext } from \"@cimplify/sdk/react\";\nimport { cn } from \"@cimplify/sdk/react\";\n\nexport interface ProductGridClassNames {\n  root?: string;\n  item?: string;\n  empty?: string;\n}\n\nexport interface ProductGridProps {\n  /** Products to display in the grid. */\n  products: Product[];\n  /** Responsive column counts at each breakpoint. */\n  columns?: { sm?: number; md?: number; lg?: number; xl?: number };\n  /** Custom card renderer per product. */\n  renderCard?: (product: Product) => React.ReactNode;\n  /** Custom image renderer passed to default ProductCards. */\n  renderImage?: (props: {\n    src: string;\n    alt: string;\n    className?: string;\n  }) => React.ReactNode;\n  /** Text shown when `products` is empty. */\n  emptyMessage?: string;\n  className?: string;\n  classNames?: ProductGridClassNames;\n}\n\n/**\n * ProductGrid — responsive CSS grid that renders ProductCards.\n *\n * Injects an inline `<style>` tag with media queries for responsive columns.\n * Uses `React.useId()` for a hydration-safe, collision-free CSS selector.\n */\nexport function ProductGrid({\n  products,\n  columns,\n  renderCard,\n  renderImage,\n  emptyMessage,\n  className,\n  classNames,\n}: ProductGridProps): React.ReactElement {\n  const rawId = React.useId();\n  // CSS selectors can't contain colons, so strip them from the React-generated ID\n  const gridId = `cimplify-grid-${rawId.replace(/:/g, \"\")}`;\n\n  const sm = columns?.sm ?? 1;\n  const md = columns?.md ?? 2;\n  const lg = columns?.lg ?? 3;\n  const xl = columns?.xl ?? 4;\n  const css = React.useMemo(\n    () =>\n      [\n        `#${gridId}{display:grid;grid-template-columns:repeat(${sm},1fr);gap:1rem}`,\n        `@media(min-width:768px){#${gridId}{grid-template-columns:repeat(${md},1fr)}}`,\n        `@media(min-width:1024px){#${gridId}{grid-template-columns:repeat(${lg},1fr)}}`,\n        `@media(min-width:1280px){#${gridId}{grid-template-columns:repeat(${xl},1fr)}}`,\n      ].join(\"\"),\n    [gridId, sm, md, lg, xl],\n  );\n\n  if (products.length === 0) {\n    return (\n      <div\n        data-cimplify-product-grid\n        data-empty\n        className={cn(className, classNames?.root, classNames?.empty)}\n      >\n        <p>{emptyMessage ?? \"No products found\"}</p>\n      </div>\n    );\n  }\n\n  return (\n    <>\n      <style dangerouslySetInnerHTML={{ __html: css }} />\n      <div\n        id={gridId}\n        data-cimplify-product-grid\n        className={cn(className, classNames?.root)}\n      >\n        {products.map((product, index) => {\n          const card = renderCard ? (\n            renderCard(product)\n          ) : (\n            <ProductCard product={product} renderImage={renderImage} />\n          );\n          return (\n            <div\n              key={product.id}\n              data-cimplify-product-grid-item\n              className={classNames?.item}\n            >\n              {index < xl ? (\n                <ImagePriorityContext.Provider value={true}>{card}</ImagePriorityContext.Provider>\n              ) : (\n                card\n              )}\n            </div>\n          );\n        })}\n      </div>\n    </>\n  );\n}\n"
    }
  ]
}
