Components
Loading preview...
Here is Popover component
npx shadcn@latest add https://21st.dev/r/anubra266/popover-1"use client";
import { Popover } from "@ark-ui/react/popover";
import { Portal } from "@ark-ui/react/portal";
import { MessageSquare, Send } from "lucide-react";
import { useState } from "react";
export default function PopoverFeedback() {
const [feedback, setFeedback] = useState("");
const [isSubmitting, setIsSubmitting] = useState(false);
const [submitted, setSubmitted] = useState(false);
const handleSubmit = async (e: React.FormEvent) => {
e.preventDefault();
if (!feedback.trim()) return;
setIsSubmitting(true);
// Simulate API call
await new Promise((resolve) => setTimeout(resolve, 1000));
setIsSubmitting(false);
setSubmitted(true);
// Reset after showing success
setTimeout(() => {
setSubmitted(false);
setFeedback("");
}, 2000);
};
return (
<Popover.Root>
<Popover.Trigger className="inline-flex items-center gap-2 rounded-lg border border-gray-300 dark:border-gray-600 bg-white dark:bg-gray-800 px-3 py-1.5 text-sm font-medium text-gray-700 dark:text-gray-200 hover:bg-gray-50 dark:hover:bg-gray-700 focus:outline-none focus:ring-2 focus:ring-blue-500 focus:ring-offset-2">
<MessageSquare className="h-4 w-4" />
Feedback
</Popover.Trigger>
<Portal>
<Popover.Positioner>
<Popover.Content className="z-50 w-80 rounded-lg border border-gray-200 dark:border-gray-700 bg-white dark:bg-gray-800 p-4 shadow-lg data-[state=open]:animate-fade-in data-[state=closed]:animate-fade-out">
<Popover.Arrow className="[--arrow-size:12px] [--arrow-background:var(--color-white)] dark:[--arrow-background:var(--color-gray-800)]">
<Popover.ArrowTip className="border-t border-l border-gray-200 dark:border-gray-700" />
</Popover.Arrow>
<Popover.Title className="mb-3 text-lg font-semibold text-gray-900 dark:text-gray-100">
Send us feedback
</Popover.Title>
{submitted ? (
<div className="py-6 text-center">
<div className="mx-auto mb-3 flex h-10 w-10 items-center justify-center rounded-full bg-green-100 dark:bg-green-900">
<svg
className="h-5 w-5 text-green-600 dark:text-green-400"
fill="none"
viewBox="0 0 24 24"
stroke="currentColor"
>
<path
strokeLinecap="round"
strokeLinejoin="round"
strokeWidth={2}
d="M5 13l4 4L19 7"
/>
</svg>
</div>
<p className="text-sm text-gray-600 dark:text-gray-400">
Thank you for your feedback!
</p>
</div>
) : (
<form onSubmit={handleSubmit} className="space-y-3">
<div>
<textarea
value={feedback}
onChange={(e) => setFeedback(e.target.value)}
placeholder="How can we improve this product?"
rows={3}
className="w-full resize-none rounded-lg border border-gray-200 dark:border-gray-600 bg-gray-50 dark:bg-gray-700 px-3 py-2 text-sm text-gray-900 dark:text-gray-100 placeholder-gray-500 dark:placeholder-gray-400 focus:border-blue-500 focus:bg-white dark:focus:bg-gray-800 focus:outline-none focus:ring-1 focus:ring-blue-500"
disabled={isSubmitting}
/>
</div>
<div className="flex justify-end">
<button
type="submit"
disabled={!feedback.trim() || isSubmitting}
className="inline-flex items-center gap-2 rounded-lg bg-gray-900 dark:bg-gray-100 px-3 py-1.5 text-sm font-medium text-white dark:text-gray-900 hover:bg-gray-800 dark:hover:bg-gray-200 focus:outline-none focus:ring-2 focus:ring-gray-500 focus:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50"
>
{isSubmitting ? (
<>
<svg
className="h-4 w-4 animate-spin"
fill="none"
viewBox="0 0 24 24"
>
<circle
className="opacity-25"
cx="12"
cy="12"
r="10"
stroke="currentColor"
strokeWidth="4"
/>
<path
className="opacity-75"
fill="currentColor"
d="M4 12a8 8 0 0 1 8-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 0 1 4 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z"
/>
</svg>
Sending...
</>
) : (
<>
<Send className="h-4 w-4" />
Send feedback
</>
)}
</button>
</div>
</form>
)}
</Popover.Content>
</Popover.Positioner>
</Portal>
</Popover.Root>
);
}