Components
Loading preview...
Enhanced shadcn/ui dialog
npx shadcn@latest add https://21st.dev/r/originui/dialog"use client";
import { cn } from "@/lib/utils";
import { Button } from "@/components/ui/button";
import {
Dialog,
DialogContent,
DialogDescription,
DialogHeader,
DialogTitle,
DialogTrigger,
} from "@/components/ui/dialog";
import { Input } from "@/components/ui/input";
import { Label } from "@/components/ui/label";
import {
Tooltip,
TooltipContent,
TooltipProvider,
TooltipTrigger,
} from "@/components/ui/tooltip";
import { Check, Copy, UserRoundPlus } from "lucide-react";
import { useId, useRef, useState } from "react";
function Component() {
const id = useId();
const [emails, setEmails] = useState(["mark@yourcompany.com", "jane@yourcompany.com", ""]);
const [copied, setCopied] = useState<boolean>(false);
const inputRef = useRef<HTMLInputElement>(null);
const lastInputRef = useRef<HTMLInputElement>(null);
const addEmail = () => {
setEmails([...emails, ""]);
};
const handleEmailChange = (index: number, value: string) => {
const newEmails = [...emails];
newEmails[index] = value;
setEmails(newEmails);
};
const handleCopy = () => {
if (inputRef.current) {
navigator.clipboard.writeText(inputRef.current.value);
setCopied(true);
setTimeout(() => setCopied(false), 1500);
}
};
return (
<Dialog>
<DialogTrigger asChild>
<Button variant="outline">Invite members</Button>
</DialogTrigger>
<DialogContent
onOpenAutoFocus={(e) => {
e.preventDefault();
lastInputRef.current?.focus();
}}
>
<div className="flex flex-col gap-2">
<div
className="flex size-11 shrink-0 items-center justify-center rounded-full border border-border"
aria-hidden="true"
>
<UserRoundPlus className="opacity-80" size={16} strokeWidth={2} />
</div>
<DialogHeader>
<DialogTitle className="text-left">Invite team members</DialogTitle>
<DialogDescription className="text-left">
Invite teammates to earn free components.
</DialogDescription>
</DialogHeader>
</div>
<form className="space-y-5">
<div className="space-y-4">
<div className="space-y-2">
<Label>Invite via email</Label>
<div className="space-y-3">
{emails.map((email, index) => (
<Input
key={index}
id={`team-email-${index + 1}`}
placeholder="hi@yourcompany.com"
type="email"
value={email}
onChange={(e) => handleEmailChange(index, e.target.value)}
ref={index === emails.length - 1 ? lastInputRef : undefined}
/>
))}
</div>
</div>
<button
type="button"
onClick={addEmail}
className="text-sm underline hover:no-underline"
>
+ Add another
</button>
</div>
<Button type="button" className="w-full">
Send invites
</Button>
</form>
<hr className="my-1 border-t border-border" />
<div className="space-y-2">
<Label htmlFor={id}>Invite via magic link</Label>
<div className="relative">
<Input
ref={inputRef}
id={id}
className="pe-9"
type="text"
defaultValue="https://originui.com/refer/87689"
readOnly
/>
<TooltipProvider delayDuration={0}>
<Tooltip>
<TooltipTrigger asChild>
<button
onClick={handleCopy}
className="absolute inset-y-0 end-0 flex h-full w-9 items-center justify-center rounded-e-lg border border-transparent text-muted-foreground/80 outline-offset-2 transition-colors hover:text-foreground focus-visible:text-foreground focus-visible:outline focus-visible:outline-2 focus-visible:outline-ring/70 disabled:pointer-events-none disabled:cursor-not-allowed"
aria-label={copied ? "Copied" : "Copy to clipboard"}
disabled={copied}
>
<div
className={cn(
"transition-all",
copied ? "scale-100 opacity-100" : "scale-0 opacity-0",
)}
>
<Check
className="stroke-emerald-500"
size={16}
strokeWidth={2}
aria-hidden="true"
/>
</div>
<div
className={cn(
"absolute transition-all",
copied ? "scale-0 opacity-0" : "scale-100 opacity-100",
)}
>
<Copy size={16} strokeWidth={2} aria-hidden="true" />
</div>
</button>
</TooltipTrigger>
<TooltipContent className="px-2 py-1 text-xs">Copy to clipboard</TooltipContent>
</Tooltip>
</TooltipProvider>
</div>
</div>
</DialogContent>
</Dialog>
);
}
export { Component };