{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "feedback",
  "type": "registry:component",
  "title": "Feedback",
  "description": "A feedback component with popover form.",
  "dependencies": [
    "@hookform/resolvers",
    "react-hook-form",
    "sonner",
    "zod",
    "lucide-react",
    "class-variance-authority"
  ],
  "registryDependencies": [
    "button",
    "form",
    "popover",
    "textarea"
  ],
  "files": [
    {
      "path": "src/components/feedback.tsx",
      "content": "\"use client\";\n\nimport { zodResolver } from \"@hookform/resolvers/zod\";\nimport { Inbox, LoaderCircle } from \"lucide-react\";\nimport { useEffect, useState, useTransition } from \"react\";\nimport { useForm } from \"react-hook-form\";\nimport { toast } from \"sonner\";\nimport { z } from \"zod\";\nimport { Button } from \"@/components/ui/button\";\nimport {\n  Form,\n  FormControl,\n  FormField,\n  FormItem,\n  FormLabel,\n} from \"@/components/ui/form\";\nimport {\n  Popover,\n  PopoverContent,\n  PopoverTrigger,\n} from \"@/components/ui/popover\";\nimport { Textarea } from \"@/components/ui/textarea\";\nimport { useIsMobile } from \"@/hooks/use-mobile\";\nimport { cn } from \"@/lib/utils\";\n\nconst schema = z.object({\n  message: z.string().min(1),\n});\n\nexport function Feedback({\n  className,\n  ...props\n}: React.ComponentProps<typeof Button>) {\n  const [open, setOpen] = useState(false);\n  const isMobile = useIsMobile();\n  const form = useForm<z.infer<typeof schema>>({\n    resolver: zodResolver(schema),\n    defaultValues: {\n      message: \"\",\n    },\n  });\n  const [isSuccess, setIsSuccess] = useState(false);\n  const [pending, startTransition] = useTransition();\n\n  const onSubmit = (values: z.infer<typeof schema>) => {\n    startTransition(async () => {\n      const promise = new Promise<void>((resolve) => {\n        setTimeout(() => {\n          setIsSuccess(true);\n          resolve();\n        }, 1000);\n      });\n      console.log(values);\n      toast.promise(promise, {\n        loading: \"Sending feedback...\",\n        success: \"Feedback sent!\",\n        error: \"Failed to send feedback\",\n      });\n      await promise;\n    });\n  };\n\n  useEffect(() => {\n    if (!open && isSuccess) {\n      // NOTE: the popover takes 300ms to close, so we need to wait for that\n      setTimeout(() => setIsSuccess(false), 300);\n    }\n  }, [open, isSuccess]);\n\n  useEffect(() => {\n    const down = (e: KeyboardEvent) => {\n      if (open && (e.metaKey || e.ctrlKey) && e.key === \"Enter\") {\n        form.handleSubmit(onSubmit)();\n      }\n\n      const target = e.target as HTMLElement;\n      const isTyping =\n        target.tagName === \"INPUT\" ||\n        target.tagName === \"TEXTAREA\" ||\n        target.isContentEditable;\n\n      if (isTyping) return;\n\n      if (!open) {\n        if (e.key === \"f\") {\n          e.preventDefault();\n          setOpen(true);\n        }\n        return;\n      }\n    };\n    document.addEventListener(\"keydown\", down);\n    return () => document.removeEventListener(\"keydown\", down);\n  }, [open, form, onSubmit]);\n\n  useEffect(() => {\n    if (!open) {\n      form.reset();\n    }\n  }, [open, form]);\n\n  if (isMobile) {\n    return null;\n  }\n\n  return (\n    <Popover open={open} onOpenChange={setOpen}>\n      <PopoverTrigger asChild>\n        <Button\n          variant=\"ghost\"\n          size=\"sm\"\n          className={cn(\n            \"group gap-0 px-2 text-muted-foreground text-sm hover:bg-transparent hover:text-foreground data-[state=open]:text-foreground\",\n            className,\n          )}\n          {...props}\n        >\n          Feedback{\" \"}\n          <kbd className=\"-me-1 ms-2 inline-flex h-5 max-h-full items-center rounded border bg-background px-1 font-medium font-mono text-[0.625rem] text-muted-foreground/70 group-hover:text-foreground group-data-[state=open]:text-foreground\">\n            F\n          </kbd>\n        </Button>\n      </PopoverTrigger>\n      <PopoverContent align=\"end\" className=\"relative border-none p-0\">\n        {isSuccess ? (\n          <div className=\"flex h-[110px] flex-col items-center justify-center gap-1 rounded-md border border-input p-3 text-base shadow-xs\">\n            <Inbox className=\"size-4 shrink-0\" />\n            <p className=\"text-center font-medium\">Thanks for sharing!</p>\n            <p className=\"text-center text-muted-foreground text-sm\">\n              We&apos;ll get in touch if there&apos;s a follow-up.\n            </p>\n          </div>\n        ) : (\n          <Form {...form}>\n            <form onSubmit={form.handleSubmit(onSubmit)}>\n              <FormField\n                control={form.control}\n                name=\"message\"\n                render={({ field }) => (\n                  <FormItem>\n                    <FormLabel className=\"sr-only\">Feedback</FormLabel>\n                    <FormControl>\n                      <Textarea\n                        placeholder=\"Ideas, bugs, or anything else...\"\n                        className=\"field-sizing-fixed h-[110px] resize-none p-3\"\n                        rows={4}\n                        {...field}\n                      />\n                    </FormControl>\n                  </FormItem>\n                )}\n              />\n              <Button\n                size=\"sm\"\n                variant=\"ghost\"\n                className=\"group absolute right-1.5 bottom-1.5 gap-0\"\n                type=\"submit\"\n                disabled={pending}\n              >\n                {pending ? (\n                  <LoaderCircle className=\"size-4 animate-spin\" />\n                ) : (\n                  <>\n                    Send\n                    <kbd className=\"-me-1 ms-2 inline-flex h-5 max-h-full items-center rounded border bg-background px-1 font-medium font-mono text-[0.625rem] text-muted-foreground/70 group-hover:text-foreground\">\n                      ⌘\n                    </kbd>\n                    <kbd className=\"-me-1 ms-2 inline-flex h-5 max-h-full items-center rounded border bg-background px-1 font-medium font-mono text-[0.625rem] text-muted-foreground/70 group-hover:text-foreground\">\n                      ↵\n                    </kbd>\n                  </>\n                )}\n              </Button>\n            </form>\n          </Form>\n        )}\n      </PopoverContent>\n    </Popover>\n  );\n}\n",
      "type": "registry:component"
    }
  ]
}