{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "file-input",
  "title": "File Input",
  "description": "A drop zone that wakes up when a file is over it, with new file rows arriving one after another.",
  "dependencies": [
    "@ui-sdk/core@0.1.0-beta.2",
    "@ui-sdk/react@0.1.0-beta.2"
  ],
  "registryDependencies": [
    "@keyframery/motion"
  ],
  "files": [
    {
      "path": "registry/components/keyframery/primitives/file-input.tsx",
      "content": "\"use client\"\n\n/* Animated drop-in for `@ui-sdk/react/file-input`: same parts, same props. Swap the import and keep\n   your code. This file adds motion only. Colours, radii, shadows and spacing stay yours. */\n\nimport {\n  FileInputDropzone as FileInputDropzonePart,\n  FileInputItem as FileInputItemPart,\n  FileInputItemRemove as FileInputItemRemovePart,\n  FileInputTrigger as FileInputTriggerPart,\n} from \"@ui-sdk/react/file-input\"\nimport * as React from \"react\"\n\nimport { cn } from \"@/lib/utils\"\n\nexport * from \"@ui-sdk/react/file-input\"\n\n/* While files are over it, the zone lifts toward them on a spring. */\nfunction FileInputDropzone({\n  className,\n  ...props\n}: React.ComponentProps<typeof FileInputDropzonePart>) {\n  return (\n    <FileInputDropzonePart\n      className={cn(\n        \"[transition:translate_calc(460ms*var(--kf-speed,1))_var(--ease-spring),scale_calc(460ms*var(--kf-speed,1))_var(--ease-spring),border-color_calc(150ms*var(--kf-speed,1))_var(--ease-house),background-color_calc(150ms*var(--kf-speed,1))_var(--ease-house),box-shadow_calc(200ms*var(--kf-speed,1))_var(--ease-house)]\",\n        \"data-dragging:-translate-y-1.5 data-dragging:scale-[1.02]\",\n        \"motion-reduce:[transition:none]!\",\n        className,\n      )}\n      {...props}\n    />\n  )\n}\n\nfunction FileInputTrigger({ className, ...props }: React.ComponentProps<typeof FileInputTriggerPart>) {\n  return (\n    <FileInputTriggerPart\n      className={cn(\n        \"[transition:scale_calc(260ms*var(--kf-speed,1))_var(--ease-pop),translate_calc(260ms*var(--kf-speed,1))_var(--ease-pop),box-shadow_calc(120ms*var(--kf-speed,1))_var(--ease-house),background-color_calc(120ms*var(--kf-speed,1))_var(--ease-house)] active:scale-[0.96]\",\n        \"motion-reduce:[transition:none]!\",\n        className,\n      )}\n      {...props}\n    />\n  )\n}\n\n/* Cascade: rows rise in out of a blur, each one 32ms after the row above it. Only opacity,\n   translate and filter transition here, so the delay never slows anything else. */\nfunction FileInputItem({ className, ...props }: React.ComponentProps<typeof FileInputItemPart>) {\n  return (\n    <FileInputItemPart\n      className={cn(\n        \"transition-[opacity,translate,filter] duration-[calc(420ms*var(--kf-speed,1))] ease-spring [transition-delay:calc((min(sibling-index(),10)-1)*calc(32ms*var(--kf-speed,1)))]\",\n        \"starting:translate-y-2 starting:opacity-0 starting:blur-[4px]\",\n        \"motion-reduce:transition-none!\",\n        className,\n      )}\n      {...props}\n    />\n  )\n}\n\nfunction FileInputItemRemove({\n  className,\n  ...props\n}: React.ComponentProps<typeof FileInputItemRemovePart>) {\n  return (\n    <FileInputItemRemovePart\n      className={cn(\n        \"[transition:scale_calc(260ms*var(--kf-speed,1))_var(--ease-pop),color_calc(150ms*var(--kf-speed,1)),background-color_calc(150ms*var(--kf-speed,1))] active:scale-[0.82]\",\n        \"motion-reduce:[transition:none]!\",\n        className,\n      )}\n      {...props}\n    />\n  )\n}\n\nexport { FileInputDropzone, FileInputItem, FileInputItemRemove, FileInputTrigger }\n",
      "type": "registry:component",
      "target": "components/keyframery/primitives/file-input.tsx"
    },
    {
      "path": "registry/components/keyframery/file-input.tsx",
      "content": "\"use client\"\n\n/* Styled file input: the animated primitive plus the Keyframery look. */\n\nimport * as React from \"react\"\n\nimport {\n  FileInput as FileInputRoot,\n  FileInputDropzone as FileInputDropzonePart,\n  FileInputItem as FileInputItemPart,\n  FileInputItemRemove as FileInputItemRemovePart,\n  FileInputList as FileInputListPart,\n  FileInputTrigger as FileInputTriggerPart,\n  createFileInputController,\n  formatFileSize,\n  relativePath,\n} from \"@/components/keyframery/primitives/file-input\"\nimport { cn } from \"@/lib/utils\"\n\nfunction FileInput({ className, ...props }: React.ComponentProps<typeof FileInputRoot>) {\n  return <FileInputRoot className={cn(\"flex min-w-0 flex-col gap-3\", className)} {...props} />\n}\n\n/* A dashed hairline at rest. While files are over it the line turns solid and the zone\n   tints: ring colour when they would be taken, destructive when they would not. */\nfunction FileInputDropzone({\n  className,\n  ...props\n}: React.ComponentProps<typeof FileInputDropzonePart>) {\n  return (\n    <FileInputDropzonePart\n      className={cn(\n        \"group/drop relative flex flex-col items-center justify-center gap-1 rounded-[14px] border border-dashed border-foreground/15 px-6 py-7 text-center text-[13px] outline-none\",\n        \"hover:border-foreground/25\",\n        \"data-dragging:shadow-[0_10px_28px_-12px_rgb(0_0_0/0.25)] dark:data-dragging:shadow-[0_12px_32px_-12px_rgb(0_0_0/0.8)]\",\n        \"focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 focus-visible:ring-offset-background\",\n        \"data-dragging:border-solid\",\n        \"data-drag-accept:border-ring data-drag-accept:bg-ring/[0.07] data-drag-accept:ring-[3px] data-drag-accept:ring-ring/15\",\n        \"data-drag-reject:border-destructive data-drag-reject:bg-destructive/[0.05] data-drag-reject:ring-[3px] data-drag-reject:ring-destructive/15\",\n        \"data-disabled:cursor-not-allowed data-disabled:border-foreground/10 data-disabled:opacity-60\",\n        \"[&>svg]:size-4\",\n        className,\n      )}\n      {...props}\n    />\n  )\n}\n\n/* The mark at the top of a drop zone: a small key holding the upload arrow. While files are\n   over the zone it rises 8px with a pop, on top of the zone's own lift. Purely visual. */\nfunction FileInputDropzoneIcon({ className, ...props }: React.ComponentProps<\"span\">) {\n  return (\n    <span\n      aria-hidden=\"true\"\n      data-slot=\"file-input-dropzone-icon\"\n      className={cn(\n        \"mb-2 grid size-9 place-items-center rounded-[10px] bg-background text-foreground\",\n        \"shadow-[0_0_0_1px_rgb(0_0_0/0.06),0_1px_1px_rgb(0_0_0/0.05),0_3px_6px_-2px_rgb(0_0_0/0.1)]\",\n        \"dark:bg-accent dark:shadow-[0_0_0_1px_rgb(255_255_255/0.07),inset_0_1px_0_rgb(255_255_255/0.08),0_2px_6px_-1px_rgb(0_0_0/0.6)]\",\n        \"[transition:translate_calc(480ms*var(--kf-speed,1))_var(--ease-pop),scale_calc(480ms*var(--kf-speed,1))_var(--ease-pop),color_calc(150ms*var(--kf-speed,1))_var(--ease-house)] motion-reduce:[transition:none]!\",\n        \"group-data-dragging/drop:-translate-y-2 group-data-dragging/drop:scale-110 group-data-drag-accept/drop:text-ring group-data-drag-reject/drop:text-destructive\",\n        \"[&_svg]:size-4\",\n        className,\n      )}\n      {...props}\n    >\n      <svg\n        viewBox=\"0 0 16 16\"\n        fill=\"none\"\n        stroke=\"currentColor\"\n        strokeWidth=\"1.5\"\n        strokeLinecap=\"round\"\n        strokeLinejoin=\"round\"\n      >\n        <path d=\"M8 10.5V2.75M4.75 6 8 2.75 11.25 6\" />\n        <path d=\"M2.75 10.5v1.25c0 .83.67 1.5 1.5 1.5h7.5c.83 0 1.5-.67 1.5-1.5V10.5\" />\n      </svg>\n    </span>\n  )\n}\n\n/* A key that opens the system picker. */\nfunction FileInputTrigger({\n  className,\n  ...props\n}: React.ComponentProps<typeof FileInputTriggerPart>) {\n  return (\n    <FileInputTriggerPart\n      className={cn(\n        \"inline-flex h-8 shrink-0 cursor-pointer items-center justify-center gap-1.5 rounded-[9px] bg-background px-3 text-[13px] leading-none font-medium whitespace-nowrap text-foreground outline-none select-none\",\n        \"shadow-[0_0_0_1px_rgb(0_0_0/0.06),0_1px_1px_rgb(0_0_0/0.05),0_3px_6px_-2px_rgb(0_0_0/0.1)]\",\n        \"dark:bg-accent dark:shadow-[0_0_0_1px_rgb(255_255_255/0.07),inset_0_1px_0_rgb(255_255_255/0.08),0_2px_6px_-1px_rgb(0_0_0/0.6)]\",\n        \"hover:bg-muted/50 dark:hover:bg-accent/80\",\n        \"active:translate-y-px active:shadow-[0_0_0_1px_rgb(0_0_0/0.07),0_0_1px_rgb(0_0_0/0.08)] dark:active:shadow-[0_0_0_1px_rgb(255_255_255/0.06),0_0_1px_rgb(0_0_0/0.6)]\",\n        \"focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 focus-visible:ring-offset-background\",\n        \"data-invalid:ring-[3px] data-invalid:ring-destructive/25 data-invalid:shadow-[0_0_0_1px_var(--destructive)]\",\n        \"data-disabled:pointer-events-none data-disabled:opacity-50\",\n        \"[&_svg]:size-4 [&_svg]:shrink-0\",\n        className,\n      )}\n      {...props}\n    />\n  )\n}\n\n/* One Tab stop; arrow keys move between rows. */\nfunction FileInputList({\n  className,\n  ...props\n}: React.ComponentProps<typeof FileInputListPart>) {\n  return (\n    <FileInputListPart\n      className={cn(\n        \"flex flex-col gap-1.5 rounded-[12px] outline-none empty:hidden\",\n        \"focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 focus-visible:ring-offset-background\",\n        className,\n      )}\n      {...props}\n    />\n  )\n}\n\n/* A row; the primitive cascades the rows in. */\nfunction FileInputItem({\n  className,\n  ...props\n}: React.ComponentProps<typeof FileInputItemPart>) {\n  return (\n    <FileInputItemPart\n      className={cn(\n        \"flex min-w-0 items-center gap-3 rounded-[10px] bg-background py-1.5 ps-1.5 pe-2 text-[13px] text-foreground outline-none\",\n        \"shadow-[0_0_0_1px_rgb(0_0_0/0.08),0_1px_2px_rgb(0_0_0/0.04)] dark:bg-accent/40 dark:shadow-[0_0_0_1px_rgb(255_255_255/0.08)]\",\n        \"focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 focus-visible:ring-offset-background\",\n        \"data-disabled:opacity-60\",\n        className,\n      )}\n      {...props}\n    />\n  )\n}\n\nfunction FileInputItemRemove({\n  className,\n  children,\n  ...props\n}: React.ComponentProps<typeof FileInputItemRemovePart>) {\n  return (\n    <FileInputItemRemovePart\n      className={cn(\n        \"ms-auto grid size-6 shrink-0 cursor-pointer place-items-center rounded-[7px] text-muted-foreground outline-none\",\n        \"hover:bg-foreground/[0.06] hover:text-foreground\",\n        \"focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 focus-visible:ring-offset-background\",\n        \"data-disabled:pointer-events-none data-disabled:opacity-40\",\n        \"[&_svg]:size-3.5\",\n        className,\n      )}\n      {...props}\n    >\n      {children ?? (\n        <svg\n          viewBox=\"0 0 16 16\"\n          fill=\"none\"\n          stroke=\"currentColor\"\n          strokeWidth=\"1.5\"\n          strokeLinecap=\"round\"\n          strokeLinejoin=\"round\"\n          aria-hidden=\"true\"\n        >\n          <path d=\"m4.5 4.5 7 7m0-7-7 7\" />\n        </svg>\n      )}\n    </FileInputItemRemovePart>\n  )\n}\n\nexport {\n  FileInput,\n  FileInputDropzone,\n  FileInputDropzoneIcon,\n  FileInputItem,\n  FileInputItemRemove,\n  FileInputList,\n  FileInputTrigger,\n  createFileInputController,\n  formatFileSize,\n  relativePath,\n}\n",
      "type": "registry:component",
      "target": "components/keyframery/file-input.tsx"
    }
  ],
  "type": "registry:component"
}