{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "bento",
  "type": "registry:ui",
  "files": [
    {
      "path": "registry/designali/ui/bento.tsx",
      "content": "\"use client\"\n\nimport { useEffect, useState, type CSSProperties, type ReactNode } from \"react\"\n\nimport { cn } from \"@workspace/ui/lib/utils\"\n\n/* -------------------------------------------------------------------------- */\n/*                                   Types                                    */\n/* -------------------------------------------------------------------------- */\n\ntype Breakpoint = \"base\" | \"sm\" | \"md\" | \"lg\" | \"xl\" | \"2xl\"\ntype ResponsiveValue<T> = T | Partial<Record<Breakpoint, T>>\nconst BREAKPOINTS: Breakpoint[] = [\"base\", \"sm\", \"md\", \"lg\", \"xl\", \"2xl\"]\n\n/* -------------------------------------------------------------------------- */\n/*             Fully Static Responsive Maps (Tailwind JIT safe)               */\n/*                                                                            */\n/*  Tailwind's scanner reads source TEXT — it does NOT execute code.          */\n/*  Every class that might be emitted MUST appear as a complete literal       */\n/*  string somewhere in the scanned files.                                    */\n/*                                                                            */\n/*  Dynamic construction like `${bp}:col-span-${n}` or even               */\n/*  `${bp}:${map[value]}` is invisible to the scanner — Tailwind only       */\n/*  sees the template literal syntax, not what it evaluates to.             */\n/*                                                                            */\n/*  These nested objects are the single source of truth. Every possible       */\n/*  responsive class is written out verbatim so JIT includes them all.        */\n/* -------------------------------------------------------------------------- */\n\nconst RESPONSIVE_GRID_COLS = {\n  base: {\n    1: \"grid-cols-1\",\n    2: \"grid-cols-2\",\n    3: \"grid-cols-3\",\n    4: \"grid-cols-4\",\n    5: \"grid-cols-5\",\n    6: \"grid-cols-6\",\n    7: \"grid-cols-7\",\n    8: \"grid-cols-8\",\n  },\n  sm: {\n    1: \"sm:grid-cols-1\",\n    2: \"sm:grid-cols-2\",\n    3: \"sm:grid-cols-3\",\n    4: \"sm:grid-cols-4\",\n    5: \"sm:grid-cols-5\",\n    6: \"sm:grid-cols-6\",\n    7: \"sm:grid-cols-7\",\n    8: \"sm:grid-cols-8\",\n  },\n  md: {\n    1: \"md:grid-cols-1\",\n    2: \"md:grid-cols-2\",\n    3: \"md:grid-cols-3\",\n    4: \"md:grid-cols-4\",\n    5: \"md:grid-cols-5\",\n    6: \"md:grid-cols-6\",\n    7: \"md:grid-cols-7\",\n    8: \"md:grid-cols-8\",\n  },\n  lg: {\n    1: \"lg:grid-cols-1\",\n    2: \"lg:grid-cols-2\",\n    3: \"lg:grid-cols-3\",\n    4: \"lg:grid-cols-4\",\n    5: \"lg:grid-cols-5\",\n    6: \"lg:grid-cols-6\",\n    7: \"lg:grid-cols-7\",\n    8: \"lg:grid-cols-8\",\n  },\n  xl: {\n    1: \"xl:grid-cols-1\",\n    2: \"xl:grid-cols-2\",\n    3: \"xl:grid-cols-3\",\n    4: \"xl:grid-cols-4\",\n    5: \"xl:grid-cols-5\",\n    6: \"xl:grid-cols-6\",\n    7: \"xl:grid-cols-7\",\n    8: \"xl:grid-cols-8\",\n  },\n  \"2xl\": {\n    1: \"2xl:grid-cols-1\",\n    2: \"2xl:grid-cols-2\",\n    3: \"2xl:grid-cols-3\",\n    4: \"2xl:grid-cols-4\",\n    5: \"2xl:grid-cols-5\",\n    6: \"2xl:grid-cols-6\",\n    7: \"2xl:grid-cols-7\",\n    8: \"2xl:grid-cols-8\",\n  },\n} as const\n\nconst RESPONSIVE_COL_SPAN = {\n  base: {\n    1: \"col-span-1\",\n    2: \"col-span-2\",\n    3: \"col-span-3\",\n    4: \"col-span-4\",\n    5: \"col-span-5\",\n  },\n  sm: {\n    1: \"sm:col-span-1\",\n    2: \"sm:col-span-2\",\n    3: \"sm:col-span-3\",\n    4: \"sm:col-span-4\",\n    5: \"sm:col-span-5\",\n  },\n  md: {\n    1: \"md:col-span-1\",\n    2: \"md:col-span-2\",\n    3: \"md:col-span-3\",\n    4: \"md:col-span-4\",\n    5: \"md:col-span-5\",\n  },\n  lg: {\n    1: \"lg:col-span-1\",\n    2: \"lg:col-span-2\",\n    3: \"lg:col-span-3\",\n    4: \"lg:col-span-4\",\n    5: \"lg:col-span-5\",\n  },\n  xl: {\n    1: \"xl:col-span-1\",\n    2: \"xl:col-span-2\",\n    3: \"xl:col-span-3\",\n    4: \"xl:col-span-4\",\n    5: \"xl:col-span-5\",\n  },\n  \"2xl\": {\n    1: \"2xl:col-span-1\",\n    2: \"2xl:col-span-2\",\n    3: \"2xl:col-span-3\",\n    4: \"2xl:col-span-4\",\n    5: \"2xl:col-span-5\",\n  },\n} as const\n\nconst RESPONSIVE_ROW_SPAN = {\n  base: {\n    1: \"row-span-1\",\n    2: \"row-span-2\",\n    3: \"row-span-3\",\n    4: \"row-span-4\",\n    5: \"row-span-5\",\n  },\n  sm: {\n    1: \"sm:row-span-1\",\n    2: \"sm:row-span-2\",\n    3: \"sm:row-span-3\",\n    4: \"sm:row-span-4\",\n    5: \"sm:row-span-5\",\n  },\n  md: {\n    1: \"md:row-span-1\",\n    2: \"md:row-span-2\",\n    3: \"md:row-span-3\",\n    4: \"md:row-span-4\",\n    5: \"md:row-span-5\",\n  },\n  lg: {\n    1: \"lg:row-span-1\",\n    2: \"lg:row-span-2\",\n    3: \"lg:row-span-3\",\n    4: \"lg:row-span-4\",\n    5: \"lg:row-span-5\",\n  },\n  xl: {\n    1: \"xl:row-span-1\",\n    2: \"xl:row-span-2\",\n    3: \"xl:row-span-3\",\n    4: \"xl:row-span-4\",\n    5: \"xl:row-span-5\",\n  },\n  \"2xl\": {\n    1: \"2xl:row-span-1\",\n    2: \"2xl:row-span-2\",\n    3: \"2xl:row-span-3\",\n    4: \"2xl:row-span-4\",\n    5: \"2xl:row-span-5\",\n  },\n} as const\n\nconst RESPONSIVE_GAP = {\n  base: {\n    0: \"gap-0\",\n    1: \"gap-1\",\n    2: \"gap-2\",\n    3: \"gap-3\",\n    4: \"gap-4\",\n    5: \"gap-5\",\n    6: \"gap-6\",\n    8: \"gap-8\",\n    10: \"gap-10\",\n  },\n  sm: {\n    0: \"sm:gap-0\",\n    1: \"sm:gap-1\",\n    2: \"sm:gap-2\",\n    3: \"sm:gap-3\",\n    4: \"sm:gap-4\",\n    5: \"sm:gap-5\",\n    6: \"sm:gap-6\",\n    8: \"sm:gap-8\",\n    10: \"sm:gap-10\",\n  },\n  md: {\n    0: \"md:gap-0\",\n    1: \"md:gap-1\",\n    2: \"md:gap-2\",\n    3: \"md:gap-3\",\n    4: \"md:gap-4\",\n    5: \"md:gap-5\",\n    6: \"md:gap-6\",\n    8: \"md:gap-8\",\n    10: \"md:gap-10\",\n  },\n  lg: {\n    0: \"lg:gap-0\",\n    1: \"lg:gap-1\",\n    2: \"lg:gap-2\",\n    3: \"lg:gap-3\",\n    4: \"lg:gap-4\",\n    5: \"lg:gap-5\",\n    6: \"lg:gap-6\",\n    8: \"lg:gap-8\",\n    10: \"lg:gap-10\",\n  },\n  xl: {\n    0: \"xl:gap-0\",\n    1: \"xl:gap-1\",\n    2: \"xl:gap-2\",\n    3: \"xl:gap-3\",\n    4: \"xl:gap-4\",\n    5: \"xl:gap-5\",\n    6: \"xl:gap-6\",\n    8: \"xl:gap-8\",\n    10: \"xl:gap-10\",\n  },\n  \"2xl\": {\n    0: \"2xl:gap-0\",\n    1: \"2xl:gap-1\",\n    2: \"2xl:gap-2\",\n    3: \"2xl:gap-3\",\n    4: \"2xl:gap-4\",\n    5: \"2xl:gap-5\",\n    6: \"2xl:gap-6\",\n    8: \"2xl:gap-8\",\n    10: \"2xl:gap-10\",\n  },\n} as const\n\n/* -------------------------------------------------------------------------- */\n/*                      Responsive Class Generator                            */\n/* -------------------------------------------------------------------------- */\n\n// eslint-disable-next-line @typescript-eslint/no-explicit-any\ntype ResponsiveMap<T extends keyof any> = Record<Breakpoint, Record<T, string>>\n\nfunction getResponsiveClasses<T extends number>(\n  value: ResponsiveValue<T> | undefined,\n  map: ResponsiveMap<T>\n): string {\n  if (value === undefined) return \"\"\n\n  if (typeof value !== \"object\") {\n    return map.base[value] ?? \"\"\n  }\n\n  return BREAKPOINTS.filter(\n    (bp) => (value as Partial<Record<Breakpoint, T>>)[bp] !== undefined\n  )\n    .map((bp) => {\n      const key = (value as Partial<Record<Breakpoint, T>>)[bp] as T\n      return map[bp]?.[key] ?? \"\"\n    })\n    .filter(Boolean)\n    .join(\" \")\n}\n\n/* -------------------------------------------------------------------------- */\n/*                     Responsive Row Height Hook                             */\n/* -------------------------------------------------------------------------- */\n\nfunction useResponsiveRowHeight(\n  value: ResponsiveValue<string> | undefined\n): CSSProperties | undefined {\n  const [rowHeight, setRowHeight] = useState<string | undefined>(\n    typeof value === \"string\" ? value : value?.base\n  )\n\n  useEffect(() => {\n    if (!value || typeof value === \"string\") return\n\n    const queries: Record<Exclude<Breakpoint, \"base\">, MediaQueryList> = {\n      sm: window.matchMedia(\"(min-width: 640px)\"),\n      md: window.matchMedia(\"(min-width: 768px)\"),\n      lg: window.matchMedia(\"(min-width: 1024px)\"),\n      xl: window.matchMedia(\"(min-width: 1280px)\"),\n      \"2xl\": window.matchMedia(\"(min-width: 1536px)\"),\n    }\n\n    const update = () => {\n      const ordered: Breakpoint[] = [\"2xl\", \"xl\", \"lg\", \"md\", \"sm\"]\n      for (const bp of ordered) {\n        if (queries[bp as keyof typeof queries].matches && value[bp]) {\n          setRowHeight(value[bp])\n          return\n        }\n      }\n      setRowHeight(value.base)\n    }\n\n    update()\n    Object.values(queries).forEach((q) => q.addEventListener(\"change\", update))\n    return () =>\n      Object.values(queries).forEach((q) =>\n        q.removeEventListener(\"change\", update)\n      )\n  }, [value])\n\n  return rowHeight ? { gridAutoRows: rowHeight } : undefined\n}\n\n/* -------------------------------------------------------------------------- */\n/*                               BentoGrid                                    */\n/* -------------------------------------------------------------------------- */\n\ntype GridColsValue = keyof typeof RESPONSIVE_GRID_COLS.base\ntype GapValue = keyof typeof RESPONSIVE_GAP.base\n\ninterface BentoGridProps {\n  children: ReactNode\n  className?: string\n  cols?: ResponsiveValue<GridColsValue>\n  gap?: ResponsiveValue<GapValue>\n  rowHeight?: ResponsiveValue<string>\n}\n\nexport function BentoGrid({\n  children,\n  className,\n  cols = { base: 1, md: 3, lg: 5 },\n  gap = { base: 1, md: 2, lg: 4 },\n  rowHeight = { base: \"60px\", md: \"80px\", lg: \"100px\" },\n}: BentoGridProps) {\n  const style = useResponsiveRowHeight(rowHeight)\n  return (\n    <div\n      style={style}\n      className={cn(\n        \"mx-auto grid h-full w-full\",\n        getResponsiveClasses(\n          cols,\n          RESPONSIVE_GRID_COLS as ResponsiveMap<GridColsValue>\n        ),\n        getResponsiveClasses(gap, RESPONSIVE_GAP as ResponsiveMap<GapValue>),\n        className\n      )}\n    >\n      {children}\n    </div>\n  )\n}\n\n/* -------------------------------------------------------------------------- */\n/*                             BentoGridItem                                  */\n/* -------------------------------------------------------------------------- */\n\ntype ColSpanValue = keyof typeof RESPONSIVE_COL_SPAN.base\ntype RowSpanValue = keyof typeof RESPONSIVE_ROW_SPAN.base\n\ninterface BentoGridItemProps {\n  children: ReactNode\n  className?: string\n  colSpan?: ResponsiveValue<ColSpanValue>\n  rowSpan?: ResponsiveValue<RowSpanValue>\n}\n\nexport function BentoGridItem({\n  children,\n  className,\n  colSpan = 1,\n  rowSpan = 1,\n}: BentoGridItemProps) {\n  return (\n    <div\n      className={cn(\n        \"border bg-background p-4 transition-transform\",\n        getResponsiveClasses(\n          colSpan,\n          RESPONSIVE_COL_SPAN as ResponsiveMap<ColSpanValue>\n        ),\n        getResponsiveClasses(\n          rowSpan,\n          RESPONSIVE_ROW_SPAN as ResponsiveMap<RowSpanValue>\n        ),\n        className\n      )}\n    >\n      {children}\n    </div>\n  )\n}\n",
      "type": "registry:ui"
    }
  ]
}