1438 lines
57 KiB
TypeScript
1438 lines
57 KiB
TypeScript
import {
|
||
Accordion,
|
||
AccordionContent,
|
||
AccordionItem,
|
||
AccordionTrigger,
|
||
} from "@/components/ui/accordion";
|
||
import { Alert, AlertDescription, AlertTitle } from "@/components/ui/alert";
|
||
import { AspectRatio } from "@/components/ui/aspect-ratio";
|
||
import { Avatar, AvatarFallback, AvatarImage } from "@/components/ui/avatar";
|
||
import { Badge } from "@/components/ui/badge";
|
||
import {
|
||
Breadcrumb,
|
||
BreadcrumbItem,
|
||
BreadcrumbLink,
|
||
BreadcrumbList,
|
||
BreadcrumbPage,
|
||
BreadcrumbSeparator,
|
||
} from "@/components/ui/breadcrumb";
|
||
import { Button } from "@/components/ui/button";
|
||
import { Calendar } from "@/components/ui/calendar";
|
||
import {
|
||
Card,
|
||
CardContent,
|
||
CardDescription,
|
||
CardFooter,
|
||
CardHeader,
|
||
CardTitle,
|
||
} from "@/components/ui/card";
|
||
import {
|
||
Carousel,
|
||
CarouselContent,
|
||
CarouselItem,
|
||
CarouselNext,
|
||
CarouselPrevious,
|
||
} from "@/components/ui/carousel";
|
||
import { Checkbox } from "@/components/ui/checkbox";
|
||
import {
|
||
Collapsible,
|
||
CollapsibleContent,
|
||
CollapsibleTrigger,
|
||
} from "@/components/ui/collapsible";
|
||
import {
|
||
Command,
|
||
CommandEmpty,
|
||
CommandGroup,
|
||
CommandInput,
|
||
CommandItem,
|
||
CommandList,
|
||
} from "@/components/ui/command";
|
||
import {
|
||
ContextMenu,
|
||
ContextMenuContent,
|
||
ContextMenuItem,
|
||
ContextMenuTrigger,
|
||
} from "@/components/ui/context-menu";
|
||
import {
|
||
Dialog,
|
||
DialogContent,
|
||
DialogDescription,
|
||
DialogHeader,
|
||
DialogTitle,
|
||
DialogTrigger,
|
||
} from "@/components/ui/dialog";
|
||
import {
|
||
Drawer,
|
||
DrawerClose,
|
||
DrawerContent,
|
||
DrawerDescription,
|
||
DrawerFooter,
|
||
DrawerHeader,
|
||
DrawerTitle,
|
||
DrawerTrigger,
|
||
} from "@/components/ui/drawer";
|
||
import {
|
||
DropdownMenu,
|
||
DropdownMenuContent,
|
||
DropdownMenuItem,
|
||
DropdownMenuLabel,
|
||
DropdownMenuSeparator,
|
||
DropdownMenuTrigger,
|
||
} from "@/components/ui/dropdown-menu";
|
||
import {
|
||
HoverCard,
|
||
HoverCardContent,
|
||
HoverCardTrigger,
|
||
} from "@/components/ui/hover-card";
|
||
import { Input } from "@/components/ui/input";
|
||
import {
|
||
InputOTP,
|
||
InputOTPGroup,
|
||
InputOTPSlot,
|
||
} from "@/components/ui/input-otp";
|
||
import { Label } from "@/components/ui/label";
|
||
import {
|
||
Menubar,
|
||
MenubarContent,
|
||
MenubarItem,
|
||
MenubarMenu,
|
||
MenubarSeparator,
|
||
MenubarTrigger,
|
||
} from "@/components/ui/menubar";
|
||
import {
|
||
Pagination,
|
||
PaginationContent,
|
||
PaginationItem,
|
||
PaginationLink,
|
||
PaginationNext,
|
||
PaginationPrevious,
|
||
} from "@/components/ui/pagination";
|
||
import {
|
||
Popover,
|
||
PopoverContent,
|
||
PopoverTrigger,
|
||
} from "@/components/ui/popover";
|
||
import { Progress } from "@/components/ui/progress";
|
||
import { RadioGroup, RadioGroupItem } from "@/components/ui/radio-group";
|
||
import {
|
||
ResizableHandle,
|
||
ResizablePanel,
|
||
ResizablePanelGroup,
|
||
} from "@/components/ui/resizable";
|
||
import { ScrollArea } from "@/components/ui/scroll-area";
|
||
import {
|
||
Select,
|
||
SelectContent,
|
||
SelectItem,
|
||
SelectTrigger,
|
||
SelectValue,
|
||
} from "@/components/ui/select";
|
||
import { Separator } from "@/components/ui/separator";
|
||
import {
|
||
Sheet,
|
||
SheetContent,
|
||
SheetDescription,
|
||
SheetHeader,
|
||
SheetTitle,
|
||
SheetTrigger,
|
||
} from "@/components/ui/sheet";
|
||
import { Skeleton } from "@/components/ui/skeleton";
|
||
import { Slider } from "@/components/ui/slider";
|
||
import { Switch } from "@/components/ui/switch";
|
||
import {
|
||
Table,
|
||
TableBody,
|
||
TableCaption,
|
||
TableCell,
|
||
TableHead,
|
||
TableHeader,
|
||
TableRow,
|
||
} from "@/components/ui/table";
|
||
import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs";
|
||
import { Textarea } from "@/components/ui/textarea";
|
||
import { Toggle } from "@/components/ui/toggle";
|
||
import { ToggleGroup, ToggleGroupItem } from "@/components/ui/toggle-group";
|
||
import {
|
||
Tooltip,
|
||
TooltipContent,
|
||
TooltipTrigger,
|
||
} from "@/components/ui/tooltip";
|
||
import { useTheme } from "@/contexts/ThemeContext";
|
||
import { format } from "date-fns";
|
||
import { zhCN } from "date-fns/locale";
|
||
import {
|
||
AlertCircle,
|
||
CalendarIcon,
|
||
Check,
|
||
Clock,
|
||
Moon,
|
||
Sun,
|
||
X,
|
||
} from "lucide-react";
|
||
import { useState } from "react";
|
||
import { toast as sonnerToast } from "sonner";
|
||
import { AIChatBox, type Message } from "@/components/AIChatBox";
|
||
|
||
export default function ComponentsShowcase() {
|
||
const { theme, toggleTheme } = useTheme();
|
||
const [date, setDate] = useState<Date | undefined>(new Date());
|
||
const [datePickerDate, setDatePickerDate] = useState<Date>();
|
||
const [selectedFruits, setSelectedFruits] = useState<string[]>([]);
|
||
const [progress, setProgress] = useState(33);
|
||
const [currentPage, setCurrentPage] = useState(2);
|
||
const [openCombobox, setOpenCombobox] = useState(false);
|
||
const [selectedFramework, setSelectedFramework] = useState("");
|
||
const [selectedMonth, setSelectedMonth] = useState("");
|
||
const [selectedYear, setSelectedYear] = useState("");
|
||
const [dialogInput, setDialogInput] = useState("");
|
||
const [dialogOpen, setDialogOpen] = useState(false);
|
||
|
||
// AI ChatBox demo state
|
||
const [chatMessages, setChatMessages] = useState<Message[]>([
|
||
{ role: "system", content: "You are a helpful assistant." },
|
||
]);
|
||
const [isChatLoading, setIsChatLoading] = useState(false);
|
||
|
||
const handleDialogSubmit = () => {
|
||
console.log("Dialog submitted with value:", dialogInput);
|
||
sonnerToast.success("Submitted successfully", {
|
||
description: `Input: ${dialogInput}`,
|
||
});
|
||
setDialogInput("");
|
||
setDialogOpen(false);
|
||
};
|
||
|
||
const handleDialogKeyDown = (e: React.KeyboardEvent<HTMLInputElement>) => {
|
||
if (e.key === "Enter" && !e.nativeEvent.isComposing) {
|
||
e.preventDefault();
|
||
handleDialogSubmit();
|
||
}
|
||
};
|
||
|
||
const handleChatSend = (content: string) => {
|
||
// Add user message
|
||
const newMessages: Message[] = [...chatMessages, { role: "user", content }];
|
||
setChatMessages(newMessages);
|
||
|
||
// Simulate AI response with delay
|
||
setIsChatLoading(true);
|
||
setTimeout(() => {
|
||
const aiResponse: Message = {
|
||
role: "assistant",
|
||
content: `This is a **demo response**. In a real app, you would call a tRPC mutation here:\n\n\`\`\`typescript\nconst chatMutation = trpc.ai.chat.useMutation({\n onSuccess: (response) => {\n setChatMessages(prev => [...prev, {\n role: "assistant",\n content: response.choices[0].message.content\n }]);\n }\n});\n\nchatMutation.mutate({ messages: newMessages });\n\`\`\`\n\nYour message was: "${content}"`,
|
||
};
|
||
setChatMessages([...newMessages, aiResponse]);
|
||
setIsChatLoading(false);
|
||
}, 1500);
|
||
};
|
||
|
||
return (
|
||
<div className="min-h-screen bg-background text-foreground">
|
||
<main className="container max-w-6xl mx-auto">
|
||
<div className="space-y-2 justify-between flex">
|
||
<h2 className="text-3xl font-bold tracking-tight mb-6">
|
||
Shadcn/ui Component Library
|
||
</h2>
|
||
<Button variant="outline" size="icon" onClick={toggleTheme}>
|
||
{theme === "light" ? (
|
||
<Moon className="h-5 w-5" />
|
||
) : (
|
||
<Sun className="h-5 w-5" />
|
||
)}
|
||
</Button>
|
||
</div>
|
||
|
||
<div className="space-y-12">
|
||
{/* Text Colors Section */}
|
||
<section className="space-y-4">
|
||
<h3 className="text-2xl font-semibold">Text Colors</h3>
|
||
<Card>
|
||
<CardContent className="pt-6">
|
||
<div className="grid grid-cols-1 md:grid-cols-2 gap-6">
|
||
<div className="space-y-3">
|
||
<div>
|
||
<p className="text-sm text-muted-foreground mb-1">
|
||
Foreground (Default)
|
||
</p>
|
||
<p className="text-foreground text-lg">
|
||
Default text color for main content
|
||
</p>
|
||
</div>
|
||
<div>
|
||
<p className="text-sm text-muted-foreground mb-1">
|
||
Muted Foreground
|
||
</p>
|
||
<p className="text-muted-foreground text-lg">
|
||
Muted text for secondary information
|
||
</p>
|
||
</div>
|
||
<div>
|
||
<p className="text-sm text-muted-foreground mb-1">
|
||
Primary
|
||
</p>
|
||
<p className="text-primary text-lg font-medium">
|
||
Primary brand color text
|
||
</p>
|
||
</div>
|
||
<div>
|
||
<p className="text-sm text-muted-foreground mb-1">
|
||
Secondary Foreground
|
||
</p>
|
||
<p className="text-secondary-foreground text-lg">
|
||
Secondary action text color
|
||
</p>
|
||
</div>
|
||
</div>
|
||
<div className="space-y-3">
|
||
<div>
|
||
<p className="text-sm text-muted-foreground mb-1">
|
||
Accent Foreground
|
||
</p>
|
||
<p className="text-accent-foreground text-lg">
|
||
Accent text for emphasis
|
||
</p>
|
||
</div>
|
||
<div>
|
||
<p className="text-sm text-muted-foreground mb-1">
|
||
Destructive
|
||
</p>
|
||
<p className="text-destructive text-lg font-medium">
|
||
Error or destructive action text
|
||
</p>
|
||
</div>
|
||
<div>
|
||
<p className="text-sm text-muted-foreground mb-1">
|
||
Card Foreground
|
||
</p>
|
||
<p className="text-card-foreground text-lg">
|
||
Text color on card backgrounds
|
||
</p>
|
||
</div>
|
||
<div>
|
||
<p className="text-sm text-muted-foreground mb-1">
|
||
Popover Foreground
|
||
</p>
|
||
<p className="text-popover-foreground text-lg">
|
||
Text color in popovers
|
||
</p>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</CardContent>
|
||
</Card>
|
||
</section>
|
||
|
||
{/* Color Combinations Section */}
|
||
<section className="space-y-4">
|
||
<h3 className="text-2xl font-semibold">Color Combinations</h3>
|
||
<Card>
|
||
<CardContent className="pt-6">
|
||
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-4">
|
||
<div className="bg-primary text-primary-foreground rounded-lg p-4">
|
||
<p className="font-medium mb-1">Primary</p>
|
||
<p className="text-sm opacity-90">
|
||
Primary background with foreground text
|
||
</p>
|
||
</div>
|
||
<div className="bg-secondary text-secondary-foreground rounded-lg p-4">
|
||
<p className="font-medium mb-1">Secondary</p>
|
||
<p className="text-sm opacity-90">
|
||
Secondary background with foreground text
|
||
</p>
|
||
</div>
|
||
<div className="bg-muted text-muted-foreground rounded-lg p-4">
|
||
<p className="font-medium mb-1">Muted</p>
|
||
<p className="text-sm opacity-90">
|
||
Muted background with foreground text
|
||
</p>
|
||
</div>
|
||
<div className="bg-accent text-accent-foreground rounded-lg p-4">
|
||
<p className="font-medium mb-1">Accent</p>
|
||
<p className="text-sm opacity-90">
|
||
Accent background with foreground text
|
||
</p>
|
||
</div>
|
||
<div className="bg-destructive text-destructive-foreground rounded-lg p-4">
|
||
<p className="font-medium mb-1">Destructive</p>
|
||
<p className="text-sm opacity-90">
|
||
Destructive background with foreground text
|
||
</p>
|
||
</div>
|
||
<div className="bg-card text-card-foreground rounded-lg p-4 border">
|
||
<p className="font-medium mb-1">Card</p>
|
||
<p className="text-sm opacity-90">
|
||
Card background with foreground text
|
||
</p>
|
||
</div>
|
||
<div className="bg-popover text-popover-foreground rounded-lg p-4 border">
|
||
<p className="font-medium mb-1">Popover</p>
|
||
<p className="text-sm opacity-90">
|
||
Popover background with foreground text
|
||
</p>
|
||
</div>
|
||
<div className="bg-background text-foreground rounded-lg p-4 border">
|
||
<p className="font-medium mb-1">Background</p>
|
||
<p className="text-sm opacity-90">
|
||
Default background with foreground text
|
||
</p>
|
||
</div>
|
||
</div>
|
||
</CardContent>
|
||
</Card>
|
||
</section>
|
||
|
||
{/* Buttons Section */}
|
||
<section className="space-y-4">
|
||
<h3 className="text-2xl font-semibold">Buttons</h3>
|
||
<Card>
|
||
<CardContent className="pt-6">
|
||
<div className="flex flex-wrap gap-4">
|
||
<Button>Default</Button>
|
||
<Button variant="secondary">Secondary</Button>
|
||
<Button variant="destructive">Destructive</Button>
|
||
<Button variant="outline">Outline</Button>
|
||
<Button variant="ghost">Ghost</Button>
|
||
<Button variant="link">Link</Button>
|
||
<Button size="sm">Small</Button>
|
||
<Button size="lg">Large</Button>
|
||
<Button size="icon">
|
||
<Check className="h-4 w-4" />
|
||
</Button>
|
||
</div>
|
||
</CardContent>
|
||
</Card>
|
||
</section>
|
||
|
||
{/* Form Inputs Section */}
|
||
<section className="space-y-4">
|
||
<h3 className="text-2xl font-semibold">Form Inputs</h3>
|
||
<Card>
|
||
<CardContent className="pt-6 space-y-6">
|
||
<div className="space-y-2">
|
||
<Label htmlFor="email">Email</Label>
|
||
<Input id="email" type="email" placeholder="Email" />
|
||
</div>
|
||
<div className="space-y-2">
|
||
<Label htmlFor="message">Message</Label>
|
||
<Textarea
|
||
id="message"
|
||
placeholder="Type your message here."
|
||
/>
|
||
</div>
|
||
<div className="space-y-2">
|
||
<Label>Select</Label>
|
||
<Select>
|
||
<SelectTrigger>
|
||
<SelectValue placeholder="Select a fruit" />
|
||
</SelectTrigger>
|
||
<SelectContent>
|
||
<SelectItem value="apple">Apple</SelectItem>
|
||
<SelectItem value="banana">Banana</SelectItem>
|
||
<SelectItem value="orange">Orange</SelectItem>
|
||
</SelectContent>
|
||
</Select>
|
||
</div>
|
||
<div className="flex items-center space-x-2">
|
||
<Checkbox id="terms" />
|
||
<Label htmlFor="terms">Accept terms and conditions</Label>
|
||
</div>
|
||
<div className="flex items-center space-x-2">
|
||
<Switch id="airplane-mode" />
|
||
<Label htmlFor="airplane-mode">Airplane Mode</Label>
|
||
</div>
|
||
<div className="space-y-2">
|
||
<Label>Radio Group</Label>
|
||
<RadioGroup defaultValue="option-one">
|
||
<div className="flex items-center space-x-2">
|
||
<RadioGroupItem value="option-one" id="option-one" />
|
||
<Label htmlFor="option-one">Option One</Label>
|
||
</div>
|
||
<div className="flex items-center space-x-2">
|
||
<RadioGroupItem value="option-two" id="option-two" />
|
||
<Label htmlFor="option-two">Option Two</Label>
|
||
</div>
|
||
</RadioGroup>
|
||
</div>
|
||
<div className="space-y-2">
|
||
<Label>Slider</Label>
|
||
<Slider defaultValue={[50]} max={100} step={1} />
|
||
</div>
|
||
<div className="space-y-2">
|
||
<Label>Input OTP</Label>
|
||
<InputOTP maxLength={6}>
|
||
<InputOTPGroup>
|
||
<InputOTPSlot index={0} />
|
||
<InputOTPSlot index={1} />
|
||
<InputOTPSlot index={2} />
|
||
<InputOTPSlot index={3} />
|
||
<InputOTPSlot index={4} />
|
||
<InputOTPSlot index={5} />
|
||
</InputOTPGroup>
|
||
</InputOTP>
|
||
</div>
|
||
<div className="space-y-2">
|
||
<Label>Date Time Picker</Label>
|
||
<Popover>
|
||
<PopoverTrigger asChild>
|
||
<Button
|
||
variant="outline"
|
||
className={`w-full justify-start text-left font-normal ${
|
||
!datePickerDate && "text-muted-foreground"
|
||
}`}
|
||
>
|
||
<CalendarIcon className="mr-2 h-4 w-4" />
|
||
{datePickerDate ? (
|
||
format(datePickerDate, "PPP HH:mm", { locale: zhCN })
|
||
) : (
|
||
<span>Select date and time</span>
|
||
)}
|
||
</Button>
|
||
</PopoverTrigger>
|
||
<PopoverContent className="w-auto p-0" align="start">
|
||
<div className="p-3 space-y-3">
|
||
<Calendar
|
||
mode="single"
|
||
selected={datePickerDate}
|
||
onSelect={setDatePickerDate}
|
||
/>
|
||
<div className="border-t pt-3 space-y-2">
|
||
<Label className="flex items-center gap-2">
|
||
<Clock className="h-4 w-4" />
|
||
Time
|
||
</Label>
|
||
<div className="flex gap-2">
|
||
<Input
|
||
type="time"
|
||
value={
|
||
datePickerDate
|
||
? format(datePickerDate, "HH:mm")
|
||
: "00:00"
|
||
}
|
||
onChange={e => {
|
||
const [hours, minutes] =
|
||
e.target.value.split(":");
|
||
const newDate = datePickerDate
|
||
? new Date(datePickerDate)
|
||
: new Date();
|
||
newDate.setHours(parseInt(hours));
|
||
newDate.setMinutes(parseInt(minutes));
|
||
setDatePickerDate(newDate);
|
||
}}
|
||
/>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</PopoverContent>
|
||
</Popover>
|
||
{datePickerDate && (
|
||
<p className="text-sm text-muted-foreground">
|
||
Selected:{" "}
|
||
{format(datePickerDate, "yyyy/MM/dd HH:mm", {
|
||
locale: zhCN,
|
||
})}
|
||
</p>
|
||
)}
|
||
</div>
|
||
<div className="space-y-2">
|
||
<Label>Searchable Dropdown</Label>
|
||
<Popover open={openCombobox} onOpenChange={setOpenCombobox}>
|
||
<PopoverTrigger asChild>
|
||
<Button
|
||
variant="outline"
|
||
role="combobox"
|
||
aria-expanded={openCombobox}
|
||
className="w-full justify-between"
|
||
>
|
||
{selectedFramework
|
||
? [
|
||
{ value: "react", label: "React" },
|
||
{ value: "vue", label: "Vue" },
|
||
{ value: "angular", label: "Angular" },
|
||
{ value: "svelte", label: "Svelte" },
|
||
{ value: "nextjs", label: "Next.js" },
|
||
{ value: "nuxt", label: "Nuxt" },
|
||
{ value: "remix", label: "Remix" },
|
||
].find(fw => fw.value === selectedFramework)?.label
|
||
: "Select framework..."}
|
||
<CalendarIcon className="ml-2 h-4 w-4 shrink-0 opacity-50" />
|
||
</Button>
|
||
</PopoverTrigger>
|
||
<PopoverContent className="w-full p-0">
|
||
<Command>
|
||
<CommandInput placeholder="Search frameworks..." />
|
||
<CommandList>
|
||
<CommandEmpty>No framework found</CommandEmpty>
|
||
<CommandGroup>
|
||
{[
|
||
{ value: "react", label: "React" },
|
||
{ value: "vue", label: "Vue" },
|
||
{ value: "angular", label: "Angular" },
|
||
{ value: "svelte", label: "Svelte" },
|
||
{ value: "nextjs", label: "Next.js" },
|
||
{ value: "nuxt", label: "Nuxt" },
|
||
{ value: "remix", label: "Remix" },
|
||
].map(framework => (
|
||
<CommandItem
|
||
key={framework.value}
|
||
value={framework.value}
|
||
onSelect={currentValue => {
|
||
setSelectedFramework(
|
||
currentValue === selectedFramework
|
||
? ""
|
||
: currentValue
|
||
);
|
||
setOpenCombobox(false);
|
||
}}
|
||
>
|
||
<Check
|
||
className={`mr-2 h-4 w-4 ${
|
||
selectedFramework === framework.value
|
||
? "opacity-100"
|
||
: "opacity-0"
|
||
}`}
|
||
/>
|
||
{framework.label}
|
||
</CommandItem>
|
||
))}
|
||
</CommandGroup>
|
||
</CommandList>
|
||
</Command>
|
||
</PopoverContent>
|
||
</Popover>
|
||
{selectedFramework && (
|
||
<p className="text-sm text-muted-foreground">
|
||
Selected:{" "}
|
||
{
|
||
[
|
||
{ value: "react", label: "React" },
|
||
{ value: "vue", label: "Vue" },
|
||
{ value: "angular", label: "Angular" },
|
||
{ value: "svelte", label: "Svelte" },
|
||
{ value: "nextjs", label: "Next.js" },
|
||
{ value: "nuxt", label: "Nuxt" },
|
||
{ value: "remix", label: "Remix" },
|
||
].find(fw => fw.value === selectedFramework)?.label
|
||
}
|
||
</p>
|
||
)}
|
||
</div>
|
||
<div className="space-y-2">
|
||
<div className="grid grid-cols-2 gap-4">
|
||
<div className="space-y-2">
|
||
<Label htmlFor="month" className="text-sm font-medium">
|
||
Month
|
||
</Label>
|
||
<Select
|
||
value={selectedMonth}
|
||
onValueChange={setSelectedMonth}
|
||
>
|
||
<SelectTrigger id="month">
|
||
<SelectValue placeholder="MM" />
|
||
</SelectTrigger>
|
||
<SelectContent>
|
||
{Array.from({ length: 12 }, (_, i) => i + 1).map(
|
||
month => (
|
||
<SelectItem
|
||
key={month}
|
||
value={month.toString().padStart(2, "0")}
|
||
>
|
||
{month.toString().padStart(2, "0")}
|
||
</SelectItem>
|
||
)
|
||
)}
|
||
</SelectContent>
|
||
</Select>
|
||
</div>
|
||
<div className="space-y-2">
|
||
<Label htmlFor="year" className="text-sm font-medium">
|
||
Year
|
||
</Label>
|
||
<Select
|
||
value={selectedYear}
|
||
onValueChange={setSelectedYear}
|
||
>
|
||
<SelectTrigger id="year">
|
||
<SelectValue placeholder="YYYY" />
|
||
</SelectTrigger>
|
||
<SelectContent>
|
||
{Array.from(
|
||
{ length: 10 },
|
||
(_, i) => new Date().getFullYear() - 5 + i
|
||
).map(year => (
|
||
<SelectItem key={year} value={year.toString()}>
|
||
{year}
|
||
</SelectItem>
|
||
))}
|
||
</SelectContent>
|
||
</Select>
|
||
</div>
|
||
</div>
|
||
{selectedMonth && selectedYear && (
|
||
<p className="text-sm text-muted-foreground">
|
||
Selected: {selectedYear}/{selectedMonth}/
|
||
</p>
|
||
)}
|
||
</div>
|
||
</CardContent>
|
||
</Card>
|
||
</section>
|
||
|
||
{/* Data Display Section */}
|
||
<section className="space-y-4">
|
||
<h3 className="text-2xl font-semibold">Data Display</h3>
|
||
<Card>
|
||
<CardContent className="pt-6 space-y-6">
|
||
<div className="space-y-2">
|
||
<Label>Badges</Label>
|
||
<div className="flex flex-wrap gap-2">
|
||
<Badge>Default</Badge>
|
||
<Badge variant="secondary">Secondary</Badge>
|
||
<Badge variant="destructive">Destructive</Badge>
|
||
<Badge variant="outline">Outline</Badge>
|
||
</div>
|
||
</div>
|
||
<Separator />
|
||
<div className="space-y-2">
|
||
<Label>Avatar</Label>
|
||
<div className="flex gap-4">
|
||
<Avatar>
|
||
<AvatarImage src="https://github.com/shadcn.png" />
|
||
<AvatarFallback>CN</AvatarFallback>
|
||
</Avatar>
|
||
<Avatar>
|
||
<AvatarFallback>AB</AvatarFallback>
|
||
</Avatar>
|
||
</div>
|
||
</div>
|
||
<Separator />
|
||
<div className="space-y-2">
|
||
<Label>Progress</Label>
|
||
<Progress value={progress} />
|
||
<div className="flex gap-2">
|
||
<Button
|
||
size="sm"
|
||
onClick={() => setProgress(Math.max(0, progress - 10))}
|
||
>
|
||
-10
|
||
</Button>
|
||
<Button
|
||
size="sm"
|
||
onClick={() => setProgress(Math.min(100, progress + 10))}
|
||
>
|
||
+10
|
||
</Button>
|
||
</div>
|
||
</div>
|
||
<Separator />
|
||
<div className="space-y-2">
|
||
<Label>Skeleton</Label>
|
||
<div className="space-y-2">
|
||
<Skeleton className="h-4 w-full" />
|
||
<Skeleton className="h-4 w-3/4" />
|
||
<Skeleton className="h-4 w-1/2" />
|
||
</div>
|
||
</div>
|
||
<Separator />
|
||
<div className="space-y-2">
|
||
<Label>Pagination</Label>
|
||
<Pagination>
|
||
<PaginationContent>
|
||
<PaginationItem>
|
||
<PaginationPrevious
|
||
href="#"
|
||
onClick={e => {
|
||
e.preventDefault();
|
||
setCurrentPage(Math.max(1, currentPage - 1));
|
||
}}
|
||
/>
|
||
</PaginationItem>
|
||
{[1, 2, 3, 4, 5].map(page => (
|
||
<PaginationItem key={page}>
|
||
<PaginationLink
|
||
href="#"
|
||
isActive={currentPage === page}
|
||
onClick={e => {
|
||
e.preventDefault();
|
||
setCurrentPage(page);
|
||
}}
|
||
>
|
||
{page}
|
||
</PaginationLink>
|
||
</PaginationItem>
|
||
))}
|
||
<PaginationItem>
|
||
<PaginationNext
|
||
href="#"
|
||
onClick={e => {
|
||
e.preventDefault();
|
||
setCurrentPage(Math.min(5, currentPage + 1));
|
||
}}
|
||
/>
|
||
</PaginationItem>
|
||
</PaginationContent>
|
||
</Pagination>
|
||
<p className="text-sm text-muted-foreground text-center">
|
||
Current page: {currentPage}
|
||
</p>
|
||
</div>
|
||
<Separator />
|
||
<div className="space-y-2">
|
||
<Label>Table</Label>
|
||
<Table>
|
||
<TableCaption>A list of your recent invoices.</TableCaption>
|
||
<TableHeader>
|
||
<TableRow>
|
||
<TableHead className="w-[100px]">Invoice</TableHead>
|
||
<TableHead>Status</TableHead>
|
||
<TableHead>Method</TableHead>
|
||
<TableHead className="text-right">Amount</TableHead>
|
||
</TableRow>
|
||
</TableHeader>
|
||
<TableBody>
|
||
<TableRow>
|
||
<TableCell className="font-medium">INV001</TableCell>
|
||
<TableCell>Paid</TableCell>
|
||
<TableCell>Credit Card</TableCell>
|
||
<TableCell className="text-right">$250.00</TableCell>
|
||
</TableRow>
|
||
<TableRow>
|
||
<TableCell className="font-medium">INV002</TableCell>
|
||
<TableCell>Pending</TableCell>
|
||
<TableCell>PayPal</TableCell>
|
||
<TableCell className="text-right">$150.00</TableCell>
|
||
</TableRow>
|
||
<TableRow>
|
||
<TableCell className="font-medium">INV003</TableCell>
|
||
<TableCell>Unpaid</TableCell>
|
||
<TableCell>Bank Transfer</TableCell>
|
||
<TableCell className="text-right">$350.00</TableCell>
|
||
</TableRow>
|
||
</TableBody>
|
||
</Table>
|
||
</div>
|
||
<Separator />
|
||
<div className="space-y-2">
|
||
<Label>Menubar</Label>
|
||
<Menubar>
|
||
<MenubarMenu>
|
||
<MenubarTrigger>File</MenubarTrigger>
|
||
<MenubarContent>
|
||
<MenubarItem>New Tab</MenubarItem>
|
||
<MenubarItem>New Window</MenubarItem>
|
||
<MenubarSeparator />
|
||
<MenubarItem>Share</MenubarItem>
|
||
<MenubarSeparator />
|
||
<MenubarItem>Print</MenubarItem>
|
||
</MenubarContent>
|
||
</MenubarMenu>
|
||
<MenubarMenu>
|
||
<MenubarTrigger>Edit</MenubarTrigger>
|
||
<MenubarContent>
|
||
<MenubarItem>Undo</MenubarItem>
|
||
<MenubarItem>Redo</MenubarItem>
|
||
</MenubarContent>
|
||
</MenubarMenu>
|
||
<MenubarMenu>
|
||
<MenubarTrigger>View</MenubarTrigger>
|
||
<MenubarContent>
|
||
<MenubarItem>Reload</MenubarItem>
|
||
<MenubarItem>Force Reload</MenubarItem>
|
||
</MenubarContent>
|
||
</MenubarMenu>
|
||
</Menubar>
|
||
</div>
|
||
<Separator />
|
||
<div className="space-y-2">
|
||
<Label>Breadcrumb</Label>
|
||
<Breadcrumb>
|
||
<BreadcrumbList>
|
||
<BreadcrumbItem>
|
||
<BreadcrumbLink href="/">Home</BreadcrumbLink>
|
||
</BreadcrumbItem>
|
||
<BreadcrumbSeparator />
|
||
<BreadcrumbItem>
|
||
<BreadcrumbLink href="/components">
|
||
Components
|
||
</BreadcrumbLink>
|
||
</BreadcrumbItem>
|
||
<BreadcrumbSeparator />
|
||
<BreadcrumbItem>
|
||
<BreadcrumbPage>Breadcrumb</BreadcrumbPage>
|
||
</BreadcrumbItem>
|
||
</BreadcrumbList>
|
||
</Breadcrumb>
|
||
</div>
|
||
</CardContent>
|
||
</Card>
|
||
</section>
|
||
|
||
{/* Alerts Section */}
|
||
<section className="space-y-4">
|
||
<h3 className="text-2xl font-semibold">Alerts</h3>
|
||
<div className="space-y-4">
|
||
<Alert>
|
||
<AlertCircle className="h-4 w-4" />
|
||
<AlertTitle>Heads up!</AlertTitle>
|
||
<AlertDescription>
|
||
You can add components to your app using the cli.
|
||
</AlertDescription>
|
||
</Alert>
|
||
<Alert variant="destructive">
|
||
<X className="h-4 w-4" />
|
||
<AlertTitle>Error</AlertTitle>
|
||
<AlertDescription>
|
||
Your session has expired. Please log in again.
|
||
</AlertDescription>
|
||
</Alert>
|
||
</div>
|
||
</section>
|
||
|
||
{/* Tabs Section */}
|
||
<section className="space-y-4">
|
||
<h3 className="text-2xl font-semibold">Tabs</h3>
|
||
<Tabs defaultValue="account" className="w-full">
|
||
<TabsList className="grid w-full grid-cols-3">
|
||
<TabsTrigger value="account">Account</TabsTrigger>
|
||
<TabsTrigger value="password">Password</TabsTrigger>
|
||
<TabsTrigger value="settings">Settings</TabsTrigger>
|
||
</TabsList>
|
||
<TabsContent value="account">
|
||
<Card>
|
||
<CardHeader>
|
||
<CardTitle>Account</CardTitle>
|
||
<CardDescription>
|
||
Make changes to your account here.
|
||
</CardDescription>
|
||
</CardHeader>
|
||
<CardContent className="space-y-2">
|
||
<div className="space-y-1">
|
||
<Label htmlFor="name">Name</Label>
|
||
<Input id="name" defaultValue="Pedro Duarte" />
|
||
</div>
|
||
</CardContent>
|
||
<CardFooter>
|
||
<Button>Save changes</Button>
|
||
</CardFooter>
|
||
</Card>
|
||
</TabsContent>
|
||
<TabsContent value="password">
|
||
<Card>
|
||
<CardHeader>
|
||
<CardTitle>Password</CardTitle>
|
||
<CardDescription>
|
||
Change your password here.
|
||
</CardDescription>
|
||
</CardHeader>
|
||
<CardContent className="space-y-2">
|
||
<div className="space-y-1">
|
||
<Label htmlFor="current">Current password</Label>
|
||
<Input id="current" type="password" />
|
||
</div>
|
||
<div className="space-y-1">
|
||
<Label htmlFor="new">New password</Label>
|
||
<Input id="new" type="password" />
|
||
</div>
|
||
</CardContent>
|
||
<CardFooter>
|
||
<Button>Save password</Button>
|
||
</CardFooter>
|
||
</Card>
|
||
</TabsContent>
|
||
<TabsContent value="settings">
|
||
<Card>
|
||
<CardHeader>
|
||
<CardTitle>Settings</CardTitle>
|
||
<CardDescription>
|
||
Manage your settings here.
|
||
</CardDescription>
|
||
</CardHeader>
|
||
<CardContent>
|
||
<p className="text-sm text-muted-foreground">
|
||
Settings content goes here.
|
||
</p>
|
||
</CardContent>
|
||
</Card>
|
||
</TabsContent>
|
||
</Tabs>
|
||
</section>
|
||
|
||
{/* Accordion Section */}
|
||
<section className="space-y-4">
|
||
<h3 className="text-2xl font-semibold">Accordion</h3>
|
||
<Accordion type="single" collapsible className="w-full">
|
||
<AccordionItem value="item-1">
|
||
<AccordionTrigger>Is it accessible?</AccordionTrigger>
|
||
<AccordionContent>
|
||
Yes. It adheres to the WAI-ARIA design pattern.
|
||
</AccordionContent>
|
||
</AccordionItem>
|
||
<AccordionItem value="item-2">
|
||
<AccordionTrigger>Is it styled?</AccordionTrigger>
|
||
<AccordionContent>
|
||
Yes. It comes with default styles that matches the other
|
||
components' aesthetic.
|
||
</AccordionContent>
|
||
</AccordionItem>
|
||
<AccordionItem value="item-3">
|
||
<AccordionTrigger>Is it animated?</AccordionTrigger>
|
||
<AccordionContent>
|
||
Yes. It's animated by default, but you can disable it if you
|
||
prefer.
|
||
</AccordionContent>
|
||
</AccordionItem>
|
||
</Accordion>
|
||
</section>
|
||
|
||
{/* Collapsible Section */}
|
||
<section className="space-y-4">
|
||
<h3 className="text-2xl font-semibold">Collapsible</h3>
|
||
<Collapsible>
|
||
<Card>
|
||
<CardHeader>
|
||
<CollapsibleTrigger asChild>
|
||
<Button variant="ghost" className="w-full justify-between">
|
||
<CardTitle>@peduarte starred 3 repositories</CardTitle>
|
||
</Button>
|
||
</CollapsibleTrigger>
|
||
</CardHeader>
|
||
<CollapsibleContent>
|
||
<CardContent>
|
||
<div className="space-y-2">
|
||
<div className="rounded-md border px-4 py-3 font-mono text-sm">
|
||
@radix-ui/primitives
|
||
</div>
|
||
<div className="rounded-md border px-4 py-3 font-mono text-sm">
|
||
@radix-ui/colors
|
||
</div>
|
||
<div className="rounded-md border px-4 py-3 font-mono text-sm">
|
||
@stitches/react
|
||
</div>
|
||
</div>
|
||
</CardContent>
|
||
</CollapsibleContent>
|
||
</Card>
|
||
</Collapsible>
|
||
</section>
|
||
|
||
{/* Dialog, Sheet, Drawer Section */}
|
||
<section className="space-y-4">
|
||
<h3 className="text-2xl font-semibold">Overlays</h3>
|
||
<Card>
|
||
<CardContent className="pt-6">
|
||
<div className="flex flex-wrap gap-4">
|
||
<Dialog open={dialogOpen} onOpenChange={setDialogOpen}>
|
||
<DialogTrigger asChild>
|
||
<Button variant="outline">Open Dialog</Button>
|
||
</DialogTrigger>
|
||
<DialogContent>
|
||
<DialogHeader>
|
||
<DialogTitle>Test Input</DialogTitle>
|
||
<DialogDescription>
|
||
Enter some text below. Press Enter to submit (IME composition supported).
|
||
</DialogDescription>
|
||
</DialogHeader>
|
||
<div className="space-y-4 py-4">
|
||
<div className="space-y-2">
|
||
<Label htmlFor="dialog-input">Input</Label>
|
||
<Input
|
||
id="dialog-input"
|
||
placeholder="Type something..."
|
||
value={dialogInput}
|
||
onChange={(e) => setDialogInput(e.target.value)}
|
||
onKeyDown={handleDialogKeyDown}
|
||
autoFocus
|
||
/>
|
||
</div>
|
||
</div>
|
||
<div className="flex justify-end gap-2">
|
||
<Button
|
||
variant="outline"
|
||
onClick={() => setDialogOpen(false)}
|
||
>
|
||
Cancel
|
||
</Button>
|
||
<Button onClick={handleDialogSubmit}>Submit</Button>
|
||
</div>
|
||
</DialogContent>
|
||
</Dialog>
|
||
|
||
<Sheet>
|
||
<SheetTrigger asChild>
|
||
<Button variant="outline">Open Sheet</Button>
|
||
</SheetTrigger>
|
||
<SheetContent>
|
||
<SheetHeader>
|
||
<SheetTitle>Edit profile</SheetTitle>
|
||
<SheetDescription>
|
||
Make changes to your profile here. Click save when
|
||
you're done.
|
||
</SheetDescription>
|
||
</SheetHeader>
|
||
</SheetContent>
|
||
</Sheet>
|
||
|
||
<Drawer>
|
||
<DrawerTrigger asChild>
|
||
<Button variant="outline">Open Drawer</Button>
|
||
</DrawerTrigger>
|
||
<DrawerContent>
|
||
<DrawerHeader>
|
||
<DrawerTitle>Are you absolutely sure?</DrawerTitle>
|
||
<DrawerDescription>
|
||
This action cannot be undone.
|
||
</DrawerDescription>
|
||
</DrawerHeader>
|
||
<DrawerFooter>
|
||
<Button>Submit</Button>
|
||
<DrawerClose asChild>
|
||
<Button variant="outline">Cancel</Button>
|
||
</DrawerClose>
|
||
</DrawerFooter>
|
||
</DrawerContent>
|
||
</Drawer>
|
||
|
||
<Popover>
|
||
<PopoverTrigger asChild>
|
||
<Button variant="outline">Open Popover</Button>
|
||
</PopoverTrigger>
|
||
<PopoverContent>
|
||
<div className="space-y-2">
|
||
<h4 className="font-medium leading-none">Dimensions</h4>
|
||
<p className="text-sm text-muted-foreground">
|
||
Set the dimensions for the layer.
|
||
</p>
|
||
</div>
|
||
</PopoverContent>
|
||
</Popover>
|
||
|
||
<Tooltip>
|
||
<TooltipTrigger asChild>
|
||
<Button variant="outline">Hover me</Button>
|
||
</TooltipTrigger>
|
||
<TooltipContent>
|
||
<p>Add to library</p>
|
||
</TooltipContent>
|
||
</Tooltip>
|
||
</div>
|
||
</CardContent>
|
||
</Card>
|
||
</section>
|
||
|
||
{/* Menus Section */}
|
||
<section className="space-y-4">
|
||
<h3 className="text-2xl font-semibold">Menus</h3>
|
||
<Card>
|
||
<CardContent className="pt-6">
|
||
<div className="flex flex-wrap gap-4">
|
||
<DropdownMenu>
|
||
<DropdownMenuTrigger asChild>
|
||
<Button variant="outline">Dropdown Menu</Button>
|
||
</DropdownMenuTrigger>
|
||
<DropdownMenuContent>
|
||
<DropdownMenuLabel>My Account</DropdownMenuLabel>
|
||
<DropdownMenuSeparator />
|
||
<DropdownMenuItem>Profile</DropdownMenuItem>
|
||
<DropdownMenuItem>Billing</DropdownMenuItem>
|
||
<DropdownMenuItem>Team</DropdownMenuItem>
|
||
<DropdownMenuItem>Subscription</DropdownMenuItem>
|
||
</DropdownMenuContent>
|
||
</DropdownMenu>
|
||
|
||
<ContextMenu>
|
||
<ContextMenuTrigger asChild>
|
||
<Button variant="outline">Right Click Me</Button>
|
||
</ContextMenuTrigger>
|
||
<ContextMenuContent>
|
||
<ContextMenuItem>Profile</ContextMenuItem>
|
||
<ContextMenuItem>Billing</ContextMenuItem>
|
||
<ContextMenuItem>Team</ContextMenuItem>
|
||
<ContextMenuItem>Subscription</ContextMenuItem>
|
||
</ContextMenuContent>
|
||
</ContextMenu>
|
||
|
||
<HoverCard>
|
||
<HoverCardTrigger asChild>
|
||
<Button variant="outline">Hover Card</Button>
|
||
</HoverCardTrigger>
|
||
<HoverCardContent>
|
||
<div className="space-y-2">
|
||
<h4 className="text-sm font-semibold">@nextjs</h4>
|
||
<p className="text-sm">
|
||
The React Framework – created and maintained by
|
||
@vercel.
|
||
</p>
|
||
</div>
|
||
</HoverCardContent>
|
||
</HoverCard>
|
||
</div>
|
||
</CardContent>
|
||
</Card>
|
||
</section>
|
||
|
||
{/* Calendar Section */}
|
||
<section className="space-y-4">
|
||
<h3 className="text-2xl font-semibold">Calendar</h3>
|
||
<Card>
|
||
<CardContent className="pt-6 flex justify-center">
|
||
<Calendar
|
||
mode="single"
|
||
selected={date}
|
||
onSelect={setDate}
|
||
className="rounded-md border"
|
||
/>
|
||
</CardContent>
|
||
</Card>
|
||
</section>
|
||
|
||
{/* Carousel Section */}
|
||
<section className="space-y-4">
|
||
<h3 className="text-2xl font-semibold">Carousel</h3>
|
||
<Card>
|
||
<CardContent className="pt-6">
|
||
<Carousel className="w-full max-w-xs mx-auto">
|
||
<CarouselContent>
|
||
{Array.from({ length: 5 }).map((_, index) => (
|
||
<CarouselItem key={index}>
|
||
<div className="p-1">
|
||
<Card>
|
||
<CardContent className="flex aspect-square items-center justify-center p-6">
|
||
<span className="text-4xl font-semibold">
|
||
{index + 1}
|
||
</span>
|
||
</CardContent>
|
||
</Card>
|
||
</div>
|
||
</CarouselItem>
|
||
))}
|
||
</CarouselContent>
|
||
<CarouselPrevious />
|
||
<CarouselNext />
|
||
</Carousel>
|
||
</CardContent>
|
||
</Card>
|
||
</section>
|
||
|
||
{/* Toggle Section */}
|
||
<section className="space-y-4">
|
||
<h3 className="text-2xl font-semibold">Toggle</h3>
|
||
<Card>
|
||
<CardContent className="pt-6 space-y-4">
|
||
<div className="space-y-2">
|
||
<Label>Toggle</Label>
|
||
<div className="flex gap-2">
|
||
<Toggle aria-label="Toggle italic">
|
||
<span className="font-bold">B</span>
|
||
</Toggle>
|
||
<Toggle aria-label="Toggle italic">
|
||
<span className="italic">I</span>
|
||
</Toggle>
|
||
<Toggle aria-label="Toggle underline">
|
||
<span className="underline">U</span>
|
||
</Toggle>
|
||
</div>
|
||
</div>
|
||
<Separator />
|
||
<div className="space-y-2">
|
||
<Label>Toggle Group</Label>
|
||
<ToggleGroup type="multiple">
|
||
<ToggleGroupItem value="bold" aria-label="Toggle bold">
|
||
<span className="font-bold">B</span>
|
||
</ToggleGroupItem>
|
||
<ToggleGroupItem value="italic" aria-label="Toggle italic">
|
||
<span className="italic">I</span>
|
||
</ToggleGroupItem>
|
||
<ToggleGroupItem
|
||
value="underline"
|
||
aria-label="Toggle underline"
|
||
>
|
||
<span className="underline">U</span>
|
||
</ToggleGroupItem>
|
||
</ToggleGroup>
|
||
</div>
|
||
</CardContent>
|
||
</Card>
|
||
</section>
|
||
|
||
{/* Aspect Ratio & Scroll Area Section */}
|
||
<section className="space-y-4">
|
||
<h3 className="text-2xl font-semibold">Layout Components</h3>
|
||
<Card>
|
||
<CardContent className="pt-6 space-y-6">
|
||
<div className="space-y-2">
|
||
<Label>Aspect Ratio (16/9)</Label>
|
||
<AspectRatio ratio={16 / 9} className="bg-muted">
|
||
<div className="flex h-full items-center justify-center">
|
||
<p className="text-muted-foreground">16:9 Aspect Ratio</p>
|
||
</div>
|
||
</AspectRatio>
|
||
</div>
|
||
<Separator />
|
||
<div className="space-y-2">
|
||
<Label>Scroll Area</Label>
|
||
<ScrollArea className="h-[200px] w-full rounded-md border overflow-hidden">
|
||
<div className="p-4">
|
||
<div className="space-y-4">
|
||
{Array.from({ length: 20 }).map((_, i) => (
|
||
<div key={i} className="text-sm">
|
||
Item {i + 1}: This is a scrollable content area
|
||
</div>
|
||
))}
|
||
</div>
|
||
</div>
|
||
</ScrollArea>
|
||
</div>
|
||
</CardContent>
|
||
</Card>
|
||
</section>
|
||
|
||
{/* Resizable Section */}
|
||
<section className="space-y-4">
|
||
<h3 className="text-2xl font-semibold">Resizable Panels</h3>
|
||
<Card>
|
||
<CardContent className="pt-6">
|
||
<ResizablePanelGroup
|
||
direction="horizontal"
|
||
className="min-h-[200px] rounded-lg border"
|
||
>
|
||
<ResizablePanel defaultSize={50}>
|
||
<div className="flex h-full items-center justify-center p-6">
|
||
<span className="font-semibold">Panel One</span>
|
||
</div>
|
||
</ResizablePanel>
|
||
<ResizableHandle />
|
||
<ResizablePanel defaultSize={50}>
|
||
<div className="flex h-full items-center justify-center p-6">
|
||
<span className="font-semibold">Panel Two</span>
|
||
</div>
|
||
</ResizablePanel>
|
||
</ResizablePanelGroup>
|
||
</CardContent>
|
||
</Card>
|
||
</section>
|
||
|
||
{/* Toast Section */}
|
||
<section className="space-y-4">
|
||
<h3 className="text-2xl font-semibold">Toast</h3>
|
||
<Card>
|
||
<CardContent className="pt-6 space-y-4">
|
||
<div className="space-y-2">
|
||
<Label>Sonner Toast</Label>
|
||
<div className="flex flex-wrap gap-2">
|
||
<Button
|
||
variant="outline"
|
||
onClick={() => {
|
||
sonnerToast.success("Operation successful", {
|
||
description: "Your changes have been saved",
|
||
});
|
||
}}
|
||
>
|
||
Success
|
||
</Button>
|
||
<Button
|
||
variant="outline"
|
||
onClick={() => {
|
||
sonnerToast.error("Operation failed", {
|
||
description:
|
||
"Cannot complete operation, please try again",
|
||
});
|
||
}}
|
||
>
|
||
Error
|
||
</Button>
|
||
<Button
|
||
variant="outline"
|
||
onClick={() => {
|
||
sonnerToast.info("Information", {
|
||
description: "This is an information message",
|
||
});
|
||
}}
|
||
>
|
||
Info
|
||
</Button>
|
||
<Button
|
||
variant="outline"
|
||
onClick={() => {
|
||
sonnerToast.warning("Warning", {
|
||
description:
|
||
"Please note the impact of this operation",
|
||
});
|
||
}}
|
||
>
|
||
Warning
|
||
</Button>
|
||
<Button
|
||
variant="outline"
|
||
onClick={() => {
|
||
sonnerToast.loading("Loading", {
|
||
description: "Please wait",
|
||
});
|
||
}}
|
||
>
|
||
Loading
|
||
</Button>
|
||
<Button
|
||
variant="outline"
|
||
onClick={() => {
|
||
const promise = new Promise(resolve =>
|
||
setTimeout(resolve, 2000)
|
||
);
|
||
sonnerToast.promise(promise, {
|
||
loading: "Processing...",
|
||
success: "Processing complete!",
|
||
error: "Processing failed",
|
||
});
|
||
}}
|
||
>
|
||
Promise
|
||
</Button>
|
||
</div>
|
||
</div>
|
||
</CardContent>
|
||
</Card>
|
||
</section>
|
||
|
||
{/* AI ChatBox Section */}
|
||
<section className="space-y-4">
|
||
<h3 className="text-2xl font-semibold">AI ChatBox</h3>
|
||
<Card>
|
||
<CardContent className="pt-6">
|
||
<div className="space-y-4">
|
||
<div className="text-sm text-muted-foreground">
|
||
<p>
|
||
A ready-to-use chat interface component that integrates with the LLM system.
|
||
Features markdown rendering, auto-scrolling, and loading states.
|
||
</p>
|
||
<p className="mt-2">
|
||
This is a demo with simulated responses. In a real app, you'd connect it to a tRPC mutation.
|
||
</p>
|
||
</div>
|
||
<AIChatBox
|
||
messages={chatMessages}
|
||
onSendMessage={handleChatSend}
|
||
isLoading={isChatLoading}
|
||
placeholder="Try sending a message..."
|
||
height="500px"
|
||
emptyStateMessage="How can I help you today?"
|
||
suggestedPrompts={[
|
||
"What is React?",
|
||
"Explain TypeScript",
|
||
"How to use tRPC?",
|
||
"Best practices for web development",
|
||
]}
|
||
/>
|
||
</div>
|
||
</CardContent>
|
||
</Card>
|
||
</section>
|
||
</div>
|
||
</main>
|
||
|
||
<footer className="border-t py-6 mt-12">
|
||
<div className="container text-center text-sm text-muted-foreground">
|
||
<p>Shadcn/ui Component Showcase</p>
|
||
</div>
|
||
</footer>
|
||
</div>
|
||
);
|
||
}
|