feat: add new UI components and update dependencies
- Introduced new components: Badge, Card, Command, Dialog, Label, and Popover for enhanced UI functionality. - Updated package.json and pnpm-lock.yaml to include new dependencies: @radix-ui/react-label, @radix-ui/react-popover, and cmdk. - Improved overall UI consistency and usability with the addition of these components.
This commit is contained in:
		
							
								
								
									
										46
									
								
								src/components/ui/badge.tsx
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										46
									
								
								src/components/ui/badge.tsx
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,46 @@ | ||||
| import * as React from "react" | ||||
| import { Slot } from "@radix-ui/react-slot" | ||||
| import { cva, type VariantProps } from "class-variance-authority" | ||||
|  | ||||
| import { cn } from "@/lib/utils" | ||||
|  | ||||
| const badgeVariants = cva( | ||||
|   "inline-flex items-center justify-center rounded-full border px-2 py-0.5 text-xs font-medium w-fit whitespace-nowrap shrink-0 [&>svg]:size-3 gap-1 [&>svg]:pointer-events-none focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring-[3px] aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive transition-[color,box-shadow] overflow-hidden", | ||||
|   { | ||||
|     variants: { | ||||
|       variant: { | ||||
|         default: | ||||
|           "border-transparent bg-primary text-primary-foreground [a&]:hover:bg-primary/90", | ||||
|         secondary: | ||||
|           "border-transparent bg-secondary text-secondary-foreground [a&]:hover:bg-secondary/90", | ||||
|         destructive: | ||||
|           "border-transparent bg-destructive text-white [a&]:hover:bg-destructive/90 focus-visible:ring-destructive/20 dark:focus-visible:ring-destructive/40 dark:bg-destructive/60", | ||||
|         outline: | ||||
|           "text-foreground [a&]:hover:bg-accent [a&]:hover:text-accent-foreground", | ||||
|       }, | ||||
|     }, | ||||
|     defaultVariants: { | ||||
|       variant: "default", | ||||
|     }, | ||||
|   } | ||||
| ) | ||||
|  | ||||
| function Badge({ | ||||
|   className, | ||||
|   variant, | ||||
|   asChild = false, | ||||
|   ...props | ||||
| }: React.ComponentProps<"span"> & | ||||
|   VariantProps<typeof badgeVariants> & { asChild?: boolean }) { | ||||
|   const Comp = asChild ? Slot : "span" | ||||
|  | ||||
|   return ( | ||||
|     <Comp | ||||
|       data-slot="badge" | ||||
|       className={cn(badgeVariants({ variant }), className)} | ||||
|       {...props} | ||||
|     /> | ||||
|   ) | ||||
| } | ||||
|  | ||||
| export { Badge, badgeVariants } | ||||
							
								
								
									
										92
									
								
								src/components/ui/card.tsx
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										92
									
								
								src/components/ui/card.tsx
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,92 @@ | ||||
| import * as React from "react" | ||||
|  | ||||
| import { cn } from "@/lib/utils" | ||||
|  | ||||
| function Card({ className, ...props }: React.ComponentProps<"div">) { | ||||
|   return ( | ||||
|     <div | ||||
|       data-slot="card" | ||||
|       className={cn( | ||||
|         "bg-card text-card-foreground flex flex-col gap-6 rounded-xl border py-6 shadow-sm", | ||||
|         className | ||||
|       )} | ||||
|       {...props} | ||||
|     /> | ||||
|   ) | ||||
| } | ||||
|  | ||||
| function CardHeader({ className, ...props }: React.ComponentProps<"div">) { | ||||
|   return ( | ||||
|     <div | ||||
|       data-slot="card-header" | ||||
|       className={cn( | ||||
|         "@container/card-header grid auto-rows-min grid-rows-[auto_auto] items-start gap-2 px-6 has-data-[slot=card-action]:grid-cols-[1fr_auto] [.border-b]:pb-6", | ||||
|         className | ||||
|       )} | ||||
|       {...props} | ||||
|     /> | ||||
|   ) | ||||
| } | ||||
|  | ||||
| function CardTitle({ className, ...props }: React.ComponentProps<"div">) { | ||||
|   return ( | ||||
|     <div | ||||
|       data-slot="card-title" | ||||
|       className={cn("leading-none font-semibold", className)} | ||||
|       {...props} | ||||
|     /> | ||||
|   ) | ||||
| } | ||||
|  | ||||
| function CardDescription({ className, ...props }: React.ComponentProps<"div">) { | ||||
|   return ( | ||||
|     <div | ||||
|       data-slot="card-description" | ||||
|       className={cn("text-muted-foreground text-sm", className)} | ||||
|       {...props} | ||||
|     /> | ||||
|   ) | ||||
| } | ||||
|  | ||||
| function CardAction({ className, ...props }: React.ComponentProps<"div">) { | ||||
|   return ( | ||||
|     <div | ||||
|       data-slot="card-action" | ||||
|       className={cn( | ||||
|         "col-start-2 row-span-2 row-start-1 self-start justify-self-end", | ||||
|         className | ||||
|       )} | ||||
|       {...props} | ||||
|     /> | ||||
|   ) | ||||
| } | ||||
|  | ||||
| function CardContent({ className, ...props }: React.ComponentProps<"div">) { | ||||
|   return ( | ||||
|     <div | ||||
|       data-slot="card-content" | ||||
|       className={cn("px-6", className)} | ||||
|       {...props} | ||||
|     /> | ||||
|   ) | ||||
| } | ||||
|  | ||||
| function CardFooter({ className, ...props }: React.ComponentProps<"div">) { | ||||
|   return ( | ||||
|     <div | ||||
|       data-slot="card-footer" | ||||
|       className={cn("flex items-center px-6 [.border-t]:pt-6", className)} | ||||
|       {...props} | ||||
|     /> | ||||
|   ) | ||||
| } | ||||
|  | ||||
| export { | ||||
|   Card, | ||||
|   CardHeader, | ||||
|   CardFooter, | ||||
|   CardTitle, | ||||
|   CardAction, | ||||
|   CardDescription, | ||||
|   CardContent, | ||||
| } | ||||
							
								
								
									
										184
									
								
								src/components/ui/command.tsx
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										184
									
								
								src/components/ui/command.tsx
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,184 @@ | ||||
| "use client" | ||||
|  | ||||
| import * as React from "react" | ||||
| import { Command as CommandPrimitive } from "cmdk" | ||||
| import { SearchIcon } from "lucide-react" | ||||
|  | ||||
| import { cn } from "@/lib/utils" | ||||
| import { | ||||
|   Dialog, | ||||
|   DialogContent, | ||||
|   DialogDescription, | ||||
|   DialogHeader, | ||||
|   DialogTitle, | ||||
| } from "@/components/ui/dialog" | ||||
|  | ||||
| function Command({ | ||||
|   className, | ||||
|   ...props | ||||
| }: React.ComponentProps<typeof CommandPrimitive>) { | ||||
|   return ( | ||||
|     <CommandPrimitive | ||||
|       data-slot="command" | ||||
|       className={cn( | ||||
|         "bg-popover text-popover-foreground flex h-full w-full flex-col overflow-hidden rounded-md", | ||||
|         className | ||||
|       )} | ||||
|       {...props} | ||||
|     /> | ||||
|   ) | ||||
| } | ||||
|  | ||||
| function CommandDialog({ | ||||
|   title = "Command Palette", | ||||
|   description = "Search for a command to run...", | ||||
|   children, | ||||
|   className, | ||||
|   showCloseButton = true, | ||||
|   ...props | ||||
| }: React.ComponentProps<typeof Dialog> & { | ||||
|   title?: string | ||||
|   description?: string | ||||
|   className?: string | ||||
|   showCloseButton?: boolean | ||||
| }) { | ||||
|   return ( | ||||
|     <Dialog {...props}> | ||||
|       <DialogHeader className="sr-only"> | ||||
|         <DialogTitle>{title}</DialogTitle> | ||||
|         <DialogDescription>{description}</DialogDescription> | ||||
|       </DialogHeader> | ||||
|       <DialogContent | ||||
|         className={cn("overflow-hidden p-0", className)} | ||||
|         showCloseButton={showCloseButton} | ||||
|       > | ||||
|         <Command className="[&_[cmdk-group-heading]]:text-muted-foreground **:data-[slot=command-input-wrapper]:h-12 [&_[cmdk-group-heading]]:px-2 [&_[cmdk-group-heading]]:font-medium [&_[cmdk-group]]:px-2 [&_[cmdk-group]:not([hidden])_~[cmdk-group]]:pt-0 [&_[cmdk-input-wrapper]_svg]:h-5 [&_[cmdk-input-wrapper]_svg]:w-5 [&_[cmdk-input]]:h-12 [&_[cmdk-item]]:px-2 [&_[cmdk-item]]:py-3 [&_[cmdk-item]_svg]:h-5 [&_[cmdk-item]_svg]:w-5"> | ||||
|           {children} | ||||
|         </Command> | ||||
|       </DialogContent> | ||||
|     </Dialog> | ||||
|   ) | ||||
| } | ||||
|  | ||||
| function CommandInput({ | ||||
|   className, | ||||
|   ...props | ||||
| }: React.ComponentProps<typeof CommandPrimitive.Input>) { | ||||
|   return ( | ||||
|     <div | ||||
|       data-slot="command-input-wrapper" | ||||
|       className="flex h-9 items-center gap-2 border-b px-3" | ||||
|     > | ||||
|       <SearchIcon className="size-4 shrink-0 opacity-50" /> | ||||
|       <CommandPrimitive.Input | ||||
|         data-slot="command-input" | ||||
|         className={cn( | ||||
|           "placeholder:text-muted-foreground flex h-10 w-full rounded-md bg-transparent py-3 text-sm outline-hidden disabled:cursor-not-allowed disabled:opacity-50", | ||||
|           className | ||||
|         )} | ||||
|         {...props} | ||||
|       /> | ||||
|     </div> | ||||
|   ) | ||||
| } | ||||
|  | ||||
| function CommandList({ | ||||
|   className, | ||||
|   ...props | ||||
| }: React.ComponentProps<typeof CommandPrimitive.List>) { | ||||
|   return ( | ||||
|     <CommandPrimitive.List | ||||
|       data-slot="command-list" | ||||
|       className={cn( | ||||
|         "max-h-[300px] scroll-py-1 overflow-x-hidden overflow-y-auto", | ||||
|         className | ||||
|       )} | ||||
|       {...props} | ||||
|     /> | ||||
|   ) | ||||
| } | ||||
|  | ||||
| function CommandEmpty({ | ||||
|   ...props | ||||
| }: React.ComponentProps<typeof CommandPrimitive.Empty>) { | ||||
|   return ( | ||||
|     <CommandPrimitive.Empty | ||||
|       data-slot="command-empty" | ||||
|       className="py-6 text-center text-sm" | ||||
|       {...props} | ||||
|     /> | ||||
|   ) | ||||
| } | ||||
|  | ||||
| function CommandGroup({ | ||||
|   className, | ||||
|   ...props | ||||
| }: React.ComponentProps<typeof CommandPrimitive.Group>) { | ||||
|   return ( | ||||
|     <CommandPrimitive.Group | ||||
|       data-slot="command-group" | ||||
|       className={cn( | ||||
|         "text-foreground [&_[cmdk-group-heading]]:text-muted-foreground overflow-hidden p-1 [&_[cmdk-group-heading]]:px-2 [&_[cmdk-group-heading]]:py-1.5 [&_[cmdk-group-heading]]:text-xs [&_[cmdk-group-heading]]:font-medium", | ||||
|         className | ||||
|       )} | ||||
|       {...props} | ||||
|     /> | ||||
|   ) | ||||
| } | ||||
|  | ||||
| function CommandSeparator({ | ||||
|   className, | ||||
|   ...props | ||||
| }: React.ComponentProps<typeof CommandPrimitive.Separator>) { | ||||
|   return ( | ||||
|     <CommandPrimitive.Separator | ||||
|       data-slot="command-separator" | ||||
|       className={cn("bg-border -mx-1 h-px", className)} | ||||
|       {...props} | ||||
|     /> | ||||
|   ) | ||||
| } | ||||
|  | ||||
| function CommandItem({ | ||||
|   className, | ||||
|   ...props | ||||
| }: React.ComponentProps<typeof CommandPrimitive.Item>) { | ||||
|   return ( | ||||
|     <CommandPrimitive.Item | ||||
|       data-slot="command-item" | ||||
|       className={cn( | ||||
|         "data-[selected=true]:bg-accent data-[selected=true]:text-accent-foreground [&_svg:not([class*='text-'])]:text-muted-foreground relative flex cursor-default items-center gap-2 rounded-sm px-2 py-1.5 text-sm outline-hidden select-none data-[disabled=true]:pointer-events-none data-[disabled=true]:opacity-50 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4", | ||||
|         className | ||||
|       )} | ||||
|       {...props} | ||||
|     /> | ||||
|   ) | ||||
| } | ||||
|  | ||||
| function CommandShortcut({ | ||||
|   className, | ||||
|   ...props | ||||
| }: React.ComponentProps<"span">) { | ||||
|   return ( | ||||
|     <span | ||||
|       data-slot="command-shortcut" | ||||
|       className={cn( | ||||
|         "text-muted-foreground ml-auto text-xs tracking-widest", | ||||
|         className | ||||
|       )} | ||||
|       {...props} | ||||
|     /> | ||||
|   ) | ||||
| } | ||||
|  | ||||
| export { | ||||
|   Command, | ||||
|   CommandDialog, | ||||
|   CommandInput, | ||||
|   CommandList, | ||||
|   CommandEmpty, | ||||
|   CommandGroup, | ||||
|   CommandItem, | ||||
|   CommandShortcut, | ||||
|   CommandSeparator, | ||||
| } | ||||
							
								
								
									
										141
									
								
								src/components/ui/dialog.tsx
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										141
									
								
								src/components/ui/dialog.tsx
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,141 @@ | ||||
| import * as React from "react" | ||||
| import * as DialogPrimitive from "@radix-ui/react-dialog" | ||||
| import { XIcon } from "lucide-react" | ||||
|  | ||||
| import { cn } from "@/lib/utils" | ||||
|  | ||||
| function Dialog({ | ||||
|   ...props | ||||
| }: React.ComponentProps<typeof DialogPrimitive.Root>) { | ||||
|   return <DialogPrimitive.Root data-slot="dialog" {...props} /> | ||||
| } | ||||
|  | ||||
| function DialogTrigger({ | ||||
|   ...props | ||||
| }: React.ComponentProps<typeof DialogPrimitive.Trigger>) { | ||||
|   return <DialogPrimitive.Trigger data-slot="dialog-trigger" {...props} /> | ||||
| } | ||||
|  | ||||
| function DialogPortal({ | ||||
|   ...props | ||||
| }: React.ComponentProps<typeof DialogPrimitive.Portal>) { | ||||
|   return <DialogPrimitive.Portal data-slot="dialog-portal" {...props} /> | ||||
| } | ||||
|  | ||||
| function DialogClose({ | ||||
|   ...props | ||||
| }: React.ComponentProps<typeof DialogPrimitive.Close>) { | ||||
|   return <DialogPrimitive.Close data-slot="dialog-close" {...props} /> | ||||
| } | ||||
|  | ||||
| function DialogOverlay({ | ||||
|   className, | ||||
|   ...props | ||||
| }: React.ComponentProps<typeof DialogPrimitive.Overlay>) { | ||||
|   return ( | ||||
|     <DialogPrimitive.Overlay | ||||
|       data-slot="dialog-overlay" | ||||
|       className={cn( | ||||
|         "data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 fixed inset-0 z-50 bg-black/50", | ||||
|         className | ||||
|       )} | ||||
|       {...props} | ||||
|     /> | ||||
|   ) | ||||
| } | ||||
|  | ||||
| function DialogContent({ | ||||
|   className, | ||||
|   children, | ||||
|   showCloseButton = true, | ||||
|   ...props | ||||
| }: React.ComponentProps<typeof DialogPrimitive.Content> & { | ||||
|   showCloseButton?: boolean | ||||
| }) { | ||||
|   return ( | ||||
|     <DialogPortal data-slot="dialog-portal"> | ||||
|       <DialogOverlay /> | ||||
|       <DialogPrimitive.Content | ||||
|         data-slot="dialog-content" | ||||
|         className={cn( | ||||
|           "bg-background data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 fixed top-[50%] left-[50%] z-50 grid w-full max-w-[calc(100%-2rem)] translate-x-[-50%] translate-y-[-50%] gap-4 rounded-lg border p-6 shadow-lg duration-200 sm:max-w-lg", | ||||
|           className | ||||
|         )} | ||||
|         {...props} | ||||
|       > | ||||
|         {children} | ||||
|         {showCloseButton && ( | ||||
|           <DialogPrimitive.Close | ||||
|             data-slot="dialog-close" | ||||
|             className="ring-offset-background focus:ring-ring data-[state=open]:bg-accent data-[state=open]:text-muted-foreground absolute top-4 right-4 rounded-xs opacity-70 transition-opacity hover:opacity-100 focus:ring-2 focus:ring-offset-2 focus:outline-hidden disabled:pointer-events-none [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4" | ||||
|           > | ||||
|             <XIcon /> | ||||
|             <span className="sr-only">Close</span> | ||||
|           </DialogPrimitive.Close> | ||||
|         )} | ||||
|       </DialogPrimitive.Content> | ||||
|     </DialogPortal> | ||||
|   ) | ||||
| } | ||||
|  | ||||
| function DialogHeader({ className, ...props }: React.ComponentProps<"div">) { | ||||
|   return ( | ||||
|     <div | ||||
|       data-slot="dialog-header" | ||||
|       className={cn("flex flex-col gap-2 text-center sm:text-left", className)} | ||||
|       {...props} | ||||
|     /> | ||||
|   ) | ||||
| } | ||||
|  | ||||
| function DialogFooter({ className, ...props }: React.ComponentProps<"div">) { | ||||
|   return ( | ||||
|     <div | ||||
|       data-slot="dialog-footer" | ||||
|       className={cn( | ||||
|         "flex flex-col-reverse gap-2 sm:flex-row sm:justify-end", | ||||
|         className | ||||
|       )} | ||||
|       {...props} | ||||
|     /> | ||||
|   ) | ||||
| } | ||||
|  | ||||
| function DialogTitle({ | ||||
|   className, | ||||
|   ...props | ||||
| }: React.ComponentProps<typeof DialogPrimitive.Title>) { | ||||
|   return ( | ||||
|     <DialogPrimitive.Title | ||||
|       data-slot="dialog-title" | ||||
|       className={cn("text-lg leading-none font-semibold", className)} | ||||
|       {...props} | ||||
|     /> | ||||
|   ) | ||||
| } | ||||
|  | ||||
| function DialogDescription({ | ||||
|   className, | ||||
|   ...props | ||||
| }: React.ComponentProps<typeof DialogPrimitive.Description>) { | ||||
|   return ( | ||||
|     <DialogPrimitive.Description | ||||
|       data-slot="dialog-description" | ||||
|       className={cn("text-muted-foreground text-sm", className)} | ||||
|       {...props} | ||||
|     /> | ||||
|   ) | ||||
| } | ||||
|  | ||||
| export { | ||||
|   Dialog, | ||||
|   DialogClose, | ||||
|   DialogContent, | ||||
|   DialogDescription, | ||||
|   DialogFooter, | ||||
|   DialogHeader, | ||||
|   DialogOverlay, | ||||
|   DialogPortal, | ||||
|   DialogTitle, | ||||
|   DialogTrigger, | ||||
| } | ||||
							
								
								
									
										24
									
								
								src/components/ui/label.tsx
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										24
									
								
								src/components/ui/label.tsx
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,24 @@ | ||||
| "use client" | ||||
|  | ||||
| import * as React from "react" | ||||
| import * as LabelPrimitive from "@radix-ui/react-label" | ||||
|  | ||||
| import { cn } from "@/lib/utils" | ||||
|  | ||||
| function Label({ | ||||
|   className, | ||||
|   ...props | ||||
| }: React.ComponentProps<typeof LabelPrimitive.Root>) { | ||||
|   return ( | ||||
|     <LabelPrimitive.Root | ||||
|       data-slot="label" | ||||
|       className={cn( | ||||
|         "flex items-center gap-2 text-sm leading-none font-medium select-none group-data-[disabled=true]:pointer-events-none group-data-[disabled=true]:opacity-50 peer-disabled:cursor-not-allowed peer-disabled:opacity-50", | ||||
|         className | ||||
|       )} | ||||
|       {...props} | ||||
|     /> | ||||
|   ) | ||||
| } | ||||
|  | ||||
| export { Label } | ||||
							
								
								
									
										46
									
								
								src/components/ui/popover.tsx
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										46
									
								
								src/components/ui/popover.tsx
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,46 @@ | ||||
| import * as React from "react" | ||||
| import * as PopoverPrimitive from "@radix-ui/react-popover" | ||||
|  | ||||
| import { cn } from "@/lib/utils" | ||||
|  | ||||
| function Popover({ | ||||
|   ...props | ||||
| }: React.ComponentProps<typeof PopoverPrimitive.Root>) { | ||||
|   return <PopoverPrimitive.Root data-slot="popover" {...props} /> | ||||
| } | ||||
|  | ||||
| function PopoverTrigger({ | ||||
|   ...props | ||||
| }: React.ComponentProps<typeof PopoverPrimitive.Trigger>) { | ||||
|   return <PopoverPrimitive.Trigger data-slot="popover-trigger" {...props} /> | ||||
| } | ||||
|  | ||||
| function PopoverContent({ | ||||
|   className, | ||||
|   align = "center", | ||||
|   sideOffset = 4, | ||||
|   ...props | ||||
| }: React.ComponentProps<typeof PopoverPrimitive.Content>) { | ||||
|   return ( | ||||
|     <PopoverPrimitive.Portal> | ||||
|       <PopoverPrimitive.Content | ||||
|         data-slot="popover-content" | ||||
|         align={align} | ||||
|         sideOffset={sideOffset} | ||||
|         className={cn( | ||||
|           "bg-popover text-popover-foreground data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 z-50 w-72 origin-(--radix-popover-content-transform-origin) rounded-md border p-4 shadow-md outline-hidden", | ||||
|           className | ||||
|         )} | ||||
|         {...props} | ||||
|       /> | ||||
|     </PopoverPrimitive.Portal> | ||||
|   ) | ||||
| } | ||||
|  | ||||
| function PopoverAnchor({ | ||||
|   ...props | ||||
| }: React.ComponentProps<typeof PopoverPrimitive.Anchor>) { | ||||
|   return <PopoverPrimitive.Anchor data-slot="popover-anchor" {...props} /> | ||||
| } | ||||
|  | ||||
| export { Popover, PopoverTrigger, PopoverContent, PopoverAnchor } | ||||
		Reference in New Issue
	
	Block a user
	 typist
					typist