diff --git a/app/src/components/ui-base/button.tsx b/app/src/components/ui-base/button.tsx new file mode 100644 index 0000000..e31cafd --- /dev/null +++ b/app/src/components/ui-base/button.tsx @@ -0,0 +1,43 @@ +'use client'; + +import * as React from 'react'; +import { Button as BaseButton } from '@base-ui/react/button'; +import { cva, type VariantProps } from 'class-variance-authority'; +import { cn } from '@/lib/utils'; + +const buttonVariants = cva( + 'inline-flex items-center justify-center gap-2 whitespace-nowrap text-sm font-medium transition-colors select-none focus-visible:outline focus-visible:outline-2 focus-visible:outline-blue-800 focus-visible:-outline-offset-1 data-[disabled]:pointer-events-none data-[disabled]:opacity-50 cursor-pointer', + { + variants: { + variant: { + default: 'bg-primary text-primary-foreground shadow hover:bg-primary/90 active:bg-primary/80', + destructive: 'bg-destructive text-white shadow-sm hover:bg-destructive/90 active:bg-destructive/80', + outline: + 'border border-input bg-background shadow-xs hover:bg-accent hover:text-accent-foreground active:bg-accent/80', + secondary: + 'bg-secondary text-secondary-foreground shadow-sm hover:bg-secondary/80 active:bg-secondary/70', + ghost: 'hover:bg-accent hover:text-accent-foreground active:bg-accent/80', + link: 'text-primary underline-offset-4 hover:underline', + }, + size: { + default: 'h-9 px-4 py-2 rounded-md', + sm: 'h-8 rounded-md px-3 text-xs', + lg: 'h-10 rounded-md px-8', + icon: 'h-9 w-9 rounded-md', + }, + }, + defaultVariants: { + variant: 'default', + size: 'default', + }, + }, +); + +interface ButtonProps extends React.ButtonHTMLAttributes, VariantProps {} + +const Button = React.forwardRef(({ className, variant, size, ...props }, ref) => { + return ; +}); +Button.displayName = 'Button'; + +export { Button, buttonVariants }; diff --git a/app/src/components/ui-base/collapsible.tsx b/app/src/components/ui-base/collapsible.tsx new file mode 100644 index 0000000..f5b4fea --- /dev/null +++ b/app/src/components/ui-base/collapsible.tsx @@ -0,0 +1,39 @@ +'use client'; + +import * as React from 'react'; +import { Collapsible as BaseCollapsible } from '@base-ui/react/collapsible'; +import { cn } from '@/lib/utils'; + +const Collapsible = BaseCollapsible.Root; + +const CollapsibleTrigger = React.forwardRef< + HTMLButtonElement, + React.ComponentPropsWithoutRef +>(({ className, ...props }, ref) => ( + svg]:rotate-180', + className, + )} + {...props} + /> +)); +CollapsibleTrigger.displayName = 'CollapsibleTrigger'; + +const CollapsibleContent = React.forwardRef< + HTMLDivElement, + React.ComponentPropsWithoutRef +>(({ className, ...props }, ref) => ( + +)); +CollapsibleContent.displayName = 'CollapsibleContent'; + +export { Collapsible, CollapsibleTrigger, CollapsibleContent }; diff --git a/app/src/components/ui-base/dialog.tsx b/app/src/components/ui-base/dialog.tsx new file mode 100644 index 0000000..ec7ded7 --- /dev/null +++ b/app/src/components/ui-base/dialog.tsx @@ -0,0 +1,99 @@ +'use client'; + +import * as React from 'react'; +import { Dialog as BaseDialog } from '@base-ui/react/dialog'; +import { cn } from '@/lib/utils'; +import { X } from 'lucide-react'; + +const Dialog = BaseDialog.Root; + +const DialogTrigger = BaseDialog.Trigger; + +const DialogPortal = BaseDialog.Portal; + +const DialogClose = BaseDialog.Close; + +const DialogBackdrop = React.forwardRef>( + ({ className, ...props }, ref) => ( + + ), +); +DialogBackdrop.displayName = 'DialogBackdrop'; + +const DialogContent = React.forwardRef< + HTMLDivElement, + React.ComponentPropsWithoutRef & { + showCloseButton?: boolean; + } +>(({ className, children, showCloseButton = true, ...props }, ref) => ( + + + + {children} + {showCloseButton && ( + + + Close + + )} + + +)); +DialogContent.displayName = 'DialogContent'; + +const DialogHeader = ({ className, ...props }: React.HTMLAttributes) => ( +
+); +DialogHeader.displayName = 'DialogHeader'; + +const DialogFooter = ({ className, ...props }: React.HTMLAttributes) => ( +
+); +DialogFooter.displayName = 'DialogFooter'; + +const DialogTitle = React.forwardRef>( + ({ className, ...props }, ref) => ( + + ), +); +DialogTitle.displayName = 'DialogTitle'; + +const DialogDescription = React.forwardRef< + HTMLParagraphElement, + React.ComponentPropsWithoutRef +>(({ className, ...props }, ref) => ( + +)); +DialogDescription.displayName = 'DialogDescription'; + +export { + Dialog, + DialogPortal, + DialogBackdrop, + DialogClose, + DialogTrigger, + DialogContent, + DialogHeader, + DialogFooter, + DialogTitle, + DialogDescription, +}; diff --git a/app/src/components/ui-base/menu.tsx b/app/src/components/ui-base/menu.tsx new file mode 100644 index 0000000..66cd927 --- /dev/null +++ b/app/src/components/ui-base/menu.tsx @@ -0,0 +1,81 @@ +'use client'; + +import * as React from 'react'; +import { Menu as BaseMenu } from '@base-ui/react/menu'; +import { cn } from '@/lib/utils'; + +const Menu = BaseMenu.Root; + +const MenuTrigger = React.forwardRef>( + ({ className, ...props }, ref) => ( + + ), +); +MenuTrigger.displayName = 'MenuTrigger'; + +const MenuPortal = BaseMenu.Portal; + +const MenuPositioner = React.forwardRef>( + ({ className, sideOffset = 8, ...props }, ref) => ( + + ), +); +MenuPositioner.displayName = 'MenuPositioner'; + +const MenuPopup = React.forwardRef>( + ({ className, ...props }, ref) => ( + + ), +); +MenuPopup.displayName = 'MenuPopup'; + +const MenuItem = React.forwardRef>( + ({ className, ...props }, ref) => ( + + ), +); +MenuItem.displayName = 'MenuItem'; + +const MenuSeparator = React.forwardRef>( + ({ className, ...props }, ref) => ( + + ), +); +MenuSeparator.displayName = 'MenuSeparator'; + +const MenuGroup = BaseMenu.Group; + +const MenuGroupLabel = React.forwardRef>( + ({ className, ...props }, ref) => ( + + ), +); +MenuGroupLabel.displayName = 'MenuGroupLabel'; + +export { Menu, MenuTrigger, MenuPortal, MenuPositioner, MenuPopup, MenuItem, MenuSeparator, MenuGroup, MenuGroupLabel };