diff --git a/aha-ui/src/assets/logo copy.tsx b/aha-ui/src/assets/logo copy.tsx deleted file mode 100644 index 7571f44..0000000 --- a/aha-ui/src/assets/logo copy.tsx +++ /dev/null @@ -1,24 +0,0 @@ -import { type SVGProps } from 'react' -import { cn } from '@/lib/utils' - -export function Logo({ className, ...props }: SVGProps) { - return ( - - ) -} diff --git a/aha-ui/src/components/ui/alert-dialog.tsx b/aha-ui/src/components/ui/alert-dialog.tsx new file mode 100644 index 0000000..184fa90 --- /dev/null +++ b/aha-ui/src/components/ui/alert-dialog.tsx @@ -0,0 +1,154 @@ +import * as React from 'react' +import * as AlertDialogPrimitive from '@radix-ui/react-alert-dialog' +import { cn } from '@/lib/utils' +import { buttonVariants } from '@/components/ui/button' + +function AlertDialog({ + ...props +}: React.ComponentProps) { + return +} + +function AlertDialogTrigger({ + ...props +}: React.ComponentProps) { + return ( + + ) +} + +function AlertDialogPortal({ + ...props +}: React.ComponentProps) { + return ( + + ) +} + +function AlertDialogOverlay({ + className, + ...props +}: React.ComponentProps) { + return ( + + ) +} + +function AlertDialogContent({ + className, + ...props +}: React.ComponentProps) { + return ( + + + + + ) +} + +function AlertDialogHeader({ + className, + ...props +}: React.ComponentProps<'div'>) { + return ( +
+ ) +} + +function AlertDialogFooter({ + className, + ...props +}: React.ComponentProps<'div'>) { + return ( +
+ ) +} + +function AlertDialogTitle({ + className, + ...props +}: React.ComponentProps) { + return ( + + ) +} + +function AlertDialogDescription({ + className, + ...props +}: React.ComponentProps) { + return ( + + ) +} + +function AlertDialogAction({ + className, + ...props +}: React.ComponentProps) { + return ( + + ) +} + +function AlertDialogCancel({ + className, + ...props +}: React.ComponentProps) { + return ( + + ) +} + +export { + AlertDialog, + AlertDialogPortal, + AlertDialogOverlay, + AlertDialogTrigger, + AlertDialogContent, + AlertDialogHeader, + AlertDialogFooter, + AlertDialogTitle, + AlertDialogDescription, + AlertDialogAction, + AlertDialogCancel, +} diff --git a/aha-ui/src/components/ui/alert.tsx b/aha-ui/src/components/ui/alert.tsx new file mode 100644 index 0000000..debcdf6 --- /dev/null +++ b/aha-ui/src/components/ui/alert.tsx @@ -0,0 +1,65 @@ +import * as React from 'react' +import { cva, type VariantProps } from 'class-variance-authority' +import { cn } from '@/lib/utils' + +const alertVariants = cva( + 'relative w-full rounded-lg border px-4 py-3 text-sm grid has-[>svg]:grid-cols-[calc(var(--spacing)*4)_1fr] grid-cols-[0_1fr] has-[>svg]:gap-x-3 gap-y-0.5 items-start [&>svg]:size-4 [&>svg]:translate-y-0.5 [&>svg]:text-current', + { + variants: { + variant: { + default: 'bg-card text-card-foreground', + destructive: + 'text-destructive bg-card [&>svg]:text-current *:data-[slot=alert-description]:text-destructive/90', + }, + }, + defaultVariants: { + variant: 'default', + }, + } +) + +function Alert({ + className, + variant, + ...props +}: React.ComponentProps<'div'> & VariantProps) { + return ( +
+ ) +} + +function AlertTitle({ className, ...props }: React.ComponentProps<'div'>) { + return ( +
+ ) +} + +function AlertDescription({ + className, + ...props +}: React.ComponentProps<'div'>) { + return ( +
+ ) +} + +export { Alert, AlertTitle, AlertDescription } diff --git a/aha-ui/src/components/ui/avatar.tsx b/aha-ui/src/components/ui/avatar.tsx new file mode 100644 index 0000000..b2a343e --- /dev/null +++ b/aha-ui/src/components/ui/avatar.tsx @@ -0,0 +1,50 @@ +import * as React from 'react' +import * as AvatarPrimitive from '@radix-ui/react-avatar' +import { cn } from '@/lib/utils' + +function Avatar({ + className, + ...props +}: React.ComponentProps) { + return ( + + ) +} + +function AvatarImage({ + className, + ...props +}: React.ComponentProps) { + return ( + + ) +} + +function AvatarFallback({ + className, + ...props +}: React.ComponentProps) { + return ( + + ) +} + +export { Avatar, AvatarImage, AvatarFallback } diff --git a/aha-ui/src/components/ui/badge.tsx b/aha-ui/src/components/ui/badge.tsx new file mode 100644 index 0000000..26769c3 --- /dev/null +++ b/aha-ui/src/components/ui/badge.tsx @@ -0,0 +1,45 @@ +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-md 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 & { asChild?: boolean }) { + const Comp = asChild ? Slot : 'span' + + return ( + + ) +} + +export { Badge, badgeVariants } diff --git a/aha-ui/src/components/ui/button.tsx b/aha-ui/src/components/ui/button.tsx new file mode 100644 index 0000000..27b86ed --- /dev/null +++ b/aha-ui/src/components/ui/button.tsx @@ -0,0 +1,58 @@ +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 buttonVariants = cva( + "inline-flex items-center justify-center gap-2 whitespace-nowrap rounded-md text-sm font-medium transition-all disabled:pointer-events-none disabled:opacity-50 [&_svg]:pointer-events-none [&_svg:not([class*='size-'])]:size-4 shrink-0 [&_svg]:shrink-0 outline-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", + { + variants: { + variant: { + default: + 'bg-primary text-primary-foreground shadow-xs hover:bg-primary/90', + destructive: + 'bg-destructive text-white shadow-xs hover:bg-destructive/90 focus-visible:ring-destructive/20 dark:focus-visible:ring-destructive/40 dark:bg-destructive/60', + outline: + 'border bg-background shadow-xs hover:bg-accent hover:text-accent-foreground dark:bg-input/30 dark:border-input dark:hover:bg-input/50', + secondary: + 'bg-secondary text-secondary-foreground shadow-xs hover:bg-secondary/80', + ghost: + 'hover:bg-accent hover:text-accent-foreground dark:hover:bg-accent/50', + link: 'text-primary underline-offset-4 hover:underline', + }, + size: { + default: 'h-9 px-4 py-2 has-[>svg]:px-3', + sm: 'h-8 rounded-md gap-1.5 px-3 has-[>svg]:px-2.5', + lg: 'h-10 rounded-md px-6 has-[>svg]:px-4', + icon: 'size-9', + }, + }, + defaultVariants: { + variant: 'default', + size: 'default', + }, + } +) + +function Button({ + className, + variant, + size, + asChild = false, + ...props +}: React.ComponentProps<'button'> & + VariantProps & { + asChild?: boolean + }) { + const Comp = asChild ? Slot : 'button' + + return ( + + ) +} + +export { Button, buttonVariants } diff --git a/aha-ui/src/components/ui/calendar.tsx b/aha-ui/src/components/ui/calendar.tsx new file mode 100644 index 0000000..7b1c90a --- /dev/null +++ b/aha-ui/src/components/ui/calendar.tsx @@ -0,0 +1,210 @@ +import * as React from 'react' +import { + ChevronDownIcon, + ChevronLeftIcon, + ChevronRightIcon, +} from 'lucide-react' +import { DayButton, DayPicker, getDefaultClassNames } from 'react-day-picker' +import { cn } from '@/lib/utils' +import { Button, buttonVariants } from '@/components/ui/button' + +function Calendar({ + className, + classNames, + showOutsideDays = true, + captionLayout = 'label', + buttonVariant = 'ghost', + formatters, + components, + ...props +}: React.ComponentProps & { + buttonVariant?: React.ComponentProps['variant'] +}) { + const defaultClassNames = getDefaultClassNames() + + return ( + svg]:rotate-180`, + String.raw`rtl:**:[.rdp-button\_previous>svg]:rotate-180`, + className + )} + captionLayout={captionLayout} + formatters={{ + formatMonthDropdown: (date) => + date.toLocaleString('default', { month: 'short' }), + ...formatters, + }} + classNames={{ + root: cn('w-fit', defaultClassNames.root), + months: cn( + 'relative flex flex-col gap-4 md:flex-row', + defaultClassNames.months + ), + month: cn('flex w-full flex-col gap-4', defaultClassNames.month), + nav: cn( + 'absolute inset-x-0 top-0 flex w-full items-center justify-between gap-1', + defaultClassNames.nav + ), + button_previous: cn( + buttonVariants({ variant: buttonVariant }), + 'size-(--cell-size) p-0 select-none aria-disabled:opacity-50', + defaultClassNames.button_previous + ), + button_next: cn( + buttonVariants({ variant: buttonVariant }), + 'size-(--cell-size) p-0 select-none aria-disabled:opacity-50', + defaultClassNames.button_next + ), + month_caption: cn( + 'flex h-(--cell-size) w-full items-center justify-center px-(--cell-size)', + defaultClassNames.month_caption + ), + dropdowns: cn( + 'flex h-(--cell-size) w-full items-center justify-center gap-1.5 text-sm font-medium', + defaultClassNames.dropdowns + ), + dropdown_root: cn( + 'relative rounded-md border border-input shadow-xs has-focus:border-ring has-focus:ring-[3px] has-focus:ring-ring/50', + defaultClassNames.dropdown_root + ), + dropdown: cn( + 'absolute inset-0 bg-popover opacity-0', + defaultClassNames.dropdown + ), + caption_label: cn( + 'font-medium select-none', + captionLayout === 'label' + ? 'text-sm' + : 'flex h-8 items-center gap-1 rounded-md ps-2 pe-1 text-sm [&>svg]:size-3.5 [&>svg]:text-muted-foreground', + defaultClassNames.caption_label + ), + table: 'w-full border-collapse', + weekdays: cn('flex', defaultClassNames.weekdays), + weekday: cn( + 'flex-1 rounded-md text-[0.8rem] font-normal text-muted-foreground select-none', + defaultClassNames.weekday + ), + week: cn('mt-2 flex w-full', defaultClassNames.week), + week_number_header: cn( + 'w-(--cell-size) select-none', + defaultClassNames.week_number_header + ), + week_number: cn( + 'text-[0.8rem] text-muted-foreground select-none', + defaultClassNames.week_number + ), + day: cn( + 'group/day relative aspect-square h-full w-full p-0 text-center select-none [&:first-child[data-selected=true]_button]:rounded-l-md [&:last-child[data-selected=true]_button]:rounded-r-md', + defaultClassNames.day + ), + range_start: cn( + 'rounded-l-md bg-accent', + defaultClassNames.range_start + ), + range_middle: cn('rounded-none', defaultClassNames.range_middle), + range_end: cn('rounded-r-md bg-accent', defaultClassNames.range_end), + today: cn( + 'rounded-md bg-accent text-accent-foreground data-[selected=true]:rounded-none', + defaultClassNames.today + ), + outside: cn( + 'text-muted-foreground aria-selected:text-muted-foreground', + defaultClassNames.outside + ), + disabled: cn( + 'text-muted-foreground opacity-50', + defaultClassNames.disabled + ), + hidden: cn('invisible', defaultClassNames.hidden), + ...classNames, + }} + components={{ + Root: ({ className, rootRef, ...props }) => { + return ( +
+ ) + }, + Chevron: ({ className, orientation, ...props }) => { + if (orientation === 'left') { + return ( + + ) + } + + if (orientation === 'right') { + return ( + + ) + } + + return ( + + ) + }, + DayButton: CalendarDayButton, + WeekNumber: ({ children, ...props }) => { + return ( + +
+ {children} +
+ + ) + }, + ...components, + }} + {...props} + /> + ) +} + +function CalendarDayButton({ + className, + day, + modifiers, + ...props +}: React.ComponentProps) { + const defaultClassNames = getDefaultClassNames() + + const ref = React.useRef(null) + React.useEffect(() => { + if (modifiers.focused) ref.current?.focus() + }, [modifiers.focused]) + + return ( + + ) +} + +function SidebarRail({ className, ...props }: React.ComponentProps<'button'>) { + const { toggleSidebar } = useSidebar() + + return ( +