{"$schema":"https://ui.shadcn.com/schema/registry-item.json","name":"typing-indicator","type":"registry:ui","title":"TypingIndicator","description":"Animated three-dot presence indicator with a staggered CSS wave, tuned to signal activity without pulling focus","author":"fancy-ui <https://fancy-ui.rama.app>","categories":["ai-chat"],"registryDependencies":["https://fancy-ui.rama.app/r/utils.json"],"files":[{"path":"react/src/components/typing-indicator/TypingIndicator.tsx","content":"\"use client\";\n\nimport { forwardRef, type HTMLAttributes } from \"react\";\nimport { cn } from \"../../utils\";\nimport \"./typing-indicator.css\";\n\nexport interface TypingIndicatorProps extends HTMLAttributes<HTMLDivElement> {\n\t/** Dot diameter in pixels */\n\tsize?: number;\n\t/** Dot color; any CSS color or custom property expression */\n\tcolor?: string;\n\t/** Duration of one full animation cycle in seconds */\n\tspeed?: number;\n\t/** Visually hidden text announced to assistive technology */\n\tlabel?: string;\n\t/** Additional CSS classes */\n\tclassName?: string;\n}\n\n/**\n * Three dots animated as a single wave, wrapped in a polite live region.\n */\nexport const TypingIndicator = forwardRef<HTMLDivElement, TypingIndicatorProps>(\n\t(\n\t\t{\n\t\t\tsize = 6,\n\t\t\tcolor = \"var(--ft-typing-color, currentColor)\",\n\t\t\tspeed = 1.2,\n\t\t\tlabel = \"Typing\",\n\t\t\tclassName,\n\t\t\tstyle,\n\t\t\t...rest\n\t\t},\n\t\tref\n\t) => {\n\t\t// A zero or negative animation-duration invalidates the whole declaration.\n\t\tconst cycle = Math.max(0.01, speed);\n\n\t\t// `color` already resolves the public `--ft-typing-color`, so the dots read the\n\t\t// outcome under a separate name — a custom property may not reference itself.\n\t\tconst rootStyle = {\n\t\t\t...style,\n\t\t\t[\"--ft-typing-size\" as string]: `${size}px`,\n\t\t\t[\"--ft-typing-color-resolved\" as string]: color,\n\t\t\t[\"--ft-typing-speed\" as string]: `${cycle}s`,\n\t\t\tgap: \"calc(var(--ft-typing-size) * 0.6)\",\n\t\t};\n\n\t\t// Each dot lags the previous one by a sixth of the cycle, so the wave reads\n\t\t// as a single gesture rather than three independent blinks.\n\t\tconst dotStyles = [0, 1, 2].map((i) => ({\n\t\t\tanimationDelay: `${Number(((i * cycle) / 6).toFixed(4))}s`,\n\t\t}));\n\n\t\treturn (\n\t\t\t<div\n\t\t\t\t{...rest}\n\t\t\t\tref={ref}\n\t\t\t\tclassName={cn(\"ft-typing inline-flex items-center\", className)}\n\t\t\t\tstyle={rootStyle}\n\t\t\t\trole=\"status\"\n\t\t\t\taria-live=\"polite\"\n\t\t\t>\n\t\t\t\t{dotStyles.map((dotStyle, i) => (\n\t\t\t\t\t<span key={i} className=\"ft-dot\" style={dotStyle} aria-hidden=\"true\" />\n\t\t\t\t))}\n\t\t\t\t<span className=\"sr-only\">{label}</span>\n\t\t\t</div>\n\t\t);\n\t}\n);\n\nTypingIndicator.displayName = \"TypingIndicator\";\n","type":"registry:ui","target":"@ui/fancy/components/typing-indicator/TypingIndicator.tsx"},{"path":"react/src/components/typing-indicator/index.ts","content":"\"use client\";\n\nexport { TypingIndicator } from \"./TypingIndicator\";\nexport type { TypingIndicatorProps } from \"./TypingIndicator\";\n","type":"registry:ui","target":"@ui/fancy/components/typing-indicator/index.ts"},{"path":"react/src/components/typing-indicator/typing-indicator.css","content":"/*\n * Svelte scoped these rules to the component; plain CSS is global. The source\n * root carries no class of its own, so `.ft-typing` is added by the port purely\n * as an anchor — without it a bare `.ft-dot` in a consumer's page would inherit\n * the dot geometry and the wave.\n */\n.ft-typing .ft-dot {\n\tdisplay: block;\n\twidth: var(--ft-typing-size);\n\theight: var(--ft-typing-size);\n\tborder-radius: 9999px;\n\tbackground-color: var(--ft-typing-color-resolved);\n\t/*\n\t * Reduced motion holds the dots here: three static dots still read as\n\t * presence. Keeping the wave entirely behind the no-preference query means\n\t * there is nothing to override.\n\t */\n\topacity: 0.5;\n}\n\n@media (prefers-reduced-motion: no-preference) {\n\t.ft-typing .ft-dot {\n\t\tanimation: ft-typing-pulse var(--ft-typing-speed) ease-in-out infinite;\n\t}\n\n\t@keyframes ft-typing-pulse {\n\t\t0%,\n\t\t60%,\n\t\t100% {\n\t\t\ttransform: translateY(0);\n\t\t\topacity: 0.45;\n\t\t}\n\t\t30% {\n\t\t\ttransform: translateY(-25%);\n\t\t\topacity: 1;\n\t\t}\n\t}\n}\n","type":"registry:ui","target":"@ui/fancy/components/typing-indicator/typing-indicator.css"}],"meta":{"license":"MIT","package":"fancy-ui-react","version":"0.7.0","source":"https://fancy-ui.rama.app/docs/components/typing-indicator?framework=react"}}