feat: add Minecraft server page with copyable address, map integration, and supporting UI components

This commit is contained in:
2026-06-26 18:44:02 +07:00 Unverified
parent 5121229b93
commit 91e9d9c54e
9 changed files with 225 additions and 1 deletions
+54
View File
@@ -0,0 +1,54 @@
"use client";
import React, { useState } from "react";
import { Check, Copy } from "lucide-react";
import { Button } from "@/components/ui/button";
import { copyToClipboard } from "@/lib/utils";
import { cn } from "@/lib/utils";
export interface CopyButtonProps extends React.ButtonHTMLAttributes<HTMLButtonElement> {
text?: string | (() => string);
onCopy?: () => void;
iconOnly?: boolean;
variant?: any;
}
export const CopyButton = React.forwardRef<HTMLButtonElement, CopyButtonProps>(
({ text, onCopy, iconOnly = true, className, variant = "link", children, onClick, ...props }, ref) => {
const [copied, setCopied] = useState(false);
const handleCopy = (e: React.MouseEvent<HTMLButtonElement>) => {
e.stopPropagation();
if (text) {
const copyContent = typeof text === "function" ? text() : text;
copyToClipboard(copyContent);
}
if (onCopy) {
onCopy();
}
if (onClick) {
onClick(e);
}
setCopied(true);
setTimeout(() => setCopied(false), 2000);
};
return (
<Button
ref={ref}
onClick={handleCopy}
variant={variant}
className={cn(iconOnly ? "h-auto p-0 text-green-400 hover:text-green-300" : "", className)}
{...props}
>
{copied ? <Check className={iconOnly ? "w-3 h-3" : "w-4 h-4 mr-2 text-green-400"} /> : <Copy className={iconOnly ? "w-3 h-3" : "w-4 h-4 mr-2"} />}
{!iconOnly && children}
</Button>
);
}
);
CopyButton.displayName = "CopyButton";
+26
View File
@@ -0,0 +1,26 @@
import { cn } from "@/lib/utils"
function Kbd({ className, ...props }: React.ComponentProps<"kbd">) {
return (
<kbd
data-slot="kbd"
className={cn(
"pointer-events-none inline-flex h-5 w-fit min-w-5 items-center justify-center gap-1 rounded-sm bg-muted px-1 font-sans text-xs font-medium text-muted-foreground select-none in-data-[slot=tooltip-content]:bg-background/20 in-data-[slot=tooltip-content]:text-background dark:in-data-[slot=tooltip-content]:bg-background/10 [&_svg:not([class*='size-'])]:size-3",
className
)}
{...props}
/>
)
}
function KbdGroup({ className, ...props }: React.ComponentProps<"div">) {
return (
<kbd
data-slot="kbd-group"
className={cn("inline-flex items-center gap-1", className)}
{...props}
/>
)
}
export { Kbd, KbdGroup }