@teamix-evo/ui 0.1.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +336 -0
- package/_data.json +12 -0
- package/manifest.json +1688 -0
- package/package.json +90 -0
- package/src/components/accordion/accordion.meta.md +87 -0
- package/src/components/accordion/accordion.stories.tsx +67 -0
- package/src/components/accordion/accordion.tsx +58 -0
- package/src/components/affix/affix.meta.md +80 -0
- package/src/components/affix/affix.stories.tsx +57 -0
- package/src/components/affix/affix.tsx +97 -0
- package/src/components/alert/alert.meta.md +101 -0
- package/src/components/alert/alert.stories.tsx +93 -0
- package/src/components/alert/alert.tsx +132 -0
- package/src/components/alert-dialog/alert-dialog.meta.md +107 -0
- package/src/components/alert-dialog/alert-dialog.stories.tsx +81 -0
- package/src/components/alert-dialog/alert-dialog.tsx +136 -0
- package/src/components/anchor/anchor.meta.md +87 -0
- package/src/components/anchor/anchor.stories.tsx +74 -0
- package/src/components/anchor/anchor.tsx +130 -0
- package/src/components/app/app.meta.md +86 -0
- package/src/components/app/app.stories.tsx +62 -0
- package/src/components/app/app.tsx +58 -0
- package/src/components/aspect-ratio/aspect-ratio.meta.md +81 -0
- package/src/components/aspect-ratio/aspect-ratio.stories.tsx +59 -0
- package/src/components/aspect-ratio/aspect-ratio.tsx +22 -0
- package/src/components/auto-complete/auto-complete.meta.md +102 -0
- package/src/components/auto-complete/auto-complete.stories.tsx +93 -0
- package/src/components/auto-complete/auto-complete.tsx +205 -0
- package/src/components/avatar/avatar.meta.md +94 -0
- package/src/components/avatar/avatar.stories.tsx +80 -0
- package/src/components/avatar/avatar.tsx +126 -0
- package/src/components/badge/badge.meta.md +119 -0
- package/src/components/badge/badge.stories.tsx +153 -0
- package/src/components/badge/badge.tsx +210 -0
- package/src/components/breadcrumb/breadcrumb.meta.md +107 -0
- package/src/components/breadcrumb/breadcrumb.stories.tsx +84 -0
- package/src/components/breadcrumb/breadcrumb.tsx +122 -0
- package/src/components/button/button.meta.md +98 -0
- package/src/components/button/button.stories.tsx +235 -0
- package/src/components/button/button.tsx +160 -0
- package/src/components/button-group/button-group.meta.md +92 -0
- package/src/components/button-group/button-group.stories.tsx +90 -0
- package/src/components/button-group/button-group.tsx +75 -0
- package/src/components/calendar/calendar.meta.md +118 -0
- package/src/components/calendar/calendar.stories.tsx +68 -0
- package/src/components/calendar/calendar.tsx +107 -0
- package/src/components/card/card.meta.md +117 -0
- package/src/components/card/card.stories.tsx +112 -0
- package/src/components/card/card.tsx +222 -0
- package/src/components/carousel/carousel.meta.md +117 -0
- package/src/components/carousel/carousel.stories.tsx +84 -0
- package/src/components/carousel/carousel.tsx +224 -0
- package/src/components/cascader/cascader.meta.md +110 -0
- package/src/components/cascader/cascader.stories.tsx +108 -0
- package/src/components/cascader/cascader.tsx +198 -0
- package/src/components/checkbox/checkbox.meta.md +99 -0
- package/src/components/checkbox/checkbox.stories.tsx +130 -0
- package/src/components/checkbox/checkbox.tsx +125 -0
- package/src/components/collapsible/collapsible.meta.md +80 -0
- package/src/components/collapsible/collapsible.stories.tsx +35 -0
- package/src/components/collapsible/collapsible.tsx +18 -0
- package/src/components/color-picker/color-picker.meta.md +84 -0
- package/src/components/color-picker/color-picker.stories.tsx +80 -0
- package/src/components/color-picker/color-picker.tsx +160 -0
- package/src/components/combobox/combobox.meta.md +93 -0
- package/src/components/combobox/combobox.stories.tsx +55 -0
- package/src/components/combobox/combobox.tsx +130 -0
- package/src/components/command/command.meta.md +104 -0
- package/src/components/command/command.stories.tsx +59 -0
- package/src/components/command/command.tsx +147 -0
- package/src/components/context-menu/context-menu.meta.md +90 -0
- package/src/components/context-menu/context-menu.stories.tsx +46 -0
- package/src/components/context-menu/context-menu.tsx +191 -0
- package/src/components/data-table/data-table.meta.md +149 -0
- package/src/components/data-table/data-table.stories.tsx +125 -0
- package/src/components/data-table/data-table.tsx +185 -0
- package/src/components/date-picker/date-picker.meta.md +106 -0
- package/src/components/date-picker/date-picker.stories.tsx +58 -0
- package/src/components/date-picker/date-picker.tsx +156 -0
- package/src/components/descriptions/descriptions.meta.md +78 -0
- package/src/components/descriptions/descriptions.stories.tsx +60 -0
- package/src/components/descriptions/descriptions.tsx +129 -0
- package/src/components/dialog/dialog.meta.md +105 -0
- package/src/components/dialog/dialog.stories.tsx +93 -0
- package/src/components/dialog/dialog.tsx +128 -0
- package/src/components/drawer/drawer.meta.md +96 -0
- package/src/components/drawer/drawer.stories.tsx +54 -0
- package/src/components/drawer/drawer.tsx +114 -0
- package/src/components/dropdown-menu/dropdown-menu.meta.md +103 -0
- package/src/components/dropdown-menu/dropdown-menu.stories.tsx +112 -0
- package/src/components/dropdown-menu/dropdown-menu.tsx +195 -0
- package/src/components/empty/empty.meta.md +81 -0
- package/src/components/empty/empty.stories.tsx +46 -0
- package/src/components/empty/empty.tsx +47 -0
- package/src/components/field/field.meta.md +116 -0
- package/src/components/field/field.stories.tsx +117 -0
- package/src/components/field/field.tsx +164 -0
- package/src/components/flex/flex.meta.md +94 -0
- package/src/components/flex/flex.stories.tsx +112 -0
- package/src/components/flex/flex.tsx +122 -0
- package/src/components/float-button/float-button.meta.md +87 -0
- package/src/components/float-button/float-button.stories.tsx +78 -0
- package/src/components/float-button/float-button.tsx +143 -0
- package/src/components/form/form.meta.md +131 -0
- package/src/components/form/form.stories.tsx +122 -0
- package/src/components/form/form.tsx +194 -0
- package/src/components/grid/grid.meta.md +87 -0
- package/src/components/grid/grid.stories.tsx +99 -0
- package/src/components/grid/grid.tsx +130 -0
- package/src/components/hover-card/hover-card.meta.md +92 -0
- package/src/components/hover-card/hover-card.stories.tsx +68 -0
- package/src/components/hover-card/hover-card.tsx +29 -0
- package/src/components/image/image.meta.md +94 -0
- package/src/components/image/image.stories.tsx +55 -0
- package/src/components/image/image.tsx +138 -0
- package/src/components/input/input.meta.md +109 -0
- package/src/components/input/input.stories.tsx +117 -0
- package/src/components/input/input.tsx +213 -0
- package/src/components/input-group/input-group.meta.md +92 -0
- package/src/components/input-group/input-group.stories.tsx +88 -0
- package/src/components/input-group/input-group.tsx +107 -0
- package/src/components/input-number/input-number.meta.md +91 -0
- package/src/components/input-number/input-number.stories.tsx +87 -0
- package/src/components/input-number/input-number.tsx +210 -0
- package/src/components/input-otp/input-otp.meta.md +105 -0
- package/src/components/input-otp/input-otp.stories.tsx +65 -0
- package/src/components/input-otp/input-otp.tsx +97 -0
- package/src/components/item/item.meta.md +116 -0
- package/src/components/item/item.stories.tsx +113 -0
- package/src/components/item/item.tsx +171 -0
- package/src/components/kbd/kbd.meta.md +85 -0
- package/src/components/kbd/kbd.stories.tsx +70 -0
- package/src/components/kbd/kbd.tsx +81 -0
- package/src/components/label/label.meta.md +91 -0
- package/src/components/label/label.stories.tsx +87 -0
- package/src/components/label/label.tsx +66 -0
- package/src/components/masonry/masonry.meta.md +85 -0
- package/src/components/masonry/masonry.stories.tsx +66 -0
- package/src/components/masonry/masonry.tsx +59 -0
- package/src/components/mentions/mentions.meta.md +89 -0
- package/src/components/mentions/mentions.stories.tsx +75 -0
- package/src/components/mentions/mentions.tsx +237 -0
- package/src/components/menubar/menubar.meta.md +100 -0
- package/src/components/menubar/menubar.stories.tsx +81 -0
- package/src/components/menubar/menubar.tsx +232 -0
- package/src/components/native-select/native-select.meta.md +88 -0
- package/src/components/native-select/native-select.stories.tsx +80 -0
- package/src/components/native-select/native-select.tsx +54 -0
- package/src/components/navigation-menu/navigation-menu.meta.md +108 -0
- package/src/components/navigation-menu/navigation-menu.stories.tsx +112 -0
- package/src/components/navigation-menu/navigation-menu.tsx +125 -0
- package/src/components/notification/notification.meta.md +91 -0
- package/src/components/notification/notification.stories.tsx +96 -0
- package/src/components/notification/notification.tsx +84 -0
- package/src/components/pagination/pagination.meta.md +127 -0
- package/src/components/pagination/pagination.stories.tsx +62 -0
- package/src/components/pagination/pagination.tsx +285 -0
- package/src/components/popconfirm/popconfirm.meta.md +109 -0
- package/src/components/popconfirm/popconfirm.stories.tsx +76 -0
- package/src/components/popconfirm/popconfirm.tsx +134 -0
- package/src/components/popover/popover.meta.md +97 -0
- package/src/components/popover/popover.stories.tsx +82 -0
- package/src/components/popover/popover.tsx +55 -0
- package/src/components/progress/progress.meta.md +86 -0
- package/src/components/progress/progress.stories.tsx +75 -0
- package/src/components/progress/progress.tsx +195 -0
- package/src/components/radio-group/radio-group.meta.md +103 -0
- package/src/components/radio-group/radio-group.stories.tsx +77 -0
- package/src/components/radio-group/radio-group.tsx +78 -0
- package/src/components/rate/rate.meta.md +87 -0
- package/src/components/rate/rate.stories.tsx +81 -0
- package/src/components/rate/rate.tsx +153 -0
- package/src/components/resizable/resizable.meta.md +92 -0
- package/src/components/resizable/resizable.stories.tsx +104 -0
- package/src/components/resizable/resizable.tsx +56 -0
- package/src/components/result/result.meta.md +90 -0
- package/src/components/result/result.stories.tsx +71 -0
- package/src/components/result/result.tsx +91 -0
- package/src/components/scroll-area/scroll-area.meta.md +84 -0
- package/src/components/scroll-area/scroll-area.stories.tsx +41 -0
- package/src/components/scroll-area/scroll-area.tsx +51 -0
- package/src/components/segmented/segmented.meta.md +103 -0
- package/src/components/segmented/segmented.stories.tsx +101 -0
- package/src/components/segmented/segmented.tsx +138 -0
- package/src/components/select/select.meta.md +110 -0
- package/src/components/select/select.stories.tsx +100 -0
- package/src/components/select/select.tsx +188 -0
- package/src/components/separator/separator.meta.md +74 -0
- package/src/components/separator/separator.stories.tsx +71 -0
- package/src/components/separator/separator.tsx +104 -0
- package/src/components/sheet/sheet.meta.md +97 -0
- package/src/components/sheet/sheet.stories.tsx +82 -0
- package/src/components/sheet/sheet.tsx +139 -0
- package/src/components/sidebar/sidebar.meta.md +131 -0
- package/src/components/sidebar/sidebar.stories.tsx +82 -0
- package/src/components/sidebar/sidebar.tsx +351 -0
- package/src/components/skeleton/skeleton.meta.md +95 -0
- package/src/components/skeleton/skeleton.stories.tsx +79 -0
- package/src/components/skeleton/skeleton.tsx +144 -0
- package/src/components/slider/slider.meta.md +94 -0
- package/src/components/slider/slider.stories.tsx +69 -0
- package/src/components/slider/slider.tsx +86 -0
- package/src/components/sonner/sonner.meta.md +96 -0
- package/src/components/sonner/sonner.stories.tsx +91 -0
- package/src/components/sonner/sonner.tsx +40 -0
- package/src/components/space/space.meta.md +94 -0
- package/src/components/space/space.stories.tsx +94 -0
- package/src/components/space/space.tsx +106 -0
- package/src/components/spinner/spinner.meta.md +76 -0
- package/src/components/spinner/spinner.stories.tsx +71 -0
- package/src/components/spinner/spinner.tsx +64 -0
- package/src/components/statistic/statistic.meta.md +99 -0
- package/src/components/statistic/statistic.stories.tsx +71 -0
- package/src/components/statistic/statistic.tsx +197 -0
- package/src/components/steps/steps.meta.md +102 -0
- package/src/components/steps/steps.stories.tsx +75 -0
- package/src/components/steps/steps.tsx +170 -0
- package/src/components/switch/switch.meta.md +92 -0
- package/src/components/switch/switch.stories.tsx +75 -0
- package/src/components/switch/switch.tsx +101 -0
- package/src/components/table/table.meta.md +95 -0
- package/src/components/table/table.stories.tsx +75 -0
- package/src/components/table/table.tsx +122 -0
- package/src/components/tabs/tabs.meta.md +98 -0
- package/src/components/tabs/tabs.stories.tsx +70 -0
- package/src/components/tabs/tabs.tsx +119 -0
- package/src/components/tag/tag.meta.md +94 -0
- package/src/components/tag/tag.stories.tsx +77 -0
- package/src/components/tag/tag.tsx +185 -0
- package/src/components/textarea/textarea.meta.md +83 -0
- package/src/components/textarea/textarea.stories.tsx +63 -0
- package/src/components/textarea/textarea.tsx +113 -0
- package/src/components/time-picker/time-picker.meta.md +83 -0
- package/src/components/time-picker/time-picker.stories.tsx +59 -0
- package/src/components/time-picker/time-picker.tsx +94 -0
- package/src/components/timeline/timeline.meta.md +102 -0
- package/src/components/timeline/timeline.stories.tsx +104 -0
- package/src/components/timeline/timeline.tsx +147 -0
- package/src/components/toggle/toggle.meta.md +88 -0
- package/src/components/toggle/toggle.stories.tsx +66 -0
- package/src/components/toggle/toggle.tsx +53 -0
- package/src/components/toggle-group/toggle-group.meta.md +90 -0
- package/src/components/toggle-group/toggle-group.stories.tsx +83 -0
- package/src/components/toggle-group/toggle-group.tsx +78 -0
- package/src/components/tooltip/tooltip.meta.md +99 -0
- package/src/components/tooltip/tooltip.stories.tsx +71 -0
- package/src/components/tooltip/tooltip.tsx +93 -0
- package/src/components/tour/tour.meta.md +116 -0
- package/src/components/tour/tour.stories.tsx +66 -0
- package/src/components/tour/tour.tsx +242 -0
- package/src/components/transfer/transfer.meta.md +90 -0
- package/src/components/transfer/transfer.stories.tsx +68 -0
- package/src/components/transfer/transfer.tsx +251 -0
- package/src/components/tree/tree.meta.md +111 -0
- package/src/components/tree/tree.stories.tsx +109 -0
- package/src/components/tree/tree.tsx +367 -0
- package/src/components/tree-select/tree-select.meta.md +100 -0
- package/src/components/tree-select/tree-select.stories.tsx +80 -0
- package/src/components/tree-select/tree-select.tsx +171 -0
- package/src/components/typography/typography.meta.md +102 -0
- package/src/components/typography/typography.stories.tsx +115 -0
- package/src/components/typography/typography.tsx +245 -0
- package/src/components/upload/upload.meta.md +111 -0
- package/src/components/upload/upload.stories.tsx +75 -0
- package/src/components/upload/upload.tsx +265 -0
- package/src/components/watermark/watermark.meta.md +95 -0
- package/src/components/watermark/watermark.stories.tsx +78 -0
- package/src/components/watermark/watermark.tsx +165 -0
- package/src/utils/cn.ts +6 -0
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
import * as React from 'react';
|
|
2
|
+
import { Slot } from '@radix-ui/react-slot';
|
|
3
|
+
import { ChevronRight, MoreHorizontal } from 'lucide-react';
|
|
4
|
+
|
|
5
|
+
import { cn } from '@/utils/cn';
|
|
6
|
+
|
|
7
|
+
export interface BreadcrumbProps extends React.ComponentPropsWithoutRef<'nav'> {
|
|
8
|
+
/**
|
|
9
|
+
* 自定义分隔符(antd `separator` 并集)。每个 `<BreadcrumbSeparator />` 默认渲染 `<ChevronRight />`,
|
|
10
|
+
* 通过设置 children 可全局覆盖。
|
|
11
|
+
*/
|
|
12
|
+
separator?: React.ReactNode;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
const Breadcrumb = React.forwardRef<HTMLElement, BreadcrumbProps>(
|
|
16
|
+
({ ...props }, ref) => <nav ref={ref} aria-label="breadcrumb" {...props} />,
|
|
17
|
+
);
|
|
18
|
+
Breadcrumb.displayName = 'Breadcrumb';
|
|
19
|
+
|
|
20
|
+
const BreadcrumbList = React.forwardRef<
|
|
21
|
+
HTMLOListElement,
|
|
22
|
+
React.ComponentPropsWithoutRef<'ol'>
|
|
23
|
+
>(({ className, ...props }, ref) => (
|
|
24
|
+
<ol
|
|
25
|
+
ref={ref}
|
|
26
|
+
className={cn(
|
|
27
|
+
'flex flex-wrap items-center gap-1.5 text-sm text-muted-foreground sm:gap-2.5',
|
|
28
|
+
className,
|
|
29
|
+
)}
|
|
30
|
+
{...props}
|
|
31
|
+
/>
|
|
32
|
+
));
|
|
33
|
+
BreadcrumbList.displayName = 'BreadcrumbList';
|
|
34
|
+
|
|
35
|
+
const BreadcrumbItem = React.forwardRef<
|
|
36
|
+
HTMLLIElement,
|
|
37
|
+
React.ComponentPropsWithoutRef<'li'>
|
|
38
|
+
>(({ className, ...props }, ref) => (
|
|
39
|
+
<li
|
|
40
|
+
ref={ref}
|
|
41
|
+
className={cn('inline-flex items-center gap-1.5', className)}
|
|
42
|
+
{...props}
|
|
43
|
+
/>
|
|
44
|
+
));
|
|
45
|
+
BreadcrumbItem.displayName = 'BreadcrumbItem';
|
|
46
|
+
|
|
47
|
+
export interface BreadcrumbLinkProps
|
|
48
|
+
extends React.ComponentPropsWithoutRef<'a'> {
|
|
49
|
+
/** 用 Slot 渲染为子元素(常配合 React Router / Next.js Link)。 */
|
|
50
|
+
asChild?: boolean;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
const BreadcrumbLink = React.forwardRef<HTMLAnchorElement, BreadcrumbLinkProps>(
|
|
54
|
+
({ asChild, className, ...props }, ref) => {
|
|
55
|
+
const Comp = asChild ? Slot : 'a';
|
|
56
|
+
return (
|
|
57
|
+
<Comp
|
|
58
|
+
ref={ref}
|
|
59
|
+
className={cn('transition-colors hover:text-foreground', className)}
|
|
60
|
+
{...props}
|
|
61
|
+
/>
|
|
62
|
+
);
|
|
63
|
+
},
|
|
64
|
+
);
|
|
65
|
+
BreadcrumbLink.displayName = 'BreadcrumbLink';
|
|
66
|
+
|
|
67
|
+
const BreadcrumbPage = React.forwardRef<
|
|
68
|
+
HTMLSpanElement,
|
|
69
|
+
React.ComponentPropsWithoutRef<'span'>
|
|
70
|
+
>(({ className, ...props }, ref) => (
|
|
71
|
+
<span
|
|
72
|
+
ref={ref}
|
|
73
|
+
role="link"
|
|
74
|
+
aria-disabled="true"
|
|
75
|
+
aria-current="page"
|
|
76
|
+
className={cn('font-normal text-foreground', className)}
|
|
77
|
+
{...props}
|
|
78
|
+
/>
|
|
79
|
+
));
|
|
80
|
+
BreadcrumbPage.displayName = 'BreadcrumbPage';
|
|
81
|
+
|
|
82
|
+
const BreadcrumbSeparator = ({
|
|
83
|
+
children,
|
|
84
|
+
className,
|
|
85
|
+
...props
|
|
86
|
+
}: React.ComponentProps<'li'>) => (
|
|
87
|
+
<li
|
|
88
|
+
role="presentation"
|
|
89
|
+
aria-hidden="true"
|
|
90
|
+
className={cn('[&>svg]:size-3.5', className)}
|
|
91
|
+
{...props}
|
|
92
|
+
>
|
|
93
|
+
{children ?? <ChevronRight />}
|
|
94
|
+
</li>
|
|
95
|
+
);
|
|
96
|
+
BreadcrumbSeparator.displayName = 'BreadcrumbSeparator';
|
|
97
|
+
|
|
98
|
+
const BreadcrumbEllipsis = ({
|
|
99
|
+
className,
|
|
100
|
+
...props
|
|
101
|
+
}: React.ComponentProps<'span'>) => (
|
|
102
|
+
<span
|
|
103
|
+
role="presentation"
|
|
104
|
+
aria-hidden="true"
|
|
105
|
+
className={cn('flex size-9 items-center justify-center', className)}
|
|
106
|
+
{...props}
|
|
107
|
+
>
|
|
108
|
+
<MoreHorizontal className="size-4" />
|
|
109
|
+
<span className="sr-only">More</span>
|
|
110
|
+
</span>
|
|
111
|
+
);
|
|
112
|
+
BreadcrumbEllipsis.displayName = 'BreadcrumbEllipsis';
|
|
113
|
+
|
|
114
|
+
export {
|
|
115
|
+
Breadcrumb,
|
|
116
|
+
BreadcrumbList,
|
|
117
|
+
BreadcrumbItem,
|
|
118
|
+
BreadcrumbLink,
|
|
119
|
+
BreadcrumbPage,
|
|
120
|
+
BreadcrumbSeparator,
|
|
121
|
+
BreadcrumbEllipsis,
|
|
122
|
+
};
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
---
|
|
2
|
+
id: button
|
|
3
|
+
name: Button
|
|
4
|
+
type: component
|
|
5
|
+
category: foundation
|
|
6
|
+
since: 0.1.0
|
|
7
|
+
package: "@teamix-evo/ui"
|
|
8
|
+
---
|
|
9
|
+
|
|
10
|
+
# Button
|
|
11
|
+
|
|
12
|
+
通用按钮 — 基于 shadcn/ui Button 二次封装,补齐 antd 的 `loading / icon / shape / block` 等开箱即用能力。
|
|
13
|
+
按钮文本、aria 状态由消费方提供;视觉与可访问性由组件保障。
|
|
14
|
+
|
|
15
|
+
## When to use
|
|
16
|
+
|
|
17
|
+
- 表单提交、对话框确认、列表行操作等触发性交互
|
|
18
|
+
- 需要明确反馈"按下→处理中→完成"过程的场景(用 `loading`)
|
|
19
|
+
- 需要纯图标按钮时(用 `size="icon"` + `shape="circle"` 组合)
|
|
20
|
+
|
|
21
|
+
## When NOT to use
|
|
22
|
+
|
|
23
|
+
- 仅做导航而无副作用 → 用 `<a>` 或路由 `Link`(必要时配 `variant="link"` 视觉)
|
|
24
|
+
- 切换布尔状态 → 用 `Switch` / `Toggle`,不要用按钮模拟
|
|
25
|
+
- 需要承载多行说明文字 → 用 `Card` 等容器组件
|
|
26
|
+
|
|
27
|
+
## Props
|
|
28
|
+
|
|
29
|
+
> 以下表格由 `pnpm --filter @teamix-evo/ui gen:meta` 自动生成,数据源是 [`button.tsx`](./button.tsx) 的 `ButtonProps` interface JSDoc。**手工编辑 marker 之间的内容会在下次生成时被覆盖**。
|
|
30
|
+
|
|
31
|
+
<!-- auto:props:begin -->
|
|
32
|
+
| 名称 | 类型 | 默认值 | 必填 | 说明 |
|
|
33
|
+
| --- | --- | --- | --- | --- |
|
|
34
|
+
| `variant` | `'default' \| 'secondary' \| 'destructive' \| 'outline' \| 'dashed' \| 'ghost' \| 'link'` | `"default"` | – | 视觉风格,语义按 design token 走。 |
|
|
35
|
+
| `size` | `'sm' \| 'default' \| 'lg' \| 'icon'` | `"default"` | – | 尺寸,`icon` 为正方形纯图标按钮。 |
|
|
36
|
+
| `shape` | `'default' \| 'round' \| 'circle'` | `"default"` | – | 形状,`circle` 配 `size="icon"` 得圆形按钮。 |
|
|
37
|
+
| `block` | `boolean` | `false` | – | `true` 时撑满父容器宽度。 |
|
|
38
|
+
| `asChild` | `boolean` | `false` | – | 用 Radix Slot 模式渲染为子元素;此时 `loading` / `icon` 被忽略。 |
|
|
39
|
+
| `loading` | `boolean` | `false` | – | 显示自旋 spinner,自动 disabled,保留视觉宽度。 |
|
|
40
|
+
| `icon` | `React.ReactNode` | – | – | 前置 / 后置图标(来自 `lucide-react`)。loading 时被 spinner 替换。 |
|
|
41
|
+
| `iconPosition` | `'start' \| 'end'` | `"start"` | – | 图标 / spinner 相对文本的位置。 |
|
|
42
|
+
| `children` | `React.ReactNode` | – | – | 按钮文本 / 内容。 |
|
|
43
|
+
<!-- auto:props:end -->
|
|
44
|
+
|
|
45
|
+
## 依赖
|
|
46
|
+
|
|
47
|
+
> 以下表格由 `pnpm --filter @teamix-evo/ui gen:meta` 自动生成,数据源是 [`manifest.json`](../../../manifest.json)。**手工编辑 marker 之间的内容会在下次生成时被覆盖**。
|
|
48
|
+
|
|
49
|
+
<!-- auto:deps:begin -->
|
|
50
|
+
### 同库依赖
|
|
51
|
+
|
|
52
|
+
> `teamix-evo ui add button` 时,以下 entry 会被自动连带安装(无需手动 add)。
|
|
53
|
+
|
|
54
|
+
| Entry | 类型 | 描述 |
|
|
55
|
+
| --- | --- | --- |
|
|
56
|
+
| `cn` | util | Tailwind className 合并工具(clsx + tailwind-merge) |
|
|
57
|
+
|
|
58
|
+
### npm 依赖
|
|
59
|
+
|
|
60
|
+
> 业务侧需要先 `pnpm add` / `npm install` 这些包。CLI 在 `ui add` 完成后会列出此提示。
|
|
61
|
+
|
|
62
|
+
```bash
|
|
63
|
+
pnpm add @radix-ui/react-slot@^1.1.0 class-variance-authority@^0.7.0 lucide-react@^0.460.0
|
|
64
|
+
```
|
|
65
|
+
<!-- auto:deps:end -->
|
|
66
|
+
|
|
67
|
+
> 除上述声明的 props 外,Button 同时透传所有原生 `<button>` 属性(`onClick` / `type` / `aria-*` / ...);`asChild=true` 时透传至 Radix Slot 的子元素。
|
|
68
|
+
|
|
69
|
+
## AI 生成纪律
|
|
70
|
+
|
|
71
|
+
- **variant 语义**:危险动作(删除/重置)用 `destructive`;次要动作(取消)用 `secondary` 或 `ghost`;表单主操作用 `default`;**不要**用 inline 任意值(如 `bg-[#ff0000]`)。
|
|
72
|
+
- **icon 来源**:固定从 `lucide-react` import,**不要**内联 SVG,不要混用其他 icon 库。
|
|
73
|
+
- **loading + disabled**:写 `loading` 即可,**不要**额外手动加 `disabled`,组件已自动处理。
|
|
74
|
+
- **shape="circle"**:必须配 `size="icon"` 一起使用,否则视觉会变形(扁圆)。
|
|
75
|
+
- **block**:`true` 时按钮撑满,**不要**再用 `className="w-full"` 重复。
|
|
76
|
+
- **asChild**:仅当需要渲染为 `<a>` / `<Link>` 等其他元素时启用,且**不要**与 `loading` / `icon` 同时使用,会被忽略。
|
|
77
|
+
|
|
78
|
+
## Examples
|
|
79
|
+
|
|
80
|
+
```tsx
|
|
81
|
+
// 基本
|
|
82
|
+
<Button>提交</Button>
|
|
83
|
+
|
|
84
|
+
// 危险 + 加载
|
|
85
|
+
<Button variant="destructive" loading>删除中</Button>
|
|
86
|
+
|
|
87
|
+
// 图标 + 后置
|
|
88
|
+
<Button icon={<ChevronRight />} iconPosition="end">下一步</Button>
|
|
89
|
+
|
|
90
|
+
// 圆形图标按钮
|
|
91
|
+
<Button size="icon" shape="circle" variant="ghost" icon={<X />} aria-label="关闭" />
|
|
92
|
+
|
|
93
|
+
// 块级
|
|
94
|
+
<Button block>登录</Button>
|
|
95
|
+
|
|
96
|
+
// 作为链接
|
|
97
|
+
<Button asChild variant="link"><a href="/docs">查看文档</a></Button>
|
|
98
|
+
```
|
|
@@ -0,0 +1,235 @@
|
|
|
1
|
+
import type { ReactNode } from 'react';
|
|
2
|
+
import type { Meta, StoryObj } from '@storybook/react';
|
|
3
|
+
import { ChevronRight, Download, Plus, Trash, X } from 'lucide-react';
|
|
4
|
+
import { Button } from './button';
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Storybook Controls cannot edit React nodes directly, so we expose a string
|
|
8
|
+
* key whose value is mapped to a real ReactNode via `mapping`. Editors see a
|
|
9
|
+
* dropdown of icon names; the underlying prop receives the actual element.
|
|
10
|
+
*/
|
|
11
|
+
const iconMap: Record<string, ReactNode> = {
|
|
12
|
+
none: undefined,
|
|
13
|
+
chevronRight: <ChevronRight />,
|
|
14
|
+
download: <Download />,
|
|
15
|
+
plus: <Plus />,
|
|
16
|
+
trash: <Trash />,
|
|
17
|
+
x: <X />,
|
|
18
|
+
};
|
|
19
|
+
|
|
20
|
+
const meta: Meta<typeof Button> = {
|
|
21
|
+
title: '基础原语 · Foundation/Button',
|
|
22
|
+
component: Button,
|
|
23
|
+
tags: ['autodocs'],
|
|
24
|
+
parameters: {
|
|
25
|
+
docs: {
|
|
26
|
+
description: {
|
|
27
|
+
component:
|
|
28
|
+
'通用按钮 — shadcn 实现 + antd 功能扩展(loading / icon / shape / block / dashed variant)。className 走 OpenTrek semantic tokens,所有视觉来自 `@teamix-evo/design`,无 mock。',
|
|
29
|
+
},
|
|
30
|
+
},
|
|
31
|
+
},
|
|
32
|
+
argTypes: {
|
|
33
|
+
variant: {
|
|
34
|
+
description: '视觉风格,语义按 design token 走',
|
|
35
|
+
control: 'select',
|
|
36
|
+
options: [
|
|
37
|
+
'default',
|
|
38
|
+
'secondary',
|
|
39
|
+
'destructive',
|
|
40
|
+
'outline',
|
|
41
|
+
'dashed',
|
|
42
|
+
'ghost',
|
|
43
|
+
'link',
|
|
44
|
+
],
|
|
45
|
+
},
|
|
46
|
+
size: {
|
|
47
|
+
description: '尺寸,`icon` 为正方形纯图标按钮',
|
|
48
|
+
control: 'select',
|
|
49
|
+
options: ['sm', 'default', 'lg', 'icon'],
|
|
50
|
+
},
|
|
51
|
+
shape: {
|
|
52
|
+
description: '形状,`circle` 配 `size="icon"` 得圆形按钮',
|
|
53
|
+
control: 'select',
|
|
54
|
+
options: ['default', 'round', 'circle'],
|
|
55
|
+
},
|
|
56
|
+
block: {
|
|
57
|
+
description: '`true` 时撑满父容器宽度',
|
|
58
|
+
control: 'boolean',
|
|
59
|
+
},
|
|
60
|
+
loading: {
|
|
61
|
+
description: '显示自旋 spinner,自动 disabled,保留视觉宽度',
|
|
62
|
+
control: 'boolean',
|
|
63
|
+
},
|
|
64
|
+
iconPosition: {
|
|
65
|
+
description: '图标 / spinner 相对文本的位置',
|
|
66
|
+
control: 'inline-radio',
|
|
67
|
+
options: ['start', 'end'],
|
|
68
|
+
},
|
|
69
|
+
icon: {
|
|
70
|
+
description: '前置 / 后置图标(从 lucide-react 选择,演示用 mapping)',
|
|
71
|
+
options: Object.keys(iconMap),
|
|
72
|
+
mapping: iconMap,
|
|
73
|
+
control: {
|
|
74
|
+
type: 'select',
|
|
75
|
+
labels: {
|
|
76
|
+
none: '无',
|
|
77
|
+
chevronRight: '→ ChevronRight',
|
|
78
|
+
download: '⬇ Download',
|
|
79
|
+
plus: '+ Plus',
|
|
80
|
+
trash: '🗑 Trash',
|
|
81
|
+
x: '✕ X',
|
|
82
|
+
},
|
|
83
|
+
},
|
|
84
|
+
},
|
|
85
|
+
asChild: {
|
|
86
|
+
description: '用 Radix Slot 渲染为子元素(此时忽略 loading / icon)',
|
|
87
|
+
control: 'boolean',
|
|
88
|
+
},
|
|
89
|
+
disabled: { control: 'boolean' },
|
|
90
|
+
},
|
|
91
|
+
args: {
|
|
92
|
+
children: 'Button',
|
|
93
|
+
variant: 'default',
|
|
94
|
+
size: 'default',
|
|
95
|
+
shape: 'default',
|
|
96
|
+
block: false,
|
|
97
|
+
loading: false,
|
|
98
|
+
iconPosition: 'start',
|
|
99
|
+
asChild: false,
|
|
100
|
+
disabled: false,
|
|
101
|
+
},
|
|
102
|
+
};
|
|
103
|
+
|
|
104
|
+
export default meta;
|
|
105
|
+
type Story = StoryObj<typeof Button>;
|
|
106
|
+
|
|
107
|
+
// ─── Single-knob stories: editable via the Controls panel ─────────────────────
|
|
108
|
+
|
|
109
|
+
/**
|
|
110
|
+
* 默认按钮。可在 Controls 面板调 variant / size / shape / loading / icon 等
|
|
111
|
+
* 全部 props,即时预览。这是最常用的演示形态。
|
|
112
|
+
*/
|
|
113
|
+
export const Playground: Story = {
|
|
114
|
+
args: { children: 'Submit' },
|
|
115
|
+
};
|
|
116
|
+
|
|
117
|
+
/**
|
|
118
|
+
* 危险动作按钮。视觉用 `destructive` variant,语义来自 design token 的
|
|
119
|
+
* destructive 配色;不要硬编码 `bg-red-600`。
|
|
120
|
+
*/
|
|
121
|
+
export const Destructive: Story = {
|
|
122
|
+
args: { children: 'Delete', variant: 'destructive' },
|
|
123
|
+
};
|
|
124
|
+
|
|
125
|
+
/**
|
|
126
|
+
* Loading 态:自动 disabled + 替换 icon 为 spinner,视觉宽度保持一致以避免布局抖动。
|
|
127
|
+
*/
|
|
128
|
+
export const Loading: Story = {
|
|
129
|
+
args: { children: 'Saving', loading: true },
|
|
130
|
+
};
|
|
131
|
+
|
|
132
|
+
/**
|
|
133
|
+
* 带图标的按钮。`icon` 接受 ReactNode,推荐 `lucide-react`;`iconPosition` 控制
|
|
134
|
+
* 位于文本前(start)还是后(end)。
|
|
135
|
+
*/
|
|
136
|
+
export const WithIcon: Story = {
|
|
137
|
+
args: {
|
|
138
|
+
children: 'Next step',
|
|
139
|
+
icon: <ChevronRight />,
|
|
140
|
+
iconPosition: 'end',
|
|
141
|
+
},
|
|
142
|
+
};
|
|
143
|
+
|
|
144
|
+
/**
|
|
145
|
+
* 块级按钮:`block=true` 撑满父容器宽度。常用于表单主操作 / 移动端。
|
|
146
|
+
*/
|
|
147
|
+
export const Block: Story = {
|
|
148
|
+
args: { children: 'Sign in', block: true },
|
|
149
|
+
decorators: [(Story) => <div style={{ width: 360 }}>{Story()}</div>],
|
|
150
|
+
};
|
|
151
|
+
|
|
152
|
+
// ─── Visual reference stories: comparison tables, no Controls ─────────────────
|
|
153
|
+
|
|
154
|
+
/**
|
|
155
|
+
* 七种 variant 横向对照(`default / secondary / destructive / outline /
|
|
156
|
+
* dashed / ghost / link`)。视觉锚点 — 调 design token 时这条故事最直观。
|
|
157
|
+
*/
|
|
158
|
+
export const VariantsMatrix: Story = {
|
|
159
|
+
parameters: { controls: { disable: true } },
|
|
160
|
+
render: () => (
|
|
161
|
+
<div className="flex flex-wrap items-center gap-3">
|
|
162
|
+
<Button>Default</Button>
|
|
163
|
+
<Button variant="secondary">Secondary</Button>
|
|
164
|
+
<Button variant="destructive">Destructive</Button>
|
|
165
|
+
<Button variant="outline">Outline</Button>
|
|
166
|
+
<Button variant="dashed">Dashed</Button>
|
|
167
|
+
<Button variant="ghost">Ghost</Button>
|
|
168
|
+
<Button variant="link">Link</Button>
|
|
169
|
+
</div>
|
|
170
|
+
),
|
|
171
|
+
};
|
|
172
|
+
|
|
173
|
+
/**
|
|
174
|
+
* 四种 size(`sm / default / lg / icon`)。`icon` 尺寸用于纯图标按钮。
|
|
175
|
+
*/
|
|
176
|
+
export const SizesMatrix: Story = {
|
|
177
|
+
parameters: { controls: { disable: true } },
|
|
178
|
+
render: () => (
|
|
179
|
+
<div className="flex flex-wrap items-center gap-3">
|
|
180
|
+
<Button size="sm">Small</Button>
|
|
181
|
+
<Button size="default">Default</Button>
|
|
182
|
+
<Button size="lg">Large</Button>
|
|
183
|
+
<Button size="icon" aria-label="Add">
|
|
184
|
+
<Plus />
|
|
185
|
+
</Button>
|
|
186
|
+
</div>
|
|
187
|
+
),
|
|
188
|
+
};
|
|
189
|
+
|
|
190
|
+
/**
|
|
191
|
+
* 三种 shape(`default / round / circle`)。`circle` 必须配 `size="icon"`。
|
|
192
|
+
*/
|
|
193
|
+
export const ShapesMatrix: Story = {
|
|
194
|
+
parameters: { controls: { disable: true } },
|
|
195
|
+
render: () => (
|
|
196
|
+
<div className="flex flex-wrap items-center gap-3">
|
|
197
|
+
<Button shape="default">Default</Button>
|
|
198
|
+
<Button shape="round">Round</Button>
|
|
199
|
+
<Button size="icon" shape="circle" aria-label="Close">
|
|
200
|
+
<X />
|
|
201
|
+
</Button>
|
|
202
|
+
</div>
|
|
203
|
+
),
|
|
204
|
+
};
|
|
205
|
+
|
|
206
|
+
/**
|
|
207
|
+
* 状态对照:常态 / 禁用 / 加载。注意 loading 自动 disabled,不需要再传
|
|
208
|
+
* `disabled`。
|
|
209
|
+
*/
|
|
210
|
+
export const StatesMatrix: Story = {
|
|
211
|
+
parameters: { controls: { disable: true } },
|
|
212
|
+
render: () => (
|
|
213
|
+
<div className="flex flex-wrap items-center gap-3">
|
|
214
|
+
<Button>Default</Button>
|
|
215
|
+
<Button disabled>Disabled</Button>
|
|
216
|
+
<Button loading>Loading</Button>
|
|
217
|
+
<Button variant="outline" loading icon={<Download />}>
|
|
218
|
+
Loading replaces icon
|
|
219
|
+
</Button>
|
|
220
|
+
</div>
|
|
221
|
+
),
|
|
222
|
+
};
|
|
223
|
+
|
|
224
|
+
/**
|
|
225
|
+
* `asChild` 模式:把按钮视觉应用到子元素,常见用法是渲染为 `<a>` 链接。
|
|
226
|
+
* 注意此模式下 `loading` 和 `icon` 会被忽略。
|
|
227
|
+
*/
|
|
228
|
+
export const AsChild: Story = {
|
|
229
|
+
parameters: { controls: { disable: true } },
|
|
230
|
+
render: () => (
|
|
231
|
+
<Button asChild variant="link">
|
|
232
|
+
<a href="https://teamix-evo.dev">Open docs</a>
|
|
233
|
+
</Button>
|
|
234
|
+
),
|
|
235
|
+
};
|
|
@@ -0,0 +1,160 @@
|
|
|
1
|
+
import * as React from 'react';
|
|
2
|
+
import { Slot } from '@radix-ui/react-slot';
|
|
3
|
+
import { cva, type VariantProps } from 'class-variance-authority';
|
|
4
|
+
import { Loader2 } from 'lucide-react';
|
|
5
|
+
|
|
6
|
+
import { cn } from '@/utils/cn';
|
|
7
|
+
|
|
8
|
+
const buttonVariants = cva(
|
|
9
|
+
'inline-flex items-center justify-center gap-2 whitespace-nowrap text-sm font-medium transition-colors focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring disabled:pointer-events-none disabled:opacity-50 [&_svg]:pointer-events-none [&_svg]:size-4 [&_svg]:shrink-0',
|
|
10
|
+
{
|
|
11
|
+
variants: {
|
|
12
|
+
variant: {
|
|
13
|
+
default:
|
|
14
|
+
'bg-primary text-primary-foreground shadow hover:bg-primary/90',
|
|
15
|
+
secondary:
|
|
16
|
+
'bg-secondary text-secondary-foreground shadow-sm hover:bg-secondary/80',
|
|
17
|
+
destructive:
|
|
18
|
+
'bg-destructive text-destructive-foreground shadow-sm hover:bg-destructive/90',
|
|
19
|
+
outline:
|
|
20
|
+
'border border-input bg-background shadow-sm hover:bg-accent hover:text-accent-foreground',
|
|
21
|
+
dashed:
|
|
22
|
+
'border border-dashed border-input bg-background shadow-sm hover:bg-accent hover:text-accent-foreground',
|
|
23
|
+
ghost: 'hover:bg-accent hover:text-accent-foreground',
|
|
24
|
+
link: 'text-primary underline-offset-4 hover:underline',
|
|
25
|
+
},
|
|
26
|
+
size: {
|
|
27
|
+
sm: 'h-8 px-3 text-xs',
|
|
28
|
+
default: 'h-9 px-4 py-2',
|
|
29
|
+
lg: 'h-10 px-8',
|
|
30
|
+
icon: 'h-9 w-9',
|
|
31
|
+
},
|
|
32
|
+
shape: {
|
|
33
|
+
default: 'rounded-md',
|
|
34
|
+
round: 'rounded-full',
|
|
35
|
+
circle: 'rounded-full aspect-square p-0',
|
|
36
|
+
},
|
|
37
|
+
block: {
|
|
38
|
+
true: 'w-full',
|
|
39
|
+
false: '',
|
|
40
|
+
},
|
|
41
|
+
},
|
|
42
|
+
defaultVariants: {
|
|
43
|
+
variant: 'default',
|
|
44
|
+
size: 'default',
|
|
45
|
+
shape: 'default',
|
|
46
|
+
block: false,
|
|
47
|
+
},
|
|
48
|
+
},
|
|
49
|
+
);
|
|
50
|
+
|
|
51
|
+
export interface ButtonProps
|
|
52
|
+
extends Omit<React.ButtonHTMLAttributes<HTMLButtonElement>, 'children'>,
|
|
53
|
+
VariantProps<typeof buttonVariants> {
|
|
54
|
+
/**
|
|
55
|
+
* 视觉风格,语义按 design token 走。
|
|
56
|
+
* @default "default"
|
|
57
|
+
*/
|
|
58
|
+
variant?:
|
|
59
|
+
| 'default'
|
|
60
|
+
| 'secondary'
|
|
61
|
+
| 'destructive'
|
|
62
|
+
| 'outline'
|
|
63
|
+
| 'dashed'
|
|
64
|
+
| 'ghost'
|
|
65
|
+
| 'link';
|
|
66
|
+
/**
|
|
67
|
+
* 尺寸,`icon` 为正方形纯图标按钮。
|
|
68
|
+
* @default "default"
|
|
69
|
+
*/
|
|
70
|
+
size?: 'sm' | 'default' | 'lg' | 'icon';
|
|
71
|
+
/**
|
|
72
|
+
* 形状,`circle` 配 `size="icon"` 得圆形按钮。
|
|
73
|
+
* @default "default"
|
|
74
|
+
*/
|
|
75
|
+
shape?: 'default' | 'round' | 'circle';
|
|
76
|
+
/**
|
|
77
|
+
* `true` 时撑满父容器宽度。
|
|
78
|
+
* @default false
|
|
79
|
+
*/
|
|
80
|
+
block?: boolean;
|
|
81
|
+
/**
|
|
82
|
+
* 用 Radix Slot 模式渲染为子元素;此时 `loading` / `icon` 被忽略。
|
|
83
|
+
* @default false
|
|
84
|
+
*/
|
|
85
|
+
asChild?: boolean;
|
|
86
|
+
/**
|
|
87
|
+
* 显示自旋 spinner,自动 disabled,保留视觉宽度。
|
|
88
|
+
* @default false
|
|
89
|
+
*/
|
|
90
|
+
loading?: boolean;
|
|
91
|
+
/**
|
|
92
|
+
* 前置 / 后置图标(来自 `lucide-react`)。loading 时被 spinner 替换。
|
|
93
|
+
*/
|
|
94
|
+
icon?: React.ReactNode;
|
|
95
|
+
/**
|
|
96
|
+
* 图标 / spinner 相对文本的位置。
|
|
97
|
+
* @default "start"
|
|
98
|
+
*/
|
|
99
|
+
iconPosition?: 'start' | 'end';
|
|
100
|
+
/** 按钮文本 / 内容。 */
|
|
101
|
+
children?: React.ReactNode;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
const Button = React.forwardRef<HTMLButtonElement, ButtonProps>(
|
|
105
|
+
(
|
|
106
|
+
{
|
|
107
|
+
className,
|
|
108
|
+
variant,
|
|
109
|
+
size,
|
|
110
|
+
shape,
|
|
111
|
+
block,
|
|
112
|
+
asChild = false,
|
|
113
|
+
loading = false,
|
|
114
|
+
icon,
|
|
115
|
+
iconPosition = 'start',
|
|
116
|
+
disabled,
|
|
117
|
+
children,
|
|
118
|
+
...props
|
|
119
|
+
},
|
|
120
|
+
ref,
|
|
121
|
+
) => {
|
|
122
|
+
const composedClassName = cn(
|
|
123
|
+
buttonVariants({ variant, size, shape, block, className }),
|
|
124
|
+
);
|
|
125
|
+
|
|
126
|
+
if (asChild) {
|
|
127
|
+
return (
|
|
128
|
+
<Slot ref={ref} className={composedClassName} {...props}>
|
|
129
|
+
{children}
|
|
130
|
+
</Slot>
|
|
131
|
+
);
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
const leading = loading ? (
|
|
135
|
+
<Loader2
|
|
136
|
+
className="animate-spin motion-reduce:animate-none"
|
|
137
|
+
aria-hidden="true"
|
|
138
|
+
/>
|
|
139
|
+
) : (
|
|
140
|
+
icon
|
|
141
|
+
);
|
|
142
|
+
|
|
143
|
+
return (
|
|
144
|
+
<button
|
|
145
|
+
ref={ref}
|
|
146
|
+
className={composedClassName}
|
|
147
|
+
disabled={disabled || loading}
|
|
148
|
+
aria-busy={loading || undefined}
|
|
149
|
+
{...props}
|
|
150
|
+
>
|
|
151
|
+
{iconPosition === 'start' && leading}
|
|
152
|
+
{children}
|
|
153
|
+
{iconPosition === 'end' && leading}
|
|
154
|
+
</button>
|
|
155
|
+
);
|
|
156
|
+
},
|
|
157
|
+
);
|
|
158
|
+
Button.displayName = 'Button';
|
|
159
|
+
|
|
160
|
+
export { Button, buttonVariants };
|