Something went wrong. Try again.
[READ-ONLY] Mirror of https://github.com/openstatusHQ/openstatus. ๐ซ Status page with uptime monitoring & API monitoring as code ๐ซ openstatus.dev
bun drizzle-orm monitoring monitoring-as-code nextjs observability on-call open-source shadcn-ui status-page statuspage synthetic-monitoring tinybird turso uptime uptime-checker uptime-monitor
Something went wrong. Try again.
3.4 kB ยท 120 lines
TSX
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121"use client";
import { Button } from "@openstatus/ui/components/ui/button";import { ButtonGroup } from "@openstatus/ui/components/ui/button-group";import { DropdownMenu, DropdownMenuContent, DropdownMenuGroup, DropdownMenuItem, DropdownMenuTrigger,} from "@openstatus/ui/components/ui/dropdown-menu";import { useCopyToClipboard } from "@openstatus/ui/hooks/use-copy-to-clipboard";import { cn } from "@openstatus/ui/lib/utils";import { toast } from "sonner";
export function CopyButton({ className, copyText, buttonText = "copy", copiedText = "copied", ...props}: React.ComponentProps<typeof Button> & { copyText: string; buttonText?: string; copiedText?: string;}) { const { copy, isCopied } = useCopyToClipboard();
return ( <Button variant="ghost" size="lg" className={cn("rounded-none p-4", className)} onClick={() => copy(copyText, { withToast: true })} {...props} > {isCopied ? `[${copiedText}]` : `[${buttonText}]`} </Button> );}
export function CopyDropdownButton({ className, ...props}: React.ComponentProps<typeof ButtonGroup>) { const { copy, isCopied } = useCopyToClipboard();
const handleCopyLink = () => { copy(window.location.href, { successMessage: "Link copied to clipboard", withToast: true, }); };
const handleCopyMarkdown = async () => { try { const item = new ClipboardItem({ "text/plain": fetch(window.location.pathname, { headers: { Accept: "text/markdown" }, }) .then((r) => { if (!r.ok) throw new Error("Failed to fetch markdown"); return r.text(); }) .then((text) => new Blob([text], { type: "text/plain" })), });
await navigator.clipboard.write([item]); toast.success("Markdown copied to clipboard"); } catch (_error) { toast.error("Failed to copy markdown"); } };
return ( <ButtonGroup className={cn("rounded-none border-none p-4", className)} {...props} > <Button variant="ghost" className="rounded-none p-4" onClick={handleCopyLink} > {isCopied ? "[link copied]" : "[copy link]"} </Button> <DropdownMenu> <DropdownMenuTrigger asChild> <Button variant="ghost" className="group rounded-none" aria-label="Copy dropdown" > <span className="text-muted-foreground group-hover:text-foreground group-data-[state=open]:text-foreground relative top-[1px] shrink-0 origin-center font-sans text-[10px] transition duration-300 group-data-[state=open]:rotate-180" aria-hidden="true" > โฒ </span> </Button> </DropdownMenuTrigger> <DropdownMenuContent align="end" className="min-w-[var(--radix-dropdown-menu-trigger-width)] rounded-none" alignOffset={0} sideOffset={0} > <DropdownMenuGroup> <DropdownMenuItem className="rounded-none font-mono" onClick={handleCopyMarkdown} > [copy markdown] </DropdownMenuItem> </DropdownMenuGroup> </DropdownMenuContent> </DropdownMenu> </ButtonGroup> );}