From b5305dbfb04e14f05ca6312b14afe9893443219f Mon Sep 17 00:00:00 2001 From: PoRi <1960825664@qq.com> Date: Sat, 25 Apr 2026 17:15:56 +0800 Subject: [PATCH] =?UTF-8?q?```=20feat(ui):=20=E6=B7=BB=E5=8A=A0=E5=A4=9A?= =?UTF-8?q?=E4=B8=AAUI=E7=BB=84=E4=BB=B6=E5=B9=B6=E5=88=A0=E9=99=A4logo?= =?UTF-8?q?=E5=89=AF=E6=9C=AC=E6=96=87=E4=BB=B6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 添加了以下UI组件: - alert-dialog.tsx - 警告对话框组件 - alert.tsx - 提示组件 - avatar.tsx - 头像组件 - badge.tsx - 徽章组件 - button.tsx - 按钮组件 - calendar.tsx - 日历组件 - card.tsx - 卡片组件 - checkbox.tsx - 复选框组件 - collapsible.tsx - 折叠组件 - command.tsx - 命令组件 - dialog.tsx - 对话框组件 同时删除了src/assets/logo copy.tsx文件 ``` --- aha-ui/src/assets/logo copy.tsx | 24 - aha-ui/src/components/ui/alert-dialog.tsx | 154 +++++ aha-ui/src/components/ui/alert.tsx | 65 ++ aha-ui/src/components/ui/avatar.tsx | 50 ++ aha-ui/src/components/ui/badge.tsx | 45 ++ aha-ui/src/components/ui/button.tsx | 58 ++ aha-ui/src/components/ui/calendar.tsx | 210 ++++++ aha-ui/src/components/ui/card.tsx | 91 +++ aha-ui/src/components/ui/checkbox.tsx | 29 + aha-ui/src/components/ui/collapsible.tsx | 31 + aha-ui/src/components/ui/command.tsx | 181 +++++ aha-ui/src/components/ui/dialog.tsx | 142 ++++ aha-ui/src/components/ui/dropdown-menu.tsx | 254 +++++++ aha-ui/src/components/ui/form.tsx | 164 +++++ aha-ui/src/components/ui/input-otp.tsx | 74 +++ aha-ui/src/components/ui/input.tsx | 20 + aha-ui/src/components/ui/label.tsx | 23 + aha-ui/src/components/ui/popover.tsx | 45 ++ aha-ui/src/components/ui/radio-group.tsx | 42 ++ aha-ui/src/components/ui/scroll-area.tsx | 65 ++ aha-ui/src/components/ui/select.tsx | 182 ++++++ aha-ui/src/components/ui/separator.tsx | 25 + aha-ui/src/components/ui/sheet.tsx | 136 ++++ aha-ui/src/components/ui/sidebar.tsx | 728 +++++++++++++++++++++ aha-ui/src/components/ui/skeleton.tsx | 13 + aha-ui/src/components/ui/sonner.tsx | 21 + aha-ui/src/components/ui/switch.tsx | 28 + aha-ui/src/components/ui/table.tsx | 113 ++++ aha-ui/src/components/ui/tabs.tsx | 63 ++ aha-ui/src/components/ui/textarea.tsx | 17 + aha-ui/src/components/ui/tooltip.tsx | 60 ++ 31 files changed, 3129 insertions(+), 24 deletions(-) delete mode 100644 aha-ui/src/assets/logo copy.tsx create mode 100644 aha-ui/src/components/ui/alert-dialog.tsx create mode 100644 aha-ui/src/components/ui/alert.tsx create mode 100644 aha-ui/src/components/ui/avatar.tsx create mode 100644 aha-ui/src/components/ui/badge.tsx create mode 100644 aha-ui/src/components/ui/button.tsx create mode 100644 aha-ui/src/components/ui/calendar.tsx create mode 100644 aha-ui/src/components/ui/card.tsx create mode 100644 aha-ui/src/components/ui/checkbox.tsx create mode 100644 aha-ui/src/components/ui/collapsible.tsx create mode 100644 aha-ui/src/components/ui/command.tsx create mode 100644 aha-ui/src/components/ui/dialog.tsx create mode 100644 aha-ui/src/components/ui/dropdown-menu.tsx create mode 100644 aha-ui/src/components/ui/form.tsx create mode 100644 aha-ui/src/components/ui/input-otp.tsx create mode 100644 aha-ui/src/components/ui/input.tsx create mode 100644 aha-ui/src/components/ui/label.tsx create mode 100644 aha-ui/src/components/ui/popover.tsx create mode 100644 aha-ui/src/components/ui/radio-group.tsx create mode 100644 aha-ui/src/components/ui/scroll-area.tsx create mode 100644 aha-ui/src/components/ui/select.tsx create mode 100644 aha-ui/src/components/ui/separator.tsx create mode 100644 aha-ui/src/components/ui/sheet.tsx create mode 100644 aha-ui/src/components/ui/sidebar.tsx create mode 100644 aha-ui/src/components/ui/skeleton.tsx create mode 100644 aha-ui/src/components/ui/sonner.tsx create mode 100644 aha-ui/src/components/ui/switch.tsx create mode 100644 aha-ui/src/components/ui/table.tsx create mode 100644 aha-ui/src/components/ui/tabs.tsx create mode 100644 aha-ui/src/components/ui/textarea.tsx create mode 100644 aha-ui/src/components/ui/tooltip.tsx 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 ( +