{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "fluid-expanding-grid",
  "title": "Fluid Expanding Grid",
  "description": "A responsive gallery grid that fluidly shifts and expands items using motion layout.",
  "dependencies": [
    "motion",
    "clsx",
    "tailwind-merge"
  ],
  "files": [
    {
      "path": "registry/default/example/fluid-expanding-grid.tsx",
      "content": "\"use client\";\n\nimport React, { useState } from \"react\";\nimport { motion, LayoutGroup } from \"framer-motion\";\nimport { cn } from \"@/lib/utils\";\n\ninterface GalleryItem {\n  id: string;\n  title: string;\n  subtitle: string;\n  image: string;\n  color: string;\n}\n\nconst ITEMS: GalleryItem[] = [\n  {\n    id: \"grassy\",\n    title: \"Highlands\",\n    subtitle: \"Golden fields under the giant\",\n    image:\n      \"https://images.unsplash.com/photo-1755441172753-ac9b90dcd930?w=600&auto=format&fit=crop&q=60&ixlib=rb-4.1.0&ixid=M3wxMjA3fDB8MHxleHBsb3JlLWZlZWR8OHx8fGVufDB8fHx8fA%3D%3D\",\n    color: \"#84cc16\",\n  },\n  {\n    id: \"misty\",\n    title: \"Crimson\",\n    subtitle: \"A scarlet flame in the mountains\",\n    image:\n      \"https://plus.unsplash.com/premium_photo-1667423711653-1ffb899172bc?w=600&auto=format&fit=crop&q=60&ixlib=rb-4.1.0&ixid=M3wxMjA3fDB8MHxleHBsb3JlLWZlZWR8MjZ8fHxlbnwwfHx8fHw%3D\",\n    color: \"#10b981\",\n  },\n  {\n    id: \"desert\",\n    title: \"Deep Sea\",\n    subtitle: \"Floating gracefully in the abyss\",\n    image:\n      \"https://images.unsplash.com/photo-1757263005786-43d955f07fb1?w=600&auto=format&fit=crop&q=60&ixlib=rb-4.1.0&ixid=M3wxMjA3fDB8MHxleHBsb3JlLWZlZWR8Mjd8fHxlbnwwfHx8fHw%3D\",\n    color: \"#0369a1\",\n  },\n];\n\ninterface FluidExpandingGridProps {\n  items?: GalleryItem[];\n  className?: string;\n  id?: string;\n}\n\nexport default function FluidExpandingGrid({\n  items = ITEMS,\n  className,\n  id = \"fluid-gallery\",\n}: FluidExpandingGridProps) {\n  const [layout, setLayout] = useState(() => {\n    const ids = items.map((item) => item.id);\n    return {\n      row1: ids.slice(0, 2),\n      row2: ids.slice(2, Math.min(items.length, 4)),\n    };\n  });\n\n  const handleExpand = (id: string) => {\n    const inRow1 = layout.row1.includes(id);\n    const inRow2 = layout.row2.includes(id);\n\n    if (\n      (inRow1 && layout.row1.length === 1) ||\n      (inRow2 && layout.row2.length === 1)\n    )\n      return;\n\n    if (inRow1) {\n      const neighbor = layout.row1.find((i) => i !== id)!;\n      setLayout({\n        row1: [id],\n        row2: [neighbor, ...layout.row2.filter((i) => i !== neighbor)].slice(\n          0,\n          2\n        ),\n      });\n    } else {\n      const neighbor = layout.row2.find((i) => i !== id)!;\n      setLayout({\n        row1: [neighbor, ...layout.row1.filter((i) => i !== neighbor)].slice(\n          0,\n          2\n        ),\n        row2: [id],\n      });\n    }\n  };\n\n  return (\n    <div\n      className={cn(\n        \"w-full h-full flex items-center justify-center overflow-hidden py-12 not-prose\",\n        className\n      )}\n    >\n      <div className=\"w-full max-w-2xl px-6\">\n        <LayoutGroup id={id}>\n          <motion.div\n            layout\n            className=\"grid grid-cols-2 grid-rows-2 gap-6 w-full h-[340px] sm:h-[540px]\"\n          >\n            {items.map((item) => {\n              const isRow1 = layout.row1.includes(item.id);\n              const rowArr = isRow1 ? layout.row1 : layout.row2;\n              const isSelected = rowArr.length === 1 && rowArr[0] === item.id;\n\n              const gridRow = isRow1 ? 1 : 2;\n              let gridColumn = \"\";\n              if (isSelected) {\n                gridColumn = \"1 / span 2\";\n              } else {\n                if (isRow1) {\n                  gridColumn = layout.row1.indexOf(item.id) === 0 ? \"1\" : \"2\";\n                } else {\n                  gridColumn = layout.row2.indexOf(item.id) === 0 ? \"1\" : \"2\";\n                }\n              }\n\n              return (\n                <motion.div\n                  key={item.id}\n                  layoutId={`${id}-${item.id}`}\n                  onClick={() => handleExpand(item.id)}\n                  style={{ gridRow, gridColumn } as any}\n                  className={cn(\n                    \"relative cursor-pointer group w-full h-full\",\n                    isSelected ? \"z-30\" : \"z-10\"\n                  )}\n                  transition={{\n                    layout: {\n                      type: \"spring\",\n                      stiffness: 100,\n                      damping: 25,\n                    },\n                  }}\n                >\n                  <motion.div\n                    layoutId={`${id}-${item.id}-mask-wrapper`}\n                    className=\"absolute inset-0 overflow-hidden bg-zinc-100\"\n                    style={{ borderRadius: 32 }}\n                  >\n                    <img\n                      src={item.image}\n                      alt={item.title}\n                      className={cn(\n                        \"absolute inset-0 w-full h-full object-cover transition-all duration-1000 ease-in-out\",\n                        isSelected\n                          ? \"object-[center_35%]\"\n                          : \"object-[center_50%]\"\n                      )}\n                    />\n                    <motion.div\n                      layoutId={`${id}-${item.id}-mask`}\n                      className={cn(\n                        \"absolute inset-0 transition-colors duration-700\",\n                        isSelected ? \"bg-black/0\" : \"bg-black/20\"\n                      )}\n                    />\n                  </motion.div>\n\n                  <motion.div\n                    layout=\"position\"\n                    className=\"absolute inset-0 p-6 flex flex-col justify-end text-white z-10 select-none\"\n                  >\n                    <motion.div layout=\"position\" className=\"overflow-hidden\">\n                      <motion.h3\n                        layout=\"position\"\n                        className=\"text-2xl sm:text-3xl font-medium mb-1 tracking-tight\"\n                      >\n                        {item.title}\n                      </motion.h3>\n                      <motion.p\n                        layout=\"position\"\n                        className=\"text-xs sm:text-sm text-white/80 font-normal whitespace-nowrap\"\n                      >\n                        {item.subtitle}\n                      </motion.p>\n                    </motion.div>\n                  </motion.div>\n\n                  <motion.div\n                    layoutId={`${id}-${item.id}-overlay`}\n                    className=\"absolute inset-0 pointer-events-none\"\n                    style={{\n                      borderRadius: 32,\n                      background:\n                        \"linear-gradient(to top, rgba(0,0,0,0.5) 0%, transparent 60%)\",\n                    }}\n                  />\n                  <motion.div\n                    layoutId={`${id}-${item.id}-border`}\n                    className=\"absolute inset-0 border border-white/10 group-hover:border-white/20 transition-colors duration-500 pointer-events-none\"\n                    style={{ borderRadius: 32 }}\n                  />\n                </motion.div>\n              );\n            })}\n          </motion.div>\n        </LayoutGroup>\n      </div>\n    </div>\n  );\n}\n",
      "type": "registry:component"
    }
  ],
  "type": "registry:component"
}