@ptengine/design-components 0.3.0

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.
Files changed (197) hide show
  1. package/README.md +309 -0
  2. package/components.json +21 -0
  3. package/dist/components/ui/accordion.d.ts +8 -0
  4. package/dist/components/ui/accordion.d.ts.map +1 -0
  5. package/dist/components/ui/alert-dialog.d.ts +21 -0
  6. package/dist/components/ui/alert-dialog.d.ts.map +1 -0
  7. package/dist/components/ui/alert.d.ts +9 -0
  8. package/dist/components/ui/alert.d.ts.map +1 -0
  9. package/dist/components/ui/aspect-ratio.d.ts +4 -0
  10. package/dist/components/ui/aspect-ratio.d.ts.map +1 -0
  11. package/dist/components/ui/attachment.d.ts +26 -0
  12. package/dist/components/ui/attachment.d.ts.map +1 -0
  13. package/dist/components/ui/avatar.d.ts +10 -0
  14. package/dist/components/ui/avatar.d.ts.map +1 -0
  15. package/dist/components/ui/badge.d.ts +10 -0
  16. package/dist/components/ui/badge.d.ts.map +1 -0
  17. package/dist/components/ui/breadcrumb.d.ts +22 -0
  18. package/dist/components/ui/breadcrumb.d.ts.map +1 -0
  19. package/dist/components/ui/bubble.d.ts +19 -0
  20. package/dist/components/ui/bubble.d.ts.map +1 -0
  21. package/dist/components/ui/button-group.d.ts +13 -0
  22. package/dist/components/ui/button-group.d.ts.map +1 -0
  23. package/dist/components/ui/button.d.ts +13 -0
  24. package/dist/components/ui/button.d.ts.map +1 -0
  25. package/dist/components/ui/calendar.d.ts +9 -0
  26. package/dist/components/ui/calendar.d.ts.map +1 -0
  27. package/dist/components/ui/card.d.ts +9 -0
  28. package/dist/components/ui/card.d.ts.map +1 -0
  29. package/dist/components/ui/carousel.d.ts +23 -0
  30. package/dist/components/ui/carousel.d.ts.map +1 -0
  31. package/dist/components/ui/chart.d.ts +63 -0
  32. package/dist/components/ui/chart.d.ts.map +1 -0
  33. package/dist/components/ui/checkbox.d.ts +5 -0
  34. package/dist/components/ui/checkbox.d.ts.map +1 -0
  35. package/dist/components/ui/collapsible.d.ts +6 -0
  36. package/dist/components/ui/collapsible.d.ts.map +1 -0
  37. package/dist/components/ui/command.d.ts +81 -0
  38. package/dist/components/ui/command.d.ts.map +1 -0
  39. package/dist/components/ui/context-menu.d.ts +28 -0
  40. package/dist/components/ui/context-menu.d.ts.map +1 -0
  41. package/dist/components/ui/dialog.d.ts +22 -0
  42. package/dist/components/ui/dialog.d.ts.map +1 -0
  43. package/dist/components/ui/direction.d.ts +8 -0
  44. package/dist/components/ui/direction.d.ts.map +1 -0
  45. package/dist/components/ui/drawer.d.ts +23 -0
  46. package/dist/components/ui/drawer.d.ts.map +1 -0
  47. package/dist/components/ui/dropdown-menu.d.ts +28 -0
  48. package/dist/components/ui/dropdown-menu.d.ts.map +1 -0
  49. package/dist/components/ui/empty.d.ts +13 -0
  50. package/dist/components/ui/empty.d.ts.map +1 -0
  51. package/dist/components/ui/field.d.ts +25 -0
  52. package/dist/components/ui/field.d.ts.map +1 -0
  53. package/dist/components/ui/hover-card.d.ts +7 -0
  54. package/dist/components/ui/hover-card.d.ts.map +1 -0
  55. package/dist/components/ui/input-group.d.ts +13 -0
  56. package/dist/components/ui/input-group.d.ts.map +1 -0
  57. package/dist/components/ui/input-otp.d.ts +35 -0
  58. package/dist/components/ui/input-otp.d.ts.map +1 -0
  59. package/dist/components/ui/input.d.ts +5 -0
  60. package/dist/components/ui/input.d.ts.map +1 -0
  61. package/dist/components/ui/item.d.ts +24 -0
  62. package/dist/components/ui/item.d.ts.map +1 -0
  63. package/dist/components/ui/kbd.d.ts +5 -0
  64. package/dist/components/ui/kbd.d.ts.map +1 -0
  65. package/dist/components/ui/label.d.ts +6 -0
  66. package/dist/components/ui/label.d.ts.map +1 -0
  67. package/dist/components/ui/marker.d.ts +13 -0
  68. package/dist/components/ui/marker.d.ts.map +1 -0
  69. package/dist/components/ui/menubar.d.ts +29 -0
  70. package/dist/components/ui/menubar.d.ts.map +1 -0
  71. package/dist/components/ui/message-scroller.d.ts +26 -0
  72. package/dist/components/ui/message-scroller.d.ts.map +1 -0
  73. package/dist/components/ui/message.d.ts +11 -0
  74. package/dist/components/ui/message.d.ts.map +1 -0
  75. package/dist/components/ui/native-select.d.ts +4 -0
  76. package/dist/components/ui/native-select.d.ts.map +1 -0
  77. package/dist/components/ui/navigation-menu.d.ts +13 -0
  78. package/dist/components/ui/navigation-menu.d.ts.map +1 -0
  79. package/dist/components/ui/pagination.d.ts +35 -0
  80. package/dist/components/ui/pagination.d.ts.map +1 -0
  81. package/dist/components/ui/popover.d.ts +8 -0
  82. package/dist/components/ui/popover.d.ts.map +1 -0
  83. package/dist/components/ui/progress.d.ts +5 -0
  84. package/dist/components/ui/progress.d.ts.map +1 -0
  85. package/dist/components/ui/radio-group.d.ts +6 -0
  86. package/dist/components/ui/radio-group.d.ts.map +1 -0
  87. package/dist/components/ui/resizable.d.ts +24 -0
  88. package/dist/components/ui/resizable.d.ts.map +1 -0
  89. package/dist/components/ui/scroll-area.d.ts +6 -0
  90. package/dist/components/ui/scroll-area.d.ts.map +1 -0
  91. package/dist/components/ui/search-input.d.ts +23 -0
  92. package/dist/components/ui/search-input.d.ts.map +1 -0
  93. package/dist/components/ui/select.d.ts +14 -0
  94. package/dist/components/ui/select.d.ts.map +1 -0
  95. package/dist/components/ui/separator.d.ts +5 -0
  96. package/dist/components/ui/separator.d.ts.map +1 -0
  97. package/dist/components/ui/sheet.d.ts +28 -0
  98. package/dist/components/ui/sheet.d.ts.map +1 -0
  99. package/dist/components/ui/sidebar.d.ts +70 -0
  100. package/dist/components/ui/sidebar.d.ts.map +1 -0
  101. package/dist/components/ui/skeleton.d.ts +4 -0
  102. package/dist/components/ui/skeleton.d.ts.map +1 -0
  103. package/dist/components/ui/slider.d.ts +5 -0
  104. package/dist/components/ui/slider.d.ts.map +1 -0
  105. package/dist/components/ui/sonner.d.ts +6 -0
  106. package/dist/components/ui/sonner.d.ts.map +1 -0
  107. package/dist/components/ui/spinner.d.ts +7 -0
  108. package/dist/components/ui/spinner.d.ts.map +1 -0
  109. package/dist/components/ui/switch.d.ts +5 -0
  110. package/dist/components/ui/switch.d.ts.map +1 -0
  111. package/dist/components/ui/table.d.ts +11 -0
  112. package/dist/components/ui/table.d.ts.map +1 -0
  113. package/dist/components/ui/tabs.d.ts +13 -0
  114. package/dist/components/ui/tabs.d.ts.map +1 -0
  115. package/dist/components/ui/textarea.d.ts +5 -0
  116. package/dist/components/ui/textarea.d.ts.map +1 -0
  117. package/dist/components/ui/toggle-group.d.ts +13 -0
  118. package/dist/components/ui/toggle-group.d.ts.map +1 -0
  119. package/dist/components/ui/toggle.d.ts +13 -0
  120. package/dist/components/ui/toggle.d.ts.map +1 -0
  121. package/dist/components/ui/tooltip.d.ts +8 -0
  122. package/dist/components/ui/tooltip.d.ts.map +1 -0
  123. package/dist/hooks/use-mobile.d.ts +2 -0
  124. package/dist/hooks/use-mobile.d.ts.map +1 -0
  125. package/dist/index.cjs +82 -0
  126. package/dist/index.d.ts +71 -0
  127. package/dist/index.d.ts.map +1 -0
  128. package/dist/index.js +20969 -0
  129. package/dist/lib/utils.d.ts +4 -0
  130. package/dist/lib/utils.d.ts.map +1 -0
  131. package/llms.txt +173 -0
  132. package/package.json +125 -0
  133. package/src/components/ui/accordion.tsx +51 -0
  134. package/src/components/ui/alert-dialog.tsx +115 -0
  135. package/src/components/ui/alert.tsx +48 -0
  136. package/src/components/ui/aspect-ratio.tsx +5 -0
  137. package/src/components/ui/attachment.tsx +186 -0
  138. package/src/components/ui/avatar.tsx +57 -0
  139. package/src/components/ui/badge.tsx +40 -0
  140. package/src/components/ui/breadcrumb.tsx +101 -0
  141. package/src/components/ui/bubble.tsx +123 -0
  142. package/src/components/ui/button-group.tsx +74 -0
  143. package/src/components/ui/button.tsx +64 -0
  144. package/src/components/ui/calendar.tsx +63 -0
  145. package/src/components/ui/card.tsx +55 -0
  146. package/src/components/ui/carousel.tsx +252 -0
  147. package/src/components/ui/chart.tsx +323 -0
  148. package/src/components/ui/checkbox.tsx +26 -0
  149. package/src/components/ui/collapsible.tsx +9 -0
  150. package/src/components/ui/command.tsx +136 -0
  151. package/src/components/ui/context-menu.tsx +187 -0
  152. package/src/components/ui/dialog.tsx +103 -0
  153. package/src/components/ui/direction.tsx +15 -0
  154. package/src/components/ui/drawer.tsx +98 -0
  155. package/src/components/ui/dropdown-menu.tsx +186 -0
  156. package/src/components/ui/empty.tsx +75 -0
  157. package/src/components/ui/field.tsx +175 -0
  158. package/src/components/ui/hover-card.tsx +27 -0
  159. package/src/components/ui/input-group.tsx +80 -0
  160. package/src/components/ui/input-otp.tsx +67 -0
  161. package/src/components/ui/input.tsx +24 -0
  162. package/src/components/ui/item.tsx +118 -0
  163. package/src/components/ui/kbd.tsx +21 -0
  164. package/src/components/ui/label.tsx +19 -0
  165. package/src/components/ui/marker.tsx +67 -0
  166. package/src/components/ui/menubar.tsx +218 -0
  167. package/src/components/ui/message-scroller.tsx +206 -0
  168. package/src/components/ui/message.tsx +85 -0
  169. package/src/components/ui/native-select.tsx +29 -0
  170. package/src/components/ui/navigation-menu.tsx +120 -0
  171. package/src/components/ui/pagination.tsx +108 -0
  172. package/src/components/ui/popover.tsx +31 -0
  173. package/src/components/ui/progress.tsx +23 -0
  174. package/src/components/ui/radio-group.tsx +36 -0
  175. package/src/components/ui/resizable.tsx +40 -0
  176. package/src/components/ui/scroll-area.tsx +44 -0
  177. package/src/components/ui/search-input.tsx +97 -0
  178. package/src/components/ui/select.tsx +151 -0
  179. package/src/components/ui/separator.tsx +24 -0
  180. package/src/components/ui/sheet.tsx +124 -0
  181. package/src/components/ui/sidebar.tsx +704 -0
  182. package/src/components/ui/skeleton.tsx +9 -0
  183. package/src/components/ui/slider.tsx +41 -0
  184. package/src/components/ui/sonner.tsx +45 -0
  185. package/src/components/ui/spinner.tsx +21 -0
  186. package/src/components/ui/switch.tsx +27 -0
  187. package/src/components/ui/table.tsx +95 -0
  188. package/src/components/ui/tabs.tsx +119 -0
  189. package/src/components/ui/textarea.tsx +23 -0
  190. package/src/components/ui/toggle-group.tsx +55 -0
  191. package/src/components/ui/toggle.tsx +40 -0
  192. package/src/components/ui/tooltip.tsx +30 -0
  193. package/src/hooks/use-mobile.ts +19 -0
  194. package/src/index.ts +414 -0
  195. package/src/lib/utils.ts +7 -0
  196. package/src/styles/tokens.css +360 -0
  197. package/tailwind.preset.js +235 -0
@@ -0,0 +1,186 @@
1
+ import * as React from 'react';
2
+ import { Slot } from '@radix-ui/react-slot';
3
+ import { cva, type VariantProps } from 'class-variance-authority';
4
+
5
+ import { cn } from '../../lib/utils';
6
+ import { Button } from './button';
7
+
8
+ const attachmentVariants = cva(
9
+ 'group/attachment relative isolate flex w-fit max-w-full min-w-0 shrink-0 flex-wrap rounded-[var(--pt-radius)] border bg-card text-card-foreground transition-colors focus-within:ring-1 focus-within:ring-ring data-[state=error]:border-destructive/30 data-[state=idle]:border-dashed after:pointer-events-none after:absolute after:inset-0 after:rounded-[inherit] after:bg-[rgb(var(--pt-interaction-hover))] after:opacity-0 after:transition-opacity has-[>a,>button]:hover:after:opacity-100 has-[>a,>button]:active:after:bg-[rgb(var(--pt-interaction-press))] has-[>a,>button]:active:after:opacity-100',
10
+ {
11
+ variants: {
12
+ size: {
13
+ default:
14
+ 'gap-[8px] text-[14px] leading-[20px] has-[[data-slot=attachment-content]]:px-[10px] has-[[data-slot=attachment-content]]:py-[8px] has-[[data-slot=attachment-media]]:p-[8px]',
15
+ sm: 'gap-[10px] text-[13px] leading-[20px] has-[[data-slot=attachment-content]]:px-[8px] has-[[data-slot=attachment-content]]:py-[6px] has-[[data-slot=attachment-media]]:p-[6px]',
16
+ xs: 'gap-[6px] rounded-[var(--pt-radius-md)] text-[13px] leading-[20px] has-[[data-slot=attachment-content]]:px-[6px] has-[[data-slot=attachment-content]]:py-[4px] has-[[data-slot=attachment-media]]:p-[4px]'
17
+ },
18
+ orientation: {
19
+ horizontal: 'min-w-[160px] items-center',
20
+ vertical: 'w-[96px] flex-col has-[[data-slot=attachment-content]]:w-[7.5rem]'
21
+ }
22
+ }
23
+ }
24
+ );
25
+
26
+ function Attachment({
27
+ className,
28
+ state = 'done',
29
+ size = 'default',
30
+ orientation = 'horizontal',
31
+ ...props
32
+ }: React.HTMLAttributes<HTMLDivElement> &
33
+ VariantProps<typeof attachmentVariants> & {
34
+ state?: 'idle' | 'uploading' | 'processing' | 'error' | 'done';
35
+ }) {
36
+ return (
37
+ <div
38
+ data-slot="attachment"
39
+ data-state={state}
40
+ data-size={size}
41
+ data-orientation={orientation}
42
+ className={cn(attachmentVariants({ size, orientation }), className)}
43
+ {...props}
44
+ />
45
+ );
46
+ }
47
+
48
+ const attachmentMediaVariants = cva(
49
+ "relative flex aspect-square w-[40px] shrink-0 items-center justify-center overflow-hidden rounded-[var(--pt-radius-md)] bg-muted text-foreground group-data-[orientation=vertical]/attachment:w-full group-data-[size=sm]/attachment:w-[32px] group-data-[size=xs]/attachment:w-[28px] group-data-[size=xs]/attachment:rounded-[var(--pt-radius-sm)] group-data-[state=error]/attachment:bg-destructive/10 group-data-[state=error]/attachment:text-destructive group-data-[orientation=vertical]/attachment:[&>[data-slot=spinner]]:!size-[24px] [&_svg]:pointer-events-none [&_svg:not([class*='size-'])]:size-[16px] group-data-[orientation=vertical]/attachment:[&_svg:not([class*='size-'])]:size-[24px] group-data-[size=xs]/attachment:[&_svg:not([class*='size-'])]:size-[14px]",
50
+ {
51
+ variants: {
52
+ variant: {
53
+ icon: '',
54
+ image: 'opacity-60 group-data-[state=done]/attachment:opacity-100 group-data-[state=idle]/attachment:opacity-100 [&>img]:aspect-square [&>img]:w-full [&>img]:object-cover'
55
+ }
56
+ },
57
+ defaultVariants: {
58
+ variant: 'icon'
59
+ }
60
+ }
61
+ );
62
+
63
+ function AttachmentMedia({
64
+ className,
65
+ variant = 'icon',
66
+ ...props
67
+ }: React.HTMLAttributes<HTMLDivElement> & VariantProps<typeof attachmentMediaVariants>) {
68
+ return (
69
+ <div
70
+ data-slot="attachment-media"
71
+ data-variant={variant}
72
+ className={cn(attachmentMediaVariants({ variant }), className)}
73
+ {...props}
74
+ />
75
+ );
76
+ }
77
+
78
+ function AttachmentContent({ className, ...props }: React.HTMLAttributes<HTMLDivElement>) {
79
+ return (
80
+ <div
81
+ data-slot="attachment-content"
82
+ className={cn(
83
+ 'max-w-full min-w-0 flex-1 leading-tight group-data-[orientation=vertical]/attachment:px-[4px]',
84
+ className
85
+ )}
86
+ {...props}
87
+ />
88
+ );
89
+ }
90
+
91
+ function AttachmentTitle({ className, ...props }: React.HTMLAttributes<HTMLSpanElement>) {
92
+ return (
93
+ <span
94
+ data-slot="attachment-title"
95
+ className={cn(
96
+ 'block max-w-full min-w-0 truncate font-medium group-data-[state=processing]/attachment:animate-pulse group-data-[state=uploading]/attachment:animate-pulse',
97
+ className
98
+ )}
99
+ {...props}
100
+ />
101
+ );
102
+ }
103
+
104
+ function AttachmentDescription({ className, ...props }: React.HTMLAttributes<HTMLSpanElement>) {
105
+ return (
106
+ <span
107
+ data-slot="attachment-description"
108
+ className={cn(
109
+ 'mt-[2px] block max-w-full min-w-0 truncate text-[13px] leading-[20px] text-muted-foreground group-data-[state=error]/attachment:text-destructive/80',
110
+ className
111
+ )}
112
+ {...props}
113
+ />
114
+ );
115
+ }
116
+
117
+ function AttachmentActions({ className, ...props }: React.HTMLAttributes<HTMLDivElement>) {
118
+ return (
119
+ <div
120
+ data-slot="attachment-actions"
121
+ className={cn(
122
+ 'relative z-20 flex shrink-0 items-center group-data-[orientation=vertical]/attachment:absolute group-data-[orientation=vertical]/attachment:right-[12px] group-data-[orientation=vertical]/attachment:top-[12px] group-data-[orientation=vertical]/attachment:gap-[4px]',
123
+ className
124
+ )}
125
+ {...props}
126
+ />
127
+ );
128
+ }
129
+
130
+ function AttachmentAction({ className, variant, size, ...props }: React.ComponentProps<typeof Button>) {
131
+ return (
132
+ <Button
133
+ data-slot="attachment-action"
134
+ variant={variant ?? 'ghost'}
135
+ size={size ?? 'icon'}
136
+ className={cn('h-[24px] w-[24px] rounded-[var(--pt-radius-sm)] [&_svg]:size-[14px]', className)}
137
+ {...props}
138
+ />
139
+ );
140
+ }
141
+
142
+ function AttachmentTrigger({
143
+ className,
144
+ asChild = false,
145
+ type,
146
+ ...props
147
+ }: React.ButtonHTMLAttributes<HTMLButtonElement> & {
148
+ /** Render the child element as the root (e.g. an <a>), keeping the hit area. */
149
+ asChild?: boolean;
150
+ }) {
151
+ const Comp = asChild ? Slot : 'button';
152
+
153
+ return (
154
+ <Comp
155
+ data-slot="attachment-trigger"
156
+ type={asChild ? undefined : (type ?? 'button')}
157
+ className={cn('absolute inset-0 z-10 cursor-pointer outline-none', className)}
158
+ {...props}
159
+ />
160
+ );
161
+ }
162
+
163
+ function AttachmentGroup({ className, ...props }: React.HTMLAttributes<HTMLDivElement>) {
164
+ return (
165
+ <div
166
+ data-slot="attachment-group"
167
+ className={cn(
168
+ 'flex min-w-0 snap-x snap-mandatory scroll-px-[4px] gap-[12px] overflow-x-auto overscroll-x-contain py-[4px] [scrollbar-width:none] [&::-webkit-scrollbar]:hidden [&>[data-slot=attachment]]:flex-none [&>[data-slot=attachment]]:snap-start',
169
+ className
170
+ )}
171
+ {...props}
172
+ />
173
+ );
174
+ }
175
+
176
+ export {
177
+ Attachment,
178
+ AttachmentGroup,
179
+ AttachmentMedia,
180
+ AttachmentContent,
181
+ AttachmentTitle,
182
+ AttachmentDescription,
183
+ AttachmentActions,
184
+ AttachmentAction,
185
+ AttachmentTrigger
186
+ };
@@ -0,0 +1,57 @@
1
+ import * as React from 'react';
2
+ import * as AvatarPrimitive from '@radix-ui/react-avatar';
3
+ import { cva, type VariantProps } from 'class-variance-authority';
4
+
5
+ import { cn } from '../../lib/utils';
6
+
7
+ /* 头像三档:sm 24(字12)· md 32(字13)· lg 40(字14,默认)。字号作用于 Fallback 首字母 */
8
+ const avatarVariants = cva('relative flex shrink-0 overflow-hidden rounded-full', {
9
+ variants: {
10
+ size: {
11
+ sm: 'h-[24px] w-[24px] text-[12px] leading-[16px]',
12
+ md: 'h-[32px] w-[32px] text-[13px] leading-[20px]',
13
+ lg: 'h-[40px] w-[40px] text-[14px] leading-[20px]'
14
+ }
15
+ },
16
+ defaultVariants: {
17
+ size: 'lg'
18
+ }
19
+ });
20
+
21
+ const Avatar = React.forwardRef<
22
+ React.ElementRef<typeof AvatarPrimitive.Root>,
23
+ React.ComponentPropsWithoutRef<typeof AvatarPrimitive.Root> &
24
+ VariantProps<typeof avatarVariants>
25
+ >(({ className, size, ...props }, ref) => (
26
+ <AvatarPrimitive.Root ref={ref} className={cn(avatarVariants({ size }), className)} {...props} />
27
+ ));
28
+ Avatar.displayName = AvatarPrimitive.Root.displayName;
29
+
30
+ const AvatarImage = React.forwardRef<
31
+ React.ElementRef<typeof AvatarPrimitive.Image>,
32
+ React.ComponentPropsWithoutRef<typeof AvatarPrimitive.Image>
33
+ >(({ className, ...props }, ref) => (
34
+ <AvatarPrimitive.Image
35
+ ref={ref}
36
+ className={cn('aspect-square h-full w-full', className)}
37
+ {...props}
38
+ />
39
+ ));
40
+ AvatarImage.displayName = AvatarPrimitive.Image.displayName;
41
+
42
+ const AvatarFallback = React.forwardRef<
43
+ React.ElementRef<typeof AvatarPrimitive.Fallback>,
44
+ React.ComponentPropsWithoutRef<typeof AvatarPrimitive.Fallback>
45
+ >(({ className, ...props }, ref) => (
46
+ <AvatarPrimitive.Fallback
47
+ ref={ref}
48
+ className={cn(
49
+ 'flex h-full w-full items-center justify-center rounded-full bg-secondary text-foreground',
50
+ className
51
+ )}
52
+ {...props}
53
+ />
54
+ ));
55
+ AvatarFallback.displayName = AvatarPrimitive.Fallback.displayName;
56
+
57
+ export { Avatar, AvatarImage, AvatarFallback };
@@ -0,0 +1,40 @@
1
+ import * as React from 'react';
2
+ import { cva, type VariantProps } from 'class-variance-authority';
3
+
4
+ import { cn } from '../../lib/utils';
5
+
6
+ const badgeVariants = cva(
7
+ 'inline-flex items-center rounded-full border px-[10px] py-[2px] text-[13px] leading-[20px] font-semibold transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 [--pt-state-layer-border:1px] [&:is(a,button)]:cursor-pointer',
8
+ {
9
+ variants: {
10
+ variant: {
11
+ default:
12
+ 'border-transparent bg-primary text-primary-foreground [&:is(a,button)]:state-layer-inverse',
13
+ secondary:
14
+ 'border-transparent bg-neutral-subtler text-neutral [&:is(a,button)]:state-layer',
15
+ destructive:
16
+ 'border-transparent bg-danger-subtler text-danger [&:is(a,button)]:state-layer',
17
+ success:
18
+ 'border-transparent bg-positive-subtler text-success [&:is(a,button)]:state-layer',
19
+ warning:
20
+ 'border-transparent bg-warning-subtler text-warning [&:is(a,button)]:state-layer',
21
+ information:
22
+ 'border-transparent bg-information-subtler text-information [&:is(a,button)]:state-layer',
23
+ ai: 'border-transparent bg-ai-subtler text-ai [&:is(a,button)]:state-layer'
24
+ }
25
+ },
26
+ defaultVariants: {
27
+ variant: 'default'
28
+ }
29
+ }
30
+ );
31
+
32
+ export interface BadgeProps
33
+ extends React.HTMLAttributes<HTMLDivElement>,
34
+ VariantProps<typeof badgeVariants> {}
35
+
36
+ function Badge({ className, variant, ...props }: BadgeProps) {
37
+ return <div className={cn(badgeVariants({ variant }), className)} {...props} />;
38
+ }
39
+
40
+ export { Badge, badgeVariants };
@@ -0,0 +1,101 @@
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 '../../lib/utils';
6
+
7
+ const Breadcrumb = React.forwardRef<
8
+ HTMLElement,
9
+ React.ComponentPropsWithoutRef<'nav'> & { separator?: React.ReactNode }
10
+ >(({ ...props }, ref) => <nav ref={ref} aria-label="breadcrumb" {...props} />);
11
+ Breadcrumb.displayName = 'Breadcrumb';
12
+
13
+ const BreadcrumbList = React.forwardRef<HTMLOListElement, React.ComponentPropsWithoutRef<'ol'>>(
14
+ ({ className, ...props }, ref) => (
15
+ <ol
16
+ ref={ref}
17
+ className={cn(
18
+ 'flex flex-wrap items-center gap-[6px] break-words text-[14px] leading-[20px] text-muted-foreground sm:gap-[10px]',
19
+ className
20
+ )}
21
+ {...props}
22
+ />
23
+ )
24
+ );
25
+ BreadcrumbList.displayName = 'BreadcrumbList';
26
+
27
+ const BreadcrumbItem = React.forwardRef<HTMLLIElement, React.ComponentPropsWithoutRef<'li'>>(
28
+ ({ className, ...props }, ref) => (
29
+ <li ref={ref} className={cn('inline-flex items-center gap-[6px]', className)} {...props} />
30
+ )
31
+ );
32
+ BreadcrumbItem.displayName = 'BreadcrumbItem';
33
+
34
+ const BreadcrumbLink = React.forwardRef<
35
+ HTMLAnchorElement,
36
+ React.ComponentPropsWithoutRef<'a'> & { asChild?: boolean }
37
+ >(({ asChild, className, ...props }, ref) => {
38
+ const Comp = asChild ? Slot : 'a';
39
+ return (
40
+ <Comp
41
+ ref={ref}
42
+ className={cn('transition-colors hover:text-foreground', className)}
43
+ {...props}
44
+ />
45
+ );
46
+ });
47
+ BreadcrumbLink.displayName = 'BreadcrumbLink';
48
+
49
+ const BreadcrumbPage = React.forwardRef<HTMLSpanElement, React.ComponentPropsWithoutRef<'span'>>(
50
+ ({ className, ...props }, ref) => (
51
+ <span
52
+ ref={ref}
53
+ role="link"
54
+ aria-disabled="true"
55
+ aria-current="page"
56
+ className={cn('font-normal text-foreground', className)}
57
+ {...props}
58
+ />
59
+ )
60
+ );
61
+ BreadcrumbPage.displayName = 'BreadcrumbPage';
62
+
63
+ const BreadcrumbSeparator = ({ children, className, ...props }: React.ComponentProps<'li'>) => (
64
+ <li
65
+ role="presentation"
66
+ aria-hidden="true"
67
+ className={cn('[&>svg]:size-[14px]', className)}
68
+ {...props}
69
+ >
70
+ {children ?? <ChevronRight />}
71
+ </li>
72
+ );
73
+ BreadcrumbSeparator.displayName = 'BreadcrumbSeparator';
74
+
75
+ /* label:读屏文案,库不做 i18n,消费方传译文(默认英文) */
76
+ const BreadcrumbEllipsis = ({
77
+ className,
78
+ label = 'More',
79
+ ...props
80
+ }: React.ComponentProps<'span'> & { label?: string }) => (
81
+ <span
82
+ role="presentation"
83
+ aria-hidden="true"
84
+ className={cn('flex h-[32px] w-[36px] items-center justify-center', className)}
85
+ {...props}
86
+ >
87
+ <MoreHorizontal className="h-[16px] w-[16px]" />
88
+ <span className="sr-only">{label}</span>
89
+ </span>
90
+ );
91
+ BreadcrumbEllipsis.displayName = 'BreadcrumbEllipsis';
92
+
93
+ export {
94
+ Breadcrumb,
95
+ BreadcrumbList,
96
+ BreadcrumbItem,
97
+ BreadcrumbLink,
98
+ BreadcrumbPage,
99
+ BreadcrumbSeparator,
100
+ BreadcrumbEllipsis
101
+ };
@@ -0,0 +1,123 @@
1
+ import * as React from 'react';
2
+ import { Slot } from '@radix-ui/react-slot';
3
+ import { cva, type VariantProps } from 'class-variance-authority';
4
+
5
+ import { cn } from '../../lib/utils';
6
+
7
+ function BubbleGroup({ className, ...props }: React.HTMLAttributes<HTMLDivElement>) {
8
+ return (
9
+ <div
10
+ data-slot="bubble-group"
11
+ className={cn('flex min-w-0 flex-col gap-[8px]', className)}
12
+ {...props}
13
+ />
14
+ );
15
+ }
16
+
17
+ const bubbleVariants = cva(
18
+ 'group/bubble relative flex w-fit max-w-[80%] min-w-0 flex-col gap-[4px] group-data-[align=end]/message:self-end data-[align=end]:self-end data-[variant=ghost]:max-w-full',
19
+ {
20
+ variants: {
21
+ variant: {
22
+ default:
23
+ '[&>[data-slot=bubble-content]]:bg-primary [&>[data-slot=bubble-content]]:text-primary-foreground [&>[data-slot=bubble-content]]:[--pt-interaction-hover:var(--pt-interaction-reversal-hover)] [&>[data-slot=bubble-content]]:[--pt-interaction-press:var(--pt-interaction-reversal-press)]',
24
+ secondary:
25
+ '[&>[data-slot=bubble-content]]:bg-secondary [&>[data-slot=bubble-content]]:text-secondary-foreground',
26
+ muted: '[&>[data-slot=bubble-content]]:bg-muted',
27
+ outline:
28
+ '[&>[data-slot=bubble-content]]:border-input [&>[data-slot=bubble-content]]:bg-background [&>[data-slot=bubble-content]]:[--pt-state-layer-border:1px]',
29
+ ghost: 'border-none [&>[data-slot=bubble-content]]:rounded-none [&>[data-slot=bubble-content]]:bg-transparent [&>[data-slot=bubble-content]]:p-0',
30
+ destructive:
31
+ '[&>[data-slot=bubble-content]]:bg-destructive/10 [&>[data-slot=bubble-content]]:text-destructive dark:[&>[data-slot=bubble-content]]:bg-destructive/20'
32
+ }
33
+ },
34
+ defaultVariants: {
35
+ variant: 'default'
36
+ }
37
+ }
38
+ );
39
+
40
+ function Bubble({
41
+ variant = 'default',
42
+ align = 'start',
43
+ className,
44
+ ...props
45
+ }: React.HTMLAttributes<HTMLDivElement> &
46
+ VariantProps<typeof bubbleVariants> & {
47
+ align?: 'start' | 'end';
48
+ }) {
49
+ return (
50
+ <div
51
+ data-slot="bubble"
52
+ data-variant={variant}
53
+ data-align={align}
54
+ className={cn(bubbleVariants({ variant }), className)}
55
+ {...props}
56
+ />
57
+ );
58
+ }
59
+
60
+ function BubbleContent({
61
+ asChild = false,
62
+ className,
63
+ ...props
64
+ }: React.HTMLAttributes<HTMLDivElement> & {
65
+ /** Render the child element as the root (e.g. a <button>), keeping Bubble styles. */
66
+ asChild?: boolean;
67
+ }) {
68
+ const Comp = asChild ? Slot : 'div';
69
+
70
+ return (
71
+ <Comp
72
+ data-slot="bubble-content"
73
+ className={cn(
74
+ 'w-fit max-w-full min-w-0 overflow-hidden break-words rounded-[var(--pt-radius)] border border-transparent px-[12px] py-[8px] text-[14px] leading-[20px] leading-relaxed group-data-[align=end]/bubble:self-end',
75
+ '[&:is(button,a)]:state-layer [&:is(button)]:text-left [&:is(button,a)]:cursor-pointer [&:is(button,a)]:focus-visible:outline-none [&:is(button,a)]:focus-visible:ring-1 [&:is(button,a)]:focus-visible:ring-ring',
76
+ className
77
+ )}
78
+ {...props}
79
+ />
80
+ );
81
+ }
82
+
83
+ const bubbleReactionsVariants = cva(
84
+ 'absolute z-10 flex w-fit shrink-0 items-center justify-center gap-[4px] rounded-full bg-muted px-[6px] py-[2px] text-[14px] leading-[20px] ring ring-card has-[button]:p-0',
85
+ {
86
+ variants: {
87
+ side: {
88
+ top: 'top-0 -translate-y-3/4',
89
+ bottom: 'bottom-0 translate-y-3/4'
90
+ },
91
+ align: {
92
+ start: 'left-[12px]',
93
+ end: 'right-[12px]'
94
+ }
95
+ },
96
+ defaultVariants: {
97
+ side: 'bottom',
98
+ align: 'end'
99
+ }
100
+ }
101
+ );
102
+
103
+ function BubbleReactions({
104
+ side = 'bottom',
105
+ align = 'end',
106
+ className,
107
+ ...props
108
+ }: React.HTMLAttributes<HTMLDivElement> & {
109
+ align?: 'start' | 'end';
110
+ side?: 'top' | 'bottom';
111
+ }) {
112
+ return (
113
+ <div
114
+ data-slot="bubble-reactions"
115
+ data-align={align}
116
+ data-side={side}
117
+ className={cn(bubbleReactionsVariants({ side, align }), className)}
118
+ {...props}
119
+ />
120
+ );
121
+ }
122
+
123
+ export { BubbleGroup, Bubble, BubbleContent, BubbleReactions };
@@ -0,0 +1,74 @@
1
+ import * as React from 'react';
2
+ import { Slot } from '@radix-ui/react-slot';
3
+ import { cva, type VariantProps } from 'class-variance-authority';
4
+
5
+ import { cn } from '../../lib/utils';
6
+ import { Separator } from './separator';
7
+
8
+ const buttonGroupVariants = cva(
9
+ "flex w-fit items-stretch has-[>[data-slot=button-group]]:gap-[8px] [&>*]:focus-visible:relative [&>*]:focus-visible:z-10 [&>[data-slot=select-trigger]:not([class*='w-'])]:w-fit [&>input]:flex-1",
10
+ {
11
+ variants: {
12
+ orientation: {
13
+ horizontal:
14
+ '[&>*:not(:first-child)]:rounded-l-none [&>*:not(:first-child)]:border-l-0 [&>*:not(:last-child)]:rounded-r-none [&>*:not(:first-child)]:after:left-0',
15
+ vertical:
16
+ 'flex-col [&>*:not(:first-child)]:rounded-t-none [&>*:not(:first-child)]:border-t-0 [&>*:not(:last-child)]:rounded-b-none [&>*:not(:first-child)]:after:top-0'
17
+ }
18
+ },
19
+ defaultVariants: {
20
+ orientation: 'horizontal'
21
+ }
22
+ }
23
+ );
24
+
25
+ function ButtonGroup({
26
+ className,
27
+ orientation,
28
+ ...props
29
+ }: React.HTMLAttributes<HTMLDivElement> & VariantProps<typeof buttonGroupVariants>) {
30
+ return (
31
+ <div
32
+ role="group"
33
+ data-slot="button-group"
34
+ data-orientation={orientation}
35
+ className={cn(buttonGroupVariants({ orientation }), className)}
36
+ {...props}
37
+ />
38
+ );
39
+ }
40
+
41
+ function ButtonGroupText({
42
+ className,
43
+ asChild = false,
44
+ ...props
45
+ }: React.HTMLAttributes<HTMLDivElement> & { asChild?: boolean }) {
46
+ const Comp = asChild ? Slot : 'div';
47
+ return (
48
+ <Comp
49
+ data-slot="button-group-text"
50
+ className={cn(
51
+ "flex items-center gap-[8px] rounded-[var(--pt-radius-md)] border bg-muted px-[16px] text-[14px] leading-[20px] font-medium text-foreground [&_svg]:pointer-events-none [&_svg:not([class*='size-'])]:size-[16px]",
52
+ className
53
+ )}
54
+ {...props}
55
+ />
56
+ );
57
+ }
58
+
59
+ function ButtonGroupSeparator({
60
+ className,
61
+ orientation = 'vertical',
62
+ ...props
63
+ }: React.ComponentProps<typeof Separator>) {
64
+ return (
65
+ <Separator
66
+ data-slot="button-group-separator"
67
+ orientation={orientation}
68
+ className={cn('relative !m-0 self-stretch bg-input data-[orientation=vertical]:h-auto', className)}
69
+ {...props}
70
+ />
71
+ );
72
+ }
73
+
74
+ export { ButtonGroup, ButtonGroupSeparator, ButtonGroupText, buttonGroupVariants };
@@ -0,0 +1,64 @@
1
+ import * as React from 'react';
2
+ import { Slot } from '@radix-ui/react-slot';
3
+ import { cva, type VariantProps } from 'class-variance-authority';
4
+
5
+ import { cn } from '../../lib/utils';
6
+
7
+ const buttonVariants = cva(
8
+ 'inline-flex items-center justify-center gap-[8px] whitespace-nowrap rounded-[var(--pt-radius-md)] text-[14px] leading-[20px] font-medium transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50 [&_svg]:pointer-events-none [&_svg]:size-[16px] [&_svg]:shrink-0',
9
+ {
10
+ variants: {
11
+ variant: {
12
+ default: 'state-layer-inverse bg-primary text-primary-foreground',
13
+ brand: 'state-layer bg-brand text-brand-foreground',
14
+ secondary:
15
+ 'state-layer border border-input bg-background text-foreground [--pt-state-layer-border:1px]',
16
+ tertiary: 'state-layer bg-secondary text-secondary-foreground',
17
+ destructive: 'state-layer bg-destructive text-destructive-foreground',
18
+ ghost: 'state-layer text-foreground',
19
+ /* 静默警示钮:常态同 ghost/secondary,hover/按下 内容变 --pt-red(删除等场景) */
20
+ 'ghost-destructive':
21
+ 'state-layer text-foreground hover:text-[rgb(var(--pt-red))] active:text-[rgb(var(--pt-red))]',
22
+ 'secondary-destructive':
23
+ 'state-layer border border-input bg-background text-foreground [--pt-state-layer-border:1px] hover:text-[rgb(var(--pt-red))] active:text-[rgb(var(--pt-red))]',
24
+ link: 'text-primary underline-offset-4 hover:underline'
25
+ },
26
+ /* 控件三档刻度:sm 24(字12/图16)· default 32(字13/图16)· lg 40(字14/图20);语义图标 16/16/20 */
27
+ size: {
28
+ sm: 'h-[24px] gap-[4px] rounded-[var(--pt-radius-sm)] px-[8px] text-[12px] leading-[16px]',
29
+ default: 'h-[32px] rounded-[var(--pt-radius-md)] px-[12px] text-[13px] leading-[20px]',
30
+ lg: 'h-[40px] rounded-[var(--pt-radius-md)] px-[16px] text-[14px] leading-[20px] [&_svg]:size-[20px]',
31
+ 'icon-sm': 'h-[24px] w-[24px] rounded-[var(--pt-radius-sm)]',
32
+ icon: 'h-[32px] w-[32px]',
33
+ 'icon-lg': 'h-[40px] w-[40px] [&_svg]:size-[20px]'
34
+ }
35
+ },
36
+ defaultVariants: {
37
+ variant: 'default',
38
+ size: 'default'
39
+ }
40
+ }
41
+ );
42
+
43
+ export interface ButtonProps
44
+ extends React.ButtonHTMLAttributes<HTMLButtonElement>,
45
+ VariantProps<typeof buttonVariants> {
46
+ /** Render the child element as the root (e.g. an <a>), keeping Button styles. */
47
+ asChild?: boolean;
48
+ }
49
+
50
+ const Button = React.forwardRef<HTMLButtonElement, ButtonProps>(
51
+ ({ className, variant, size, asChild = false, ...props }, ref) => {
52
+ const Comp = asChild ? Slot : 'button';
53
+ return (
54
+ <Comp
55
+ className={cn(buttonVariants({ variant, size, className }))}
56
+ ref={ref}
57
+ {...props}
58
+ />
59
+ );
60
+ }
61
+ );
62
+ Button.displayName = 'Button';
63
+
64
+ export { Button, buttonVariants };