| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437 |
- 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>
- );
- }
|