@shipfox/react-ui 0.3.0 → 0.4.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 (290) hide show
  1. package/.storybook/preview.tsx +1 -1
  2. package/.turbo/turbo-build.log +2 -2
  3. package/.turbo/turbo-check.log +2 -2
  4. package/.turbo/turbo-type.log +1 -1
  5. package/CHANGELOG.md +11 -0
  6. package/dist/components/alert/alert.d.ts +2 -2
  7. package/dist/components/alert/alert.js +3 -3
  8. package/dist/components/alert/alert.js.map +1 -1
  9. package/dist/components/alert/alert.stories.js +2 -2
  10. package/dist/components/alert/alert.stories.js.map +1 -1
  11. package/dist/components/avatar/avatar-group.js +3 -3
  12. package/dist/components/avatar/avatar-group.js.map +1 -1
  13. package/dist/components/avatar/avatar.d.ts +4 -1
  14. package/dist/components/avatar/avatar.d.ts.map +1 -1
  15. package/dist/components/avatar/avatar.js +7 -8
  16. package/dist/components/avatar/avatar.js.map +1 -1
  17. package/dist/components/avatar/avatar.stories.js +15 -3
  18. package/dist/components/avatar/avatar.stories.js.map +1 -1
  19. package/dist/components/badge/badge.d.ts +48 -0
  20. package/dist/components/badge/badge.d.ts.map +1 -0
  21. package/dist/components/badge/badge.js +72 -0
  22. package/dist/components/badge/badge.js.map +1 -0
  23. package/dist/components/badge/badge.stories.js +802 -0
  24. package/dist/components/badge/badge.stories.js.map +1 -0
  25. package/dist/components/badge/icon-badge.d.ts +9 -0
  26. package/dist/components/badge/icon-badge.d.ts.map +1 -0
  27. package/dist/components/badge/icon-badge.js +32 -0
  28. package/dist/components/badge/icon-badge.js.map +1 -0
  29. package/dist/components/badge/index.d.ts +5 -0
  30. package/dist/components/badge/index.d.ts.map +1 -0
  31. package/dist/components/badge/index.js +6 -0
  32. package/dist/components/badge/index.js.map +1 -0
  33. package/dist/components/badge/status-badge.d.ts +9 -0
  34. package/dist/components/badge/status-badge.d.ts.map +1 -0
  35. package/dist/components/badge/status-badge.js +29 -0
  36. package/dist/components/badge/status-badge.js.map +1 -0
  37. package/dist/components/badge/user-badge.d.ts +8 -0
  38. package/dist/components/badge/user-badge.d.ts.map +1 -0
  39. package/dist/components/badge/user-badge.js +24 -0
  40. package/dist/components/badge/user-badge.js.map +1 -0
  41. package/dist/components/{button.d.ts → button/button.d.ts} +1 -1
  42. package/dist/components/button/button.d.ts.map +1 -0
  43. package/dist/components/{button.js → button/button.js} +2 -2
  44. package/dist/components/button/button.js.map +1 -0
  45. package/dist/components/{button.stories.js → button/button.stories.js} +1 -1
  46. package/dist/components/button/button.stories.js.map +1 -0
  47. package/dist/components/button/index.d.ts +2 -0
  48. package/dist/components/button/index.d.ts.map +1 -0
  49. package/dist/components/button/index.js +3 -0
  50. package/dist/components/button/index.js.map +1 -0
  51. package/dist/components/checkbox/checkbox-label.d.ts +14 -0
  52. package/dist/components/checkbox/checkbox-label.d.ts.map +1 -0
  53. package/dist/components/checkbox/checkbox-label.js +82 -0
  54. package/dist/components/checkbox/checkbox-label.js.map +1 -0
  55. package/dist/components/checkbox/checkbox-links.d.ts +18 -0
  56. package/dist/components/checkbox/checkbox-links.d.ts.map +1 -0
  57. package/dist/components/checkbox/checkbox-links.js +58 -0
  58. package/dist/components/checkbox/checkbox-links.js.map +1 -0
  59. package/dist/components/checkbox/checkbox.d.ts +9 -0
  60. package/dist/components/checkbox/checkbox.d.ts.map +1 -0
  61. package/dist/components/checkbox/checkbox.js +49 -0
  62. package/dist/components/checkbox/checkbox.js.map +1 -0
  63. package/dist/components/checkbox/checkbox.stories.js +566 -0
  64. package/dist/components/checkbox/checkbox.stories.js.map +1 -0
  65. package/dist/components/checkbox/index.d.ts +4 -0
  66. package/dist/components/checkbox/index.d.ts.map +1 -0
  67. package/dist/components/checkbox/index.js +5 -0
  68. package/dist/components/checkbox/index.js.map +1 -0
  69. package/dist/components/code-block/code-block-footer.d.ts +26 -0
  70. package/dist/components/code-block/code-block-footer.d.ts.map +1 -0
  71. package/dist/components/code-block/code-block-footer.js +86 -0
  72. package/dist/components/code-block/code-block-footer.js.map +1 -0
  73. package/dist/components/code-block/code-block.d.ts +50 -0
  74. package/dist/components/code-block/code-block.d.ts.map +1 -0
  75. package/dist/components/code-block/code-block.js +142 -0
  76. package/dist/components/code-block/code-block.js.map +1 -0
  77. package/dist/components/code-block/code-block.stories.js +341 -0
  78. package/dist/components/code-block/code-block.stories.js.map +1 -0
  79. package/dist/components/code-block/code-content.d.ts +11 -0
  80. package/dist/components/code-block/code-content.d.ts.map +1 -0
  81. package/dist/components/code-block/code-content.js +29 -0
  82. package/dist/components/code-block/code-content.js.map +1 -0
  83. package/dist/components/code-block/code-copy-button.d.ts +11 -0
  84. package/dist/components/code-block/code-copy-button.d.ts.map +1 -0
  85. package/dist/components/code-block/code-copy-button.js +49 -0
  86. package/dist/components/code-block/code-copy-button.js.map +1 -0
  87. package/dist/components/code-block/code-tabs.d.ts +16 -0
  88. package/dist/components/code-block/code-tabs.d.ts.map +1 -0
  89. package/dist/components/code-block/code-tabs.js +98 -0
  90. package/dist/components/code-block/code-tabs.js.map +1 -0
  91. package/dist/components/code-block/index.d.ts +4 -0
  92. package/dist/components/code-block/index.d.ts.map +1 -0
  93. package/dist/components/code-block/index.js +5 -0
  94. package/dist/components/code-block/index.js.map +1 -0
  95. package/dist/components/dynamic-item/dynamic-item.d.ts +13 -0
  96. package/dist/components/dynamic-item/dynamic-item.d.ts.map +1 -0
  97. package/dist/components/dynamic-item/dynamic-item.js +43 -0
  98. package/dist/components/dynamic-item/dynamic-item.js.map +1 -0
  99. package/dist/components/dynamic-item/dynamic-item.stories.js +375 -0
  100. package/dist/components/dynamic-item/dynamic-item.stories.js.map +1 -0
  101. package/dist/components/dynamic-item/index.d.ts +2 -0
  102. package/dist/components/dynamic-item/index.d.ts.map +1 -0
  103. package/dist/components/dynamic-item/index.js +3 -0
  104. package/dist/components/dynamic-item/index.js.map +1 -0
  105. package/dist/components/icon/custom/index.d.ts +2 -0
  106. package/dist/components/icon/custom/index.d.ts.map +1 -1
  107. package/dist/components/icon/custom/index.js +2 -0
  108. package/dist/components/icon/custom/index.js.map +1 -1
  109. package/dist/components/icon/custom/slack-logo.d.ts +6 -0
  110. package/dist/components/icon/custom/slack-logo.d.ts.map +1 -0
  111. package/dist/components/icon/custom/slack-logo.js +34 -0
  112. package/dist/components/icon/custom/slack-logo.js.map +1 -0
  113. package/dist/components/icon/custom/stripe-logo.d.ts +8 -0
  114. package/dist/components/icon/custom/stripe-logo.d.ts.map +1 -0
  115. package/dist/components/icon/custom/stripe-logo.js +24 -0
  116. package/dist/components/icon/custom/stripe-logo.js.map +1 -0
  117. package/dist/components/icon/icon.d.ts +11 -2
  118. package/dist/components/icon/icon.d.ts.map +1 -1
  119. package/dist/components/icon/icon.js +12 -3
  120. package/dist/components/icon/icon.js.map +1 -1
  121. package/dist/components/icon/icon.stories.js +6 -3
  122. package/dist/components/icon/icon.stories.js.map +1 -1
  123. package/dist/components/index.d.ts +9 -1
  124. package/dist/components/index.d.ts.map +1 -1
  125. package/dist/components/index.js +10 -2
  126. package/dist/components/index.js.map +1 -1
  127. package/dist/components/inline-tips/inline-tips.d.ts +1 -1
  128. package/dist/components/inline-tips/inline-tips.d.ts.map +1 -1
  129. package/dist/components/inline-tips/inline-tips.js +1 -1
  130. package/dist/components/inline-tips/inline-tips.js.map +1 -1
  131. package/dist/components/inline-tips/inline-tips.stories.js +5 -5
  132. package/dist/components/inline-tips/inline-tips.stories.js.map +1 -1
  133. package/dist/components/input/index.d.ts +2 -0
  134. package/dist/components/input/index.d.ts.map +1 -0
  135. package/dist/components/input/index.js +3 -0
  136. package/dist/components/input/index.js.map +1 -0
  137. package/dist/components/input/input.d.ts.map +1 -0
  138. package/dist/components/{input.js → input/input.js} +2 -2
  139. package/dist/components/input/input.js.map +1 -0
  140. package/dist/components/{input.stories.js → input/input.stories.js} +1 -1
  141. package/dist/components/input/input.stories.js.map +1 -0
  142. package/dist/components/item/index.d.ts +2 -0
  143. package/dist/components/item/index.d.ts.map +1 -0
  144. package/dist/components/item/index.js +3 -0
  145. package/dist/components/item/index.js.map +1 -0
  146. package/dist/components/item/item.d.ts +32 -0
  147. package/dist/components/item/item.d.ts.map +1 -0
  148. package/dist/components/item/item.js +120 -0
  149. package/dist/components/item/item.js.map +1 -0
  150. package/dist/components/item/item.stories.js +232 -0
  151. package/dist/components/item/item.stories.js.map +1 -0
  152. package/dist/components/label/index.d.ts +2 -0
  153. package/dist/components/label/index.d.ts.map +1 -0
  154. package/dist/components/label/index.js +3 -0
  155. package/dist/components/label/index.js.map +1 -0
  156. package/dist/components/label/label.d.ts +7 -0
  157. package/dist/components/label/label.d.ts.map +1 -0
  158. package/dist/components/label/label.js +13 -0
  159. package/dist/components/label/label.js.map +1 -0
  160. package/dist/components/label/label.stories.js +105 -0
  161. package/dist/components/label/label.stories.js.map +1 -0
  162. package/dist/components/moving-border/moving-border.d.ts +9 -0
  163. package/dist/components/moving-border/moving-border.d.ts.map +1 -0
  164. package/dist/components/moving-border/moving-border.js +54 -0
  165. package/dist/components/moving-border/moving-border.js.map +1 -0
  166. package/dist/components/textarea/textarea.js +1 -1
  167. package/dist/components/textarea/textarea.js.map +1 -1
  168. package/dist/components/theme/index.d.ts +2 -0
  169. package/dist/components/theme/index.d.ts.map +1 -0
  170. package/dist/components/theme/index.js +3 -0
  171. package/dist/components/theme/index.js.map +1 -0
  172. package/dist/components/{theme-provider.d.ts → theme/theme-provider.d.ts} +1 -1
  173. package/dist/components/theme/theme-provider.d.ts.map +1 -0
  174. package/dist/components/{theme-provider.js → theme/theme-provider.js} +1 -1
  175. package/dist/components/theme/theme-provider.js.map +1 -0
  176. package/dist/components/toast/index.d.ts +3 -0
  177. package/dist/components/toast/index.d.ts.map +1 -0
  178. package/dist/components/toast/index.js +4 -0
  179. package/dist/components/toast/index.js.map +1 -0
  180. package/dist/components/toast/toast-custom.d.ts +19 -0
  181. package/dist/components/toast/toast-custom.d.ts.map +1 -0
  182. package/dist/components/toast/toast-custom.js +134 -0
  183. package/dist/components/toast/toast-custom.js.map +1 -0
  184. package/dist/components/toast/toast.d.ts +5 -0
  185. package/dist/components/toast/toast.d.ts.map +1 -0
  186. package/dist/components/toast/toast.js +40 -0
  187. package/dist/components/toast/toast.js.map +1 -0
  188. package/dist/components/toast/toast.stories.js +326 -0
  189. package/dist/components/toast/toast.stories.js.map +1 -0
  190. package/dist/components/tooltip/index.d.ts +2 -0
  191. package/dist/components/tooltip/index.d.ts.map +1 -0
  192. package/dist/components/tooltip/index.js +3 -0
  193. package/dist/components/tooltip/index.js.map +1 -0
  194. package/dist/components/tooltip/tooltip.d.ts +18 -5
  195. package/dist/components/tooltip/tooltip.d.ts.map +1 -1
  196. package/dist/components/tooltip/tooltip.js +63 -3
  197. package/dist/components/tooltip/tooltip.js.map +1 -1
  198. package/dist/components/tooltip/tooltip.stories.js +560 -0
  199. package/dist/components/tooltip/tooltip.stories.js.map +1 -0
  200. package/dist/hooks/index.d.ts +3 -0
  201. package/dist/hooks/index.d.ts.map +1 -1
  202. package/dist/hooks/index.js +3 -0
  203. package/dist/hooks/index.js.map +1 -1
  204. package/dist/hooks/useResolvedTheme.d.ts +2 -0
  205. package/dist/hooks/useResolvedTheme.d.ts.map +1 -0
  206. package/dist/hooks/useResolvedTheme.js +24 -0
  207. package/dist/hooks/useResolvedTheme.js.map +1 -0
  208. package/dist/hooks/useShikiHighlight.d.ts +28 -0
  209. package/dist/hooks/useShikiHighlight.d.ts.map +1 -0
  210. package/dist/hooks/useShikiHighlight.js +106 -0
  211. package/dist/hooks/useShikiHighlight.js.map +1 -0
  212. package/dist/hooks/useShikiStyleInjection.d.ts +2 -0
  213. package/dist/hooks/useShikiStyleInjection.d.ts.map +1 -0
  214. package/dist/hooks/useShikiStyleInjection.js +34 -0
  215. package/dist/hooks/useShikiStyleInjection.js.map +1 -0
  216. package/index.css +101 -9
  217. package/package.json +6 -3
  218. package/src/assets/illustration-1.svg +92 -0
  219. package/src/assets/illustration-2.svg +14 -0
  220. package/src/assets/illustration-gradient.svg +7049 -0
  221. package/src/components/alert/alert.stories.tsx +2 -2
  222. package/src/components/alert/alert.tsx +3 -3
  223. package/src/components/avatar/avatar-group.tsx +3 -3
  224. package/src/components/avatar/avatar.stories.tsx +9 -2
  225. package/src/components/avatar/avatar.tsx +10 -6
  226. package/src/components/badge/badge.stories.tsx +468 -0
  227. package/src/components/badge/badge.tsx +147 -0
  228. package/src/components/badge/icon-badge.tsx +43 -0
  229. package/src/components/badge/index.ts +4 -0
  230. package/src/components/badge/status-badge.tsx +43 -0
  231. package/src/components/badge/user-badge.tsx +34 -0
  232. package/src/components/{button.tsx → button/button.tsx} +1 -1
  233. package/src/components/button/index.ts +1 -0
  234. package/src/components/checkbox/checkbox-label.tsx +125 -0
  235. package/src/components/checkbox/checkbox-links.tsx +90 -0
  236. package/src/components/checkbox/checkbox.stories.tsx +375 -0
  237. package/src/components/checkbox/checkbox.tsx +71 -0
  238. package/src/components/checkbox/index.ts +3 -0
  239. package/src/components/code-block/code-block-footer.tsx +173 -0
  240. package/src/components/code-block/code-block.stories.tsx +323 -0
  241. package/src/components/code-block/code-block.tsx +283 -0
  242. package/src/components/code-block/code-content.tsx +60 -0
  243. package/src/components/code-block/code-copy-button.tsx +73 -0
  244. package/src/components/code-block/code-tabs.tsx +170 -0
  245. package/src/components/code-block/index.ts +3 -0
  246. package/src/components/dynamic-item/dynamic-item.stories.tsx +261 -0
  247. package/src/components/dynamic-item/dynamic-item.tsx +68 -0
  248. package/src/components/dynamic-item/index.ts +1 -0
  249. package/src/components/icon/custom/index.ts +2 -0
  250. package/src/components/icon/custom/slack-logo.tsx +35 -0
  251. package/src/components/icon/custom/stripe-logo.tsx +27 -0
  252. package/src/components/icon/icon.stories.tsx +3 -1
  253. package/src/components/icon/icon.tsx +19 -1
  254. package/src/components/index.ts +9 -1
  255. package/src/components/inline-tips/inline-tips.stories.tsx +3 -3
  256. package/src/components/inline-tips/inline-tips.tsx +2 -2
  257. package/src/components/input/index.ts +1 -0
  258. package/src/components/{input.tsx → input/input.tsx} +1 -1
  259. package/src/components/item/index.ts +1 -0
  260. package/src/components/item/item.stories.tsx +150 -0
  261. package/src/components/item/item.tsx +182 -0
  262. package/src/components/label/index.ts +1 -0
  263. package/src/components/label/label.stories.tsx +67 -0
  264. package/src/components/label/label.tsx +15 -0
  265. package/src/components/moving-border/moving-border.tsx +67 -0
  266. package/src/components/textarea/textarea.tsx +1 -1
  267. package/src/components/theme/index.ts +1 -0
  268. package/src/components/toast/index.ts +2 -0
  269. package/src/components/toast/toast-custom.tsx +154 -0
  270. package/src/components/toast/toast.stories.tsx +369 -0
  271. package/src/components/toast/toast.tsx +41 -0
  272. package/src/components/tooltip/index.ts +1 -0
  273. package/src/components/tooltip/tooltip.stories.tsx +284 -0
  274. package/src/components/tooltip/tooltip.tsx +79 -10
  275. package/src/hooks/index.ts +3 -0
  276. package/src/hooks/useResolvedTheme.ts +34 -0
  277. package/src/hooks/useShikiHighlight.ts +140 -0
  278. package/src/hooks/useShikiStyleInjection.ts +34 -0
  279. package/dist/components/button.d.ts.map +0 -1
  280. package/dist/components/button.js.map +0 -1
  281. package/dist/components/button.stories.js.map +0 -1
  282. package/dist/components/input.d.ts.map +0 -1
  283. package/dist/components/input.js.map +0 -1
  284. package/dist/components/input.stories.js.map +0 -1
  285. package/dist/components/theme-provider.d.ts.map +0 -1
  286. package/dist/components/theme-provider.js.map +0 -1
  287. /package/dist/components/{input.d.ts → input/input.d.ts} +0 -0
  288. /package/src/components/{button.stories.tsx → button/button.stories.tsx} +0 -0
  289. /package/src/components/{input.stories.tsx → input/input.stories.tsx} +0 -0
  290. /package/src/components/{theme-provider.tsx → theme/theme-provider.tsx} +0 -0
@@ -0,0 +1,147 @@
1
+ import {Slot} from '@radix-ui/react-slot';
2
+ import {cva, type VariantProps} from 'class-variance-authority';
3
+ import {Icon, type IconName} from 'components/icon';
4
+ import type {ComponentProps} from 'react';
5
+ import {cn} from 'utils/cn';
6
+
7
+ export const badgeVariants = cva(
8
+ 'inline-flex select-none items-center justify-center font-medium transition-colors shrink-0 leading-20',
9
+ {
10
+ variants: {
11
+ variant: {
12
+ neutral:
13
+ 'bg-tag-neutral-bg text-tag-neutral-text border border-tag-neutral-border hover:bg-tag-neutral-bg-hover',
14
+ info: 'bg-tag-blue-bg text-tag-blue-text border border-tag-blue-border hover:bg-tag-blue-bg-hover',
15
+ feature:
16
+ 'bg-tag-purple-bg text-tag-purple-text border border-tag-purple-border hover:bg-tag-purple-bg-hover',
17
+ success:
18
+ 'bg-tag-success-bg text-tag-success-text border border-tag-success-border hover:bg-tag-success-bg-hover',
19
+ warning:
20
+ 'bg-tag-warning-bg text-tag-warning-text border border-tag-warning-border hover:bg-tag-warning-bg-hover',
21
+ error:
22
+ 'bg-tag-error-bg text-tag-error-text border border-tag-error-border hover:bg-tag-error-bg-hover',
23
+ },
24
+ size: {
25
+ '2xs': 'h-20 px-6 text-xs gap-4',
26
+ xs: 'h-24 px-8 text-xs gap-6',
27
+ },
28
+ radius: {
29
+ default: 'rounded-6',
30
+ rounded: 'rounded-full',
31
+ },
32
+ },
33
+ defaultVariants: {
34
+ variant: 'neutral',
35
+ size: '2xs',
36
+ radius: 'default',
37
+ },
38
+ },
39
+ );
40
+
41
+ export type BadgeVariant = VariantProps<typeof badgeVariants>['variant'];
42
+
43
+ type BaseBadgeProps = ComponentProps<'span'> &
44
+ VariantProps<typeof badgeVariants> & {
45
+ asChild?: boolean;
46
+ };
47
+
48
+ type BadgePropsWithLeftClick = BaseBadgeProps & {
49
+ iconLeft: IconName;
50
+ onIconLeftClick: (e: React.MouseEvent<HTMLButtonElement>) => void;
51
+ iconLeftAriaLabel: string;
52
+ iconRight?: IconName;
53
+ onIconRightClick?: (e: React.MouseEvent<HTMLButtonElement>) => void;
54
+ iconRightAriaLabel?: string;
55
+ };
56
+
57
+ type BadgePropsWithRightClick = BaseBadgeProps & {
58
+ iconRight: IconName;
59
+ onIconRightClick: (e: React.MouseEvent<HTMLButtonElement>) => void;
60
+ iconRightAriaLabel: string;
61
+ iconLeft?: IconName;
62
+ onIconLeftClick?: (e: React.MouseEvent<HTMLButtonElement>) => void;
63
+ iconLeftAriaLabel?: string;
64
+ };
65
+
66
+ type BadgePropsWithBothClicks = BaseBadgeProps & {
67
+ iconLeft: IconName;
68
+ onIconLeftClick: (e: React.MouseEvent<HTMLButtonElement>) => void;
69
+ iconLeftAriaLabel: string;
70
+ iconRight: IconName;
71
+ onIconRightClick: (e: React.MouseEvent<HTMLButtonElement>) => void;
72
+ iconRightAriaLabel: string;
73
+ };
74
+
75
+ type BadgePropsWithoutClicks = BaseBadgeProps & {
76
+ iconLeft?: IconName;
77
+ iconRight?: IconName;
78
+ onIconLeftClick?: never;
79
+ onIconRightClick?: never;
80
+ iconLeftAriaLabel?: never;
81
+ iconRightAriaLabel?: never;
82
+ };
83
+
84
+ export type BadgeProps =
85
+ | BadgePropsWithLeftClick
86
+ | BadgePropsWithRightClick
87
+ | BadgePropsWithBothClicks
88
+ | BadgePropsWithoutClicks;
89
+
90
+ export function Badge({
91
+ className,
92
+ variant,
93
+ size,
94
+ radius,
95
+ asChild = false,
96
+ children,
97
+ iconLeft,
98
+ iconRight,
99
+ onIconLeftClick,
100
+ onIconRightClick,
101
+ iconLeftAriaLabel,
102
+ iconRightAriaLabel,
103
+ ...props
104
+ }: BadgeProps) {
105
+ const Comp = asChild ? Slot : 'span';
106
+
107
+ const renderIcon = (
108
+ iconName: IconName,
109
+ position: 'left' | 'right',
110
+ onClick?: (e: React.MouseEvent<HTMLButtonElement>) => void,
111
+ ariaLabel?: string,
112
+ ) => {
113
+ const isInteractive = Boolean(onClick);
114
+
115
+ if (isInteractive) {
116
+ if (!ariaLabel) {
117
+ // biome-ignore lint/suspicious/noConsole: Development warning for accessibility
118
+ console.warn(
119
+ `Badge: Missing aria-label for interactive ${position} icon. Please provide icon${position === 'left' ? 'Left' : 'Right'}AriaLabel prop.`,
120
+ );
121
+
122
+ return null;
123
+ }
124
+
125
+ return (
126
+ <button
127
+ type="button"
128
+ onClick={onClick}
129
+ className="inline-flex items-center justify-center transition-colors shrink-0 hover:opacity-70 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-offset-1 focus-visible:ring-blue-500"
130
+ aria-label={ariaLabel}
131
+ >
132
+ <Icon name={iconName} className="size-12" />
133
+ </button>
134
+ );
135
+ }
136
+
137
+ return <Icon name={iconName} className="size-12" />;
138
+ };
139
+
140
+ return (
141
+ <Comp className={cn(badgeVariants({variant, size, radius}), className)} {...props}>
142
+ {iconLeft && renderIcon(iconLeft, 'left', onIconLeftClick, iconLeftAriaLabel)}
143
+ {children}
144
+ {iconRight && renderIcon(iconRight, 'right', onIconRightClick, iconRightAriaLabel)}
145
+ </Comp>
146
+ );
147
+ }
@@ -0,0 +1,43 @@
1
+ import {Icon, type IconName} from 'components/icon';
2
+ import type {ComponentProps} from 'react';
3
+ import {cn} from 'utils/cn';
4
+
5
+ export type IconBadgeVariant = 'neutral' | 'info' | 'feature' | 'success' | 'primary' | 'error';
6
+
7
+ export type IconBadgeProps = ComponentProps<'span'> & {
8
+ variant?: IconBadgeVariant;
9
+ name?: IconName;
10
+ };
11
+
12
+ const variantStyles: Record<IconBadgeVariant, string> = {
13
+ neutral: 'bg-tag-neutral-bg border-tag-neutral-border',
14
+ info: 'bg-tag-blue-bg border-tag-blue-border',
15
+ feature: 'bg-tag-purple-bg border-tag-purple-border',
16
+ success: 'bg-tag-success-bg border-tag-success-border',
17
+ primary: 'bg-tag-warning-bg border-tag-warning-border',
18
+ error: 'bg-tag-error-bg border-tag-error-border',
19
+ };
20
+
21
+ const iconColorStyles: Record<IconBadgeVariant, string> = {
22
+ neutral: 'text-tag-neutral-icon',
23
+ info: 'text-tag-blue-icon',
24
+ feature: 'text-tag-purple-icon',
25
+ success: 'text-tag-success-icon',
26
+ primary: 'text-tag-warning-icon',
27
+ error: 'text-tag-error-icon',
28
+ };
29
+
30
+ export function IconBadge({className, variant = 'neutral', name, ...props}: IconBadgeProps) {
31
+ return (
32
+ <span
33
+ className={cn(
34
+ 'inline-flex size-20 items-center justify-center rounded-6 border',
35
+ variantStyles[variant],
36
+ className,
37
+ )}
38
+ {...props}
39
+ >
40
+ {name && <Icon name={name} className={cn('shrink-0', iconColorStyles[variant])} size={12} />}
41
+ </span>
42
+ );
43
+ }
@@ -0,0 +1,4 @@
1
+ export {Badge, type BadgeProps, type BadgeVariant, badgeVariants} from './badge';
2
+ export {IconBadge, type IconBadgeProps, type IconBadgeVariant} from './icon-badge';
3
+ export {StatusBadge, type StatusBadgeProps} from './status-badge';
4
+ export {UserBadge, type UserBadgeProps} from './user-badge';
@@ -0,0 +1,43 @@
1
+ import type {ComponentProps} from 'react';
2
+ import {cn} from 'utils/cn';
3
+ import {badgeVariants} from './badge';
4
+
5
+ export type StatusBadgeProps = ComponentProps<'span'> & {
6
+ variant?: StatusVariant;
7
+ dotClassName?: string;
8
+ };
9
+
10
+ type StatusVariant = 'neutral' | 'info' | 'feature' | 'success' | 'warning' | 'error';
11
+
12
+ const dotVariantStyles: Record<StatusVariant, string> = {
13
+ neutral: 'bg-tag-neutral-icon',
14
+ info: 'bg-tag-blue-icon',
15
+ feature: 'bg-tag-purple-icon',
16
+ success: 'bg-tag-success-icon',
17
+ warning: 'bg-tag-warning-icon',
18
+ error: 'bg-tag-error-icon',
19
+ };
20
+
21
+ export function StatusBadge({
22
+ className,
23
+ variant = 'neutral',
24
+ children,
25
+ dotClassName,
26
+ ...props
27
+ }: StatusBadgeProps) {
28
+ return (
29
+ <span
30
+ className={cn(badgeVariants({variant, size: '2xs', radius: 'default'}), 'gap-6', className)}
31
+ {...props}
32
+ >
33
+ <span
34
+ className={cn(
35
+ 'size-8.5 rounded-2 shrink-0',
36
+ dotVariantStyles[variant as StatusVariant],
37
+ dotClassName,
38
+ )}
39
+ />
40
+ {children}
41
+ </span>
42
+ );
43
+ }
@@ -0,0 +1,34 @@
1
+ import {Avatar} from 'components/avatar';
2
+ import type {ComponentProps} from 'react';
3
+ import {cn} from 'utils/cn';
4
+
5
+ export type UserBadgeProps = ComponentProps<'button'> & {
6
+ name: string;
7
+ avatarSrc?: string;
8
+ avatarFallback?: string;
9
+ };
10
+
11
+ export function UserBadge({className, name, avatarSrc, avatarFallback, ...props}: UserBadgeProps) {
12
+ return (
13
+ <button
14
+ type="button"
15
+ className={cn(
16
+ 'inline-flex items-center gap-6 rounded-full px-6 py-2 text-xs font-medium leading-20',
17
+ 'bg-background-components-base hover:bg-background-components-hover active:bg-background-components-pressed',
18
+ 'text-foreground-neutral-base transition-colors',
19
+ 'border border-border-neutral-base-component',
20
+ 'h-28',
21
+ className,
22
+ )}
23
+ {...props}
24
+ >
25
+ <Avatar
26
+ className="size-16 shrink-0"
27
+ content="image"
28
+ src={avatarSrc}
29
+ fallback={avatarFallback}
30
+ />
31
+ <span className="whitespace-nowrap">{name}</span>
32
+ </button>
33
+ );
34
+ }
@@ -1,8 +1,8 @@
1
1
  import {Slot} from '@radix-ui/react-slot';
2
2
  import {cva, type VariantProps} from 'class-variance-authority';
3
+ import {Icon, type IconName} from 'components/icon';
3
4
  import type {ComponentProps} from 'react';
4
5
  import {cn} from 'utils/cn';
5
- import {Icon, type IconName} from './icon/icon';
6
6
 
7
7
  export const buttonVariants = cva(
8
8
  'rounded-6 inline-flex items-center justify-center whitespace-nowrap transition-colors disabled:pointer-events-none shrink-0 outline-none',
@@ -0,0 +1 @@
1
+ export * from './button';
@@ -0,0 +1,125 @@
1
+ import {useId} from 'react';
2
+ import {cn} from 'utils/cn';
3
+ import {Icon} from '../icon/icon';
4
+ import {Label} from '../label/label';
5
+ import {Checkbox, type CheckboxProps} from './checkbox';
6
+
7
+ export type CheckboxLabelProps = Omit<CheckboxProps, 'id'> & {
8
+ id?: string;
9
+ label: string;
10
+ optional?: boolean;
11
+ description?: string;
12
+ showInfoIcon?: boolean;
13
+ border?: boolean;
14
+ className?: string;
15
+ labelClassName?: string;
16
+ descriptionClassName?: string;
17
+ };
18
+
19
+ export function CheckboxLabel({
20
+ id,
21
+ label,
22
+ optional = false,
23
+ description,
24
+ showInfoIcon = false,
25
+ border = false,
26
+ className,
27
+ labelClassName,
28
+ descriptionClassName,
29
+ ...checkboxProps
30
+ }: CheckboxLabelProps) {
31
+ const generateId = useId();
32
+ const checkboxId = id || generateId;
33
+ const isDisabled = checkboxProps.disabled ?? false;
34
+
35
+ const renderContent = (checkboxId: string) => (
36
+ <div className="flex flex-col gap-4 flex-1 min-w-0">
37
+ <div className="flex gap-4 items-center">
38
+ <Label
39
+ className={cn(
40
+ 'text-sm leading-20 overflow-hidden text-ellipsis whitespace-nowrap',
41
+ isDisabled
42
+ ? 'font-normal text-foreground-neutral-subtle'
43
+ : 'font-medium text-foreground-neutral-base',
44
+ labelClassName,
45
+ )}
46
+ htmlFor={checkboxId}
47
+ >
48
+ {label}
49
+ </Label>
50
+ {optional && (
51
+ <span className="text-sm leading-20 font-regular text-foreground-neutral-muted whitespace-nowrap">
52
+ (Optional)
53
+ </span>
54
+ )}
55
+ {showInfoIcon && (
56
+ <Icon
57
+ name="info"
58
+ className="size-16 text-foreground-neutral-muted shrink-0"
59
+ aria-hidden="true"
60
+ />
61
+ )}
62
+ </div>
63
+ {description && (
64
+ <p
65
+ className={cn(
66
+ 'text-sm leading-20',
67
+ isDisabled ? 'text-foreground-neutral-disabled' : 'text-foreground-neutral-subtle',
68
+ descriptionClassName,
69
+ )}
70
+ >
71
+ {description}
72
+ </p>
73
+ )}
74
+ </div>
75
+ );
76
+
77
+ if (border) {
78
+ return (
79
+ <Label
80
+ htmlFor={checkboxId}
81
+ className={cn(
82
+ // Base container styles with border
83
+ 'flex items-start gap-10 rounded-8 p-8 transition-all duration-100',
84
+ // Unchecked state - default
85
+ 'bg-checkbox-unchecked-bg shadow-checkbox-unchecked',
86
+ // Unchecked state - hover
87
+ 'hover:bg-checkbox-unchecked-bg-hover',
88
+ // Unchecked state - focus
89
+ 'has-data-[state=unchecked]:focus-visible:shadow-border-interactive-with-active',
90
+ // Checked state - default
91
+ 'has-data-[state=checked]:bg-background-neutral-base has-data-[state=checked]:shadow-checkbox-checked',
92
+ // Checked state - hover
93
+ 'has-data-[state=checked]:hover:bg-background-neutral-hover',
94
+ // Checked state - focus
95
+ 'has-data-[state=checked]:focus-visible:shadow-checkbox-checked-focus',
96
+ // Indeterminate state - default
97
+ 'has-data-[state=indeterminate]:bg-background-neutral-base has-data-[state=indeterminate]:shadow-checkbox-indeterminate',
98
+ // Indeterminate state - hover
99
+ 'has-data-[state=indeterminate]:hover:bg-background-neutral-hover',
100
+ // Indeterminate state - focus
101
+ 'has-data-[state=indeterminate]:focus-visible:shadow-checkbox-indeterminate-focus',
102
+ // Disabled state
103
+ isDisabled && 'opacity-50 cursor-not-allowed',
104
+ !isDisabled && 'cursor-pointer',
105
+ className,
106
+ )}
107
+ >
108
+ <span className="p-4">
109
+ <Checkbox id={checkboxId} {...checkboxProps} />
110
+ </span>
111
+ {renderContent(checkboxId)}
112
+ </Label>
113
+ );
114
+ }
115
+
116
+ // Without border variant
117
+ return (
118
+ <div className={cn('flex items-start gap-10', className)}>
119
+ <span className="p-2">
120
+ <Checkbox id={checkboxId} {...checkboxProps} />
121
+ </span>
122
+ {renderContent(checkboxId)}
123
+ </div>
124
+ );
125
+ }
@@ -0,0 +1,90 @@
1
+ import {Label} from 'components/label';
2
+ import {type ReactNode, useId} from 'react';
3
+ import {cn} from 'utils/cn';
4
+ import {Checkbox, type CheckboxProps} from './checkbox';
5
+
6
+ export type CheckboxLink = {
7
+ label: string;
8
+ href?: string;
9
+ onClick?: () => void;
10
+ };
11
+
12
+ export type CheckboxLinksProps = Omit<CheckboxProps, 'id'> & {
13
+ id?: string;
14
+ label: string;
15
+ links: CheckboxLink[];
16
+ separator?: ReactNode;
17
+ className?: string;
18
+ labelClassName?: string;
19
+ linkClassName?: string;
20
+ };
21
+
22
+ export function CheckboxLinks({
23
+ id,
24
+ label,
25
+ links,
26
+ separator,
27
+ className,
28
+ labelClassName,
29
+ linkClassName,
30
+ ...checkboxProps
31
+ }: CheckboxLinksProps) {
32
+ const generateId = useId();
33
+ const checkboxId = id || generateId;
34
+ const isDisabled = checkboxProps.disabled ?? false;
35
+ const defaultSeparator = (
36
+ <span className="size-3 rounded-full bg-foreground-neutral-muted" aria-hidden="true" />
37
+ );
38
+
39
+ return (
40
+ <div className={cn('flex gap-10 items-start', className)}>
41
+ <span className="p-2">
42
+ <Checkbox id={checkboxId} {...checkboxProps} />
43
+ </span>
44
+ <div className="flex flex-col gap-4 items-start flex-1">
45
+ <Label
46
+ htmlFor={checkboxId}
47
+ className={cn(
48
+ 'text-sm leading-20 font-medium text-foreground-neutral-base',
49
+ isDisabled && 'cursor-not-allowed opacity-50',
50
+ labelClassName,
51
+ )}
52
+ >
53
+ {label}
54
+ </Label>
55
+ <div className="flex gap-6 items-center">
56
+ {links.map((link, index) => (
57
+ <div key={link.label} className="flex gap-6 items-center">
58
+ {link.href ? (
59
+ <a
60
+ href={link.href}
61
+ onClick={link.onClick}
62
+ className={cn(
63
+ 'text-sm leading-20 font-medium text-foreground-highlight-interactive',
64
+ 'hover:text-foreground-highlight-interactive-hover',
65
+ linkClassName,
66
+ )}
67
+ >
68
+ {link.label}
69
+ </a>
70
+ ) : (
71
+ <button
72
+ type="button"
73
+ onClick={link.onClick}
74
+ className={cn(
75
+ 'text-sm leading-20 font-medium text-foreground-highlight-interactive',
76
+ 'hover:text-foreground-highlight-interactive-hover',
77
+ linkClassName,
78
+ )}
79
+ >
80
+ {link.label}
81
+ </button>
82
+ )}
83
+ {index < links.length - 1 && (separator ?? defaultSeparator)}
84
+ </div>
85
+ ))}
86
+ </div>
87
+ </div>
88
+ </div>
89
+ );
90
+ }