{"$schema":"https://ui.shadcn.com/schema/registry-item.json","name":"motion-field","type":"registry:lib","title":"Motion field","description":"The description and error every motion form control shows: an error grows in under the control and folds away.","dependencies":["motion"],"registryDependencies":["https://dashral.com/r/utils.json","https://dashral.com/r/motion-reduced.json","https://dashral.com/r/motion-tokens.json","https://dashral.com/r/field.json"],"files":[{"path":"registry/dashral/lib/motion-field.tsx","type":"registry:file","target":"components/dashral/lib/motion-field.tsx","content":"\"use client\"\n\n// The description and error every motion form control shows: an error grows in under the control and folds away.\n// The words below glide down as it grows instead of jumping, and a new message crosses over the old one in the same\n// space. Each line brings its own space above it, so a message's space grows in with it rather than popping.\n\nimport * as React from \"react\"\nimport { AnimatePresence, motion } from \"motion/react\"\nimport type { FieldMessages } from \"../field\"\nimport { cn } from \"./utils\"\n\nimport { useReducedMotion } from \"./motion-reduced\"\nimport { duration, ease } from \"./motion-tokens\"\n\n/** What a message says, for telling a new one from a re-render of the same; null when there's none. */\nfunction messageKeyOf(message: React.ReactNode) {\n  if (!message) return null\n  return typeof message === \"string\" || typeof message === \"number\" ? String(message) : \"node\"\n}\n\n/** An error under (or beside) a control: it grows in and folds away, and a new one crosses over the old. */\nexport function FieldMessage({\n  id,\n  message,\n  space = \"pt-2\",\n  className,\n}: {\n  id?: string\n  message: React.ReactNode\n  /** The space above it, grown in with it: pt-2 under a control, pt-1 in a checkbox's or switch's label column. */\n  space?: string\n  className?: string\n}) {\n  const reduced = useReducedMotion()\n  const fold = { duration: reduced ? 0 : duration.standard, ease: ease.standard }\n  return (\n    <AnimatePresence initial={false}>\n      {message && (\n        <motion.div\n          key=\"error\"\n          className=\"overflow-hidden\"\n          initial={{ height: 0, opacity: 0 }}\n          animate={{ height: \"auto\", opacity: 1 }}\n          exit={{ height: 0, opacity: 0, transition: { ...fold, duration: reduced ? 0 : duration.fast } }}\n          transition={fold}\n        >\n          <div className={cn(\"grid\", space)}>\n            <AnimatePresence mode=\"popLayout\" initial={false}>\n              <motion.p\n                key={messageKeyOf(message)}\n                id={id}\n                data-slot=\"field-error\"\n                className={cn(\"col-start-1 row-start-1 text-xs text-destructive-text\", className)}\n                initial={reduced ? { opacity: 0 } : { opacity: 0, y: -4 }}\n                animate={{ opacity: 1, y: 0 }}\n                exit={{ opacity: 0, transition: { duration: 0.1, ease: ease.exit } }}\n                transition={{ duration: duration.standard, ease: ease.enter }}\n              >\n                {message}\n              </motion.p>\n            </AnimatePresence>\n          </div>\n        </motion.div>\n      )}\n    </AnimatePresence>\n  )\n}\n\n/**\n * For a kit Field, FieldWrapper or ChoiceField's renderMessages: the description, and the error growing in under it.\n * The field's own gap is taken back above them (each line brings its space), so nothing pops as an error arrives.\n */\nexport const motionMessages: FieldMessages = ({ description, descriptionId, error, errorId, layout, classNames }) => {\n  const space = layout === \"field\" ? \"pt-2\" : \"pt-1\"\n  return (\n    <div className={cn(\"grid min-w-0\", layout === \"field\" ? \"-mt-2\" : \"-mt-1\")}>\n      {description && (\n        <p id={descriptionId} data-slot=\"field-description\" className={cn(space, \"text-xs text-muted-foreground\", classNames?.description)}>\n          {description}\n        </p>\n      )}\n      <FieldMessage id={errorId} message={error} space={space} className={classNames?.error} />\n    </div>\n  )\n}\n"}]}