{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "loaders-morph",
  "title": "Morph loaders",
  "description": "Styled loaders: the Morph set. 5 decorative loaders variations (BlobLoader, FlipLoader, StretchLoader, TriadLoader, SpinSquareLoader) on the ai2 token system, powered by framer-motion, reduced-motion aware and sized sm to xl. Part of the free styled layer.",
  "dependencies": [
    "motion@^12.42.2"
  ],
  "registryDependencies": [
    "@ai2/tokens"
  ],
  "files": [
    {
      "path": "registry/ai2/styled/loaders-morph.tsx",
      "content": "\"use client\"\n\nimport type * as React from \"react\"\nimport { motion, useReducedMotion } from \"motion/react\"\n\nimport { cn } from \"@/lib/utils\"\n\n/* Morph loader family: 5 decorative indicators that change shape and form. Colour comes ONLY from tokens (currentColor / bg-current). Size = the box scale (aligned with the base spinner). DELIBERATE: the loader keeps moving under reduced-motion too (a stopped loader reads as \"hung\"). Every value is fixed and deterministic, nothing random. */\n\nexport type StyledSize = \"sm\" | \"md\" | \"lg\" | \"xl\"\n\nconst box: Record<StyledSize, string> = {\n  sm: \"size-4\",\n  md: \"size-5\",\n  lg: \"size-6\",\n  xl: \"size-8\",\n}\n\ntype Props = React.ComponentProps<\"div\"> & { size?: StyledSize; label?: string }\n\n/* Blob: bir sekil border-radius'unu surekli akiskan degistirir. */\nexport function BlobLoader({ className, size = \"md\", label = \"Loading\", ...props }: Props) {\n  return (\n    <div\n      data-slot=\"styled-loader\"\n      role=\"status\"\n      aria-label={label}\n      className={cn(\"inline-flex items-center justify-center text-primary\", box[size], className)}\n      {...props}\n    >\n      <motion.span\n        className=\"block size-full bg-current\"\n        animate={{\n          borderRadius: [\n            \"42% 58% 63% 37% / 41% 44% 56% 59%\",\n            \"67% 33% 40% 60% / 62% 57% 43% 38%\",\n            \"38% 62% 55% 45% / 55% 38% 62% 45%\",\n            \"42% 58% 63% 37% / 41% 44% 56% 59%\",\n          ],\n          rotate: [0, 120, 240, 360],\n        }}\n        transition={{ duration: 2.6, ease: \"easeInOut\", repeat: Infinity }}\n      />\n    </div>\n  )\n}\n\n/* Flip: bir kare donerek daireye yuvarlaniyor ve geri donuyor. */\nexport function FlipLoader({ className, size = \"md\", label = \"Loading\", ...props }: Props) {\n  return (\n    <div\n      data-slot=\"styled-loader\"\n      role=\"status\"\n      aria-label={label}\n      className={cn(\"inline-flex items-center justify-center text-info\", box[size], className)}\n      {...props}\n    >\n      <motion.span\n        className=\"block size-4/5 bg-current\"\n        animate={{\n          borderRadius: [\"12%\", \"50%\", \"12%\"],\n          rotate: [0, 180, 360],\n        }}\n        transition={{ duration: 1.4, ease: \"easeInOut\", repeat: Infinity }}\n      />\n    </div>\n  )\n}\n\n/* Stretch: a pill stretches and flattens with scaleX/scaleY. */\nexport function StretchLoader({ className, size = \"md\", label = \"Loading\", ...props }: Props) {\n  return (\n    <div\n      data-slot=\"styled-loader\"\n      role=\"status\"\n      aria-label={label}\n      className={cn(\"inline-flex items-center justify-center text-brand\", box[size], className)}\n      {...props}\n    >\n      <motion.span\n        className=\"block size-1/2 rounded-full bg-current\"\n        animate={{ scaleX: [1, 1.9, 0.8, 1], scaleY: [1, 0.6, 1.6, 1] }}\n        transition={{ duration: 1.1, ease: \"easeInOut\", repeat: Infinity }}\n      />\n    </div>\n  )\n}\n\n/* Triad: 3 nokta ucgen dongusunde pozisyon degistirir. */\nexport function TriadLoader({ className, size = \"md\", label = \"Loading\", ...props }: Props) {\n  const corners = [\n    { top: \"0%\", left: \"50%\" },\n    { top: \"80%\", left: \"8%\" },\n    { top: \"80%\", left: \"92%\" },\n  ]\n  return (\n    <div\n      data-slot=\"styled-loader\"\n      role=\"status\"\n      aria-label={label}\n      className={cn(\"relative inline-block text-success\", box[size], className)}\n      {...props}\n    >\n      {corners.map((_, i) => (\n        <motion.span\n          key={i}\n          className=\"absolute size-1/4 -translate-x-1/2 -translate-y-1/2 rounded-full bg-current\"\n          animate={{\n            top: [corners[i].top, corners[(i + 1) % 3].top, corners[(i + 2) % 3].top, corners[i].top],\n            left: [corners[i].left, corners[(i + 1) % 3].left, corners[(i + 2) % 3].left, corners[i].left],\n          }}\n          transition={{ duration: 1.8, ease: \"easeInOut\", repeat: Infinity }}\n        />\n      ))}\n    </div>\n  )\n}\n\n/* SpinSquare: yuvarlatilmis kare easing duraklamalariyla doner. */\nexport function SpinSquareLoader({ className, size = \"md\", label = \"Loading\", ...props }: Props) {\n  return (\n    <div\n      data-slot=\"styled-loader\"\n      role=\"status\"\n      aria-label={label}\n      className={cn(\"inline-flex items-center justify-center text-warning\", box[size], className)}\n      {...props}\n    >\n      <motion.span\n        className=\"block size-4/5 rounded-[30%] border-2 border-current\"\n        animate={{ rotate: [0, 90, 180, 270, 360] }}\n        transition={{ duration: 2, ease: \"easeInOut\", times: [0, 0.25, 0.5, 0.75, 1], repeat: Infinity }}\n      />\n    </div>\n  )\n}\n",
      "type": "registry:component",
      "target": "components/ui/loaders-morph.tsx"
    }
  ],
  "categories": [
    "styled",
    "loaders"
  ],
  "type": "registry:component"
}