{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "loaders-text",
  "title": "Text loaders",
  "description": "Styled loaders: the Text set. 5 decorative loaders variations (TypingLoader, EllipsisLoader, ScrambleLoader, WaveTextLoader, BlinkLoader) 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-text.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/* Text loader family: 5 decorative indicators with a label plus typographic motion. Colour comes ONLY from tokens (text-*). Size = the font scale. When `label` is a string it is split into letters; when it is not, it renders as-is. DELIBERATE: it keeps moving under reduced-motion too. The delays are index-derived and fixed, 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\nconst font: Record<StyledSize, string> = {\n  sm: \"text-xs\",\n  md: \"text-sm\",\n  lg: \"text-base\",\n  xl: \"text-lg\",\n}\n\ntype Props = React.ComponentProps<\"div\"> & { size?: StyledSize; label?: string }\n\n/* Typing: label sozcugu + animasyonlu ... noktalari. */\nexport function TypingLoader({ className, size = \"md\", label = \"Loading\", ...props }: Props) {\n  const dots = [0, 1, 2]\n  return (\n    <div\n      data-slot=\"styled-loader\"\n      role=\"status\"\n      aria-label={label}\n      className={cn(\"inline-flex items-center gap-1 font-medium text-primary\", font[size], className)}\n      {...props}\n    >\n      <span>{label}</span>\n      <span className=\"inline-flex\">\n        {dots.map((i) => (\n          <motion.span\n            key={i}\n            className=\"inline-block\"\n            animate={{ opacity: [0.2, 1, 0.2] }}\n            transition={{ duration: 1.2, ease: \"easeInOut\", repeat: Infinity, delay: i * 0.2 }}\n          >\n            .\n          </motion.span>\n        ))}\n      </span>\n    </div>\n  )\n}\n\n/* Ellipsis: a dot cycle . .. ... (the width changes). */\nexport function EllipsisLoader({ className, size = \"md\", label = \"Loading\", ...props }: Props) {\n  const dots = [0, 1, 2]\n  return (\n    <div\n      data-slot=\"styled-loader\"\n      role=\"status\"\n      aria-label={label}\n      className={cn(\"inline-flex items-center font-medium text-info\", font[size], className)}\n      {...props}\n    >\n      {dots.map((i) => (\n        <motion.span\n          key={i}\n          className=\"inline-block\"\n          animate={{ opacity: [0, 1, 0] }}\n          transition={{ duration: 1.5, ease: \"easeInOut\", repeat: Infinity, delay: i * 0.25 }}\n        >\n          .\n        </motion.span>\n      ))}\n    </div>\n  )\n}\n\n/* letter-splitting yardimcisi: label string ise harflere boler. */\nfunction letters(label: string) {\n  return Array.from(label)\n}\n\n/* Scramble: the letters of a short label jump and shake. */\nexport function ScrambleLoader({ className, size = \"md\", label = \"Loading\", ...props }: Props) {\n  const chars = letters(label)\n  return (\n    <div\n      data-slot=\"styled-loader\"\n      role=\"status\"\n      aria-label={label}\n      className={cn(\"inline-flex items-center font-medium text-brand\", font[size], className)}\n      {...props}\n    >\n      {chars.map((ch, i) => (\n        <motion.span\n          key={i}\n          className=\"inline-block whitespace-pre\"\n          animate={{ y: [0, -3, 0], opacity: [0.5, 1, 0.5] }}\n          transition={{ duration: 0.9, ease: \"easeInOut\", repeat: Infinity, delay: i * 0.06 }}\n        >\n          {ch}\n        </motion.span>\n      ))}\n    </div>\n  )\n}\n\n/* WaveText: label harfleri dalga halinde zipla. */\nexport function WaveTextLoader({ className, size = \"md\", label = \"Loading\", ...props }: Props) {\n  const chars = letters(label)\n  return (\n    <div\n      data-slot=\"styled-loader\"\n      role=\"status\"\n      aria-label={label}\n      className={cn(\"inline-flex items-center font-medium text-success\", font[size], className)}\n      {...props}\n    >\n      {chars.map((ch, i) => (\n        <motion.span\n          key={i}\n          className=\"inline-block whitespace-pre\"\n          animate={{ y: [\"0%\", \"-45%\", \"0%\"] }}\n          transition={{ duration: 1, ease: \"easeInOut\", repeat: Infinity, delay: i * 0.08 }}\n        >\n          {ch}\n        </motion.span>\n      ))}\n    </div>\n  )\n}\n\n/* Blink: label + yanip sonen blok imlec. */\nexport function BlinkLoader({ className, size = \"md\", label = \"Loading\", ...props }: Props) {\n  const reduce = useReducedMotion()\n  return (\n    <div\n      data-slot=\"styled-loader\"\n      role=\"status\"\n      aria-label={label}\n      className={cn(\"inline-flex items-center gap-1 font-medium text-warning\", font[size], className)}\n      {...props}\n    >\n      <span>{label}</span>\n      <motion.span\n        aria-hidden=\"true\"\n        className=\"inline-block h-[1em] w-[0.5ch] bg-current align-middle\"\n        animate={{ opacity: reduce ? [0.3, 1, 0.3] : [1, 1, 0, 0] }}\n        transition={{ duration: 1, ease: \"linear\", repeat: Infinity, times: reduce ? [0, 0.5, 1] : [0, 0.5, 0.5, 1] }}\n      />\n    </div>\n  )\n}\n",
      "type": "registry:component",
      "target": "components/ui/loaders-text.tsx"
    }
  ],
  "categories": [
    "styled",
    "loaders"
  ],
  "type": "registry:component"
}